From 8e4253cf7c6a6c25fc64f37580c322be1bf85ef4 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 12 Jun 2023 14:39:43 +0200 Subject: [PATCH 001/112] initial commit --- .gitignore | 3 + CITATIONS.md | 11 +- assets/multiqc_config.yml | 13 - assets/samplesheet.csv | 6 +- assets/schema_input.json | 14 +- bin/check_samplesheet.py | 341 ++++++++++++-- bin/collect_metadata.py | 72 +++ conf/modules.config | 149 ++++++- conf/test.config | 20 +- docs/output.md | 160 ++++++- docs/usage.md | 216 +++++++++ lib/WorkflowPixelator.groovy | 6 - main.nf | 1 - modules.json | 13 +- modules/local/collect_metadata.nf | 82 ++++ modules/local/pixelator/analysis/main.nf | 45 ++ modules/local/pixelator/analysis/meta.yaml | 59 +++ modules/local/pixelator/annotate/main.nf | 48 ++ modules/local/pixelator/annotate/meta.yaml | 64 +++ modules/local/pixelator/collapse/main.nf | 51 +++ modules/local/pixelator/collapse/meta.yaml | 55 +++ modules/local/pixelator/concatenate/main.nf | 46 ++ modules/local/pixelator/concatenate/meta.yaml | 53 +++ modules/local/pixelator/demux/main.nf | 49 ++ modules/local/pixelator/demux/meta.yaml | 62 +++ modules/local/pixelator/graph/main.nf | 49 ++ modules/local/pixelator/graph/meta.yaml | 66 +++ modules/local/pixelator/qc/main.nf | 78 ++++ modules/local/pixelator/qc/meta.yaml | 100 +++++ modules/local/pixelator/report/main.nf | 47 ++ modules/local/rename_reads.nf | 46 ++ modules/local/samplesheet_check.nf | 16 +- modules/nf-core/cat/fastq/main.nf | 80 ++++ modules/nf-core/cat/fastq/meta.yml | 40 ++ .../custom/dumpsoftwareversions/main.nf | 2 +- .../templates/dumpsoftwareversions.py | 3 +- modules/nf-core/fastqc/main.nf | 51 --- modules/nf-core/fastqc/meta.yml | 52 --- modules/nf-core/multiqc/main.nf | 53 --- modules/nf-core/multiqc/meta.yml | 56 --- nextflow.config | 94 +++- nextflow_schema.json | 417 +++++++++++++++--- subworkflows/local/input_check.nf | 39 +- workflows/pixelator.nf | 213 +++++++-- 44 files changed, 2707 insertions(+), 434 deletions(-) create mode 100755 bin/collect_metadata.py create mode 100644 modules/local/collect_metadata.nf create mode 100644 modules/local/pixelator/analysis/main.nf create mode 100644 modules/local/pixelator/analysis/meta.yaml create mode 100644 modules/local/pixelator/annotate/main.nf create mode 100644 modules/local/pixelator/annotate/meta.yaml create mode 100644 modules/local/pixelator/collapse/main.nf create mode 100644 modules/local/pixelator/collapse/meta.yaml create mode 100644 modules/local/pixelator/concatenate/main.nf create mode 100644 modules/local/pixelator/concatenate/meta.yaml create mode 100644 modules/local/pixelator/demux/main.nf create mode 100644 modules/local/pixelator/demux/meta.yaml create mode 100644 modules/local/pixelator/graph/main.nf create mode 100644 modules/local/pixelator/graph/meta.yaml create mode 100644 modules/local/pixelator/qc/main.nf create mode 100644 modules/local/pixelator/qc/meta.yaml create mode 100644 modules/local/pixelator/report/main.nf create mode 100644 modules/local/rename_reads.nf create mode 100644 modules/nf-core/cat/fastq/main.nf create mode 100644 modules/nf-core/cat/fastq/meta.yml delete mode 100644 modules/nf-core/fastqc/main.nf delete mode 100644 modules/nf-core/fastqc/meta.yml delete mode 100644 modules/nf-core/multiqc/main.nf delete mode 100644 modules/nf-core/multiqc/meta.yml diff --git a/.gitignore b/.gitignore index 5124c9ac..b94a8f30 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,6 @@ results/ testing/ testing* *.pyc +node_modules/ +.idea +.vscode diff --git a/CITATIONS.md b/CITATIONS.md index d466b05a..598e535c 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -10,10 +10,15 @@ ## Pipeline tools -- [FastQC](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/) +- [pixelator](https://doi.org/10.1101/2023.06.05.543770) + > Karlsson, Filip, Tomasz Kallas, Divya Thiagarajan, Max Karlsson, Maud Schweitzer, Jose Fernandez Navarro, Louise Leijonancker, et al. “Molecular Pixelation: Single Cell Spatial Proteomics by Sequencing.” bioRxiv, June 8, 2023. https://doi.org/10.1101/2023.06.05.543770. + +- [cutadapt] (http://dx.doi.org/10.14806/ej.17.1.200) + > Martin, Marcel. “Cutadapt Removes Adapter Sequences from High-Throughput Sequencing Reads.” EMBnet.Journal 17, no. 1 (May 2, 2011): 10–12. https://doi.org/10.14806/ej.17.1.200. + +- [fastp] (https://doi.org/10.1002/imt2.107) + > Chen, Shifu. “Ultrafast One-Pass FASTQ Data Preprocessing, Quality Control, and Deduplication Using Fastp.” IMeta 2, no. 2 (2023): e107. https://doi.org/10.1002/imt2.107. -- [MultiQC](https://pubmed.ncbi.nlm.nih.gov/27312411/) - > Ewels P, Magnusson M, Lundin S, Käller M. MultiQC: summarize analysis results for multiple tools and samples in a single report. Bioinformatics. 2016 Oct 1;32(19):3047-8. doi: 10.1093/bioinformatics/btw354. Epub 2016 Jun 16. PubMed PMID: 27312411; PubMed Central PMCID: PMC5039924. ## Software packaging/containerisation tools diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index 000a465b..e69de29b 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -1,13 +0,0 @@ -report_comment: > - This report has been generated by the nf-core/pixelator - analysis pipeline. For information about how to interpret these results, please see the - documentation. -report_section_order: - "nf-core-pixelator-methods-description": - order: -1000 - software_versions: - order: -1001 - "nf-core-pixelator-summary": - order: -1002 - -export_plots: true diff --git a/assets/samplesheet.csv b/assets/samplesheet.csv index 5f653ab7..c9713e42 100644 --- a/assets/samplesheet.csv +++ b/assets/samplesheet.csv @@ -1,3 +1,3 @@ -sample,fastq_1,fastq_2 -SAMPLE_PAIRED_END,/path/to/fastq/files/AEG588A1_S1_L002_R1_001.fastq.gz,/path/to/fastq/files/AEG588A1_S1_L002_R2_001.fastq.gz -SAMPLE_SINGLE_END,/path/to/fastq/files/AEG588A4_S4_L003_R1_001.fastq.gz, +sample,design,panel,fastq_1,fastq_2 +test_data_se,D12,/path/to/test_panel.csv,/path/to/test_data.fastq.gz, +uropod_control,D21,/path/to/UNO_D21_conjV21.csv,/path/to/uropod_control_300k_S1_R1_001.fastq.gz,uropod_control_300k_S1_R2_001.fastq.gz diff --git a/assets/schema_input.json b/assets/schema_input.json index c3a45df3..fec206cb 100644 --- a/assets/schema_input.json +++ b/assets/schema_input.json @@ -12,6 +12,18 @@ "pattern": "^\\S+$", "errorMessage": "Sample name must be provided and cannot contain spaces" }, + "design": { + "type": "string", + "enum": [ + "D21" + ], + "errorMessage": "Design must be specified" + }, + "panel": { + "type": "string", + "pattern": "^\\S+.(csv|tsv)$", + "errorMessage": "Panel file must be provided, cannot contain spaces and must have extension '.csv'" + }, "fastq_1": { "type": "string", "pattern": "^\\S+\\.f(ast)?q\\.gz$", @@ -31,6 +43,6 @@ ] } }, - "required": ["sample", "fastq_1"] + "required": ["sample", "design", "panel", "fastq_1"] } } diff --git a/bin/check_samplesheet.py b/bin/check_samplesheet.py index 4a758fe0..0e655ce6 100755 --- a/bin/check_samplesheet.py +++ b/bin/check_samplesheet.py @@ -4,17 +4,92 @@ """Provide a command line tool to validate and transform tabular samplesheets.""" +import abc import argparse import csv import logging import sys +import urllib.parse from collections import Counter -from pathlib import Path +from pathlib import Path, PurePath +import re +from typing import Iterable, List, MutableMapping, Optional, Set +from os import PathLike logger = logging.getLogger() -class RowChecker: +def make_absolute_path(path: str, base: PathLike = None) -> str: + """If `path` is a relative path without a scheme, resolve it as relative to `base` + + Take into account that paths can be references to remote resources eg. [s3://, az://, gs://, file:///, https://, ... ] + """ + url = urllib.parse.urlparse(path) + if url.scheme: + return path + + if base is None: + url_props = list(url) + url_props[0] = "file" + path_component = PurePath(url_props[2]) + url_props[2] = str(path_component) if path_component.is_absolute() else str(PurePath("/", str(path_component))) + urllib.parse.urlunparse(url_props) + return urllib.parse.urlunparse(url_props) + + # If the base url has a scheme we need to keep that + # purepath will remove double slashes and this invalidates the base scheme + base_url = urllib.parse.urlparse(str(base)) + scheme = base_url[0] or "file" + resolved_path = PurePath(base_url.path) / path + url = list(base_url) + url[0] = base_url[0] or "file" + url[2] = str(resolved_path) + + # Make sure there are three /// (hidden netloc) in urls. + # other schemes [s3, gs, az] only use two // + if scheme == "file": + resolved_path = str(resolved_path) if resolved_path.is_absolute() else str(PurePath("/", str(resolved_path))) + else: + resolved_path = str(resolved_path).lstrip("/") + + return f"{scheme}://{resolved_path}" + + +def validate_whitespace(row: MutableMapping[str, str], index: int): + sample_name = row["sample"] + for k, v in row.items(): + if re.search("^\s+|s+$", v): + raise AssertionError( + f'The sample sheet contains leading or trailing whitespaces in column "{k}" for sample "{sample_name}". ' + "Remove whitespace or enclose with quotes!" + ) + + +def read_design_options_file(path: Path) -> Set[str]: + designs = [] + with open(str(path), "r") as f: + designs = f.readlines() + + return {d.strip() for d in designs} + + +class BaseChecker(metaclass=abc.ABCMeta): + REQUIRED_COLUMNS: Set[str] = {} + + @classmethod + def check_headers(cls, headers) -> bool: + return set(cls.REQUIRED_COLUMNS).issubset(headers) + + @abc.abstractmethod + def validate_and_transform(self, row): + return NotImplemented + + @classmethod + def output_headers(cls, headers: Iterable[str]) -> List[str]: + return list(headers) + + +class RowChecker(BaseChecker): """ Define a service that can validate and transform each given row. @@ -60,6 +135,12 @@ def __init__( self._seen = set() self.modified = [] + @staticmethod + def get_base_dir(path: str) -> str: + url = urllib.parse.urlparse(path) + parent_path = PurePath(url.path).parent + return f"{url.scheme}://{str(parent_path)}" + def validate_and_transform(self, row): """ Perform all validations on the given row and insert the read pairing status. @@ -130,7 +211,188 @@ def validate_unique_samples(self): row[self._sample_col] = f"{sample}_T{seen[sample]}" -def read_head(handle, num_lines=10): +class PixelatorRowChecker(RowChecker): + DEFAULT_GROUP = "default" + REQUIRED_COLUMNS = ["sample", "assay", "design", "panel", "fastq_1", "fastq_2"] + + def __init__(self, samplesheet_path=None, design_options: Optional[Set[str]] = None, **kwargs): + super().__init__( + sample_col="sample", first_col="fastq_1", second_col="fastq_2", single_col="single_end", **kwargs + ) + self._panel_col = "panel" + self._assay_col = "assay" + self._design_col = "design" + self._samplesheet_path = samplesheet_path + self._base_dir = self.get_base_dir(samplesheet_path) if samplesheet_path else None + self.design_options = design_options + + @classmethod + def output_headers(cls, headers: Iterable[str]) -> List[str]: + headers = list(headers) + headers.insert(1, "single_end") + return headers + + def _validate_assay(self, row): + """Assert that the assay column exists and has supported values.""" + val = row[self._assay_col] + if len(val) <= 0: + raise AssertionError(f"The {self._assay_col} field is required.") + + def _validate_design(self, row): + """Assert that the design column exists and has supported values.""" + val = row[self._design_col] + + if len(val) <= 0: + raise AssertionError(f"The {self._design_col} field is required.") + + if self.design_options is None: + return + + if val not in self.design_options: + supported_designs = ",".join(self.design_options) + raise AssertionError(f'Unsupported design: "{val}", expected one of: {supported_designs}') + + def _validate_panelfile(self, row): + """Assert that the panel column exists and has supported values.""" + if len(row[self._panel_col]) <= 0: + raise AssertionError(f"The {self._panel_col} field is required.") + + def _resolve_relative_paths(self, row): + first = make_absolute_path(row[self._first_col], self._base_dir) + second = make_absolute_path(row[self._second_col], self._base_dir) + panel = make_absolute_path(row[self._panel_col], self._base_dir) + + row[self._first_col] = first + row[self._second_col] = second + row[self._panel_col] = panel + + def validate_and_transform(self, row): + """ + Perform all validations on the given row and insert the read pairing status. + + Args: + row (dict): A mapping from column headers (keys) to elements of that row + (values). + + """ + self._validate_sample(row) + self._validate_assay(row) + self._validate_design(row) + self._validate_first(row) + self._validate_second(row) + self._validate_pair(row) + self._resolve_relative_paths(row) + self._seen.add((row[self._sample_col], row[self._first_col])) + self.modified.append(row) + + +class PixelatorAggregateRowChecker(BaseChecker): + """ + Define a service that can validate and transform each given row. + + Attributes: + modified (list): A list of dicts, where each dict corresponds to a previously + validated and transformed row. The order of rows is maintained. + + """ + + REQUIRED_COLUMNS = ["sample", "matrix"] + + VALID_FORMATS = ( + ".h5ad", + ".h5ad.gz", + ) + + def __init__( + self, + sample_col="sample", + group_col="group", + matrix_col="matrix", + samplesheet_path=None, + **kwargs, + ): + """ + Initialize the row checker with the expected column names. + + Args: + sample_col (str): The name of the column that contains the sample name + (default "sample"). + group_col (str): The name of the column that contains the group + assignment + second_col (str): The name of the column that contains the matrix file + in .h5ad or .h5ad.gz format + """ + self._sample_col = sample_col + self._group_col = group_col + self._matrix_col = matrix_col + self._samplesheet_path = samplesheet_path + self._base_dir = PurePath(self._samplesheet_path).parent + self._seen = set() + self.modified = [] + + @classmethod + def output_headers(cls, headers: Iterable[str]) -> List[str]: + headers = list(headers) + if not "group" in headers: + headers.insert(1, "group") + + return headers + + def validate_and_transform(self, row): + """ + Perform all validations on the given row and insert the read pairing status. + + Args: + row (dict): A mapping from column headers (keys) to elements of that row + (values). + + """ + self._validate_sample(row) + self._validate_group(row) + self._validate_matrix(row) + self._seen.add(row[self._sample_col]) + self.modified.append(row) + + def _validate_sample(self, row): + """Assert that the sample name exists and convert spaces to underscores.""" + if len(row[self._sample_col]) <= 0: + raise AssertionError("Sample input is required.") + # Sanitize samples slightly. + row[self._sample_col] = row[self._sample_col].replace(" ", "_") + + def _validate_group(self, row): + """Add default group entry if not set.""" + if not self._group_col in row: + row[self._group_col] = 0 + + def _validate_matrix(self, row): + """Assert that the matrix entry has the right format if it exists.""" + if len(row[self._matrix_col]) <= 0: + raise AssertionError("The matrix field is required") + + self._validate_h5ad_format(row[self._matrix_col]) + matrix_path = make_absolute_path(row[self._matrix_col], self._base_dir) + + row[self._matrix_col] = matrix_path + + def _validate_h5ad_format(self, filename): + """Assert that a given filename has one of the expected H5AD extensions.""" + + if not any(filename.endswith(extension) for extension in self.VALID_FORMATS): + raise AssertionError( + f"The matrix file has an unrecognized extension: {filename}\n" + f"It should be one of: {', '.join(self.VALID_FORMATS)}" + ) + + def validate_unique_samples(self): + """ + Assert that the sample name is unique. + """ + if len(self._seen) != len(self.modified): + raise AssertionError("The sample name must be unique.") + + +def read_head(handle, num_lines=5): """Read the specified number of lines from the current position in the file.""" lines = [] for idx, line in enumerate(handle): @@ -158,11 +420,14 @@ def sniff_format(handle): peek = read_head(handle) handle.seek(0) sniffer = csv.Sniffer() + if not sniffer.has_header(peek): + logger.critical("The given sample sheet does not appear to contain a header.") + sys.exit(1) dialect = sniffer.sniff(peek) return dialect -def check_samplesheet(file_in, file_out): +def check_samplesheet(file_in, file_out, checker: BaseChecker): """ Check that the tabular samplesheet has the structure expected by nf-core pipelines. @@ -174,40 +439,29 @@ def check_samplesheet(file_in, file_out): CSV, TSV, or any other format automatically recognized by ``csv.Sniffer``. file_out (pathlib.Path): Where the validated and transformed samplesheet should be created; always in CSV format. - - Example: - This function checks that the samplesheet follows the following structure, - see also the `viral recon samplesheet`_:: - - sample,fastq_1,fastq_2 - SAMPLE_PE,SAMPLE_PE_RUN1_1.fastq.gz,SAMPLE_PE_RUN1_2.fastq.gz - SAMPLE_PE,SAMPLE_PE_RUN2_1.fastq.gz,SAMPLE_PE_RUN2_2.fastq.gz - SAMPLE_SE,SAMPLE_SE_RUN1_1.fastq.gz, - - .. _viral recon samplesheet: - https://raw.githubusercontent.com/nf-core/test-datasets/viralrecon/samplesheet/samplesheet_test_illumina_amplicon.csv - """ - required_columns = {"sample", "fastq_1", "fastq_2"} # See https://docs.python.org/3.9/library/csv.html#id3 to read up on `newline=""`. with file_in.open(newline="") as in_handle: reader = csv.DictReader(in_handle, dialect=sniff_format(in_handle)) + # Validate the existence of the expected header columns. - if not required_columns.issubset(reader.fieldnames): - req_cols = ", ".join(required_columns) + if not checker.check_headers(reader.fieldnames): + req_cols = ", ".join(checker.REQUIRED_COLUMNS) logger.critical(f"The sample sheet **must** contain these column headers: {req_cols}.") sys.exit(1) - # Validate each row. - checker = RowChecker() + for i, row in enumerate(reader): try: + validate_whitespace(row, i) checker.validate_and_transform(row) except AssertionError as error: logger.critical(f"{str(error)} On line {i + 2}.") sys.exit(1) + checker.validate_unique_samples() - header = list(reader.fieldnames) - header.insert(1, "single_end") + + header = checker.output_headers(reader.fieldnames) + # See https://docs.python.org/3.9/library/csv.html#id3 to read up on `newline=""`. with file_out.open(mode="w", newline="") as out_handle: writer = csv.DictWriter(out_handle, header, delimiter=",") @@ -234,6 +488,23 @@ def parse_args(argv=None): type=Path, help="Transformed output samplesheet in CSV format.", ) + parser.add_argument( + "--design-options", metavar="DESIGNS_FILE", type=Path, help="File with possible design options on each line" + ) + parser.add_argument( + "--samplesheet-path", + metavar="PATH", + type=str, + help="Local or remote location of the samplesheet", + ) + parser.add_argument( + "--mode", + metavar="SampleSheetType", + type=str, + choices=("main", "aggregate"), + default="main", + help="Type of samplesheet (default: main)", + ) parser.add_argument( "-l", "--log-level", @@ -248,11 +519,31 @@ def main(argv=None): """Coordinate argument parsing and program execution.""" args = parse_args(argv) logging.basicConfig(level=args.log_level, format="[%(levelname)s] %(message)s") + if not args.file_in.is_file(): logger.error(f"The given input file {args.file_in} was not found!") sys.exit(2) + args.file_out.parent.mkdir(parents=True, exist_ok=True) - check_samplesheet(args.file_in, args.file_out) + + design_options = None + if args.design_options: + if not args.design_options.is_file(): + logger.error(f"The given design options file {args.design_options} was not found!") + sys.exit(2) + + design_options = read_design_options_file(args.design_options) + + checker = None + if args.mode == "main": + checker = PixelatorRowChecker(samplesheet_path=args.samplesheet_path, design_options=design_options) + elif args.mode == "aggregate": + checker = PixelatorAggregateRowChecker(samplesheet_path=args.samplesheet_path) + else: + logger.error(f"The given samplesheet mode {args.mode} is invalid!") + sys.exit(2) + + check_samplesheet(args.file_in, args.file_out, checker) if __name__ == "__main__": diff --git a/bin/collect_metadata.py b/bin/collect_metadata.py new file mode 100755 index 00000000..7e1b3fe4 --- /dev/null +++ b/bin/collect_metadata.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python + +import sys +import subprocess +from pathlib import Path +import importlib.metadata +import json +import argparse +import ruamel.yaml as yaml + + +installed_packages = {d.name: d.version for d in importlib.metadata.distributions()} + + +def subtool_versions(): + cutadapt_proc = subprocess.run(["cutadapt", "--version"], capture_output=True, text=True) + fastp_proc = subprocess.run(["fastp", "--version"], capture_output=True, text=True) + + cutadapt_version = cutadapt_proc.stdout.strip("\n") + fastp_version = fastp_proc.stderr.strip("\n").split(" ")[-1] + + return {"cutadapt_version": cutadapt_version, "fastp_version": fastp_version} + + +def main(args): + dep_versions = subtool_versions() + root = { + "platform": sys.platform, + "python": { + "version": { + "major": sys.version_info.major, + "minor": sys.version_info.minor, + "micro": sys.version_info.micro, + "releaselevel": sys.version_info.releaselevel, + "serial": sys.version_info.serial, + }, + "packages": installed_packages, + }, + "fastp": {"version": dep_versions["fastp_version"]}, + "cutadapt": {"version": dep_versions["cutadapt_version"]}, + } + + workflow_data = None + if args.workflow_data is not None and args.workflow_data.exists(): + with open(str(args.workflow_data)) as f: + workflow_data = json.load(f) + + if workflow_data: + root = {**root, **workflow_data} + + with open("metadata.json", "w") as f: + json.dump(root, f, indent=4) + + with open("versions.yml", "w") as f: + yaml.dump( + data={ + "args.process_name": { + "python": f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}" + } + }, + stream=f, + ) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + + parser.add_argument("--process-name", dest="process_name", type=str) + parser.add_argument("--workflow-data", dest="workflow_data", type=Path, default=None) + args = parser.parse_args() + + main(args) diff --git a/conf/modules.config b/conf/modules.config index da58a5d8..b7ac545a 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -10,6 +10,21 @@ ---------------------------------------------------------------------------------------- */ +def get_pixelator_container(params) { + if (params.pixelator_container) { + return params.pixelator_container + } + if (params.pixelator_container_source == 'ghcr' && params.pixelator_tag) { + return "ghcr.io/pixelgentechnologies/pixelator:${params.pixelator_tag}" + } + if (params.pixelator_container_source == 'aws-ecr' && params.pixelator_tag) { + return "890888997283.dkr.ecr.eu-north-1.amazonaws.com/pixelator:${params.pixelator_tag}" + } + + return null +} + + process { publishDir = [ @@ -24,10 +39,139 @@ process { mode: params.publish_dir_mode, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] + + // Important: Use closure to delay evaluation until all params are available + container = { get_pixelator_container(params) } + } + + withName: "PIXELATOR.*" { + ext.singularity_pull_docker_container = true + + publishDir = [ + [ + path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + mode: "${params.publish_dir_mode}", + saveAs: { filename -> (filename.endsWith('.log') || filename.equals('versions.yml')) ? null : filename } + ], + [ + path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}/logs" }, + mode: params.publish_dir_mode, + pattern: "*.log" + ] + ] + + // Important: Use closure to delay evaluation until all params are available + container = { get_pixelator_container(params) } + } + + withName: RENAME_READS { + publishDir = [ + enabled: false, + path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + ] + } + + withName: PIXELATOR_CONCATENATE { + ext.args = { + [ + "--design ${meta.design}", + ].join(' ').trim() + } } - withName: FASTQC { - ext.args = '--quiet' + withName: PIXELATOR_QC { + ext.args = { + [ + "--design ${meta.design}", + params.trim_front ? "--trim-front ${params.trim_front}": '', + params.trim_tail ? "--trim-tail ${params.trim_tail}": '', + params.max_length ? "--max-length ${params.max_length}": '', + params.min_length ? "--min-length ${params.min_length}": '', + (params.max_n_bases != null) ? "--max-n-bases ${params.max_n_bases}": '', + params.avg_qual ? "--avg-qual ${params.avg_qual}": '', + params.dedup ? "--dedup ${params.dedup}": '', + params.remove_polyg ? "--remove_polyg ${params.remove_polyg}": '', + ].join(' ').trim() + } + + ext.args2 = { [ + "--design ${meta.design}", + params.adapterqc_mismatches ? "--mismatches ${params.adapterqc_mismatches}": '', + params.pbs1 ? "--pbs1 ${params.pbs1}": '', + params.pbs2 ? "--pbs1 ${params.pbs2}": '', + ].join(' ').trim() + } + } + + withName: PIXELATOR_DEMUX { + ext.args = { + [ + "--design ${meta.design}", + params.demux_mismatches ? "--mismatches ${params.demux_mismatches}": '', + params.demux_min_length ? "--mismatches ${params.demux_min_length}": '', + params.anchored ? "--anchored ${params.anchored}": '', + params.rev_complement ? "--rev-complement ${params.rev_complement}": '', + ].join(' ').trim() + } + } + + withName: PIXELATOR_COLLAPSE { + ext.args = [ + params.algorithm ? "--algorithm ${params.algorithm}": '', + params.upia_start ? "--upi1-start ${params.upia_start}": '', + params.upia_end ? "--upi1-end ${params.upia_end}": '', + params.upib_start ? "--upi2-start ${params.upib_start}": '', + params.upib_end ? "--upi2-end ${params.upib_end}": '', + params.umia_start ? "--umi1-start ${params.umia_start}": '', + params.umia_end ? "--umi1-end ${params.umia_end}": '', + params.umib_start ? "--umi2-start ${params.umib_start}": '', + params.umib_end ? "--umi2-end ${params.umib_end}": '', + params.neighbours ? "--neighbours ${params.neighbours}": '', + params.collapse_mismatches ? "--mismatches ${params.collapse_mismatches}": '', + params.collapse_min_count ? "--min-count ${params.collapse_min_count}": '', + params.use_counts ? "--use-counts": '', + ].join(' ').trim() + } + + withName: PIXELATOR_GRAPH { + ext.args = [ + params.multiplet_recovery ? "--multiplet-recovery ${params.multiplet_recovery}" : '', + params.fast_greedy_fraction ? "--fast-greedy-fraction ${params.fast_greedy_fraction}": '', + params.fast_greedy_cutoff ? "--fast-greedy-cutoff ${params.fast_greedy_cutoff}": '', + params.leiden_iterations ? "--leiden-iterations ${params.leiden_iterations}": '', + params.cluster_min_count ? "--min-count ${params.cluster_min_count}" : '', + ].join(' ').trim() + } + + withName: PIXELATOR_ANNOTATE { + ext.args = [ + params.min_size ? "--min-size ${params.min_size}" : '', + params.max_size ? "--max-size ${params.max_size}" : '', + params.dynamic_filter ? "--dynamic-filter ${params.dynamic_filter}" : '', + params.cell_type_assignments ? "--cell-type-assignments" : '', + params.majority_vote ? "--majority-vote" : '', + params.aggregate_calling ? "--aggregate-calling" : '', + ].join(' ').trim() + } + + withName: PIXELATOR_ANALYSIS { + ext.args = [ + params.compute_polarization ? "--compute-polarization" : '', + params.compute_colocalization ? "--compute-colocalization" : '', + params.use_full_bipartite ? "--use-full-bipartite " : '', + params.normalization ? "--normalization ${params.normalization}" : '', + ].join(' ').trim() + } + + withName: COLLECT_METADATA { + publishDir = [ + path: { "${params.outdir}/pipeline_info" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + + // Important: Use closure to delay evaluation until all params are available + container = { get_pixelator_container(params) } } withName: CUSTOM_DUMPSOFTWAREVERSIONS { @@ -37,5 +181,4 @@ process { pattern: '*_versions.yml' ] } - } diff --git a/conf/test.config b/conf/test.config index bd0b4ab3..0f63aa02 100644 --- a/conf/test.config +++ b/conf/test.config @@ -22,8 +22,22 @@ params { // Input data // TODO nf-core: Specify the paths to your test data on nf-core/test-datasets // TODO nf-core: Give any required params for the test so that command line flags are not needed - input = 'https://raw.githubusercontent.com/nf-core/test-datasets/viralrecon/samplesheet/samplesheet_test_illumina_amplicon.csv' + // input = 'https://raw.githubusercontent.com/nf-core/test-datasets/viralrecon/samplesheet/samplesheet_test_illumina_amplicon.csv' - // Genome references - genome = 'R64-1-1' + // TODO nf-core: Specify the paths to your test data on nf-core/test-datasets + // TODO nf-core: Give any required params for the test so that command line flags are not needed + input = "${params.testdata_root}/testdata/micro/test_samplesheet.csv" + + anchored = false + collapse_mismatches = 1 + cluster_min_count = 2 + min_size = 1 + max_size = 10000 + dynamic_filter = false + cell_type_assignments = false + majority_vote = false + + compute_polarization = true + compute_colocalization = false + use_full_bipartite = true } diff --git a/docs/output.md b/docs/output.md index 454a4c35..6091030d 100644 --- a/docs/output.md +++ b/docs/output.md @@ -1,57 +1,168 @@ -# nf-core/pixelator: Output +# PixelgenTechnologies/nf-core-pixelator: Output ## Introduction -This document describes the output produced by the pipeline. Most of the plots are taken from the MultiQC report, which summarises results at the end of the pipeline. - +This document describes the output produced by the pipeline. The directories listed below will be created in the results directory after the pipeline has finished. All paths are relative to the top-level results directory. ## Pipeline overview -The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes data using the following steps: +The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes data using multiple subcommands of the [`pixelator`](https://github.com/PixelgenTechnologies/pixelator) tool. -- [FastQC](#fastqc) - Raw read QC -- [MultiQC](#multiqc) - Aggregate report describing results and QC from the whole pipeline -- [Pipeline information](#pipeline-information) - Report metrics generated during the workflow execution +- [`pixelator concatenate`](#pixelator-concatenate)(Optional) - Concatenate paired end data +- [`pixelator preqc`](#pixelator-preqc)) - Read QC and filtering +- [`pixelator adapterqc`](#pixelator-adapterqc)) - Check correctness/presence of PBS1/2 sequences +- [`pixelator demux`](#pixelator-demux)) - Assign a marker (barcode) to each read +- [`pixelator collapse`](#pixelator-collapse)) - Error correction, duplicate removal, compute read counts +- [`pixelator cluster`](#pixelator-cluster)) - Compute undirected graphs and basic size filtering +- [`pixelator analysis`](#pixelator-analysis)) - Downstream analysis for each cell +- [`pixelator annotate`](#pixelator-annotate)) - Filter, annotate and call cells on samples +- [`pixelator aggregate`](#pixelator-aggregate)) - Aggregate results +- [`pixelator report`](#pixelator-report)) - Report generation -### FastQC +### pixelator concatenate
Output files -- `fastqc/` - - `*_fastqc.html`: FastQC report containing quality metrics. - - `*_fastqc.zip`: Zip archive containing the FastQC report, tab-delimited data file and plot images. +- `pixelator` + - `concatenate` + - `*merged.fastq.gz`: Concatenated R1 and R2 reads. + - `/logs` - `*pixelator-concatenate.log`: Pixelator concatenate log output. +
- +### pixelator preqc + +
+Output files + +- `pixelator` + - `preqc` + - `*processed.fastq.gz`: Processed reads. + - `*failed.fastq.gz`: Discarded reads. + - `*report.html`: Fastp html report. + - `*report.json`: Fastp json report. + - `/logs` - `*pixelator-preqc.log`: Pixelator preqc log output. +
+ +### pixelator adapterqc + +
+Output files + +- `pixelator` + - `adapterqc` + - `*processed.fastq.gz`: Processed reads. + - `*failed.fastq.gz`: Discarded reads. + - `*report.json`: Cutadapt json report. + - `/logs` - `*pixelator-adapterqc.log`: Pixelator adapterqc log output. +
+ +### pixelator demux + +
+Output files + +- `pixelator` + - `adapterqc` + - `*processed-*-.fastq.gz`: Reads demultiplexed per antibody. + - `*report.json`: Cutadapt json report. + - `/logs` - `*pixelator-demultiplex.log`: Pixelator adapterqc log output. +
-[FastQC](http://www.bioinformatics.babraham.ac.uk/projects/fastqc/) gives general quality metrics about your sequenced reads. It provides information about the quality score distribution across your reads, per base sequence content (%A/T/G/C), adapter contamination and overrepresented sequences. For further reading and documentation see the [FastQC help pages](http://www.bioinformatics.babraham.ac.uk/projects/fastqc/Help/). +### pixelator collapse -![MultiQC - FastQC sequence counts plot](images/mqc_fastqc_counts.png) +
+Output files + +- `pixelator` + - `adapterqc` + - `*.collapse.csv`: Edge list matrix. + - `*collapse.json`: Statistics. + - `/logs` - `*pixelator-collapse.log`: Pixelator collapse log output. +
+ +### pixelator cluster -![MultiQC - FastQC mean quality scores plot](images/mqc_fastqc_quality.png) +
+Output files -![MultiQC - FastQC adapter content plot](images/mqc_fastqc_adapter.png) +- `pixelator` + - `cluster` + - `.components_recovered.csv` + - `.data_summary.png` + - `.raw_anndata.h5ad` + - `.raw_antibody_metrics.csv` + - `.raw_antibody_metrics.png` + - `.raw_components_antibody.csv` + - `.raw_components_dist.png` + - `.raw_components_metrics.csv` + - `.raw_pixel_data.csv` + - `.report.json` + - `/logs` - `*pixelator-cluster.log`: Pixelator cluster log output. +
+ +### pixelator annotate -> **NB:** The FastQC plots displayed in the MultiQC report shows _untrimmed_ reads. They may contain adapter sequence and potentially regions with low quality. +
+Output files -### MultiQC +- `pixelator` + - `annotate` + - `.data_summary.png` + - `.anndata.h5ad` + - `.raw_anndata.h5ad` + - `.antibody_metrics.csv` + - `.antibody_metrics.png` + - `.components_antibody.csv` + - `.components_dist.png` + - `.components_metrics.csv` + - `.pixel_data.csv` + - `.report.json` + - `/logs` - `*pixelator-annotate.log`: Pixelator cluster log output. +
+ +### pixelator analysis
Output files -- `multiqc/` - - `multiqc_report.html`: a standalone HTML file that can be viewed in your web browser. - - `multiqc_data/`: directory containing parsed statistics from the different tools used in the pipeline. - - `multiqc_plots/`: directory containing static images from the report in various formats. +- `pixelator` + - `analysis` + - `.anndata.h5ad` + - `.polarization_boxplot.png` + - `.polarization_heatmap.png` + - `.polarization_matrix.csv` + - `.polarization_scores.csv` + - `.polarization_matrix.csv` + - `.report.json` + - `/logs` - `*pixelator-analysis.log`: Pixelator analysis log output.
-[MultiQC](http://multiqc.info) is a visualization tool that generates a single HTML report summarising all samples in your project. Most of the pipeline QC results are visualised in the report and further statistics are available in the report data directory. +### pixelator aggregate + +
+Output files + +- `pixelator` + - `aggregate` + - `.merged_anndata.h5ad`: Anndata object with aggregated data of multiple samples + - `/logs` - `*pixelator-report.log`: Pixelator report log output. +
+ +### pixelator report + +
+Output files -Results generated by MultiQC collate pipeline QC from supported tools e.g. FastQC. The pipeline has special steps which also allow the software versions to be reported in the MultiQC output for future traceability. For more information about how to use MultiQC reports, see . +- `pixelator` + - `report/` + - `report.html`: + - `/logs` - `*pixelator-report.log`: Pixelator report log output. +
### Pipeline information @@ -62,6 +173,7 @@ Results generated by MultiQC collate pipeline QC from supported tools e.g. FastQ - Reports generated by Nextflow: `execution_report.html`, `execution_timeline.html`, `execution_trace.txt` and `pipeline_dag.dot`/`pipeline_dag.svg`. - Reports generated by the pipeline: `pipeline_report.html`, `pipeline_report.txt` and `software_versions.yml`. The `pipeline_report*` files will only be present if the `--email` / `--email_on_fail` parameter's are used when running the pipeline. - Reformatted samplesheet files used as input to the pipeline: `samplesheet.valid.csv`. + - Metadata file with software versions, environment information and pipeline configuration for debugging: 'metadata.json' diff --git a/docs/usage.md b/docs/usage.md index b167116f..0af47078 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -214,3 +214,219 @@ We recommend adding the following line to your environment to limit this (typica ```bash NXF_OPTS='-Xms1g -Xmx4g' ``` +# nf-core/pixelator: Usage + +## :warning: Please read this documentation on the nf-core website: [https://nf-co.re/pixelator/usage](https://nf-co.re/pixelator/usage) + +> _Documentation of pipeline parameters is generated automatically from the pipeline schema and can no longer be found in markdown files._ + +## Introduction + + + +## Samplesheet input + +You will need to create a samplesheet with information about the samples you would like to analyse before running the pipeline. Use this parameter to specify its location. It has to be a comma-separated file with 3 columns, and a header row as shown in the examples below. + +```bash +--input '[path to samplesheet file]' +``` + +### Multiple runs of the same sample + +The `sample` identifiers have to be the same when you have re-sequenced the same sample more than once e.g. to increase sequencing depth. The pipeline will concatenate the raw reads before performing any downstream analysis. Below is an example for the same sample sequenced across 3 lanes: + +```console +sample,fastq_1,fastq_2 +CONTROL_REP1,AEG588A1_S1_L002_R1_001.fastq.gz,AEG588A1_S1_L002_R2_001.fastq.gz +CONTROL_REP1,AEG588A1_S1_L003_R1_001.fastq.gz,AEG588A1_S1_L003_R2_001.fastq.gz +CONTROL_REP1,AEG588A1_S1_L004_R1_001.fastq.gz,AEG588A1_S1_L004_R2_001.fastq.gz +``` + +### Full samplesheet + +The pipeline will auto-detect whether a sample is single- or paired-end using the information provided in the samplesheet. The samplesheet can have as many columns as you desire, however, there is a strict requirement for the first 3 columns to match those defined in the table below. + +A final samplesheet file consisting of both single- and paired-end data may look something like the one below. This is for 6 samples, where `TREATMENT_REP3` has been sequenced twice. + +```console +sample,fastq_1,fastq_2 +CONTROL_REP1,AEG588A1_S1_L002_R1_001.fastq.gz,AEG588A1_S1_L002_R2_001.fastq.gz +CONTROL_REP2,AEG588A2_S2_L002_R1_001.fastq.gz,AEG588A2_S2_L002_R2_001.fastq.gz +CONTROL_REP3,AEG588A3_S3_L002_R1_001.fastq.gz,AEG588A3_S3_L002_R2_001.fastq.gz +TREATMENT_REP1,AEG588A4_S4_L003_R1_001.fastq.gz, +TREATMENT_REP2,AEG588A5_S5_L003_R1_001.fastq.gz, +TREATMENT_REP3,AEG588A6_S6_L003_R1_001.fastq.gz, +TREATMENT_REP3,AEG588A6_S6_L004_R1_001.fastq.gz, +``` + +| Column | Description | +| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `sample` | Custom sample name. This entry will be identical for multiple sequencing libraries/runs from the same sample. Spaces in sample names are automatically converted to underscores (`_`). | +| `fastq_1` | Full path to FastQ file for Illumina short reads 1. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | +| `fastq_2` | Full path to FastQ file for Illumina short reads 2. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | + +An [example samplesheet](../assets/samplesheet.csv) has been provided with the pipeline. + +## Running the pipeline + +The typical command for running the pipeline is as follows: + +```bash +nextflow run nf-core/pixelator --input samplesheet.csv --outdir --genome GRCh37 -profile docker +``` + +This will launch the pipeline with the `docker` configuration profile. See below for more information about profiles. + +Note that the pipeline will create the following files in your working directory: + +```bash +work # Directory containing the nextflow working files + # Finished results in specified location (defined with --outdir) +.nextflow_log # Log file from Nextflow +# Other nextflow hidden files, eg. history of pipeline runs and old logs. +``` + +If you wish to repeatedly use the same parameters for multiple runs, rather than specifying each flag in the command, you can specify these in a params file. + +Pipeline settings can be provided in a `yaml` or `json` file via `-params-file `. + +> ⚠️ Do not use `-c ` to specify parameters as this will result in errors. Custom config files specified with `-c` must only be used for [tuning process resource specifications](https://nf-co.re/docs/usage/configuration#tuning-workflow-resources), other infrastructural tweaks (such as output directories), or module arguments (args). +> The above pipeline run specified with a params file in yaml format: + +```bash +nextflow run nf-core/pixelator -profile docker -params-file params.yaml +``` + +with `params.yaml` containing: + +```yaml +input: './samplesheet.csv' +outdir: './results/' +genome: 'GRCh37' +input: 'data' +<...> +``` + +You can also generate such `YAML`/`JSON` files via [nf-core/launch](https://nf-co.re/launch). + +### Updating the pipeline + +When you run the above command, Nextflow automatically pulls the pipeline code from GitHub and stores it as a cached version. When running the pipeline after this, it will always use the cached version if available - even if the pipeline has been updated since. To make sure that you're running the latest version of the pipeline, make sure that you regularly update the cached version of the pipeline: + +```bash +nextflow pull nf-core/pixelator +``` + +### Reproducibility + +It is a good idea to specify a pipeline version when running the pipeline on your data. This ensures that a specific version of the pipeline code and software are used when you run your pipeline. If you keep using the same tag, you'll be running the same version of the pipeline, even if there have been changes to the code since. + +First, go to the [nf-core/pixelator releases page](https://github.com/nf-core/pixelator/releases) and find the latest pipeline version - numeric only (eg. `1.3.1`). Then specify this when running the pipeline with `-r` (one hyphen) - eg. `-r 1.3.1`. Of course, you can switch to another version by changing the number after the `-r` flag. + +This version number will be logged in reports when you run the pipeline, so that you'll know what you used when you look back in the future. For example, at the bottom of the MultiQC reports. + +To further assist in reproducbility, you can use share and re-use [parameter files](#running-the-pipeline) to repeat pipeline runs with the same settings without having to write out a command with every single parameter. + +> 💡 If you wish to share such profile (such as upload as supplementary material for academic publications), make sure to NOT include cluster specific paths to files, nor institutional specific profiles. + +## Core Nextflow arguments + +> **NB:** These options are part of Nextflow and use a _single_ hyphen (pipeline parameters use a double-hyphen). + +### `-profile` + +Use this parameter to choose a configuration profile. Profiles can give configuration presets for different compute environments. + +Several generic profiles are bundled with the pipeline which instruct the pipeline to use software packaged using different methods (Docker, Singularity, Podman, Shifter, Charliecloud, Apptainer, Conda) - see below. + +> We highly recommend the use of Docker or Singularity containers for full pipeline reproducibility, however when this is not possible, Conda is also supported. + +The pipeline also dynamically loads configurations from [https://github.com/nf-core/configs](https://github.com/nf-core/configs) when it runs, making multiple config profiles for various institutional clusters available at run time. For more information and to see if your system is available in these configs please see the [nf-core/configs documentation](https://github.com/nf-core/configs#documentation). + +Note that multiple profiles can be loaded, for example: `-profile test,docker` - the order of arguments is important! +They are loaded in sequence, so later profiles can overwrite earlier profiles. + +If `-profile` is not specified, the pipeline will run locally and expect all software to be installed and available on the `PATH`. This is _not_ recommended, since it can lead to different results on different machines dependent on the computer enviroment. + +- `test` + - A profile with a complete configuration for automated testing + - Includes links to test data so needs no other parameters +- `docker` + - A generic configuration profile to be used with [Docker](https://docker.com/) +- `singularity` + - A generic configuration profile to be used with [Singularity](https://sylabs.io/docs/) +- `podman` + - A generic configuration profile to be used with [Podman](https://podman.io/) +- `shifter` + - A generic configuration profile to be used with [Shifter](https://nersc.gitlab.io/development/shifter/how-to-use/) +- `charliecloud` + - A generic configuration profile to be used with [Charliecloud](https://hpc.github.io/charliecloud/) +- `apptainer` + - A generic configuration profile to be used with [Apptainer](https://apptainer.org/) +- `conda` + - A generic configuration profile to be used with [Conda](https://conda.io/docs/). Please only use Conda as a last resort i.e. when it's not possible to run the pipeline with Docker, Singularity, Podman, Shifter, Charliecloud, or Apptainer. + +### `-resume` + +Specify this when restarting a pipeline. Nextflow will use cached results from any pipeline steps where the inputs are the same, continuing from where it got to previously. For input to be considered the same, not only the names must be identical but the files' contents as well. For more info about this parameter, see [this blog post](https://www.nextflow.io/blog/2019/demystifying-nextflow-resume.html). + +You can also supply a run name to resume a specific run: `-resume [run-name]`. Use the `nextflow log` command to show previous run names. + +### `-c` + +Specify the path to a specific config file (this is a core Nextflow command). See the [nf-core website documentation](https://nf-co.re/usage/configuration) for more information. + +## Custom configuration + +### Resource requests + +Whilst the default requirements set within the pipeline will hopefully work for most people and with most input data, you may find that you want to customise the compute resources that the pipeline requests. Each step in the pipeline has a default set of requirements for number of CPUs, memory and time. For most of the steps in the pipeline, if the job exits with any of the error codes specified [here](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/conf/base.config#L18) it will automatically be resubmitted with higher requests (2 x original, then 3 x original). If it still fails after the third attempt then the pipeline execution is stopped. + +To change the resource requests, please see the [max resources](https://nf-co.re/docs/usage/configuration#max-resources) and [tuning workflow resources](https://nf-co.re/docs/usage/configuration#tuning-workflow-resources) section of the nf-core website. + +### Custom Containers + +In some cases you may wish to change which container or conda environment a step of the pipeline uses for a particular tool. By default nf-core pipelines use containers and software from the [biocontainers](https://biocontainers.pro/) or [bioconda](https://bioconda.github.io/) projects. However in some cases the pipeline specified version maybe out of date. + +To use a different container from the default container or conda environment specified in a pipeline, please see the [updating tool versions](https://nf-co.re/docs/usage/configuration#updating-tool-versions) section of the nf-core website. + +### Custom Tool Arguments + +A pipeline might not always support every possible argument or option of a particular tool used in pipeline. Fortunately, nf-core pipelines provide some freedom to users to insert additional parameters that the pipeline does not include by default. + +To learn how to provide additional arguments to a particular tool of the pipeline, please see the [customising tool arguments](https://nf-co.re/docs/usage/configuration#customising-tool-arguments) section of the nf-core website. + +### nf-core/configs + +In most cases, you will only need to create a custom config as a one-off but if you and others within your organisation are likely to be running nf-core pipelines regularly and need to use the same settings regularly it may be a good idea to request that your custom config file is uploaded to the `nf-core/configs` git repository. Before you do this please can you test that the config file works with your pipeline of choice using the `-c` parameter. You can then create a pull request to the `nf-core/configs` repository with the addition of your config file, associated documentation file (see examples in [`nf-core/configs/docs`](https://github.com/nf-core/configs/tree/master/docs)), and amending [`nfcore_custom.config`](https://github.com/nf-core/configs/blob/master/nfcore_custom.config) to include your custom profile. + +See the main [Nextflow documentation](https://www.nextflow.io/docs/latest/config.html) for more information about creating your own configuration files. + +If you have any questions or issues please send us a message on [Slack](https://nf-co.re/join/slack) on the [`#configs` channel](https://nfcore.slack.com/channels/configs). + +## Azure Resource Requests + +To be used with the `azurebatch` profile by specifying the `-profile azurebatch`. +We recommend providing a compute `params.vm_type` of `Standard_D16_v3` VMs by default but these options can be changed if required. + +Note that the choice of VM size depends on your quota and the overall workload during the analysis. +For a thorough list, please refer the [Azure Sizes for virtual machines in Azure](https://docs.microsoft.com/en-us/azure/virtual-machines/sizes). + +## Running in the background + +Nextflow handles job submissions and supervises the running jobs. The Nextflow process must run until the pipeline is finished. + +The Nextflow `-bg` flag launches Nextflow in the background, detached from your terminal so that the workflow does not stop if you log out of your session. The logs are saved to a file. + +Alternatively, you can use `screen` / `tmux` or similar tool to create a detached session which you can log back into at a later time. +Some HPC setups also allow you to run nextflow within a cluster job submitted your job scheduler (from where it submits more jobs). + +## Nextflow memory requirements + +In some cases, the Nextflow Java virtual machines can start to request a large amount of memory. +We recommend adding the following line to your environment to limit this (typically in `~/.bashrc` or `~./bash_profile`): + +```bash +NXF_OPTS='-Xms1g -Xmx4g' +``` diff --git a/lib/WorkflowPixelator.groovy b/lib/WorkflowPixelator.groovy index de998ec6..2e576ecb 100755 --- a/lib/WorkflowPixelator.groovy +++ b/lib/WorkflowPixelator.groovy @@ -11,12 +11,6 @@ class WorkflowPixelator { // Check and validate parameters // public static void initialise(params, log) { - genomeExistsError(params, log) - - - if (!params.fasta) { - Nextflow.error "Genome fasta file not specified with e.g. '--fasta genome.fa' or via a detectable config file." - } } // diff --git a/main.nf b/main.nf index b4333fe6..fe304f59 100644 --- a/main.nf +++ b/main.nf @@ -17,7 +17,6 @@ nextflow.enable.dsl = 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -params.fasta = WorkflowMain.getGenomeAttribute(params, 'fasta') /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/modules.json b/modules.json index 9f4ac028..0b9c23d6 100644 --- a/modules.json +++ b/modules.json @@ -5,19 +5,14 @@ "https://github.com/nf-core/modules.git": { "modules": { "nf-core": { - "custom/dumpsoftwareversions": { + "cat/fastq": { "branch": "master", - "git_sha": "76cc4938c1f6ea5c7d83fed1eeffc146787f9543", + "git_sha": "5c460c5a4736974abde2843294f35307ee2b0e5e", "installed_by": ["modules"] }, - "fastqc": { - "branch": "master", - "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", - "installed_by": ["modules"] - }, - "multiqc": { + "custom/dumpsoftwareversions": { "branch": "master", - "git_sha": "f2d63bd5b68925f98f572eed70993d205cc694b7", + "git_sha": "911696ea0b62df80e900ef244d7867d177971f73", "installed_by": ["modules"] } } diff --git a/modules/local/collect_metadata.nf b/modules/local/collect_metadata.nf new file mode 100644 index 00000000..875c36c6 --- /dev/null +++ b/modules/local/collect_metadata.nf @@ -0,0 +1,82 @@ +import org.json.JSONObject +import org.json.JSONTokener +import org.json.JSONArray +import groovy.json.JsonSlurper +import groovy.json.JsonBuilder + +process COLLECT_METADATA { + + label "process_single" + cache false + + conda "local::pixelator=0.10.0" + container 'ghcr.io/pixelgentechnologies/pixelator:0.10.0' + + input: + + output: + path "metadata.json", emit: metadata + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + + Map nextflow_dict = [ + version: workflow.nextflow.version, + build: workflow.nextflow.build, + timestamp: workflow.nextflow.timestamp?.toString(), + ] + Map manifest_dict = [ + author: workflow.manifest.getAuthor(), + defaultBranch: workflow.manifest.getDefaultBranch(), + description: workflow.manifest.getDescription(), + homePage: workflow.manifest.getHomePage(), + gitmodules: workflow.manifest.getGitmodules(), + mainScript: workflow.manifest.getMainScript(), + version: workflow.manifest.getVersion(), + nextflowVersion: workflow.manifest.getNextflowVersion(), + doi: workflow.manifest.getDoi(), + ] + + Map workflow_dict = [ + scriptId: workflow.scriptId, + scriptName: workflow.scriptName, + scriptFile: workflow.scriptFile.toString(), + repository: workflow.repository, + commitId: workflow.commitId, + revision: workflow.revision, + projectDir: workflow.projectDir.toString(), + launchDir: workflow.launchDir.toString(), + workDir: workflow.workDir.toString(), + homeDir: workflow.homeDir.toString(), + userName: workflow.userName, + configFiles: workflow.configFiles.collect { it.toString() }, + container: workflow.container.collectEntries { [it.key, it.value?.toString()] }, + containerEngine: workflow.containerEngine, + commandLine: workflow.commandLine, + profile: workflow.profile, + runName: workflow.runName, + sessionId: workflow.sessionId, + resume: workflow.resume, + stubRun: workflow.stubRun, + start: workflow.start?.toString(), + ] + + def metadata = [ + nextflow: nextflow_dict, + manifest: manifest_dict, + workflow : workflow_dict, + parameters: params + ] + + def builder = new JsonBuilder(metadata) + def nextflowJson = builder.toPrettyString() + + """ + echo '${nextflowJson}' > nextflow-metadata.json + collect_metadata.py --process-name ${task.process} --workflow-data "nextflow-metadata.json" + """ + +} diff --git a/modules/local/pixelator/analysis/main.nf b/modules/local/pixelator/analysis/main.nf new file mode 100644 index 00000000..ea8b3d74 --- /dev/null +++ b/modules/local/pixelator/analysis/main.nf @@ -0,0 +1,45 @@ + +process PIXELATOR_ANALYSIS { + tag "$meta.id" + label 'process_medium' + + conda "local::pixelator=0.10.0" + container "ghcr.io/pixelgentechnologies/pixelator:0.10.0" + + input: + tuple val(meta), path(h5ad) + + output: + tuple val(meta), path("analysis/*dataset.pxl"), emit: dataset + tuple val(meta), path("analysis/*report.json"), emit: report_json + tuple val(meta), path("analysis/*.meta.json"), emit: metadata + tuple val(meta), path("analysis/*"), emit: all_results + tuple val(meta), path("*pixelator-analysis.log"), emit: log + + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + + prefix = task.ext.prefix ?: "${meta.id}" + def args = task.ext.args ?: '' + + """ + pixelator \\ + --cores $task.cpus \\ + --log-file ${prefix}.pixelator-analysis.log \\ + --verbose \\ + single-cell \\ + analysis \\ + --output . \\ + $args \\ + $h5ad + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + pixelator: \$(echo \$(pixelator --version 2>/dev/null) | sed 's/pixelator, version //g' ) + END_VERSIONS + """ +} diff --git a/modules/local/pixelator/analysis/meta.yaml b/modules/local/pixelator/analysis/meta.yaml new file mode 100644 index 00000000..7cadf9c8 --- /dev/null +++ b/modules/local/pixelator/analysis/meta.yaml @@ -0,0 +1,59 @@ +name: pixelator/analysis +description: | + Perform different analyses on a PixelDataset from pixelator annotate +keywords: + - "molecular pixelator" +tools: + - pixelator: + description: | + Software package to process sequencing data into Molecular Pixelation data. + homepage: https://github.com/PixelgenTechnologies/pixelator + documentation: https://github.com/PixelgenTechnologies/pixelator + arxiv: null + licence: ["MIT"] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: | + List of input FastQ files of size 1 and 2 for single-end and paired-end data, + respectively. + +output: + - dataset: + type: file + description: Pixelator dataset file + pattern: "*.dataset.pxl" + + - report_json: + type: file + description: JSON file with statistics for the cluster stage + pattern: "*.report.json" + + - metadata: + type: file + description: Command invocation metadata files for pixelator collapse + pattern: "*.meta.json" + + - all_results: + type: file + description: All output files from the pixelator cluster Command + pattern: "*" + + - log: + type: file + description: Pixelator log files + pattern: "*pixelator-analysis*.log" + + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@fbdtemme" diff --git a/modules/local/pixelator/annotate/main.nf b/modules/local/pixelator/annotate/main.nf new file mode 100644 index 00000000..ba74f704 --- /dev/null +++ b/modules/local/pixelator/annotate/main.nf @@ -0,0 +1,48 @@ + +process PIXELATOR_ANNOTATE { + tag "$meta.id" + label 'process_medium' + + conda "local::pixelator=0.10.0" + container "ghcr.io/pixelgentechnologies/pixelator:0.10.0" + + input: + tuple val(meta), path(dataset), path(panel) + + output: + tuple val(meta), path("annotate/*.dataset.pxl"), emit: dataset + tuple val(meta), path("annotate/*.report.json"), emit: report_json + tuple val(meta), path("annotate/*.png"), emit: png + tuple val(meta), path("annotate/*.meta.json"), emit: metadata + tuple val(meta), path("annotate/*"), emit: all_results + tuple val(meta), path("*pixelator-annotate.log"), emit: log + + + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + + prefix = task.ext.prefix ?: "${meta.id}" + def args = task.ext.args ?: '' + + """ + pixelator \\ + --cores $task.cpus \\ + --log-file ${prefix}.pixelator-annotate.log \\ + --verbose \\ + single-cell \\ + annotate \\ + --output . \\ + $args \\ + $dataset \\ + --panel-file $panel + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + pixelator: \$(echo \$(pixelator --version 2>/dev/null) | sed 's/pixelator, version //g' ) + END_VERSIONS + """ +} diff --git a/modules/local/pixelator/annotate/meta.yaml b/modules/local/pixelator/annotate/meta.yaml new file mode 100644 index 00000000..1b4c44d1 --- /dev/null +++ b/modules/local/pixelator/annotate/meta.yaml @@ -0,0 +1,64 @@ +name: pixelator/annotate +description: | + Filter, annotate and call cells from an edge list produced by pixelator cluster +keywords: + - "molecular pixelator" +tools: + - pixelator: + description: | + Software package to process sequencing data into Molecular Pixelation data. + homepage: https://github.com/PixelgenTechnologies/pixelator + documentation: https://github.com/PixelgenTechnologies/pixelator + arxiv: null + licence: ["MIT"] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: | + List of input FastQ files of size 1 and 2 for single-end and paired-end data, + respectively. + +output: + - dataset: + type: file + description: Pixelator dataset file + pattern: "*.dataset.pxl" + + - report_json: + type: file + description: JSON file with statistics for the cluster stage + pattern: "*.report.json" + + - png: + type: file + description: PNG plots + pattern: "*.png" + + - metadata: + type: file + description: Command invocation metadata files for pixelator collapse + pattern: "*.meta.json" + + - all_results: + type: file + description: All output files from the pixelator cluster Command + pattern: "*" + + - log: + type: file + description: Pixelator log files + pattern: "*pixelator-annotate*.log" + + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@fbdtemme" diff --git a/modules/local/pixelator/collapse/main.nf b/modules/local/pixelator/collapse/main.nf new file mode 100644 index 00000000..511d8746 --- /dev/null +++ b/modules/local/pixelator/collapse/main.nf @@ -0,0 +1,51 @@ + + +process PIXELATOR_COLLAPSE { + tag "$meta.id" + label 'process_medium' + + + conda "local::pixelator=0.10.0" + + container "ghcr.io/pixelgentechnologies/pixelator:0.10.0" + + input: + tuple val(meta), path(reads) + + output: + tuple val(meta), path("collapse/*.collapsed.csv.gz"), emit: collapsed + tuple val(meta), path("collapse/*.report.json"), emit: report_json + tuple val(meta), path("collapse/*.meta.json"), emit: metadata + tuple val(meta), path("*pixelator-collapse.log"), emit: log + + + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + assert meta.design != null + + prefix = task.ext.prefix ?: "${meta.id}" + def args = task.ext.args ?: '' + def readsArg = reads.join(' ') + + """ + pixelator \\ + --cores $task.cpus \\ + --log-file ${prefix}.pixelator-collapse.log \\ + --verbose \\ + single-cell \\ + collapse \\ + --output . \\ + --design ${meta.design} \\ + $args \\ + ${readsArg} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + pixelator: \$(echo \$(pixelator --version 2>/dev/null) | sed 's/pixelator, version //g' ) + END_VERSIONS + """ +} diff --git a/modules/local/pixelator/collapse/meta.yaml b/modules/local/pixelator/collapse/meta.yaml new file mode 100644 index 00000000..b5c5ac03 --- /dev/null +++ b/modules/local/pixelator/collapse/meta.yaml @@ -0,0 +1,55 @@ +name: pixelator/collapse +description: | + Collapse Molecular Pixelation data (FASTQ) by umi-upi to remove duplicates + and perform error correction. +keywords: + - "molecular pixelator" +tools: + - pixelator: + description: | + Software package to process sequencing data into Molecular Pixelation data. + homepage: https://github.com/PixelgenTechnologies/pixelator + documentation: https://github.com/PixelgenTechnologies/pixelator + arxiv: null + licence: ["MIT"] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: | + List of input FastQ files of size 1 and 2 for single-end and paired-end data, + respectively. + +output: + - collapsed: + type: file + description: FastQ file containing an edge-list of collapsed reads + pattern: "*collapsed.csv.gz" + + - report_json: + type: file + description: JSON file with statistics for the collapse stage + pattern: "*.report.json" + + - metadata: + type: file + description: Command invocation metadata files for pixelator collapse + pattern: "*.meta.json" + + - log: + type: file + description: Pixelator log files + pattern: "*pixelator-collapse*.log" + + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@fbdtemme" diff --git a/modules/local/pixelator/concatenate/main.nf b/modules/local/pixelator/concatenate/main.nf new file mode 100644 index 00000000..3edbb285 --- /dev/null +++ b/modules/local/pixelator/concatenate/main.nf @@ -0,0 +1,46 @@ + + +process PIXELATOR_CONCATENATE { + tag "$meta.id" + label 'process_medium' + + + conda "local::pixelator=0.10.0" + + container "ghcr.io/pixelgentechnologies/pixelator:0.10.0" + + input: + tuple val(meta), path(reads) + + output: + tuple val(meta), path("concatenate/*.merged.{fq,fastq}.gz"), emit: merged + tuple val(meta), path("concatenate/*.report.json"), emit: report_json + tuple val(meta), path("concatenate/*.meta.json"), emit: metadata + tuple val(meta), path("*pixelator-concatenate.log"), emit: log + + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def prefix = task.ext.prefix ?: "${meta.id}" + def args = task.ext.args ?: '' + + """ + pixelator \\ + --cores $task.cpus \\ + --log-file ${prefix}.pixelator-concatenate.log \\ + --verbose \\ + single-cell \\ + concatenate \\ + --output . \\ + $args \\ + ${reads} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + pixelator: \$(echo \$(pixelator --version 2>/dev/null) | sed 's/pixelator, version //g' ) + END_VERSIONS + """ +} diff --git a/modules/local/pixelator/concatenate/meta.yaml b/modules/local/pixelator/concatenate/meta.yaml new file mode 100644 index 00000000..6db8f65f --- /dev/null +++ b/modules/local/pixelator/concatenate/meta.yaml @@ -0,0 +1,53 @@ +name: pixelator/concatenate +description: Demultiplex molecular pixelation reads according to the panel file barcodes +keywords: + - "molecular pixelator" +tools: + - pixelator: + description: | + Software package to process sequencing data into Molecular Pixelation data. + homepage: https://github.com/PixelgenTechnologies/pixelator + documentation: https://github.com/PixelgenTechnologies/pixelator + arxiv: null + licence: ["MIT"] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: | + List of input FastQ files of size 1 and 2 for single-end and paired-end data, + respectively. + +output: + - merged: + type: file + description: FastQ file containing combined and trimmed reads + pattern: "*merged*.{fq,fastq}.gz" + + - report_json: + type: file + description: JSON file with statistics for the concatenate stage + pattern: "*.report.json" + + - metadata: + type: file + description: Command invocation metadata files for pixelator concatenate + pattern: "*.meta.json" + + - log: + type: file + description: Pixelator log files + pattern: "*pixelator-concatenate*.log" + + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@fbdtemme" diff --git a/modules/local/pixelator/demux/main.nf b/modules/local/pixelator/demux/main.nf new file mode 100644 index 00000000..fe3df319 --- /dev/null +++ b/modules/local/pixelator/demux/main.nf @@ -0,0 +1,49 @@ + + +process PIXELATOR_DEMUX { + tag "$meta.id" + label 'process_medium' + + conda "local::pixelator=0.10.0" + + container "ghcr.io/pixelgentechnologies/pixelator:0.10.0" + + input: + tuple val(meta), path(reads), path(antibody_panel) + + output: + tuple val(meta), path("demux/*processed*.{fq,fastq}.gz"), emit: processed + tuple val(meta), path("demux/*failed.{fq,fastq}.gz"), emit: failed + tuple val(meta), path("demux/*.report.json"), emit: report_json + tuple val(meta), path("demux/*.meta.json"), emit: metadata + tuple val(meta), path("*pixelator-demux.log"), emit: log + + + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + // --design is passed in meta and added to args through modules.conf + + prefix = task.ext.prefix ?: "${meta.id}" + def args = task.ext.args ?: '' + """ + pixelator \\ + --cores $task.cpus \\ + --log-file ${prefix}.pixelator-demux.log \\ + --verbose \\ + single-cell \\ + demux \\ + --output . \\ + --panel-file ${antibody_panel} \\ + $args \\ + ${reads} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + pixelator: \$(echo \$(pixelator --version 2>/dev/null) | sed 's/pixelator, version //g' ) + END_VERSIONS + """ +} diff --git a/modules/local/pixelator/demux/meta.yaml b/modules/local/pixelator/demux/meta.yaml new file mode 100644 index 00000000..2f150d01 --- /dev/null +++ b/modules/local/pixelator/demux/meta.yaml @@ -0,0 +1,62 @@ +name: pixelator/demux +description: Demultiplex molecular pixelation reads according to the panel file barcodes +keywords: + - "molecular pixelator" + - demultiplexing + - +tools: + - pixelator: + description: | + Software package to process sequencing data into Molecular Pixelation data. + homepage: https://github.com/PixelgenTechnologies/pixelator + documentation: https://github.com/PixelgenTechnologies/pixelator + arxiv: null + licence: ["MIT"] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: | + FastQ file containing the molecular pixelator amplicon. + - antibody_panel: + type: file + description: | + Panel file used by the molecular pixelator kit. + +output: + - processed: + type: file + description: All demultiplexed FastQ files + pattern: "*processed*.{fq,fastq}.gz" + + - failed: + type: file + description: FastQ file containing reads with invalid barcodes + pattern: "*failed.{fq,fastq}.gz" + + - report_json: + type: file + description: JSON file with statistics for the demux stage + pattern: "*.report.json" + + - metadata: + type: file + description: Command invocation metadata files for pixelator demux + pattern: "*.meta.json" + + - log: + type: file + description: Pixelator log files + pattern: "*pixelator-demux*.log" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@fbdtemme" diff --git a/modules/local/pixelator/graph/main.nf b/modules/local/pixelator/graph/main.nf new file mode 100644 index 00000000..0b1d9b08 --- /dev/null +++ b/modules/local/pixelator/graph/main.nf @@ -0,0 +1,49 @@ + + +process PIXELATOR_GRAPH { + tag "$meta.id" + label 'process_medium' + + conda "local::pixelator=0.10.0" + + container "ghcr.io/pixelgentechnologies/pixelator:0.10.0" + + input: + tuple val(meta), path(edge_list) + + output: + tuple val(meta), path("graph/*.edgelist.csv.gz"), emit: edgelist + tuple val(meta), path("graph/*.raw_edgelist.csv.gz"), emit: raw_edgelist + tuple val(meta), path("graph/*.components_recovered.csv"), emit: components_recovered, optional: true + tuple val(meta), path("graph/*.report.json"), emit: report_json + tuple val(meta), path("graph/*.meta.json"), emit: input_params + tuple val(meta), path("graph/*"), emit: all_results + tuple val(meta), path("*pixelator-graph.log"), emit: log + + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + + prefix = task.ext.prefix ?: "${meta.id}" + def args = task.ext.args ?: '' + + """ + pixelator \\ + --cores $task.cpus \\ + --log-file ${prefix}.pixelator-graph.log \\ + --verbose \\ + single-cell \\ + graph \\ + --output . \\ + $args \\ + ${edge_list} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + pixelator: \$(echo \$(pixelator --version 2>/dev/null) | sed 's/pixelator, version //g' ) + END_VERSIONS + """ +} diff --git a/modules/local/pixelator/graph/meta.yaml b/modules/local/pixelator/graph/meta.yaml new file mode 100644 index 00000000..500df785 --- /dev/null +++ b/modules/local/pixelator/graph/meta.yaml @@ -0,0 +1,66 @@ +name: pixelator/graph +description: | + Compute graph, components and other metrics from a edge list + produced by pixelator collapse +keywords: + - "molecular pixelator" +tools: + - pixelator: + description: | + Software package to process sequencing data into Molecular Pixelation data. + homepage: https://github.com/PixelgenTechnologies/pixelator + documentation: https://github.com/PixelgenTechnologies/pixelator + arxiv: null + licence: ["MIT"] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: | + List of input FastQ files of size 1 and 2 for single-end and paired-end data, + respectively. + +output: + - edgelist: + type: file + description: Filtered edgelist + pattern: "*.edgelist.csv.gz" + + - raw_edgelist: + type: file + description: Raw (unfiltered) edgelist + pattern: "*.raw_edgelist.csv.gz" + + - components_recovered: + type: file + description: CSV file with recovered components. Only available when the --multiplet-recovery flag is set. + pattern: "*.components_recovered.json" + optional: true + + - report_json: + type: file + description: JSON file with statistics for the graph stage + pattern: "*.report.json" + + - all_results: + type: file + description: All output files from the pixelator graph Command + pattern: "*" + + - log: + type: file + description: Pixelator log files + pattern: "*pixelator-graph*.log" + + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@fbdtemme" diff --git a/modules/local/pixelator/qc/main.nf b/modules/local/pixelator/qc/main.nf new file mode 100644 index 00000000..d44c7944 --- /dev/null +++ b/modules/local/pixelator/qc/main.nf @@ -0,0 +1,78 @@ + + +process PIXELATOR_QC { + tag "$meta.id" + label 'process_medium' + conda "local::pixelator=0.10.0" + + // TODO: make pixelator available on galaxyproject and quay.io support + container "ghcr.io/pixelgentechnologies/pixelator:0.10.0" + + input: + tuple val(meta), path(reads) + + output: + tuple val(meta), path("preqc/*.processed.{fq,fastq}.gz"), emit: processed + + tuple val(meta), path("adapterqc/*.processed.{fq,fastq}.gz"), emit: adapterqc_processed + tuple val(meta), path("preqc/*.processed.{fq,fastq}.gz"), emit: preqc_processed + + tuple val(meta), path("adapterqc/*.failed.{fq,fastq}.gz"), emit: adapterqc_failed + tuple val(meta), path("preqc/*.failed.{fq,fastq}.gz"), emit: preqc_failed + tuple val(meta), path("{adapterqc,preqc}/*.failed.{fq,fastq}.gz"), emit: failed + + tuple val(meta), path("adapterqc/*.report.json"), emit: adapterqc_report_json + tuple val(meta), path("preqc/*.report.json"), emit: preqc_report_json + tuple val(meta), path("{adapterqc,preqc}/*.report.json"), emit: report_json + + tuple val(meta), path("adapterqc/*.meta.json"), emit: adapterqc_metadata + tuple val(meta), path("preqc/*.meta.json"), emit: preqc_metadata + tuple val(meta), path("{adapterqc,preqc}/*.meta.json"), emit: metadata + + tuple val(meta), path("*pixelator-*.log"), emit: log + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + assert meta.design + + prefix = task.ext.prefix ?: "${meta.id}" + def preqc_args = task.ext.args ?: '' + def adapterqc_args = task.ext.args2 ?: '' + + // --design is passed in meta and added to args and args2 through modules.conf + + """ + pixelator \\ + --cores $task.cpus \\ + --log-file ${prefix}.pixelator-qc.log \\ + --verbose \\ + single-cell \\ + preqc \\ + --output . \\ + ${preqc_args} \\ + ${reads} + + shopt -s nullglob + preqc_results=( preqc/*.processed.* ) + echo \${preqc_results[@]} + shopt -u nullglob # Turn off nullglob to make sure it doesn't interfere with anything later + + pixelator \\ + --cores $task.cpus \\ + --log-file ${prefix}.pixelator-qc.log \\ + --verbose \\ + single-cell \\ + adapterqc \\ + --output . \\ + ${adapterqc_args} \\ + \${preqc_results[@]} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + pixelator: \$(echo \$(pixelator --version 2>/dev/null) | sed 's/pixelator, version //g' ) + END_VERSIONS + """ +} diff --git a/modules/local/pixelator/qc/meta.yaml b/modules/local/pixelator/qc/meta.yaml new file mode 100644 index 00000000..0b03923a --- /dev/null +++ b/modules/local/pixelator/qc/meta.yaml @@ -0,0 +1,100 @@ +name: pixelator/qc +description: Performs quality control before processing of molecular pixelation data +keywords: + - "molecular pixelator" + - qc + - fastp + - cutadapt +tools: + - pixelator: + description: | + Software package to process sequencing data into Molecular Pixelation data. + homepage: https://github.com/PixelgenTechnologies/pixelator + documentation: https://github.com/PixelgenTechnologies/pixelator + arxiv: null + licence: ["MIT"] + +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: | + List of input FastQ files of size 1 and 2 for single-end and paired-end data, + respectively. +output: + - processed: + type: file + description: All processed FastQ files + pattern: "preqc/*.processed.{fq,fastq}.gz" + + - preqc_processed: + type: file + description: FastQ files containing all reads that passed the preqc stage + pattern: "adapterqc/*.processed.{fq,fastq}.gz" + + - adapterqc_processed: + type: file + description: FastQ files containing all reads that passed the adapter stage + pattern: "adapterqc/*.processed.{fq,fastq}.gz" + + - preqc_failed: + type: file + description: FastQ files containing all reads that were rejected by the preqc stage + pattern: "preqc/*.failed.{fq,fastq}.gz" + + - adapterqc_failed: + type: file + description: FastQ files containing all reads that were rejected by the adapter stage + pattern: "adapterqc/*.failed.{fq,fastq}.gz" + + - failed: + type: file + description: FastQ files containing all reads that were rejected by qc + pattern: "adapterqc/*.failed.{fq,fastq}.gz" + + - preqc_report: + type: file + description: JSON file with statistics for the preqc stage as reported by fastp + pattern: "preqc/*.report.json" + + - adapterqc_report: + type: file + description: JSON file with statistics for the adapoterqc stage as reported by cutadapt + pattern: "adapterqc/*.report.json" + + - report: + type: file + description: JSON files with QC metrics + pattern: "{preqc,adapterqc}/*.report.json" + + - preqc_metadata: + type: file + description: Command invocation metadata files for the pixelator preqc stage + pattern: "preqc/*.meta.json" + + - adapterqc_metadata: + type: file + description: Command invocation metadata files for the pixelator adapterqc stage + pattern: "adapterqc/*.meta.json" + + - metadata: + type: file + description: Command invocation metadata files for pixelator qc stages + pattern: "{preqc,adapterqc}/*.meta.json" + + - log: + type: file + description: Pixelator log files + pattern: "*pixelator-*.log" + + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@fbdtemme" diff --git a/modules/local/pixelator/report/main.nf b/modules/local/pixelator/report/main.nf new file mode 100644 index 00000000..1f350a83 --- /dev/null +++ b/modules/local/pixelator/report/main.nf @@ -0,0 +1,47 @@ + + +process PIXELATOR_REPORT { + tag "$meta.id" + label 'process_low' + + conda "local::pixelator=0.10.0" + + container "ghcr.io/pixelgentechnologies/pixelator:0.10.0" + + input: + val meta + path panel_file + path concatenate_data, stageAs: "results/concatenate/*" + path preqc_data, stageAs: "results/preqc/*" + path adapterqc_data, stageAs: "results/adapterqc/*" + path demux_data, stageAs: "results/demux/*" + path collapse_data, stageAs: "results/collapse/*" + path graph_data, stageAs: "results/graph/*" + path annotate_data, stageAs: "results/annotate/*" + path analysis_data, stageAs: "results/analysis/*" + + output: + path "report/*.html", emit: reports + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + + """ + pixelator \\ + single-cell \\ + report \\ + --output . \\ + --panel-file ${panel_file} \\ + $args \\ + results + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + pixelator: \$(echo \$(pixelator --version 2>/dev/null) | sed 's/pixelator, version //g' ) + END_VERSIONS + """ +} diff --git a/modules/local/rename_reads.nf b/modules/local/rename_reads.nf new file mode 100644 index 00000000..a888c3ff --- /dev/null +++ b/modules/local/rename_reads.nf @@ -0,0 +1,46 @@ +process RENAME_READS { + tag "$meta.id" + label "process_single" + + conda "conda-forge::sed=4.7" + if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { + container "https://depot.galaxyproject.org/singularity/ubuntu:20.04" + } else { + container "registry.hub.docker.com/biocontainers/biocontainers:v1.2.0_cv2" + } + + input: + tuple val(meta), path(reads) + + output: + tuple val(meta), path("${meta.id}{,_R1,_R2}*"), emit: reads + path "versions.yml", emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + + if (reads in List) { + """ + r1_ext=\$(echo ${reads[0]} | grep -E -o "f(ast)?q.gz") + r2_ext=\$(echo ${reads[1]} | grep -E -o "f(ast)?q.gz") + + mv ${reads[0]} ${meta.id}_R1.\${r1_ext} + mv ${reads[1]} ${meta.id}_R2.\${r2_ext} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": {} + END_VERSIONS + """ + } else { + """ + r1_ext=\$(echo ${reads} | grep -E -o "f(ast)?q.gz") + mv ${reads} ${meta.id}.\${r1_ext} + + cat <<-END_VERSIONS > versions.yml + "${task.process}": {} + END_VERSIONS + """ + } +} diff --git a/modules/local/samplesheet_check.nf b/modules/local/samplesheet_check.nf index c4cfb8a0..b91e67cb 100644 --- a/modules/local/samplesheet_check.nf +++ b/modules/local/samplesheet_check.nf @@ -2,13 +2,12 @@ process SAMPLESHEET_CHECK { tag "$samplesheet" label 'process_single' - conda "conda-forge::python=3.8.3" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/python:3.8.3' : - 'biocontainers/python:3.8.3' }" + // conda "local::pixelator=0.10.0" + // container "ghcr.io/pixelgentechnologies/pixelator:0.10.0" input: path samplesheet + val samplesheet_path output: path '*.csv' , emit: csv @@ -18,10 +17,17 @@ process SAMPLESHEET_CHECK { task.ext.when == null || task.ext.when script: // This script is bundled with the pipeline, in nf-core/pixelator/bin/ + def args = task.ext.args ?: '' + """ + pixelator single-cell --list-designs > design_options.txt + check_samplesheet.py \\ $samplesheet \\ - samplesheet.valid.csv + samplesheet.valid.csv \\ + --samplesheet-path $samplesheet_path \\ + --design-options design_options.txt \\ + $args cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/nf-core/cat/fastq/main.nf b/modules/nf-core/cat/fastq/main.nf new file mode 100644 index 00000000..5021e6fc --- /dev/null +++ b/modules/nf-core/cat/fastq/main.nf @@ -0,0 +1,80 @@ +process CAT_FASTQ { + tag "$meta.id" + label 'process_single' + + conda "conda-forge::sed=4.7" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/ubuntu:20.04' : + 'nf-core/ubuntu:20.04' }" + + input: + tuple val(meta), path(reads, stageAs: "input*/*") + + output: + tuple val(meta), path("*.merged.fastq.gz"), emit: reads + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.id}" + def readList = reads instanceof List ? reads.collect{ it.toString() } : [reads.toString()] + if (meta.single_end) { + if (readList.size >= 1) { + """ + cat ${readList.join(' ')} > ${prefix}.merged.fastq.gz + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + cat: \$(echo \$(cat --version 2>&1) | sed 's/^.*coreutils) //; s/ .*\$//') + END_VERSIONS + """ + } + } else { + if (readList.size >= 2) { + def read1 = [] + def read2 = [] + readList.eachWithIndex{ v, ix -> ( ix & 1 ? read2 : read1 ) << v } + """ + cat ${read1.join(' ')} > ${prefix}_1.merged.fastq.gz + cat ${read2.join(' ')} > ${prefix}_2.merged.fastq.gz + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + cat: \$(echo \$(cat --version 2>&1) | sed 's/^.*coreutils) //; s/ .*\$//') + END_VERSIONS + """ + } + } + + stub: + def prefix = task.ext.prefix ?: "${meta.id}" + def readList = reads instanceof List ? reads.collect{ it.toString() } : [reads.toString()] + if (meta.single_end) { + if (readList.size > 1) { + """ + touch ${prefix}.merged.fastq.gz + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + cat: \$(echo \$(cat --version 2>&1) | sed 's/^.*coreutils) //; s/ .*\$//') + END_VERSIONS + """ + } + } else { + if (readList.size > 2) { + """ + touch ${prefix}_1.merged.fastq.gz + touch ${prefix}_2.merged.fastq.gz + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + cat: \$(echo \$(cat --version 2>&1) | sed 's/^.*coreutils) //; s/ .*\$//') + END_VERSIONS + """ + } + } + +} diff --git a/modules/nf-core/cat/fastq/meta.yml b/modules/nf-core/cat/fastq/meta.yml new file mode 100644 index 00000000..8a39e309 --- /dev/null +++ b/modules/nf-core/cat/fastq/meta.yml @@ -0,0 +1,40 @@ +name: cat_fastq +description: Concatenates fastq files +keywords: + - cat + - fastq + - concatenate +tools: + - cat: + description: | + The cat utility reads files sequentially, writing them to the standard output. + documentation: https://www.gnu.org/software/coreutils/manual/html_node/cat-invocation.html + licence: ["GPL-3.0-or-later"] +input: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: | + List of input FastQ files to be concatenated. +output: + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - reads: + type: file + description: Merged fastq file + pattern: "*.{merged.fastq.gz}" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" + +authors: + - "@joseespinosa" + - "@drpatelh" diff --git a/modules/nf-core/custom/dumpsoftwareversions/main.nf b/modules/nf-core/custom/dumpsoftwareversions/main.nf index 800a6099..ebc87273 100644 --- a/modules/nf-core/custom/dumpsoftwareversions/main.nf +++ b/modules/nf-core/custom/dumpsoftwareversions/main.nf @@ -5,7 +5,7 @@ process CUSTOM_DUMPSOFTWAREVERSIONS { conda "bioconda::multiqc=1.14" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/multiqc:1.14--pyhdfd78af_0' : - 'quay.io/biocontainers/multiqc:1.14--pyhdfd78af_0' }" + 'biocontainers/multiqc:1.14--pyhdfd78af_0' }" input: path versions diff --git a/modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py b/modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py index e55b8d43..da033408 100755 --- a/modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py +++ b/modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py @@ -4,11 +4,10 @@ """Provide functions to merge multiple versions.yml files.""" +import yaml import platform from textwrap import dedent -import yaml - def _make_versions_html(versions): """Generate a tabular HTML output of all versions for MultiQC.""" diff --git a/modules/nf-core/fastqc/main.nf b/modules/nf-core/fastqc/main.nf deleted file mode 100644 index 9ae58381..00000000 --- a/modules/nf-core/fastqc/main.nf +++ /dev/null @@ -1,51 +0,0 @@ -process FASTQC { - tag "$meta.id" - label 'process_medium' - - conda "bioconda::fastqc=0.11.9" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/fastqc:0.11.9--0' : - 'quay.io/biocontainers/fastqc:0.11.9--0' }" - - input: - tuple val(meta), path(reads) - - output: - tuple val(meta), path("*.html"), emit: html - tuple val(meta), path("*.zip") , emit: zip - path "versions.yml" , emit: versions - - when: - task.ext.when == null || task.ext.when - - script: - def args = task.ext.args ?: '' - def prefix = task.ext.prefix ?: "${meta.id}" - // Make list of old name and new name pairs to use for renaming in the bash while loop - def old_new_pairs = reads instanceof Path || reads.size() == 1 ? [[ reads, "${prefix}.${reads.extension}" ]] : reads.withIndex().collect { entry, index -> [ entry, "${prefix}_${index + 1}.${entry.extension}" ] } - def rename_to = old_new_pairs*.join(' ').join(' ') - def renamed_files = old_new_pairs.collect{ old_name, new_name -> new_name }.join(' ') - """ - printf "%s %s\\n" $rename_to | while read old_name new_name; do - [ -f "\${new_name}" ] || ln -s \$old_name \$new_name - done - fastqc $args --threads $task.cpus $renamed_files - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - fastqc: \$( fastqc --version | sed -e "s/FastQC v//g" ) - END_VERSIONS - """ - - stub: - def prefix = task.ext.prefix ?: "${meta.id}" - """ - touch ${prefix}.html - touch ${prefix}.zip - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - fastqc: \$( fastqc --version | sed -e "s/FastQC v//g" ) - END_VERSIONS - """ -} diff --git a/modules/nf-core/fastqc/meta.yml b/modules/nf-core/fastqc/meta.yml deleted file mode 100644 index 4da5bb5a..00000000 --- a/modules/nf-core/fastqc/meta.yml +++ /dev/null @@ -1,52 +0,0 @@ -name: fastqc -description: Run FastQC on sequenced reads -keywords: - - quality control - - qc - - adapters - - fastq -tools: - - fastqc: - description: | - FastQC gives general quality metrics about your reads. - It provides information about the quality score distribution - across your reads, the per base sequence content (%A/C/G/T). - You get information about adapter contamination and other - overrepresented sequences. - homepage: https://www.bioinformatics.babraham.ac.uk/projects/fastqc/ - documentation: https://www.bioinformatics.babraham.ac.uk/projects/fastqc/Help/ - licence: ["GPL-2.0-only"] -input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - reads: - type: file - description: | - List of input FastQ files of size 1 and 2 for single-end and paired-end data, - respectively. -output: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - html: - type: file - description: FastQC report - pattern: "*_{fastqc.html}" - - zip: - type: file - description: FastQC report archive - pattern: "*_{fastqc.zip}" - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" -authors: - - "@drpatelh" - - "@grst" - - "@ewels" - - "@FelixKrueger" diff --git a/modules/nf-core/multiqc/main.nf b/modules/nf-core/multiqc/main.nf deleted file mode 100644 index 4b604749..00000000 --- a/modules/nf-core/multiqc/main.nf +++ /dev/null @@ -1,53 +0,0 @@ -process MULTIQC { - label 'process_single' - - conda "bioconda::multiqc=1.14" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/multiqc:1.14--pyhdfd78af_0' : - 'quay.io/biocontainers/multiqc:1.14--pyhdfd78af_0' }" - - input: - path multiqc_files, stageAs: "?/*" - path(multiqc_config) - path(extra_multiqc_config) - path(multiqc_logo) - - output: - path "*multiqc_report.html", emit: report - path "*_data" , emit: data - path "*_plots" , optional:true, emit: plots - path "versions.yml" , emit: versions - - when: - task.ext.when == null || task.ext.when - - script: - def args = task.ext.args ?: '' - def config = multiqc_config ? "--config $multiqc_config" : '' - def extra_config = extra_multiqc_config ? "--config $extra_multiqc_config" : '' - """ - multiqc \\ - --force \\ - $args \\ - $config \\ - $extra_config \\ - . - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - multiqc: \$( multiqc --version | sed -e "s/multiqc, version //g" ) - END_VERSIONS - """ - - stub: - """ - touch multiqc_data - touch multiqc_plots - touch multiqc_report.html - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - multiqc: \$( multiqc --version | sed -e "s/multiqc, version //g" ) - END_VERSIONS - """ -} diff --git a/modules/nf-core/multiqc/meta.yml b/modules/nf-core/multiqc/meta.yml deleted file mode 100644 index f93b5ee5..00000000 --- a/modules/nf-core/multiqc/meta.yml +++ /dev/null @@ -1,56 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/yaml-schema.json -name: MultiQC -description: Aggregate results from bioinformatics analyses across many samples into a single report -keywords: - - QC - - bioinformatics tools - - Beautiful stand-alone HTML report -tools: - - multiqc: - description: | - MultiQC searches a given directory for analysis logs and compiles a HTML report. - It's a general use tool, perfect for summarising the output from numerous bioinformatics tools. - homepage: https://multiqc.info/ - documentation: https://multiqc.info/docs/ - licence: ["GPL-3.0-or-later"] - -input: - - multiqc_files: - type: file - description: | - List of reports / files recognised by MultiQC, for example the html and zip output of FastQC - - multiqc_config: - type: file - description: Optional config yml for MultiQC - pattern: "*.{yml,yaml}" - - extra_multiqc_config: - type: file - description: Second optional config yml for MultiQC. Will override common sections in multiqc_config. - pattern: "*.{yml,yaml}" - - multiqc_logo: - type: file - description: Optional logo file for MultiQC - pattern: "*.{png}" - -output: - - report: - type: file - description: MultiQC report file - pattern: "multiqc_report.html" - - data: - type: directory - description: MultiQC data dir - pattern: "multiqc_data" - - plots: - type: file - description: Plots created by MultiQC - pattern: "*_data" - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" -authors: - - "@abhi18av" - - "@bunop" - - "@drpatelh" - - "@jfy133" diff --git a/nextflow.config b/nextflow.config index 59fdf4cf..fac0173d 100644 --- a/nextflow.config +++ b/nextflow.config @@ -6,24 +6,85 @@ ---------------------------------------------------------------------------------------- */ +plugins { + id 'nf-validation@0.2.1' +} + // Global default params, used in configs params { - // TODO nf-core: Specify your pipeline's command line flags // Input options input = null + // General options + pixelator_tag = "dev" + pixelator_container_source = "ghcr" + pixelator_container = null + + // Preqc options + trim_front = 0 + trim_tail = 0 + max_length = null + min_length = null + max_n_bases = 0 + avg_qual = 20 + dedup = false + remove_polyg = false + + // adapterqc options + adapterqc_mismatches = 0.1 + pbs1 = null + pbs2 = null + + //demux options + demux_mismatches = 0.1 + demux_min_length = null + anchored = true + rev_complement = false + + //collapse options + algorithm = 'adjacency' + upia_start = null + upia_end = null + upib_start = null + upib_end = null + umia_start = null + umia_end = null + umib_start = null + umib_end = null + neighbours = 60 + collapse_mismatches = 2 + collapse_min_count = 2 + use_counts = false - // References - genome = null - igenomes_base = 's3://ngi-igenomes/igenomes' - igenomes_ignore = false - // MultiQC options - multiqc_config = null - multiqc_title = null - multiqc_logo = null - max_multiqc_email_size = '25.MB' - multiqc_methods_description = null + // cluster options + multiplet_recovery = 'leiden' + fast_greedy_fraction = 0.5 + fast_greedy_cutoff = 5000 + leiden_iterations = 10 + cluster_min_count = 2 + + // annotate options + min_size = null + max_size = null + dynamic_filter = 'min' + cell_type_assignments = false + majority_vote = false + aggregate_calling = true + + // analysis options + compute_polarization = true + compute_colocalization = true + use_full_bipartite = false + normalization = 'clr' + binarization = false + + // skip options + skip_report = false + skip_analysis = false + + // report options + report_name = "report" // Boilerplate options outdir = null @@ -38,7 +99,7 @@ params { version = false validate_params = true show_hidden_params = false - schema_ignore_params = 'genomes' + schema_ignore_params = '' // Config options @@ -56,6 +117,8 @@ params { max_cpus = 16 max_time = '240.h' + // Testdata options + testdata_root = "../nf-core-pixelator-datasets" } // Load base.config by default for all pipelines @@ -173,13 +236,6 @@ profiles { } -// Load igenomes.config if required -if (!params.igenomes_ignore) { - includeConfig 'conf/igenomes.config' -} else { - params.genomes = [:] -} - // Export these variables to prevent local Python/R libraries from conflicting with those in the container // The JULIA depot path has been adjusted to a fixed path `/usr/local/share/julia` that needs to be used for packages in the container. diff --git a/nextflow_schema.json b/nextflow_schema.json index f559b384..74bd75f0 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -1,6 +1,6 @@ { "$schema": "http://json-schema.org/draft-07/schema", - "$id": "https://raw.githubusercontent.com/nf-core/pixelator/master/nextflow_schema.json", + "$id": "https://raw.githubusercontent.com/PixelgenTechnologies/nf-core-pixelator/master/nextflow_schema.json", "title": "nf-core/pixelator pipeline parameters", "description": "Pipeline for analysis of Molecular Pixelation assays", "type": "object", @@ -15,9 +15,8 @@ "input": { "type": "string", "format": "file-path", - "mimetype": "text/csv", - "pattern": "^\\S+\\.csv$", "schema": "assets/schema_input.json", + "mimetype": "text/csv", "description": "Path to comma-separated file containing information about the samples in the experiment.", "help_text": "You will need to create a design file with information about the samples in your experiment before running the pipeline. Use this parameter to specify its location. It has to be a comma-separated file with 3 columns, and a header row. See [usage docs](https://nf-co.re/pixelator/usage#samplesheet-input).", "fa_icon": "fas fa-file-csv" @@ -34,49 +33,314 @@ "fa_icon": "fas fa-envelope", "help_text": "Set this parameter to your e-mail address to get a summary e-mail with details of the run sent to you when the workflow exits. If set in your user config file (`~/.nextflow/config`) then you don't need to specify this on the command line for every run.", "pattern": "^([a-zA-Z0-9_\\-\\.]+)@([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$" - }, - "multiqc_title": { - "type": "string", - "description": "MultiQC report title. Printed as page header, used for filename if not otherwise specified.", - "fa_icon": "fas fa-file-signature" } } }, - "reference_genome_options": { - "title": "Reference genome options", + "preqc_options": { + "title": "QC/Filtering/Trimming options", "type": "object", - "fa_icon": "fas fa-dna", - "description": "Reference genome related files and options required for the workflow.", + "fa_icon": "fas fa-terminal", + "properties": { + "trim_front": { + "fa_icon": "fas backward-step", + "type": "integer", + "description": "Trim N bases from the front of the reads", + "default": 0 + }, + "trim_tail": { + "fa_icon": "fas forward-step", + "type": "integer", + "description": "Trim N bases from the tail of the reads", + "default": 0 + }, + "max_length": { + "fa_icon": "fas up-right-and-down-left-from-center", + "type": "integer", + "description": "The maximum length (bases) of a read (longer reads will be trimmed off). If you set this argument it will overrrule the value from the chosen design" + }, + "min_length": { + "fa_icon": "fas down-left-and-up-right-to-center", + "type": "integer", + "description": "The minimum length (bases) of a read (shorter reads will be discarded). If you set this argument it will overrrule the value from the chosen design." + }, + "max_n_bases": { + "fa_icon": "fas n", + "description": "The maximum number of Ns allowed in a read", + "type": "integer", + "default": 0 + }, + "avg_qual": { + "fa_icon": "fas gauge", + "description": "Minimum avg. quality a read must have (0 will disable the filter)", + "type": "integer", + "default": 20 + }, + "dedup": { + "fa_icon": "fas clone", + "description": "Remove duplicated reads (exact same sequence)", + "type": "boolean", + "default": false + }, + "remove_polyg": { + "fa_icon": "fas g", + "description": "Remove PolyG sequences (length of 10 or more)", + "type": "boolean", + "default": false + } + } + }, + "adapterqc_options": { + "title": "Adapter QC Options", + "properties": { + "adapterqc_mismatches": { + "fa_icon": "fas not-equal", + "description": "The number of mismatches allowed (in percentage) [default: 0.1; 0.0<=x<=0.9]", + "type": "number", + "default": 0.1 + }, + "pbs1": { + "description": "The PBS1 sequence that must be present in the reads. If you set this argument it will overrrule the value from the chosen design", + "type": "string" + }, + "pbs2": { + "description": "The PBS2 sequence that must be present in the reads. If you set this argument it will overrrule the value from the chosen design", + "type": ["string", "null"] + } + } + }, + "demux_options": { + "title": "Demux options", + "properties": { + "demux_mismatches": { + "fa_icon": "fas not-equal", + "description": "The number of mismatches allowed (in percentage) [default: 0.1; 0.0<=x<=0.9]", + "type": "number", + "default": 0.1, + "minimum": 0.0, + "maximum": 0.9 + }, + "demux_min_length": { + "fa_icon": "fas down-left-and-up-right-to-center", + "description": "The minimum length of the barcode that must overlap when matching. If you set this argument it will overrrule the value from the chosen design", + "type": ["integer", "null"] + }, + "anchored": { + "fa_icon": "fas anchor", + "description": "Enforce the barcodes to be anchored (at the end of the read)", + "type": "boolean", + "default": true + }, + "rev_complement": { + "fa_icon": "fas right-left", + "description": "Use the reverse complement of the barcodes sequences", + "type": "boolean", + "default": false + } + } + }, + "collapse_options": { + "title": "Collapse options", + "properties": { + "algorithm": { + "fa_icon": "fas code-fork", + "description": "The algorithm to use for collapsing (adjacency will peform error correction using the number of mismatches given) [default: adjacency]", + "default": "adjacency", + "enum": ["adjacency", "unique"], + "type": "string" + }, + "upia_start": { + "fa_icon": "fas backward-step", + "description": "The start position (0-based) of UPIA. If you set this argument it will overrrule the value from the chosen design", + "type": "integer" + }, + "upia_end": { + "fa_icon": "fas forward-step", + "description": "The end position (1-based) of UPIA. If you set this argument it will overrrule the value from the chosen design", + "type": "integer" + }, + "upib_start": { + "fa_icon": "fas backward-step", + "description": "The start position (0-based) of UPIB. If you set this argument it will overrrule the value from the chosen design", + "type": "integer" + }, + "upib_end": { + "fa_icon": "fas forward-step", + "description": "The end position (1-based) of UPIB. If you set this argument it will overrrule the value from the chosen design", + "type": "integer" + }, + "umia_start": { + "fa_icon": "fas backward-step", + "description": "The start position (0-based) of UMIA (disabled by default). If you set this argument it will overrrule the value from the chosen design", + "type": "integer" + }, + "umia_end": { + "fa_icon": "fas forward-step", + "description": "The end position (1-based) of UMIA (disabled by default). If you set this argument it will overrrule the value from the chosen design", + "type": "integer" + }, + "umib_start": { + "fa_icon": "fas forward-step", + "description": "The start position (0-based) of UMIB (disabled by default). If you set this argument it will overrrule the value from the chosen design", + "type": "integer" + }, + "umib_end": { + "fa_icon": "fas backward-step", + "description": "The end position (1-based) of UMIB (disabled by default). If you set this argument it will overrrule the value from the chosen design", + "type": "integer" + }, + "neighbours": { + "fa_icon": "fas circle-nodes", + "description": "The number of neighbours to use when searching for similar sequences (adjacency) This number depends on the sequence depth and the ratio of erronous molecules expected. A high value can make the algoritthm slower. [default: 60; 1<=x<=250]", + "default": 60, + "minimum": 1, + "maximum": 250, + "type": "integer" + }, + "collapse_mismatches": { + "fa_icon": "fas not-equal", + "description": "The number of mismatches allowed when collapsing (adjacency) [default: 2; 0<=x<=5]", + "default": 2, + "minimum": 0, + "maximum": 5, + "type": "integer" + }, + "collapse_min_count": { + "fa_icon": "fas more-than-equal", + "description": "Discard molecules with with a count (reads) lower than this value [default: 2]", + "default": 2, + "minimum": 1, + "type": "integer" + }, + "use_counts": { + "description": "Use counts when collapsing (the difference in counts between two molecules must be more than double in order to be collapsed)", + "default": false, + "type": "boolean" + } + } + }, + "graph_options": { + "title": "Options for pixelator graph command.", "properties": { - "genome": { + "multiplet_recovery": { + "description": "Activate the multiplet recovery (leiden or fast-greedy): leiden: use the leiden algorithm (whole graph) fast-greedy: use fast greedy algorithm (per-component)", "type": "string", - "description": "Name of iGenomes reference.", - "fa_icon": "fas fa-book", - "help_text": "If using a reference genome configured in the pipeline using iGenomes, use this parameter to give the ID for the reference. This is then used to build the full paths for all required reference genome files e.g. `--genome GRCh38`. \n\nSee the [nf-core website docs](https://nf-co.re/usage/reference_genomes) for more details." + "enum": ["leiden", "fast-greedy"], + "default": "leiden" + }, + "fast_greedy_fraction": { + "description": "Maximum fraction of edges allowed to be removed in a component for the fast-greedy algorithm [default: 0.05; 0.0<=x<=1.0]", + "type": "number", + "default": 0.5, + "minimum": 0.0, + "maximum": 1.0 + }, + "fast_greedy_cutoff": { + "fa_icon": "fas less-than-equal", + "description": "Minimum size (edges) of a component to be considered for the fast-greedy algorithm [default: 5000]", + "type": "integer", + "default": 5000 + }, + "leiden_iterations": { + "fa_icon": "fas repeat", + "description": "Number of iterations for the leiden algorithm, high values will decrease the variance of the results but increase the runtime [default: 10; 1<=x<=100]", + "type": "integer", + "default": 10, + "minimum": 1, + "maximum": 100 }, - "fasta": { + "cluster_min_count": { + "fa_icon": "fas less-than-equal", + "description": "Discard edges (pixels) with a count (reads) lower than this [default: 2; 1<=x<=50] use 1 to disable", + "type": "integer", + "default": 2, + "minimum": 1, + "maximum": 50 + } + } + }, + "annotate_options": { + "title": "Options for pixelator annotate command.", + "properties": { + "min_size": { + "description": "The minimum size (pixels) a component/cell can have (default is no filtering)", + "type": "integer" + }, + "max_size": { + "description": "The maximum size (pixels) a component/cell can have (default is no filtering)", + "type": "integer" + }, + "dynamic_filter": { + "description": " Enable the estimation of dynamic size filters using a log-rank approach both: estimate both min and max size, min: estimate min size (--min-size), max: estimate max size (--max-size)", "type": "string", - "format": "file-path", - "mimetype": "text/plain", - "pattern": "^\\S+\\.fn?a(sta)?(\\.gz)?$", - "description": "Path to FASTA genome file.", - "help_text": "This parameter is *mandatory* if `--genome` is not specified. If you don't have a BWA index available this will be generated for you automatically. Combine with `--save_reference` to save BWA index for future runs.", - "fa_icon": "far fa-file-code" + "enum": ["both", "min", "max"], + "default": "min" }, - "igenomes_base": { + "cell_type_assignments": { + "description": "Enable cell type assignment using pre-trained models", + "default": false, + "type": "boolean" + }, + "majority_vote": { + "description": "Enable cell type majority voting using clustering of components", + "default": false, + "type": "boolean" + }, + "aggregate_calling": { + "description": "Enable aggregate calling, information on potential aggregates will be added to the output data", + "default": true, + "type": "boolean" + } + } + }, + "analysis_options": { + "title": "Options for pixelator analysis command.", + "properties": { + "skip_analysis": { + "description": "Skip analysis step", + "type": "boolean", + "default": false + }, + "compute_polarization": { + "description": "Compute polarization scores matrix (clusters by markers)", + "type": "boolean", + "default": true + }, + "compute_colocalization": { + "description": " Compute colocalization scores (marker by marker) for each component", + "type": "boolean", + "default": true + }, + "use_full_bipartite": { + "description": "Use the bipartite graph instead of the one-node projection when computing polarization, coabundance and colocalization scores", + "type": "boolean", + "default": false + }, + "normalization": { + "description": "Which approach to use to normalize the antibody counts: raw will use the raw counts CLR will use the CLR-transformed counts denoise will use CLR-transformed counts and subtract the counts of control antibodies", "type": "string", - "format": "directory-path", - "description": "Directory / URL base for iGenomes references.", - "default": "s3://ngi-igenomes/igenomes", - "fa_icon": "fas fa-cloud-download-alt", - "hidden": true + "enum": ["raw", "clr", "denoise"], + "default": "clr" }, - "igenomes_ignore": { + "binarization": { + "fa_icon": "fas binary", + "description": "Transform the antibody counts to 0-1 (binarize) when computing polarization", "type": "boolean", - "description": "Do not load the iGenomes reference config.", - "fa_icon": "fas fa-ban", - "hidden": true, - "help_text": "Do not load `igenomes.config` when running the pipeline. You may choose this option if you observe clashes between custom parameters and those supplied in `igenomes.config`." + "default": false + } + } + }, + "report_options": { + "title": "Options for pixelator report command.", + "properties": { + "skip_report": { + "description": "Skip report generation", + "type": "boolean", + "default": false + }, + "report_name": { + "description": "The name for the report", + "type": ["string"], + "default": "report" } } }, @@ -163,6 +427,36 @@ } } }, + "global_options": { + "title": "Global options", + "type": "object", + "fa_icon": "fas fa-file-import", + "description": "Less common options for the pipeline (specific to nf-core-pixelator), typically set in a config file.", + "help_text": "These options allow you to customize some of the core preferences for how the pipeline runs.\n\nTypically these options would be set in a Nextflow config file loaded for all pipeline runs, such as `~/.nextflow/config`.", + "properties": { + "pixelator_container_source": { + "type": "string", + "description": "The source repository for the pixelator docker container.", + "enum": ["aws-ecr", "ghcr", "internal"], + "default": "ghcr", + "hidden": true + }, + "pixelator_tag": { + "type": "string", + "description": "Override which container tag of pixelator to use. Use carefully!", + "help_text": "This option allows you to use a different container tag for the pixelator tool.\nThis is intended for developers and power-users and can break the pipeline. Use on your own risk!", + "fa_icon": "fas fa-question-circle", + "hidden": true, + "default": "0.11.0" + }, + "pixelator_container": { + "type": "string", + "description": "The exact container to use. Overrides container defined by pixelator_container_source and pixelator_tag.", + "hidden": true, + "default": null + } + } + }, "generic_options": { "title": "Generic options", "type": "object", @@ -207,7 +501,7 @@ }, "max_multiqc_email_size": { "type": "string", - "description": "File size limit when attaching MultiQC reports to summary emails.", + "description": "File size limit when attaching reports to summary emails.", "pattern": "^\\d+(\\.\\d+)?\\.?\\s*(K|M|G|T)?B$", "default": "25.MB", "fa_icon": "fas fa-file-upload", @@ -226,23 +520,6 @@ "help_text": "Incoming hook URL for messaging service. Currently, MS Teams and Slack are supported.", "hidden": true }, - "multiqc_config": { - "type": "string", - "description": "Custom config file to supply to MultiQC.", - "fa_icon": "fas fa-cog", - "hidden": true - }, - "multiqc_logo": { - "type": "string", - "description": "Custom logo file to supply to MultiQC. File name must also be set in the MultiQC config file", - "fa_icon": "fas fa-image", - "hidden": true - }, - "multiqc_methods_description": { - "type": "string", - "description": "Custom MultiQC yaml file containing HTML including a methods description.", - "fa_icon": "fas fa-cog" - }, "tracedir": { "type": "string", "description": "Directory to keep pipeline Nextflow logs and reports.", @@ -263,6 +540,16 @@ "description": "Show all params when using `--help`", "hidden": true, "help_text": "By default, parameters set as _hidden_ in the schema are not shown on the command line when a user runs with `--help`. Specifying this option will tell the pipeline to show all parameters." + }, + "testdata_root": { + "type": "string", + "description": "Root path to testdata for running local tests", + "hidden": true, + "fa_icon": "fas fa-folder-tree" + }, + "schema_ignore_params": { + "type": "string", + "description": "A comma separated string of inputs the schema validation should ignore." } } } @@ -272,7 +559,28 @@ "$ref": "#/definitions/input_output_options" }, { - "$ref": "#/definitions/reference_genome_options" + "$ref": "#/definitions/preqc_options" + }, + { + "$ref": "#/definitions/adapterqc_options" + }, + { + "$ref": "#/definitions/demux_options" + }, + { + "$ref": "#/definitions/collapse_options" + }, + { + "$ref": "#/definitions/graph_options" + }, + { + "$ref": "#/definitions/annotate_options" + }, + { + "$ref": "#/definitions/analysis_options" + }, + { + "$ref": "#/definitions/report_options" }, { "$ref": "#/definitions/institutional_config_options" @@ -280,6 +588,9 @@ { "$ref": "#/definitions/max_job_request_options" }, + { + "$ref": "#/definitions/global_options" + }, { "$ref": "#/definitions/generic_options" } diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf index 0aecf87f..b1b614e0 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -9,23 +9,35 @@ workflow INPUT_CHECK { samplesheet // file: /path/to/samplesheet.csv main: - SAMPLESHEET_CHECK ( samplesheet ) + ch_samplesheet = SAMPLESHEET_CHECK ( samplesheet, samplesheet.toUri() ) .csv .splitCsv ( header:true, sep:',' ) - .map { create_fastq_channel(it) } - .set { reads } + + reads = ch_samplesheet.map { create_fastq_channel(it) } + panels = ch_samplesheet.map { create_panels_channel(it) } emit: - reads // channel: [ val(meta), [ reads ] ] - versions = SAMPLESHEET_CHECK.out.versions // channel: [ versions.yml ] + reads // channel: [ val(meta), [ reads ] ] + panels // channel: [ val(meta), panel ] + + versions = SAMPLESHEET_CHECK.out.versions // channel: [ versions.yml ] +} + + +def get_meta(LinkedHashMap row) { + def meta = [:] + meta.id = row.sample + meta.single_end = row.single_end.toBoolean() + meta.design = row.design + meta.group = row.group + meta.assay = row.assay + return meta } // Function to get list of [ meta, [ fastq_1, fastq_2 ] ] def create_fastq_channel(LinkedHashMap row) { // create meta map - def meta = [:] - meta.id = row.sample - meta.single_end = row.single_end.toBoolean() + def meta = get_meta(row) // add path(s) of the fastq file(s) to the meta map def fastq_meta = [] @@ -42,3 +54,14 @@ def create_fastq_channel(LinkedHashMap row) { } return fastq_meta } + +// Function to get list of [ meta, panel ] +def create_panels_channel(LinkedHashMap row) { + def meta = get_meta(row) + + if (file(row.panel).exists()) { + return [ meta, file(row.panel) ] + } + + exit 1, "ERROR: Please check panel field: ${row.panel}: Could not find existing csv file." +} diff --git a/workflows/pixelator.nf b/workflows/pixelator.nf index 379c5eb7..6f8e9c6b 100644 --- a/workflows/pixelator.nf +++ b/workflows/pixelator.nf @@ -9,25 +9,22 @@ def summary_params = NfcoreSchema.paramsSummaryMap(workflow, params) // Validate input parameters WorkflowPixelator.initialise(params, log) -// TODO nf-core: Add all file path parameters for the pipeline to the list below // Check input path parameters to see if they exist -def checkPathParamList = [ params.input, params.multiqc_config, params.fasta ] +def checkPathParamList = [ params.input ] for (param in checkPathParamList) { if (param) { file(param, checkIfExists: true) } } // Check mandatory parameters if (params.input) { ch_input = file(params.input) } else { exit 1, 'Input samplesheet not specified!' } +// Inject the samplesheet SHA into the params object +params.samplesheet_sha = ch_input.bytes.digest('sha-1') + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CONFIG FILES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -ch_multiqc_config = Channel.fromPath("$projectDir/assets/multiqc_config.yml", checkIfExists: true) -ch_multiqc_custom_config = params.multiqc_config ? Channel.fromPath( params.multiqc_config, checkIfExists: true ) : Channel.empty() -ch_multiqc_logo = params.multiqc_logo ? Channel.fromPath( params.multiqc_logo, checkIfExists: true ) : Channel.empty() -ch_multiqc_custom_methods_description = params.multiqc_methods_description ? file(params.multiqc_methods_description, checkIfExists: true) : file("$projectDir/assets/methods_description_template.yml", checkIfExists: true) - /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IMPORT LOCAL MODULES/SUBWORKFLOWS @@ -37,8 +34,7 @@ ch_multiqc_custom_methods_description = params.multiqc_methods_description ? fil // // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules // -include { INPUT_CHECK } from '../subworkflows/local/input_check' - +include { INPUT_CHECK } from '../subworkflows/local/input_check' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IMPORT NF-CORE MODULES/SUBWORKFLOWS @@ -48,12 +44,30 @@ include { INPUT_CHECK } from '../subworkflows/local/input_check' // // MODULE: Installed directly from nf-core/modules // -include { FASTQC } from '../modules/nf-core/fastqc/main' -include { MULTIQC } from '../modules/nf-core/multiqc/main' include { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/custom/dumpsoftwareversions/main' +include { CAT_FASTQ } from '../modules/nf-core/cat/fastq/main' +/* +======================================================================================== + IMPORT CUSTOM MODULES/SUBWORKFLOWS +======================================================================================== +*/ + +// +// MODULE: Defined locally +// +include { PIXELATOR_CONCATENATE } from '../modules/local/pixelator/concatenate/main' +include { PIXELATOR_QC } from '../modules/local/pixelator/qc/main' +include { PIXELATOR_DEMUX } from '../modules/local/pixelator/demux/main' +include { PIXELATOR_COLLAPSE } from '../modules/local/pixelator/collapse/main' +include { PIXELATOR_GRAPH } from '../modules/local/pixelator/graph/main' +include { PIXELATOR_ANALYSIS } from '../modules/local/pixelator/analysis/main' +include { PIXELATOR_ANNOTATE } from '../modules/local/pixelator/annotate/main' +include { PIXELATOR_REPORT } from '../modules/local/pixelator/report/main' +include { RENAME_READS } from '../modules/local/rename_reads' +include { COLLECT_METADATA } from '../modules/local/collect_metadata' /* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +======================================================================================== RUN MAIN WORKFLOW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -62,51 +76,164 @@ include { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/custom/dumpsoft def multiqc_report = [] workflow PIXELATOR { - ch_versions = Channel.empty() + COLLECT_METADATA() + ch_versions = ch_versions.mix(COLLECT_METADATA.out.versions) + // // SUBWORKFLOW: Read in samplesheet, validate and stage input files // - INPUT_CHECK ( - ch_input - ) + ch_fastq = INPUT_CHECK ( ch_input ).reads + + ch_fastq_split = ch_fastq + .map { + meta, fastq -> + new_id = meta.id - ~/_T\d+/ + [ meta + [id: new_id], fastq ] + } + .groupTuple() + .branch { + meta, fastq -> + single : fastq.size() == 1 + return [ meta, fastq.flatten() ] + multiple: fastq.size() > 1 + return [ meta, fastq.flatten() ] + } + ch_versions = ch_versions.mix(INPUT_CHECK.out.versions) // - // MODULE: Run FastQC + // MODULE: Concatenate FastQ files from same sample if required // - FASTQC ( - INPUT_CHECK.out.reads - ) - ch_versions = ch_versions.mix(FASTQC.out.versions.first()) + ch_cat_fastq = CAT_FASTQ ( ch_fastq_split.multiple ) + .reads + .mix(ch_fastq_split.single) + + ch_versions = ch_versions.mix(CAT_FASTQ.out.versions.first().ifEmpty(null)) + + ch_reads = INPUT_CHECK.out.reads + ch_panels = INPUT_CHECK.out.panels + + // We need to rename to make all reads match the sample name, + // since pixelator extracts sample_names from read names + RENAME_READS ( ch_reads ) + ch_renamed_reads = RENAME_READS.out.reads + ch_versions = ch_versions.mix(RENAME_READS.out.versions.first()) + + ch_renamed_branched = ch_renamed_reads.branch { + single_end: it[0].single_end + paired_end: true + } + + PIXELATOR_CONCATENATE ( ch_renamed_reads ) + ch_merged = PIXELATOR_CONCATENATE.out.merged + ch_versions = ch_versions.mix(PIXELATOR_CONCATENATE.out.versions.first()) + + ch_input_reads = ch_merged + + PIXELATOR_QC( ch_input_reads ) + ch_qc = PIXELATOR_QC.out.processed + ch_versions = ch_versions.mix(PIXELATOR_QC.out.versions.first()) + + ch_qc_and_panel = ch_qc.join(ch_panels) + PIXELATOR_DEMUX ( ch_qc_and_panel ) + ch_demuxed = PIXELATOR_DEMUX.out.processed + ch_versions = ch_versions.mix(PIXELATOR_DEMUX.out.versions.first()) + + PIXELATOR_COLLAPSE ( ch_demuxed ) + ch_collapsed = PIXELATOR_COLLAPSE.out.collapsed + ch_versions = ch_versions.mix(PIXELATOR_COLLAPSE.out.versions.first()) + + PIXELATOR_GRAPH ( ch_collapsed ) + ch_clustered = PIXELATOR_GRAPH.out.edgelist + ch_versions = ch_versions.mix(PIXELATOR_GRAPH.out.versions.first()) + + ch_clustered_and_panel = ch_clustered.join(ch_panels) + + PIXELATOR_ANNOTATE ( ch_clustered_and_panel ) + ch_annotated = PIXELATOR_ANNOTATE.out.dataset + ch_versions = ch_versions.mix(PIXELATOR_ANNOTATE.out.versions.first()) + + if (!params.skip_analysis) { + PIXELATOR_ANALYSIS ( ch_annotated ) + ch_analysed = PIXELATOR_ANALYSIS.out.dataset + ch_versions = ch_versions.mix(PIXELATOR_ANALYSIS.out.versions.first()) + } + + if (!params.skip_report) { + // Do some heroic transformations to split the inputs into their stages + // and have all these "stage output" channel output their files list in the same order + // Note that we need to split inout per stage to stage those files in the right subdirs + // as expected by pixelator single-cell report + + ch_meta_col = ch_panels.map { meta, data -> [ meta.id, meta] } + ch_panels_col = ch_panels.map { meta, data -> [ meta.id, data] } + + ch_concatenate_col = PIXELATOR_CONCATENATE.out.report_json.mix(PIXELATOR_CONCATENATE.out.metadata) + .map { meta, data -> [ meta.id, data] }.groupTuple() + + ch_preqc_col = PIXELATOR_QC.out.preqc_report_json.mix(PIXELATOR_QC.out.preqc_metadata) + .map { meta, data -> [ meta.id, data] } + .groupTuple() + + ch_adapterqc_col = PIXELATOR_QC.out.adapterqc_report_json.mix(PIXELATOR_QC.out.adapterqc_metadata) + .map { meta, data -> [ meta.id, data] } + .groupTuple() + + ch_demux_col = PIXELATOR_DEMUX.out.report_json.mix(PIXELATOR_DEMUX.out.metadata) + .map { meta, data -> [ meta.id, data] } + .groupTuple() + + ch_collapse_col = PIXELATOR_COLLAPSE.out.report_json.mix(PIXELATOR_COLLAPSE.out.metadata) + .map { meta, data -> [ meta.id, data] } + .groupTuple() + + ch_cluster_col = PIXELATOR_GRAPH.out.all_results + .map { meta, data -> [meta.id, data] } + .groupTuple() + + ch_annotate_col = PIXELATOR_ANNOTATE.out.all_results + .map { meta, data -> [meta.id, data] } + .groupTuple() + + + ch_analysis_col = null + if (!params.skip_analysis) { + ch_analysis_col = PIXELATOR_ANALYSIS.out.all_results + .map { meta, data -> [meta.id, data] } + .groupTuple() + } else { + ch_analysis_col = ch_meta_col.map { id, meta -> [id, []]} + } + } CUSTOM_DUMPSOFTWAREVERSIONS ( ch_versions.unique().collectFile(name: 'collated_versions.yml') ) - // - // MODULE: MultiQC - // - workflow_summary = WorkflowPixelator.paramsSummaryMultiqc(workflow, summary_params) - ch_workflow_summary = Channel.value(workflow_summary) - - methods_description = WorkflowPixelator.methodsDescriptionText(workflow, ch_multiqc_custom_methods_description) - ch_methods_description = Channel.value(methods_description) - - ch_multiqc_files = Channel.empty() - ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) - ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml')) - ch_multiqc_files = ch_multiqc_files.mix(CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect()) - ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([])) - - MULTIQC ( - ch_multiqc_files.collect(), - ch_multiqc_config.toList(), - ch_multiqc_custom_config.toList(), - ch_multiqc_logo.toList() - ) - multiqc_report = MULTIQC.out.report.toList() + // // + // // MODULE: MultiQC + // // + // workflow_summary = WorkflowPixelator.paramsSummaryMultiqc(workflow, summary_params) + // ch_workflow_summary = Channel.value(workflow_summary) + + // methods_description = WorkflowPixelator.methodsDescriptionText(workflow, ch_multiqc_custom_methods_description) + // ch_methods_description = Channel.value(methods_description) + + // ch_multiqc_files = Channel.empty() + // ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) + // ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml')) + // ch_multiqc_files = ch_multiqc_files.mix(CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect()) + // ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([])) + + // MULTIQC ( + // ch_multiqc_files.collect(), + // ch_multiqc_config.toList(), + // ch_multiqc_custom_config.toList(), + // ch_multiqc_logo.toList() + // ) + // multiqc_report = MULTIQC.out.report.toList() } /* From 7a9b0c520f8aab59c8d3d2e87a6d4cbae5c77a69 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 13 Jun 2023 16:01:13 +0200 Subject: [PATCH 002/112] syncronize with pixelator 0.11.0 release --- conf/modules.config | 84 ++++---- docs/output.md | 180 +++++++++--------- modules/local/collect_metadata.nf | 2 +- .../{ => single-cell}/analysis/main.nf | 2 +- .../{ => single-cell}/analysis/meta.yaml | 2 +- .../{ => single-cell}/annotate/main.nf | 2 +- .../{ => single-cell}/annotate/meta.yaml | 4 +- .../{ => single-cell}/collapse/main.nf | 2 +- .../{ => single-cell}/collapse/meta.yaml | 2 +- .../{ => single-cell}/concatenate/main.nf | 2 +- .../{ => single-cell}/concatenate/meta.yaml | 2 +- .../pixelator/{ => single-cell}/demux/main.nf | 2 +- .../{ => single-cell}/demux/meta.yaml | 2 +- .../pixelator/{ => single-cell}/graph/main.nf | 2 +- .../{ => single-cell}/graph/meta.yaml | 2 +- .../pixelator/{ => single-cell}/qc/main.nf | 2 +- .../pixelator/{ => single-cell}/qc/meta.yaml | 2 +- .../{ => single-cell}/report/main.nf | 2 +- modules/local/samplesheet_check.nf | 3 +- nextflow.config | 27 +-- nextflow_schema.json | 165 +++++++--------- workflows/pixelator.nf | 89 ++++++--- 22 files changed, 293 insertions(+), 289 deletions(-) rename modules/local/pixelator/{ => single-cell}/analysis/main.nf (94%) rename modules/local/pixelator/{ => single-cell}/analysis/meta.yaml (97%) rename modules/local/pixelator/{ => single-cell}/annotate/main.nf (95%) rename modules/local/pixelator/{ => single-cell}/annotate/meta.yaml (92%) rename modules/local/pixelator/{ => single-cell}/collapse/main.nf (95%) rename modules/local/pixelator/{ => single-cell}/collapse/meta.yaml (97%) rename modules/local/pixelator/{ => single-cell}/concatenate/main.nf (94%) rename modules/local/pixelator/{ => single-cell}/concatenate/meta.yaml (97%) rename modules/local/pixelator/{ => single-cell}/demux/main.nf (95%) rename modules/local/pixelator/{ => single-cell}/demux/meta.yaml (97%) rename modules/local/pixelator/{ => single-cell}/graph/main.nf (95%) rename modules/local/pixelator/{ => single-cell}/graph/meta.yaml (97%) rename modules/local/pixelator/{ => single-cell}/qc/main.nf (97%) rename modules/local/pixelator/{ => single-cell}/qc/meta.yaml (98%) rename modules/local/pixelator/{ => single-cell}/report/main.nf (95%) diff --git a/conf/modules.config b/conf/modules.config index b7ac545a..c3a6eed7 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -10,20 +10,6 @@ ---------------------------------------------------------------------------------------- */ -def get_pixelator_container(params) { - if (params.pixelator_container) { - return params.pixelator_container - } - if (params.pixelator_container_source == 'ghcr' && params.pixelator_tag) { - return "ghcr.io/pixelgentechnologies/pixelator:${params.pixelator_tag}" - } - if (params.pixelator_container_source == 'aws-ecr' && params.pixelator_tag) { - return "890888997283.dkr.ecr.eu-north-1.amazonaws.com/pixelator:${params.pixelator_tag}" - } - - return null -} - process { @@ -39,14 +25,24 @@ process { mode: params.publish_dir_mode, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] - - // Important: Use closure to delay evaluation until all params are available - container = { get_pixelator_container(params) } } - withName: "PIXELATOR.*" { + withName: 'SAMPLESHEET_CHECK|COLLECT_METADATA|PIXELATOR_*' { ext.singularity_pull_docker_container = true + // Use this to override all usages of the pixelator container + // container = { get_pixelator_container(params) } + } + + withName: COLLECT_METADATA { + publishDir = [ + path: { "${params.outdir}/pipeline_info" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + + withName: "PIXELATOR.*" { publishDir = [ [ path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, @@ -59,9 +55,6 @@ process { pattern: "*.log" ] ] - - // Important: Use closure to delay evaluation until all params are available - container = { get_pixelator_container(params) } } withName: RENAME_READS { @@ -71,6 +64,10 @@ process { ] } + + // use explicit params.my_option != null checks to avoid issues with 0 evaluating false + // some pixelator flags do accept zero as a value + withName: PIXELATOR_CONCATENATE { ext.args = { [ @@ -80,25 +77,27 @@ process { } withName: PIXELATOR_QC { + // Options for pixelator preqc ext.args = { [ "--design ${meta.design}", - params.trim_front ? "--trim-front ${params.trim_front}": '', - params.trim_tail ? "--trim-tail ${params.trim_tail}": '', - params.max_length ? "--max-length ${params.max_length}": '', - params.min_length ? "--min-length ${params.min_length}": '', + (params.trim_front != null)? "--trim-front ${params.trim_front}": '', + (params.trim_tail != null) ? "--trim-tail ${params.trim_tail}": '', + (params.max_length != null) ? "--max-length ${params.max_length}": '', + (params.min_length != null) ? "--min-length ${params.min_length}": '', (params.max_n_bases != null) ? "--max-n-bases ${params.max_n_bases}": '', - params.avg_qual ? "--avg-qual ${params.avg_qual}": '', - params.dedup ? "--dedup ${params.dedup}": '', - params.remove_polyg ? "--remove_polyg ${params.remove_polyg}": '', + (params.avg_qual != null)? "--avg-qual ${params.avg_qual}": '', + params.dedup ? "--dedup": '', + params.remove_polyg ? "--remove_polyg": '', ].join(' ').trim() } + // Options for pixelator adapterqc ext.args2 = { [ "--design ${meta.design}", params.adapterqc_mismatches ? "--mismatches ${params.adapterqc_mismatches}": '', - params.pbs1 ? "--pbs1 ${params.pbs1}": '', - params.pbs2 ? "--pbs1 ${params.pbs2}": '', + (params.pbs1 != null)? "--pbs1 ${params.pbs1}": '', + (params.pbs2 != null) ? "--pbs2 ${params.pbs2}": '', ].join(' ').trim() } } @@ -107,16 +106,17 @@ process { ext.args = { [ "--design ${meta.design}", - params.demux_mismatches ? "--mismatches ${params.demux_mismatches}": '', - params.demux_min_length ? "--mismatches ${params.demux_min_length}": '', - params.anchored ? "--anchored ${params.anchored}": '', - params.rev_complement ? "--rev-complement ${params.rev_complement}": '', + (params.demux_mismatches != null) ? "--mismatches ${params.demux_mismatches}": '', + (params.demux_min_length != null) ? "--mismatches ${params.demux_min_length}": '', + (params.anchored != null) ? "--anchored ${params.anchored}": '', + (params.rev_complement != null) ? "--rev-complement ${params.rev_complement}": '', ].join(' ').trim() } } withName: PIXELATOR_COLLAPSE { ext.args = [ + params.markers_ignore ? "--markers_ignore ${markers_ignore}": params.algorithm ? "--algorithm ${params.algorithm}": '', params.upia_start ? "--upi1-start ${params.upia_start}": '', params.upia_end ? "--upi1-end ${params.upia_end}": '', @@ -135,7 +135,7 @@ process { withName: PIXELATOR_GRAPH { ext.args = [ - params.multiplet_recovery ? "--multiplet-recovery ${params.multiplet_recovery}" : '', + (params.multiplet_recovery && params.multiplet_recovery != "none") ? "--multiplet-recovery ${params.multiplet_recovery}" : '', params.fast_greedy_fraction ? "--fast-greedy-fraction ${params.fast_greedy_fraction}": '', params.fast_greedy_cutoff ? "--fast-greedy-cutoff ${params.fast_greedy_cutoff}": '', params.leiden_iterations ? "--leiden-iterations ${params.leiden_iterations}": '', @@ -145,8 +145,8 @@ process { withName: PIXELATOR_ANNOTATE { ext.args = [ - params.min_size ? "--min-size ${params.min_size}" : '', - params.max_size ? "--max-size ${params.max_size}" : '', + (params.min_size != null) ? "--min-size ${params.min_size}" : '', + (params.max_size != null) ? "--max-size ${params.max_size}" : '', params.dynamic_filter ? "--dynamic-filter ${params.dynamic_filter}" : '', params.cell_type_assignments ? "--cell-type-assignments" : '', params.majority_vote ? "--majority-vote" : '', @@ -163,16 +163,6 @@ process { ].join(' ').trim() } - withName: COLLECT_METADATA { - publishDir = [ - path: { "${params.outdir}/pipeline_info" }, - mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - - // Important: Use closure to delay evaluation until all params are available - container = { get_pixelator_container(params) } - } withName: CUSTOM_DUMPSOFTWAREVERSIONS { publishDir = [ diff --git a/docs/output.md b/docs/output.md index 6091030d..3bed53c0 100644 --- a/docs/output.md +++ b/docs/output.md @@ -12,79 +12,97 @@ The directories listed below will be created in the results directory after the The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes data using multiple subcommands of the [`pixelator`](https://github.com/PixelgenTechnologies/pixelator) tool. - [`pixelator concatenate`](#pixelator-concatenate)(Optional) - Concatenate paired end data -- [`pixelator preqc`](#pixelator-preqc)) - Read QC and filtering -- [`pixelator adapterqc`](#pixelator-adapterqc)) - Check correctness/presence of PBS1/2 sequences -- [`pixelator demux`](#pixelator-demux)) - Assign a marker (barcode) to each read -- [`pixelator collapse`](#pixelator-collapse)) - Error correction, duplicate removal, compute read counts -- [`pixelator cluster`](#pixelator-cluster)) - Compute undirected graphs and basic size filtering -- [`pixelator analysis`](#pixelator-analysis)) - Downstream analysis for each cell -- [`pixelator annotate`](#pixelator-annotate)) - Filter, annotate and call cells on samples -- [`pixelator aggregate`](#pixelator-aggregate)) - Aggregate results -- [`pixelator report`](#pixelator-report)) - Report generation +- [`pixelator preqc`](#pixelator-preqc) - Read QC and filtering +- [`pixelator adapterqc`](#pixelator-adapterqc) - Check for correctness of PBS1/2 sequences +- [`pixelator demux`](#pixelator-demux) - Assign a marker (barcode) to each read +- [`pixelator collapse`](#pixelator-collapse) - Error correction, duplicate removal, compute read counts +- [`pixelator cluster`](#pixelator-cluster) - Compute undirected graphs and basic size filtering +- [`pixelator analysis`](#pixelator-analysis) - Downstream analysis for each cell +- [`pixelator annotate`](#pixelator-annotate) - Filter, annotate and call cells on samples +- [`pixelator aggregate`](#pixelator-aggregate) - Aggregate results +- [`pixelator report`](#pixelator-report) - Report generation ### pixelator concatenate +// TODO: High level description of concatenate step and output files +
Output files - `pixelator` - `concatenate` - - `*merged.fastq.gz`: Concatenated R1 and R2 reads. - - `/logs` - `*pixelator-concatenate.log`: Pixelator concatenate log output. -
+ - `.merged.fastq.gz`: + Combine R1 and R2 reads into full amplicon reads and calculate Q30 scores for the amplicon regions. + - `.report.json`: Q30 metrics of the amplicon. + - `.meta.json`: Command invocation metadata. +- `logs` + - *pixelator-concatenate.log`: pixelator log output. -### pixelator preqc + -
-Output files +### pixelator qc -- `pixelator` - - `preqc` - - `*processed.fastq.gz`: Processed reads. - - `*failed.fastq.gz`: Discarded reads. - - `*report.html`: Fastp html report. - - `*report.json`: Fastp json report. - - `/logs` - `*pixelator-preqc.log`: Pixelator preqc log output. -
- -### pixelator adapterqc +// TODO: High level description of QC step and output files
Output files - `pixelator` + - `preqc` + - `.processed.fastq.gz`: Processed reads. + - `.failed.fastq.gz`: Discarded reads. + - `.report.json`: Fastp json report. + - `.meta.json`: Command invocation metadata. - `adapterqc` - - `*processed.fastq.gz`: Processed reads. - - `*failed.fastq.gz`: Discarded reads. - - `*report.json`: Cutadapt json report. - - `/logs` - `*pixelator-adapterqc.log`: Pixelator adapterqc log output. -
+ - `.processed.fastq.gz`: Processed reads. + - `.failed.fastq.gz`: Discarded reads. + - `.report.json`: Cutadapt json report. + - `.meta.json`: Command invocation metadata. + +- `logs` - `*pixelator-preqc.log`: pixelator log output. + + ### pixelator demux +// TODO: High level description of demux step and output files +
Output files - `pixelator` - - `adapterqc` - - `*processed-*-.fastq.gz`: Reads demultiplexed per antibody. - - `*report.json`: Cutadapt json report. - - `/logs` - `*pixelator-demultiplex.log`: Pixelator adapterqc log output. -
+ - `demux` + - `.processed-.fastq.gz`: Reads demultiplexed per antibody. + - `.failed.fastq.gz`: Discarded reads that do not match an antibody barcode. + - `.report.json`: Cutadapt json report. + - `.meta.json`: Command invocation metadata. + +- `logs` + - `*pixelator-demultiplex.log`: pixelator log output. + + ### pixelator collapse +// TODO: High level description of collapse step and output files +
Output files - `pixelator` - `adapterqc` - - `*.collapse.csv`: Edge list matrix. - - `*collapse.json`: Statistics. - - `/logs` - `*pixelator-collapse.log`: Pixelator collapse log output. -
+ - `.collapsed.csv.gz`: Edgelist of the graph. + - `.report.json`: Statistics for the collapse step. + - `.meta.json`: Command invocation metadata. + +- `logs` + - `*pixelator-collapse.log`: pixelator log output. + + + +### pixelator graph -### pixelator cluster +// TODO: High level description of graph step and output files
Output files @@ -92,77 +110,69 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d - `pixelator` - `cluster` - `.components_recovered.csv` - - `.data_summary.png` - - `.raw_anndata.h5ad` - - `.raw_antibody_metrics.csv` - - `.raw_antibody_metrics.png` - - `.raw_components_antibody.csv` - - `.raw_components_dist.png` - - `.raw_components_metrics.csv` - - `.raw_pixel_data.csv` + - `.edgelist.csv.gz` + - `.raw_edgelist.csv.gz` + - `.meta.json`: Command invocation metadata. - `.report.json` - - `/logs` - `*pixelator-cluster.log`: Pixelator cluster log output. -
+ - `*.meta.json`: Command invocation metadata. + +- `logs` + - `*pixelator-cluster.log`: pixelator log output. + + ### pixelator annotate +// TODO: High level description of annotate step and output files +
Output files - `pixelator` - `annotate` - - `.data_summary.png` - - `.anndata.h5ad` - - `.raw_anndata.h5ad` - - `.antibody_metrics.csv` - - `.antibody_metrics.png` - - `.components_antibody.csv` - - `.components_dist.png` - - `.components_metrics.csv` - - `.pixel_data.csv` - - `.report.json` - - `/logs` - `*pixelator-annotate.log`: Pixelator cluster log output. -
+ - `.dataset.pxl` + - `.meta.json`: Command invocation metadata. + - `.rank_vs_size.png` + - `.raw_components_metrics.csv` + - `.report.json` + - `.umap.png` + +- `logs` + - `*pixelator-annotate.log`: pixelator log output. + ### pixelator analysis +// TODO: High level description of analysis step and output files +
Output files - `pixelator` - `analysis` - - `.anndata.h5ad` - - `.polarization_boxplot.png` - - `.polarization_heatmap.png` - - `.polarization_matrix.csv` - - `.polarization_scores.csv` - - `.polarization_matrix.csv` - - `.report.json` - - `/logs` - `*pixelator-analysis.log`: Pixelator analysis log output. - -
- -### pixelator aggregate + - `.dataset.pxl` + - `.meta.json`: Command invocation metadata. + - `.report.json` -
-Output files +- `logs` + - `*pixelator-analysis.log`: pixelator log output. -- `pixelator` - - `aggregate` - - `.merged_anndata.h5ad`: Anndata object with aggregated data of multiple samples - - `/logs` - `*pixelator-report.log`: Pixelator report log output. -
+ ### pixelator report +// TODO: High level description of report step and output files +
Output files - `pixelator` - - `report/` - - `report.html`: - - `/logs` - `*pixelator-report.log`: Pixelator report log output. -
+ - `report` + - `_report.html` +- `logs` + - `*pixelator-report.log`: Pixelator report log output. + + ### Pipeline information diff --git a/modules/local/collect_metadata.nf b/modules/local/collect_metadata.nf index 875c36c6..97ac5404 100644 --- a/modules/local/collect_metadata.nf +++ b/modules/local/collect_metadata.nf @@ -10,7 +10,7 @@ process COLLECT_METADATA { cache false conda "local::pixelator=0.10.0" - container 'ghcr.io/pixelgentechnologies/pixelator:0.10.0' + container 'ghcr.io/pixelgentechnologies/pixelator:0.11.0' input: diff --git a/modules/local/pixelator/analysis/main.nf b/modules/local/pixelator/single-cell/analysis/main.nf similarity index 94% rename from modules/local/pixelator/analysis/main.nf rename to modules/local/pixelator/single-cell/analysis/main.nf index ea8b3d74..68103232 100644 --- a/modules/local/pixelator/analysis/main.nf +++ b/modules/local/pixelator/single-cell/analysis/main.nf @@ -4,7 +4,7 @@ process PIXELATOR_ANALYSIS { label 'process_medium' conda "local::pixelator=0.10.0" - container "ghcr.io/pixelgentechnologies/pixelator:0.10.0" + container "ghcr.io/pixelgentechnologies/pixelator:0.11.0" input: tuple val(meta), path(h5ad) diff --git a/modules/local/pixelator/analysis/meta.yaml b/modules/local/pixelator/single-cell/analysis/meta.yaml similarity index 97% rename from modules/local/pixelator/analysis/meta.yaml rename to modules/local/pixelator/single-cell/analysis/meta.yaml index 7cadf9c8..404317a4 100644 --- a/modules/local/pixelator/analysis/meta.yaml +++ b/modules/local/pixelator/single-cell/analysis/meta.yaml @@ -9,7 +9,7 @@ tools: Software package to process sequencing data into Molecular Pixelation data. homepage: https://github.com/PixelgenTechnologies/pixelator documentation: https://github.com/PixelgenTechnologies/pixelator - arxiv: null + doi: 10.1101/2023.06.05.543770 licence: ["MIT"] input: diff --git a/modules/local/pixelator/annotate/main.nf b/modules/local/pixelator/single-cell/annotate/main.nf similarity index 95% rename from modules/local/pixelator/annotate/main.nf rename to modules/local/pixelator/single-cell/annotate/main.nf index ba74f704..3f488c0c 100644 --- a/modules/local/pixelator/annotate/main.nf +++ b/modules/local/pixelator/single-cell/annotate/main.nf @@ -4,7 +4,7 @@ process PIXELATOR_ANNOTATE { label 'process_medium' conda "local::pixelator=0.10.0" - container "ghcr.io/pixelgentechnologies/pixelator:0.10.0" + container "ghcr.io/pixelgentechnologies/pixelator:0.11.0" input: tuple val(meta), path(dataset), path(panel) diff --git a/modules/local/pixelator/annotate/meta.yaml b/modules/local/pixelator/single-cell/annotate/meta.yaml similarity index 92% rename from modules/local/pixelator/annotate/meta.yaml rename to modules/local/pixelator/single-cell/annotate/meta.yaml index 1b4c44d1..b9c0c7aa 100644 --- a/modules/local/pixelator/annotate/meta.yaml +++ b/modules/local/pixelator/single-cell/annotate/meta.yaml @@ -9,7 +9,7 @@ tools: Software package to process sequencing data into Molecular Pixelation data. homepage: https://github.com/PixelgenTechnologies/pixelator documentation: https://github.com/PixelgenTechnologies/pixelator - arxiv: null + doi: 10.1101/2023.06.05.543770 licence: ["MIT"] input: @@ -47,7 +47,7 @@ output: - all_results: type: file - description: All output files from the pixelator cluster Command + description: All output files from the pixelator single-cell cluster command pattern: "*" - log: diff --git a/modules/local/pixelator/collapse/main.nf b/modules/local/pixelator/single-cell/collapse/main.nf similarity index 95% rename from modules/local/pixelator/collapse/main.nf rename to modules/local/pixelator/single-cell/collapse/main.nf index 511d8746..12a03d95 100644 --- a/modules/local/pixelator/collapse/main.nf +++ b/modules/local/pixelator/single-cell/collapse/main.nf @@ -7,7 +7,7 @@ process PIXELATOR_COLLAPSE { conda "local::pixelator=0.10.0" - container "ghcr.io/pixelgentechnologies/pixelator:0.10.0" + container "ghcr.io/pixelgentechnologies/pixelator:0.11.0" input: tuple val(meta), path(reads) diff --git a/modules/local/pixelator/collapse/meta.yaml b/modules/local/pixelator/single-cell/collapse/meta.yaml similarity index 97% rename from modules/local/pixelator/collapse/meta.yaml rename to modules/local/pixelator/single-cell/collapse/meta.yaml index b5c5ac03..5579089a 100644 --- a/modules/local/pixelator/collapse/meta.yaml +++ b/modules/local/pixelator/single-cell/collapse/meta.yaml @@ -10,7 +10,7 @@ tools: Software package to process sequencing data into Molecular Pixelation data. homepage: https://github.com/PixelgenTechnologies/pixelator documentation: https://github.com/PixelgenTechnologies/pixelator - arxiv: null + doi: 10.1101/2023.06.05.543770 licence: ["MIT"] input: diff --git a/modules/local/pixelator/concatenate/main.nf b/modules/local/pixelator/single-cell/concatenate/main.nf similarity index 94% rename from modules/local/pixelator/concatenate/main.nf rename to modules/local/pixelator/single-cell/concatenate/main.nf index 3edbb285..f2a417aa 100644 --- a/modules/local/pixelator/concatenate/main.nf +++ b/modules/local/pixelator/single-cell/concatenate/main.nf @@ -7,7 +7,7 @@ process PIXELATOR_CONCATENATE { conda "local::pixelator=0.10.0" - container "ghcr.io/pixelgentechnologies/pixelator:0.10.0" + container "ghcr.io/pixelgentechnologies/pixelator:0.11.0" input: tuple val(meta), path(reads) diff --git a/modules/local/pixelator/concatenate/meta.yaml b/modules/local/pixelator/single-cell/concatenate/meta.yaml similarity index 97% rename from modules/local/pixelator/concatenate/meta.yaml rename to modules/local/pixelator/single-cell/concatenate/meta.yaml index 6db8f65f..7e4920a3 100644 --- a/modules/local/pixelator/concatenate/meta.yaml +++ b/modules/local/pixelator/single-cell/concatenate/meta.yaml @@ -8,7 +8,7 @@ tools: Software package to process sequencing data into Molecular Pixelation data. homepage: https://github.com/PixelgenTechnologies/pixelator documentation: https://github.com/PixelgenTechnologies/pixelator - arxiv: null + doi: 10.1101/2023.06.05.543770 licence: ["MIT"] input: diff --git a/modules/local/pixelator/demux/main.nf b/modules/local/pixelator/single-cell/demux/main.nf similarity index 95% rename from modules/local/pixelator/demux/main.nf rename to modules/local/pixelator/single-cell/demux/main.nf index fe3df319..996d3076 100644 --- a/modules/local/pixelator/demux/main.nf +++ b/modules/local/pixelator/single-cell/demux/main.nf @@ -6,7 +6,7 @@ process PIXELATOR_DEMUX { conda "local::pixelator=0.10.0" - container "ghcr.io/pixelgentechnologies/pixelator:0.10.0" + container "ghcr.io/pixelgentechnologies/pixelator:0.11.0" input: tuple val(meta), path(reads), path(antibody_panel) diff --git a/modules/local/pixelator/demux/meta.yaml b/modules/local/pixelator/single-cell/demux/meta.yaml similarity index 97% rename from modules/local/pixelator/demux/meta.yaml rename to modules/local/pixelator/single-cell/demux/meta.yaml index 2f150d01..6b6b837b 100644 --- a/modules/local/pixelator/demux/meta.yaml +++ b/modules/local/pixelator/single-cell/demux/meta.yaml @@ -10,7 +10,7 @@ tools: Software package to process sequencing data into Molecular Pixelation data. homepage: https://github.com/PixelgenTechnologies/pixelator documentation: https://github.com/PixelgenTechnologies/pixelator - arxiv: null + doi: 10.1101/2023.06.05.543770 licence: ["MIT"] input: diff --git a/modules/local/pixelator/graph/main.nf b/modules/local/pixelator/single-cell/graph/main.nf similarity index 95% rename from modules/local/pixelator/graph/main.nf rename to modules/local/pixelator/single-cell/graph/main.nf index 0b1d9b08..914de8b0 100644 --- a/modules/local/pixelator/graph/main.nf +++ b/modules/local/pixelator/single-cell/graph/main.nf @@ -6,7 +6,7 @@ process PIXELATOR_GRAPH { conda "local::pixelator=0.10.0" - container "ghcr.io/pixelgentechnologies/pixelator:0.10.0" + container "ghcr.io/pixelgentechnologies/pixelator:0.11.0" input: tuple val(meta), path(edge_list) diff --git a/modules/local/pixelator/graph/meta.yaml b/modules/local/pixelator/single-cell/graph/meta.yaml similarity index 97% rename from modules/local/pixelator/graph/meta.yaml rename to modules/local/pixelator/single-cell/graph/meta.yaml index 500df785..77736b15 100644 --- a/modules/local/pixelator/graph/meta.yaml +++ b/modules/local/pixelator/single-cell/graph/meta.yaml @@ -10,7 +10,7 @@ tools: Software package to process sequencing data into Molecular Pixelation data. homepage: https://github.com/PixelgenTechnologies/pixelator documentation: https://github.com/PixelgenTechnologies/pixelator - arxiv: null + doi: 10.1101/2023.06.05.543770 licence: ["MIT"] input: diff --git a/modules/local/pixelator/qc/main.nf b/modules/local/pixelator/single-cell/qc/main.nf similarity index 97% rename from modules/local/pixelator/qc/main.nf rename to modules/local/pixelator/single-cell/qc/main.nf index d44c7944..5ba0cb3a 100644 --- a/modules/local/pixelator/qc/main.nf +++ b/modules/local/pixelator/single-cell/qc/main.nf @@ -6,7 +6,7 @@ process PIXELATOR_QC { conda "local::pixelator=0.10.0" // TODO: make pixelator available on galaxyproject and quay.io support - container "ghcr.io/pixelgentechnologies/pixelator:0.10.0" + container "ghcr.io/pixelgentechnologies/pixelator:0.11.0" input: tuple val(meta), path(reads) diff --git a/modules/local/pixelator/qc/meta.yaml b/modules/local/pixelator/single-cell/qc/meta.yaml similarity index 98% rename from modules/local/pixelator/qc/meta.yaml rename to modules/local/pixelator/single-cell/qc/meta.yaml index 0b03923a..bdaaed6f 100644 --- a/modules/local/pixelator/qc/meta.yaml +++ b/modules/local/pixelator/single-cell/qc/meta.yaml @@ -11,7 +11,7 @@ tools: Software package to process sequencing data into Molecular Pixelation data. homepage: https://github.com/PixelgenTechnologies/pixelator documentation: https://github.com/PixelgenTechnologies/pixelator - arxiv: null + doi: 10.1101/2023.06.05.543770 licence: ["MIT"] input: diff --git a/modules/local/pixelator/report/main.nf b/modules/local/pixelator/single-cell/report/main.nf similarity index 95% rename from modules/local/pixelator/report/main.nf rename to modules/local/pixelator/single-cell/report/main.nf index 1f350a83..96d585f9 100644 --- a/modules/local/pixelator/report/main.nf +++ b/modules/local/pixelator/single-cell/report/main.nf @@ -6,7 +6,7 @@ process PIXELATOR_REPORT { conda "local::pixelator=0.10.0" - container "ghcr.io/pixelgentechnologies/pixelator:0.10.0" + container "ghcr.io/pixelgentechnologies/pixelator:0.11.0" input: val meta diff --git a/modules/local/samplesheet_check.nf b/modules/local/samplesheet_check.nf index b91e67cb..7c9f9780 100644 --- a/modules/local/samplesheet_check.nf +++ b/modules/local/samplesheet_check.nf @@ -2,8 +2,7 @@ process SAMPLESHEET_CHECK { tag "$samplesheet" label 'process_single' - // conda "local::pixelator=0.10.0" - // container "ghcr.io/pixelgentechnologies/pixelator:0.10.0" + container "ghcr.io/pixelgentechnologies/pixelator:0.11.0" input: path samplesheet diff --git a/nextflow.config b/nextflow.config index fac0173d..66a435c6 100644 --- a/nextflow.config +++ b/nextflow.config @@ -14,12 +14,7 @@ plugins { params { // Input options - input = null - - // General options - pixelator_tag = "dev" - pixelator_container_source = "ghcr" - pixelator_container = null + input = null // Preqc options trim_front = 0 @@ -39,10 +34,11 @@ params { //demux options demux_mismatches = 0.1 demux_min_length = null - anchored = true - rev_complement = false + anchored = null + rev_complement = null //collapse options + markers_ignore = null algorithm = 'adjacency' upia_start = null upia_end = null @@ -57,8 +53,8 @@ params { collapse_min_count = 2 use_counts = false - // cluster options - multiplet_recovery = 'leiden' + // graph options + multiplet_recovery = 'none' fast_greedy_fraction = 0.5 fast_greedy_cutoff = 5000 leiden_iterations = 10 @@ -70,7 +66,7 @@ params { dynamic_filter = 'min' cell_type_assignments = false majority_vote = false - aggregate_calling = true + aggregate_calling = false // analysis options compute_polarization = true @@ -83,9 +79,6 @@ params { skip_report = false skip_analysis = false - // report options - report_name = "report" - // Boilerplate options outdir = null tracedir = "${params.outdir}/pipeline_info" @@ -99,7 +92,7 @@ params { version = false validate_params = true show_hidden_params = false - schema_ignore_params = '' + schema_ignore_params = "" // Config options @@ -272,12 +265,12 @@ dag { manifest { name = 'nf-core/pixelator' author = """Pixelgen Technologies AB""" - homePage = 'https://github.com/nf-core/pixelator' + homePage = 'https://github.com/pixelgentechnologies/pixelator' description = """Pipeline for analysis of Molecular Pixelation assays""" mainScript = 'main.nf' nextflowVersion = '!>=22.10.1' version = '1.0.0dev' - doi = '' + doi = '10.1101/2023.06.05.543770' } // Load modules.config for DSL2 module specific options diff --git a/nextflow_schema.json b/nextflow_schema.json index 74bd75f0..4be5dca5 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -56,16 +56,19 @@ "max_length": { "fa_icon": "fas up-right-and-down-left-from-center", "type": "integer", - "description": "The maximum length (bases) of a read (longer reads will be trimmed off). If you set this argument it will overrrule the value from the chosen design" + "description": "The maximum length of a read", + "help_text": "Reads longer then given length will be trimmed to the given length. If you set this argument it will overrule the value from the chosen design" }, "min_length": { "fa_icon": "fas down-left-and-up-right-to-center", "type": "integer", - "description": "The minimum length (bases) of a read (shorter reads will be discarded). If you set this argument it will overrrule the value from the chosen design." + "description": "The minimum length (bases) of a read", + "help_text": "Reads shorter then given length will be discarded. If you set this argument it will overrule the value from the chosen design." }, "max_n_bases": { "fa_icon": "fas n", "description": "The maximum number of Ns allowed in a read", + "help_text": "The default value of 0 means any reads with N in it will be filtered out", "type": "integer", "default": 0 }, @@ -96,15 +99,21 @@ "fa_icon": "fas not-equal", "description": "The number of mismatches allowed (in percentage) [default: 0.1; 0.0<=x<=0.9]", "type": "number", - "default": 0.1 + "default": 0.1, + "minimum": 0.0, + "maximum": 0.9 }, "pbs1": { - "description": "The PBS1 sequence that must be present in the reads. If you set this argument it will overrrule the value from the chosen design", - "type": "string" + "description": "The PBS1 sequence that must be present in the reads.", + "help_text": "If you set this argument it will overrule the default value from the chosen design", + "type": "string", + "hidden": true }, "pbs2": { - "description": "The PBS2 sequence that must be present in the reads. If you set this argument it will overrrule the value from the chosen design", - "type": ["string", "null"] + "description": "The PBS2 sequence that must be present in the reads.", + "help_text": "If you set this argument it will overrule the default value from the chosen design", + "type": "string", + "hidden": true } } }, @@ -113,7 +122,7 @@ "properties": { "demux_mismatches": { "fa_icon": "fas not-equal", - "description": "The number of mismatches allowed (in percentage) [default: 0.1; 0.0<=x<=0.9]", + "description": "The number of mismatches allowed (as a fraction)", "type": "number", "default": 0.1, "minimum": 0.0, @@ -121,29 +130,37 @@ }, "demux_min_length": { "fa_icon": "fas down-left-and-up-right-to-center", - "description": "The minimum length of the barcode that must overlap when matching. If you set this argument it will overrrule the value from the chosen design", - "type": ["integer", "null"] + "description": "The minimum length of the barcode that must overlap when matching", + "help_text": "If you set this argument it will overrule the value from the chosen design", + "type": "integer" }, "anchored": { "fa_icon": "fas anchor", - "description": "Enforce the barcodes to be anchored (at the end of the read)", + "description": "Enforce the barcodes to be anchored (at the end of the read).", + "help_text": "If you set this argument it will overrule the value from the chosen design", "type": "boolean", - "default": true + "hidden": true }, "rev_complement": { "fa_icon": "fas right-left", - "description": "Use the reverse complement of the barcodes sequences", + "description": "Use the reverse complement of the barcodes sequences. Set to null to use the default specified by the design.", "type": "boolean", - "default": false + "hidden": true } } }, "collapse_options": { "title": "Collapse options", "properties": { + "markers_ignore": { + "fa-icon": "fas fa-list", + "description": "A list of comma separated antibodies to discard", + "type": "string", + "pattern": "(\\S+)?(,\\S+)*" + }, "algorithm": { "fa_icon": "fas code-fork", - "description": "The algorithm to use for collapsing (adjacency will peform error correction using the number of mismatches given) [default: adjacency]", + "description": "The algorithm to use for collapsing (adjacency will peform error correction using the number of mismatches given)", "default": "adjacency", "enum": ["adjacency", "unique"], "type": "string" @@ -151,69 +168,77 @@ "upia_start": { "fa_icon": "fas backward-step", "description": "The start position (0-based) of UPIA. If you set this argument it will overrrule the value from the chosen design", - "type": "integer" + "type": "integer", + "hidden": true }, "upia_end": { "fa_icon": "fas forward-step", "description": "The end position (1-based) of UPIA. If you set this argument it will overrrule the value from the chosen design", - "type": "integer" + "type": "integer", + "hidden": true }, "upib_start": { "fa_icon": "fas backward-step", "description": "The start position (0-based) of UPIB. If you set this argument it will overrrule the value from the chosen design", - "type": "integer" + "type": "integer", + "hidden": true }, "upib_end": { "fa_icon": "fas forward-step", "description": "The end position (1-based) of UPIB. If you set this argument it will overrrule the value from the chosen design", - "type": "integer" + "type": "integer", + "hidden": true }, "umia_start": { "fa_icon": "fas backward-step", "description": "The start position (0-based) of UMIA (disabled by default). If you set this argument it will overrrule the value from the chosen design", - "type": "integer" + "type": "integer", + "hidden": true }, "umia_end": { "fa_icon": "fas forward-step", "description": "The end position (1-based) of UMIA (disabled by default). If you set this argument it will overrrule the value from the chosen design", - "type": "integer" + "type": "integer", + "hidden": true }, "umib_start": { "fa_icon": "fas forward-step", "description": "The start position (0-based) of UMIB (disabled by default). If you set this argument it will overrrule the value from the chosen design", - "type": "integer" + "type": "integer", + "hidden": true }, "umib_end": { "fa_icon": "fas backward-step", "description": "The end position (1-based) of UMIB (disabled by default). If you set this argument it will overrrule the value from the chosen design", - "type": "integer" + "type": "integer", + "hidden": true }, "neighbours": { "fa_icon": "fas circle-nodes", - "description": "The number of neighbours to use when searching for similar sequences (adjacency) This number depends on the sequence depth and the ratio of erronous molecules expected. A high value can make the algoritthm slower. [default: 60; 1<=x<=250]", + "description": "The number of neighbours to use when searching for similar sequences (adjacency) This number depends on the sequence depth and the ratio of erroneous molecules expected. A high value can make the algorithm slower.", "default": 60, "minimum": 1, "maximum": 250, - "type": "integer" + "type": "integer", + "hidden": true }, "collapse_mismatches": { "fa_icon": "fas not-equal", - "description": "The number of mismatches allowed when collapsing (adjacency) [default: 2; 0<=x<=5]", + "description": "The number of mismatches allowed when collapsing (adjacency)", + "type": "integer", "default": 2, "minimum": 0, - "maximum": 5, - "type": "integer" + "maximum": 5 }, "collapse_min_count": { "fa_icon": "fas more-than-equal", - "description": "Discard molecules with with a count (reads) lower than this value [default: 2]", + "description": "Discard molecules with with a count (reads) lower than this value", "default": 2, "minimum": 1, "type": "integer" }, "use_counts": { "description": "Use counts when collapsing (the difference in counts between two molecules must be more than double in order to be collapsed)", - "default": false, "type": "boolean" } } @@ -224,37 +249,42 @@ "multiplet_recovery": { "description": "Activate the multiplet recovery (leiden or fast-greedy): leiden: use the leiden algorithm (whole graph) fast-greedy: use fast greedy algorithm (per-component)", "type": "string", - "enum": ["leiden", "fast-greedy"], - "default": "leiden" + "enum": ["leiden", "fast-greedy", "none"], + "default": "null" }, "fast_greedy_fraction": { "description": "Maximum fraction of edges allowed to be removed in a component for the fast-greedy algorithm [default: 0.05; 0.0<=x<=1.0]", "type": "number", - "default": 0.5, "minimum": 0.0, - "maximum": 1.0 + "maximum": 1.0, + "default": 0.05, + "hidden": true }, "fast_greedy_cutoff": { "fa_icon": "fas less-than-equal", "description": "Minimum size (edges) of a component to be considered for the fast-greedy algorithm [default: 5000]", "type": "integer", - "default": 5000 + "default": 5000, + "hidden": true + }, "leiden_iterations": { "fa_icon": "fas repeat", "description": "Number of iterations for the leiden algorithm, high values will decrease the variance of the results but increase the runtime [default: 10; 1<=x<=100]", "type": "integer", - "default": 10, "minimum": 1, - "maximum": 100 + "maximum": 100, + "default": 10, + "hidden": true }, "cluster_min_count": { "fa_icon": "fas less-than-equal", - "description": "Discard edges (pixels) with a count (reads) lower than this [default: 2; 1<=x<=50] use 1 to disable", + "description": "Discard edges (pixels) with a count (reads) lower than this, use 1 to disable", "type": "integer", "default": 2, "minimum": 1, - "maximum": 50 + "maximum": 50, + "hidden": true } } }, @@ -262,11 +292,11 @@ "title": "Options for pixelator annotate command.", "properties": { "min_size": { - "description": "The minimum size (pixels) a component/cell can have (default is no filtering)", + "description": "The minimum size (pixels) a component/cell can have (disabled by default)", "type": "integer" }, "max_size": { - "description": "The maximum size (pixels) a component/cell can have (default is no filtering)", + "description": "The maximum size (pixels) a component/cell can have (disabled by default)", "type": "integer" }, "dynamic_filter": { @@ -277,17 +307,14 @@ }, "cell_type_assignments": { "description": "Enable cell type assignment using pre-trained models", - "default": false, "type": "boolean" }, "majority_vote": { "description": "Enable cell type majority voting using clustering of components", - "default": false, "type": "boolean" }, "aggregate_calling": { "description": "Enable aggregate calling, information on potential aggregates will be added to the output data", - "default": true, "type": "boolean" } } @@ -316,7 +343,8 @@ "default": false }, "normalization": { - "description": "Which approach to use to normalize the antibody counts: raw will use the raw counts CLR will use the CLR-transformed counts denoise will use CLR-transformed counts and subtract the counts of control antibodies", + "description": "Which approach to use to normalize the antibody counts.", + "help_text": "- `raw`: use the raw counts.\n- `CLR`: use the CLR-transformed counts.\n- `denoise`: use CLR-transformed counts and subtract the counts of control antibodies", "type": "string", "enum": ["raw", "clr", "denoise"], "default": "clr" @@ -336,11 +364,6 @@ "description": "Skip report generation", "type": "boolean", "default": false - }, - "report_name": { - "description": "The name for the report", - "type": ["string"], - "default": "report" } } }, @@ -427,36 +450,6 @@ } } }, - "global_options": { - "title": "Global options", - "type": "object", - "fa_icon": "fas fa-file-import", - "description": "Less common options for the pipeline (specific to nf-core-pixelator), typically set in a config file.", - "help_text": "These options allow you to customize some of the core preferences for how the pipeline runs.\n\nTypically these options would be set in a Nextflow config file loaded for all pipeline runs, such as `~/.nextflow/config`.", - "properties": { - "pixelator_container_source": { - "type": "string", - "description": "The source repository for the pixelator docker container.", - "enum": ["aws-ecr", "ghcr", "internal"], - "default": "ghcr", - "hidden": true - }, - "pixelator_tag": { - "type": "string", - "description": "Override which container tag of pixelator to use. Use carefully!", - "help_text": "This option allows you to use a different container tag for the pixelator tool.\nThis is intended for developers and power-users and can break the pipeline. Use on your own risk!", - "fa_icon": "fas fa-question-circle", - "hidden": true, - "default": "0.11.0" - }, - "pixelator_container": { - "type": "string", - "description": "The exact container to use. Overrides container defined by pixelator_container_source and pixelator_tag.", - "hidden": true, - "default": null - } - } - }, "generic_options": { "title": "Generic options", "type": "object", @@ -499,14 +492,6 @@ "fa_icon": "fas fa-remove-format", "hidden": true }, - "max_multiqc_email_size": { - "type": "string", - "description": "File size limit when attaching reports to summary emails.", - "pattern": "^\\d+(\\.\\d+)?\\.?\\s*(K|M|G|T)?B$", - "default": "25.MB", - "fa_icon": "fas fa-file-upload", - "hidden": true - }, "monochrome_logs": { "type": "boolean", "description": "Do not use coloured log outputs.", @@ -549,6 +534,7 @@ }, "schema_ignore_params": { "type": "string", + "default": "", "description": "A comma separated string of inputs the schema validation should ignore." } } @@ -588,9 +574,6 @@ { "$ref": "#/definitions/max_job_request_options" }, - { - "$ref": "#/definitions/global_options" - }, { "$ref": "#/definitions/generic_options" } diff --git a/workflows/pixelator.nf b/workflows/pixelator.nf index 6f8e9c6b..6822fcf4 100644 --- a/workflows/pixelator.nf +++ b/workflows/pixelator.nf @@ -55,17 +55,19 @@ include { CAT_FASTQ } from '../modules/nf-core/cat/fastq/main' // // MODULE: Defined locally // -include { PIXELATOR_CONCATENATE } from '../modules/local/pixelator/concatenate/main' -include { PIXELATOR_QC } from '../modules/local/pixelator/qc/main' -include { PIXELATOR_DEMUX } from '../modules/local/pixelator/demux/main' -include { PIXELATOR_COLLAPSE } from '../modules/local/pixelator/collapse/main' -include { PIXELATOR_GRAPH } from '../modules/local/pixelator/graph/main' -include { PIXELATOR_ANALYSIS } from '../modules/local/pixelator/analysis/main' -include { PIXELATOR_ANNOTATE } from '../modules/local/pixelator/annotate/main' -include { PIXELATOR_REPORT } from '../modules/local/pixelator/report/main' + include { RENAME_READS } from '../modules/local/rename_reads' include { COLLECT_METADATA } from '../modules/local/collect_metadata' +include { PIXELATOR_CONCATENATE } from '../modules/local/pixelator/single-cell/concatenate/main' +include { PIXELATOR_QC } from '../modules/local/pixelator/single-cell/qc/main' +include { PIXELATOR_DEMUX } from '../modules/local/pixelator/single-cell/demux/main' +include { PIXELATOR_COLLAPSE } from '../modules/local/pixelator/single-cell/collapse/main' +include { PIXELATOR_GRAPH } from '../modules/local/pixelator/single-cell/graph/main' +include { PIXELATOR_ANALYSIS } from '../modules/local/pixelator/single-cell/analysis/main' +include { PIXELATOR_ANNOTATE } from '../modules/local/pixelator/single-cell/annotate/main' +include { PIXELATOR_REPORT } from '../modules/local/pixelator/single-cell/report/main' + /* ======================================================================================== RUN MAIN WORKFLOW @@ -206,34 +208,61 @@ workflow PIXELATOR { } else { ch_analysis_col = ch_meta_col.map { id, meta -> [id, []]} } + + // Combine all inputs and group them to make per-stage channels have their output in the same order + // ch_report_data: [[ + // meta, panels_file, + // [concatenate files...], [preqc files...], [adapterqc files...], [demux files...], + // [collapse files...], [cluster files], [annotate files...], [analysis files...] + // ], ...] + ch_report_data = ch_meta_col + .concat ( ch_panels_col ) + .concat ( ch_concatenate_col ) + .concat ( ch_preqc_col ) + .concat ( ch_adapterqc_col ) + .concat ( ch_demux_col ) + .concat ( ch_collapse_col ) + .concat ( ch_cluster_col ) + .concat ( ch_annotate_col ) + .concat ( ch_analysis_col ) + .groupTuple() + + // Split up everything per stage so we can recreate the expected directory structure for + // pixelator single-cell report using stageAs + + ch_meta_grouped = ch_report_data.map { id, data -> data[0] } + ch_panels_grouped = ch_report_data.map { id, data -> data[1] } + ch_concatenate_grouped = ch_report_data.map { id, data -> data[2].flatten() } + ch_preqc_grouped = ch_report_data.map { id, data -> data[3].flatten() } + ch_adapterqc_grouped = ch_report_data.map { id, data -> data[4].flatten() } + ch_demux_grouped = ch_report_data.map { id, data -> data[5].flatten() } + ch_collapse_grouped = ch_report_data.map { id, data -> data[6].flatten() } + ch_cluster_grouped = ch_report_data.map { id, data -> data[7].flatten() } + ch_annotate_grouped = ch_report_data.map { id, data -> data[8].flatten() } + ch_analysis_grouped = ch_report_data.map { id, data -> data[9].flatten() } + + PIXELATOR_REPORT ( + ch_meta_grouped, + ch_panels_grouped, + ch_concatenate_grouped, + ch_preqc_grouped, + ch_adapterqc_grouped, + ch_demux_grouped, + ch_collapse_grouped, + ch_cluster_grouped, + ch_annotate_grouped, + ch_analysis_grouped, + ) + + + ch_versions = ch_versions.mix(PIXELATOR_REPORT.out.versions) } CUSTOM_DUMPSOFTWAREVERSIONS ( ch_versions.unique().collectFile(name: 'collated_versions.yml') ) - // // - // // MODULE: MultiQC - // // - // workflow_summary = WorkflowPixelator.paramsSummaryMultiqc(workflow, summary_params) - // ch_workflow_summary = Channel.value(workflow_summary) - - // methods_description = WorkflowPixelator.methodsDescriptionText(workflow, ch_multiqc_custom_methods_description) - // ch_methods_description = Channel.value(methods_description) - - // ch_multiqc_files = Channel.empty() - // ch_multiqc_files = ch_multiqc_files.mix(ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) - // ch_multiqc_files = ch_multiqc_files.mix(ch_methods_description.collectFile(name: 'methods_description_mqc.yaml')) - // ch_multiqc_files = ch_multiqc_files.mix(CUSTOM_DUMPSOFTWAREVERSIONS.out.mqc_yml.collect()) - // ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}.ifEmpty([])) - - // MULTIQC ( - // ch_multiqc_files.collect(), - // ch_multiqc_config.toList(), - // ch_multiqc_custom_config.toList(), - // ch_multiqc_logo.toList() - // ) - // multiqc_report = MULTIQC.out.report.toList() + // TODO: Add MultiQC after plugins are available } /* From f9af04c48bdae9b13587fcd1d5fd7468b5965530 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 13 Jun 2023 17:38:22 +0200 Subject: [PATCH 003/112] use nf-validation for samplesheet --- assets/schema_input.json | 4 +- lib/WorkflowPixelator.groovy | 3 +- subworkflows/local/input_check.nf | 64 ++++++------------------------- workflows/pixelator.nf | 21 +++++----- 4 files changed, 26 insertions(+), 66 deletions(-) diff --git a/assets/schema_input.json b/assets/schema_input.json index fec206cb..7ffeeb06 100644 --- a/assets/schema_input.json +++ b/assets/schema_input.json @@ -10,13 +10,15 @@ "sample": { "type": "string", "pattern": "^\\S+$", - "errorMessage": "Sample name must be provided and cannot contain spaces" + "errorMessage": "Sample name must be provided and cannot contain spaces", + "meta": [ "id" ] }, "design": { "type": "string", "enum": [ "D21" ], + "meta": [ "design" ], "errorMessage": "Design must be specified" }, "panel": { diff --git a/lib/WorkflowPixelator.groovy b/lib/WorkflowPixelator.groovy index 2e576ecb..6855ff8e 100755 --- a/lib/WorkflowPixelator.groovy +++ b/lib/WorkflowPixelator.groovy @@ -10,8 +10,7 @@ class WorkflowPixelator { // // Check and validate parameters // - public static void initialise(params, log) { - } + public static void initialise(params, log) { } // // Get workflow summary for MultiQC diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf index b1b614e0..8231d171 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -2,66 +2,26 @@ // Check input samplesheet and get read channels // -include { SAMPLESHEET_CHECK } from '../../modules/local/samplesheet_check' +include { fromSamplesheet } from 'plugin/nf-validation' workflow INPUT_CHECK { take: - samplesheet // file: /path/to/samplesheet.csv main: - ch_samplesheet = SAMPLESHEET_CHECK ( samplesheet, samplesheet.toUri() ) - .csv - .splitCsv ( header:true, sep:',' ) + ch_samplesheet = Channel.fromSamplesheet("input") - reads = ch_samplesheet.map { create_fastq_channel(it) } - panels = ch_samplesheet.map { create_panels_channel(it) } - - emit: - reads // channel: [ val(meta), [ reads ] ] - panels // channel: [ val(meta), panel ] - - versions = SAMPLESHEET_CHECK.out.versions // channel: [ versions.yml ] -} - - -def get_meta(LinkedHashMap row) { - def meta = [:] - meta.id = row.sample - meta.single_end = row.single_end.toBoolean() - meta.design = row.design - meta.group = row.group - meta.assay = row.assay - return meta -} - -// Function to get list of [ meta, [ fastq_1, fastq_2 ] ] -def create_fastq_channel(LinkedHashMap row) { - // create meta map - def meta = get_meta(row) - - // add path(s) of the fastq file(s) to the meta map - def fastq_meta = [] - if (!file(row.fastq_1).exists()) { - exit 1, "ERROR: Please check input samplesheet -> Read 1 FastQ file does not exist!\n${row.fastq_1}" - } - if (meta.single_end) { - fastq_meta = [ meta, [ file(row.fastq_1) ] ] - } else { - if (!file(row.fastq_2).exists()) { - exit 1, "ERROR: Please check input samplesheet -> Read 2 FastQ file does not exist!\n${row.fastq_2}" + reads = ch_samplesheet.map { meta, panel, fastq_1, fastq_2 -> + def r = [] + r.add(fastq_1) + if (fastq_2 != null) { + r.add(fastq_2) } - fastq_meta = [ meta, [ file(row.fastq_1), file(row.fastq_2) ] ] + [meta, r] } - return fastq_meta -} -// Function to get list of [ meta, panel ] -def create_panels_channel(LinkedHashMap row) { - def meta = get_meta(row) + panels = ch_samplesheet.map { meta, panel, fastq_1, fastq_2 -> [meta, panel] } - if (file(row.panel).exists()) { - return [ meta, file(row.panel) ] - } - - exit 1, "ERROR: Please check panel field: ${row.panel}: Could not find existing csv file." + emit: + reads // channel: [ val(meta), [ reads ] ] + panels // channel: [ val(meta), panel ] } diff --git a/workflows/pixelator.nf b/workflows/pixelator.nf index 6822fcf4..19b8fd12 100644 --- a/workflows/pixelator.nf +++ b/workflows/pixelator.nf @@ -4,10 +4,6 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -def summary_params = NfcoreSchema.paramsSummaryMap(workflow, params) - -// Validate input parameters -WorkflowPixelator.initialise(params, log) // Check input path parameters to see if they exist def checkPathParamList = [ params.input ] @@ -35,6 +31,7 @@ params.samplesheet_sha = ch_input.bytes.digest('sha-1') // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules // include { INPUT_CHECK } from '../subworkflows/local/input_check' + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ IMPORT NF-CORE MODULES/SUBWORKFLOWS @@ -86,9 +83,14 @@ workflow PIXELATOR { // // SUBWORKFLOW: Read in samplesheet, validate and stage input files // - ch_fastq = INPUT_CHECK ( ch_input ).reads + // Create a new channel of metadata from a sample sheet + // NB: `input` corresponds to `params.input` and associated sample sheet schema + INPUT_CHECK() + + ch_reads = INPUT_CHECK.out.reads + ch_panels = INPUT_CHECK.out.panels - ch_fastq_split = ch_fastq + ch_fastq_split = ch_reads .map { meta, fastq -> new_id = meta.id - ~/_T\d+/ @@ -103,8 +105,6 @@ workflow PIXELATOR { return [ meta, fastq.flatten() ] } - ch_versions = ch_versions.mix(INPUT_CHECK.out.versions) - // // MODULE: Concatenate FastQ files from same sample if required // @@ -114,12 +114,11 @@ workflow PIXELATOR { ch_versions = ch_versions.mix(CAT_FASTQ.out.versions.first().ifEmpty(null)) - ch_reads = INPUT_CHECK.out.reads - ch_panels = INPUT_CHECK.out.panels + // We need to rename to make all reads match the sample name, // since pixelator extracts sample_names from read names - RENAME_READS ( ch_reads ) + RENAME_READS ( ch_cat_fastq ) ch_renamed_reads = RENAME_READS.out.reads ch_versions = ch_versions.mix(RENAME_READS.out.versions.first()) From 2ee0d64bbf0a1e27bd741c1ce15c3a5c92040df1 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 23 Jun 2023 10:37:17 +0200 Subject: [PATCH 004/112] refactor and sync with pixelator dev --- .nf-core.yml | 9 + assets/multiqc_config.yml | 0 assets/samplesheet.csv | 2 +- bin/check_samplesheet.py | 134 +-- conf/base.config | 2 - conf/igenomes.config | 440 ------- conf/modules.config | 27 +- conf/test.config | 12 +- design_options.txt | 1 + docs/images/mqc_fastqc_adapter.png | Bin 23458 -> 0 bytes docs/images/mqc_fastqc_counts.png | Bin 33918 -> 0 bytes docs/images/mqc_fastqc_quality.png | Bin 55769 -> 0 bytes docs/usage.md | 4 +- modules/local/collect_metadata.nf | 3 +- .../pixelator/single-cell/analysis/main.nf | 4 +- .../pixelator/single-cell/analysis/meta.yaml | 59 - .../pixelator/single-cell/annotate/meta.yaml | 64 - .../pixelator/single-cell/collapse/main.nf | 3 +- .../pixelator/single-cell/collapse/meta.yaml | 55 - .../pixelator/single-cell/concatenate/main.nf | 2 +- .../single-cell/concatenate/meta.yaml | 53 - .../pixelator/single-cell/demux/meta.yaml | 62 - .../pixelator/single-cell/graph/meta.yaml | 66 -- .../local/pixelator/single-cell/qc/meta.yaml | 100 -- modules/local/samplesheet_check.nf | 4 +- nextflow.config | 14 +- nextflow_schema.json | 52 +- .../execution_report_2023-06-23_10-03-02.html | 1041 +++++++++++++++++ ...xecution_timeline_2023-06-23_10-03-02.html | 222 ++++ .../pipeline_dag_2023-06-23_10-03-02.html | 425 +++++++ samplesheet.transformed.csv | 4 + subworkflows/local/generate_reports.nf | 122 ++ subworkflows/local/input_check.nf | 65 +- workflows/pixelator.nf | 149 +-- 34 files changed, 1986 insertions(+), 1214 deletions(-) delete mode 100644 assets/multiqc_config.yml delete mode 100644 conf/igenomes.config create mode 100644 design_options.txt delete mode 100755 docs/images/mqc_fastqc_adapter.png delete mode 100755 docs/images/mqc_fastqc_counts.png delete mode 100755 docs/images/mqc_fastqc_quality.png delete mode 100644 modules/local/pixelator/single-cell/analysis/meta.yaml delete mode 100644 modules/local/pixelator/single-cell/annotate/meta.yaml delete mode 100644 modules/local/pixelator/single-cell/collapse/meta.yaml delete mode 100644 modules/local/pixelator/single-cell/concatenate/meta.yaml delete mode 100644 modules/local/pixelator/single-cell/demux/meta.yaml delete mode 100644 modules/local/pixelator/single-cell/graph/meta.yaml delete mode 100644 modules/local/pixelator/single-cell/qc/meta.yaml create mode 100644 null/pipeline_info/execution_report_2023-06-23_10-03-02.html create mode 100644 null/pipeline_info/execution_timeline_2023-06-23_10-03-02.html create mode 100644 null/pipeline_info/pipeline_dag_2023-06-23_10-03-02.html create mode 100644 samplesheet.transformed.csv create mode 100644 subworkflows/local/generate_reports.nf diff --git a/.nf-core.yml b/.nf-core.yml index 3805dc81..f04c9179 100644 --- a/.nf-core.yml +++ b/.nf-core.yml @@ -1 +1,10 @@ repository_type: pipeline +lint: + # No multiqc support for now + multiqc_config: false + # TODO: Remove after move to nf-core + nextflow_config: + manifest.homePage: false + files_exist: + - assets/multiqc_config.yml + - conf/igenomes.config diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml deleted file mode 100644 index e69de29b..00000000 diff --git a/assets/samplesheet.csv b/assets/samplesheet.csv index c9713e42..f6644d80 100644 --- a/assets/samplesheet.csv +++ b/assets/samplesheet.csv @@ -1,3 +1,3 @@ sample,design,panel,fastq_1,fastq_2 test_data_se,D12,/path/to/test_panel.csv,/path/to/test_data.fastq.gz, -uropod_control,D21,/path/to/UNO_D21_conjV21.csv,/path/to/uropod_control_300k_S1_R1_001.fastq.gz,uropod_control_300k_S1_R2_001.fastq.gz +uropod_control,D21,to/UNO_D21_conjV21.csv,/path/to/uropod_control_300k_S1_R1_001.fastq.gz,uropod_control_300k_S1_R2_001.fastq.gz diff --git a/bin/check_samplesheet.py b/bin/check_samplesheet.py index 0e655ce6..5cf6b9e7 100755 --- a/bin/check_samplesheet.py +++ b/bin/check_samplesheet.py @@ -194,33 +194,16 @@ def _validate_fastq_format(self, filename): f"It should be one of: {', '.join(self.VALID_FORMATS)}" ) - def validate_unique_samples(self): - """ - Assert that the combination of sample name and FASTQ filename is unique. - - In addition to the validation, also rename all samples to have a suffix of _T{n}, where n is the - number of times the same sample exist, but with different FASTQ files, e.g., multiple runs per experiment. - - """ - if len(self._seen) != len(self.modified): - raise AssertionError("The pair of sample name and FASTQ must be unique.") - seen = Counter() - for row in self.modified: - sample = row[self._sample_col] - seen[sample] += 1 - row[self._sample_col] = f"{sample}_T{seen[sample]}" - class PixelatorRowChecker(RowChecker): DEFAULT_GROUP = "default" - REQUIRED_COLUMNS = ["sample", "assay", "design", "panel", "fastq_1", "fastq_2"] + REQUIRED_COLUMNS = ["sample", "design", "panel", "fastq_1", "fastq_2"] def __init__(self, samplesheet_path=None, design_options: Optional[Set[str]] = None, **kwargs): super().__init__( sample_col="sample", first_col="fastq_1", second_col="fastq_2", single_col="single_end", **kwargs ) self._panel_col = "panel" - self._assay_col = "assay" self._design_col = "design" self._samplesheet_path = samplesheet_path self._base_dir = self.get_base_dir(samplesheet_path) if samplesheet_path else None @@ -232,12 +215,6 @@ def output_headers(cls, headers: Iterable[str]) -> List[str]: headers.insert(1, "single_end") return headers - def _validate_assay(self, row): - """Assert that the assay column exists and has supported values.""" - val = row[self._assay_col] - if len(val) <= 0: - raise AssertionError(f"The {self._assay_col} field is required.") - def _validate_design(self, row): """Assert that the design column exists and has supported values.""" val = row[self._design_col] @@ -276,7 +253,6 @@ def validate_and_transform(self, row): """ self._validate_sample(row) - self._validate_assay(row) self._validate_design(row) self._validate_first(row) self._validate_second(row) @@ -286,112 +262,6 @@ def validate_and_transform(self, row): self.modified.append(row) -class PixelatorAggregateRowChecker(BaseChecker): - """ - Define a service that can validate and transform each given row. - - Attributes: - modified (list): A list of dicts, where each dict corresponds to a previously - validated and transformed row. The order of rows is maintained. - - """ - - REQUIRED_COLUMNS = ["sample", "matrix"] - - VALID_FORMATS = ( - ".h5ad", - ".h5ad.gz", - ) - - def __init__( - self, - sample_col="sample", - group_col="group", - matrix_col="matrix", - samplesheet_path=None, - **kwargs, - ): - """ - Initialize the row checker with the expected column names. - - Args: - sample_col (str): The name of the column that contains the sample name - (default "sample"). - group_col (str): The name of the column that contains the group - assignment - second_col (str): The name of the column that contains the matrix file - in .h5ad or .h5ad.gz format - """ - self._sample_col = sample_col - self._group_col = group_col - self._matrix_col = matrix_col - self._samplesheet_path = samplesheet_path - self._base_dir = PurePath(self._samplesheet_path).parent - self._seen = set() - self.modified = [] - - @classmethod - def output_headers(cls, headers: Iterable[str]) -> List[str]: - headers = list(headers) - if not "group" in headers: - headers.insert(1, "group") - - return headers - - def validate_and_transform(self, row): - """ - Perform all validations on the given row and insert the read pairing status. - - Args: - row (dict): A mapping from column headers (keys) to elements of that row - (values). - - """ - self._validate_sample(row) - self._validate_group(row) - self._validate_matrix(row) - self._seen.add(row[self._sample_col]) - self.modified.append(row) - - def _validate_sample(self, row): - """Assert that the sample name exists and convert spaces to underscores.""" - if len(row[self._sample_col]) <= 0: - raise AssertionError("Sample input is required.") - # Sanitize samples slightly. - row[self._sample_col] = row[self._sample_col].replace(" ", "_") - - def _validate_group(self, row): - """Add default group entry if not set.""" - if not self._group_col in row: - row[self._group_col] = 0 - - def _validate_matrix(self, row): - """Assert that the matrix entry has the right format if it exists.""" - if len(row[self._matrix_col]) <= 0: - raise AssertionError("The matrix field is required") - - self._validate_h5ad_format(row[self._matrix_col]) - matrix_path = make_absolute_path(row[self._matrix_col], self._base_dir) - - row[self._matrix_col] = matrix_path - - def _validate_h5ad_format(self, filename): - """Assert that a given filename has one of the expected H5AD extensions.""" - - if not any(filename.endswith(extension) for extension in self.VALID_FORMATS): - raise AssertionError( - f"The matrix file has an unrecognized extension: {filename}\n" - f"It should be one of: {', '.join(self.VALID_FORMATS)}" - ) - - def validate_unique_samples(self): - """ - Assert that the sample name is unique. - """ - if len(self._seen) != len(self.modified): - raise AssertionError("The sample name must be unique.") - - def read_head(handle, num_lines=5): """Read the specified number of lines from the current position in the file.""" lines = [] @@ -458,8 +328,6 @@ def check_samplesheet(file_in, file_out, checker: BaseChecker): logger.critical(f"{str(error)} On line {i + 2}.") sys.exit(1) - checker.validate_unique_samples() - header = checker.output_headers(reader.fieldnames) # See https://docs.python.org/3.9/library/csv.html#id3 to read up on `newline=""`. diff --git a/conf/base.config b/conf/base.config index a01f953c..9861f0c5 100644 --- a/conf/base.config +++ b/conf/base.config @@ -10,7 +10,6 @@ process { - // TODO nf-core: Check the defaults for all processes cpus = { check_max( 1 * task.attempt, 'cpus' ) } memory = { check_max( 6.GB * task.attempt, 'memory' ) } time = { check_max( 4.h * task.attempt, 'time' ) } @@ -24,7 +23,6 @@ process { // These labels are used and recognised by default in DSL2 files hosted on nf-core/modules. // If possible, it would be nice to keep the same label naming convention when // adding in your local modules too. - // TODO nf-core: Customise requirements for specific processes. // See https://www.nextflow.io/docs/latest/config.html#config-process-selectors withLabel:process_single { cpus = { check_max( 1 , 'cpus' ) } diff --git a/conf/igenomes.config b/conf/igenomes.config deleted file mode 100644 index 3f114377..00000000 --- a/conf/igenomes.config +++ /dev/null @@ -1,440 +0,0 @@ -/* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Nextflow config file for iGenomes paths -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Defines reference genomes using iGenome paths. - Can be used by any config that customises the base path using: - $params.igenomes_base / --igenomes_base ----------------------------------------------------------------------------------------- -*/ - -params { - // illumina iGenomes reference file paths - genomes { - 'GRCh37' { - fasta = "${params.igenomes_base}/Homo_sapiens/Ensembl/GRCh37/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Homo_sapiens/Ensembl/GRCh37/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Homo_sapiens/Ensembl/GRCh37/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Homo_sapiens/Ensembl/GRCh37/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Homo_sapiens/Ensembl/GRCh37/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Homo_sapiens/Ensembl/GRCh37/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Homo_sapiens/Ensembl/GRCh37/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Homo_sapiens/Ensembl/GRCh37/Annotation/README.txt" - mito_name = "MT" - macs_gsize = "2.7e9" - blacklist = "${projectDir}/assets/blacklists/GRCh37-blacklist.bed" - } - 'GRCh38' { - fasta = "${params.igenomes_base}/Homo_sapiens/NCBI/GRCh38/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Homo_sapiens/NCBI/GRCh38/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Homo_sapiens/NCBI/GRCh38/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Homo_sapiens/NCBI/GRCh38/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Homo_sapiens/NCBI/GRCh38/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Homo_sapiens/NCBI/GRCh38/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Homo_sapiens/NCBI/GRCh38/Annotation/Genes/genes.bed" - mito_name = "chrM" - macs_gsize = "2.7e9" - blacklist = "${projectDir}/assets/blacklists/hg38-blacklist.bed" - } - 'CHM13' { - fasta = "${params.igenomes_base}/Homo_sapiens/UCSC/CHM13/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Homo_sapiens/UCSC/CHM13/Sequence/BWAIndex/" - bwamem2 = "${params.igenomes_base}/Homo_sapiens/UCSC/CHM13/Sequence/BWAmem2Index/" - gtf = "${params.igenomes_base}/Homo_sapiens/NCBI/CHM13/Annotation/Genes/genes.gtf" - gff = "ftp://ftp.ncbi.nlm.nih.gov/genomes/all/GCF/009/914/755/GCF_009914755.1_T2T-CHM13v2.0/GCF_009914755.1_T2T-CHM13v2.0_genomic.gff.gz" - mito_name = "chrM" - } - 'GRCm38' { - fasta = "${params.igenomes_base}/Mus_musculus/Ensembl/GRCm38/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Mus_musculus/Ensembl/GRCm38/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Mus_musculus/Ensembl/GRCm38/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Mus_musculus/Ensembl/GRCm38/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Mus_musculus/Ensembl/GRCm38/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Mus_musculus/Ensembl/GRCm38/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Mus_musculus/Ensembl/GRCm38/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Mus_musculus/Ensembl/GRCm38/Annotation/README.txt" - mito_name = "MT" - macs_gsize = "1.87e9" - blacklist = "${projectDir}/assets/blacklists/GRCm38-blacklist.bed" - } - 'TAIR10' { - fasta = "${params.igenomes_base}/Arabidopsis_thaliana/Ensembl/TAIR10/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Arabidopsis_thaliana/Ensembl/TAIR10/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Arabidopsis_thaliana/Ensembl/TAIR10/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Arabidopsis_thaliana/Ensembl/TAIR10/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Arabidopsis_thaliana/Ensembl/TAIR10/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Arabidopsis_thaliana/Ensembl/TAIR10/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Arabidopsis_thaliana/Ensembl/TAIR10/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Arabidopsis_thaliana/Ensembl/TAIR10/Annotation/README.txt" - mito_name = "Mt" - } - 'EB2' { - fasta = "${params.igenomes_base}/Bacillus_subtilis_168/Ensembl/EB2/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Bacillus_subtilis_168/Ensembl/EB2/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Bacillus_subtilis_168/Ensembl/EB2/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Bacillus_subtilis_168/Ensembl/EB2/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Bacillus_subtilis_168/Ensembl/EB2/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Bacillus_subtilis_168/Ensembl/EB2/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Bacillus_subtilis_168/Ensembl/EB2/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Bacillus_subtilis_168/Ensembl/EB2/Annotation/README.txt" - } - 'UMD3.1' { - fasta = "${params.igenomes_base}/Bos_taurus/Ensembl/UMD3.1/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Bos_taurus/Ensembl/UMD3.1/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Bos_taurus/Ensembl/UMD3.1/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Bos_taurus/Ensembl/UMD3.1/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Bos_taurus/Ensembl/UMD3.1/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Bos_taurus/Ensembl/UMD3.1/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Bos_taurus/Ensembl/UMD3.1/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Bos_taurus/Ensembl/UMD3.1/Annotation/README.txt" - mito_name = "MT" - } - 'WBcel235' { - fasta = "${params.igenomes_base}/Caenorhabditis_elegans/Ensembl/WBcel235/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Caenorhabditis_elegans/Ensembl/WBcel235/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Caenorhabditis_elegans/Ensembl/WBcel235/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Caenorhabditis_elegans/Ensembl/WBcel235/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Caenorhabditis_elegans/Ensembl/WBcel235/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Caenorhabditis_elegans/Ensembl/WBcel235/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Caenorhabditis_elegans/Ensembl/WBcel235/Annotation/Genes/genes.bed" - mito_name = "MtDNA" - macs_gsize = "9e7" - } - 'CanFam3.1' { - fasta = "${params.igenomes_base}/Canis_familiaris/Ensembl/CanFam3.1/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Canis_familiaris/Ensembl/CanFam3.1/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Canis_familiaris/Ensembl/CanFam3.1/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Canis_familiaris/Ensembl/CanFam3.1/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Canis_familiaris/Ensembl/CanFam3.1/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Canis_familiaris/Ensembl/CanFam3.1/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Canis_familiaris/Ensembl/CanFam3.1/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Canis_familiaris/Ensembl/CanFam3.1/Annotation/README.txt" - mito_name = "MT" - } - 'GRCz10' { - fasta = "${params.igenomes_base}/Danio_rerio/Ensembl/GRCz10/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Danio_rerio/Ensembl/GRCz10/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Danio_rerio/Ensembl/GRCz10/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Danio_rerio/Ensembl/GRCz10/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Danio_rerio/Ensembl/GRCz10/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Danio_rerio/Ensembl/GRCz10/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Danio_rerio/Ensembl/GRCz10/Annotation/Genes/genes.bed" - mito_name = "MT" - } - 'BDGP6' { - fasta = "${params.igenomes_base}/Drosophila_melanogaster/Ensembl/BDGP6/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Drosophila_melanogaster/Ensembl/BDGP6/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Drosophila_melanogaster/Ensembl/BDGP6/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Drosophila_melanogaster/Ensembl/BDGP6/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Drosophila_melanogaster/Ensembl/BDGP6/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Drosophila_melanogaster/Ensembl/BDGP6/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Drosophila_melanogaster/Ensembl/BDGP6/Annotation/Genes/genes.bed" - mito_name = "M" - macs_gsize = "1.2e8" - } - 'EquCab2' { - fasta = "${params.igenomes_base}/Equus_caballus/Ensembl/EquCab2/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Equus_caballus/Ensembl/EquCab2/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Equus_caballus/Ensembl/EquCab2/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Equus_caballus/Ensembl/EquCab2/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Equus_caballus/Ensembl/EquCab2/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Equus_caballus/Ensembl/EquCab2/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Equus_caballus/Ensembl/EquCab2/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Equus_caballus/Ensembl/EquCab2/Annotation/README.txt" - mito_name = "MT" - } - 'EB1' { - fasta = "${params.igenomes_base}/Escherichia_coli_K_12_DH10B/Ensembl/EB1/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Escherichia_coli_K_12_DH10B/Ensembl/EB1/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Escherichia_coli_K_12_DH10B/Ensembl/EB1/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Escherichia_coli_K_12_DH10B/Ensembl/EB1/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Escherichia_coli_K_12_DH10B/Ensembl/EB1/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Escherichia_coli_K_12_DH10B/Ensembl/EB1/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Escherichia_coli_K_12_DH10B/Ensembl/EB1/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Escherichia_coli_K_12_DH10B/Ensembl/EB1/Annotation/README.txt" - } - 'Galgal4' { - fasta = "${params.igenomes_base}/Gallus_gallus/Ensembl/Galgal4/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Gallus_gallus/Ensembl/Galgal4/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Gallus_gallus/Ensembl/Galgal4/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Gallus_gallus/Ensembl/Galgal4/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Gallus_gallus/Ensembl/Galgal4/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Gallus_gallus/Ensembl/Galgal4/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Gallus_gallus/Ensembl/Galgal4/Annotation/Genes/genes.bed" - mito_name = "MT" - } - 'Gm01' { - fasta = "${params.igenomes_base}/Glycine_max/Ensembl/Gm01/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Glycine_max/Ensembl/Gm01/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Glycine_max/Ensembl/Gm01/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Glycine_max/Ensembl/Gm01/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Glycine_max/Ensembl/Gm01/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Glycine_max/Ensembl/Gm01/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Glycine_max/Ensembl/Gm01/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Glycine_max/Ensembl/Gm01/Annotation/README.txt" - } - 'Mmul_1' { - fasta = "${params.igenomes_base}/Macaca_mulatta/Ensembl/Mmul_1/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Macaca_mulatta/Ensembl/Mmul_1/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Macaca_mulatta/Ensembl/Mmul_1/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Macaca_mulatta/Ensembl/Mmul_1/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Macaca_mulatta/Ensembl/Mmul_1/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Macaca_mulatta/Ensembl/Mmul_1/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Macaca_mulatta/Ensembl/Mmul_1/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Macaca_mulatta/Ensembl/Mmul_1/Annotation/README.txt" - mito_name = "MT" - } - 'IRGSP-1.0' { - fasta = "${params.igenomes_base}/Oryza_sativa_japonica/Ensembl/IRGSP-1.0/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Oryza_sativa_japonica/Ensembl/IRGSP-1.0/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Oryza_sativa_japonica/Ensembl/IRGSP-1.0/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Oryza_sativa_japonica/Ensembl/IRGSP-1.0/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Oryza_sativa_japonica/Ensembl/IRGSP-1.0/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Oryza_sativa_japonica/Ensembl/IRGSP-1.0/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Oryza_sativa_japonica/Ensembl/IRGSP-1.0/Annotation/Genes/genes.bed" - mito_name = "Mt" - } - 'CHIMP2.1.4' { - fasta = "${params.igenomes_base}/Pan_troglodytes/Ensembl/CHIMP2.1.4/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Pan_troglodytes/Ensembl/CHIMP2.1.4/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Pan_troglodytes/Ensembl/CHIMP2.1.4/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Pan_troglodytes/Ensembl/CHIMP2.1.4/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Pan_troglodytes/Ensembl/CHIMP2.1.4/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Pan_troglodytes/Ensembl/CHIMP2.1.4/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Pan_troglodytes/Ensembl/CHIMP2.1.4/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Pan_troglodytes/Ensembl/CHIMP2.1.4/Annotation/README.txt" - mito_name = "MT" - } - 'Rnor_5.0' { - fasta = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_5.0/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_5.0/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_5.0/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_5.0/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_5.0/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_5.0/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_5.0/Annotation/Genes/genes.bed" - mito_name = "MT" - } - 'Rnor_6.0' { - fasta = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_6.0/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_6.0/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_6.0/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_6.0/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_6.0/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_6.0/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Rattus_norvegicus/Ensembl/Rnor_6.0/Annotation/Genes/genes.bed" - mito_name = "MT" - } - 'R64-1-1' { - fasta = "${params.igenomes_base}/Saccharomyces_cerevisiae/Ensembl/R64-1-1/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Saccharomyces_cerevisiae/Ensembl/R64-1-1/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Saccharomyces_cerevisiae/Ensembl/R64-1-1/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Saccharomyces_cerevisiae/Ensembl/R64-1-1/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Saccharomyces_cerevisiae/Ensembl/R64-1-1/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Saccharomyces_cerevisiae/Ensembl/R64-1-1/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Saccharomyces_cerevisiae/Ensembl/R64-1-1/Annotation/Genes/genes.bed" - mito_name = "MT" - macs_gsize = "1.2e7" - } - 'EF2' { - fasta = "${params.igenomes_base}/Schizosaccharomyces_pombe/Ensembl/EF2/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Schizosaccharomyces_pombe/Ensembl/EF2/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Schizosaccharomyces_pombe/Ensembl/EF2/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Schizosaccharomyces_pombe/Ensembl/EF2/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Schizosaccharomyces_pombe/Ensembl/EF2/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Schizosaccharomyces_pombe/Ensembl/EF2/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Schizosaccharomyces_pombe/Ensembl/EF2/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Schizosaccharomyces_pombe/Ensembl/EF2/Annotation/README.txt" - mito_name = "MT" - macs_gsize = "1.21e7" - } - 'Sbi1' { - fasta = "${params.igenomes_base}/Sorghum_bicolor/Ensembl/Sbi1/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Sorghum_bicolor/Ensembl/Sbi1/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Sorghum_bicolor/Ensembl/Sbi1/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Sorghum_bicolor/Ensembl/Sbi1/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Sorghum_bicolor/Ensembl/Sbi1/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Sorghum_bicolor/Ensembl/Sbi1/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Sorghum_bicolor/Ensembl/Sbi1/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Sorghum_bicolor/Ensembl/Sbi1/Annotation/README.txt" - } - 'Sscrofa10.2' { - fasta = "${params.igenomes_base}/Sus_scrofa/Ensembl/Sscrofa10.2/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Sus_scrofa/Ensembl/Sscrofa10.2/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Sus_scrofa/Ensembl/Sscrofa10.2/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Sus_scrofa/Ensembl/Sscrofa10.2/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Sus_scrofa/Ensembl/Sscrofa10.2/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Sus_scrofa/Ensembl/Sscrofa10.2/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Sus_scrofa/Ensembl/Sscrofa10.2/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Sus_scrofa/Ensembl/Sscrofa10.2/Annotation/README.txt" - mito_name = "MT" - } - 'AGPv3' { - fasta = "${params.igenomes_base}/Zea_mays/Ensembl/AGPv3/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Zea_mays/Ensembl/AGPv3/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Zea_mays/Ensembl/AGPv3/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Zea_mays/Ensembl/AGPv3/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Zea_mays/Ensembl/AGPv3/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Zea_mays/Ensembl/AGPv3/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Zea_mays/Ensembl/AGPv3/Annotation/Genes/genes.bed" - mito_name = "Mt" - } - 'hg38' { - fasta = "${params.igenomes_base}/Homo_sapiens/UCSC/hg38/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Homo_sapiens/UCSC/hg38/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Homo_sapiens/UCSC/hg38/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Homo_sapiens/UCSC/hg38/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Homo_sapiens/UCSC/hg38/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Homo_sapiens/UCSC/hg38/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Homo_sapiens/UCSC/hg38/Annotation/Genes/genes.bed" - mito_name = "chrM" - macs_gsize = "2.7e9" - blacklist = "${projectDir}/assets/blacklists/hg38-blacklist.bed" - } - 'hg19' { - fasta = "${params.igenomes_base}/Homo_sapiens/UCSC/hg19/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Homo_sapiens/UCSC/hg19/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Homo_sapiens/UCSC/hg19/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Homo_sapiens/UCSC/hg19/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Homo_sapiens/UCSC/hg19/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Homo_sapiens/UCSC/hg19/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Homo_sapiens/UCSC/hg19/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Homo_sapiens/UCSC/hg19/Annotation/README.txt" - mito_name = "chrM" - macs_gsize = "2.7e9" - blacklist = "${projectDir}/assets/blacklists/hg19-blacklist.bed" - } - 'mm10' { - fasta = "${params.igenomes_base}/Mus_musculus/UCSC/mm10/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Mus_musculus/UCSC/mm10/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Mus_musculus/UCSC/mm10/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Mus_musculus/UCSC/mm10/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Mus_musculus/UCSC/mm10/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Mus_musculus/UCSC/mm10/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Mus_musculus/UCSC/mm10/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Mus_musculus/UCSC/mm10/Annotation/README.txt" - mito_name = "chrM" - macs_gsize = "1.87e9" - blacklist = "${projectDir}/assets/blacklists/mm10-blacklist.bed" - } - 'bosTau8' { - fasta = "${params.igenomes_base}/Bos_taurus/UCSC/bosTau8/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Bos_taurus/UCSC/bosTau8/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Bos_taurus/UCSC/bosTau8/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Bos_taurus/UCSC/bosTau8/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Bos_taurus/UCSC/bosTau8/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Bos_taurus/UCSC/bosTau8/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Bos_taurus/UCSC/bosTau8/Annotation/Genes/genes.bed" - mito_name = "chrM" - } - 'ce10' { - fasta = "${params.igenomes_base}/Caenorhabditis_elegans/UCSC/ce10/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Caenorhabditis_elegans/UCSC/ce10/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Caenorhabditis_elegans/UCSC/ce10/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Caenorhabditis_elegans/UCSC/ce10/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Caenorhabditis_elegans/UCSC/ce10/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Caenorhabditis_elegans/UCSC/ce10/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Caenorhabditis_elegans/UCSC/ce10/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Caenorhabditis_elegans/UCSC/ce10/Annotation/README.txt" - mito_name = "chrM" - macs_gsize = "9e7" - } - 'canFam3' { - fasta = "${params.igenomes_base}/Canis_familiaris/UCSC/canFam3/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Canis_familiaris/UCSC/canFam3/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Canis_familiaris/UCSC/canFam3/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Canis_familiaris/UCSC/canFam3/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Canis_familiaris/UCSC/canFam3/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Canis_familiaris/UCSC/canFam3/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Canis_familiaris/UCSC/canFam3/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Canis_familiaris/UCSC/canFam3/Annotation/README.txt" - mito_name = "chrM" - } - 'danRer10' { - fasta = "${params.igenomes_base}/Danio_rerio/UCSC/danRer10/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Danio_rerio/UCSC/danRer10/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Danio_rerio/UCSC/danRer10/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Danio_rerio/UCSC/danRer10/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Danio_rerio/UCSC/danRer10/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Danio_rerio/UCSC/danRer10/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Danio_rerio/UCSC/danRer10/Annotation/Genes/genes.bed" - mito_name = "chrM" - macs_gsize = "1.37e9" - } - 'dm6' { - fasta = "${params.igenomes_base}/Drosophila_melanogaster/UCSC/dm6/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Drosophila_melanogaster/UCSC/dm6/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Drosophila_melanogaster/UCSC/dm6/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Drosophila_melanogaster/UCSC/dm6/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Drosophila_melanogaster/UCSC/dm6/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Drosophila_melanogaster/UCSC/dm6/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Drosophila_melanogaster/UCSC/dm6/Annotation/Genes/genes.bed" - mito_name = "chrM" - macs_gsize = "1.2e8" - } - 'equCab2' { - fasta = "${params.igenomes_base}/Equus_caballus/UCSC/equCab2/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Equus_caballus/UCSC/equCab2/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Equus_caballus/UCSC/equCab2/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Equus_caballus/UCSC/equCab2/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Equus_caballus/UCSC/equCab2/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Equus_caballus/UCSC/equCab2/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Equus_caballus/UCSC/equCab2/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Equus_caballus/UCSC/equCab2/Annotation/README.txt" - mito_name = "chrM" - } - 'galGal4' { - fasta = "${params.igenomes_base}/Gallus_gallus/UCSC/galGal4/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Gallus_gallus/UCSC/galGal4/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Gallus_gallus/UCSC/galGal4/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Gallus_gallus/UCSC/galGal4/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Gallus_gallus/UCSC/galGal4/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Gallus_gallus/UCSC/galGal4/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Gallus_gallus/UCSC/galGal4/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Gallus_gallus/UCSC/galGal4/Annotation/README.txt" - mito_name = "chrM" - } - 'panTro4' { - fasta = "${params.igenomes_base}/Pan_troglodytes/UCSC/panTro4/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Pan_troglodytes/UCSC/panTro4/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Pan_troglodytes/UCSC/panTro4/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Pan_troglodytes/UCSC/panTro4/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Pan_troglodytes/UCSC/panTro4/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Pan_troglodytes/UCSC/panTro4/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Pan_troglodytes/UCSC/panTro4/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Pan_troglodytes/UCSC/panTro4/Annotation/README.txt" - mito_name = "chrM" - } - 'rn6' { - fasta = "${params.igenomes_base}/Rattus_norvegicus/UCSC/rn6/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Rattus_norvegicus/UCSC/rn6/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Rattus_norvegicus/UCSC/rn6/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Rattus_norvegicus/UCSC/rn6/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Rattus_norvegicus/UCSC/rn6/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Rattus_norvegicus/UCSC/rn6/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Rattus_norvegicus/UCSC/rn6/Annotation/Genes/genes.bed" - mito_name = "chrM" - } - 'sacCer3' { - fasta = "${params.igenomes_base}/Saccharomyces_cerevisiae/UCSC/sacCer3/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Saccharomyces_cerevisiae/UCSC/sacCer3/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Saccharomyces_cerevisiae/UCSC/sacCer3/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Saccharomyces_cerevisiae/UCSC/sacCer3/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Saccharomyces_cerevisiae/UCSC/sacCer3/Sequence/BismarkIndex/" - readme = "${params.igenomes_base}/Saccharomyces_cerevisiae/UCSC/sacCer3/Annotation/README.txt" - mito_name = "chrM" - macs_gsize = "1.2e7" - } - 'susScr3' { - fasta = "${params.igenomes_base}/Sus_scrofa/UCSC/susScr3/Sequence/WholeGenomeFasta/genome.fa" - bwa = "${params.igenomes_base}/Sus_scrofa/UCSC/susScr3/Sequence/BWAIndex/version0.6.0/" - bowtie2 = "${params.igenomes_base}/Sus_scrofa/UCSC/susScr3/Sequence/Bowtie2Index/" - star = "${params.igenomes_base}/Sus_scrofa/UCSC/susScr3/Sequence/STARIndex/" - bismark = "${params.igenomes_base}/Sus_scrofa/UCSC/susScr3/Sequence/BismarkIndex/" - gtf = "${params.igenomes_base}/Sus_scrofa/UCSC/susScr3/Annotation/Genes/genes.gtf" - bed12 = "${params.igenomes_base}/Sus_scrofa/UCSC/susScr3/Annotation/Genes/genes.bed" - readme = "${params.igenomes_base}/Sus_scrofa/UCSC/susScr3/Annotation/README.txt" - mito_name = "chrM" - } - } -} diff --git a/conf/modules.config b/conf/modules.config index c3a6eed7..e7538f08 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -19,15 +19,8 @@ process { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] - withName: SAMPLESHEET_CHECK { - publishDir = [ - path: { "${params.outdir}/pipeline_info" }, - mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } - ] - } - withName: 'SAMPLESHEET_CHECK|COLLECT_METADATA|PIXELATOR_*' { + withName: 'COLLECT_METADATA|PIXELATOR_*' { ext.singularity_pull_docker_container = true // Use this to override all usages of the pixelator container @@ -135,10 +128,8 @@ process { withName: PIXELATOR_GRAPH { ext.args = [ - (params.multiplet_recovery && params.multiplet_recovery != "none") ? "--multiplet-recovery ${params.multiplet_recovery}" : '', - params.fast_greedy_fraction ? "--fast-greedy-fraction ${params.fast_greedy_fraction}": '', - params.fast_greedy_cutoff ? "--fast-greedy-cutoff ${params.fast_greedy_cutoff}": '', - params.leiden_iterations ? "--leiden-iterations ${params.leiden_iterations}": '', + params.multiplet_recovery ? "--multiplet-recovery" : '', + params.leiden_iterations ? "--leiden-iterations ${params.leiden_iterations}" : '', params.cluster_min_count ? "--min-count ${params.cluster_min_count}" : '', ].join(' ').trim() } @@ -155,14 +146,24 @@ process { } withName: PIXELATOR_ANALYSIS { + ext.when = { !params.skip_analysis } ext.args = [ params.compute_polarization ? "--compute-polarization" : '', params.compute_colocalization ? "--compute-colocalization" : '', params.use_full_bipartite ? "--use-full-bipartite " : '', - params.normalization ? "--normalization ${params.normalization}" : '', + params.polarization_normalization ? "--polarization-normalization ${params.polarization_normalization}" : '', + params.polarization_binarization ? "--polarization-binarization" : '', + params.colocalization_transformation ? "--colocalization-transformation ${params.colocalization_transformation}" : '', + (params.colocalization_neighbourhood_size != null) ? "--colocalization-neighbourhood-size ${params.colocalization_neighbourhood_size}" : '', + (params.colocalization_n_permutations != null) ? "--colocalization-n-permutations ${params.colocalization_n_permutations}" : '', + (params.colocalization_min_region_count != null) ? "--colocalization-min-region-count ${params.colocalization_min_region_count}" : '', ].join(' ').trim() } + withName: PIXELATOR_REPORT { + ext.when = { !params.skip_report } + } + withName: CUSTOM_DUMPSOFTWAREVERSIONS { publishDir = [ diff --git a/conf/test.config b/conf/test.config index 0f63aa02..35a1483c 100644 --- a/conf/test.config +++ b/conf/test.config @@ -19,16 +19,11 @@ params { max_memory = '6.GB' max_time = '6.h' - // Input data - // TODO nf-core: Specify the paths to your test data on nf-core/test-datasets - // TODO nf-core: Give any required params for the test so that command line flags are not needed - // input = 'https://raw.githubusercontent.com/nf-core/test-datasets/viralrecon/samplesheet/samplesheet_test_illumina_amplicon.csv' - - // TODO nf-core: Specify the paths to your test data on nf-core/test-datasets - // TODO nf-core: Give any required params for the test so that command line flags are not needed + // TODO pixelgen: Move to public test data once available input = "${params.testdata_root}/testdata/micro/test_samplesheet.csv" + outdir = "results" + tracedir = "results" - anchored = false collapse_mismatches = 1 cluster_min_count = 2 min_size = 1 @@ -36,7 +31,6 @@ params { dynamic_filter = false cell_type_assignments = false majority_vote = false - compute_polarization = true compute_colocalization = false use_full_bipartite = true diff --git a/design_options.txt b/design_options.txt new file mode 100644 index 00000000..c7571d5f --- /dev/null +++ b/design_options.txt @@ -0,0 +1 @@ +D21 diff --git a/docs/images/mqc_fastqc_adapter.png b/docs/images/mqc_fastqc_adapter.png deleted file mode 100755 index 361d0e47acfb424dea1f326590d1eb2f6dfa26b5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 23458 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 diff --git a/docs/images/mqc_fastqc_quality.png b/docs/images/mqc_fastqc_quality.png deleted file mode 100755 index a4b89bf56ab2ba88cab87841916eb680a816deae..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 55769 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�)v~C(0C)c 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 M)78&qol`;+0Fu7JivR!s diff --git a/docs/usage.md b/docs/usage.md index 0af47078..c9dc9137 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -10,7 +10,9 @@ ## Samplesheet input -You will need to create a samplesheet with information about the samples you would like to analyse before running the pipeline. Use this parameter to specify its location. It has to be a comma-separated file with 3 columns, and a header row as shown in the examples below. +You will need to create a samplesheet with information about the samples you would like to analyse before running the pipeline. +Use this parameter to specify its location. +It has to be a comma-separated file with 3 columns, and a header row as shown in the examples below. ```bash --input '[path to samplesheet file]' diff --git a/modules/local/collect_metadata.nf b/modules/local/collect_metadata.nf index 97ac5404..e9cbee0c 100644 --- a/modules/local/collect_metadata.nf +++ b/modules/local/collect_metadata.nf @@ -5,8 +5,7 @@ import groovy.json.JsonSlurper import groovy.json.JsonBuilder process COLLECT_METADATA { - - label "process_single" + label 'process_single' cache false conda "local::pixelator=0.10.0" diff --git a/modules/local/pixelator/single-cell/analysis/main.nf b/modules/local/pixelator/single-cell/analysis/main.nf index 68103232..24969681 100644 --- a/modules/local/pixelator/single-cell/analysis/main.nf +++ b/modules/local/pixelator/single-cell/analysis/main.nf @@ -7,7 +7,7 @@ process PIXELATOR_ANALYSIS { container "ghcr.io/pixelgentechnologies/pixelator:0.11.0" input: - tuple val(meta), path(h5ad) + tuple val(meta), path(data) output: tuple val(meta), path("analysis/*dataset.pxl"), emit: dataset @@ -35,7 +35,7 @@ process PIXELATOR_ANALYSIS { analysis \\ --output . \\ $args \\ - $h5ad + $data cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/pixelator/single-cell/analysis/meta.yaml b/modules/local/pixelator/single-cell/analysis/meta.yaml deleted file mode 100644 index 404317a4..00000000 --- a/modules/local/pixelator/single-cell/analysis/meta.yaml +++ /dev/null @@ -1,59 +0,0 @@ -name: pixelator/analysis -description: | - Perform different analyses on a PixelDataset from pixelator annotate -keywords: - - "molecular pixelator" -tools: - - pixelator: - description: | - Software package to process sequencing data into Molecular Pixelation data. - homepage: https://github.com/PixelgenTechnologies/pixelator - documentation: https://github.com/PixelgenTechnologies/pixelator - doi: 10.1101/2023.06.05.543770 - licence: ["MIT"] - -input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - reads: - type: file - description: | - List of input FastQ files of size 1 and 2 for single-end and paired-end data, - respectively. - -output: - - dataset: - type: file - description: Pixelator dataset file - pattern: "*.dataset.pxl" - - - report_json: - type: file - description: JSON file with statistics for the cluster stage - pattern: "*.report.json" - - - metadata: - type: file - description: Command invocation metadata files for pixelator collapse - pattern: "*.meta.json" - - - all_results: - type: file - description: All output files from the pixelator cluster Command - pattern: "*" - - - log: - type: file - description: Pixelator log files - pattern: "*pixelator-analysis*.log" - - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" - -authors: - - "@fbdtemme" diff --git a/modules/local/pixelator/single-cell/annotate/meta.yaml b/modules/local/pixelator/single-cell/annotate/meta.yaml deleted file mode 100644 index b9c0c7aa..00000000 --- a/modules/local/pixelator/single-cell/annotate/meta.yaml +++ /dev/null @@ -1,64 +0,0 @@ -name: pixelator/annotate -description: | - Filter, annotate and call cells from an edge list produced by pixelator cluster -keywords: - - "molecular pixelator" -tools: - - pixelator: - description: | - Software package to process sequencing data into Molecular Pixelation data. - homepage: https://github.com/PixelgenTechnologies/pixelator - documentation: https://github.com/PixelgenTechnologies/pixelator - doi: 10.1101/2023.06.05.543770 - licence: ["MIT"] - -input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - reads: - type: file - description: | - List of input FastQ files of size 1 and 2 for single-end and paired-end data, - respectively. - -output: - - dataset: - type: file - description: Pixelator dataset file - pattern: "*.dataset.pxl" - - - report_json: - type: file - description: JSON file with statistics for the cluster stage - pattern: "*.report.json" - - - png: - type: file - description: PNG plots - pattern: "*.png" - - - metadata: - type: file - description: Command invocation metadata files for pixelator collapse - pattern: "*.meta.json" - - - all_results: - type: file - description: All output files from the pixelator single-cell cluster command - pattern: "*" - - - log: - type: file - description: Pixelator log files - pattern: "*pixelator-annotate*.log" - - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" - -authors: - - "@fbdtemme" diff --git a/modules/local/pixelator/single-cell/collapse/main.nf b/modules/local/pixelator/single-cell/collapse/main.nf index 12a03d95..3afa135b 100644 --- a/modules/local/pixelator/single-cell/collapse/main.nf +++ b/modules/local/pixelator/single-cell/collapse/main.nf @@ -10,7 +10,7 @@ process PIXELATOR_COLLAPSE { container "ghcr.io/pixelgentechnologies/pixelator:0.11.0" input: - tuple val(meta), path(reads) + tuple val(meta), path(reads), path(panel) output: tuple val(meta), path("collapse/*.collapsed.csv.gz"), emit: collapsed @@ -40,6 +40,7 @@ process PIXELATOR_COLLAPSE { collapse \\ --output . \\ --design ${meta.design} \\ + --panel-file ${panel} \\ $args \\ ${readsArg} diff --git a/modules/local/pixelator/single-cell/collapse/meta.yaml b/modules/local/pixelator/single-cell/collapse/meta.yaml deleted file mode 100644 index 5579089a..00000000 --- a/modules/local/pixelator/single-cell/collapse/meta.yaml +++ /dev/null @@ -1,55 +0,0 @@ -name: pixelator/collapse -description: | - Collapse Molecular Pixelation data (FASTQ) by umi-upi to remove duplicates - and perform error correction. -keywords: - - "molecular pixelator" -tools: - - pixelator: - description: | - Software package to process sequencing data into Molecular Pixelation data. - homepage: https://github.com/PixelgenTechnologies/pixelator - documentation: https://github.com/PixelgenTechnologies/pixelator - doi: 10.1101/2023.06.05.543770 - licence: ["MIT"] - -input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - reads: - type: file - description: | - List of input FastQ files of size 1 and 2 for single-end and paired-end data, - respectively. - -output: - - collapsed: - type: file - description: FastQ file containing an edge-list of collapsed reads - pattern: "*collapsed.csv.gz" - - - report_json: - type: file - description: JSON file with statistics for the collapse stage - pattern: "*.report.json" - - - metadata: - type: file - description: Command invocation metadata files for pixelator collapse - pattern: "*.meta.json" - - - log: - type: file - description: Pixelator log files - pattern: "*pixelator-collapse*.log" - - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" - -authors: - - "@fbdtemme" diff --git a/modules/local/pixelator/single-cell/concatenate/main.nf b/modules/local/pixelator/single-cell/concatenate/main.nf index f2a417aa..b4240268 100644 --- a/modules/local/pixelator/single-cell/concatenate/main.nf +++ b/modules/local/pixelator/single-cell/concatenate/main.nf @@ -2,7 +2,7 @@ process PIXELATOR_CONCATENATE { tag "$meta.id" - label 'process_medium' + label 'process_low' conda "local::pixelator=0.10.0" diff --git a/modules/local/pixelator/single-cell/concatenate/meta.yaml b/modules/local/pixelator/single-cell/concatenate/meta.yaml deleted file mode 100644 index 7e4920a3..00000000 --- a/modules/local/pixelator/single-cell/concatenate/meta.yaml +++ /dev/null @@ -1,53 +0,0 @@ -name: pixelator/concatenate -description: Demultiplex molecular pixelation reads according to the panel file barcodes -keywords: - - "molecular pixelator" -tools: - - pixelator: - description: | - Software package to process sequencing data into Molecular Pixelation data. - homepage: https://github.com/PixelgenTechnologies/pixelator - documentation: https://github.com/PixelgenTechnologies/pixelator - doi: 10.1101/2023.06.05.543770 - licence: ["MIT"] - -input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - reads: - type: file - description: | - List of input FastQ files of size 1 and 2 for single-end and paired-end data, - respectively. - -output: - - merged: - type: file - description: FastQ file containing combined and trimmed reads - pattern: "*merged*.{fq,fastq}.gz" - - - report_json: - type: file - description: JSON file with statistics for the concatenate stage - pattern: "*.report.json" - - - metadata: - type: file - description: Command invocation metadata files for pixelator concatenate - pattern: "*.meta.json" - - - log: - type: file - description: Pixelator log files - pattern: "*pixelator-concatenate*.log" - - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" - -authors: - - "@fbdtemme" diff --git a/modules/local/pixelator/single-cell/demux/meta.yaml b/modules/local/pixelator/single-cell/demux/meta.yaml deleted file mode 100644 index 6b6b837b..00000000 --- a/modules/local/pixelator/single-cell/demux/meta.yaml +++ /dev/null @@ -1,62 +0,0 @@ -name: pixelator/demux -description: Demultiplex molecular pixelation reads according to the panel file barcodes -keywords: - - "molecular pixelator" - - demultiplexing - - -tools: - - pixelator: - description: | - Software package to process sequencing data into Molecular Pixelation data. - homepage: https://github.com/PixelgenTechnologies/pixelator - documentation: https://github.com/PixelgenTechnologies/pixelator - doi: 10.1101/2023.06.05.543770 - licence: ["MIT"] - -input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - reads: - type: file - description: | - FastQ file containing the molecular pixelator amplicon. - - antibody_panel: - type: file - description: | - Panel file used by the molecular pixelator kit. - -output: - - processed: - type: file - description: All demultiplexed FastQ files - pattern: "*processed*.{fq,fastq}.gz" - - - failed: - type: file - description: FastQ file containing reads with invalid barcodes - pattern: "*failed.{fq,fastq}.gz" - - - report_json: - type: file - description: JSON file with statistics for the demux stage - pattern: "*.report.json" - - - metadata: - type: file - description: Command invocation metadata files for pixelator demux - pattern: "*.meta.json" - - - log: - type: file - description: Pixelator log files - pattern: "*pixelator-demux*.log" - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" - -authors: - - "@fbdtemme" diff --git a/modules/local/pixelator/single-cell/graph/meta.yaml b/modules/local/pixelator/single-cell/graph/meta.yaml deleted file mode 100644 index 77736b15..00000000 --- a/modules/local/pixelator/single-cell/graph/meta.yaml +++ /dev/null @@ -1,66 +0,0 @@ -name: pixelator/graph -description: | - Compute graph, components and other metrics from a edge list - produced by pixelator collapse -keywords: - - "molecular pixelator" -tools: - - pixelator: - description: | - Software package to process sequencing data into Molecular Pixelation data. - homepage: https://github.com/PixelgenTechnologies/pixelator - documentation: https://github.com/PixelgenTechnologies/pixelator - doi: 10.1101/2023.06.05.543770 - licence: ["MIT"] - -input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - reads: - type: file - description: | - List of input FastQ files of size 1 and 2 for single-end and paired-end data, - respectively. - -output: - - edgelist: - type: file - description: Filtered edgelist - pattern: "*.edgelist.csv.gz" - - - raw_edgelist: - type: file - description: Raw (unfiltered) edgelist - pattern: "*.raw_edgelist.csv.gz" - - - components_recovered: - type: file - description: CSV file with recovered components. Only available when the --multiplet-recovery flag is set. - pattern: "*.components_recovered.json" - optional: true - - - report_json: - type: file - description: JSON file with statistics for the graph stage - pattern: "*.report.json" - - - all_results: - type: file - description: All output files from the pixelator graph Command - pattern: "*" - - - log: - type: file - description: Pixelator log files - pattern: "*pixelator-graph*.log" - - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" - -authors: - - "@fbdtemme" diff --git a/modules/local/pixelator/single-cell/qc/meta.yaml b/modules/local/pixelator/single-cell/qc/meta.yaml deleted file mode 100644 index bdaaed6f..00000000 --- a/modules/local/pixelator/single-cell/qc/meta.yaml +++ /dev/null @@ -1,100 +0,0 @@ -name: pixelator/qc -description: Performs quality control before processing of molecular pixelation data -keywords: - - "molecular pixelator" - - qc - - fastp - - cutadapt -tools: - - pixelator: - description: | - Software package to process sequencing data into Molecular Pixelation data. - homepage: https://github.com/PixelgenTechnologies/pixelator - documentation: https://github.com/PixelgenTechnologies/pixelator - doi: 10.1101/2023.06.05.543770 - licence: ["MIT"] - -input: - - meta: - type: map - description: | - Groovy Map containing sample information - e.g. [ id:'test', single_end:false ] - - reads: - type: file - description: | - List of input FastQ files of size 1 and 2 for single-end and paired-end data, - respectively. -output: - - processed: - type: file - description: All processed FastQ files - pattern: "preqc/*.processed.{fq,fastq}.gz" - - - preqc_processed: - type: file - description: FastQ files containing all reads that passed the preqc stage - pattern: "adapterqc/*.processed.{fq,fastq}.gz" - - - adapterqc_processed: - type: file - description: FastQ files containing all reads that passed the adapter stage - pattern: "adapterqc/*.processed.{fq,fastq}.gz" - - - preqc_failed: - type: file - description: FastQ files containing all reads that were rejected by the preqc stage - pattern: "preqc/*.failed.{fq,fastq}.gz" - - - adapterqc_failed: - type: file - description: FastQ files containing all reads that were rejected by the adapter stage - pattern: "adapterqc/*.failed.{fq,fastq}.gz" - - - failed: - type: file - description: FastQ files containing all reads that were rejected by qc - pattern: "adapterqc/*.failed.{fq,fastq}.gz" - - - preqc_report: - type: file - description: JSON file with statistics for the preqc stage as reported by fastp - pattern: "preqc/*.report.json" - - - adapterqc_report: - type: file - description: JSON file with statistics for the adapoterqc stage as reported by cutadapt - pattern: "adapterqc/*.report.json" - - - report: - type: file - description: JSON files with QC metrics - pattern: "{preqc,adapterqc}/*.report.json" - - - preqc_metadata: - type: file - description: Command invocation metadata files for the pixelator preqc stage - pattern: "preqc/*.meta.json" - - - adapterqc_metadata: - type: file - description: Command invocation metadata files for the pixelator adapterqc stage - pattern: "adapterqc/*.meta.json" - - - metadata: - type: file - description: Command invocation metadata files for pixelator qc stages - pattern: "{preqc,adapterqc}/*.meta.json" - - - log: - type: file - description: Pixelator log files - pattern: "*pixelator-*.log" - - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" - -authors: - - "@fbdtemme" diff --git a/modules/local/samplesheet_check.nf b/modules/local/samplesheet_check.nf index 7c9f9780..aaff79c1 100644 --- a/modules/local/samplesheet_check.nf +++ b/modules/local/samplesheet_check.nf @@ -2,7 +2,8 @@ process SAMPLESHEET_CHECK { tag "$samplesheet" label 'process_single' - container "ghcr.io/pixelgentechnologies/pixelator:0.11.0" + // conda "local::pixelator=0.10.0" + // container "ghcr.io/pixelgentechnologies/pixelator:0.10.0" input: path samplesheet @@ -31,6 +32,7 @@ process SAMPLESHEET_CHECK { cat <<-END_VERSIONS > versions.yml "${task.process}": python: \$(python --version | sed 's/Python //g') + pixelator: \$(echo \$(pixelator --version 2>/dev/null) | sed 's/pixelator, version //g' ) END_VERSIONS """ } diff --git a/nextflow.config b/nextflow.config index 66a435c6..2743fd93 100644 --- a/nextflow.config +++ b/nextflow.config @@ -54,9 +54,7 @@ params { use_counts = false // graph options - multiplet_recovery = 'none' - fast_greedy_fraction = 0.5 - fast_greedy_cutoff = 5000 + multiplet_recovery = false leiden_iterations = 10 cluster_min_count = 2 @@ -72,8 +70,12 @@ params { compute_polarization = true compute_colocalization = true use_full_bipartite = false - normalization = 'clr' - binarization = false + polarization_normalization = "clr" + polarization_binarization = false + colocalization_transformation = "log1p" + colocalization_neighbourhood_size = 1 + colocalization_n_permutations = 50 + colocalization_min_region_count = 5 // skip options skip_report = false @@ -92,7 +94,7 @@ params { version = false validate_params = true show_hidden_params = false - schema_ignore_params = "" + schema_ignore_params = "awsqueue,awsregion" // Config options diff --git a/nextflow_schema.json b/nextflow_schema.json index 4be5dca5..7c32dd72 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -247,26 +247,8 @@ "title": "Options for pixelator graph command.", "properties": { "multiplet_recovery": { - "description": "Activate the multiplet recovery (leiden or fast-greedy): leiden: use the leiden algorithm (whole graph) fast-greedy: use fast greedy algorithm (per-component)", - "type": "string", - "enum": ["leiden", "fast-greedy", "none"], - "default": "null" - }, - "fast_greedy_fraction": { - "description": "Maximum fraction of edges allowed to be removed in a component for the fast-greedy algorithm [default: 0.05; 0.0<=x<=1.0]", - "type": "number", - "minimum": 0.0, - "maximum": 1.0, - "default": 0.05, - "hidden": true - }, - "fast_greedy_cutoff": { - "fa_icon": "fas less-than-equal", - "description": "Minimum size (edges) of a component to be considered for the fast-greedy algorithm [default: 5000]", - "type": "integer", - "default": 5000, - "hidden": true - + "description": "Activate the multiplet recovery using leiden community detection", + "type": "boolean" }, "leiden_iterations": { "fa_icon": "fas repeat", @@ -342,18 +324,42 @@ "type": "boolean", "default": false }, - "normalization": { + "polarization_normalization": { "description": "Which approach to use to normalize the antibody counts.", "help_text": "- `raw`: use the raw counts.\n- `CLR`: use the CLR-transformed counts.\n- `denoise`: use CLR-transformed counts and subtract the counts of control antibodies", "type": "string", "enum": ["raw", "clr", "denoise"], "default": "clr" }, - "binarization": { + "polarization_binarization": { "fa_icon": "fas binary", "description": "Transform the antibody counts to 0-1 (binarize) when computing polarization", "type": "boolean", "default": false + }, + "colocalization_transformation": { + "type": "string", + "enum": ["raw", "clr", "log1p", "relative"], + "default": "log1p", + "description": "Select the type of transformation to use on the node by antibody counts matrix when computing colocalization" + }, + "colocalization_neighbourhood_size": { + "type": "integer", + "description": "Select the size of the neighbourhood to use when computing colocalization metrics on each component", + "default": 1, + "minimum": 0 + }, + "colocalization_n_permutations": { + "type": "integer", + "description": "Set the number of permutations use to compute the empiricalp-value for the colocalization score", + "default": 50, + "minimum": 5 + }, + "colocalization_min_region_count": { + "type": "integer", + "description": "The minimum number of counts in a region for it to be concideredvalid for computing colocalization", + "default": 5, + "minimum": 0 } } }, @@ -534,7 +540,7 @@ }, "schema_ignore_params": { "type": "string", - "default": "", + "default": "awsqueue,awsregion", "description": "A comma separated string of inputs the schema validation should ignore." } } diff --git a/null/pipeline_info/execution_report_2023-06-23_10-03-02.html b/null/pipeline_info/execution_report_2023-06-23_10-03-02.html new file mode 100644 index 00000000..d2b87498 --- /dev/null +++ b/null/pipeline_info/execution_report_2023-06-23_10-03-02.html @@ -0,0 +1,1041 @@ + + + + + + + + + + + [nasty_payne] Nextflow Workflow Report + + + + + + + +
+
+ +

Nextflow workflow report

+

[nasty_payne] (resumed run)

+ + +
+

Workflow execution completed unsuccessfully!

+

The exit status of the task that caused the workflow execution to fail was: null.

+

The full error message was:

+
SIGINT
+
+ + +
+
Run times
+
+ 23-Jun-2023 10:03:03 - 23-Jun-2023 10:11:35 + (duration: 8m 32s) +
+ +
+
+
  0 succeeded  
+
  1 cached  
+
  0 ignored  
+
  0 failed  
+
+
+ +
Nextflow command
+
nextflow run . -profile test -resume -dump-channels
+
+ +
+
CPU-Hours
+
(a few seconds)
+ +
Launch directory
+
/home/fbdtemme/Documents/pixelgen/nf-core-pixelator
+ +
Work directory
+
/home/fbdtemme/Documents/pixelgen/nf-core-pixelator/work
+ +
Project directory
+
/home/fbdtemme/Documents/pixelgen/nf-core-pixelator
+ + +
Script name
+
main.nf
+ + + +
Script ID
+
b3274aaed8cf10ffcd20b4c363f189b7
+ + +
Workflow session
+
58b7b27f-74c3-49e7-8d00-c264bb09f1e7
+ + + +
Workflow profile
+
test
+ + + +
Nextflow version
+
version 23.04.1, build 5866 (15-04-2023 06:51 UTC)
+
+
+
+ +
+

Resource Usage

+

These plots give an overview of the distribution of resource usage for each process.

+ +

CPU

+ +
+
+
+
+
+
+
+ +
+ +

Memory

+ +
+
+
+
+
+
+
+
+
+
+
+ +

Job Duration

+ +
+
+
+
+
+
+
+
+ +

I/O

+ +
+
+
+
+
+
+
+
+
+ +
+
+

Tasks

+

This table shows information about each task in the workflow. Use the search box on the right + to filter rows for specific values. Clicking headers will sort the table by that value and + scrolling side to side will reveal more columns.

+
+ + +
+
+
+
+
+ +
+ (tasks table omitted because the dataset is too big) +
+
+ +
+
+ Generated by Nextflow, version 23.04.1 +
+
+ + + + + diff --git a/null/pipeline_info/execution_timeline_2023-06-23_10-03-02.html b/null/pipeline_info/execution_timeline_2023-06-23_10-03-02.html new file mode 100644 index 00000000..6739ceda --- /dev/null +++ b/null/pipeline_info/execution_timeline_2023-06-23_10-03-02.html @@ -0,0 +1,222 @@ + + + + + + + + + + + + +
+

Processes execution timeline

+

+ Launch time:
+ Elapsed time:
+ Legend: job wall time / memory usage (RAM) +

+
+
+ + + + + + + diff --git a/null/pipeline_info/pipeline_dag_2023-06-23_10-03-02.html b/null/pipeline_info/pipeline_dag_2023-06-23_10-03-02.html new file mode 100644 index 00000000..3ea531d7 --- /dev/null +++ b/null/pipeline_info/pipeline_dag_2023-06-23_10-03-02.html @@ -0,0 +1,425 @@ + + + + + + Nextflow Cytoscape.js with Dagre + + + + + + + + + + + +

Nextflow Cytoscape.js with Dagre

+
+ + + diff --git a/samplesheet.transformed.csv b/samplesheet.transformed.csv new file mode 100644 index 00000000..96273ae2 --- /dev/null +++ b/samplesheet.transformed.csv @@ -0,0 +1,4 @@ +sample,design,panel,fastq_1,fastq_2 +uropod_control_1,D21,az://testdata/micro/UNO_D21_conjV21.csv,az://testdata/micro/uropod_control_300k_S1_R1_001.fastq.gz,az://testdata/micro/uropod_control_300k_S1_R2_001.fastq.gz +uropod_control_1,D21,az://testdata/micro/UNO_D21_conjV21.csv,az://testdata/micro/uropod_control_300k_S1_R1_001.fastq.gz,az://testdata/micro/uropod_control_300k_S1_R2_001.fastq.gz +uropod_control_2,D21,az://testdata/micro/UNO_D21_conjV21.csv,az://testdata/micro/uropod_control_300k_S1_R1_001.fastq.gz,az://testdata/micro/uropod_control_300k_S1_R2_001.fastq.gz diff --git a/subworkflows/local/generate_reports.nf b/subworkflows/local/generate_reports.nf new file mode 100644 index 00000000..6abec16a --- /dev/null +++ b/subworkflows/local/generate_reports.nf @@ -0,0 +1,122 @@ +include { PIXELATOR_REPORT } from '../../modules/local/pixelator/single-cell/report/main' + + +workflow GENERATE_REPORTS { + take: + panels + concatenate_data + preqc_data + adapterqc_data + demux_data + collapse_data + graph_data + annotate_data + analysis_data + + main: + ch_versions = Channel.empty() + + ch_meta_col = panels + .map { meta, data -> [ meta.id, meta] } + .groupTuple() + .map { id, data -> + if (data instanceof List) { + def newMeta = [:] + for (item in data) { + newMeta += item + } + return [id, newMeta] + } + return [id, data] + } + + // Make sure panel files are unique, we can have duplicates if we concatenated multiple samples + ch_panels_col = panels + .map { meta, data -> [ meta.id, data] } + .groupTuple() + .map { id, data -> [ id, data.unique() ] } + + + ch_concatenate_col = concatenate_data + .map { meta, data -> [ meta.id, data] } + .groupTuple() + + ch_preqc_col = preqc_data + .map { meta, data -> [ meta.id, data] } + .groupTuple() + + ch_adapterqc_col = adapterqc_data + .map { meta, data -> [ meta.id, data] } + .groupTuple() + + ch_demux_col = demux_data + .map { meta, data -> [ meta.id, data] } + .groupTuple() + + ch_collapse_col = collapse_data + .map { meta, data -> [ meta.id, data] } + .groupTuple() + + ch_graph_col = graph_data + .map { meta, data -> [meta.id, data] } + .groupTuple() + + ch_annotate_col = annotate_data + .map { meta, data -> [meta.id, data] } + .groupTuple() + + ch_analysis_col = analysis_data + .map { meta, data -> [meta.id, data] } + .groupTuple() + + // Combine all inputs and group them to make per-stage channels have their output in the same order + // ch_report_data: [[ + // meta, panels_file, + // [concatenate files...], [preqc files...], [adapterqc files...], [demux files...], + // [collapse files...], [cluster files], [annotate files...], [analysis files...] + // ], ...] + ch_report_data = ch_meta_col + .concat ( ch_panels_col ) + .concat ( ch_concatenate_col ) + .concat ( ch_preqc_col ) + .concat ( ch_adapterqc_col ) + .concat ( ch_demux_col ) + .concat ( ch_collapse_col ) + .concat ( ch_graph_col ) + .concat ( ch_annotate_col ) + .concat ( ch_analysis_col ) + .groupTuple() + + // Split up everything per stage so we can recreate the expected directory structure for + // pixelator single-cell report using stageAs + + ch_meta_grouped = ch_report_data.map { id, data -> data[0] } + ch_panels_grouped = ch_report_data.map { id, data -> data[1] } + ch_concatenate_grouped = ch_report_data.map { id, data -> data[2] ? data[2].flatten() : [] } + ch_preqc_grouped = ch_report_data.map { id, data -> data[3] ? data[3].flatten() : [] } + ch_adapterqc_grouped = ch_report_data.map { id, data -> data[4] ? data[4].flatten() : [] } + ch_demux_grouped = ch_report_data.map { id, data -> data[5] ? data[5].flatten() : [] } + ch_collapse_grouped = ch_report_data.map { id, data -> data[6] ? data[6].flatten() : [] } + ch_graph_grouped = ch_report_data.map { id, data -> data[7] ? data[7].flatten() : [] } + ch_annotate_grouped = ch_report_data.map { id, data -> data[8] ? data[8].flatten() : [] } + ch_analysis_grouped = ch_report_data.map { id, data -> data[9] ? data[9].flatten() : [] } + + PIXELATOR_REPORT ( + ch_meta_grouped, + ch_panels_grouped, + ch_concatenate_grouped, + ch_preqc_grouped, + ch_adapterqc_grouped, + ch_demux_grouped, + ch_collapse_grouped, + ch_graph_grouped, + ch_annotate_grouped, + ch_analysis_grouped, + ) + + ch_versions = ch_versions.mix(PIXELATOR_REPORT.out.versions.first()) + + emit: + pixelator_reports = PIXELATOR_REPORT.out.reports + versions = ch_versions +} diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf index 8231d171..1bce4c2a 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -2,26 +2,67 @@ // Check input samplesheet and get read channels // -include { fromSamplesheet } from 'plugin/nf-validation' +include { SAMPLESHEET_CHECK } from '../../modules/local/samplesheet_check' workflow INPUT_CHECK { take: + samplesheet // file: /path/to/samplesheet.csv main: - ch_samplesheet = Channel.fromSamplesheet("input") + ch_samplesheet_rows = SAMPLESHEET_CHECK ( samplesheet, samplesheet.toUri() ) + .csv + .splitCsv ( header:true, sep:',' ) - reads = ch_samplesheet.map { meta, panel, fastq_1, fastq_2 -> - def r = [] - r.add(fastq_1) - if (fastq_2 != null) { - r.add(fastq_2) - } - [meta, r] - } - - panels = ch_samplesheet.map { meta, panel, fastq_1, fastq_2 -> [meta, panel] } + reads = ch_samplesheet_rows.map { create_fastq_channel(it) } + panels = ch_samplesheet_rows.map { create_panels_channel(it) } emit: reads // channel: [ val(meta), [ reads ] ] panels // channel: [ val(meta), panel ] + + versions = SAMPLESHEET_CHECK.out.versions // channel: [ versions.yml ] +} + + +def get_meta(LinkedHashMap row) { + def meta = [:] + meta.id = row.sample + meta.single_end = row.single_end.toBoolean() + meta.design = row.design + meta.group = row.group + meta.assay = row.assay + return meta +} + +// Function to get list of [ meta, [ fastq_1, fastq_2 ] ] +def create_fastq_channel(LinkedHashMap row) { + def meta = get_meta(row) + + // add path(s) of the fastq file(s) to the meta map + def fastq_meta = [] + + if (!file(row.fastq_1).exists()) { + exit 1, "ERROR: Please check input samplesheet -> Read 1 FastQ file does not exist!\n${row.fastq_1}" + } + + if (meta.single_end) { + fastq_meta = [ meta, [ file(row.fastq_1) ] ] + } else { + if (!file(row.fastq_2).exists()) { + exit 1, "ERROR: Please check input samplesheet -> Read 2 FastQ file does not exist!\n${row.fastq_2}" + } + fastq_meta = [ meta, [ file(row.fastq_1), file(row.fastq_2) ] ] + } + return fastq_meta +} + + +def create_panels_channel(LinkedHashMap row) { + def meta = get_meta(row) + + if (file(row.panel).exists()) { + return [ meta, file(row.panel) ] + } + + exit 1, "ERROR: Please check panel field: ${row.panel}: Could not find existing csv file." } diff --git a/workflows/pixelator.nf b/workflows/pixelator.nf index 19b8fd12..d123f26e 100644 --- a/workflows/pixelator.nf +++ b/workflows/pixelator.nf @@ -31,6 +31,7 @@ params.samplesheet_sha = ch_input.bytes.digest('sha-1') // SUBWORKFLOW: Consisting of a mix of local and nf-core/modules // include { INPUT_CHECK } from '../subworkflows/local/input_check' +include { GENERATE_REPORTS } from '../subworkflows/local/generate_reports' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -52,10 +53,8 @@ include { CAT_FASTQ } from '../modules/nf-core/cat/fastq/main' // // MODULE: Defined locally // - include { RENAME_READS } from '../modules/local/rename_reads' include { COLLECT_METADATA } from '../modules/local/collect_metadata' - include { PIXELATOR_CONCATENATE } from '../modules/local/pixelator/single-cell/concatenate/main' include { PIXELATOR_QC } from '../modules/local/pixelator/single-cell/qc/main' include { PIXELATOR_DEMUX } from '../modules/local/pixelator/single-cell/demux/main' @@ -63,7 +62,6 @@ include { PIXELATOR_COLLAPSE } from '../modules/local/pixelator/singl include { PIXELATOR_GRAPH } from '../modules/local/pixelator/single-cell/graph/main' include { PIXELATOR_ANALYSIS } from '../modules/local/pixelator/single-cell/analysis/main' include { PIXELATOR_ANNOTATE } from '../modules/local/pixelator/single-cell/annotate/main' -include { PIXELATOR_REPORT } from '../modules/local/pixelator/single-cell/report/main' /* ======================================================================================== @@ -77,7 +75,7 @@ def multiqc_report = [] workflow PIXELATOR { ch_versions = Channel.empty() - COLLECT_METADATA() + COLLECT_METADATA () ch_versions = ch_versions.mix(COLLECT_METADATA.out.versions) // @@ -85,7 +83,7 @@ workflow PIXELATOR { // // Create a new channel of metadata from a sample sheet // NB: `input` corresponds to `params.input` and associated sample sheet schema - INPUT_CHECK() + INPUT_CHECK ( ch_input ) ch_reads = INPUT_CHECK.out.reads ch_panels = INPUT_CHECK.out.panels @@ -133,7 +131,7 @@ workflow PIXELATOR { ch_input_reads = ch_merged - PIXELATOR_QC( ch_input_reads ) + PIXELATOR_QC ( ch_input_reads ) ch_qc = PIXELATOR_QC.out.processed ch_versions = ch_versions.mix(PIXELATOR_QC.out.versions.first()) @@ -142,7 +140,8 @@ workflow PIXELATOR { ch_demuxed = PIXELATOR_DEMUX.out.processed ch_versions = ch_versions.mix(PIXELATOR_DEMUX.out.versions.first()) - PIXELATOR_COLLAPSE ( ch_demuxed ) + ch_demuxed_and_panel = ch_demuxed.join(ch_panels) + PIXELATOR_COLLAPSE ( ch_demuxed_and_panel ) ch_collapsed = PIXELATOR_COLLAPSE.out.collapsed ch_versions = ch_versions.mix(PIXELATOR_COLLAPSE.out.versions.first()) @@ -154,108 +153,42 @@ workflow PIXELATOR { PIXELATOR_ANNOTATE ( ch_clustered_and_panel ) ch_annotated = PIXELATOR_ANNOTATE.out.dataset - ch_versions = ch_versions.mix(PIXELATOR_ANNOTATE.out.versions.first()) - - if (!params.skip_analysis) { - PIXELATOR_ANALYSIS ( ch_annotated ) - ch_analysed = PIXELATOR_ANALYSIS.out.dataset - ch_versions = ch_versions.mix(PIXELATOR_ANALYSIS.out.versions.first()) - } - - if (!params.skip_report) { - // Do some heroic transformations to split the inputs into their stages - // and have all these "stage output" channel output their files list in the same order - // Note that we need to split inout per stage to stage those files in the right subdirs - // as expected by pixelator single-cell report - - ch_meta_col = ch_panels.map { meta, data -> [ meta.id, meta] } - ch_panels_col = ch_panels.map { meta, data -> [ meta.id, data] } - - ch_concatenate_col = PIXELATOR_CONCATENATE.out.report_json.mix(PIXELATOR_CONCATENATE.out.metadata) - .map { meta, data -> [ meta.id, data] }.groupTuple() - - ch_preqc_col = PIXELATOR_QC.out.preqc_report_json.mix(PIXELATOR_QC.out.preqc_metadata) - .map { meta, data -> [ meta.id, data] } - .groupTuple() - - ch_adapterqc_col = PIXELATOR_QC.out.adapterqc_report_json.mix(PIXELATOR_QC.out.adapterqc_metadata) - .map { meta, data -> [ meta.id, data] } - .groupTuple() - - ch_demux_col = PIXELATOR_DEMUX.out.report_json.mix(PIXELATOR_DEMUX.out.metadata) - .map { meta, data -> [ meta.id, data] } - .groupTuple() - - ch_collapse_col = PIXELATOR_COLLAPSE.out.report_json.mix(PIXELATOR_COLLAPSE.out.metadata) - .map { meta, data -> [ meta.id, data] } - .groupTuple() - - ch_cluster_col = PIXELATOR_GRAPH.out.all_results - .map { meta, data -> [meta.id, data] } - .groupTuple() - - ch_annotate_col = PIXELATOR_ANNOTATE.out.all_results - .map { meta, data -> [meta.id, data] } - .groupTuple() - - - ch_analysis_col = null - if (!params.skip_analysis) { - ch_analysis_col = PIXELATOR_ANALYSIS.out.all_results - .map { meta, data -> [meta.id, data] } - .groupTuple() - } else { - ch_analysis_col = ch_meta_col.map { id, meta -> [id, []]} - } + ch_versions = ch_versions.mix( PIXELATOR_ANNOTATE.out.versions.first() ) + + PIXELATOR_ANALYSIS ( ch_annotated ) + ch_analysed = PIXELATOR_ANALYSIS.out.dataset + ch_versions = ch_versions.mix(PIXELATOR_ANALYSIS.out.versions.first()) + + + // Do some transformations to split the inputs into their stages + // and have all these "stage output" channels output in the same order + // Note that we need to split inout per stage to stage those files in the right subdirs + // as expected by pixelator single-cell report + + // Make sure meta objects are unique, we can have duplicates if we concatenated multiple samples + + ch_concatenate_data = PIXELATOR_CONCATENATE.out.report_json.mix(PIXELATOR_CONCATENATE.out.metadata) + ch_preqc_data = PIXELATOR_QC.out.preqc_report_json.mix(PIXELATOR_QC.out.preqc_metadata) + ch_adapterqc_data = PIXELATOR_QC.out.adapterqc_report_json.mix(PIXELATOR_QC.out.adapterqc_metadata) + ch_demux_data = PIXELATOR_DEMUX.out.report_json.mix(PIXELATOR_DEMUX.out.metadata) + ch_collapse_data = PIXELATOR_COLLAPSE.out.report_json.mix(PIXELATOR_COLLAPSE.out.metadata) + ch_cluster_data = PIXELATOR_GRAPH.out.all_results + ch_annotate_data = PIXELATOR_ANNOTATE.out.all_results + ch_analysis_data = PIXELATOR_ANALYSIS.out.all_results + + GENERATE_REPORTS( + ch_panels, + ch_concatenate_data, + ch_preqc_data, + ch_adapterqc_data, + ch_demux_data, + ch_collapse_data, + ch_cluster_data, + ch_annotate_data, + ch_analysis_data + ) - // Combine all inputs and group them to make per-stage channels have their output in the same order - // ch_report_data: [[ - // meta, panels_file, - // [concatenate files...], [preqc files...], [adapterqc files...], [demux files...], - // [collapse files...], [cluster files], [annotate files...], [analysis files...] - // ], ...] - ch_report_data = ch_meta_col - .concat ( ch_panels_col ) - .concat ( ch_concatenate_col ) - .concat ( ch_preqc_col ) - .concat ( ch_adapterqc_col ) - .concat ( ch_demux_col ) - .concat ( ch_collapse_col ) - .concat ( ch_cluster_col ) - .concat ( ch_annotate_col ) - .concat ( ch_analysis_col ) - .groupTuple() - - // Split up everything per stage so we can recreate the expected directory structure for - // pixelator single-cell report using stageAs - - ch_meta_grouped = ch_report_data.map { id, data -> data[0] } - ch_panels_grouped = ch_report_data.map { id, data -> data[1] } - ch_concatenate_grouped = ch_report_data.map { id, data -> data[2].flatten() } - ch_preqc_grouped = ch_report_data.map { id, data -> data[3].flatten() } - ch_adapterqc_grouped = ch_report_data.map { id, data -> data[4].flatten() } - ch_demux_grouped = ch_report_data.map { id, data -> data[5].flatten() } - ch_collapse_grouped = ch_report_data.map { id, data -> data[6].flatten() } - ch_cluster_grouped = ch_report_data.map { id, data -> data[7].flatten() } - ch_annotate_grouped = ch_report_data.map { id, data -> data[8].flatten() } - ch_analysis_grouped = ch_report_data.map { id, data -> data[9].flatten() } - - PIXELATOR_REPORT ( - ch_meta_grouped, - ch_panels_grouped, - ch_concatenate_grouped, - ch_preqc_grouped, - ch_adapterqc_grouped, - ch_demux_grouped, - ch_collapse_grouped, - ch_cluster_grouped, - ch_annotate_grouped, - ch_analysis_grouped, - ) - - - ch_versions = ch_versions.mix(PIXELATOR_REPORT.out.versions) - } + ch_versions = ch_versions.mix(GENERATE_REPORTS.out.versions) CUSTOM_DUMPSOFTWAREVERSIONS ( ch_versions.unique().collectFile(name: 'collated_versions.yml') From b510391c496c5b8c0bfe1db65fb6f935c2786f4c Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 23 Jun 2023 11:04:49 +0200 Subject: [PATCH 005/112] chore: remove report files --- .../execution_report_2023-06-23_10-03-02.html | 1041 ----------------- ...xecution_timeline_2023-06-23_10-03-02.html | 222 ---- .../pipeline_dag_2023-06-23_10-03-02.html | 425 ------- 3 files changed, 1688 deletions(-) delete mode 100644 null/pipeline_info/execution_report_2023-06-23_10-03-02.html delete mode 100644 null/pipeline_info/execution_timeline_2023-06-23_10-03-02.html delete mode 100644 null/pipeline_info/pipeline_dag_2023-06-23_10-03-02.html diff --git a/null/pipeline_info/execution_report_2023-06-23_10-03-02.html b/null/pipeline_info/execution_report_2023-06-23_10-03-02.html deleted file mode 100644 index d2b87498..00000000 --- a/null/pipeline_info/execution_report_2023-06-23_10-03-02.html +++ /dev/null @@ -1,1041 +0,0 @@ - - - - - - - - - - - [nasty_payne] Nextflow Workflow Report - - - - - - - -
-
- -

Nextflow workflow report

-

[nasty_payne] (resumed run)

- - -
-

Workflow execution completed unsuccessfully!

-

The exit status of the task that caused the workflow execution to fail was: null.

-

The full error message was:

-
SIGINT
-
- - -
-
Run times
-
- 23-Jun-2023 10:03:03 - 23-Jun-2023 10:11:35 - (duration: 8m 32s) -
- -
-
-
  0 succeeded  
-
  1 cached  
-
  0 ignored  
-
  0 failed  
-
-
- -
Nextflow command
-
nextflow run . -profile test -resume -dump-channels
-
- -
-
CPU-Hours
-
(a few seconds)
- -
Launch directory
-
/home/fbdtemme/Documents/pixelgen/nf-core-pixelator
- -
Work directory
-
/home/fbdtemme/Documents/pixelgen/nf-core-pixelator/work
- -
Project directory
-
/home/fbdtemme/Documents/pixelgen/nf-core-pixelator
- - -
Script name
-
main.nf
- - - -
Script ID
-
b3274aaed8cf10ffcd20b4c363f189b7
- - -
Workflow session
-
58b7b27f-74c3-49e7-8d00-c264bb09f1e7
- - - -
Workflow profile
-
test
- - - -
Nextflow version
-
version 23.04.1, build 5866 (15-04-2023 06:51 UTC)
-
-
-
- -
-

Resource Usage

-

These plots give an overview of the distribution of resource usage for each process.

- -

CPU

- -
-
-
-
-
-
-
- -
- -

Memory

- -
-
-
-
-
-
-
-
-
-
-
- -

Job Duration

- -
-
-
-
-
-
-
-
- -

I/O

- -
-
-
-
-
-
-
-
-
- -
-
-

Tasks

-

This table shows information about each task in the workflow. Use the search box on the right - to filter rows for specific values. Clicking headers will sort the table by that value and - scrolling side to side will reveal more columns.

-
- - -
-
-
-
-
- -
- (tasks table omitted because the dataset is too big) -
-
- -
-
- Generated by Nextflow, version 23.04.1 -
-
- - - - - diff --git a/null/pipeline_info/execution_timeline_2023-06-23_10-03-02.html b/null/pipeline_info/execution_timeline_2023-06-23_10-03-02.html deleted file mode 100644 index 6739ceda..00000000 --- a/null/pipeline_info/execution_timeline_2023-06-23_10-03-02.html +++ /dev/null @@ -1,222 +0,0 @@ - - - - - - - - - - - - -
-

Processes execution timeline

-

- Launch time:
- Elapsed time:
- Legend: job wall time / memory usage (RAM) -

-
-
- - - - - - - diff --git a/null/pipeline_info/pipeline_dag_2023-06-23_10-03-02.html b/null/pipeline_info/pipeline_dag_2023-06-23_10-03-02.html deleted file mode 100644 index 3ea531d7..00000000 --- a/null/pipeline_info/pipeline_dag_2023-06-23_10-03-02.html +++ /dev/null @@ -1,425 +0,0 @@ - - - - - - Nextflow Cytoscape.js with Dagre - - - - - - - - - - - -

Nextflow Cytoscape.js with Dagre

-
- - - From 47f455948b811b5cde2479810558a2baadb8e1c6 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 23 Jun 2023 11:09:10 +0200 Subject: [PATCH 006/112] chore: add CODEOWNERS file --- CODEOWNERS | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CODEOWNERS diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 00000000..45c65854 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,5 @@ +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence, +# @global-owner1 and @global-owner2 will be requested for +# review when someone opens a pull request. +* @fbdtemme From 578912e30e4dc9ad80e706b9f69d370302b220de Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 23 Jun 2023 11:26:13 +0200 Subject: [PATCH 007/112] style: reformat --- CITATIONS.md | 3 ++- assets/schema_input.json | 8 +++----- docs/output.md | 10 ++++++++-- docs/usage.md | 1 + 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/CITATIONS.md b/CITATIONS.md index 598e535c..e91c7033 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -11,15 +11,16 @@ ## Pipeline tools - [pixelator](https://doi.org/10.1101/2023.06.05.543770) + > Karlsson, Filip, Tomasz Kallas, Divya Thiagarajan, Max Karlsson, Maud Schweitzer, Jose Fernandez Navarro, Louise Leijonancker, et al. “Molecular Pixelation: Single Cell Spatial Proteomics by Sequencing.” bioRxiv, June 8, 2023. https://doi.org/10.1101/2023.06.05.543770. - [cutadapt] (http://dx.doi.org/10.14806/ej.17.1.200) + > Martin, Marcel. “Cutadapt Removes Adapter Sequences from High-Throughput Sequencing Reads.” EMBnet.Journal 17, no. 1 (May 2, 2011): 10–12. https://doi.org/10.14806/ej.17.1.200. - [fastp] (https://doi.org/10.1002/imt2.107) > Chen, Shifu. “Ultrafast One-Pass FASTQ Data Preprocessing, Quality Control, and Deduplication Using Fastp.” IMeta 2, no. 2 (2023): e107. https://doi.org/10.1002/imt2.107. - ## Software packaging/containerisation tools - [Anaconda](https://anaconda.com) diff --git a/assets/schema_input.json b/assets/schema_input.json index 7ffeeb06..1942aec3 100644 --- a/assets/schema_input.json +++ b/assets/schema_input.json @@ -11,14 +11,12 @@ "type": "string", "pattern": "^\\S+$", "errorMessage": "Sample name must be provided and cannot contain spaces", - "meta": [ "id" ] + "meta": ["id"] }, "design": { "type": "string", - "enum": [ - "D21" - ], - "meta": [ "design" ], + "enum": ["D21"], + "meta": ["design"], "errorMessage": "Design must be specified" }, "panel": { diff --git a/docs/output.md b/docs/output.md index 3bed53c0..c4b3fda5 100644 --- a/docs/output.md +++ b/docs/output.md @@ -36,7 +36,7 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d - `.report.json`: Q30 metrics of the amplicon. - `.meta.json`: Command invocation metadata. - `logs` - - *pixelator-concatenate.log`: pixelator log output. + - \*pixelator-concatenate.log`: pixelator log output. @@ -48,6 +48,7 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d Output files - `pixelator` + - `preqc` - `.processed.fastq.gz`: Processed reads. - `.failed.fastq.gz`: Discarded reads. @@ -71,6 +72,7 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d Output files - `pixelator` + - `demux` - `.processed-.fastq.gz`: Reads demultiplexed per antibody. - `.failed.fastq.gz`: Discarded reads that do not match an antibody barcode. @@ -90,6 +92,7 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d Output files - `pixelator` + - `adapterqc` - `.collapsed.csv.gz`: Edgelist of the graph. - `.report.json`: Statistics for the collapse step. @@ -108,6 +111,7 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d Output files - `pixelator` + - `cluster` - `.components_recovered.csv` - `.edgelist.csv.gz` @@ -129,6 +133,7 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d Output files - `pixelator` + - `annotate` - `.dataset.pxl` - `.meta.json`: Command invocation metadata. @@ -139,7 +144,7 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d - `logs` - `*pixelator-annotate.log`: pixelator log output. - + ### pixelator analysis @@ -149,6 +154,7 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d Output files - `pixelator` + - `analysis` - `.dataset.pxl` - `.meta.json`: Command invocation metadata. diff --git a/docs/usage.md b/docs/usage.md index c9dc9137..abb048ea 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -216,6 +216,7 @@ We recommend adding the following line to your environment to limit this (typica ```bash NXF_OPTS='-Xms1g -Xmx4g' ``` + # nf-core/pixelator: Usage ## :warning: Please read this documentation on the nf-core website: [https://nf-co.re/pixelator/usage](https://nf-co.re/pixelator/usage) From 01a54f2601eb9704bb29d06ad772e02834df421b Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 23 Jun 2023 11:45:12 +0200 Subject: [PATCH 008/112] feat: add params-file template --- assets/params-file.yml | 315 ++++++++++++++++++++++++++++++++ nextflow_schema.json | 6 +- utils/create-params-template.py | 89 +++++++++ 3 files changed, 407 insertions(+), 3 deletions(-) create mode 100644 assets/params-file.yml create mode 100755 utils/create-params-template.py diff --git a/assets/params-file.yml b/assets/params-file.yml new file mode 100644 index 00000000..95f9cc0b --- /dev/null +++ b/assets/params-file.yml @@ -0,0 +1,315 @@ +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## nf-core/pixelator parameter file +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## This is an example params-file.yaml for the `-params-file` option of +## nf-core/pixelator. +## Uncomment lines with a single '#' if you want to pass the parameter. +## ---------------------------------------------------------------------------------------- + +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## preqc_options +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +## ------------------------------------------------------------------------------------------ +## Trim N bases from the front of the reads +## ------------------------------------------------------------------------------------------ +# trim_front: 0 + +## ------------------------------------------------------------------------------------------ +## Trim N bases from the tail of the reads +## ------------------------------------------------------------------------------------------ +# trim_tail: 0 + +## ------------------------------------------------------------------------------------------ +## The maximum length of a read +## ------------------------------------------------------------------------------------------ +# max_length: null + +## ------------------------------------------------------------------------------------------ +## The minimum length (bases) of a read +## ------------------------------------------------------------------------------------------ +# min_length: null + +## ------------------------------------------------------------------------------------------ +## The maximum number of Ns allowed in a read +## ------------------------------------------------------------------------------------------ +# max_n_bases: 0 + +## ------------------------------------------------------------------------------------------ +## Minimum avg. quality a read must have (0 will disable the filter) +## ------------------------------------------------------------------------------------------ +# avg_qual: 20 + +## ------------------------------------------------------------------------------------------ +## Remove duplicated reads (exact same sequence) +## ------------------------------------------------------------------------------------------ +# dedup: false + +## ------------------------------------------------------------------------------------------ +## Remove PolyG sequences (length of 10 or more) +## ------------------------------------------------------------------------------------------ +# remove_polyg: false + +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## adapterqc_options +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +## ------------------------------------------------------------------------------------------ +## The number of mismatches allowed (in percentage) [default: 0.1; +## 0.0<=x<=0.9] +## ------------------------------------------------------------------------------------------ +# adapterqc_mismatches: 0.1 + +## ------------------------------------------------------------------------------------------ +## The PBS1 sequence that must be present in the reads. +## ------------------------------------------------------------------------------------------ +# pbs1: null + +## ------------------------------------------------------------------------------------------ +## The PBS2 sequence that must be present in the reads. +## ------------------------------------------------------------------------------------------ +# pbs2: null + +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## demux_options +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +## ------------------------------------------------------------------------------------------ +## The number of mismatches allowed (as a fraction) +## ------------------------------------------------------------------------------------------ +# demux_mismatches: 0.1 + +## ------------------------------------------------------------------------------------------ +## The minimum length of the barcode that must overlap when matching +## ------------------------------------------------------------------------------------------ +# demux_min_length: null + +## ------------------------------------------------------------------------------------------ +## Enforce the barcodes to be anchored (at the end of the read). +## ------------------------------------------------------------------------------------------ +# anchored: null + +## ------------------------------------------------------------------------------------------ +## Use the reverse complement of the barcodes sequences. Set to null +## to use the default specified by the design. +## ------------------------------------------------------------------------------------------ +# rev_complement: null + +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## collapse_options +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +## ------------------------------------------------------------------------------------------ +## A list of comma separated antibodies to discard +## ------------------------------------------------------------------------------------------ +# markers_ignore: null + +## ------------------------------------------------------------------------------------------ +## The algorithm to use for collapsing (adjacency will peform error +## correction using the number of mismatches given) +## ------------------------------------------------------------------------------------------ +# algorithm: adjacency + +## ------------------------------------------------------------------------------------------ +## The start position (0-based) of UPIA. If you set this argument it +## will overrrule the value from the chosen design +## ------------------------------------------------------------------------------------------ +# upia_start: null + +## ------------------------------------------------------------------------------------------ +## The end position (1-based) of UPIA. If you set this argument it +## will overrrule the value from the chosen design +## ------------------------------------------------------------------------------------------ +# upia_end: null + +## ------------------------------------------------------------------------------------------ +## The start position (0-based) of UPIB. If you set this argument it +## will overrrule the value from the chosen design +## ------------------------------------------------------------------------------------------ +# upib_start: null + +## ------------------------------------------------------------------------------------------ +## The end position (1-based) of UPIB. If you set this argument it +## will overrrule the value from the chosen design +## ------------------------------------------------------------------------------------------ +# upib_end: null + +## ------------------------------------------------------------------------------------------ +## The start position (0-based) of UMIA (disabled by default). If you +## set this argument it will overrrule the value from the chosen design +## ------------------------------------------------------------------------------------------ +# umia_start: null + +## ------------------------------------------------------------------------------------------ +## The end position (1-based) of UMIA (disabled by default). If you +## set this argument it will overrrule the value from the chosen design +## ------------------------------------------------------------------------------------------ +# umia_end: null + +## ------------------------------------------------------------------------------------------ +## The start position (0-based) of UMIB (disabled by default). If you +## set this argument it will overrrule the value from the chosen design +## ------------------------------------------------------------------------------------------ +# umib_start: null + +## ------------------------------------------------------------------------------------------ +## The end position (1-based) of UMIB (disabled by default). If you +## set this argument it will overrrule the value from the chosen design +## ------------------------------------------------------------------------------------------ +# umib_end: null + +## ------------------------------------------------------------------------------------------ +## The number of neighbours to use when searching for similar +## sequences (adjacency) This number depends on the sequence depth and +## the ratio of erroneous molecules expected. A high value can make the +## algorithm slower. +## ------------------------------------------------------------------------------------------ +# neighbours: 60 + +## ------------------------------------------------------------------------------------------ +## The number of mismatches allowed when collapsing (adjacency) +## ------------------------------------------------------------------------------------------ +# collapse_mismatches: 2 + +## ------------------------------------------------------------------------------------------ +## Discard molecules with with a count (reads) lower than this value +## ------------------------------------------------------------------------------------------ +# collapse_min_count: 2 + +## ------------------------------------------------------------------------------------------ +## Use counts when collapsing (the difference in counts between two +## molecules must be more than double in order to be collapsed) +## ------------------------------------------------------------------------------------------ +# use_counts: null + +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## graph_options +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +## ------------------------------------------------------------------------------------------ +## Activate the multiplet recovery using leiden community detection +## ------------------------------------------------------------------------------------------ +# multiplet_recovery: null + +## ------------------------------------------------------------------------------------------ +## Number of iterations for the leiden algorithm, high values will +## decrease the variance of the results but increase the runtime +## [default: 10; 1<=x<=100] +## ------------------------------------------------------------------------------------------ +# leiden_iterations: 10 + +## ------------------------------------------------------------------------------------------ +## Discard edges (pixels) with a count (reads) lower than this, use 1 +## to disable +## ------------------------------------------------------------------------------------------ +# cluster_min_count: 2 + +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## annotate_options +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +## ------------------------------------------------------------------------------------------ +## The minimum size (pixels) a component/cell can have (disabled by +## default) +## ------------------------------------------------------------------------------------------ +# min_size: null + +## ------------------------------------------------------------------------------------------ +## The maximum size (pixels) a component/cell can have (disabled by +## default) +## ------------------------------------------------------------------------------------------ +# max_size: null + +## ------------------------------------------------------------------------------------------ +## Enable the estimation of dynamic size filters using a log-rank +## approach both: estimate both min and max size, min: estimate min size +## (--min-size), max: estimate max size (--max-size) +## ------------------------------------------------------------------------------------------ +# dynamic_filter: min + +## ------------------------------------------------------------------------------------------ +## Enable cell type assignment using pre-trained models +## ------------------------------------------------------------------------------------------ +# cell_type_assignments: null + +## ------------------------------------------------------------------------------------------ +## Enable cell type majority voting using clustering of components +## ------------------------------------------------------------------------------------------ +# majority_vote: null + +## ------------------------------------------------------------------------------------------ +## Enable aggregate calling, information on potential aggregates will +## be added to the output data +## ------------------------------------------------------------------------------------------ +# aggregate_calling: null + +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## analysis_options +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +## ------------------------------------------------------------------------------------------ +## Skip analysis step +## ------------------------------------------------------------------------------------------ +# skip_analysis: false + +## ------------------------------------------------------------------------------------------ +## Compute polarization scores matrix (clusters by markers) +## ------------------------------------------------------------------------------------------ +# compute_polarization: true + +## ------------------------------------------------------------------------------------------ +## Compute colocalization scores (marker by marker) for each +## component +## ------------------------------------------------------------------------------------------ +# compute_colocalization: true + +## ------------------------------------------------------------------------------------------ +## Use the bipartite graph instead of the one-node projection when +## computing polarization, coabundance and colocalization scores +## ------------------------------------------------------------------------------------------ +# use_full_bipartite: false + +## ------------------------------------------------------------------------------------------ +## Which approach to use to normalize the antibody counts. +## ------------------------------------------------------------------------------------------ +# polarization_normalization: clr + +## ------------------------------------------------------------------------------------------ +## Transform the antibody counts to 0-1 (binarize) when computing +## polarization +## ------------------------------------------------------------------------------------------ +# polarization_binarization: false + +## ------------------------------------------------------------------------------------------ +## Select the type of transformation to use on the node by antibody +## counts matrix when computing colocalization +## ------------------------------------------------------------------------------------------ +# colocalization_transformation: log1p + +## ------------------------------------------------------------------------------------------ +## Select the size of the neighborhood to use when computing +## colocalization metrics on each component +## ------------------------------------------------------------------------------------------ +# colocalization_neighbourhood_size: 1 + +## ------------------------------------------------------------------------------------------ +## Set the number of permutations use to compute the empirical p-value +## for the colocalization score +## ------------------------------------------------------------------------------------------ +# colocalization_n_permutations: 50 + +## ------------------------------------------------------------------------------------------ +## The minimum number of counts in a region for it to be concidered +## valid for computing colocalization +## ------------------------------------------------------------------------------------------ +# colocalization_min_region_count: 5 + +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## report_options +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +## ------------------------------------------------------------------------------------------ +## Skip report generation +## ------------------------------------------------------------------------------------------ +# skip_report: false + diff --git a/nextflow_schema.json b/nextflow_schema.json index 7c32dd72..c9bd6262 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -345,19 +345,19 @@ }, "colocalization_neighbourhood_size": { "type": "integer", - "description": "Select the size of the neighbourhood to use when computing colocalization metrics on each component", + "description": "Select the size of the neighborhood to use when computing colocalization metrics on each component", "default": 1, "minimum": 0 }, "colocalization_n_permutations": { "type": "integer", - "description": "Set the number of permutations use to compute the empiricalp-value for the colocalization score", + "description": "Set the number of permutations use to compute the empirical p-value for the colocalization score", "default": 50, "minimum": 5 }, "colocalization_min_region_count": { "type": "integer", - "description": "The minimum number of counts in a region for it to be concideredvalid for computing colocalization", + "description": "The minimum number of counts in a region for it to be concidered valid for computing colocalization", "default": 5, "minimum": 0 } diff --git a/utils/create-params-template.py b/utils/create-params-template.py new file mode 100755 index 00000000..6eb5d0c8 --- /dev/null +++ b/utils/create-params-template.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 + +import argparse +import json +from pathlib import Path +from typing import Any, Dict +import textwrap + + +DEFAULT_SCHEMA_PATH = Path(__file__).parents[1] / "nextflow_schema.json" + + +GROUPS = { + "preqc_options", + "adapterqc_options", + "demux_options", + "collapse_options", + "graph_options", + "annotate_options", + "analysis_options", + "report_options", +} + + +def print_intro(): + print( + """ +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## nf-core/pixelator parameter file +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## This is an example params-file.yaml for the `-params-file` option of +## nf-core/pixelator. +## Uncomment lines with a single '#' if you want to pass the parameter. +## ---------------------------------------------------------------------------------------- +""" + ) + + +def render_params_file(schema: Dict[str, Any]): + definitions = schema["definitions"] + for definition_key, definition in definitions.items(): + if definition_key not in GROUPS: + continue + + comment = definition.get("description", definition_key) + properties = definition["properties"] + + print( + + f""" +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## {comment} +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +""") + + for prop_key, prop in properties.items(): + default_value = prop.get("default", None) + + if isinstance(default_value, bool): + default_value = str(default_value).lower() + + if default_value is None: + default_value = "null" + + description_lines = textwrap.wrap(f"## {prop.get('description', '')}") + + print("## ------------------------------------------------------------------------------------------") + print("\n## ".join(description_lines)) + print("## ------------------------------------------------------------------------------------------") + print(f"""# {prop_key}: {default_value}\n""") + + return + + +def main(args): + schema_file = args.schema + with schema_file.open("r") as f: + schema = json.load(f) + + print_intro() + render_params_file(schema) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument("--schema", type=Path, default=DEFAULT_SCHEMA_PATH) + + args = parser.parse_args() + main(args) From b8c37f6a84d083c0d8172276ac89e572badf3232 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 23 Jun 2023 12:57:01 +0200 Subject: [PATCH 009/112] style: fix formatting --- .pre-commit-config.yaml | 7 ++++++- utils/create-params-template.py | 6 +++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0c31cdb9..ca4aec07 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,10 @@ repos: - repo: https://github.com/pre-commit/mirrors-prettier - rev: "v2.7.1" + rev: "v3.0.0-alpha.9-for-vscode" hooks: - id: prettier + + - repo: https://github.com/psf/black + rev: 23.3.0 + hooks: + - id: black diff --git a/utils/create-params-template.py b/utils/create-params-template.py index 6eb5d0c8..eceecebc 100755 --- a/utils/create-params-template.py +++ b/utils/create-params-template.py @@ -46,12 +46,12 @@ def render_params_file(schema: Dict[str, Any]): properties = definition["properties"] print( - - f""" + f""" ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## {comment} ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -""") +""" + ) for prop_key, prop in properties.items(): default_value = prop.get("default", None) From 7ffc1dbe53fc3136f3c5481b3f917404ff95c695 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 23 Jun 2023 13:15:25 +0200 Subject: [PATCH 010/112] docs: update usage --- docs/usage.md | 267 +++++--------------------------------------------- 1 file changed, 27 insertions(+), 240 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index abb048ea..8824b815 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -6,277 +6,64 @@ ## Introduction - - ## Samplesheet input You will need to create a samplesheet with information about the samples you would like to analyse before running the pipeline. Use this parameter to specify its location. -It has to be a comma-separated file with 3 columns, and a header row as shown in the examples below. ```bash --input '[path to samplesheet file]' ``` -### Multiple runs of the same sample - -The `sample` identifiers have to be the same when you have re-sequenced the same sample more than once e.g. to increase sequencing depth. The pipeline will concatenate the raw reads before performing any downstream analysis. Below is an example for the same sample sequenced across 3 lanes: +An [example samplesheet](../assets/samplesheet.csv) has been provided with the pipeline. -```console -sample,fastq_1,fastq_2 -CONTROL_REP1,AEG588A1_S1_L002_R1_001.fastq.gz,AEG588A1_S1_L002_R2_001.fastq.gz -CONTROL_REP1,AEG588A1_S1_L003_R1_001.fastq.gz,AEG588A1_S1_L003_R2_001.fastq.gz -CONTROL_REP1,AEG588A1_S1_L004_R1_001.fastq.gz,AEG588A1_S1_L004_R2_001.fastq.gz -``` +### Format -### Full samplesheet +The samplesheet is a CSV or TSV formatted file with a few required and some optional columns. +You can export to CSV from spreadsheet programs such as Microsoft Excel, Google Sheets and LibreOffice Calc. -The pipeline will auto-detect whether a sample is single- or paired-end using the information provided in the samplesheet. The samplesheet can have as many columns as you desire, however, there is a strict requirement for the first 3 columns to match those defined in the table below. +Following table provides an overview of all possible columns in the samplesheet. +The samplesheet can have as many columns as you desire, however, there is a strict requirement for the first 5 columns +to match those defined in the table below. -A final samplesheet file consisting of both single- and paired-end data may look something like the one below. This is for 6 samples, where `TREATMENT_REP3` has been sequenced twice. +Below is an example of a simple samplesheet with two samples. -```console -sample,fastq_1,fastq_2 -CONTROL_REP1,AEG588A1_S1_L002_R1_001.fastq.gz,AEG588A1_S1_L002_R2_001.fastq.gz -CONTROL_REP2,AEG588A2_S2_L002_R1_001.fastq.gz,AEG588A2_S2_L002_R2_001.fastq.gz -CONTROL_REP3,AEG588A3_S3_L002_R1_001.fastq.gz,AEG588A3_S3_L002_R2_001.fastq.gz -TREATMENT_REP1,AEG588A4_S4_L003_R1_001.fastq.gz, -TREATMENT_REP2,AEG588A5_S5_L003_R1_001.fastq.gz, -TREATMENT_REP3,AEG588A6_S6_L003_R1_001.fastq.gz, -TREATMENT_REP3,AEG588A6_S6_L004_R1_001.fastq.gz, +```csv +sample,design,panel,fastq_1,fastq_2 +uropod_control,D21,UNO_D21.csv,uropod_control_S1_R1_001.fastq.gz,uropod_control_S1_R2_001.fastq.gz +uropod_stimulated,D21,UNO_D21.csv,uropod_stimulated_S1_R1_001.fastq.gz,uropod_stimulated_S1_R2_001.fastq.gz ``` +Columns not defined in the table below are ignored by the pipeline but can be useful +to add extra information for downstream processing. + | Column | Description | | --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `sample` | Custom sample name. This entry will be identical for multiple sequencing libraries/runs from the same sample. Spaces in sample names are automatically converted to underscores (`_`). | -| `fastq_1` | Full path to FastQ file for Illumina short reads 1. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | -| `fastq_2` | Full path to FastQ file for Illumina short reads 2. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | - -An [example samplesheet](../assets/samplesheet.csv) has been provided with the pipeline. - -## Running the pipeline - -The typical command for running the pipeline is as follows: - -```bash -nextflow run nf-core/pixelator --input samplesheet.csv --outdir --genome GRCh37 -profile docker -``` - -This will launch the pipeline with the `docker` configuration profile. See below for more information about profiles. - -Note that the pipeline will create the following files in your working directory: - -```bash -work # Directory containing the nextflow working files - # Finished results in specified location (defined with --outdir) -.nextflow_log # Log file from Nextflow -# Other nextflow hidden files, eg. history of pipeline runs and old logs. -``` - -If you wish to repeatedly use the same parameters for multiple runs, rather than specifying each flag in the command, you can specify these in a params file. - -Pipeline settings can be provided in a `yaml` or `json` file via `-params-file `. - -> ⚠️ Do not use `-c ` to specify parameters as this will result in errors. Custom config files specified with `-c` must only be used for [tuning process resource specifications](https://nf-co.re/docs/usage/configuration#tuning-workflow-resources), other infrastructural tweaks (such as output directories), or module arguments (args). -> The above pipeline run specified with a params file in yaml format: - -```bash -nextflow run nf-core/pixelator -profile docker -params-file params.yaml -``` - -with `params.yaml` containing: - -```yaml -input: './samplesheet.csv' -outdir: './results/' -genome: 'GRCh37' -input: 'data' -<...> -``` - -You can also generate such `YAML`/`JSON` files via [nf-core/launch](https://nf-co.re/launch). - -### Updating the pipeline - -When you run the above command, Nextflow automatically pulls the pipeline code from GitHub and stores it as a cached version. When running the pipeline after this, it will always use the cached version if available - even if the pipeline has been updated since. To make sure that you're running the latest version of the pipeline, make sure that you regularly update the cached version of the pipeline: - -```bash -nextflow pull nf-core/pixelator -``` - -### Reproducibility - -It is a good idea to specify a pipeline version when running the pipeline on your data. This ensures that a specific version of the pipeline code and software are used when you run your pipeline. If you keep using the same tag, you'll be running the same version of the pipeline, even if there have been changes to the code since. - -First, go to the [nf-core/pixelator releases page](https://github.com/nf-core/pixelator/releases) and find the latest pipeline version - numeric only (eg. `1.3.1`). Then specify this when running the pipeline with `-r` (one hyphen) - eg. `-r 1.3.1`. Of course, you can switch to another version by changing the number after the `-r` flag. - -This version number will be logged in reports when you run the pipeline, so that you'll know what you used when you look back in the future. For example, at the bottom of the MultiQC reports. - -To further assist in reproducbility, you can use share and re-use [parameter files](#running-the-pipeline) to repeat pipeline runs with the same settings without having to write out a command with every single parameter. - -> 💡 If you wish to share such profile (such as upload as supplementary material for academic publications), make sure to NOT include cluster specific paths to files, nor institutional specific profiles. - -## Core Nextflow arguments - -> **NB:** These options are part of Nextflow and use a _single_ hyphen (pipeline parameters use a double-hyphen). - -### `-profile` - -Use this parameter to choose a configuration profile. Profiles can give configuration presets for different compute environments. - -Several generic profiles are bundled with the pipeline which instruct the pipeline to use software packaged using different methods (Docker, Singularity, Podman, Shifter, Charliecloud, Apptainer, Conda) - see below. - -> We highly recommend the use of Docker or Singularity containers for full pipeline reproducibility, however when this is not possible, Conda is also supported. - -The pipeline also dynamically loads configurations from [https://github.com/nf-core/configs](https://github.com/nf-core/configs) when it runs, making multiple config profiles for various institutional clusters available at run time. For more information and to see if your system is available in these configs please see the [nf-core/configs documentation](https://github.com/nf-core/configs#documentation). - -Note that multiple profiles can be loaded, for example: `-profile test,docker` - the order of arguments is important! -They are loaded in sequence, so later profiles can overwrite earlier profiles. - -If `-profile` is not specified, the pipeline will run locally and expect all software to be installed and available on the `PATH`. This is _not_ recommended, since it can lead to different results on different machines dependent on the computer enviroment. - -- `test` - - A profile with a complete configuration for automated testing - - Includes links to test data so needs no other parameters -- `docker` - - A generic configuration profile to be used with [Docker](https://docker.com/) -- `singularity` - - A generic configuration profile to be used with [Singularity](https://sylabs.io/docs/) -- `podman` - - A generic configuration profile to be used with [Podman](https://podman.io/) -- `shifter` - - A generic configuration profile to be used with [Shifter](https://nersc.gitlab.io/development/shifter/how-to-use/) -- `charliecloud` - - A generic configuration profile to be used with [Charliecloud](https://hpc.github.io/charliecloud/) -- `apptainer` - - A generic configuration profile to be used with [Apptainer](https://apptainer.org/) -- `conda` - - A generic configuration profile to be used with [Conda](https://conda.io/docs/). Please only use Conda as a last resort i.e. when it's not possible to run the pipeline with Docker, Singularity, Podman, Shifter, Charliecloud, or Apptainer. - -### `-resume` - -Specify this when restarting a pipeline. Nextflow will use cached results from any pipeline steps where the inputs are the same, continuing from where it got to previously. For input to be considered the same, not only the names must be identical but the files' contents as well. For more info about this parameter, see [this blog post](https://www.nextflow.io/blog/2019/demystifying-nextflow-resume.html). - -You can also supply a run name to resume a specific run: `-resume [run-name]`. Use the `nextflow log` command to show previous run names. - -### `-c` - -Specify the path to a specific config file (this is a core Nextflow command). See the [nf-core website documentation](https://nf-co.re/usage/configuration) for more information. - -## Custom configuration - -### Resource requests - -Whilst the default requirements set within the pipeline will hopefully work for most people and with most input data, you may find that you want to customise the compute resources that the pipeline requests. Each step in the pipeline has a default set of requirements for number of CPUs, memory and time. For most of the steps in the pipeline, if the job exits with any of the error codes specified [here](https://github.com/nf-core/rnaseq/blob/4c27ef5610c87db00c3c5a3eed10b1d161abf575/conf/base.config#L18) it will automatically be resubmitted with higher requests (2 x original, then 3 x original). If it still fails after the third attempt then the pipeline execution is stopped. - -To change the resource requests, please see the [max resources](https://nf-co.re/docs/usage/configuration#max-resources) and [tuning workflow resources](https://nf-co.re/docs/usage/configuration#tuning-workflow-resources) section of the nf-core website. - -### Custom Containers - -In some cases you may wish to change which container or conda environment a step of the pipeline uses for a particular tool. By default nf-core pipelines use containers and software from the [biocontainers](https://biocontainers.pro/) or [bioconda](https://bioconda.github.io/) projects. However in some cases the pipeline specified version maybe out of date. - -To use a different container from the default container or conda environment specified in a pipeline, please see the [updating tool versions](https://nf-co.re/docs/usage/configuration#updating-tool-versions) section of the nf-core website. - -### Custom Tool Arguments - -A pipeline might not always support every possible argument or option of a particular tool used in pipeline. Fortunately, nf-core pipelines provide some freedom to users to insert additional parameters that the pipeline does not include by default. - -To learn how to provide additional arguments to a particular tool of the pipeline, please see the [customising tool arguments](https://nf-co.re/docs/usage/configuration#customising-tool-arguments) section of the nf-core website. - -### nf-core/configs - -In most cases, you will only need to create a custom config as a one-off but if you and others within your organisation are likely to be running nf-core pipelines regularly and need to use the same settings regularly it may be a good idea to request that your custom config file is uploaded to the `nf-core/configs` git repository. Before you do this please can you test that the config file works with your pipeline of choice using the `-c` parameter. You can then create a pull request to the `nf-core/configs` repository with the addition of your config file, associated documentation file (see examples in [`nf-core/configs/docs`](https://github.com/nf-core/configs/tree/master/docs)), and amending [`nfcore_custom.config`](https://github.com/nf-core/configs/blob/master/nfcore_custom.config) to include your custom profile. - -See the main [Nextflow documentation](https://www.nextflow.io/docs/latest/config.html) for more information about creating your own configuration files. - -If you have any questions or issues please send us a message on [Slack](https://nf-co.re/join/slack) on the [`#configs` channel](https://nfcore.slack.com/channels/configs). - -## Azure Resource Requests - -To be used with the `azurebatch` profile by specifying the `-profile azurebatch`. -We recommend providing a compute `params.vm_type` of `Standard_D16_v3` VMs by default but these options can be changed if required. - -Note that the choice of VM size depends on your quota and the overall workload during the analysis. -For a thorough list, please refer the [Azure Sizes for virtual machines in Azure](https://docs.microsoft.com/en-us/azure/virtual-machines/sizes). - -## Running in the background +| `design` | The name of the pixelator design configuration. | +| `panel` | Path to a csv file with antibody panel information. | +| `fastq_1` | Path to FastQ file for Illumina short reads 1. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | +| `fastq_2` | Path to FastQ file for Illumina short reads 2. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | -Nextflow handles job submissions and supervises the running jobs. The Nextflow process must run until the pipeline is finished. - -The Nextflow `-bg` flag launches Nextflow in the background, detached from your terminal so that the workflow does not stop if you log out of your session. The logs are saved to a file. - -Alternatively, you can use `screen` / `tmux` or similar tool to create a detached session which you can log back into at a later time. -Some HPC setups also allow you to run nextflow within a cluster job submitted your job scheduler (from where it submits more jobs). - -## Nextflow memory requirements - -In some cases, the Nextflow Java virtual machines can start to request a large amount of memory. -We recommend adding the following line to your environment to limit this (typically in `~/.bashrc` or `~./bash_profile`): - -```bash -NXF_OPTS='-Xms1g -Xmx4g' -``` - -# nf-core/pixelator: Usage - -## :warning: Please read this documentation on the nf-core website: [https://nf-co.re/pixelator/usage](https://nf-co.re/pixelator/usage) - -> _Documentation of pipeline parameters is generated automatically from the pipeline schema and can no longer be found in markdown files._ - -## Introduction - - - -## Samplesheet input - -You will need to create a samplesheet with information about the samples you would like to analyse before running the pipeline. Use this parameter to specify its location. It has to be a comma-separated file with 3 columns, and a header row as shown in the examples below. - -```bash ---input '[path to samplesheet file]' -``` +The pipeline will auto-detect whether a sample is single- or paired-end based on ifboth `fastq_1` and `fastq_2` or only `fastq_1` is present in the samplesheet. ### Multiple runs of the same sample The `sample` identifiers have to be the same when you have re-sequenced the same sample more than once e.g. to increase sequencing depth. The pipeline will concatenate the raw reads before performing any downstream analysis. Below is an example for the same sample sequenced across 3 lanes: -```console -sample,fastq_1,fastq_2 -CONTROL_REP1,AEG588A1_S1_L002_R1_001.fastq.gz,AEG588A1_S1_L002_R2_001.fastq.gz -CONTROL_REP1,AEG588A1_S1_L003_R1_001.fastq.gz,AEG588A1_S1_L003_R2_001.fastq.gz -CONTROL_REP1,AEG588A1_S1_L004_R1_001.fastq.gz,AEG588A1_S1_L004_R2_001.fastq.gz -``` - -### Full samplesheet - -The pipeline will auto-detect whether a sample is single- or paired-end using the information provided in the samplesheet. The samplesheet can have as many columns as you desire, however, there is a strict requirement for the first 3 columns to match those defined in the table below. - -A final samplesheet file consisting of both single- and paired-end data may look something like the one below. This is for 6 samples, where `TREATMENT_REP3` has been sequenced twice. - -```console -sample,fastq_1,fastq_2 -CONTROL_REP1,AEG588A1_S1_L002_R1_001.fastq.gz,AEG588A1_S1_L002_R2_001.fastq.gz -CONTROL_REP2,AEG588A2_S2_L002_R1_001.fastq.gz,AEG588A2_S2_L002_R2_001.fastq.gz -CONTROL_REP3,AEG588A3_S3_L002_R1_001.fastq.gz,AEG588A3_S3_L002_R2_001.fastq.gz -TREATMENT_REP1,AEG588A4_S4_L003_R1_001.fastq.gz, -TREATMENT_REP2,AEG588A5_S5_L003_R1_001.fastq.gz, -TREATMENT_REP3,AEG588A6_S6_L003_R1_001.fastq.gz, -TREATMENT_REP3,AEG588A6_S6_L004_R1_001.fastq.gz, +```csv +sample,design,panel,fastq_1,fastq_2 +uropod_control_1,D21,UNO_D21_Beta.csv,uropod_control_S1_L001_R1_001.fastq.gz,uropod_control_S1_L001_R2_001.fastq.gz +uropod_control_1,D21,UNO_D21_Beta.csv,uropod_control_S1_L002_R1_001.fastq.gz,uropod_control_S1_L002_R2_001.fastq.gz +uropod_control_1,D21,UNO_D21_Beta.csv,uropod_control_S1_L003_R1_001.fastq.gz,uropod_control_S1_L003_R2_001.fastq.gz ``` -| Column | Description | -| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `sample` | Custom sample name. This entry will be identical for multiple sequencing libraries/runs from the same sample. Spaces in sample names are automatically converted to underscores (`_`). | -| `fastq_1` | Full path to FastQ file for Illumina short reads 1. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | -| `fastq_2` | Full path to FastQ file for Illumina short reads 2. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | - -An [example samplesheet](../assets/samplesheet.csv) has been provided with the pipeline. - ## Running the pipeline The typical command for running the pipeline is as follows: ```bash -nextflow run nf-core/pixelator --input samplesheet.csv --outdir --genome GRCh37 -profile docker +nextflow run nf-core/pixelator --input samplesheet.csv --outdir -profile docker ``` This will launch the pipeline with the `docker` configuration profile. See below for more information about profiles. @@ -329,7 +116,7 @@ First, go to the [nf-core/pixelator releases page](https://github.com/nf-core/pi This version number will be logged in reports when you run the pipeline, so that you'll know what you used when you look back in the future. For example, at the bottom of the MultiQC reports. -To further assist in reproducbility, you can use share and re-use [parameter files](#running-the-pipeline) to repeat pipeline runs with the same settings without having to write out a command with every single parameter. +To further assist in reproducibility, you can use share and re-use [parameter files](#running-the-pipeline) to repeat pipeline runs with the same settings without having to write out a command with every single parameter. > 💡 If you wish to share such profile (such as upload as supplementary material for academic publications), make sure to NOT include cluster specific paths to files, nor institutional specific profiles. From 3d7729c7ed16a1a0c64a10f93f418c0f6dd90279 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 23 Jun 2023 14:48:54 +0200 Subject: [PATCH 011/112] feat: set test profile data to public s3 datasets --- conf/test.config | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/conf/test.config b/conf/test.config index 35a1483c..57c5146d 100644 --- a/conf/test.config +++ b/conf/test.config @@ -10,6 +10,10 @@ ---------------------------------------------------------------------------------------- */ + +aws.client.downloadParallel = true + + params { config_profile_name = 'Test profile' config_profile_description = 'Minimal test dataset to check pipeline function' @@ -19,8 +23,7 @@ params { max_memory = '6.GB' max_time = '6.h' - // TODO pixelgen: Move to public test data once available - input = "${params.testdata_root}/testdata/micro/test_samplesheet.csv" + input = "s3://pixelgen-technologies-datasets/nf-core-pixelator/testdata/micro/test_samplesheet.csv" outdir = "results" tracedir = "results" From 46d0c3b1fe8c99862582e8b6c7e2a099c988a40c Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 23 Jun 2023 17:57:14 +0200 Subject: [PATCH 012/112] docs: update README --- README.md | 41 +++- conf/modules.config | 4 +- docs/images/nf_core_pixelator_metromap.svg | 239 +++++++++++++++++++++ nextflow.config | 3 - nextflow_schema.json | 6 - 5 files changed, 272 insertions(+), 21 deletions(-) create mode 100644 docs/images/nf_core_pixelator_metromap.svg diff --git a/README.md b/README.md index 73fbd41d..5d0bc831 100644 --- a/README.md +++ b/README.md @@ -12,20 +12,30 @@ ## Introduction -**nf-core/pixelator** is a bioinformatics pipeline that ... - - - + +![](./docs/images/nf_core_pixelator_metromap.svg) + -1. Read QC ([`FastQC`](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/)) -2. Present QC for raw reads ([`MultiQC`](http://multiqc.info/)) +1. Build amplicon from input reads ([`pixelator concatenate`](https://github.com/PixelgenTechnologies/pixelator)) +2. Read QC and filtering, correctness of PBS sequences ([`pixelator preqc | pixelator adapterqc`](https://github.com/PixelgenTechnologies/pixelator)) +3. Assign a marker (barcode) to each read ([`pixelator demux`](https://github.com/PixelgenTechnologies/pixelator)) +4. Error correction, duplicate removal, compute read counts ([`pixelator collapse`](https://github.com/PixelgenTechnologies/pixelator)) +5. Compute the components/clusters of the graph from the edge list matrix.([`pixelator graph`](https://github.com/PixelgenTechnologies/pixelator)) +6. Analyze components/clusters of the graph.([`pixelator analysis`](https://github.com/PixelgenTechnologies/pixelator)) +7. Filter, annotate and call cells on samples ([`pixelator annotate`](https://github.com/PixelgenTechnologies/pixelator)) +8. Report generation ([`pixelator report`](https://github.com/PixelgenTechnologies/pixelator)) ## Usage @@ -50,9 +60,18 @@ Each row represents a fastq file (single-end) or a pair of fastq files (paired e --> -Now, you can run the pipeline using: +First, prepare a samplesheet with your input data that looks as follows: - +`samplesheet.csv`: + +```csv +sample,design,panel,fastq_1,fastq_2 +uropod_control,D21,UNO_D21_conjV21.csv,uropod_control_300k_S1_R1_001.fastq.gz,uropod_control_300k_S1_R2_001.fastq.gz +``` + +Each row represents a sample and gives the design, a panel file and the input fastq files. + +Now, you can run the pipeline using: ```bash nextflow run nf-core/pixelator \ @@ -76,9 +95,11 @@ For more details about the output files and reports, please refer to the ## Credits -nf-core/pixelator was originally written by Pixelgen Technologies AB. +nf-core/pixelator was originally written for [Pixelgen Technologies AB](https://www.pixelgen.tech/) by: -We thank the following people for their extensive assistance in the development of this pipeline: +- Florian De Temmerman +- Johan Dahlberg +- Alvaro Martinez Barrio diff --git a/conf/modules.config b/conf/modules.config index e7538f08..d0f62eee 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -20,11 +20,11 @@ process { ] - withName: 'COLLECT_METADATA|PIXELATOR_*' { + withName: 'COLLECT_METADATA|SAMPLESHEET_CHECK|PIXELATOR_*' { ext.singularity_pull_docker_container = true // Use this to override all usages of the pixelator container - // container = { get_pixelator_container(params) } + container = "ghcr.io/pixelgentechnologies/pixelator:pr-438" } withName: COLLECT_METADATA { diff --git a/docs/images/nf_core_pixelator_metromap.svg b/docs/images/nf_core_pixelator_metromap.svg new file mode 100644 index 00000000..288ba2c2 --- /dev/null +++ b/docs/images/nf_core_pixelator_metromap.svg @@ -0,0 +1,239 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/nextflow.config b/nextflow.config index 2743fd93..81c51d43 100644 --- a/nextflow.config +++ b/nextflow.config @@ -111,9 +111,6 @@ params { max_memory = '128.GB' max_cpus = 16 max_time = '240.h' - - // Testdata options - testdata_root = "../nf-core-pixelator-datasets" } // Load base.config by default for all pipelines diff --git a/nextflow_schema.json b/nextflow_schema.json index c9bd6262..288f4e97 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -532,12 +532,6 @@ "hidden": true, "help_text": "By default, parameters set as _hidden_ in the schema are not shown on the command line when a user runs with `--help`. Specifying this option will tell the pipeline to show all parameters." }, - "testdata_root": { - "type": "string", - "description": "Root path to testdata for running local tests", - "hidden": true, - "fa_icon": "fas fa-folder-tree" - }, "schema_ignore_params": { "type": "string", "default": "awsqueue,awsregion", From 1f64f56d5fd17466bc20f191cd967c25beb34f3b Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 23 Jun 2023 18:22:15 +0200 Subject: [PATCH 013/112] fix: add missing label to metromap --- docs/images/nf_core_pixelator_metromap.svg | 35 +++++++++++++--------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/docs/images/nf_core_pixelator_metromap.svg b/docs/images/nf_core_pixelator_metromap.svg index 288ba2c2..9232030d 100644 --- a/docs/images/nf_core_pixelator_metromap.svg +++ b/docs/images/nf_core_pixelator_metromap.svg @@ -1,10 +1,11 @@ + - + @@ -23,11 +24,14 @@ + - - + + + + + - @@ -95,7 +99,7 @@ - + @@ -108,10 +112,10 @@ - + - + @@ -120,7 +124,7 @@ - + @@ -160,7 +164,7 @@ - + @@ -218,21 +222,24 @@ - + - + - + - + - + + + + From d412f1611fd51d90a72f91bbcf6df82b17bf68c5 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 23 Jun 2023 18:43:33 +0200 Subject: [PATCH 014/112] fix: update used container --- conf/modules.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules.config b/conf/modules.config index d0f62eee..13d36ec9 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -24,7 +24,7 @@ process { ext.singularity_pull_docker_container = true // Use this to override all usages of the pixelator container - container = "ghcr.io/pixelgentechnologies/pixelator:pr-438" + container = "ghcr.io/pixelgentechnologies/pixelator:sha-fa7fd41" } withName: COLLECT_METADATA { From d46ee97c1878066794af468476589d56d6821541 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 26 Jun 2023 18:37:29 +0200 Subject: [PATCH 015/112] feat: fix container override --- conf/modules.config | 17 ++++++++++++----- conf/test.config | 14 ++++++-------- nextflow.config | 3 +++ nextflow_schema.json | 16 ++++++++++++++++ 4 files changed, 37 insertions(+), 13 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 13d36ec9..a78e5007 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -20,11 +20,10 @@ process { ] - withName: 'COLLECT_METADATA|SAMPLESHEET_CHECK|PIXELATOR_*' { - ext.singularity_pull_docker_container = true - - // Use this to override all usages of the pixelator container - container = "ghcr.io/pixelgentechnologies/pixelator:sha-fa7fd41" + withName: 'SAMPLESHEET_CHECK' { + if (params.pixelator_container) { + container = params.pixelator_container + } } withName: COLLECT_METADATA { @@ -33,6 +32,10 @@ process { mode: params.publish_dir_mode, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] + + if (params.pixelator_container) { + container = params.pixelator_container + } } withName: "PIXELATOR.*" { @@ -48,6 +51,10 @@ process { pattern: "*.log" ] ] + + if (params.pixelator_container) { + container = params.pixelator_container + } } withName: RENAME_READS { diff --git a/conf/test.config b/conf/test.config index 57c5146d..5b22be6a 100644 --- a/conf/test.config +++ b/conf/test.config @@ -27,14 +27,12 @@ params { outdir = "results" tracedir = "results" - collapse_mismatches = 1 - cluster_min_count = 2 - min_size = 1 - max_size = 10000 - dynamic_filter = false - cell_type_assignments = false - majority_vote = false + multiplet_recovery = true + min_size = 2 + max_size = 100000 compute_polarization = true - compute_colocalization = false use_full_bipartite = true + colocalization_min_region_count = 0 + colocalization_n_permutations = 10 + colocalization_neighbourhood_size = 1 } diff --git a/nextflow.config b/nextflow.config index 81c51d43..785f6023 100644 --- a/nextflow.config +++ b/nextflow.config @@ -81,6 +81,9 @@ params { skip_report = false skip_analysis = false + // Pipeline specific global config options + pixelator_container = "ghcr.io/pixelgentechnologies/pixelator:pr-441" + // Boilerplate options outdir = null tracedir = "${params.outdir}/pipeline_info" diff --git a/nextflow_schema.json b/nextflow_schema.json index 288f4e97..4966e4fe 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -373,6 +373,19 @@ } } }, + "global_config_options": { + "title": "Global options", + "type": "object", + "description": "Global configuration options specific to nf-core/pixelator.", + "properties": { + "pixelator_container": { + "type": "string", + "format": "url", + "description": "Override the container image reference to use for all steps using the `pixelator` command.", + "help_text": "Use this to force the pipeline to use a different image version in all steps that use the pixelator command.\nThe pipeline is not garanteed to work when using different pixelator versions." + } + } + }, "institutional_config_options": { "title": "Institutional config options", "type": "object", @@ -568,6 +581,9 @@ { "$ref": "#/definitions/report_options" }, + { + "$ref": "#/definitions/global_config_options" + }, { "$ref": "#/definitions/institutional_config_options" }, From dc600784a67080ff06a99d6a23145b5714c50712 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 26 Jun 2023 18:37:58 +0200 Subject: [PATCH 016/112] feat: bump container versions --- modules/local/collect_metadata.nf | 2 +- modules/local/pixelator/single-cell/analysis/main.nf | 2 +- modules/local/pixelator/single-cell/annotate/main.nf | 2 +- modules/local/pixelator/single-cell/collapse/main.nf | 2 +- modules/local/pixelator/single-cell/concatenate/main.nf | 2 +- modules/local/pixelator/single-cell/demux/main.nf | 2 +- modules/local/pixelator/single-cell/graph/main.nf | 2 +- modules/local/pixelator/single-cell/qc/main.nf | 2 +- modules/local/pixelator/single-cell/report/main.nf | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/modules/local/collect_metadata.nf b/modules/local/collect_metadata.nf index e9cbee0c..211a4493 100644 --- a/modules/local/collect_metadata.nf +++ b/modules/local/collect_metadata.nf @@ -9,7 +9,7 @@ process COLLECT_METADATA { cache false conda "local::pixelator=0.10.0" - container 'ghcr.io/pixelgentechnologies/pixelator:0.11.0' + container 'ghcr.io/pixelgentechnologies/pixelator:0.12.0' input: diff --git a/modules/local/pixelator/single-cell/analysis/main.nf b/modules/local/pixelator/single-cell/analysis/main.nf index 24969681..b37d8e8a 100644 --- a/modules/local/pixelator/single-cell/analysis/main.nf +++ b/modules/local/pixelator/single-cell/analysis/main.nf @@ -4,7 +4,7 @@ process PIXELATOR_ANALYSIS { label 'process_medium' conda "local::pixelator=0.10.0" - container "ghcr.io/pixelgentechnologies/pixelator:0.11.0" + container "ghcr.io/pixelgentechnologies/pixelator:0.12.0" input: tuple val(meta), path(data) diff --git a/modules/local/pixelator/single-cell/annotate/main.nf b/modules/local/pixelator/single-cell/annotate/main.nf index 3f488c0c..525cdfec 100644 --- a/modules/local/pixelator/single-cell/annotate/main.nf +++ b/modules/local/pixelator/single-cell/annotate/main.nf @@ -4,7 +4,7 @@ process PIXELATOR_ANNOTATE { label 'process_medium' conda "local::pixelator=0.10.0" - container "ghcr.io/pixelgentechnologies/pixelator:0.11.0" + container "ghcr.io/pixelgentechnologies/pixelator:0.12.0" input: tuple val(meta), path(dataset), path(panel) diff --git a/modules/local/pixelator/single-cell/collapse/main.nf b/modules/local/pixelator/single-cell/collapse/main.nf index 3afa135b..f2171167 100644 --- a/modules/local/pixelator/single-cell/collapse/main.nf +++ b/modules/local/pixelator/single-cell/collapse/main.nf @@ -7,7 +7,7 @@ process PIXELATOR_COLLAPSE { conda "local::pixelator=0.10.0" - container "ghcr.io/pixelgentechnologies/pixelator:0.11.0" + container "ghcr.io/pixelgentechnologies/pixelator:0.12.0" input: tuple val(meta), path(reads), path(panel) diff --git a/modules/local/pixelator/single-cell/concatenate/main.nf b/modules/local/pixelator/single-cell/concatenate/main.nf index b4240268..faf71233 100644 --- a/modules/local/pixelator/single-cell/concatenate/main.nf +++ b/modules/local/pixelator/single-cell/concatenate/main.nf @@ -7,7 +7,7 @@ process PIXELATOR_CONCATENATE { conda "local::pixelator=0.10.0" - container "ghcr.io/pixelgentechnologies/pixelator:0.11.0" + container "ghcr.io/pixelgentechnologies/pixelator:0.12.0" input: tuple val(meta), path(reads) diff --git a/modules/local/pixelator/single-cell/demux/main.nf b/modules/local/pixelator/single-cell/demux/main.nf index 996d3076..b0703ea1 100644 --- a/modules/local/pixelator/single-cell/demux/main.nf +++ b/modules/local/pixelator/single-cell/demux/main.nf @@ -6,7 +6,7 @@ process PIXELATOR_DEMUX { conda "local::pixelator=0.10.0" - container "ghcr.io/pixelgentechnologies/pixelator:0.11.0" + container "ghcr.io/pixelgentechnologies/pixelator:0.12.0" input: tuple val(meta), path(reads), path(antibody_panel) diff --git a/modules/local/pixelator/single-cell/graph/main.nf b/modules/local/pixelator/single-cell/graph/main.nf index 914de8b0..89abd27f 100644 --- a/modules/local/pixelator/single-cell/graph/main.nf +++ b/modules/local/pixelator/single-cell/graph/main.nf @@ -6,7 +6,7 @@ process PIXELATOR_GRAPH { conda "local::pixelator=0.10.0" - container "ghcr.io/pixelgentechnologies/pixelator:0.11.0" + container "ghcr.io/pixelgentechnologies/pixelator:0.12.0" input: tuple val(meta), path(edge_list) diff --git a/modules/local/pixelator/single-cell/qc/main.nf b/modules/local/pixelator/single-cell/qc/main.nf index 5ba0cb3a..458f4323 100644 --- a/modules/local/pixelator/single-cell/qc/main.nf +++ b/modules/local/pixelator/single-cell/qc/main.nf @@ -6,7 +6,7 @@ process PIXELATOR_QC { conda "local::pixelator=0.10.0" // TODO: make pixelator available on galaxyproject and quay.io support - container "ghcr.io/pixelgentechnologies/pixelator:0.11.0" + container "ghcr.io/pixelgentechnologies/pixelator:0.12.0" input: tuple val(meta), path(reads) diff --git a/modules/local/pixelator/single-cell/report/main.nf b/modules/local/pixelator/single-cell/report/main.nf index 96d585f9..ca3dca98 100644 --- a/modules/local/pixelator/single-cell/report/main.nf +++ b/modules/local/pixelator/single-cell/report/main.nf @@ -6,7 +6,7 @@ process PIXELATOR_REPORT { conda "local::pixelator=0.10.0" - container "ghcr.io/pixelgentechnologies/pixelator:0.11.0" + container "ghcr.io/pixelgentechnologies/pixelator:0.12.0" input: val meta From 4055280e0056a04c048bd6262457d42297d45209 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 28 Jun 2023 14:10:43 +0200 Subject: [PATCH 017/112] support panel and panel_file --- assets/params-file.yml | 63 +----------------- assets/schema_input.json | 15 ++++- bin/check_samplesheet.py | 26 ++++++-- conf/modules.config | 14 +--- docs/usage.md | 22 ++++--- .../pixelator/single-cell/annotate/main.nf | 6 +- .../pixelator/single-cell/collapse/main.nf | 6 +- .../local/pixelator/single-cell/demux/main.nf | 7 +- .../pixelator/single-cell/report/main.nf | 7 +- nextflow.config | 21 ++---- nextflow_schema.json | 65 +------------------ subworkflows/local/generate_reports.nf | 30 ++++++--- subworkflows/local/input_check.nf | 14 ++-- workflows/pixelator.nf | 32 +++++---- 14 files changed, 124 insertions(+), 204 deletions(-) diff --git a/assets/params-file.yml b/assets/params-file.yml index 95f9cc0b..0f85533c 100644 --- a/assets/params-file.yml +++ b/assets/params-file.yml @@ -84,17 +84,6 @@ ## ------------------------------------------------------------------------------------------ # demux_min_length: null -## ------------------------------------------------------------------------------------------ -## Enforce the barcodes to be anchored (at the end of the read). -## ------------------------------------------------------------------------------------------ -# anchored: null - -## ------------------------------------------------------------------------------------------ -## Use the reverse complement of the barcodes sequences. Set to null -## to use the default specified by the design. -## ------------------------------------------------------------------------------------------ -# rev_complement: null - ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## collapse_options ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -110,54 +99,6 @@ ## ------------------------------------------------------------------------------------------ # algorithm: adjacency -## ------------------------------------------------------------------------------------------ -## The start position (0-based) of UPIA. If you set this argument it -## will overrrule the value from the chosen design -## ------------------------------------------------------------------------------------------ -# upia_start: null - -## ------------------------------------------------------------------------------------------ -## The end position (1-based) of UPIA. If you set this argument it -## will overrrule the value from the chosen design -## ------------------------------------------------------------------------------------------ -# upia_end: null - -## ------------------------------------------------------------------------------------------ -## The start position (0-based) of UPIB. If you set this argument it -## will overrrule the value from the chosen design -## ------------------------------------------------------------------------------------------ -# upib_start: null - -## ------------------------------------------------------------------------------------------ -## The end position (1-based) of UPIB. If you set this argument it -## will overrrule the value from the chosen design -## ------------------------------------------------------------------------------------------ -# upib_end: null - -## ------------------------------------------------------------------------------------------ -## The start position (0-based) of UMIA (disabled by default). If you -## set this argument it will overrrule the value from the chosen design -## ------------------------------------------------------------------------------------------ -# umia_start: null - -## ------------------------------------------------------------------------------------------ -## The end position (1-based) of UMIA (disabled by default). If you -## set this argument it will overrrule the value from the chosen design -## ------------------------------------------------------------------------------------------ -# umia_end: null - -## ------------------------------------------------------------------------------------------ -## The start position (0-based) of UMIB (disabled by default). If you -## set this argument it will overrrule the value from the chosen design -## ------------------------------------------------------------------------------------------ -# umib_start: null - -## ------------------------------------------------------------------------------------------ -## The end position (1-based) of UMIB (disabled by default). If you -## set this argument it will overrrule the value from the chosen design -## ------------------------------------------------------------------------------------------ -# umib_end: null - ## ------------------------------------------------------------------------------------------ ## The number of neighbours to use when searching for similar ## sequences (adjacency) This number depends on the sequence depth and @@ -180,7 +121,7 @@ ## Use counts when collapsing (the difference in counts between two ## molecules must be more than double in order to be collapsed) ## ------------------------------------------------------------------------------------------ -# use_counts: null +# collapse_use_counts: null ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## graph_options @@ -202,7 +143,7 @@ ## Discard edges (pixels) with a count (reads) lower than this, use 1 ## to disable ## ------------------------------------------------------------------------------------------ -# cluster_min_count: 2 +# graph_min_count: 2 ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## annotate_options diff --git a/assets/schema_input.json b/assets/schema_input.json index 1942aec3..71976eab 100644 --- a/assets/schema_input.json +++ b/assets/schema_input.json @@ -20,9 +20,18 @@ "errorMessage": "Design must be specified" }, "panel": { - "type": "string", - "pattern": "^\\S+.(csv|tsv)$", - "errorMessage": "Panel file must be provided, cannot contain spaces and must have extension '.csv'" + "anyOf": [ + { + "type": "string", + "pattern": "^\\S+.(csv|tsv)$", + "errorMessage": "Panel file or panel name must be provided, cannot contain spaces and must have extension '.csv'" + }, + { + "type": "string", + "enum": ["human-sc-immunology-spatial-proteomics"], + "errorMessage": "Panel name must be specified" + } + ] }, "fastq_1": { "type": "string", diff --git a/bin/check_samplesheet.py b/bin/check_samplesheet.py index 5cf6b9e7..e6755af8 100755 --- a/bin/check_samplesheet.py +++ b/bin/check_samplesheet.py @@ -58,7 +58,7 @@ def make_absolute_path(path: str, base: PathLike = None) -> str: def validate_whitespace(row: MutableMapping[str, str], index: int): sample_name = row["sample"] for k, v in row.items(): - if re.search("^\s+|s+$", v): + if re.search("^\s+|\s+$", v): raise AssertionError( f'The sample sheet contains leading or trailing whitespaces in column "{k}" for sample "{sample_name}". ' "Remove whitespace or enclose with quotes!" @@ -197,13 +197,14 @@ def _validate_fastq_format(self, filename): class PixelatorRowChecker(RowChecker): DEFAULT_GROUP = "default" - REQUIRED_COLUMNS = ["sample", "design", "panel", "fastq_1", "fastq_2"] + REQUIRED_COLUMNS = ["sample", "design", "fastq_1", "fastq_2"] def __init__(self, samplesheet_path=None, design_options: Optional[Set[str]] = None, **kwargs): super().__init__( sample_col="sample", first_col="fastq_1", second_col="fastq_2", single_col="single_end", **kwargs ) self._panel_col = "panel" + self._panel_file_col = "panel_file" self._design_col = "design" self._samplesheet_path = samplesheet_path self._base_dir = self.get_base_dir(samplesheet_path) if samplesheet_path else None @@ -231,17 +232,29 @@ def _validate_design(self, row): def _validate_panelfile(self, row): """Assert that the panel column exists and has supported values.""" - if len(row[self._panel_col]) <= 0: - raise AssertionError(f"The {self._panel_col} field is required.") + has_panel_col = self._panel_col in row and len(row[self._panel_col]) > 0 + has_panel_file_col = self._panel_file_col in row and len(row[self._panel_file_col]) > 0 + + if not has_panel_col and not has_panel_file_col: + raise AssertionError(f"Either {self._panel_col} or {self._panel_file_col} field is required.") + + if has_panel_col and has_panel_file_col: + raise AssertionError( + f"Both {self._panel_col} and {self._panel_file_col} field are set. " + f"Only one must be set {self._panel_col} and the other column left empty." + ) def _resolve_relative_paths(self, row): + """Make filepaths in samplesheet absolute if needed""" first = make_absolute_path(row[self._first_col], self._base_dir) second = make_absolute_path(row[self._second_col], self._base_dir) - panel = make_absolute_path(row[self._panel_col], self._base_dir) + + if self._panel_file_col in row: + if row[self._panel_file_col]: + row[self._panel_file_col] = make_absolute_path(row[self._panel_file_col], self._base_dir) row[self._first_col] = first row[self._second_col] = second - row[self._panel_col] = panel def validate_and_transform(self, row): """ @@ -257,6 +270,7 @@ def validate_and_transform(self, row): self._validate_first(row) self._validate_second(row) self._validate_pair(row) + self._validate_panelfile(row) self._resolve_relative_paths(row) self._seen.add((row[self._sample_col], row[self._first_col])) self.modified.append(row) diff --git a/conf/modules.config b/conf/modules.config index a78e5007..c89da172 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -108,8 +108,6 @@ process { "--design ${meta.design}", (params.demux_mismatches != null) ? "--mismatches ${params.demux_mismatches}": '', (params.demux_min_length != null) ? "--mismatches ${params.demux_min_length}": '', - (params.anchored != null) ? "--anchored ${params.anchored}": '', - (params.rev_complement != null) ? "--rev-complement ${params.rev_complement}": '', ].join(' ').trim() } } @@ -118,18 +116,10 @@ process { ext.args = [ params.markers_ignore ? "--markers_ignore ${markers_ignore}": params.algorithm ? "--algorithm ${params.algorithm}": '', - params.upia_start ? "--upi1-start ${params.upia_start}": '', - params.upia_end ? "--upi1-end ${params.upia_end}": '', - params.upib_start ? "--upi2-start ${params.upib_start}": '', - params.upib_end ? "--upi2-end ${params.upib_end}": '', - params.umia_start ? "--umi1-start ${params.umia_start}": '', - params.umia_end ? "--umi1-end ${params.umia_end}": '', - params.umib_start ? "--umi2-start ${params.umib_start}": '', - params.umib_end ? "--umi2-end ${params.umib_end}": '', params.neighbours ? "--neighbours ${params.neighbours}": '', params.collapse_mismatches ? "--mismatches ${params.collapse_mismatches}": '', params.collapse_min_count ? "--min-count ${params.collapse_min_count}": '', - params.use_counts ? "--use-counts": '', + params.collapse_use_counts ? "--use-counts": '', ].join(' ').trim() } @@ -137,7 +127,7 @@ process { ext.args = [ params.multiplet_recovery ? "--multiplet-recovery" : '', params.leiden_iterations ? "--leiden-iterations ${params.leiden_iterations}" : '', - params.cluster_min_count ? "--min-count ${params.cluster_min_count}" : '', + params.graph_min_count ? "--min-count ${params.graph_min_count}" : '', ].join(' ').trim() } diff --git a/docs/usage.md b/docs/usage.md index 8824b815..51c490f3 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -30,20 +30,24 @@ Below is an example of a simple samplesheet with two samples. ```csv sample,design,panel,fastq_1,fastq_2 -uropod_control,D21,UNO_D21.csv,uropod_control_S1_R1_001.fastq.gz,uropod_control_S1_R2_001.fastq.gz -uropod_stimulated,D21,UNO_D21.csv,uropod_stimulated_S1_R1_001.fastq.gz,uropod_stimulated_S1_R2_001.fastq.gz +uropod_control,D21,human-sc-immunology-spatial-proteomics,uropod_control_S1_R1_001.fastq.gz,uropod_control_S1_R2_001.fastq.gz +uropod_stimulated,D21,human-sc-immunology-spatial-proteomics,uropod_stimulated_S1_R1_001.fastq.gz,uropod_stimulated_S1_R2_001.fastq.gz ``` Columns not defined in the table below are ignored by the pipeline but can be useful to add extra information for downstream processing. -| Column | Description | -| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `sample` | Custom sample name. This entry will be identical for multiple sequencing libraries/runs from the same sample. Spaces in sample names are automatically converted to underscores (`_`). | -| `design` | The name of the pixelator design configuration. | -| `panel` | Path to a csv file with antibody panel information. | -| `fastq_1` | Path to FastQ file for Illumina short reads 1. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | -| `fastq_2` | Path to FastQ file for Illumina short reads 2. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | +| Column | Description | +| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `sample` | Custom sample name. This entry will be identical for multiple sequencing libraries/runs from the same sample. Spaces in sample names are automatically converted to underscores (`_`). | +| `design` | The name of the pixelator design configuration. | +| `panel` | Name of the panel to use. | +| `panel_file` | Path to a CSV file containing a custom panel. | +| `fastq_1` | Path to FastQ file for Illumina short reads 1. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | +| `fastq_2` | Path to FastQ file for Illumina short reads 2. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | + +The `panel` and `panel_file` options are mutually exclusive. If both are specified, the pipeline will throw an error. +One of them has to be specified. The pipeline will auto-detect whether a sample is single- or paired-end based on ifboth `fastq_1` and `fastq_2` or only `fastq_1` is present in the samplesheet. diff --git a/modules/local/pixelator/single-cell/annotate/main.nf b/modules/local/pixelator/single-cell/annotate/main.nf index 525cdfec..d44d2836 100644 --- a/modules/local/pixelator/single-cell/annotate/main.nf +++ b/modules/local/pixelator/single-cell/annotate/main.nf @@ -7,7 +7,8 @@ process PIXELATOR_ANNOTATE { container "ghcr.io/pixelgentechnologies/pixelator:0.12.0" input: - tuple val(meta), path(dataset), path(panel) + tuple val(meta), path(dataset), path(panel_file) + val panel output: tuple val(meta), path("annotate/*.dataset.pxl"), emit: dataset @@ -27,6 +28,7 @@ process PIXELATOR_ANNOTATE { prefix = task.ext.prefix ?: "${meta.id}" def args = task.ext.args ?: '' + def panelOpt = panel ?: panel_file """ pixelator \\ @@ -38,7 +40,7 @@ process PIXELATOR_ANNOTATE { --output . \\ $args \\ $dataset \\ - --panel-file $panel + --panel ${panelOpt} cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/local/pixelator/single-cell/collapse/main.nf b/modules/local/pixelator/single-cell/collapse/main.nf index f2171167..4364fc88 100644 --- a/modules/local/pixelator/single-cell/collapse/main.nf +++ b/modules/local/pixelator/single-cell/collapse/main.nf @@ -10,7 +10,8 @@ process PIXELATOR_COLLAPSE { container "ghcr.io/pixelgentechnologies/pixelator:0.12.0" input: - tuple val(meta), path(reads), path(panel) + tuple val(meta), path(reads), path(panel_file) + val panel output: tuple val(meta), path("collapse/*.collapsed.csv.gz"), emit: collapsed @@ -30,6 +31,7 @@ process PIXELATOR_COLLAPSE { prefix = task.ext.prefix ?: "${meta.id}" def args = task.ext.args ?: '' def readsArg = reads.join(' ') + def panelOpt = panel ?: "${panel_file}" """ pixelator \\ @@ -40,7 +42,7 @@ process PIXELATOR_COLLAPSE { collapse \\ --output . \\ --design ${meta.design} \\ - --panel-file ${panel} \\ + --panel ${panelOpt} \\ $args \\ ${readsArg} diff --git a/modules/local/pixelator/single-cell/demux/main.nf b/modules/local/pixelator/single-cell/demux/main.nf index b0703ea1..9cc40f54 100644 --- a/modules/local/pixelator/single-cell/demux/main.nf +++ b/modules/local/pixelator/single-cell/demux/main.nf @@ -9,7 +9,8 @@ process PIXELATOR_DEMUX { container "ghcr.io/pixelgentechnologies/pixelator:0.12.0" input: - tuple val(meta), path(reads), path(antibody_panel) + tuple val(meta), path(reads), path(panel_file) + val panel output: tuple val(meta), path("demux/*processed*.{fq,fastq}.gz"), emit: processed @@ -29,6 +30,8 @@ process PIXELATOR_DEMUX { prefix = task.ext.prefix ?: "${meta.id}" def args = task.ext.args ?: '' + def panelOpt = panel ?: panel_file + """ pixelator \\ --cores $task.cpus \\ @@ -37,7 +40,7 @@ process PIXELATOR_DEMUX { single-cell \\ demux \\ --output . \\ - --panel-file ${antibody_panel} \\ + --panel ${panelOpt} \\ $args \\ ${reads} diff --git a/modules/local/pixelator/single-cell/report/main.nf b/modules/local/pixelator/single-cell/report/main.nf index ca3dca98..b35304bd 100644 --- a/modules/local/pixelator/single-cell/report/main.nf +++ b/modules/local/pixelator/single-cell/report/main.nf @@ -9,8 +9,8 @@ process PIXELATOR_REPORT { container "ghcr.io/pixelgentechnologies/pixelator:0.12.0" input: - val meta - path panel_file + tuple val(meta), path(panel_file) + val panel path concatenate_data, stageAs: "results/concatenate/*" path preqc_data, stageAs: "results/preqc/*" path adapterqc_data, stageAs: "results/adapterqc/*" @@ -29,13 +29,14 @@ process PIXELATOR_REPORT { script: def args = task.ext.args ?: '' + def panelOpt = panel ?: panel_file """ pixelator \\ single-cell \\ report \\ --output . \\ - --panel-file ${panel_file} \\ + --panel ${panelOpt} \\ $args \\ results diff --git a/nextflow.config b/nextflow.config index 785f6023..d498c4e5 100644 --- a/nextflow.config +++ b/nextflow.config @@ -6,9 +6,10 @@ ---------------------------------------------------------------------------------------- */ -plugins { - id 'nf-validation@0.2.1' -} +// TODO: Use nf-validation +// plugins { +// id 'nf-validation@0.2.1' +// } // Global default params, used in configs params { @@ -34,29 +35,19 @@ params { //demux options demux_mismatches = 0.1 demux_min_length = null - anchored = null - rev_complement = null //collapse options markers_ignore = null algorithm = 'adjacency' - upia_start = null - upia_end = null - upib_start = null - upib_end = null - umia_start = null - umia_end = null - umib_start = null - umib_end = null neighbours = 60 collapse_mismatches = 2 collapse_min_count = 2 - use_counts = false + collapse_use_counts = false // graph options multiplet_recovery = false leiden_iterations = 10 - cluster_min_count = 2 + graph_min_count = 2 // annotate options min_size = null diff --git a/nextflow_schema.json b/nextflow_schema.json index 4966e4fe..e2af1294 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -133,19 +133,6 @@ "description": "The minimum length of the barcode that must overlap when matching", "help_text": "If you set this argument it will overrule the value from the chosen design", "type": "integer" - }, - "anchored": { - "fa_icon": "fas anchor", - "description": "Enforce the barcodes to be anchored (at the end of the read).", - "help_text": "If you set this argument it will overrule the value from the chosen design", - "type": "boolean", - "hidden": true - }, - "rev_complement": { - "fa_icon": "fas right-left", - "description": "Use the reverse complement of the barcodes sequences. Set to null to use the default specified by the design.", - "type": "boolean", - "hidden": true } } }, @@ -165,54 +152,6 @@ "enum": ["adjacency", "unique"], "type": "string" }, - "upia_start": { - "fa_icon": "fas backward-step", - "description": "The start position (0-based) of UPIA. If you set this argument it will overrrule the value from the chosen design", - "type": "integer", - "hidden": true - }, - "upia_end": { - "fa_icon": "fas forward-step", - "description": "The end position (1-based) of UPIA. If you set this argument it will overrrule the value from the chosen design", - "type": "integer", - "hidden": true - }, - "upib_start": { - "fa_icon": "fas backward-step", - "description": "The start position (0-based) of UPIB. If you set this argument it will overrrule the value from the chosen design", - "type": "integer", - "hidden": true - }, - "upib_end": { - "fa_icon": "fas forward-step", - "description": "The end position (1-based) of UPIB. If you set this argument it will overrrule the value from the chosen design", - "type": "integer", - "hidden": true - }, - "umia_start": { - "fa_icon": "fas backward-step", - "description": "The start position (0-based) of UMIA (disabled by default). If you set this argument it will overrrule the value from the chosen design", - "type": "integer", - "hidden": true - }, - "umia_end": { - "fa_icon": "fas forward-step", - "description": "The end position (1-based) of UMIA (disabled by default). If you set this argument it will overrrule the value from the chosen design", - "type": "integer", - "hidden": true - }, - "umib_start": { - "fa_icon": "fas forward-step", - "description": "The start position (0-based) of UMIB (disabled by default). If you set this argument it will overrrule the value from the chosen design", - "type": "integer", - "hidden": true - }, - "umib_end": { - "fa_icon": "fas backward-step", - "description": "The end position (1-based) of UMIB (disabled by default). If you set this argument it will overrrule the value from the chosen design", - "type": "integer", - "hidden": true - }, "neighbours": { "fa_icon": "fas circle-nodes", "description": "The number of neighbours to use when searching for similar sequences (adjacency) This number depends on the sequence depth and the ratio of erroneous molecules expected. A high value can make the algorithm slower.", @@ -237,7 +176,7 @@ "minimum": 1, "type": "integer" }, - "use_counts": { + "collapse_use_counts": { "description": "Use counts when collapsing (the difference in counts between two molecules must be more than double in order to be collapsed)", "type": "boolean" } @@ -259,7 +198,7 @@ "default": 10, "hidden": true }, - "cluster_min_count": { + "graph_min_count": { "fa_icon": "fas less-than-equal", "description": "Discard edges (pixels) with a count (reads) lower than this, use 1 to disable", "type": "integer", diff --git a/subworkflows/local/generate_reports.nf b/subworkflows/local/generate_reports.nf index 6abec16a..dcfba60e 100644 --- a/subworkflows/local/generate_reports.nf +++ b/subworkflows/local/generate_reports.nf @@ -3,7 +3,7 @@ include { PIXELATOR_REPORT } from '../../modules/local/pixelator/sing workflow GENERATE_REPORTS { take: - panels + panel_files // [meta, panel_file] or [meta, []] concatenate_data preqc_data adapterqc_data @@ -16,7 +16,7 @@ workflow GENERATE_REPORTS { main: ch_versions = Channel.empty() - ch_meta_col = panels + ch_meta_col = panel_files .map { meta, data -> [ meta.id, meta] } .groupTuple() .map { id, data -> @@ -31,10 +31,19 @@ workflow GENERATE_REPORTS { } // Make sure panel files are unique, we can have duplicates if we concatenated multiple samples - ch_panels_col = panels + ch_panel_files_col = panel_files .map { meta, data -> [ meta.id, data] } .groupTuple() - .map { id, data -> [ id, data.unique() ] } + .map { id, data -> + if (!data) { + return [id, []] + } + def unique_panels = data.unique() + if (unique_panels.size() > 1) { + exit 1, "ERROR: Concatenated samples must use the same panel." + } + return [ id, unique_panels[0] ] + } ch_concatenate_col = concatenate_data @@ -76,7 +85,7 @@ workflow GENERATE_REPORTS { // [collapse files...], [cluster files], [annotate files...], [analysis files...] // ], ...] ch_report_data = ch_meta_col - .concat ( ch_panels_col ) + .concat ( ch_panel_files_col ) .concat ( ch_concatenate_col ) .concat ( ch_preqc_col ) .concat ( ch_adapterqc_col ) @@ -90,8 +99,7 @@ workflow GENERATE_REPORTS { // Split up everything per stage so we can recreate the expected directory structure for // pixelator single-cell report using stageAs - ch_meta_grouped = ch_report_data.map { id, data -> data[0] } - ch_panels_grouped = ch_report_data.map { id, data -> data[1] } + ch_panel_files_grouped = ch_report_data.map { id, data -> [ data[0], data[1] ] } ch_concatenate_grouped = ch_report_data.map { id, data -> data[2] ? data[2].flatten() : [] } ch_preqc_grouped = ch_report_data.map { id, data -> data[3] ? data[3].flatten() : [] } ch_adapterqc_grouped = ch_report_data.map { id, data -> data[4] ? data[4].flatten() : [] } @@ -101,9 +109,13 @@ workflow GENERATE_REPORTS { ch_annotate_grouped = ch_report_data.map { id, data -> data[8] ? data[8].flatten() : [] } ch_analysis_grouped = ch_report_data.map { id, data -> data[9] ? data[9].flatten() : [] } + ch_panel_keys = ch_panel_files_grouped + .map { meta, panel_file -> panel_file ? [] : meta.panel } + + PIXELATOR_REPORT ( - ch_meta_grouped, - ch_panels_grouped, + ch_panel_files_grouped, + ch_panel_keys, ch_concatenate_grouped, ch_preqc_grouped, ch_adapterqc_grouped, diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf index 1bce4c2a..5ff0023e 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -31,6 +31,7 @@ def get_meta(LinkedHashMap row) { meta.design = row.design meta.group = row.group meta.assay = row.assay + meta.panel = row.panel ?: null return meta } @@ -60,9 +61,14 @@ def create_fastq_channel(LinkedHashMap row) { def create_panels_channel(LinkedHashMap row) { def meta = get_meta(row) - if (file(row.panel).exists()) { - return [ meta, file(row.panel) ] - } + // Require a panel file if no value for panel is set + if (meta.panel == null) { + if (file(row.panel_file).exists()) { + return [ meta, file(row.panel_file) ] + } - exit 1, "ERROR: Please check panel field: ${row.panel}: Could not find existing csv file." + exit 1, "ERROR: Please check panel field: ${row.panel_file}: Could not find existing csv file." + } else { + return [ meta, [] ] + } } diff --git a/workflows/pixelator.nf b/workflows/pixelator.nf index d123f26e..0fec50ba 100644 --- a/workflows/pixelator.nf +++ b/workflows/pixelator.nf @@ -86,7 +86,7 @@ workflow PIXELATOR { INPUT_CHECK ( ch_input ) ch_reads = INPUT_CHECK.out.reads - ch_panels = INPUT_CHECK.out.panels + ch_panel_files = INPUT_CHECK.out.panels ch_fastq_split = ch_reads .map { @@ -135,13 +135,19 @@ workflow PIXELATOR { ch_qc = PIXELATOR_QC.out.processed ch_versions = ch_versions.mix(PIXELATOR_QC.out.versions.first()) - ch_qc_and_panel = ch_qc.join(ch_panels) - PIXELATOR_DEMUX ( ch_qc_and_panel ) + ch_qc_and_panel_file = ch_qc.join(ch_panel_files) + ch_demux_panel_keys = ch_qc_and_panel_file + .map { meta, fq, panel_file -> panel_file ? [] : meta.panel } + + PIXELATOR_DEMUX ( ch_qc_and_panel_file, ch_demux_panel_keys ) ch_demuxed = PIXELATOR_DEMUX.out.processed ch_versions = ch_versions.mix(PIXELATOR_DEMUX.out.versions.first()) - ch_demuxed_and_panel = ch_demuxed.join(ch_panels) - PIXELATOR_COLLAPSE ( ch_demuxed_and_panel ) + ch_demuxed_and_panel_file = ch_demuxed.join(ch_panel_files) + ch_collapse_panel_keys = ch_demuxed_and_panel_file.map { + meta, fq, panel_file -> panel_file ? [] : meta.panel } + + PIXELATOR_COLLAPSE ( ch_demuxed_and_panel_file, ch_collapse_panel_keys ) ch_collapsed = PIXELATOR_COLLAPSE.out.collapsed ch_versions = ch_versions.mix(PIXELATOR_COLLAPSE.out.versions.first()) @@ -149,9 +155,11 @@ workflow PIXELATOR { ch_clustered = PIXELATOR_GRAPH.out.edgelist ch_versions = ch_versions.mix(PIXELATOR_GRAPH.out.versions.first()) - ch_clustered_and_panel = ch_clustered.join(ch_panels) + ch_clustered_and_panel = ch_clustered.join(ch_panel_files) + ch_annotate_panel_keys = ch_demuxed_and_panel_file + .map { meta, fq, panel_file -> panel_file ? [] : meta.panel } - PIXELATOR_ANNOTATE ( ch_clustered_and_panel ) + PIXELATOR_ANNOTATE ( ch_clustered_and_panel, ch_annotate_panel_keys ) ch_annotated = PIXELATOR_ANNOTATE.out.dataset ch_versions = ch_versions.mix( PIXELATOR_ANNOTATE.out.versions.first() ) @@ -161,12 +169,10 @@ workflow PIXELATOR { // Do some transformations to split the inputs into their stages - // and have all these "stage output" channels output in the same order - // Note that we need to split inout per stage to stage those files in the right subdirs + // and have all these "pixelator subcommand output" channels output in the same order + // Note that we need to split inout per subcommand to stage those files in the right subdirs // as expected by pixelator single-cell report - // Make sure meta objects are unique, we can have duplicates if we concatenated multiple samples - ch_concatenate_data = PIXELATOR_CONCATENATE.out.report_json.mix(PIXELATOR_CONCATENATE.out.metadata) ch_preqc_data = PIXELATOR_QC.out.preqc_report_json.mix(PIXELATOR_QC.out.preqc_metadata) ch_adapterqc_data = PIXELATOR_QC.out.adapterqc_report_json.mix(PIXELATOR_QC.out.adapterqc_metadata) @@ -177,7 +183,7 @@ workflow PIXELATOR { ch_analysis_data = PIXELATOR_ANALYSIS.out.all_results GENERATE_REPORTS( - ch_panels, + ch_panel_files, ch_concatenate_data, ch_preqc_data, ch_adapterqc_data, @@ -194,7 +200,7 @@ workflow PIXELATOR { ch_versions.unique().collectFile(name: 'collated_versions.yml') ) - // TODO: Add MultiQC after plugins are available + // TODO: Add MultiQC after plugins are implemented } /* From f006e550a4f9d4bc3033d6bc658947b892b66411 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 28 Jun 2023 14:53:19 +0200 Subject: [PATCH 018/112] fix: use pr-444 pixelator container --- nextflow.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextflow.config b/nextflow.config index d498c4e5..5d5225de 100644 --- a/nextflow.config +++ b/nextflow.config @@ -73,7 +73,7 @@ params { skip_analysis = false // Pipeline specific global config options - pixelator_container = "ghcr.io/pixelgentechnologies/pixelator:pr-441" + pixelator_container = "ghcr.io/pixelgentechnologies/pixelator:pr-444" // Boilerplate options outdir = null From 04478876bbb15fea2518a9ff1d832dc90b78d1d6 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 28 Jun 2023 15:25:17 +0200 Subject: [PATCH 019/112] docs: fixed in output.md --- docs/output.md | 77 ++++++++++++++++++++++++++++---------------------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/docs/output.md b/docs/output.md index c4b3fda5..f1fef1bd 100644 --- a/docs/output.md +++ b/docs/output.md @@ -11,18 +11,18 @@ The directories listed below will be created in the results directory after the The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes data using multiple subcommands of the [`pixelator`](https://github.com/PixelgenTechnologies/pixelator) tool. -- [`pixelator concatenate`](#pixelator-concatenate)(Optional) - Concatenate paired end data -- [`pixelator preqc`](#pixelator-preqc) - Read QC and filtering -- [`pixelator adapterqc`](#pixelator-adapterqc) - Check for correctness of PBS1/2 sequences -- [`pixelator demux`](#pixelator-demux) - Assign a marker (barcode) to each read -- [`pixelator collapse`](#pixelator-collapse) - Error correction, duplicate removal, compute read counts -- [`pixelator cluster`](#pixelator-cluster) - Compute undirected graphs and basic size filtering -- [`pixelator analysis`](#pixelator-analysis) - Downstream analysis for each cell -- [`pixelator annotate`](#pixelator-annotate) - Filter, annotate and call cells on samples -- [`pixelator aggregate`](#pixelator-aggregate) - Aggregate results -- [`pixelator report`](#pixelator-report) - Report generation - -### pixelator concatenate +- [`pixelator single-cell concatenate`](#pixelator-concatenate)(Optional) - Concatenate paired end data +- [`pixelator single-cell preqc`](#pixelator-preqc) - Read QC and filtering +- [`pixelator single-cell adapterqc`](#pixelator-adapterqc) - Check for correctness of PBS1/2 sequences +- [`pixelator single-cell demux`](#pixelator-demux) - Assign a marker (barcode) to each read +- [`pixelator single-cell collapse`](#pixelator-collapse) - Error correction, duplicate removal, compute read counts +- [`pixelator single-cell cluster`](#pixelator-cluster) - Compute undirected graphs and basic size filtering +- [`pixelator single-cell analysis`](#pixelator-analysis) - Downstream analysis for each cell +- [`pixelator single-cell annotate`](#pixelator-annotate) - Filter, annotate and call cells on samples +- [`pixelator single-cell aggregate`](#pixelator-aggregate) - Aggregate results +- [`pixelator single-cell report`](#pixelator-report) - Report generation + +### pixelator single-cell concatenate // TODO: High level description of concatenate step and output files @@ -30,17 +30,20 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d Output files - `pixelator` + - `concatenate` + - `.merged.fastq.gz`: Combine R1 and R2 reads into full amplicon reads and calculate Q30 scores for the amplicon regions. - `.report.json`: Q30 metrics of the amplicon. - `.meta.json`: Command invocation metadata. -- `logs` - - \*pixelator-concatenate.log`: pixelator log output. + + - `logs` + - `.pixelator-concatenate.log`: pixelator log output. -### pixelator qc +### pixelator single-cell qc // TODO: High level description of QC step and output files @@ -55,16 +58,18 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d - `.report.json`: Fastp json report. - `.meta.json`: Command invocation metadata. - `adapterqc` + - `.processed.fastq.gz`: Processed reads. - `.failed.fastq.gz`: Discarded reads. - `.report.json`: Cutadapt json report. - `.meta.json`: Command invocation metadata. -- `logs` - `*pixelator-preqc.log`: pixelator log output. + - `logs` + - `.pixelator-preqc.log`: pixelator log output. -### pixelator demux +### pixelator single-cell demux // TODO: High level description of demux step and output files @@ -74,17 +79,18 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d - `pixelator` - `demux` + - `.processed-.fastq.gz`: Reads demultiplexed per antibody. - `.failed.fastq.gz`: Discarded reads that do not match an antibody barcode. - `.report.json`: Cutadapt json report. - `.meta.json`: Command invocation metadata. -- `logs` - - `*pixelator-demultiplex.log`: pixelator log output. + - `logs` + - `.pixelator-demultiplex.log`: pixelator log output. -### pixelator collapse +### pixelator single-cell collapse // TODO: High level description of collapse step and output files @@ -94,16 +100,17 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d - `pixelator` - `adapterqc` + - `.collapsed.csv.gz`: Edgelist of the graph. - `.report.json`: Statistics for the collapse step. - `.meta.json`: Command invocation metadata. -- `logs` - - `*pixelator-collapse.log`: pixelator log output. + - `logs` + - `.pixelator-collapse.log`: pixelator log output. -### pixelator graph +### pixelator single-cell graph // TODO: High level description of graph step and output files @@ -113,6 +120,7 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d - `pixelator` - `cluster` + - `.components_recovered.csv` - `.edgelist.csv.gz` - `.raw_edgelist.csv.gz` @@ -120,12 +128,12 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d - `.report.json` - `*.meta.json`: Command invocation metadata. -- `logs` - - `*pixelator-cluster.log`: pixelator log output. + - `logs` + - `.pixelator-cluster.log`: pixelator log output. -### pixelator annotate +### pixelator single-cell annotate // TODO: High level description of annotate step and output files @@ -135,6 +143,7 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d - `pixelator` - `annotate` + - `.dataset.pxl` - `.meta.json`: Command invocation metadata. - `.rank_vs_size.png` @@ -142,11 +151,10 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d - `.report.json` - `.umap.png` -- `logs` - - `*pixelator-annotate.log`: pixelator log output. + - `logs` - `.pixelator-annotate.log`: pixelator log output. -### pixelator analysis +### pixelator single-cell analysis // TODO: High level description of analysis step and output files @@ -156,16 +164,17 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d - `pixelator` - `analysis` + - `.dataset.pxl` - `.meta.json`: Command invocation metadata. - `.report.json` -- `logs` - - `*pixelator-analysis.log`: pixelator log output. + - `logs` + - `.pixelator-analysis.log`: pixelator log output. -### pixelator report +### pixelator single-cell report // TODO: High level description of report step and output files @@ -175,8 +184,8 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d - `pixelator` - `report` - `_report.html` -- `logs` - - `*pixelator-report.log`: Pixelator report log output. + - `logs` + - `.pixelator-report.log`: Pixelator report log output. From 18108357e870a1d82efef64f09571c4e73f93ab4 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 29 Jun 2023 10:03:02 +0200 Subject: [PATCH 020/112] docs: update output.md --- docs/output.md | 59 +++++++++++++++++++++++++++++++------------------- 1 file changed, 37 insertions(+), 22 deletions(-) diff --git a/docs/output.md b/docs/output.md index f1fef1bd..eafb5171 100644 --- a/docs/output.md +++ b/docs/output.md @@ -11,20 +11,22 @@ The directories listed below will be created in the results directory after the The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes data using multiple subcommands of the [`pixelator`](https://github.com/PixelgenTechnologies/pixelator) tool. -- [`pixelator single-cell concatenate`](#pixelator-concatenate)(Optional) - Concatenate paired end data -- [`pixelator single-cell preqc`](#pixelator-preqc) - Read QC and filtering -- [`pixelator single-cell adapterqc`](#pixelator-adapterqc) - Check for correctness of PBS1/2 sequences -- [`pixelator single-cell demux`](#pixelator-demux) - Assign a marker (barcode) to each read -- [`pixelator single-cell collapse`](#pixelator-collapse) - Error correction, duplicate removal, compute read counts +- [Preprocessing](#Preprocessing) +- [Quality control](#quality-control) +- [Demultiplexing](#demultiplexing) +- [Duplicate removal and error correction`](#duplicate-removal-and-error-correction) - [`pixelator single-cell cluster`](#pixelator-cluster) - Compute undirected graphs and basic size filtering +- [Filtering, annotation, cell-calling](#filtering-annotation-cell-calling) - [`pixelator single-cell analysis`](#pixelator-analysis) - Downstream analysis for each cell - [`pixelator single-cell annotate`](#pixelator-annotate) - Filter, annotate and call cells on samples - [`pixelator single-cell aggregate`](#pixelator-aggregate) - Aggregate results - [`pixelator single-cell report`](#pixelator-report) - Report generation -### pixelator single-cell concatenate +### Preprocessing -// TODO: High level description of concatenate step and output files +The preprocessing step uses `pixelator single-cell concatenate` to create a full amplicon sequence from both single-end and paired-end data. +It returns a single fastq per sample containing fixed length amplicons. +This step will also calculate Q30 quality scores for different regions of the library.
Output files @@ -43,9 +45,14 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d
-### pixelator single-cell qc +### Quality control -// TODO: High level description of QC step and output files +Quality control is performed using `pixelator single-cell preqc` and pixelator single-cell adapterqc`. +`preqc`used`fastp`internally.`adapterqc`will use`cutadapt` internally. + +The preqc stage performs QC and quality filtering of the raw sequencing data. It also generates a QC report in HTML and JSON formats. It saves processed reads as well as reads that were discarded (too short. too many Ns , too low quality, ...). + +The `adapterqc` stage performs a sanity check on the presence and correctness of the PBS1/2 sequences. It also generates a QC report in JSON format. It saves processed reads as well as discarded reads (no match to PBS1/2).
Output files @@ -69,9 +76,9 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d
-### pixelator single-cell demux +### Demultiplexing -// TODO: High level description of demux step and output files +The `demux` command assigns a marker (barcode) to each read. It also generates QC report in JSON format. It saves processed reads (one per antibody) as well as discarded reads (no match to given barcodes/antibodies). In this step an antibody panel file (CSV) is required (--panels-file). This file contains the antibodies present in the data as well as their sequences and it needs the following columns:
Output files @@ -90,16 +97,18 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d
-### pixelator single-cell collapse +### Duplicate removal and error correction -// TODO: High level description of collapse step and output files +This step used the `pixelator single-cell collapse` command. +The collapse command removes duplicates and performs error correction. This is achieved using the UPI and UMI sequences to check for uniqueness, collapse and compute a read count. The command generates a QC report in JSON format. Errors are allowed when collapsing reads using different collapsing algorithms (--algorithm). +The output format of this command is an edge list in CSV format.
Output files - `pixelator` - - `adapterqc` + - `collapse` - `.collapsed.csv.gz`: Edgelist of the graph. - `.report.json`: Statistics for the collapse step. @@ -112,20 +121,26 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d ### pixelator single-cell graph -// TODO: High level description of graph step and output files +This step uses the `pixelator single-cell graph` command. +The input is the edge list dataframe (CSV) generated in the collapse step and after filtering it by count (`--graph_min_count`), the connected components of the graph (graphs) are computed and added to the edge list in a column called "component". + +The graph command has the option to recover components (technical multiplets) into smaller components using community detection to detect and remove problematic edges. (See `--multiplet_recovery`). The information to keep track of the original and new (recovered) components is stored in a file (components_recovered.csv).
Output files - `pixelator` - - `cluster` + - `graph` - - `.components_recovered.csv` - - `.edgelist.csv.gz` - - `.raw_edgelist.csv.gz` + - `.edgelist.csv.gz`: + Edge list dataframe (CSV) after recovering technical multiplets. + - `.raw_edgelist.csv.gz`: + Raw edge list dataframe in csv format before recovering technical multiplets. + - `.components_recovered.csv`: + List of new components recovered (when using `--multiple-recovery`) - `.meta.json`: Command invocation metadata. - - `.report.json` + - `.report.json`: Metrics with useful information about the clustering. - `*.meta.json`: Command invocation metadata. - `logs` @@ -133,9 +148,9 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d
-### pixelator single-cell annotate +### Filtering, annotation, cell-calling -// TODO: High level description of annotate step and output files +The annotate command takes as input the edge list (CSV) file generated in the graph command. The edge list is converted to an AnnData object, the command then performs filtering, annotation and cell calling of the components.
Output files From 81080a73f41fd5dc8167365d181563845727ff60 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 29 Jun 2023 10:12:43 +0200 Subject: [PATCH 021/112] docs: fix pipeline overview bullet list order --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5d0bc831..613b30be 100644 --- a/README.md +++ b/README.md @@ -33,8 +33,8 @@ It takes a samplesheet as input and will process your data using `pixelator` to 3. Assign a marker (barcode) to each read ([`pixelator demux`](https://github.com/PixelgenTechnologies/pixelator)) 4. Error correction, duplicate removal, compute read counts ([`pixelator collapse`](https://github.com/PixelgenTechnologies/pixelator)) 5. Compute the components/clusters of the graph from the edge list matrix.([`pixelator graph`](https://github.com/PixelgenTechnologies/pixelator)) -6. Analyze components/clusters of the graph.([`pixelator analysis`](https://github.com/PixelgenTechnologies/pixelator)) -7. Filter, annotate and call cells on samples ([`pixelator annotate`](https://github.com/PixelgenTechnologies/pixelator)) +6. Filter, annotate and call cells on samples ([`pixelator annotate`](https://github.com/PixelgenTechnologies/pixelator)) +7. Analyze components/clusters of the graph.([`pixelator analysis`](https://github.com/PixelgenTechnologies/pixelator)) 8. Report generation ([`pixelator report`](https://github.com/PixelgenTechnologies/pixelator)) ## Usage From 4d9596e3cf4f941c68c7521a3361f605612f12e6 Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Thu, 29 Jun 2023 10:31:15 +0200 Subject: [PATCH 022/112] Clarifications in README --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 613b30be..1502be6e 100644 --- a/README.md +++ b/README.md @@ -29,12 +29,12 @@ It takes a samplesheet as input and will process your data using `pixelator` to 1. Build amplicon from input reads ([`pixelator concatenate`](https://github.com/PixelgenTechnologies/pixelator)) -2. Read QC and filtering, correctness of PBS sequences ([`pixelator preqc | pixelator adapterqc`](https://github.com/PixelgenTechnologies/pixelator)) +2. Read QC and filtering, correctness of the pixel binding sequence sequences ([`pixelator preqc | pixelator adapterqc`](https://github.com/PixelgenTechnologies/pixelator)) 3. Assign a marker (barcode) to each read ([`pixelator demux`](https://github.com/PixelgenTechnologies/pixelator)) 4. Error correction, duplicate removal, compute read counts ([`pixelator collapse`](https://github.com/PixelgenTechnologies/pixelator)) -5. Compute the components/clusters of the graph from the edge list matrix.([`pixelator graph`](https://github.com/PixelgenTechnologies/pixelator)) -6. Filter, annotate and call cells on samples ([`pixelator annotate`](https://github.com/PixelgenTechnologies/pixelator)) -7. Analyze components/clusters of the graph.([`pixelator analysis`](https://github.com/PixelgenTechnologies/pixelator)) +5. Compute the components of the graph from the edge list in order to create putative cells ([`pixelator graph`](https://github.com/PixelgenTechnologies/pixelator)) +6. Call and annotate cells ([`pixelator annotate`](https://github.com/PixelgenTechnologies/pixelator)) +7. Analyze the cells for polarization and colocalization ([`pixelator analysis`](https://github.com/PixelgenTechnologies/pixelator)) 8. Report generation ([`pixelator report`](https://github.com/PixelgenTechnologies/pixelator)) ## Usage @@ -66,7 +66,7 @@ First, prepare a samplesheet with your input data that looks as follows: ```csv sample,design,panel,fastq_1,fastq_2 -uropod_control,D21,UNO_D21_conjV21.csv,uropod_control_300k_S1_R1_001.fastq.gz,uropod_control_300k_S1_R2_001.fastq.gz +uropod_control,D21,human-sc-immunology-spatial-proteomics,uropod_control_300k_S1_R1_001.fastq.gz,uropod_control_300k_S1_R2_001.fastq.gz ``` Each row represents a sample and gives the design, a panel file and the input fastq files. From 5267363a82f863b4261b8b9aa9a08d54e24dc39d Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 29 Jun 2023 10:40:14 +0200 Subject: [PATCH 023/112] docs: update output.md --- docs/output.md | 57 ++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 41 insertions(+), 16 deletions(-) diff --git a/docs/output.md b/docs/output.md index eafb5171..e64dc1bc 100644 --- a/docs/output.md +++ b/docs/output.md @@ -9,18 +9,19 @@ The directories listed below will be created in the results directory after the ## Pipeline overview -The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes data using multiple subcommands of the [`pixelator`](https://github.com/PixelgenTechnologies/pixelator) tool. +The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes data using multiple subcommands +of [`pixelator`](https://github.com/PixelgenTechnologies/pixelator). + +The pipeline consists of the following steps: - [Preprocessing](#Preprocessing) - [Quality control](#quality-control) - [Demultiplexing](#demultiplexing) -- [Duplicate removal and error correction`](#duplicate-removal-and-error-correction) -- [`pixelator single-cell cluster`](#pixelator-cluster) - Compute undirected graphs and basic size filtering +- [Duplicate removal and error correction](#duplicate-removal-and-error-correction) +- [Compute connected components](#compute-connected-components) - [Filtering, annotation, cell-calling](#filtering-annotation-cell-calling) -- [`pixelator single-cell analysis`](#pixelator-analysis) - Downstream analysis for each cell -- [`pixelator single-cell annotate`](#pixelator-annotate) - Filter, annotate and call cells on samples -- [`pixelator single-cell aggregate`](#pixelator-aggregate) - Aggregate results -- [`pixelator single-cell report`](#pixelator-report) - Report generation +- [Downstream analysis](#downstream-analysis) +- [Generate reports](#generate-reports) ### Preprocessing @@ -119,12 +120,17 @@ The output format of this command is an edge list in CSV format.
-### pixelator single-cell graph +### Compute connected components This step uses the `pixelator single-cell graph` command. -The input is the edge list dataframe (CSV) generated in the collapse step and after filtering it by count (`--graph_min_count`), the connected components of the graph (graphs) are computed and added to the edge list in a column called "component". +The input is the edge list dataframe (CSV) generated in the collapse step and after filtering it +by count (`--graph_min_count`), the connected components of the graph (graphs) are computed and +added to the edge list in a column called "component". -The graph command has the option to recover components (technical multiplets) into smaller components using community detection to detect and remove problematic edges. (See `--multiplet_recovery`). The information to keep track of the original and new (recovered) components is stored in a file (components_recovered.csv). +The graph command has the option to recover components (technical multiplets) into smaller +components using community detection to detect and remove problematic edges. +(See `--multiplet_recovery`). The information to keep track of the original and +new (recovered) components is stored in a file (components_recovered.csv).
Output files @@ -150,7 +156,14 @@ The graph command has the option to recover components (technical multiplets) in ### Filtering, annotation, cell-calling -The annotate command takes as input the edge list (CSV) file generated in the graph command. The edge list is converted to an AnnData object, the command then performs filtering, annotation and cell calling of the components. +This step uses the `pixelator single-cell annotate` command. + +The annotate command takes as input the edge list (CSV) file generated in the graph command. +The edge list is converted to an AnnData object, the command then performs filtering, annotation and +cell calling of the components. + +The DataFrame contained in a pxl file will have the same dimension as in the antibody panel so any +missing antibody will be filled with 0's.
Output files @@ -169,9 +182,17 @@ The annotate command takes as input the edge list (CSV) file generated in the gr - `logs` - `.pixelator-annotate.log`: pixelator log output.
-### pixelator single-cell analysis +### Downstream analysis + +This step uses the `pixelator single-cell analysis` command. +Downstream analysis is performed on the `PixelDataset` in PXL format generated by the previous stage. -// TODO: High level description of analysis step and output files +Currently, the following analysis can be performed (if enabled): + +- polarization scores (all the statistics in a dataframe) (enable with --compute_polarization) +- co-localization scores (all pair-wise scores in a dataframe) (enable with --compute_colocalization) + +This step can be skipped using the `--skip_analysis` option.
Output files @@ -189,9 +210,13 @@ The annotate command takes as input the edge list (CSV) file generated in the gr
-### pixelator single-cell report +### Generate reports + +This step uses `pixelator single-cell report` command. +This step will collect metrics and outputs generated by previous stages +and generate a report in HTML format for each sample. -// TODO: High level description of report step and output files +This step can be skipped using the `--skip_report` option.
Output files @@ -213,7 +238,7 @@ The annotate command takes as input the edge list (CSV) file generated in the gr - Reports generated by Nextflow: `execution_report.html`, `execution_timeline.html`, `execution_trace.txt` and `pipeline_dag.dot`/`pipeline_dag.svg`. - Reports generated by the pipeline: `pipeline_report.html`, `pipeline_report.txt` and `software_versions.yml`. The `pipeline_report*` files will only be present if the `--email` / `--email_on_fail` parameter's are used when running the pipeline. - Reformatted samplesheet files used as input to the pipeline: `samplesheet.valid.csv`. - - Metadata file with software versions, environment information and pipeline configuration for debugging: 'metadata.json' + - Metadata file with software versions, environment information and pipeline configuration for debugging: `metadata.json`
From 07454a46023e3fa6891c6b4cdc793b2dac75ee6c Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 29 Jun 2023 10:42:04 +0200 Subject: [PATCH 024/112] docs: remove todo --- docs/output.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/output.md b/docs/output.md index e64dc1bc..91e48a11 100644 --- a/docs/output.md +++ b/docs/output.md @@ -5,8 +5,6 @@ This document describes the output produced by the pipeline. The directories listed below will be created in the results directory after the pipeline has finished. All paths are relative to the top-level results directory. - - ## Pipeline overview The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes data using multiple subcommands From 7077233b09c6019d8921e2d0899adb6cd3fbd48e Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 29 Jun 2023 10:49:58 +0200 Subject: [PATCH 025/112] docs: more output.md tweaks --- docs/output.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/output.md b/docs/output.md index 91e48a11..fea87e7f 100644 --- a/docs/output.md +++ b/docs/output.md @@ -174,7 +174,7 @@ missing antibody will be filled with 0's. - `.meta.json`: Command invocation metadata. - `.rank_vs_size.png` - `.raw_components_metrics.csv` - - `.report.json` + - `.report.json`: Statistics for the analysis step. - `.umap.png` - `logs` - `.pixelator-annotate.log`: pixelator log output. @@ -199,9 +199,9 @@ This step can be skipped using the `--skip_analysis` option. - `analysis` - - `.dataset.pxl` + - `.dataset.pxl`: PixelDataset updated with analysis results. - `.meta.json`: Command invocation metadata. - - `.report.json` + - `.report.json`: Statistics for the analysis step. - `logs` - `.pixelator-analysis.log`: pixelator log output. @@ -210,7 +210,7 @@ This step can be skipped using the `--skip_analysis` option. ### Generate reports -This step uses `pixelator single-cell report` command. +This step uses the `pixelator single-cell report` command. This step will collect metrics and outputs generated by previous stages and generate a report in HTML format for each sample. @@ -221,9 +221,9 @@ This step can be skipped using the `--skip_report` option. - `pixelator` - `report` - - `_report.html` + - `_report.html`: Pixelator summary report. - `logs` - - `.pixelator-report.log`: Pixelator report log output. + - `.pixelator-report.log`: Pixelator log output.
From 881d8b78aeb77406188cc3356cc698fcc4a29562 Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Thu, 29 Jun 2023 11:09:07 +0200 Subject: [PATCH 026/112] Update output docs --- docs/output.md | 50 ++++++++++++++++++++++---------------------------- 1 file changed, 22 insertions(+), 28 deletions(-) diff --git a/docs/output.md b/docs/output.md index 91e48a11..43a79f9f 100644 --- a/docs/output.md +++ b/docs/output.md @@ -46,12 +46,11 @@ This step will also calculate Q30 quality scores for different regions of the li ### Quality control -Quality control is performed using `pixelator single-cell preqc` and pixelator single-cell adapterqc`. -`preqc`used`fastp`internally.`adapterqc`will use`cutadapt` internally. +Quality control is performed using `pixelator single-cell preqc` and `pixelator single-cell adapterqc`. -The preqc stage performs QC and quality filtering of the raw sequencing data. It also generates a QC report in HTML and JSON formats. It saves processed reads as well as reads that were discarded (too short. too many Ns , too low quality, ...). +The preqc stage performs QC and quality filtering of the raw sequencing data. It also generates a QC report in HTML and JSON formats. It saves processed reads as well as reads that were discarded (i.e. were too short, had too many Ns, or too low quality, etc). Internally `preqc` uses [Fastp](https://github.com/OpenGene/fastp), and `adapterqc` used [Cutadapt](https://cutadapt.readthedocs.io/en/stable/) -The `adapterqc` stage performs a sanity check on the presence and correctness of the PBS1/2 sequences. It also generates a QC report in JSON format. It saves processed reads as well as discarded reads (no match to PBS1/2). +The `adapterqc` stage checks for the presence and correctness of the pixel binding sequences. It also generates a QC report in JSON format. It saves processed reads as well as discarded reads (i.e. reads that did not have a match for both pixel binding sequences).
Output files @@ -64,7 +63,6 @@ The `adapterqc` stage performs a sanity check on the presence and correctness of - `.report.json`: Fastp json report. - `.meta.json`: Command invocation metadata. - `adapterqc` - - `.processed.fastq.gz`: Processed reads. - `.failed.fastq.gz`: Discarded reads. - `.report.json`: Cutadapt json report. @@ -77,7 +75,7 @@ The `adapterqc` stage performs a sanity check on the presence and correctness of ### Demultiplexing -The `demux` command assigns a marker (barcode) to each read. It also generates QC report in JSON format. It saves processed reads (one per antibody) as well as discarded reads (no match to given barcodes/antibodies). In this step an antibody panel file (CSV) is required (--panels-file). This file contains the antibodies present in the data as well as their sequences and it needs the following columns: +The `pixelator single-cell demux` command assigns a marker (barcode) to each read. It also generates QC report in JSON format. It saves processed reads (one per antibody) as well as discarded reads with no match to the given barcodes/antibodies.
Output files @@ -85,7 +83,6 @@ The `demux` command assigns a marker (barcode) to each read. It also generates Q - `pixelator` - `demux` - - `.processed-.fastq.gz`: Reads demultiplexed per antibody. - `.failed.fastq.gz`: Discarded reads that do not match an antibody barcode. - `.report.json`: Cutadapt json report. @@ -98,8 +95,10 @@ The `demux` command assigns a marker (barcode) to each read. It also generates Q ### Duplicate removal and error correction -This step used the `pixelator single-cell collapse` command. -The collapse command removes duplicates and performs error correction. This is achieved using the UPI and UMI sequences to check for uniqueness, collapse and compute a read count. The command generates a QC report in JSON format. Errors are allowed when collapsing reads using different collapsing algorithms (--algorithm). +This step uses the `pixelator single-cell collapse` command. + +The `collapse` command removes duplicate reads and performs error correction. This is achieved using the umique pixel identifier and unique molecular identifier sequences to check for uniqueness, collapse and compute a read count. The command generates a QC report in JSON format. Errors are allowed when collapsing reads if `collapse_options.algorithm` is set to `adjacency` (this is the default option). + The output format of this command is an edge list in CSV format.
@@ -108,7 +107,6 @@ The output format of this command is an edge list in CSV format. - `pixelator` - `collapse` - - `.collapsed.csv.gz`: Edgelist of the graph. - `.report.json`: Statistics for the collapse step. - `.meta.json`: Command invocation metadata. @@ -126,9 +124,9 @@ by count (`--graph_min_count`), the connected components of the graph (graphs) a added to the edge list in a column called "component". The graph command has the option to recover components (technical multiplets) into smaller -components using community detection to detect and remove problematic edges. -(See `--multiplet_recovery`). The information to keep track of the original and -new (recovered) components is stored in a file (components_recovered.csv). +components using community detection to find and remove problematic edges. +(See `graph_options.multiplet_recovery`). The information to keep track of the original and +new (recovered) components are stored in a file (components_recovered.csv).
Output files @@ -136,7 +134,6 @@ new (recovered) components is stored in a file (components_recovered.csv). - `pixelator` - `graph` - - `.edgelist.csv.gz`: Edge list dataframe (CSV) after recovering technical multiplets. - `.raw_edgelist.csv.gz`: @@ -152,16 +149,13 @@ new (recovered) components is stored in a file (components_recovered.csv).
-### Filtering, annotation, cell-calling +### Cell-calling, filtering, and annotation This step uses the `pixelator single-cell annotate` command. -The annotate command takes as input the edge list (CSV) file generated in the graph command. -The edge list is converted to an AnnData object, the command then performs filtering, annotation and -cell calling of the components. - -The DataFrame contained in a pxl file will have the same dimension as in the antibody panel so any -missing antibody will be filled with 0's. +The annotate command takes as input the edge list (CSV) file generated in the graph command. It parses, and filters the +edgelist to find putative cells, and it will generate a pxl file containing the edgelist, and an (AnnData object)[https://anndata.readthedocs.io/en/latest/] +as well as some useful medatadata.
Output files @@ -169,7 +163,6 @@ missing antibody will be filled with 0's. - `pixelator` - `annotate` - - `.dataset.pxl` - `.meta.json`: Command invocation metadata. - `.rank_vs_size.png` @@ -177,18 +170,20 @@ missing antibody will be filled with 0's. - `.report.json` - `.umap.png` - - `logs` - `.pixelator-annotate.log`: pixelator log output. + - `logs` + - `.pixelator-annotate.log`: pixelator log output.
### Downstream analysis -This step uses the `pixelator single-cell analysis` command. -Downstream analysis is performed on the `PixelDataset` in PXL format generated by the previous stage. +This step uses the `pixelator single-cell analysis` command. Analysis is performed on the pxl file generated by the previous stage. Currently, the following analysis can be performed (if enabled): -- polarization scores (all the statistics in a dataframe) (enable with --compute_polarization) -- co-localization scores (all pair-wise scores in a dataframe) (enable with --compute_colocalization) +- polarization scores (enable with `analysis_options.compute_polarization`) +- co-localization scores (enable with `analysis_options.compute_colocalization`) + +The results of the analysis is added to the pxl file file. This step can be skipped using the `--skip_analysis` option. @@ -198,7 +193,6 @@ This step can be skipped using the `--skip_analysis` option. - `pixelator` - `analysis` - - `.dataset.pxl` - `.meta.json`: Command invocation metadata. - `.report.json` From 71d233ca2cad0811460434031e2a385fa580e316 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 29 Jun 2023 11:11:34 +0200 Subject: [PATCH 027/112] docs: fix unmatched backticks --- docs/output.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/output.md b/docs/output.md index fea87e7f..baa9acc6 100644 --- a/docs/output.md +++ b/docs/output.md @@ -46,8 +46,8 @@ This step will also calculate Q30 quality scores for different regions of the li ### Quality control -Quality control is performed using `pixelator single-cell preqc` and pixelator single-cell adapterqc`. -`preqc`used`fastp`internally.`adapterqc`will use`cutadapt` internally. +Quality control is performed using `pixelator single-cell preqc` and `pixelator single-cell adapterqc`. +`preqc` used `fastp` internally. `adapterqc` will use `cutadapt` internally. The preqc stage performs QC and quality filtering of the raw sequencing data. It also generates a QC report in HTML and JSON formats. It saves processed reads as well as reads that were discarded (too short. too many Ns , too low quality, ...). From 5c2924b88c2d80476062075f58637d6ed8c967ac Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 29 Jun 2023 11:12:29 +0200 Subject: [PATCH 028/112] docs: remove reference to --panel-file input --- docs/output.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/output.md b/docs/output.md index baa9acc6..299976d9 100644 --- a/docs/output.md +++ b/docs/output.md @@ -77,7 +77,7 @@ The `adapterqc` stage performs a sanity check on the presence and correctness of ### Demultiplexing -The `demux` command assigns a marker (barcode) to each read. It also generates QC report in JSON format. It saves processed reads (one per antibody) as well as discarded reads (no match to given barcodes/antibodies). In this step an antibody panel file (CSV) is required (--panels-file). This file contains the antibodies present in the data as well as their sequences and it needs the following columns: +The `demux` command assigns a marker (barcode) to each read. It also generates QC report in JSON format. It saves processed reads (one per antibody) as well as discarded reads (no match to given barcodes/antibodies). This file contains the antibodies present in the data as well as their sequences and it needs the following columns:
Output files From 05a831341e99ded22f4ed719f2d25cbef452aee8 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 29 Jun 2023 11:13:12 +0200 Subject: [PATCH 029/112] docs: add backticks --- docs/output.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/output.md b/docs/output.md index 299976d9..50e5c947 100644 --- a/docs/output.md +++ b/docs/output.md @@ -99,7 +99,7 @@ The `demux` command assigns a marker (barcode) to each read. It also generates Q ### Duplicate removal and error correction This step used the `pixelator single-cell collapse` command. -The collapse command removes duplicates and performs error correction. This is achieved using the UPI and UMI sequences to check for uniqueness, collapse and compute a read count. The command generates a QC report in JSON format. Errors are allowed when collapsing reads using different collapsing algorithms (--algorithm). +The collapse command removes duplicates and performs error correction. This is achieved using the UPI and UMI sequences to check for uniqueness, collapse and compute a read count. The command generates a QC report in JSON format. Errors are allowed when collapsing reads using different collapsing algorithms (`--algorithm`). The output format of this command is an edge list in CSV format.
From bba317b95cb2b2ff28d7e5b5517a85029e18971f Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 29 Jun 2023 11:26:55 +0200 Subject: [PATCH 030/112] docs: fix missing space in CITATIONS.md --- CITATIONS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CITATIONS.md b/CITATIONS.md index e91c7033..308fab59 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -19,6 +19,7 @@ > Martin, Marcel. “Cutadapt Removes Adapter Sequences from High-Throughput Sequencing Reads.” EMBnet.Journal 17, no. 1 (May 2, 2011): 10–12. https://doi.org/10.14806/ej.17.1.200. - [fastp] (https://doi.org/10.1002/imt2.107) + > Chen, Shifu. “Ultrafast One-Pass FASTQ Data Preprocessing, Quality Control, and Deduplication Using Fastp.” IMeta 2, no. 2 (2023): e107. https://doi.org/10.1002/imt2.107. ## Software packaging/containerisation tools From 4f5aed8aa498a699165f6adb422ab3f55049ea55 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 29 Jun 2023 12:15:06 +0200 Subject: [PATCH 031/112] feat: pin pixelator to dev branch --- nextflow.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextflow.config b/nextflow.config index 5d5225de..e58228c0 100644 --- a/nextflow.config +++ b/nextflow.config @@ -73,7 +73,7 @@ params { skip_analysis = false // Pipeline specific global config options - pixelator_container = "ghcr.io/pixelgentechnologies/pixelator:pr-444" + pixelator_container = "ghcr.io/pixelgentechnologies/pixelator:dev" // Boilerplate options outdir = null From a6176b34b4a7653202c01031e4af5f635a4e4ce2 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 29 Jun 2023 12:19:11 +0200 Subject: [PATCH 032/112] docs: update usage.md --- docs/usage.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 51c490f3..adcf43a1 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -97,11 +97,11 @@ with `params.yaml` containing: ```yaml input: './samplesheet.csv' outdir: './results/' -genome: 'GRCh37' -input: 'data' <...> ``` +You can find an extensive example of a `params.yaml` file with all options and +documentation in comments [here](../assets/params-file.yml). You can also generate such `YAML`/`JSON` files via [nf-core/launch](https://nf-co.re/launch). ### Updating the pipeline From aab1fd8a78bd268c75c797b046f6415f21945f05 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 30 Jun 2023 13:23:47 +0200 Subject: [PATCH 033/112] chore: fix pixelator to 0.12.0 --- nextflow.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextflow.config b/nextflow.config index e58228c0..cb8b7733 100644 --- a/nextflow.config +++ b/nextflow.config @@ -73,7 +73,7 @@ params { skip_analysis = false // Pipeline specific global config options - pixelator_container = "ghcr.io/pixelgentechnologies/pixelator:dev" + pixelator_container = "ghcr.io/pixelgentechnologies/pixelator:0.12.0" // Boilerplate options outdir = null From ccaabedecb40d7817f728473d94a0bfb2a8fde73 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 30 Jun 2023 15:00:16 +0200 Subject: [PATCH 034/112] chore: remove stray file --- design_options.txt | 1 - 1 file changed, 1 deletion(-) delete mode 100644 design_options.txt diff --git a/design_options.txt b/design_options.txt deleted file mode 100644 index c7571d5f..00000000 --- a/design_options.txt +++ /dev/null @@ -1 +0,0 @@ -D21 From 421c4140f3e8e806033d38c4e458e95373f8cc0c Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 30 Jun 2023 17:48:42 +0200 Subject: [PATCH 035/112] fix: remove cell calling parameters, fix defaults --- assets/params-file.yml | 14 ++------------ conf/modules.config | 2 -- nextflow.config | 8 +++----- nextflow_schema.json | 15 +++++---------- 4 files changed, 10 insertions(+), 29 deletions(-) diff --git a/assets/params-file.yml b/assets/params-file.yml index 0f85533c..683f4586 100644 --- a/assets/params-file.yml +++ b/assets/params-file.yml @@ -130,7 +130,7 @@ ## ------------------------------------------------------------------------------------------ ## Activate the multiplet recovery using leiden community detection ## ------------------------------------------------------------------------------------------ -# multiplet_recovery: null +# multiplet_recovery: true ## ------------------------------------------------------------------------------------------ ## Number of iterations for the leiden algorithm, high values will @@ -168,21 +168,11 @@ ## ------------------------------------------------------------------------------------------ # dynamic_filter: min -## ------------------------------------------------------------------------------------------ -## Enable cell type assignment using pre-trained models -## ------------------------------------------------------------------------------------------ -# cell_type_assignments: null - -## ------------------------------------------------------------------------------------------ -## Enable cell type majority voting using clustering of components -## ------------------------------------------------------------------------------------------ -# majority_vote: null - ## ------------------------------------------------------------------------------------------ ## Enable aggregate calling, information on potential aggregates will ## be added to the output data ## ------------------------------------------------------------------------------------------ -# aggregate_calling: null +# aggregate_calling: true ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## analysis_options diff --git a/conf/modules.config b/conf/modules.config index c89da172..cdccadba 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -136,8 +136,6 @@ process { (params.min_size != null) ? "--min-size ${params.min_size}" : '', (params.max_size != null) ? "--max-size ${params.max_size}" : '', params.dynamic_filter ? "--dynamic-filter ${params.dynamic_filter}" : '', - params.cell_type_assignments ? "--cell-type-assignments" : '', - params.majority_vote ? "--majority-vote" : '', params.aggregate_calling ? "--aggregate-calling" : '', ].join(' ').trim() } diff --git a/nextflow.config b/nextflow.config index cb8b7733..b8ea43d7 100644 --- a/nextflow.config +++ b/nextflow.config @@ -45,7 +45,7 @@ params { collapse_use_counts = false // graph options - multiplet_recovery = false + multiplet_recovery = true leiden_iterations = 10 graph_min_count = 2 @@ -53,9 +53,7 @@ params { min_size = null max_size = null dynamic_filter = 'min' - cell_type_assignments = false - majority_vote = false - aggregate_calling = false + aggregate_calling = true // analysis options compute_polarization = true @@ -76,7 +74,7 @@ params { pixelator_container = "ghcr.io/pixelgentechnologies/pixelator:0.12.0" // Boilerplate options - outdir = null + outdir = "./results" tracedir = "${params.outdir}/pipeline_info" publish_dir_mode = 'copy' email = null diff --git a/nextflow_schema.json b/nextflow_schema.json index e2af1294..a89a02a5 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -24,6 +24,7 @@ "outdir": { "type": "string", "format": "directory-path", + "default": "./results", "description": "The output directory where the results will be saved. You have to use absolute paths to storage on Cloud infrastructure.", "fa_icon": "fas fa-folder-open" }, @@ -187,7 +188,8 @@ "properties": { "multiplet_recovery": { "description": "Activate the multiplet recovery using leiden community detection", - "type": "boolean" + "type": "boolean", + "default": true }, "leiden_iterations": { "fa_icon": "fas repeat", @@ -226,17 +228,10 @@ "enum": ["both", "min", "max"], "default": "min" }, - "cell_type_assignments": { - "description": "Enable cell type assignment using pre-trained models", - "type": "boolean" - }, - "majority_vote": { - "description": "Enable cell type majority voting using clustering of components", - "type": "boolean" - }, "aggregate_calling": { "description": "Enable aggregate calling, information on potential aggregates will be added to the output data", - "type": "boolean" + "type": "boolean", + "default": true } } }, From 2537ef87e7402e2476f8034fd61c86566d072391 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 30 Jun 2023 17:49:02 +0200 Subject: [PATCH 036/112] fix: set slackreport.json author_name --- assets/slackreport.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/slackreport.json b/assets/slackreport.json index 043d02f2..d25c9c08 100644 --- a/assets/slackreport.json +++ b/assets/slackreport.json @@ -3,7 +3,7 @@ { "fallback": "Plain-text summary of the attachment.", "color": "<% if (success) { %>good<% } else { %>danger<%} %>", - "author_name": "sanger-tol/readmapping v${version} - ${runName}", + "author_name": "nf-core/pixelator ${version} - ${runName}", "author_icon": "https://www.nextflow.io/docs/latest/_static/favicon.ico", "text": "<% if (success) { %>Pipeline completed successfully!<% } else { %>Pipeline completed with errors<% } %>", "fields": [ From 7e0077267dc9d9499568a5159e9fbd438a8bbef6 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 30 Jun 2023 17:52:15 +0200 Subject: [PATCH 037/112] fix: remove stray file --- samplesheet.transformed.csv | 4 ---- 1 file changed, 4 deletions(-) delete mode 100644 samplesheet.transformed.csv diff --git a/samplesheet.transformed.csv b/samplesheet.transformed.csv deleted file mode 100644 index 96273ae2..00000000 --- a/samplesheet.transformed.csv +++ /dev/null @@ -1,4 +0,0 @@ -sample,design,panel,fastq_1,fastq_2 -uropod_control_1,D21,az://testdata/micro/UNO_D21_conjV21.csv,az://testdata/micro/uropod_control_300k_S1_R1_001.fastq.gz,az://testdata/micro/uropod_control_300k_S1_R2_001.fastq.gz -uropod_control_1,D21,az://testdata/micro/UNO_D21_conjV21.csv,az://testdata/micro/uropod_control_300k_S1_R1_001.fastq.gz,az://testdata/micro/uropod_control_300k_S1_R2_001.fastq.gz -uropod_control_2,D21,az://testdata/micro/UNO_D21_conjV21.csv,az://testdata/micro/uropod_control_300k_S1_R1_001.fastq.gz,az://testdata/micro/uropod_control_300k_S1_R2_001.fastq.gz From fb2d434422c4189be5947d5e37708206474dbfd9 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 30 Jun 2023 17:54:33 +0200 Subject: [PATCH 038/112] fix: set homePage to https://nf-co.re/pixelator --- nextflow.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextflow.config b/nextflow.config index b8ea43d7..79dbdb0c 100644 --- a/nextflow.config +++ b/nextflow.config @@ -256,7 +256,7 @@ dag { manifest { name = 'nf-core/pixelator' author = """Pixelgen Technologies AB""" - homePage = 'https://github.com/pixelgentechnologies/pixelator' + homePage = 'https://nf-co.re/pixelator' description = """Pipeline for analysis of Molecular Pixelation assays""" mainScript = 'main.nf' nextflowVersion = '!>=22.10.1' From f7f49c1fd24789569164b6223141cd629cf1485b Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 30 Jun 2023 17:55:08 +0200 Subject: [PATCH 039/112] revert: remove CODEOWNERS --- CODEOWNERS | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 CODEOWNERS diff --git a/CODEOWNERS b/CODEOWNERS deleted file mode 100644 index 45c65854..00000000 --- a/CODEOWNERS +++ /dev/null @@ -1,5 +0,0 @@ -# These owners will be the default owners for everything in -# the repo. Unless a later match takes precedence, -# @global-owner1 and @global-owner2 will be requested for -# review when someone opens a pull request. -* @fbdtemme From 785a2c02a82f0ed1b054e92e44a1240105fc3ad0 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 30 Jun 2023 17:56:28 +0200 Subject: [PATCH 040/112] docs: fix markdown links --- CITATIONS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CITATIONS.md b/CITATIONS.md index 308fab59..ce5378ca 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -14,11 +14,11 @@ > Karlsson, Filip, Tomasz Kallas, Divya Thiagarajan, Max Karlsson, Maud Schweitzer, Jose Fernandez Navarro, Louise Leijonancker, et al. “Molecular Pixelation: Single Cell Spatial Proteomics by Sequencing.” bioRxiv, June 8, 2023. https://doi.org/10.1101/2023.06.05.543770. -- [cutadapt] (http://dx.doi.org/10.14806/ej.17.1.200) +- [cutadapt](http://dx.doi.org/10.14806/ej.17.1.200) > Martin, Marcel. “Cutadapt Removes Adapter Sequences from High-Throughput Sequencing Reads.” EMBnet.Journal 17, no. 1 (May 2, 2011): 10–12. https://doi.org/10.14806/ej.17.1.200. -- [fastp] (https://doi.org/10.1002/imt2.107) +- [fastp](https://doi.org/10.1002/imt2.107) > Chen, Shifu. “Ultrafast One-Pass FASTQ Data Preprocessing, Quality Control, and Deduplication Using Fastp.” IMeta 2, no. 2 (2023): e107. https://doi.org/10.1002/imt2.107. From f982c0a76b05afb2cfd9c0672563c298b33a932c Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 30 Jun 2023 17:58:59 +0200 Subject: [PATCH 041/112] fix: update manifest.homePage --- .nf-core.yml | 3 --- nextflow.config | 2 +- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/.nf-core.yml b/.nf-core.yml index f04c9179..b80d615a 100644 --- a/.nf-core.yml +++ b/.nf-core.yml @@ -2,9 +2,6 @@ repository_type: pipeline lint: # No multiqc support for now multiqc_config: false - # TODO: Remove after move to nf-core - nextflow_config: - manifest.homePage: false files_exist: - assets/multiqc_config.yml - conf/igenomes.config diff --git a/nextflow.config b/nextflow.config index 79dbdb0c..ee9fa9ce 100644 --- a/nextflow.config +++ b/nextflow.config @@ -256,7 +256,7 @@ dag { manifest { name = 'nf-core/pixelator' author = """Pixelgen Technologies AB""" - homePage = 'https://nf-co.re/pixelator' + homePage = 'https://github.com/nf-core/pixelator' description = """Pipeline for analysis of Molecular Pixelation assays""" mainScript = 'main.nf' nextflowVersion = '!>=22.10.1' From a1e421333332e2c6806510f79e4801323861348d Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 30 Jun 2023 18:20:29 +0200 Subject: [PATCH 042/112] fix: add missing summary_params, fix indentation --- workflows/pixelator.nf | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/workflows/pixelator.nf b/workflows/pixelator.nf index 0fec50ba..c0d2cf0a 100644 --- a/workflows/pixelator.nf +++ b/workflows/pixelator.nf @@ -15,6 +15,8 @@ if (params.input) { ch_input = file(params.input) } else { exit 1, 'Input sample // Inject the samplesheet SHA into the params object params.samplesheet_sha = ch_input.bytes.digest('sha-1') +def summary_params = NfcoreSchema.paramsSummaryMap(workflow, params) + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CONFIG FILES @@ -113,7 +115,6 @@ workflow PIXELATOR { ch_versions = ch_versions.mix(CAT_FASTQ.out.versions.first().ifEmpty(null)) - // We need to rename to make all reads match the sample name, // since pixelator extracts sample_names from read names RENAME_READS ( ch_cat_fastq ) @@ -174,13 +175,13 @@ workflow PIXELATOR { // as expected by pixelator single-cell report ch_concatenate_data = PIXELATOR_CONCATENATE.out.report_json.mix(PIXELATOR_CONCATENATE.out.metadata) - ch_preqc_data = PIXELATOR_QC.out.preqc_report_json.mix(PIXELATOR_QC.out.preqc_metadata) - ch_adapterqc_data = PIXELATOR_QC.out.adapterqc_report_json.mix(PIXELATOR_QC.out.adapterqc_metadata) - ch_demux_data = PIXELATOR_DEMUX.out.report_json.mix(PIXELATOR_DEMUX.out.metadata) - ch_collapse_data = PIXELATOR_COLLAPSE.out.report_json.mix(PIXELATOR_COLLAPSE.out.metadata) - ch_cluster_data = PIXELATOR_GRAPH.out.all_results - ch_annotate_data = PIXELATOR_ANNOTATE.out.all_results - ch_analysis_data = PIXELATOR_ANALYSIS.out.all_results + ch_preqc_data = PIXELATOR_QC.out.preqc_report_json.mix(PIXELATOR_QC.out.preqc_metadata) + ch_adapterqc_data = PIXELATOR_QC.out.adapterqc_report_json.mix(PIXELATOR_QC.out.adapterqc_metadata) + ch_demux_data = PIXELATOR_DEMUX.out.report_json.mix(PIXELATOR_DEMUX.out.metadata) + ch_collapse_data = PIXELATOR_COLLAPSE.out.report_json.mix(PIXELATOR_COLLAPSE.out.metadata) + ch_cluster_data = PIXELATOR_GRAPH.out.all_results + ch_annotate_data = PIXELATOR_ANNOTATE.out.all_results + ch_analysis_data = PIXELATOR_ANALYSIS.out.all_results GENERATE_REPORTS( ch_panel_files, From a3d94af629a5305966f32a87ca71571ac001bf17 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 31 Jul 2023 09:47:14 +0200 Subject: [PATCH 043/112] Template update for nf-core/tools version 2.10.dev0 --- .github/CONTRIBUTING.md | 1 - .github/ISSUE_TEMPLATE/bug_report.yml | 2 +- .github/workflows/awsfulltest.yml | 11 +- .github/workflows/awstest.yml | 10 +- .github/workflows/ci.yml | 2 +- .gitpod.yml | 5 + CITATIONS.md | 6 + CODE_OF_CONDUCT.md | 133 ++++- README.md | 6 +- assets/methods_description_template.yml | 12 +- assets/multiqc_config.yml | 4 +- assets/nf-core-pixelator_logo_light.png | Bin 11469 -> 76976 bytes assets/slackreport.json | 2 +- conf/modules.config | 9 + conf/test_full.config | 2 - docs/usage.md | 6 +- lib/NfcoreSchema.groovy | 530 ------------------ lib/NfcoreTemplate.groovy | 2 +- lib/WorkflowMain.groovy | 37 -- lib/WorkflowPixelator.groovy | 45 +- main.nf | 19 + modules.json | 6 +- .../custom/dumpsoftwareversions/main.nf | 2 +- modules/nf-core/fastqc/main.nf | 8 +- modules/nf-core/multiqc/main.nf | 2 +- nextflow.config | 52 +- nextflow_schema.json | 29 +- workflows/pixelator.nf | 25 +- 28 files changed, 293 insertions(+), 675 deletions(-) delete mode 100755 lib/NfcoreSchema.groovy diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 60388880..4049c8ae 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -116,4 +116,3 @@ To get started: Devcontainer specs: - [DevContainer config](.devcontainer/devcontainer.json) -- [Dockerfile](.devcontainer/Dockerfile) diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index d2d30445..88f2d3e4 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -42,7 +42,7 @@ body: attributes: label: System information description: | - * Nextflow version _(eg. 22.10.1)_ + * Nextflow version _(eg. 23.04.0)_ * Hardware _(eg. HPC, Desktop, Cloud)_ * Executor _(eg. slurm, local, awsbatch)_ * Container engine: _(e.g. Docker, Singularity, Conda, Podman, Shifter, Charliecloud, or Apptainer)_ diff --git a/.github/workflows/awsfulltest.yml b/.github/workflows/awsfulltest.yml index 7533aa92..33799cac 100644 --- a/.github/workflows/awsfulltest.yml +++ b/.github/workflows/awsfulltest.yml @@ -14,7 +14,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Launch workflow via tower - uses: seqeralabs/action-tower-launch@v1 + uses: seqeralabs/action-tower-launch@v2 # TODO nf-core: You can customise AWS full pipeline tests as required # Add full size test data (but still relatively small datasets for few samples) # on the `test_full.config` test runs with only one set of parameters @@ -22,13 +22,18 @@ jobs: workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} + revision: ${{ github.sha }} workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/pixelator/work-${{ github.sha }} parameters: | { + "hook_url": "${{ secrets.MEGATESTS_ALERTS_SLACK_HOOK_URL }}", "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/pixelator/results-${{ github.sha }}" } - profiles: test_full,aws_tower + profiles: test_full + - uses: actions/upload-artifact@v3 with: name: Tower debug log file - path: tower_action_*.log + path: | + tower_action_*.log + tower_action_*.json diff --git a/.github/workflows/awstest.yml b/.github/workflows/awstest.yml index 6b3a7f32..8a3afb91 100644 --- a/.github/workflows/awstest.yml +++ b/.github/workflows/awstest.yml @@ -12,18 +12,22 @@ jobs: steps: # Launch workflow using Tower CLI tool action - name: Launch workflow via tower - uses: seqeralabs/action-tower-launch@v1 + uses: seqeralabs/action-tower-launch@v2 with: workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} compute_env: ${{ secrets.TOWER_COMPUTE_ENV }} + revision: ${{ github.sha }} workdir: s3://${{ secrets.AWS_S3_BUCKET }}/work/pixelator/work-${{ github.sha }} parameters: | { "outdir": "s3://${{ secrets.AWS_S3_BUCKET }}/pixelator/results-test-${{ github.sha }}" } - profiles: test,aws_tower + profiles: test + - uses: actions/upload-artifact@v3 with: name: Tower debug log file - path: tower_action_*.log + path: | + tower_action_*.log + tower_action_*.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7d197dae..9db8edf1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: strategy: matrix: NXF_VER: - - "22.10.1" + - "23.04.0" - "latest-everything" steps: - name: Check out pipeline code diff --git a/.gitpod.yml b/.gitpod.yml index 85d95ecc..25488dcc 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,4 +1,9 @@ image: nfcore/gitpod:latest +tasks: + - name: Update Nextflow and setup pre-commit + command: | + pre-commit install --install-hooks + nextflow self-update vscode: extensions: # based on nf-core.nf-core-extensionpack diff --git a/CITATIONS.md b/CITATIONS.md index d466b05a..ca3444ff 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -12,7 +12,10 @@ - [FastQC](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/) + > Andrews, S. (2010). FastQC: A Quality Control Tool for High Throughput Sequence Data [Online]. + - [MultiQC](https://pubmed.ncbi.nlm.nih.gov/27312411/) + > Ewels P, Magnusson M, Lundin S, Käller M. MultiQC: summarize analysis results for multiple tools and samples in a single report. Bioinformatics. 2016 Oct 1;32(19):3047-8. doi: 10.1093/bioinformatics/btw354. Epub 2016 Jun 16. PubMed PMID: 27312411; PubMed Central PMCID: PMC5039924. ## Software packaging/containerisation tools @@ -31,5 +34,8 @@ - [Docker](https://dl.acm.org/doi/10.5555/2600239.2600241) + > Merkel, D. (2014). Docker: lightweight linux containers for consistent development and deployment. Linux Journal, 2014(239), 2. doi: 10.5555/2600239.2600241. + - [Singularity](https://pubmed.ncbi.nlm.nih.gov/28494014/) + > Kurtzer GM, Sochat V, Bauer MW. Singularity: Scientific containers for mobility of compute. PLoS One. 2017 May 11;12(5):e0177459. doi: 10.1371/journal.pone.0177459. eCollection 2017. PubMed PMID: 28494014; PubMed Central PMCID: PMC5426675. diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index f4fd052f..c089ec78 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,18 +1,20 @@ -# Code of Conduct at nf-core (v1.0) +# Code of Conduct at nf-core (v1.4) ## Our Pledge -In the interest of fostering an open, collaborative, and welcoming environment, we as contributors and maintainers of nf-core, pledge to making participation in our projects and community a harassment-free experience for everyone, regardless of: +In the interest of fostering an open, collaborative, and welcoming environment, we as contributors and maintainers of nf-core pledge to making participation in our projects and community a harassment-free experience for everyone, regardless of: - Age +- Ability - Body size +- Caste - Familial status - Gender identity and expression - Geographical location - Level of experience - Nationality and national origins - Native language -- Physical and neurological ability +- Neurodiversity - Race or ethnicity - Religion - Sexual identity and orientation @@ -22,80 +24,133 @@ Please note that the list above is alphabetised and is therefore not ranked in a ## Preamble -> Note: This Code of Conduct (CoC) has been drafted by the nf-core Safety Officer and been edited after input from members of the nf-core team and others. "We", in this document, refers to the Safety Officer and members of the nf-core core team, both of whom are deemed to be members of the nf-core community and are therefore required to abide by this Code of Conduct. This document will amended periodically to keep it up-to-date, and in case of any dispute, the most current version will apply. +:::note +This Code of Conduct (CoC) has been drafted by Renuka Kudva, Cris Tuñí, and Michael Heuer, with input from the nf-core Core Team and Susanna Marquez from the nf-core community. "We", in this document, refers to the Safety Officers and members of the nf-core Core Team, both of whom are deemed to be members of the nf-core community and are therefore required to abide by this Code of Conduct. This document will be amended periodically to keep it up-to-date. In case of any dispute, the most current version will apply. +::: -An up-to-date list of members of the nf-core core team can be found [here](https://nf-co.re/about). Our current safety officer is Renuka Kudva. +An up-to-date list of members of the nf-core core team can be found [here](https://nf-co.re/about). + +Our Safety Officers are Saba Nafees, Cris Tuñí, and Michael Heuer. nf-core is a young and growing community that welcomes contributions from anyone with a shared vision for [Open Science Policies](https://www.fosteropenscience.eu/taxonomy/term/8). Open science policies encompass inclusive behaviours and we strive to build and maintain a safe and inclusive environment for all individuals. -We have therefore adopted this code of conduct (CoC), which we require all members of our community and attendees in nf-core events to adhere to in all our workspaces at all times. Workspaces include but are not limited to Slack, meetings on Zoom, Jitsi, YouTube live etc. +We have therefore adopted this CoC, which we require all members of our community and attendees of nf-core events to adhere to in all our workspaces at all times. Workspaces include, but are not limited to, Slack, meetings on Zoom, gather.town, YouTube live etc. -Our CoC will be strictly enforced and the nf-core team reserve the right to exclude participants who do not comply with our guidelines from our workspaces and future nf-core activities. +Our CoC will be strictly enforced and the nf-core team reserves the right to exclude participants who do not comply with our guidelines from our workspaces and future nf-core activities. -We ask all members of our community to help maintain a supportive and productive workspace and to avoid behaviours that can make individuals feel unsafe or unwelcome. Please help us maintain and uphold this CoC. +We ask all members of our community to help maintain supportive and productive workspaces and to avoid behaviours that can make individuals feel unsafe or unwelcome. Please help us maintain and uphold this CoC. -Questions, concerns or ideas on what we can include? Contact safety [at] nf-co [dot] re +Questions, concerns, or ideas on what we can include? Contact members of the Safety Team on Slack or email safety [at] nf-co [dot] re. ## Our Responsibilities -The safety officer is responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behaviour. +Members of the Safety Team (the Safety Officers) are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behaviour. -The safety officer in consultation with the nf-core core team have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. +The Safety Team, in consultation with the nf-core core team, have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this CoC, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. -Members of the core team or the safety officer who violate the CoC will be required to recuse themselves pending investigation. They will not have access to any reports of the violations and be subject to the same actions as others in violation of the CoC. +Members of the core team or the Safety Team who violate the CoC will be required to recuse themselves pending investigation. They will not have access to any reports of the violations and will be subject to the same actions as others in violation of the CoC. -## When are where does this Code of Conduct apply? +## When and where does this Code of Conduct apply? -Participation in the nf-core community is contingent on following these guidelines in all our workspaces and events. This includes but is not limited to the following listed alphabetically and therefore in no order of preference: +Participation in the nf-core community is contingent on following these guidelines in all our workspaces and events, such as hackathons, workshops, bytesize, and collaborative workspaces on gather.town. These guidelines include, but are not limited to, the following (listed alphabetically and therefore in no order of preference): - Communicating with an official project email address. - Communicating with community members within the nf-core Slack channel. - Participating in hackathons organised by nf-core (both online and in-person events). -- Participating in collaborative work on GitHub, Google Suite, community calls, mentorship meetings, email correspondence. -- Participating in workshops, training, and seminar series organised by nf-core (both online and in-person events). This applies to events hosted on web-based platforms such as Zoom, Jitsi, YouTube live etc. +- Participating in collaborative work on GitHub, Google Suite, community calls, mentorship meetings, email correspondence, and on the nf-core gather.town workspace. +- Participating in workshops, training, and seminar series organised by nf-core (both online and in-person events). This applies to events hosted on web-based platforms such as Zoom, gather.town, Jitsi, YouTube live etc. - Representing nf-core on social media. This includes both official and personal accounts. ## nf-core cares 😊 -nf-core's CoC and expectations of respectful behaviours for all participants (including organisers and the nf-core team) include but are not limited to the following (listed in alphabetical order): +nf-core's CoC and expectations of respectful behaviours for all participants (including organisers and the nf-core team) include, but are not limited to, the following (listed in alphabetical order): - Ask for consent before sharing another community member’s personal information (including photographs) on social media. - Be respectful of differing viewpoints and experiences. We are all here to learn from one another and a difference in opinion can present a good learning opportunity. -- Celebrate your accomplishments at events! (Get creative with your use of emojis 🎉 🥳 💯 🙌 !) +- Celebrate your accomplishments! (Get creative with your use of emojis 🎉 🥳 💯 🙌 !) - Demonstrate empathy towards other community members. (We don’t all have the same amount of time to dedicate to nf-core. If tasks are pending, don’t hesitate to gently remind members of your team. If you are leading a task, ask for help if you feel overwhelmed.) - Engage with and enquire after others. (This is especially important given the geographically remote nature of the nf-core community, so let’s do this the best we can) - Focus on what is best for the team and the community. (When in doubt, ask) -- Graciously accept constructive criticism, yet be unafraid to question, deliberate, and learn. +- Accept feedback, yet be unafraid to question, deliberate, and learn. - Introduce yourself to members of the community. (We’ve all been outsiders and we know that talking to strangers can be hard for some, but remember we’re interested in getting to know you and your visions for open science!) -- Show appreciation and **provide clear feedback**. (This is especially important because we don’t see each other in person and it can be harder to interpret subtleties. Also remember that not everyone understands a certain language to the same extent as you do, so **be clear in your communications to be kind.**) +- Show appreciation and **provide clear feedback**. (This is especially important because we don’t see each other in person and it can be harder to interpret subtleties. Also remember that not everyone understands a certain language to the same extent as you do, so **be clear in your communication to be kind.**) - Take breaks when you feel like you need them. -- Using welcoming and inclusive language. (Participants are encouraged to display their chosen pronouns on Zoom or in communication on Slack.) +- Use welcoming and inclusive language. (Participants are encouraged to display their chosen pronouns on Zoom or in communication on Slack) ## nf-core frowns on 😕 -The following behaviours from any participants within the nf-core community (including the organisers) will be considered unacceptable under this code of conduct. Engaging or advocating for any of the following could result in expulsion from nf-core workspaces. +The following behaviours from any participants within the nf-core community (including the organisers) will be considered unacceptable under this CoC. Engaging or advocating for any of the following could result in expulsion from nf-core workspaces: - Deliberate intimidation, stalking or following and sustained disruption of communication among participants of the community. This includes hijacking shared screens through actions such as using the annotate tool in conferencing software such as Zoom. - “Doxing” i.e. posting (or threatening to post) another person’s personal identifying information online. - Spamming or trolling of individuals on social media. -- Use of sexual or discriminatory imagery, comments, or jokes and unwelcome sexual attention. -- Verbal and text comments that reinforce social structures of domination related to gender, gender identity and expression, sexual orientation, ability, physical appearance, body size, race, age, religion or work experience. +- Use of sexual or discriminatory imagery, comments, jokes, or unwelcome sexual attention. +- Verbal and text comments that reinforce social structures of domination related to gender, gender identity and expression, sexual orientation, ability, physical appearance, body size, race, age, religion, or work experience. ### Online Trolling -The majority of nf-core interactions and events are held online. Unfortunately, holding events online comes with the added issue of online trolling. This is unacceptable, reports of such behaviour will be taken very seriously, and perpetrators will be excluded from activities immediately. +The majority of nf-core interactions and events are held online. Unfortunately, holding events online comes with the risk of online trolling. This is unacceptable — reports of such behaviour will be taken very seriously and perpetrators will be excluded from activities immediately. -All community members are required to ask members of the group they are working within for explicit consent prior to taking screenshots of individuals during video calls. +All community members are **required** to ask members of the group they are working with for explicit consent prior to taking screenshots of individuals during video calls. -## Procedures for Reporting CoC violations +## Procedures for reporting CoC violations If someone makes you feel uncomfortable through their behaviours or actions, report it as soon as possible. -You can reach out to members of the [nf-core core team](https://nf-co.re/about) and they will forward your concerns to the safety officer(s). +You can reach out to members of the Safety Team (Saba Nafees, Cris Tuñí, and Michael Heuer) on Slack. Alternatively, contact a member of the nf-core core team [nf-core core team](https://nf-co.re/about), and they will forward your concerns to the Safety Team. + +Issues directly concerning members of the Core Team or the Safety Team will be dealt with by other members of the core team and the safety manager — possible conflicts of interest will be taken into account. nf-core is also in discussions about having an ombudsperson and details will be shared in due course. + +All reports will be handled with the utmost discretion and confidentiality. + +You can also report any CoC violations to safety [at] nf-co [dot] re. In your email report, please do your best to include: + +- Your contact information. +- Identifying information (e.g. names, nicknames, pseudonyms) of the participant who has violated the Code of Conduct. +- The behaviour that was in violation and the circumstances surrounding the incident. +- The approximate time of the behaviour (if different than the time the report was made). +- Other people involved in the incident, if applicable. +- If you believe the incident is ongoing. +- If there is a publicly available record (e.g. mailing list record, a screenshot). +- Any additional information. + +After you file a report, one or more members of our Safety Team will contact you to follow up on your report. + +## Who will read and handle reports + +All reports will be read and handled by the members of the Safety Team at nf-core. + +If members of the Safety Team are deemed to have a conflict of interest with a report, they will be required to recuse themselves as per our Code of Conduct and will not have access to any follow-ups. + +To keep this first report confidential from any of the Safety Team members, please submit your first report by direct messaging on Slack/direct email to any of the nf-core members you are comfortable disclosing the information to, and be explicit about which member(s) you do not consent to sharing the information with. + +## Reviewing reports + +After receiving the report, members of the Safety Team will review the incident report to determine whether immediate action is required, for example, whether there is immediate threat to participants’ safety. + +The Safety Team, in consultation with members of the nf-core core team, will assess the information to determine whether the report constitutes a Code of Conduct violation, for them to decide on a course of action. + +In the case of insufficient information, one or more members of the Safety Team may contact the reporter, the reportee, or any other attendees to obtain more information. -Issues directly concerning members of the core team will be dealt with by other members of the core team and the safety manager, and possible conflicts of interest will be taken into account. nf-core is also in discussions about having an ombudsperson, and details will be shared in due course. +Once additional information is gathered, the Safety Team will collectively review and decide on the best course of action to take, if any. The Safety Team reserves the right to not act on a report. -All reports will be handled with utmost discretion and confidentially. +## Confidentiality + +All reports, and any additional information included, are only shared with the team of safety officers (and possibly members of the core team, in case the safety officer is in violation of the CoC). We will respect confidentiality requests for the purpose of protecting victims of abuse. + +We will not name harassment victims, beyond discussions between the safety officer and members of the nf-core team, without the explicit consent of the individuals involved. + +## Enforcement + +Actions taken by the nf-core’s Safety Team may include, but are not limited to: + +- Asking anyone to stop a behaviour. +- Asking anyone to leave the event and online spaces either temporarily, for the remainder of the event, or permanently. +- Removing access to the gather.town and Slack, either temporarily or permanently. +- Communicating to all participants to reinforce our expectations for conduct and remind what is unacceptable behaviour; this may be public for practical reasons. +- Communicating to all participants that an incident has taken place and how we will act or have acted — this may be for the purpose of letting event participants know we are aware of and dealing with the incident. +- Banning anyone from participating in nf-core-managed spaces, future events, and activities, either temporarily or permanently. +- No action. ## Attribution and Acknowledgements @@ -106,6 +161,22 @@ All reports will be handled with utmost discretion and confidentially. ## Changelog -### v1.0 - March 12th, 2021 +### v1.4 - February 8th, 2022 + +- Included a new member of the Safety Team. Corrected a typographical error in the text. + +### v1.3 - December 10th, 2021 + +- Added a statement that the CoC applies to nf-core gather.town workspaces. Corrected typographical errors in the text. + +### v1.2 - November 12th, 2021 + +- Removed information specific to reporting CoC violations at the Hackathon in October 2021. + +### v1.1 - October 14th, 2021 + +- Updated with names of new Safety Officers and specific information for the hackathon in October 2021. + +### v1.0 - March 15th, 2021 - Complete rewrite from original [Contributor Covenant](http://contributor-covenant.org/) CoC. diff --git a/README.md b/README.md index 73fbd41d..99c13025 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![AWS CI](https://img.shields.io/badge/CI%20tests-full%20size-FF9900?labelColor=000000&logo=Amazon%20AWS)](https://nf-co.re/pixelator/results)[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.XXXXXXX-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.XXXXXXX) -[![Nextflow](https://img.shields.io/badge/nextflow%20DSL2-%E2%89%A522.10.1-23aa62.svg)](https://www.nextflow.io/) +[![Nextflow](https://img.shields.io/badge/nextflow%20DSL2-%E2%89%A523.04.0-23aa62.svg)](https://www.nextflow.io/) [![run with conda](http://img.shields.io/badge/run%20with-conda-3EB049?labelColor=000000&logo=anaconda)](https://docs.conda.io/en/latest/) [![run with docker](https://img.shields.io/badge/run%20with-docker-0db7ed?labelColor=000000&logo=docker)](https://www.docker.com/) [![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/) @@ -66,11 +66,11 @@ nextflow run nf-core/pixelator \ > provided by the `-c` Nextflow option can be used to provide any configuration _**except for parameters**_; > see [docs](https://nf-co.re/usage/configuration#custom-configuration-files). -For more details, please refer to the [usage documentation](https://nf-co.re/pixelator/usage) and the [parameter documentation](https://nf-co.re/pixelator/parameters). +For more details and further functionality, please refer to the [usage documentation](https://nf-co.re/pixelator/usage) and the [parameter documentation](https://nf-co.re/pixelator/parameters). ## Pipeline output -To see the the results of a test run with a full size dataset refer to the [results](https://nf-co.re/pixelator/results) tab on the nf-core website pipeline page. +To see the results of an example test run with a full size dataset refer to the [results](https://nf-co.re/pixelator/results) tab on the nf-core website pipeline page. For more details about the output files and reports, please refer to the [output documentation](https://nf-co.re/pixelator/output). diff --git a/assets/methods_description_template.yml b/assets/methods_description_template.yml index 64149951..2b6ed3cd 100644 --- a/assets/methods_description_template.yml +++ b/assets/methods_description_template.yml @@ -3,17 +3,21 @@ description: "Suggested text and references to use when describing pipeline usag section_name: "nf-core/pixelator Methods Description" section_href: "https://github.com/nf-core/pixelator" plot_type: "html" -## TODO nf-core: Update the HTML below to your prefered methods description, e.g. add publication citation for this pipeline +## TODO nf-core: Update the HTML below to your preferred methods description, e.g. add publication citation for this pipeline ## You inject any metadata in the Nextflow '${workflow}' object data: |

Methods

-

Data was processed using nf-core/pixelator v${workflow.manifest.version} ${doi_text} of the nf-core collection of workflows (Ewels et al., 2020).

+

Data was processed using nf-core/pixelator v${workflow.manifest.version} ${doi_text} of the nf-core collection of workflows (Ewels et al., 2020), utilising reproducible software environments from the Bioconda (Grüning et al., 2018) and Biocontainers (da Veiga Leprevost et al., 2017) projects.

The pipeline was executed with Nextflow v${workflow.nextflow.version} (Di Tommaso et al., 2017) with the following command:

${workflow.commandLine}
+

${tool_citations}

References

    -
  • Di Tommaso, P., Chatzou, M., Floden, E. W., Barja, P. P., Palumbo, E., & Notredame, C. (2017). Nextflow enables reproducible computational workflows. Nature Biotechnology, 35(4), 316-319. https://doi.org/10.1038/nbt.3820
  • -
  • Ewels, P. A., Peltzer, A., Fillinger, S., Patel, H., Alneberg, J., Wilm, A., Garcia, M. U., Di Tommaso, P., & Nahnsen, S. (2020). The nf-core framework for community-curated bioinformatics pipelines. Nature Biotechnology, 38(3), 276-278. https://doi.org/10.1038/s41587-020-0439-x
  • +
  • Di Tommaso, P., Chatzou, M., Floden, E. W., Barja, P. P., Palumbo, E., & Notredame, C. (2017). Nextflow enables reproducible computational workflows. Nature Biotechnology, 35(4), 316-319. doi: 10.1038/nbt.3820
  • +
  • Ewels, P. A., Peltzer, A., Fillinger, S., Patel, H., Alneberg, J., Wilm, A., Garcia, M. U., Di Tommaso, P., & Nahnsen, S. (2020). The nf-core framework for community-curated bioinformatics pipelines. Nature Biotechnology, 38(3), 276-278. doi: 10.1038/s41587-020-0439-x
  • +
  • Grüning, B., Dale, R., Sjödin, A., Chapman, B. A., Rowe, J., Tomkins-Tinch, C. H., Valieris, R., Köster, J., & Bioconda Team. (2018). Bioconda: sustainable and comprehensive software distribution for the life sciences. Nature Methods, 15(7), 475–476. doi: 10.1038/s41592-018-0046-7
  • +
  • da Veiga Leprevost, F., Grüning, B. A., Alves Aflitos, S., Röst, H. L., Uszkoreit, J., Barsnes, H., Vaudel, M., Moreno, P., Gatto, L., Weber, J., Bai, M., Jimenez, R. C., Sachsenberg, T., Pfeuffer, J., Vera Alvarez, R., Griss, J., Nesvizhskii, A. I., & Perez-Riverol, Y. (2017). BioContainers: an open-source and community-driven framework for software standardization. Bioinformatics (Oxford, England), 33(16), 2580–2582. doi: 10.1093/bioinformatics/btx192
  • + ${tool_bibliography}
Notes:
diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index 000a465b..1e71e68e 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -1,7 +1,7 @@ report_comment: > - This report has been generated by the nf-core/pixelator + This report has been generated by the nf-core/pixelator analysis pipeline. For information about how to interpret these results, please see the - documentation. + documentation. report_section_order: "nf-core-pixelator-methods-description": order: -1000 diff --git a/assets/nf-core-pixelator_logo_light.png b/assets/nf-core-pixelator_logo_light.png index e16cf97f5ac76d5b37b950bcd9dc5bddf755a353..0ad2407e1df5477710d1bd938ea50ae963613635 100644 GIT binary patch literal 76976 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�ev`Q}H7y#R1zKkC5n?0_OP7^FqWWeXhX0t0pNK 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

)bNtXB<>| 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 D1B}NV literal 11469 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 } else { %>danger<%} %>", - "author_name": "sanger-tol/readmapping v${version} - ${runName}", + "author_name": "nf-core/pixelator v${version} - ${runName}", "author_icon": "https://www.nextflow.io/docs/latest/_static/favicon.ico", "text": "<% if (success) { %>Pipeline completed successfully!<% } else { %>Pipeline completed with errors<% } %>", "fields": [ diff --git a/conf/modules.config b/conf/modules.config index da58a5d8..39e81386 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -38,4 +38,13 @@ process { ] } + withName: 'MULTIQC' { + ext.args = params.multiqc_title ? "--title \"$params.multiqc_title\"" : '' + publishDir = [ + path: { "${params.outdir}/multiqc" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + } diff --git a/conf/test_full.config b/conf/test_full.config index 34c1ce5d..73c389cb 100644 --- a/conf/test_full.config +++ b/conf/test_full.config @@ -10,8 +10,6 @@ ---------------------------------------------------------------------------------------- */ -cleanup = true - params { config_profile_name = 'Full test profile' config_profile_description = 'Full test dataset to check pipeline function' diff --git a/docs/usage.md b/docs/usage.md index b167116f..066841a5 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -57,7 +57,7 @@ An [example samplesheet](../assets/samplesheet.csv) has been provided with the p The typical command for running the pipeline is as follows: ```bash -nextflow run nf-core/pixelator --input samplesheet.csv --outdir --genome GRCh37 -profile docker +nextflow run nf-core/pixelator --input ./samplesheet.csv --outdir ./results --genome GRCh37 -profile docker ``` This will launch the pipeline with the `docker` configuration profile. See below for more information about profiles. @@ -76,7 +76,8 @@ If you wish to repeatedly use the same parameters for multiple runs, rather than Pipeline settings can be provided in a `yaml` or `json` file via `-params-file `. > ⚠️ Do not use `-c ` to specify parameters as this will result in errors. Custom config files specified with `-c` must only be used for [tuning process resource specifications](https://nf-co.re/docs/usage/configuration#tuning-workflow-resources), other infrastructural tweaks (such as output directories), or module arguments (args). -> The above pipeline run specified with a params file in yaml format: + +The above pipeline run specified with a params file in yaml format: ```bash nextflow run nf-core/pixelator -profile docker -params-file params.yaml @@ -88,7 +89,6 @@ with `params.yaml` containing: input: './samplesheet.csv' outdir: './results/' genome: 'GRCh37' -input: 'data' <...> ``` diff --git a/lib/NfcoreSchema.groovy b/lib/NfcoreSchema.groovy deleted file mode 100755 index 9b34804d..00000000 --- a/lib/NfcoreSchema.groovy +++ /dev/null @@ -1,530 +0,0 @@ -// -// This file holds several functions used to perform JSON parameter validation, help and summary rendering for the nf-core pipeline template. -// - -import nextflow.Nextflow -import org.everit.json.schema.Schema -import org.everit.json.schema.loader.SchemaLoader -import org.everit.json.schema.ValidationException -import org.json.JSONObject -import org.json.JSONTokener -import org.json.JSONArray -import groovy.json.JsonSlurper -import groovy.json.JsonBuilder - -class NfcoreSchema { - - // - // Resolve Schema path relative to main workflow directory - // - public static String getSchemaPath(workflow, schema_filename='nextflow_schema.json') { - return "${workflow.projectDir}/${schema_filename}" - } - - // - // Function to loop over all parameters defined in schema and check - // whether the given parameters adhere to the specifications - // - /* groovylint-disable-next-line UnusedPrivateMethodParameter */ - public static void validateParameters(workflow, params, log, schema_filename='nextflow_schema.json') { - def has_error = false - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// - // Check for nextflow core params and unexpected params - def json = new File(getSchemaPath(workflow, schema_filename=schema_filename)).text - def Map schemaParams = (Map) new JsonSlurper().parseText(json).get('definitions') - def nf_params = [ - // Options for base `nextflow` command - 'bg', - 'c', - 'C', - 'config', - 'd', - 'D', - 'dockerize', - 'h', - 'log', - 'q', - 'quiet', - 'syslog', - 'v', - - // Options for `nextflow run` command - 'ansi', - 'ansi-log', - 'bg', - 'bucket-dir', - 'c', - 'cache', - 'config', - 'dsl2', - 'dump-channels', - 'dump-hashes', - 'E', - 'entry', - 'latest', - 'lib', - 'main-script', - 'N', - 'name', - 'offline', - 'params-file', - 'pi', - 'plugins', - 'poll-interval', - 'pool-size', - 'profile', - 'ps', - 'qs', - 'queue-size', - 'r', - 'resume', - 'revision', - 'stdin', - 'stub', - 'stub-run', - 'test', - 'w', - 'with-apptainer', - 'with-charliecloud', - 'with-conda', - 'with-dag', - 'with-docker', - 'with-mpi', - 'with-notification', - 'with-podman', - 'with-report', - 'with-singularity', - 'with-timeline', - 'with-tower', - 'with-trace', - 'with-weblog', - 'without-docker', - 'without-podman', - 'work-dir' - ] - def unexpectedParams = [] - - // Collect expected parameters from the schema - def expectedParams = [] - def enums = [:] - for (group in schemaParams) { - for (p in group.value['properties']) { - expectedParams.push(p.key) - if (group.value['properties'][p.key].containsKey('enum')) { - enums[p.key] = group.value['properties'][p.key]['enum'] - } - } - } - - for (specifiedParam in params.keySet()) { - // nextflow params - if (nf_params.contains(specifiedParam)) { - log.error "ERROR: You used a core Nextflow option with two hyphens: '--${specifiedParam}'. Please resubmit with '-${specifiedParam}'" - has_error = true - } - // unexpected params - def params_ignore = params.schema_ignore_params.split(',') + 'schema_ignore_params' - def expectedParamsLowerCase = expectedParams.collect{ it.replace("-", "").toLowerCase() } - def specifiedParamLowerCase = specifiedParam.replace("-", "").toLowerCase() - def isCamelCaseBug = (specifiedParam.contains("-") && !expectedParams.contains(specifiedParam) && expectedParamsLowerCase.contains(specifiedParamLowerCase)) - if (!expectedParams.contains(specifiedParam) && !params_ignore.contains(specifiedParam) && !isCamelCaseBug) { - // Temporarily remove camelCase/camel-case params #1035 - def unexpectedParamsLowerCase = unexpectedParams.collect{ it.replace("-", "").toLowerCase()} - if (!unexpectedParamsLowerCase.contains(specifiedParamLowerCase)){ - unexpectedParams.push(specifiedParam) - } - } - } - - //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~// - // Validate parameters against the schema - InputStream input_stream = new File(getSchemaPath(workflow, schema_filename=schema_filename)).newInputStream() - JSONObject raw_schema = new JSONObject(new JSONTokener(input_stream)) - - // Remove anything that's in params.schema_ignore_params - raw_schema = removeIgnoredParams(raw_schema, params) - - Schema schema = SchemaLoader.load(raw_schema) - - // Clean the parameters - def cleanedParams = cleanParameters(params) - - // Convert to JSONObject - def jsonParams = new JsonBuilder(cleanedParams) - JSONObject params_json = new JSONObject(jsonParams.toString()) - - // Validate - try { - schema.validate(params_json) - } catch (ValidationException e) { - println '' - log.error 'ERROR: Validation of pipeline parameters failed!' - JSONObject exceptionJSON = e.toJSON() - printExceptions(exceptionJSON, params_json, log, enums) - println '' - has_error = true - } - - // Check for unexpected parameters - if (unexpectedParams.size() > 0) { - Map colors = NfcoreTemplate.logColours(params.monochrome_logs) - println '' - def warn_msg = 'Found unexpected parameters:' - for (unexpectedParam in unexpectedParams) { - warn_msg = warn_msg + "\n* --${unexpectedParam}: ${params[unexpectedParam].toString()}" - } - log.warn warn_msg - log.info "- ${colors.dim}Ignore this warning: params.schema_ignore_params = \"${unexpectedParams.join(',')}\" ${colors.reset}" - println '' - } - - if (has_error) { - Nextflow.error('Exiting!') - } - } - - // - // Beautify parameters for --help - // - public static String paramsHelp(workflow, params, command, schema_filename='nextflow_schema.json') { - Map colors = NfcoreTemplate.logColours(params.monochrome_logs) - Integer num_hidden = 0 - String output = '' - output += 'Typical pipeline command:\n\n' - output += " ${colors.cyan}${command}${colors.reset}\n\n" - Map params_map = paramsLoad(getSchemaPath(workflow, schema_filename=schema_filename)) - Integer max_chars = paramsMaxChars(params_map) + 1 - Integer desc_indent = max_chars + 14 - Integer dec_linewidth = 160 - desc_indent - for (group in params_map.keySet()) { - Integer num_params = 0 - String group_output = colors.underlined + colors.bold + group + colors.reset + '\n' - def group_params = params_map.get(group) // This gets the parameters of that particular group - for (param in group_params.keySet()) { - if (group_params.get(param).hidden && !params.show_hidden_params) { - num_hidden += 1 - continue; - } - def type = '[' + group_params.get(param).type + ']' - def description = group_params.get(param).description - def defaultValue = group_params.get(param).default != null ? " [default: " + group_params.get(param).default.toString() + "]" : '' - def description_default = description + colors.dim + defaultValue + colors.reset - // Wrap long description texts - // Loosely based on https://dzone.com/articles/groovy-plain-text-word-wrap - if (description_default.length() > dec_linewidth){ - List olines = [] - String oline = "" // " " * indent - description_default.split(" ").each() { wrd -> - if ((oline.size() + wrd.size()) <= dec_linewidth) { - oline += wrd + " " - } else { - olines += oline - oline = wrd + " " - } - } - olines += oline - description_default = olines.join("\n" + " " * desc_indent) - } - group_output += " --" + param.padRight(max_chars) + colors.dim + type.padRight(10) + colors.reset + description_default + '\n' - num_params += 1 - } - group_output += '\n' - if (num_params > 0){ - output += group_output - } - } - if (num_hidden > 0){ - output += colors.dim + "!! Hiding $num_hidden params, use --show_hidden_params to show them !!\n" + colors.reset - } - output += NfcoreTemplate.dashedLine(params.monochrome_logs) - return output - } - - // - // Groovy Map summarising parameters/workflow options used by the pipeline - // - public static LinkedHashMap paramsSummaryMap(workflow, params, schema_filename='nextflow_schema.json') { - // Get a selection of core Nextflow workflow options - def Map workflow_summary = [:] - if (workflow.revision) { - workflow_summary['revision'] = workflow.revision - } - workflow_summary['runName'] = workflow.runName - if (workflow.containerEngine) { - workflow_summary['containerEngine'] = workflow.containerEngine - } - if (workflow.container) { - workflow_summary['container'] = workflow.container - } - workflow_summary['launchDir'] = workflow.launchDir - workflow_summary['workDir'] = workflow.workDir - workflow_summary['projectDir'] = workflow.projectDir - workflow_summary['userName'] = workflow.userName - workflow_summary['profile'] = workflow.profile - workflow_summary['configFiles'] = workflow.configFiles.join(', ') - - // Get pipeline parameters defined in JSON Schema - def Map params_summary = [:] - def params_map = paramsLoad(getSchemaPath(workflow, schema_filename=schema_filename)) - for (group in params_map.keySet()) { - def sub_params = new LinkedHashMap() - def group_params = params_map.get(group) // This gets the parameters of that particular group - for (param in group_params.keySet()) { - if (params.containsKey(param)) { - def params_value = params.get(param) - def schema_value = group_params.get(param).default - def param_type = group_params.get(param).type - if (schema_value != null) { - if (param_type == 'string') { - if (schema_value.contains('$projectDir') || schema_value.contains('${projectDir}')) { - def sub_string = schema_value.replace('\$projectDir', '') - sub_string = sub_string.replace('\${projectDir}', '') - if (params_value.contains(sub_string)) { - schema_value = params_value - } - } - if (schema_value.contains('$params.outdir') || schema_value.contains('${params.outdir}')) { - def sub_string = schema_value.replace('\$params.outdir', '') - sub_string = sub_string.replace('\${params.outdir}', '') - if ("${params.outdir}${sub_string}" == params_value) { - schema_value = params_value - } - } - } - } - - // We have a default in the schema, and this isn't it - if (schema_value != null && params_value != schema_value) { - sub_params.put(param, params_value) - } - // No default in the schema, and this isn't empty - else if (schema_value == null && params_value != "" && params_value != null && params_value != false) { - sub_params.put(param, params_value) - } - } - } - params_summary.put(group, sub_params) - } - return [ 'Core Nextflow options' : workflow_summary ] << params_summary - } - - // - // Beautify parameters for summary and return as string - // - public static String paramsSummaryLog(workflow, params) { - Map colors = NfcoreTemplate.logColours(params.monochrome_logs) - String output = '' - def params_map = paramsSummaryMap(workflow, params) - def max_chars = paramsMaxChars(params_map) - for (group in params_map.keySet()) { - def group_params = params_map.get(group) // This gets the parameters of that particular group - if (group_params) { - output += colors.bold + group + colors.reset + '\n' - for (param in group_params.keySet()) { - output += " " + colors.blue + param.padRight(max_chars) + ": " + colors.green + group_params.get(param) + colors.reset + '\n' - } - output += '\n' - } - } - output += "!! Only displaying parameters that differ from the pipeline defaults !!\n" - output += NfcoreTemplate.dashedLine(params.monochrome_logs) - return output - } - - // - // Loop over nested exceptions and print the causingException - // - private static void printExceptions(ex_json, params_json, log, enums, limit=5) { - def causingExceptions = ex_json['causingExceptions'] - if (causingExceptions.length() == 0) { - def m = ex_json['message'] =~ /required key \[([^\]]+)\] not found/ - // Missing required param - if (m.matches()) { - log.error "* Missing required parameter: --${m[0][1]}" - } - // Other base-level error - else if (ex_json['pointerToViolation'] == '#') { - log.error "* ${ex_json['message']}" - } - // Error with specific param - else { - def param = ex_json['pointerToViolation'] - ~/^#\// - def param_val = params_json[param].toString() - if (enums.containsKey(param)) { - def error_msg = "* --${param}: '${param_val}' is not a valid choice (Available choices" - if (enums[param].size() > limit) { - log.error "${error_msg} (${limit} of ${enums[param].size()}): ${enums[param][0..limit-1].join(', ')}, ... )" - } else { - log.error "${error_msg}: ${enums[param].join(', ')})" - } - } else { - log.error "* --${param}: ${ex_json['message']} (${param_val})" - } - } - } - for (ex in causingExceptions) { - printExceptions(ex, params_json, log, enums) - } - } - - // - // Remove an element from a JSONArray - // - private static JSONArray removeElement(json_array, element) { - def list = [] - int len = json_array.length() - for (int i=0;i - if(raw_schema.keySet().contains('definitions')){ - raw_schema.definitions.each { definition -> - for (key in definition.keySet()){ - if (definition[key].get("properties").keySet().contains(ignore_param)){ - // Remove the param to ignore - definition[key].get("properties").remove(ignore_param) - // If the param was required, change this - if (definition[key].has("required")) { - def cleaned_required = removeElement(definition[key].required, ignore_param) - definition[key].put("required", cleaned_required) - } - } - } - } - } - if(raw_schema.keySet().contains('properties') && raw_schema.get('properties').keySet().contains(ignore_param)) { - raw_schema.get("properties").remove(ignore_param) - } - if(raw_schema.keySet().contains('required') && raw_schema.required.contains(ignore_param)) { - def cleaned_required = removeElement(raw_schema.required, ignore_param) - raw_schema.put("required", cleaned_required) - } - } - return raw_schema - } - - // - // Clean and check parameters relative to Nextflow native classes - // - private static Map cleanParameters(params) { - def new_params = params.getClass().newInstance(params) - for (p in params) { - // remove anything evaluating to false - if (!p['value']) { - new_params.remove(p.key) - } - // Cast MemoryUnit to String - if (p['value'].getClass() == nextflow.util.MemoryUnit) { - new_params.replace(p.key, p['value'].toString()) - } - // Cast Duration to String - if (p['value'].getClass() == nextflow.util.Duration) { - new_params.replace(p.key, p['value'].toString().replaceFirst(/d(?!\S)/, "day")) - } - // Cast LinkedHashMap to String - if (p['value'].getClass() == LinkedHashMap) { - new_params.replace(p.key, p['value'].toString()) - } - } - return new_params - } - - // - // This function tries to read a JSON params file - // - private static LinkedHashMap paramsLoad(String json_schema) { - def params_map = new LinkedHashMap() - try { - params_map = paramsRead(json_schema) - } catch (Exception e) { - println "Could not read parameters settings from JSON. $e" - params_map = new LinkedHashMap() - } - return params_map - } - - // - // Method to actually read in JSON file using Groovy. - // Group (as Key), values are all parameters - // - Parameter1 as Key, Description as Value - // - Parameter2 as Key, Description as Value - // .... - // Group - // - - private static LinkedHashMap paramsRead(String json_schema) throws Exception { - def json = new File(json_schema).text - def Map schema_definitions = (Map) new JsonSlurper().parseText(json).get('definitions') - def Map schema_properties = (Map) new JsonSlurper().parseText(json).get('properties') - /* Tree looks like this in nf-core schema - * definitions <- this is what the first get('definitions') gets us - group 1 - title - description - properties - parameter 1 - type - description - parameter 2 - type - description - group 2 - title - description - properties - parameter 1 - type - description - * properties <- parameters can also be ungrouped, outside of definitions - parameter 1 - type - description - */ - - // Grouped params - def params_map = new LinkedHashMap() - schema_definitions.each { key, val -> - def Map group = schema_definitions."$key".properties // Gets the property object of the group - def title = schema_definitions."$key".title - def sub_params = new LinkedHashMap() - group.each { innerkey, value -> - sub_params.put(innerkey, value) - } - params_map.put(title, sub_params) - } - - // Ungrouped params - def ungrouped_params = new LinkedHashMap() - schema_properties.each { innerkey, value -> - ungrouped_params.put(innerkey, value) - } - params_map.put("Other parameters", ungrouped_params) - - return params_map - } - - // - // Get maximum number of characters across all parameter names - // - private static Integer paramsMaxChars(params_map) { - Integer max_chars = 0 - for (group in params_map.keySet()) { - def group_params = params_map.get(group) // This gets the parameters of that particular group - for (param in group_params.keySet()) { - if (param.size() > max_chars) { - max_chars = param.size() - } - } - } - return max_chars - } -} diff --git a/lib/NfcoreTemplate.groovy b/lib/NfcoreTemplate.groovy index 25a0a74a..408951ae 100755 --- a/lib/NfcoreTemplate.groovy +++ b/lib/NfcoreTemplate.groovy @@ -128,7 +128,7 @@ class NfcoreTemplate { def email_html = html_template.toString() // Render the sendmail template - def max_multiqc_email_size = params.max_multiqc_email_size as nextflow.util.MemoryUnit + def max_multiqc_email_size = (params.containsKey('max_multiqc_email_size') ? params.max_multiqc_email_size : 0) as nextflow.util.MemoryUnit def smail_fields = [ email: email_address, subject: subject, email_txt: email_txt, email_html: email_html, projectDir: "$projectDir", mqcFile: mqc_report, mqcMaxSize: max_multiqc_email_size.toBytes() ] def sf = new File("$projectDir/assets/sendmail_template.txt") def sendmail_template = engine.createTemplate(sf).make(smail_fields) diff --git a/lib/WorkflowMain.groovy b/lib/WorkflowMain.groovy index ab4ecf5f..ce9041a7 100755 --- a/lib/WorkflowMain.groovy +++ b/lib/WorkflowMain.groovy @@ -20,40 +20,11 @@ class WorkflowMain { " https://github.com/${workflow.manifest.name}/blob/master/CITATIONS.md" } - // - // Generate help string - // - public static String help(workflow, params) { - def command = "nextflow run ${workflow.manifest.name} --input samplesheet.csv --genome GRCh37 -profile docker" - def help_string = '' - help_string += NfcoreTemplate.logo(workflow, params.monochrome_logs) - help_string += NfcoreSchema.paramsHelp(workflow, params, command) - help_string += '\n' + citation(workflow) + '\n' - help_string += NfcoreTemplate.dashedLine(params.monochrome_logs) - return help_string - } - - // - // Generate parameter summary log string - // - public static String paramsSummaryLog(workflow, params) { - def summary_log = '' - summary_log += NfcoreTemplate.logo(workflow, params.monochrome_logs) - summary_log += NfcoreSchema.paramsSummaryLog(workflow, params) - summary_log += '\n' + citation(workflow) + '\n' - summary_log += NfcoreTemplate.dashedLine(params.monochrome_logs) - return summary_log - } // // Validate parameters and print summary to screen // public static void initialise(workflow, params, log) { - // Print help to screen if required - if (params.help) { - log.info help(workflow, params) - System.exit(0) - } // Print workflow version and exit on --version if (params.version) { @@ -62,14 +33,6 @@ class WorkflowMain { System.exit(0) } - // Print parameter summary log to screen - log.info paramsSummaryLog(workflow, params) - - // Validate workflow parameters via the JSON schema - if (params.validate_params) { - NfcoreSchema.validateParameters(workflow, params, log) - } - // Check that a -profile or Nextflow config has been provided to run the pipeline NfcoreTemplate.checkConfigProvided(workflow, log) diff --git a/lib/WorkflowPixelator.groovy b/lib/WorkflowPixelator.groovy index de998ec6..33b3b977 100755 --- a/lib/WorkflowPixelator.groovy +++ b/lib/WorkflowPixelator.groovy @@ -11,6 +11,7 @@ class WorkflowPixelator { // Check and validate parameters // public static void initialise(params, log) { + genomeExistsError(params, log) @@ -46,15 +47,57 @@ class WorkflowPixelator { return yaml_file_text } - public static String methodsDescriptionText(run_workflow, mqc_methods_yaml) { + // + // Generate methods description for MultiQC + // + + public static String toolCitationText(params) { + + // TODO Optionally add in-text citation tools to this list. + // Can use ternary operators to dynamically construct based conditions, e.g. params["run_xyz"] ? "Tool (Foo et al. 2023)" : "", + // Uncomment function in methodsDescriptionText to render in MultiQC report + def citation_text = [ + "Tools used in the workflow included:", + "FastQC (Andrews 2010),", + "MultiQC (Ewels et al. 2016)", + "." + ].join(' ').trim() + + return citation_text + } + + public static String toolBibliographyText(params) { + + // TODO Optionally add bibliographic entries to this list. + // Can use ternary operators to dynamically construct based conditions, e.g. params["run_xyz"] ? "

  • Author (2023) Pub name, Journal, DOI
  • " : "", + // Uncomment function in methodsDescriptionText to render in MultiQC report + def reference_text = [ + "
  • Andrews S, (2010) FastQC, URL: https://www.bioinformatics.babraham.ac.uk/projects/fastqc/).
  • ", + "
  • Ewels, P., Magnusson, M., Lundin, S., & Käller, M. (2016). MultiQC: summarize analysis results for multiple tools and samples in a single report. Bioinformatics , 32(19), 3047–3048. doi: /10.1093/bioinformatics/btw354
  • " + ].join(' ').trim() + + return reference_text + } + + public static String methodsDescriptionText(run_workflow, mqc_methods_yaml, params) { // Convert to a named map so can be used as with familar NXF ${workflow} variable syntax in the MultiQC YML file def meta = [:] meta.workflow = run_workflow.toMap() meta["manifest_map"] = run_workflow.manifest.toMap() + // Pipeline DOI meta["doi_text"] = meta.manifest_map.doi ? "(doi:
    ${meta.manifest_map.doi})" : "" meta["nodoi_text"] = meta.manifest_map.doi ? "": "
  • If available, make sure to update the text to include the Zenodo DOI of version of the pipeline used.
  • " + // Tool references + meta["tool_citations"] = "" + meta["tool_bibliography"] = "" + + // TODO Only uncomment below if logic in toolCitationText/toolBibliographyText has been filled! + //meta["tool_citations"] = toolCitationText(params).replaceAll(", \\.", ".").replaceAll("\\. \\.", ".").replaceAll(", \\.", ".") + //meta["tool_bibliography"] = toolBibliographyText(params) + + def methods_text = mqc_methods_yaml.text def engine = new SimpleTemplateEngine() diff --git a/main.nf b/main.nf index b4333fe6..f0450fce 100644 --- a/main.nf +++ b/main.nf @@ -17,6 +17,9 @@ nextflow.enable.dsl = 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +// TODO nf-core: Remove this line if you don't need a FASTA file +// This is an example of how to use getGenomeAttribute() to fetch parameters +// from igenomes.config using `--genome` params.fasta = WorkflowMain.getGenomeAttribute(params, 'fasta') /* @@ -25,6 +28,22 @@ params.fasta = WorkflowMain.getGenomeAttribute(params, 'fasta') ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ +include { validateParameters; paramsHelp } from 'plugin/nf-validation' + +// Print help message if needed +if (params.help) { + def logo = NfcoreTemplate.logo(workflow, params.monochrome_logs) + def citation = '\n' + WorkflowMain.citation(workflow) + '\n' + def String command = "nextflow run ${workflow.manifest.name} --input samplesheet.csv --genome GRCh37 -profile docker" + log.info logo + paramsHelp(command) + citation + NfcoreTemplate.dashedLine(params.monochrome_logs) + System.exit(0) +} + +// Validate input parameters +if (params.validate_params) { + validateParameters() +} + WorkflowMain.initialise(workflow, params, log) /* diff --git a/modules.json b/modules.json index 9f4ac028..d96e6898 100644 --- a/modules.json +++ b/modules.json @@ -7,17 +7,17 @@ "nf-core": { "custom/dumpsoftwareversions": { "branch": "master", - "git_sha": "76cc4938c1f6ea5c7d83fed1eeffc146787f9543", + "git_sha": "911696ea0b62df80e900ef244d7867d177971f73", "installed_by": ["modules"] }, "fastqc": { "branch": "master", - "git_sha": "c8e35eb2055c099720a75538d1b8adb3fb5a464c", + "git_sha": "bd8092b67b5103bdd52e300f75889442275c3117", "installed_by": ["modules"] }, "multiqc": { "branch": "master", - "git_sha": "f2d63bd5b68925f98f572eed70993d205cc694b7", + "git_sha": "911696ea0b62df80e900ef244d7867d177971f73", "installed_by": ["modules"] } } diff --git a/modules/nf-core/custom/dumpsoftwareversions/main.nf b/modules/nf-core/custom/dumpsoftwareversions/main.nf index 800a6099..ebc87273 100644 --- a/modules/nf-core/custom/dumpsoftwareversions/main.nf +++ b/modules/nf-core/custom/dumpsoftwareversions/main.nf @@ -5,7 +5,7 @@ process CUSTOM_DUMPSOFTWAREVERSIONS { conda "bioconda::multiqc=1.14" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/multiqc:1.14--pyhdfd78af_0' : - 'quay.io/biocontainers/multiqc:1.14--pyhdfd78af_0' }" + 'biocontainers/multiqc:1.14--pyhdfd78af_0' }" input: path versions diff --git a/modules/nf-core/fastqc/main.nf b/modules/nf-core/fastqc/main.nf index 9ae58381..249f9064 100644 --- a/modules/nf-core/fastqc/main.nf +++ b/modules/nf-core/fastqc/main.nf @@ -5,7 +5,7 @@ process FASTQC { conda "bioconda::fastqc=0.11.9" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/fastqc:0.11.9--0' : - 'quay.io/biocontainers/fastqc:0.11.9--0' }" + 'biocontainers/fastqc:0.11.9--0' }" input: tuple val(meta), path(reads) @@ -29,7 +29,11 @@ process FASTQC { printf "%s %s\\n" $rename_to | while read old_name new_name; do [ -f "\${new_name}" ] || ln -s \$old_name \$new_name done - fastqc $args --threads $task.cpus $renamed_files + + fastqc \\ + $args \\ + --threads $task.cpus \\ + $renamed_files cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/nf-core/multiqc/main.nf b/modules/nf-core/multiqc/main.nf index 4b604749..1fc387be 100644 --- a/modules/nf-core/multiqc/main.nf +++ b/modules/nf-core/multiqc/main.nf @@ -4,7 +4,7 @@ process MULTIQC { conda "bioconda::multiqc=1.14" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/multiqc:1.14--pyhdfd78af_0' : - 'quay.io/biocontainers/multiqc:1.14--pyhdfd78af_0' }" + 'biocontainers/multiqc:1.14--pyhdfd78af_0' }" input: path multiqc_files, stageAs: "?/*" diff --git a/nextflow.config b/nextflow.config index 59fdf4cf..2bd922ab 100644 --- a/nextflow.config +++ b/nextflow.config @@ -12,12 +12,12 @@ params { // TODO nf-core: Specify your pipeline's command line flags // Input options input = null - - // References genome = null igenomes_base = 's3://ngi-igenomes/igenomes' igenomes_ignore = false + + // MultiQC options multiqc_config = null multiqc_title = null @@ -27,7 +27,6 @@ params { // Boilerplate options outdir = null - tracedir = "${params.outdir}/pipeline_info" publish_dir_mode = 'copy' email = null email_on_fail = null @@ -36,19 +35,15 @@ params { hook_url = null help = false version = false - validate_params = true - show_hidden_params = false - schema_ignore_params = 'genomes' - // Config options + config_profile_name = null + config_profile_description = null custom_config_version = 'master' custom_config_base = "https://raw.githubusercontent.com/nf-core/configs/${params.custom_config_version}" - config_profile_description = null config_profile_contact = null config_profile_url = null - config_profile_name = null - + // Max resource options // Defaults only, expecting to be overwritten @@ -56,6 +51,13 @@ params { max_cpus = 16 max_time = '240.h' + // Schema validation default options + validationFailUnrecognisedParams = false + validationLenientMode = false + validationSchemaIgnoreParams = 'genomes' + validationShowHiddenParams = false + validate_params = true + } // Load base.config by default for all pipelines @@ -75,13 +77,11 @@ try { // } catch (Exception e) { // System.err.println("WARNING: Could not load nf-core/config/pixelator profiles: ${params.custom_config_base}/pipeline/pixelator.config") // } - - profiles { debug { dumpHashes = true process.beforeScript = 'echo $HOSTNAME' - cleanup = false + cleanup = false } conda { conda.enabled = true @@ -104,7 +104,6 @@ profiles { } docker { docker.enabled = true - docker.registry = 'quay.io' docker.userEmulation = true conda.enabled = false singularity.enabled = false @@ -128,7 +127,6 @@ profiles { } podman { podman.enabled = true - podman.registry = 'quay.io' conda.enabled = false docker.enabled = false singularity.enabled = false @@ -172,6 +170,18 @@ profiles { test_full { includeConfig 'conf/test_full.config' } } +// Set default registry for Apptainer, Docker, Podman and Singularity independent of -profile +// Will not be used unless Apptainer / Docker / Podman / Singularity are enabled +// Set to your registry if you have a mirror of containers +apptainer.registry = 'quay.io' +docker.registry = 'quay.io' +podman.registry = 'quay.io' +singularity.registry = 'quay.io' + +// Nextflow plugins +plugins { + id 'nf-validation' // Validation of pipeline parameters and creation of an input channel from a sample sheet +} // Load igenomes.config if required if (!params.igenomes_ignore) { @@ -179,8 +189,6 @@ if (!params.igenomes_ignore) { } else { params.genomes = [:] } - - // Export these variables to prevent local Python/R libraries from conflicting with those in the container // The JULIA depot path has been adjusted to a fixed path `/usr/local/share/julia` that needs to be used for packages in the container. // See https://apeltzer.github.io/post/03-julia-lang-nextflow/ for details on that. Once we have a common agreement on where to keep Julia packages, this is adjustable. @@ -198,19 +206,19 @@ process.shell = ['/bin/bash', '-euo', 'pipefail'] def trace_timestamp = new java.util.Date().format( 'yyyy-MM-dd_HH-mm-ss') timeline { enabled = true - file = "${params.tracedir}/execution_timeline_${trace_timestamp}.html" + file = "${params.outdir}/pipeline_info/execution_timeline_${trace_timestamp}.html" } report { enabled = true - file = "${params.tracedir}/execution_report_${trace_timestamp}.html" + file = "${params.outdir}/pipeline_info/execution_report_${trace_timestamp}.html" } trace { enabled = true - file = "${params.tracedir}/execution_trace_${trace_timestamp}.txt" + file = "${params.outdir}/pipeline_info/execution_trace_${trace_timestamp}.txt" } dag { enabled = true - file = "${params.tracedir}/pipeline_dag_${trace_timestamp}.html" + file = "${params.outdir}/pipeline_info/pipeline_dag_${trace_timestamp}.html" } manifest { @@ -219,7 +227,7 @@ manifest { homePage = 'https://github.com/nf-core/pixelator' description = """Pipeline for analysis of Molecular Pixelation assays""" mainScript = 'main.nf' - nextflowVersion = '!>=22.10.1' + nextflowVersion = '!>=23.04.0' version = '1.0.0dev' doi = '' } diff --git a/nextflow_schema.json b/nextflow_schema.json index f559b384..af353c6b 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -15,9 +15,9 @@ "input": { "type": "string", "format": "file-path", + "exists": true, "mimetype": "text/csv", "pattern": "^\\S+\\.csv$", - "schema": "assets/schema_input.json", "description": "Path to comma-separated file containing information about the samples in the experiment.", "help_text": "You will need to create a design file with information about the samples in your experiment before running the pipeline. Use this parameter to specify its location. It has to be a comma-separated file with 3 columns, and a header row. See [usage docs](https://nf-co.re/pixelator/usage#samplesheet-input).", "fa_icon": "fas fa-file-csv" @@ -57,6 +57,7 @@ "fasta": { "type": "string", "format": "file-path", + "exists": true, "mimetype": "text/plain", "pattern": "^\\S+\\.fn?a(sta)?(\\.gz)?$", "description": "Path to FASTA genome file.", @@ -157,7 +158,7 @@ "description": "Maximum amount of time that can be requested for any single job.", "default": "240.h", "fa_icon": "far fa-clock", - "pattern": "^(\\d+\\.?\\s*(s|m|h|day)\\s*)+$", + "pattern": "^(\\d+\\.?\\s*(s|m|h|d|day)\\s*)+$", "hidden": true, "help_text": "Use to set an upper-limit for the time requirement for each process. Should be a string in the format integer-unit e.g. `--max_time '2.h'`" } @@ -228,6 +229,7 @@ }, "multiqc_config": { "type": "string", + "format": "file-path", "description": "Custom config file to supply to MultiQC.", "fa_icon": "fas fa-cog", "hidden": true @@ -243,13 +245,6 @@ "description": "Custom MultiQC yaml file containing HTML including a methods description.", "fa_icon": "fas fa-cog" }, - "tracedir": { - "type": "string", - "description": "Directory to keep pipeline Nextflow logs and reports.", - "default": "${params.outdir}/pipeline_info", - "fa_icon": "fas fa-cogs", - "hidden": true - }, "validate_params": { "type": "boolean", "description": "Boolean whether to validate parameters against the schema at runtime", @@ -257,12 +252,26 @@ "fa_icon": "fas fa-check-square", "hidden": true }, - "show_hidden_params": { + "validationShowHiddenParams": { "type": "boolean", "fa_icon": "far fa-eye-slash", "description": "Show all params when using `--help`", "hidden": true, "help_text": "By default, parameters set as _hidden_ in the schema are not shown on the command line when a user runs with `--help`. Specifying this option will tell the pipeline to show all parameters." + }, + "validationFailUnrecognisedParams": { + "type": "boolean", + "fa_icon": "far fa-check-circle", + "description": "Validation of parameters fails when an unrecognised parameter is found.", + "hidden": true, + "help_text": "By default, when an unrecognised parameter is found, it returns a warinig." + }, + "validationLenientMode": { + "type": "boolean", + "fa_icon": "far fa-check-circle", + "description": "Validation of parameters in lenient more.", + "hidden": true, + "help_text": "Allows string values that are parseable as numbers or booleans. For further information see [JSONSchema docs](https://github.com/everit-org/json-schema#lenient-mode)." } } } diff --git a/workflows/pixelator.nf b/workflows/pixelator.nf index 379c5eb7..b405d1b8 100644 --- a/workflows/pixelator.nf +++ b/workflows/pixelator.nf @@ -1,21 +1,19 @@ /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - VALIDATE INPUTS + PRINT PARAMS SUMMARY ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -def summary_params = NfcoreSchema.paramsSummaryMap(workflow, params) +include { paramsSummaryLog; paramsSummaryMap } from 'plugin/nf-validation' -// Validate input parameters -WorkflowPixelator.initialise(params, log) +def logo = NfcoreTemplate.logo(workflow, params.monochrome_logs) +def citation = '\n' + WorkflowMain.citation(workflow) + '\n' +def summary_params = paramsSummaryMap(workflow) -// TODO nf-core: Add all file path parameters for the pipeline to the list below -// Check input path parameters to see if they exist -def checkPathParamList = [ params.input, params.multiqc_config, params.fasta ] -for (param in checkPathParamList) { if (param) { file(param, checkIfExists: true) } } +// Print parameter summary log to screen +log.info logo + paramsSummaryLog(workflow) + citation -// Check mandatory parameters -if (params.input) { ch_input = file(params.input) } else { exit 1, 'Input samplesheet not specified!' } +WorkflowPixelator.initialise(params, log) /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -69,9 +67,12 @@ workflow PIXELATOR { // SUBWORKFLOW: Read in samplesheet, validate and stage input files // INPUT_CHECK ( - ch_input + file(params.input) ) ch_versions = ch_versions.mix(INPUT_CHECK.out.versions) + // TODO: OPTIONAL, you can use nf-validation plugin to create an input channel from the samplesheet with Channel.fromSamplesheet("input") + // See the documentation https://nextflow-io.github.io/nf-validation/samplesheets/fromSamplesheet/ + // ! There is currently no tooling to help you write a sample sheet schema // // MODULE: Run FastQC @@ -91,7 +92,7 @@ workflow PIXELATOR { workflow_summary = WorkflowPixelator.paramsSummaryMultiqc(workflow, summary_params) ch_workflow_summary = Channel.value(workflow_summary) - methods_description = WorkflowPixelator.methodsDescriptionText(workflow, ch_multiqc_custom_methods_description) + methods_description = WorkflowPixelator.methodsDescriptionText(workflow, ch_multiqc_custom_methods_description, params) ch_methods_description = Channel.value(methods_description) ch_multiqc_files = Channel.empty() From 54d6b062677b8b8d6eb472927c3e0781ea1360d7 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 31 Jul 2023 10:22:02 +0200 Subject: [PATCH 044/112] fix: resolve TEMPLATE sync issues --- assets/schema_input.json | 3 ++- assets/test_samplesheet.csv | 4 ++++ workflows/pixelator.nf | 14 ++++---------- 3 files changed, 10 insertions(+), 11 deletions(-) create mode 100644 assets/test_samplesheet.csv diff --git a/assets/schema_input.json b/assets/schema_input.json index 71976eab..fdd4abaf 100644 --- a/assets/schema_input.json +++ b/assets/schema_input.json @@ -52,6 +52,7 @@ ] } }, - "required": ["sample", "design", "panel", "fastq_1"] + "required": ["sample", "design", "fastq_1"], + "oneOf": [{ "required": ["panel"] }, { "required": ["panel_file"] }] } } diff --git a/assets/test_samplesheet.csv b/assets/test_samplesheet.csv new file mode 100644 index 00000000..9ad1694e --- /dev/null +++ b/assets/test_samplesheet.csv @@ -0,0 +1,4 @@ +sample,design,panel,panel_file,fastq_1,fastq_2 +uropod_control_1,D21,,UNO_D21.csv,uropod_control_300k_S1_R1_001.part_001.fastq.gz,uropod_control_300k_S1_R2_001.part_001.fastq.gz +uropod_control_1,D21,,UNO_D21.csv,uropod_control_300k_S1_R1_001.part_002.fastq.gz,uropod_control_300k_S1_R2_001.part_002.fastq.gz +uropod_control_2,D21,human-sc-immunology-spatial-proteomics,,uropod_control_300k_S1_R1_001.fastq.gz,uropod_control_300k_S1_R2_001.fastq.gz diff --git a/workflows/pixelator.nf b/workflows/pixelator.nf index 6945f254..5f06c33a 100644 --- a/workflows/pixelator.nf +++ b/workflows/pixelator.nf @@ -7,15 +7,6 @@ include { paramsSummaryLog; paramsSummaryMap } from 'plugin/nf-validation' -// Check input path parameters to see if they exist -def checkPathParamList = [ params.input ] -for (param in checkPathParamList) { if (param) { file(param, checkIfExists: true) } } - -// Inject the samplesheet SHA into the params object -params.samplesheet_sha = ch_input.bytes.digest('sha-1') - -def summary_params = NfcoreSchema.paramsSummaryMap(workflow, params) - def logo = NfcoreTemplate.logo(workflow, params.monochrome_logs) def citation = '\n' + WorkflowMain.citation(workflow) + '\n' def summary_params = paramsSummaryMap(workflow) @@ -25,6 +16,10 @@ log.info logo + paramsSummaryLog(workflow) + citation WorkflowPixelator.initialise(params, log) +// Inject the samplesheet SHA-1 into the params object +ch_input = file(params.input) +params.samplesheet_sha = ch_input.bytes.digest('sha-1') + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ CONFIG FILES @@ -83,7 +78,6 @@ include { PIXELATOR_ANNOTATE } from '../modules/local/pixelator/singl def multiqc_report = [] workflow PIXELATOR { - ch_input = file(params.input) ch_versions = Channel.empty() COLLECT_METADATA () From 37e0ffa845fd2757e9519eb033d6aba0fe2733cb Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 31 Jul 2023 10:32:16 +0200 Subject: [PATCH 045/112] feat: rename pixelator sc concatenate to amplicon --- README.md | 2 +- conf/modules.config | 2 +- .../single-cell/{concatenate => amplicon}/main.nf | 14 +++++++------- modules/local/pixelator/single-cell/report/main.nf | 2 +- subworkflows/local/generate_reports.nf | 10 +++++----- workflows/pixelator.nf | 12 ++++++------ 6 files changed, 21 insertions(+), 21 deletions(-) rename modules/local/pixelator/single-cell/{concatenate => amplicon}/main.nf (63%) diff --git a/README.md b/README.md index 33a0610a..44a1d747 100644 --- a/README.md +++ b/README.md @@ -28,7 +28,7 @@ It takes a samplesheet as input and will process your data using `pixelator` to -1. Build amplicon from input reads ([`pixelator concatenate`](https://github.com/PixelgenTechnologies/pixelator)) +1. Build amplicon from input reads ([`pixelator amplicon`](https://github.com/PixelgenTechnologies/pixelator)) 2. Read QC and filtering, correctness of the pixel binding sequence sequences ([`pixelator preqc | pixelator adapterqc`](https://github.com/PixelgenTechnologies/pixelator)) 3. Assign a marker (barcode) to each read ([`pixelator demux`](https://github.com/PixelgenTechnologies/pixelator)) 4. Error correction, duplicate removal, compute read counts ([`pixelator collapse`](https://github.com/PixelgenTechnologies/pixelator)) diff --git a/conf/modules.config b/conf/modules.config index cdccadba..51f36b9b 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -68,7 +68,7 @@ process { // use explicit params.my_option != null checks to avoid issues with 0 evaluating false // some pixelator flags do accept zero as a value - withName: PIXELATOR_CONCATENATE { + withName: PIXELATOR_AMPLICON { ext.args = { [ "--design ${meta.design}", diff --git a/modules/local/pixelator/single-cell/concatenate/main.nf b/modules/local/pixelator/single-cell/amplicon/main.nf similarity index 63% rename from modules/local/pixelator/single-cell/concatenate/main.nf rename to modules/local/pixelator/single-cell/amplicon/main.nf index faf71233..4df7c3de 100644 --- a/modules/local/pixelator/single-cell/concatenate/main.nf +++ b/modules/local/pixelator/single-cell/amplicon/main.nf @@ -1,6 +1,6 @@ -process PIXELATOR_CONCATENATE { +process PIXELATOR_AMPLICON { tag "$meta.id" label 'process_low' @@ -13,10 +13,10 @@ process PIXELATOR_CONCATENATE { tuple val(meta), path(reads) output: - tuple val(meta), path("concatenate/*.merged.{fq,fastq}.gz"), emit: merged - tuple val(meta), path("concatenate/*.report.json"), emit: report_json - tuple val(meta), path("concatenate/*.meta.json"), emit: metadata - tuple val(meta), path("*pixelator-concatenate.log"), emit: log + tuple val(meta), path("amplicon/*.merged.{fq,fastq}.gz"), emit: merged + tuple val(meta), path("amplicon/*.report.json"), emit: report_json + tuple val(meta), path("amplicon/*.meta.json"), emit: metadata + tuple val(meta), path("*pixelator-amplicon.log"), emit: log path "versions.yml" , emit: versions @@ -30,10 +30,10 @@ process PIXELATOR_CONCATENATE { """ pixelator \\ --cores $task.cpus \\ - --log-file ${prefix}.pixelator-concatenate.log \\ + --log-file ${prefix}.pixelator-amplicon.log \\ --verbose \\ single-cell \\ - concatenate \\ + amplicon \\ --output . \\ $args \\ ${reads} diff --git a/modules/local/pixelator/single-cell/report/main.nf b/modules/local/pixelator/single-cell/report/main.nf index b35304bd..80e7b4fb 100644 --- a/modules/local/pixelator/single-cell/report/main.nf +++ b/modules/local/pixelator/single-cell/report/main.nf @@ -11,7 +11,7 @@ process PIXELATOR_REPORT { input: tuple val(meta), path(panel_file) val panel - path concatenate_data, stageAs: "results/concatenate/*" + path amplicon_data, stageAs: "results/amplicon/*" path preqc_data, stageAs: "results/preqc/*" path adapterqc_data, stageAs: "results/adapterqc/*" path demux_data, stageAs: "results/demux/*" diff --git a/subworkflows/local/generate_reports.nf b/subworkflows/local/generate_reports.nf index dcfba60e..dbe475b8 100644 --- a/subworkflows/local/generate_reports.nf +++ b/subworkflows/local/generate_reports.nf @@ -4,7 +4,7 @@ include { PIXELATOR_REPORT } from '../../modules/local/pixelator/sing workflow GENERATE_REPORTS { take: panel_files // [meta, panel_file] or [meta, []] - concatenate_data + amplicon_data preqc_data adapterqc_data demux_data @@ -46,7 +46,7 @@ workflow GENERATE_REPORTS { } - ch_concatenate_col = concatenate_data + ch_amplicon_col = amplicon_data .map { meta, data -> [ meta.id, data] } .groupTuple() @@ -86,7 +86,7 @@ workflow GENERATE_REPORTS { // ], ...] ch_report_data = ch_meta_col .concat ( ch_panel_files_col ) - .concat ( ch_concatenate_col ) + .concat ( ch_amplicon_col ) .concat ( ch_preqc_col ) .concat ( ch_adapterqc_col ) .concat ( ch_demux_col ) @@ -100,7 +100,7 @@ workflow GENERATE_REPORTS { // pixelator single-cell report using stageAs ch_panel_files_grouped = ch_report_data.map { id, data -> [ data[0], data[1] ] } - ch_concatenate_grouped = ch_report_data.map { id, data -> data[2] ? data[2].flatten() : [] } + ch_amplicon_grouped = ch_report_data.map { id, data -> data[2] ? data[2].flatten() : [] } ch_preqc_grouped = ch_report_data.map { id, data -> data[3] ? data[3].flatten() : [] } ch_adapterqc_grouped = ch_report_data.map { id, data -> data[4] ? data[4].flatten() : [] } ch_demux_grouped = ch_report_data.map { id, data -> data[5] ? data[5].flatten() : [] } @@ -116,7 +116,7 @@ workflow GENERATE_REPORTS { PIXELATOR_REPORT ( ch_panel_files_grouped, ch_panel_keys, - ch_concatenate_grouped, + ch_amplicon_grouped, ch_preqc_grouped, ch_adapterqc_grouped, ch_demux_grouped, diff --git a/workflows/pixelator.nf b/workflows/pixelator.nf index 5f06c33a..2ea1e347 100644 --- a/workflows/pixelator.nf +++ b/workflows/pixelator.nf @@ -60,7 +60,7 @@ include { CAT_FASTQ } from '../modules/nf-core/cat/fastq/main' // include { RENAME_READS } from '../modules/local/rename_reads' include { COLLECT_METADATA } from '../modules/local/collect_metadata' -include { PIXELATOR_CONCATENATE } from '../modules/local/pixelator/single-cell/concatenate/main' +include { PIXELATOR_AMPLICON } from '../modules/local/pixelator/single-cell/amplicon/main' include { PIXELATOR_QC } from '../modules/local/pixelator/single-cell/qc/main' include { PIXELATOR_DEMUX } from '../modules/local/pixelator/single-cell/demux/main' include { PIXELATOR_COLLAPSE } from '../modules/local/pixelator/single-cell/collapse/main' @@ -129,9 +129,9 @@ workflow PIXELATOR { paired_end: true } - PIXELATOR_CONCATENATE ( ch_renamed_reads ) - ch_merged = PIXELATOR_CONCATENATE.out.merged - ch_versions = ch_versions.mix(PIXELATOR_CONCATENATE.out.versions.first()) + PIXELATOR_AMPLICON ( ch_renamed_reads ) + ch_merged = PIXELATOR_AMPLICON.out.merged + ch_versions = ch_versions.mix(PIXELATOR_AMPLICON.out.versions.first()) ch_input_reads = ch_merged @@ -177,7 +177,7 @@ workflow PIXELATOR { // Note that we need to split inout per subcommand to stage those files in the right subdirs // as expected by pixelator single-cell report - ch_concatenate_data = PIXELATOR_CONCATENATE.out.report_json.mix(PIXELATOR_CONCATENATE.out.metadata) + ch_amplicon_data = PIXELATOR_AMPLICON.out.report_json.mix(PIXELATOR_AMPLICON.out.metadata) ch_preqc_data = PIXELATOR_QC.out.preqc_report_json.mix(PIXELATOR_QC.out.preqc_metadata) ch_adapterqc_data = PIXELATOR_QC.out.adapterqc_report_json.mix(PIXELATOR_QC.out.adapterqc_metadata) ch_demux_data = PIXELATOR_DEMUX.out.report_json.mix(PIXELATOR_DEMUX.out.metadata) @@ -188,7 +188,7 @@ workflow PIXELATOR { GENERATE_REPORTS( ch_panel_files, - ch_concatenate_data, + ch_amplicon_data, ch_preqc_data, ch_adapterqc_data, ch_demux_data, From 3c92b14e0d826ccce56f1a0216e777f31c96988c Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 31 Jul 2023 10:59:58 +0200 Subject: [PATCH 046/112] fix: remove png output channel --- modules/local/pixelator/single-cell/annotate/main.nf | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/local/pixelator/single-cell/annotate/main.nf b/modules/local/pixelator/single-cell/annotate/main.nf index d44d2836..9078943e 100644 --- a/modules/local/pixelator/single-cell/annotate/main.nf +++ b/modules/local/pixelator/single-cell/annotate/main.nf @@ -13,7 +13,6 @@ process PIXELATOR_ANNOTATE { output: tuple val(meta), path("annotate/*.dataset.pxl"), emit: dataset tuple val(meta), path("annotate/*.report.json"), emit: report_json - tuple val(meta), path("annotate/*.png"), emit: png tuple val(meta), path("annotate/*.meta.json"), emit: metadata tuple val(meta), path("annotate/*"), emit: all_results tuple val(meta), path("*pixelator-annotate.log"), emit: log From 074a0e0724099c104f3a7bcf5ae63aa440b29f6a Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 31 Jul 2023 13:21:48 +0200 Subject: [PATCH 047/112] fix: resolve issue with relative path files and http samplesheet --- bin/check_samplesheet.py | 10 +++++----- conf/test.config | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/bin/check_samplesheet.py b/bin/check_samplesheet.py index e6755af8..d64f8822 100755 --- a/bin/check_samplesheet.py +++ b/bin/check_samplesheet.py @@ -45,14 +45,13 @@ def make_absolute_path(path: str, base: PathLike = None) -> str: url[0] = base_url[0] or "file" url[2] = str(resolved_path) - # Make sure there are three /// (hidden netloc) in urls. + # Make sure there are three /// (hidden netloc) in urls with file scheme. # other schemes [s3, gs, az] only use two // if scheme == "file": resolved_path = str(resolved_path) if resolved_path.is_absolute() else str(PurePath("/", str(resolved_path))) - else: - resolved_path = str(resolved_path).lstrip("/") + return f"{scheme}://{resolved_path}" - return f"{scheme}://{resolved_path}" + return urllib.parse.urlunparse(url) def validate_whitespace(row: MutableMapping[str, str], index: int): @@ -139,7 +138,8 @@ def __init__( def get_base_dir(path: str) -> str: url = urllib.parse.urlparse(path) parent_path = PurePath(url.path).parent - return f"{url.scheme}://{str(parent_path)}" + netloc = url.netloc + return f"{url.scheme}://{netloc}{str(parent_path)}" def validate_and_transform(self, row): """ diff --git a/conf/test.config b/conf/test.config index 5b22be6a..49db9406 100644 --- a/conf/test.config +++ b/conf/test.config @@ -23,7 +23,7 @@ params { max_memory = '6.GB' max_time = '6.h' - input = "s3://pixelgen-technologies-datasets/nf-core-pixelator/testdata/micro/test_samplesheet.csv" + input = "https://pixelgen-technologies-datasets.s3.eu-north-1.amazonaws.com/nf-core-pixelator/testdata/micro/test_samplesheet.csv" outdir = "results" tracedir = "results" From f598fd94b9bf0a4a5c97f279459537520c1d3a77 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 31 Jul 2023 13:28:15 +0200 Subject: [PATCH 048/112] feat: add sync hook for schema en nf-params.yml --- .pre-commit-config.yaml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index ca4aec07..84676416 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -8,3 +8,15 @@ repos: rev: 23.3.0 hooks: - id: black + + - repo: local + hooks: + - id: nf-core/tools parameters.yaml + name: Update nf-params.yml file with schema + language: python + additional_dependencies: + - git+https://github.com/nf-core/tools@dev + entry: nf-core + args: [create-params-file, --output, assets/nf-params.yml, "--force", "."] + pass_filenames: false + files: ^nextflow_schema.json$ From 696160f41069054a3c9bac25a1cd63b408567f49 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 31 Jul 2023 13:54:48 +0200 Subject: [PATCH 049/112] fix: use nf-core as repo --- docs/output.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/output.md b/docs/output.md index 59b08674..f8f52414 100644 --- a/docs/output.md +++ b/docs/output.md @@ -1,4 +1,4 @@ -# PixelgenTechnologies/nf-core-pixelator: Output +# nf-core/pixelator: Output ## Introduction From 6fe628f0cda4db64d64c9d636ed53ffa007b9c95 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 31 Jul 2023 14:00:43 +0200 Subject: [PATCH 050/112] docs: fix typo --- docs/usage.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/usage.md b/docs/usage.md index 00c8df96..767d7916 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -49,7 +49,7 @@ to add extra information for downstream processing. The `panel` and `panel_file` options are mutually exclusive. If both are specified, the pipeline will throw an error. One of them has to be specified. -The pipeline will auto-detect whether a sample is single- or paired-end based on ifboth `fastq_1` and `fastq_2` or only `fastq_1` is present in the samplesheet. +The pipeline will auto-detect whether a sample is single- or paired-end based on if both `fastq_1` and `fastq_2` or only `fastq_1` is present in the samplesheet. ### Multiple runs of the same sample From 3884f641d515e907d43667de14f68949c9bd8d0d Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 31 Jul 2023 14:10:12 +0200 Subject: [PATCH 051/112] feat: update default containers --- modules/local/collect_metadata.nf | 2 +- modules/local/pixelator/single-cell/amplicon/main.nf | 3 +-- modules/local/pixelator/single-cell/analysis/main.nf | 2 +- modules/local/pixelator/single-cell/annotate/main.nf | 2 +- modules/local/pixelator/single-cell/collapse/main.nf | 3 +-- modules/local/pixelator/single-cell/demux/main.nf | 3 +-- modules/local/pixelator/single-cell/graph/main.nf | 3 +-- modules/local/pixelator/single-cell/qc/main.nf | 3 +-- modules/local/pixelator/single-cell/report/main.nf | 3 +-- modules/local/samplesheet_check.nf | 4 ++-- 10 files changed, 11 insertions(+), 17 deletions(-) diff --git a/modules/local/collect_metadata.nf b/modules/local/collect_metadata.nf index 211a4493..a6baf015 100644 --- a/modules/local/collect_metadata.nf +++ b/modules/local/collect_metadata.nf @@ -8,7 +8,7 @@ process COLLECT_METADATA { label 'process_single' cache false - conda "local::pixelator=0.10.0" + conda "local::pixelator=0.12.0" container 'ghcr.io/pixelgentechnologies/pixelator:0.12.0' input: diff --git a/modules/local/pixelator/single-cell/amplicon/main.nf b/modules/local/pixelator/single-cell/amplicon/main.nf index 4df7c3de..e18b0e40 100644 --- a/modules/local/pixelator/single-cell/amplicon/main.nf +++ b/modules/local/pixelator/single-cell/amplicon/main.nf @@ -5,8 +5,7 @@ process PIXELATOR_AMPLICON { label 'process_low' - conda "local::pixelator=0.10.0" - + conda "local::pixelator=0.12.0" container "ghcr.io/pixelgentechnologies/pixelator:0.12.0" input: diff --git a/modules/local/pixelator/single-cell/analysis/main.nf b/modules/local/pixelator/single-cell/analysis/main.nf index b37d8e8a..c14c82bd 100644 --- a/modules/local/pixelator/single-cell/analysis/main.nf +++ b/modules/local/pixelator/single-cell/analysis/main.nf @@ -3,7 +3,7 @@ process PIXELATOR_ANALYSIS { tag "$meta.id" label 'process_medium' - conda "local::pixelator=0.10.0" + conda "local::pixelator=0.12.0" container "ghcr.io/pixelgentechnologies/pixelator:0.12.0" input: diff --git a/modules/local/pixelator/single-cell/annotate/main.nf b/modules/local/pixelator/single-cell/annotate/main.nf index 9078943e..6c1eccfe 100644 --- a/modules/local/pixelator/single-cell/annotate/main.nf +++ b/modules/local/pixelator/single-cell/annotate/main.nf @@ -3,7 +3,7 @@ process PIXELATOR_ANNOTATE { tag "$meta.id" label 'process_medium' - conda "local::pixelator=0.10.0" + conda "local::pixelator=0.12.0" container "ghcr.io/pixelgentechnologies/pixelator:0.12.0" input: diff --git a/modules/local/pixelator/single-cell/collapse/main.nf b/modules/local/pixelator/single-cell/collapse/main.nf index 4364fc88..a84fce05 100644 --- a/modules/local/pixelator/single-cell/collapse/main.nf +++ b/modules/local/pixelator/single-cell/collapse/main.nf @@ -5,8 +5,7 @@ process PIXELATOR_COLLAPSE { label 'process_medium' - conda "local::pixelator=0.10.0" - + conda "local::pixelator=0.12.0" container "ghcr.io/pixelgentechnologies/pixelator:0.12.0" input: diff --git a/modules/local/pixelator/single-cell/demux/main.nf b/modules/local/pixelator/single-cell/demux/main.nf index 9cc40f54..5d68eccf 100644 --- a/modules/local/pixelator/single-cell/demux/main.nf +++ b/modules/local/pixelator/single-cell/demux/main.nf @@ -4,8 +4,7 @@ process PIXELATOR_DEMUX { tag "$meta.id" label 'process_medium' - conda "local::pixelator=0.10.0" - + conda "local::pixelator=0.12.0" container "ghcr.io/pixelgentechnologies/pixelator:0.12.0" input: diff --git a/modules/local/pixelator/single-cell/graph/main.nf b/modules/local/pixelator/single-cell/graph/main.nf index 89abd27f..c9b42f8a 100644 --- a/modules/local/pixelator/single-cell/graph/main.nf +++ b/modules/local/pixelator/single-cell/graph/main.nf @@ -4,8 +4,7 @@ process PIXELATOR_GRAPH { tag "$meta.id" label 'process_medium' - conda "local::pixelator=0.10.0" - + conda "local::pixelator=0.12.0" container "ghcr.io/pixelgentechnologies/pixelator:0.12.0" input: diff --git a/modules/local/pixelator/single-cell/qc/main.nf b/modules/local/pixelator/single-cell/qc/main.nf index 458f4323..bc5ef0df 100644 --- a/modules/local/pixelator/single-cell/qc/main.nf +++ b/modules/local/pixelator/single-cell/qc/main.nf @@ -3,9 +3,8 @@ process PIXELATOR_QC { tag "$meta.id" label 'process_medium' - conda "local::pixelator=0.10.0" - // TODO: make pixelator available on galaxyproject and quay.io support + conda "local::pixelator=0.12.0" container "ghcr.io/pixelgentechnologies/pixelator:0.12.0" input: diff --git a/modules/local/pixelator/single-cell/report/main.nf b/modules/local/pixelator/single-cell/report/main.nf index 80e7b4fb..4b7efe28 100644 --- a/modules/local/pixelator/single-cell/report/main.nf +++ b/modules/local/pixelator/single-cell/report/main.nf @@ -4,8 +4,7 @@ process PIXELATOR_REPORT { tag "$meta.id" label 'process_low' - conda "local::pixelator=0.10.0" - + conda "local::pixelator=0.12.0" container "ghcr.io/pixelgentechnologies/pixelator:0.12.0" input: diff --git a/modules/local/samplesheet_check.nf b/modules/local/samplesheet_check.nf index aaff79c1..a522d8b0 100644 --- a/modules/local/samplesheet_check.nf +++ b/modules/local/samplesheet_check.nf @@ -2,8 +2,8 @@ process SAMPLESHEET_CHECK { tag "$samplesheet" label 'process_single' - // conda "local::pixelator=0.10.0" - // container "ghcr.io/pixelgentechnologies/pixelator:0.10.0" + conda "local::pixelator=0.12.0" + container "ghcr.io/pixelgentechnologies/pixelator:0.12.0" input: path samplesheet From 5349eb25ea6e3598fc37087d6e92cd631bffa392 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 31 Jul 2023 14:10:29 +0200 Subject: [PATCH 052/112] chore: add zenodo doi TODO --- nextflow.config | 1 + 1 file changed, 1 insertion(+) diff --git a/nextflow.config b/nextflow.config index b777e97e..86d55e86 100644 --- a/nextflow.config +++ b/nextflow.config @@ -271,6 +271,7 @@ manifest { mainScript = 'main.nf' nextflowVersion = '!>=23.04.0' version = '1.0.0dev' + // TODO: Use zenodo DOI once available doi = '10.1101/2023.06.05.543770' } From 8e50ba473b23c172234e23a926fc9ef18dc0ee90 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 31 Jul 2023 16:31:59 +0200 Subject: [PATCH 053/112] style: remove empty lines --- modules/local/pixelator/single-cell/amplicon/main.nf | 2 -- modules/local/pixelator/single-cell/analysis/main.nf | 1 - modules/local/pixelator/single-cell/annotate/main.nf | 1 - modules/local/pixelator/single-cell/collapse/main.nf | 2 -- modules/local/pixelator/single-cell/demux/main.nf | 2 -- modules/local/pixelator/single-cell/graph/main.nf | 2 -- modules/local/pixelator/single-cell/qc/main.nf | 2 -- modules/local/pixelator/single-cell/report/main.nf | 2 -- 8 files changed, 14 deletions(-) diff --git a/modules/local/pixelator/single-cell/amplicon/main.nf b/modules/local/pixelator/single-cell/amplicon/main.nf index e18b0e40..70dabffb 100644 --- a/modules/local/pixelator/single-cell/amplicon/main.nf +++ b/modules/local/pixelator/single-cell/amplicon/main.nf @@ -1,5 +1,3 @@ - - process PIXELATOR_AMPLICON { tag "$meta.id" label 'process_low' diff --git a/modules/local/pixelator/single-cell/analysis/main.nf b/modules/local/pixelator/single-cell/analysis/main.nf index c14c82bd..20bd28b5 100644 --- a/modules/local/pixelator/single-cell/analysis/main.nf +++ b/modules/local/pixelator/single-cell/analysis/main.nf @@ -1,4 +1,3 @@ - process PIXELATOR_ANALYSIS { tag "$meta.id" label 'process_medium' diff --git a/modules/local/pixelator/single-cell/annotate/main.nf b/modules/local/pixelator/single-cell/annotate/main.nf index 6c1eccfe..3f12fd66 100644 --- a/modules/local/pixelator/single-cell/annotate/main.nf +++ b/modules/local/pixelator/single-cell/annotate/main.nf @@ -1,4 +1,3 @@ - process PIXELATOR_ANNOTATE { tag "$meta.id" label 'process_medium' diff --git a/modules/local/pixelator/single-cell/collapse/main.nf b/modules/local/pixelator/single-cell/collapse/main.nf index a84fce05..fd5eb1df 100644 --- a/modules/local/pixelator/single-cell/collapse/main.nf +++ b/modules/local/pixelator/single-cell/collapse/main.nf @@ -1,5 +1,3 @@ - - process PIXELATOR_COLLAPSE { tag "$meta.id" label 'process_medium' diff --git a/modules/local/pixelator/single-cell/demux/main.nf b/modules/local/pixelator/single-cell/demux/main.nf index 5d68eccf..ea473444 100644 --- a/modules/local/pixelator/single-cell/demux/main.nf +++ b/modules/local/pixelator/single-cell/demux/main.nf @@ -1,5 +1,3 @@ - - process PIXELATOR_DEMUX { tag "$meta.id" label 'process_medium' diff --git a/modules/local/pixelator/single-cell/graph/main.nf b/modules/local/pixelator/single-cell/graph/main.nf index c9b42f8a..8ecac585 100644 --- a/modules/local/pixelator/single-cell/graph/main.nf +++ b/modules/local/pixelator/single-cell/graph/main.nf @@ -1,5 +1,3 @@ - - process PIXELATOR_GRAPH { tag "$meta.id" label 'process_medium' diff --git a/modules/local/pixelator/single-cell/qc/main.nf b/modules/local/pixelator/single-cell/qc/main.nf index bc5ef0df..ba84e364 100644 --- a/modules/local/pixelator/single-cell/qc/main.nf +++ b/modules/local/pixelator/single-cell/qc/main.nf @@ -1,5 +1,3 @@ - - process PIXELATOR_QC { tag "$meta.id" label 'process_medium' diff --git a/modules/local/pixelator/single-cell/report/main.nf b/modules/local/pixelator/single-cell/report/main.nf index 4b7efe28..a8a3ea82 100644 --- a/modules/local/pixelator/single-cell/report/main.nf +++ b/modules/local/pixelator/single-cell/report/main.nf @@ -1,5 +1,3 @@ - - process PIXELATOR_REPORT { tag "$meta.id" label 'process_low' From ea2b5087a3a769344a54d05cab32819b1cd22c8f Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 31 Jul 2023 16:34:21 +0200 Subject: [PATCH 054/112] feat: improve channel handling for report --- subworkflows/local/generate_reports.nf | 108 +++++++++++-------------- workflows/pixelator.nf | 69 ++++++++++------ 2 files changed, 91 insertions(+), 86 deletions(-) diff --git a/subworkflows/local/generate_reports.nf b/subworkflows/local/generate_reports.nf index dbe475b8..6a5a7469 100644 --- a/subworkflows/local/generate_reports.nf +++ b/subworkflows/local/generate_reports.nf @@ -4,14 +4,14 @@ include { PIXELATOR_REPORT } from '../../modules/local/pixelator/sing workflow GENERATE_REPORTS { take: panel_files // [meta, panel_file] or [meta, []] - amplicon_data - preqc_data - adapterqc_data - demux_data - collapse_data - graph_data - annotate_data - analysis_data + amplicon_data // [meta, [.report.json, .meta.json]] + preqc_data // [meta, [.report.json, .meta.json]] + adapterqc_data // [meta, [.report.json, .meta.json]] + demux_data // [meta, [.report.json, .meta.json]] + collapse_data // [meta, [.report.json, .meta.json]] + graph_data // [meta, [list of files]] + annotate_data // [meta, [list of files]] + analysis_data // [meta, [list of files]] main: ch_versions = Channel.empty() @@ -30,62 +30,41 @@ workflow GENERATE_REPORTS { return [id, data] } - // Make sure panel files are unique, we can have duplicates if we concatenated multiple samples - ch_panel_files_col = panel_files + ch_panel_col = panel_files .map { meta, data -> [ meta.id, data] } - .groupTuple() - .map { id, data -> - if (!data) { - return [id, []] - } - def unique_panels = data.unique() - if (unique_panels.size() > 1) { - exit 1, "ERROR: Concatenated samples must use the same panel." - } - return [ id, unique_panels[0] ] - } - - - ch_amplicon_col = amplicon_data - .map { meta, data -> [ meta.id, data] } - .groupTuple() - ch_preqc_col = preqc_data - .map { meta, data -> [ meta.id, data] } - .groupTuple() - - ch_adapterqc_col = adapterqc_data - .map { meta, data -> [ meta.id, data] } - .groupTuple() + // These first subcommands each return two files per sample used by the reporting + // A json file with stats and a command invocation metadata json file + + ch_amplicon_col = amplicon_data.map { meta, data -> [ meta.id, data] } + ch_preqc_col = preqc_data.map { meta, data -> [ meta.id, data] } + ch_adapterqc_col = adapterqc_data.map { meta, data -> [ meta.id, data] } + ch_demux_col = demux_data.map { meta, data -> [ meta.id, data] } + ch_collapse_col = collapse_data.map { meta, data -> [ meta.id, data] } + ch_graph_col = graph_data.map { meta, data -> [meta.id, data] } + ch_annotate_col = annotate_data.map { meta, data -> [meta.id, data] } + ch_analysis_col = analysis_data.map { meta, data -> [meta.id, data] } + + // + // Combine all inputs and group them, then split them up again. This makes sure the per subcommand outputs have the sample order + // + // ch_report_data: [ + // [ + // meta, panel_files, + // [amplicon files...], + // [preqc files...], + // [adapterqc files...], + // [demux files...], + // [collapse files...], + // [cluster files], + // [annotate files...], + // [analysis files...] + // ], + // [ same structure repeated for each sample ] + // ] - ch_demux_col = demux_data - .map { meta, data -> [ meta.id, data] } - .groupTuple() - - ch_collapse_col = collapse_data - .map { meta, data -> [ meta.id, data] } - .groupTuple() - - ch_graph_col = graph_data - .map { meta, data -> [meta.id, data] } - .groupTuple() - - ch_annotate_col = annotate_data - .map { meta, data -> [meta.id, data] } - .groupTuple() - - ch_analysis_col = analysis_data - .map { meta, data -> [meta.id, data] } - .groupTuple() - - // Combine all inputs and group them to make per-stage channels have their output in the same order - // ch_report_data: [[ - // meta, panels_file, - // [concatenate files...], [preqc files...], [adapterqc files...], [demux files...], - // [collapse files...], [cluster files], [annotate files...], [analysis files...] - // ], ...] ch_report_data = ch_meta_col - .concat ( ch_panel_files_col ) + .concat ( ch_panel_col ) .concat ( ch_amplicon_col ) .concat ( ch_preqc_col ) .concat ( ch_adapterqc_col ) @@ -94,10 +73,14 @@ workflow GENERATE_REPORTS { .concat ( ch_graph_col ) .concat ( ch_annotate_col ) .concat ( ch_analysis_col ) - .groupTuple() + .groupTuple (size: 10) // Split up everything per stage so we can recreate the expected directory structure for - // pixelator single-cell report using stageAs + // `pixelator single-cell report` using stageAs for each stage + // + // These ch__grouped channels all emit a list of input files for each sample in the samplesheet + // The channels will emit values in the same order so eg. the first list of files from each ch__grouped + // channel will match the same sample from the samplesheet. ch_panel_files_grouped = ch_report_data.map { id, data -> [ data[0], data[1] ] } ch_amplicon_grouped = ch_report_data.map { id, data -> data[2] ? data[2].flatten() : [] } @@ -109,6 +92,7 @@ workflow GENERATE_REPORTS { ch_annotate_grouped = ch_report_data.map { id, data -> data[8] ? data[8].flatten() : [] } ch_analysis_grouped = ch_report_data.map { id, data -> data[9] ? data[9].flatten() : [] } + // If no `panel_file` is given we need to pass in `panel` from the samplesheet instead ch_panel_keys = ch_panel_files_grouped .map { meta, panel_file -> panel_file ? [] : meta.panel } diff --git a/workflows/pixelator.nf b/workflows/pixelator.nf index 2ea1e347..7a5b44e6 100644 --- a/workflows/pixelator.nf +++ b/workflows/pixelator.nf @@ -94,11 +94,6 @@ workflow PIXELATOR { ch_panel_files = INPUT_CHECK.out.panels ch_fastq_split = ch_reads - .map { - meta, fastq -> - new_id = meta.id - ~/_T\d+/ - [ meta + [id: new_id], fastq ] - } .groupTuple() .branch { meta, fastq -> @@ -115,8 +110,27 @@ workflow PIXELATOR { .reads .mix(ch_fastq_split.single) - ch_versions = ch_versions.mix(CAT_FASTQ.out.versions.first().ifEmpty(null)) + // Check that multi lane samples use the same panel file + ch_checked_panel_files = ch_panel_files + .map { meta, data -> [ meta.id, data] } + .groupTuple() + .map { id, data -> + if (!data) { + return [id, []] + } + def unique_panels = data.unique() + if (unique_panels.size() > 1) { + exit 1, "ERROR: Concatenated samples must use the same panel." + } + return [ id, unique_panels[0] ] + } + + ch_cat_panel_files = ch_cat_fastq + .map { meta, _ -> [meta.id, meta] } + .join(ch_checked_panel_files, failOnMismatch:true, failOnDuplicate:true) + .map { id, meta, panel_files -> [meta, panel_files] } + ch_versions = ch_versions.mix(CAT_FASTQ.out.versions.first().ifEmpty(null)) // We need to rename to make all reads match the sample name, // since pixelator extracts sample_names from read names @@ -124,11 +138,6 @@ workflow PIXELATOR { ch_renamed_reads = RENAME_READS.out.reads ch_versions = ch_versions.mix(RENAME_READS.out.versions.first()) - ch_renamed_branched = ch_renamed_reads.branch { - single_end: it[0].single_end - paired_end: true - } - PIXELATOR_AMPLICON ( ch_renamed_reads ) ch_merged = PIXELATOR_AMPLICON.out.merged ch_versions = ch_versions.mix(PIXELATOR_AMPLICON.out.versions.first()) @@ -139,7 +148,7 @@ workflow PIXELATOR { ch_qc = PIXELATOR_QC.out.processed ch_versions = ch_versions.mix(PIXELATOR_QC.out.versions.first()) - ch_qc_and_panel_file = ch_qc.join(ch_panel_files) + ch_qc_and_panel_file = ch_qc.join(ch_cat_panel_files, failOnMismatch:true, failOnDuplicate:true) ch_demux_panel_keys = ch_qc_and_panel_file .map { meta, fq, panel_file -> panel_file ? [] : meta.panel } @@ -147,7 +156,7 @@ workflow PIXELATOR { ch_demuxed = PIXELATOR_DEMUX.out.processed ch_versions = ch_versions.mix(PIXELATOR_DEMUX.out.versions.first()) - ch_demuxed_and_panel_file = ch_demuxed.join(ch_panel_files) + ch_demuxed_and_panel_file = ch_demuxed.join(ch_cat_panel_files, failOnMismatch:true, failOnDuplicate:true) ch_collapse_panel_keys = ch_demuxed_and_panel_file.map { meta, fq, panel_file -> panel_file ? [] : meta.panel } @@ -159,7 +168,7 @@ workflow PIXELATOR { ch_clustered = PIXELATOR_GRAPH.out.edgelist ch_versions = ch_versions.mix(PIXELATOR_GRAPH.out.versions.first()) - ch_clustered_and_panel = ch_clustered.join(ch_panel_files) + ch_clustered_and_panel = ch_clustered.join(ch_cat_panel_files, failOnMismatch:true, failOnDuplicate:true) ch_annotate_panel_keys = ch_demuxed_and_panel_file .map { meta, fq, panel_file -> panel_file ? [] : meta.panel } @@ -172,22 +181,34 @@ workflow PIXELATOR { ch_versions = ch_versions.mix(PIXELATOR_ANALYSIS.out.versions.first()) - // Do some transformations to split the inputs into their stages - // and have all these "pixelator subcommand output" channels output in the same order - // Note that we need to split inout per subcommand to stage those files in the right subdirs - // as expected by pixelator single-cell report + // Prepare all data needed by reporting for each pixelator step + + ch_amplicon_data = PIXELATOR_AMPLICON.out.report_json + .concat(PIXELATOR_AMPLICON.out.metadata) + .groupTuple(size: 2) + + ch_preqc_data = PIXELATOR_QC.out.preqc_report_json + .concat(PIXELATOR_QC.out.preqc_metadata) + .groupTuple(size: 2) + + ch_adapterqc_data = PIXELATOR_QC.out.adapterqc_report_json + .concat(PIXELATOR_QC.out.adapterqc_metadata) + .groupTuple(size: 2) + + ch_demux_data = PIXELATOR_DEMUX.out.report_json + .concat(PIXELATOR_DEMUX.out.metadata) + .groupTuple(size: 2) + + ch_collapse_data = PIXELATOR_COLLAPSE.out.report_json + .concat(PIXELATOR_COLLAPSE.out.metadata) + .groupTuple(size: 2) - ch_amplicon_data = PIXELATOR_AMPLICON.out.report_json.mix(PIXELATOR_AMPLICON.out.metadata) - ch_preqc_data = PIXELATOR_QC.out.preqc_report_json.mix(PIXELATOR_QC.out.preqc_metadata) - ch_adapterqc_data = PIXELATOR_QC.out.adapterqc_report_json.mix(PIXELATOR_QC.out.adapterqc_metadata) - ch_demux_data = PIXELATOR_DEMUX.out.report_json.mix(PIXELATOR_DEMUX.out.metadata) - ch_collapse_data = PIXELATOR_COLLAPSE.out.report_json.mix(PIXELATOR_COLLAPSE.out.metadata) ch_cluster_data = PIXELATOR_GRAPH.out.all_results ch_annotate_data = PIXELATOR_ANNOTATE.out.all_results ch_analysis_data = PIXELATOR_ANALYSIS.out.all_results GENERATE_REPORTS( - ch_panel_files, + ch_cat_panel_files, ch_amplicon_data, ch_preqc_data, ch_adapterqc_data, From b9f35977519c1064b8cc754534ba4a71d2672214 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 31 Jul 2023 16:45:22 +0200 Subject: [PATCH 055/112] style: remove commented block --- nextflow.config | 4 ---- 1 file changed, 4 deletions(-) diff --git a/nextflow.config b/nextflow.config index 86d55e86..4ae89930 100644 --- a/nextflow.config +++ b/nextflow.config @@ -6,10 +6,6 @@ ---------------------------------------------------------------------------------------- */ -// TODO: Use nf-validation -// plugins { -// id 'nf-validation@0.2.1' -// } // Global default params, used in configs params { From e296f6387d5571c48778ad3b0e3670a7277062a5 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 31 Jul 2023 17:04:30 +0200 Subject: [PATCH 056/112] fix: use proper code of conduct for 2.9 --- CODE_OF_CONDUCT.md | 133 +++++++++++---------------------------------- 1 file changed, 31 insertions(+), 102 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index c089ec78..f4fd052f 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,20 +1,18 @@ -# Code of Conduct at nf-core (v1.4) +# Code of Conduct at nf-core (v1.0) ## Our Pledge -In the interest of fostering an open, collaborative, and welcoming environment, we as contributors and maintainers of nf-core pledge to making participation in our projects and community a harassment-free experience for everyone, regardless of: +In the interest of fostering an open, collaborative, and welcoming environment, we as contributors and maintainers of nf-core, pledge to making participation in our projects and community a harassment-free experience for everyone, regardless of: - Age -- Ability - Body size -- Caste - Familial status - Gender identity and expression - Geographical location - Level of experience - Nationality and national origins - Native language -- Neurodiversity +- Physical and neurological ability - Race or ethnicity - Religion - Sexual identity and orientation @@ -24,133 +22,80 @@ Please note that the list above is alphabetised and is therefore not ranked in a ## Preamble -:::note -This Code of Conduct (CoC) has been drafted by Renuka Kudva, Cris Tuñí, and Michael Heuer, with input from the nf-core Core Team and Susanna Marquez from the nf-core community. "We", in this document, refers to the Safety Officers and members of the nf-core Core Team, both of whom are deemed to be members of the nf-core community and are therefore required to abide by this Code of Conduct. This document will be amended periodically to keep it up-to-date. In case of any dispute, the most current version will apply. -::: +> Note: This Code of Conduct (CoC) has been drafted by the nf-core Safety Officer and been edited after input from members of the nf-core team and others. "We", in this document, refers to the Safety Officer and members of the nf-core core team, both of whom are deemed to be members of the nf-core community and are therefore required to abide by this Code of Conduct. This document will amended periodically to keep it up-to-date, and in case of any dispute, the most current version will apply. -An up-to-date list of members of the nf-core core team can be found [here](https://nf-co.re/about). - -Our Safety Officers are Saba Nafees, Cris Tuñí, and Michael Heuer. +An up-to-date list of members of the nf-core core team can be found [here](https://nf-co.re/about). Our current safety officer is Renuka Kudva. nf-core is a young and growing community that welcomes contributions from anyone with a shared vision for [Open Science Policies](https://www.fosteropenscience.eu/taxonomy/term/8). Open science policies encompass inclusive behaviours and we strive to build and maintain a safe and inclusive environment for all individuals. -We have therefore adopted this CoC, which we require all members of our community and attendees of nf-core events to adhere to in all our workspaces at all times. Workspaces include, but are not limited to, Slack, meetings on Zoom, gather.town, YouTube live etc. +We have therefore adopted this code of conduct (CoC), which we require all members of our community and attendees in nf-core events to adhere to in all our workspaces at all times. Workspaces include but are not limited to Slack, meetings on Zoom, Jitsi, YouTube live etc. -Our CoC will be strictly enforced and the nf-core team reserves the right to exclude participants who do not comply with our guidelines from our workspaces and future nf-core activities. +Our CoC will be strictly enforced and the nf-core team reserve the right to exclude participants who do not comply with our guidelines from our workspaces and future nf-core activities. -We ask all members of our community to help maintain supportive and productive workspaces and to avoid behaviours that can make individuals feel unsafe or unwelcome. Please help us maintain and uphold this CoC. +We ask all members of our community to help maintain a supportive and productive workspace and to avoid behaviours that can make individuals feel unsafe or unwelcome. Please help us maintain and uphold this CoC. -Questions, concerns, or ideas on what we can include? Contact members of the Safety Team on Slack or email safety [at] nf-co [dot] re. +Questions, concerns or ideas on what we can include? Contact safety [at] nf-co [dot] re ## Our Responsibilities -Members of the Safety Team (the Safety Officers) are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behaviour. +The safety officer is responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behaviour. -The Safety Team, in consultation with the nf-core core team, have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this CoC, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. +The safety officer in consultation with the nf-core core team have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. -Members of the core team or the Safety Team who violate the CoC will be required to recuse themselves pending investigation. They will not have access to any reports of the violations and will be subject to the same actions as others in violation of the CoC. +Members of the core team or the safety officer who violate the CoC will be required to recuse themselves pending investigation. They will not have access to any reports of the violations and be subject to the same actions as others in violation of the CoC. -## When and where does this Code of Conduct apply? +## When are where does this Code of Conduct apply? -Participation in the nf-core community is contingent on following these guidelines in all our workspaces and events, such as hackathons, workshops, bytesize, and collaborative workspaces on gather.town. These guidelines include, but are not limited to, the following (listed alphabetically and therefore in no order of preference): +Participation in the nf-core community is contingent on following these guidelines in all our workspaces and events. This includes but is not limited to the following listed alphabetically and therefore in no order of preference: - Communicating with an official project email address. - Communicating with community members within the nf-core Slack channel. - Participating in hackathons organised by nf-core (both online and in-person events). -- Participating in collaborative work on GitHub, Google Suite, community calls, mentorship meetings, email correspondence, and on the nf-core gather.town workspace. -- Participating in workshops, training, and seminar series organised by nf-core (both online and in-person events). This applies to events hosted on web-based platforms such as Zoom, gather.town, Jitsi, YouTube live etc. +- Participating in collaborative work on GitHub, Google Suite, community calls, mentorship meetings, email correspondence. +- Participating in workshops, training, and seminar series organised by nf-core (both online and in-person events). This applies to events hosted on web-based platforms such as Zoom, Jitsi, YouTube live etc. - Representing nf-core on social media. This includes both official and personal accounts. ## nf-core cares 😊 -nf-core's CoC and expectations of respectful behaviours for all participants (including organisers and the nf-core team) include, but are not limited to, the following (listed in alphabetical order): +nf-core's CoC and expectations of respectful behaviours for all participants (including organisers and the nf-core team) include but are not limited to the following (listed in alphabetical order): - Ask for consent before sharing another community member’s personal information (including photographs) on social media. - Be respectful of differing viewpoints and experiences. We are all here to learn from one another and a difference in opinion can present a good learning opportunity. -- Celebrate your accomplishments! (Get creative with your use of emojis 🎉 🥳 💯 🙌 !) +- Celebrate your accomplishments at events! (Get creative with your use of emojis 🎉 🥳 💯 🙌 !) - Demonstrate empathy towards other community members. (We don’t all have the same amount of time to dedicate to nf-core. If tasks are pending, don’t hesitate to gently remind members of your team. If you are leading a task, ask for help if you feel overwhelmed.) - Engage with and enquire after others. (This is especially important given the geographically remote nature of the nf-core community, so let’s do this the best we can) - Focus on what is best for the team and the community. (When in doubt, ask) -- Accept feedback, yet be unafraid to question, deliberate, and learn. +- Graciously accept constructive criticism, yet be unafraid to question, deliberate, and learn. - Introduce yourself to members of the community. (We’ve all been outsiders and we know that talking to strangers can be hard for some, but remember we’re interested in getting to know you and your visions for open science!) -- Show appreciation and **provide clear feedback**. (This is especially important because we don’t see each other in person and it can be harder to interpret subtleties. Also remember that not everyone understands a certain language to the same extent as you do, so **be clear in your communication to be kind.**) +- Show appreciation and **provide clear feedback**. (This is especially important because we don’t see each other in person and it can be harder to interpret subtleties. Also remember that not everyone understands a certain language to the same extent as you do, so **be clear in your communications to be kind.**) - Take breaks when you feel like you need them. -- Use welcoming and inclusive language. (Participants are encouraged to display their chosen pronouns on Zoom or in communication on Slack) +- Using welcoming and inclusive language. (Participants are encouraged to display their chosen pronouns on Zoom or in communication on Slack.) ## nf-core frowns on 😕 -The following behaviours from any participants within the nf-core community (including the organisers) will be considered unacceptable under this CoC. Engaging or advocating for any of the following could result in expulsion from nf-core workspaces: +The following behaviours from any participants within the nf-core community (including the organisers) will be considered unacceptable under this code of conduct. Engaging or advocating for any of the following could result in expulsion from nf-core workspaces. - Deliberate intimidation, stalking or following and sustained disruption of communication among participants of the community. This includes hijacking shared screens through actions such as using the annotate tool in conferencing software such as Zoom. - “Doxing” i.e. posting (or threatening to post) another person’s personal identifying information online. - Spamming or trolling of individuals on social media. -- Use of sexual or discriminatory imagery, comments, jokes, or unwelcome sexual attention. -- Verbal and text comments that reinforce social structures of domination related to gender, gender identity and expression, sexual orientation, ability, physical appearance, body size, race, age, religion, or work experience. +- Use of sexual or discriminatory imagery, comments, or jokes and unwelcome sexual attention. +- Verbal and text comments that reinforce social structures of domination related to gender, gender identity and expression, sexual orientation, ability, physical appearance, body size, race, age, religion or work experience. ### Online Trolling -The majority of nf-core interactions and events are held online. Unfortunately, holding events online comes with the risk of online trolling. This is unacceptable — reports of such behaviour will be taken very seriously and perpetrators will be excluded from activities immediately. +The majority of nf-core interactions and events are held online. Unfortunately, holding events online comes with the added issue of online trolling. This is unacceptable, reports of such behaviour will be taken very seriously, and perpetrators will be excluded from activities immediately. -All community members are **required** to ask members of the group they are working with for explicit consent prior to taking screenshots of individuals during video calls. +All community members are required to ask members of the group they are working within for explicit consent prior to taking screenshots of individuals during video calls. -## Procedures for reporting CoC violations +## Procedures for Reporting CoC violations If someone makes you feel uncomfortable through their behaviours or actions, report it as soon as possible. -You can reach out to members of the Safety Team (Saba Nafees, Cris Tuñí, and Michael Heuer) on Slack. Alternatively, contact a member of the nf-core core team [nf-core core team](https://nf-co.re/about), and they will forward your concerns to the Safety Team. - -Issues directly concerning members of the Core Team or the Safety Team will be dealt with by other members of the core team and the safety manager — possible conflicts of interest will be taken into account. nf-core is also in discussions about having an ombudsperson and details will be shared in due course. - -All reports will be handled with the utmost discretion and confidentiality. - -You can also report any CoC violations to safety [at] nf-co [dot] re. In your email report, please do your best to include: - -- Your contact information. -- Identifying information (e.g. names, nicknames, pseudonyms) of the participant who has violated the Code of Conduct. -- The behaviour that was in violation and the circumstances surrounding the incident. -- The approximate time of the behaviour (if different than the time the report was made). -- Other people involved in the incident, if applicable. -- If you believe the incident is ongoing. -- If there is a publicly available record (e.g. mailing list record, a screenshot). -- Any additional information. - -After you file a report, one or more members of our Safety Team will contact you to follow up on your report. - -## Who will read and handle reports - -All reports will be read and handled by the members of the Safety Team at nf-core. - -If members of the Safety Team are deemed to have a conflict of interest with a report, they will be required to recuse themselves as per our Code of Conduct and will not have access to any follow-ups. - -To keep this first report confidential from any of the Safety Team members, please submit your first report by direct messaging on Slack/direct email to any of the nf-core members you are comfortable disclosing the information to, and be explicit about which member(s) you do not consent to sharing the information with. - -## Reviewing reports - -After receiving the report, members of the Safety Team will review the incident report to determine whether immediate action is required, for example, whether there is immediate threat to participants’ safety. - -The Safety Team, in consultation with members of the nf-core core team, will assess the information to determine whether the report constitutes a Code of Conduct violation, for them to decide on a course of action. - -In the case of insufficient information, one or more members of the Safety Team may contact the reporter, the reportee, or any other attendees to obtain more information. +You can reach out to members of the [nf-core core team](https://nf-co.re/about) and they will forward your concerns to the safety officer(s). -Once additional information is gathered, the Safety Team will collectively review and decide on the best course of action to take, if any. The Safety Team reserves the right to not act on a report. +Issues directly concerning members of the core team will be dealt with by other members of the core team and the safety manager, and possible conflicts of interest will be taken into account. nf-core is also in discussions about having an ombudsperson, and details will be shared in due course. -## Confidentiality - -All reports, and any additional information included, are only shared with the team of safety officers (and possibly members of the core team, in case the safety officer is in violation of the CoC). We will respect confidentiality requests for the purpose of protecting victims of abuse. - -We will not name harassment victims, beyond discussions between the safety officer and members of the nf-core team, without the explicit consent of the individuals involved. - -## Enforcement - -Actions taken by the nf-core’s Safety Team may include, but are not limited to: - -- Asking anyone to stop a behaviour. -- Asking anyone to leave the event and online spaces either temporarily, for the remainder of the event, or permanently. -- Removing access to the gather.town and Slack, either temporarily or permanently. -- Communicating to all participants to reinforce our expectations for conduct and remind what is unacceptable behaviour; this may be public for practical reasons. -- Communicating to all participants that an incident has taken place and how we will act or have acted — this may be for the purpose of letting event participants know we are aware of and dealing with the incident. -- Banning anyone from participating in nf-core-managed spaces, future events, and activities, either temporarily or permanently. -- No action. +All reports will be handled with utmost discretion and confidentially. ## Attribution and Acknowledgements @@ -161,22 +106,6 @@ Actions taken by the nf-core’s Safety Team may include, but are not limited to ## Changelog -### v1.4 - February 8th, 2022 - -- Included a new member of the Safety Team. Corrected a typographical error in the text. - -### v1.3 - December 10th, 2021 - -- Added a statement that the CoC applies to nf-core gather.town workspaces. Corrected typographical errors in the text. - -### v1.2 - November 12th, 2021 - -- Removed information specific to reporting CoC violations at the Hackathon in October 2021. - -### v1.1 - October 14th, 2021 - -- Updated with names of new Safety Officers and specific information for the hackathon in October 2021. - -### v1.0 - March 15th, 2021 +### v1.0 - March 12th, 2021 - Complete rewrite from original [Contributor Covenant](http://contributor-covenant.org/) CoC. From bfc4cf613e577effc851a262157ac0fc43d33367 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 3 Aug 2023 15:42:05 +0200 Subject: [PATCH 057/112] feat: use latest dev container --- nextflow.config | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nextflow.config b/nextflow.config index 4ae89930..a340a73c 100644 --- a/nextflow.config +++ b/nextflow.config @@ -66,8 +66,8 @@ params { skip_report = false skip_analysis = false - // Pipeline specific global config options - pixelator_container = "ghcr.io/pixelgentechnologies/pixelator:0.12.0" + // Main pixelator container override + pixelator_container = "ghcr.io/pixelgentechnologies/pixelator:dev" // Boilerplate options outdir = "./results" @@ -177,6 +177,7 @@ profiles { shifter.enabled = false charliecloud.enabled = false apptainer.enabled = false + podman.runOptions = '--userns=keep-id' } shifter { shifter.enabled = true From c4de1a63178a179ce422dbbc141d77738a1a6d92 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 3 Aug 2023 15:44:40 +0200 Subject: [PATCH 058/112] fix: make singularity_pull_docker_container process scope option --- conf/modules.config | 1 - modules/local/rename_reads.nf | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 51f36b9b..8bb89ce8 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -19,7 +19,6 @@ process { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] - withName: 'SAMPLESHEET_CHECK' { if (params.pixelator_container) { container = params.pixelator_container diff --git a/modules/local/rename_reads.nf b/modules/local/rename_reads.nf index a888c3ff..658baa4f 100644 --- a/modules/local/rename_reads.nf +++ b/modules/local/rename_reads.nf @@ -3,7 +3,7 @@ process RENAME_READS { label "process_single" conda "conda-forge::sed=4.7" - if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { + if (workflow.containerEngine == 'singularity' && !tast.ext.singularity_pull_docker_container) { container "https://depot.galaxyproject.org/singularity/ubuntu:20.04" } else { container "registry.hub.docker.com/biocontainers/biocontainers:v1.2.0_cv2" From 56e507bedf79fb05e8d1ad1e94baaf818a769197 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 3 Aug 2023 15:52:24 +0200 Subject: [PATCH 059/112] ci: authenticate with ghcr.io --- .github/workflows/ci.yml | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9db8edf1..b8795484 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,6 +35,14 @@ jobs: with: version: "${{ matrix.NXF_VER }}" + # TODO: Remove once image is public + - name: Connect to Github Container Registry + uses: docker/login-action@v2.2.0 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + - name: Run pipeline with test data # TODO nf-core: You can customise CI pipeline run tests as required # For example: adding multiple test runs with different parameters From e9cbb60d3883c70427f6be002c0507c57b85ac98 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 9 Aug 2023 13:23:37 +0200 Subject: [PATCH 060/112] chore: remove create-params-template.py This functionality is now merged in nf-core/tools (https://github.com/nf-core/tools/pull/2362) so we can remove this. --- utils/create-params-template.py | 89 --------------------------------- 1 file changed, 89 deletions(-) delete mode 100755 utils/create-params-template.py diff --git a/utils/create-params-template.py b/utils/create-params-template.py deleted file mode 100755 index eceecebc..00000000 --- a/utils/create-params-template.py +++ /dev/null @@ -1,89 +0,0 @@ -#!/usr/bin/env python3 - -import argparse -import json -from pathlib import Path -from typing import Any, Dict -import textwrap - - -DEFAULT_SCHEMA_PATH = Path(__file__).parents[1] / "nextflow_schema.json" - - -GROUPS = { - "preqc_options", - "adapterqc_options", - "demux_options", - "collapse_options", - "graph_options", - "annotate_options", - "analysis_options", - "report_options", -} - - -def print_intro(): - print( - """ -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## nf-core/pixelator parameter file -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## This is an example params-file.yaml for the `-params-file` option of -## nf-core/pixelator. -## Uncomment lines with a single '#' if you want to pass the parameter. -## ---------------------------------------------------------------------------------------- -""" - ) - - -def render_params_file(schema: Dict[str, Any]): - definitions = schema["definitions"] - for definition_key, definition in definitions.items(): - if definition_key not in GROUPS: - continue - - comment = definition.get("description", definition_key) - properties = definition["properties"] - - print( - f""" -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## {comment} -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -""" - ) - - for prop_key, prop in properties.items(): - default_value = prop.get("default", None) - - if isinstance(default_value, bool): - default_value = str(default_value).lower() - - if default_value is None: - default_value = "null" - - description_lines = textwrap.wrap(f"## {prop.get('description', '')}") - - print("## ------------------------------------------------------------------------------------------") - print("\n## ".join(description_lines)) - print("## ------------------------------------------------------------------------------------------") - print(f"""# {prop_key}: {default_value}\n""") - - return - - -def main(args): - schema_file = args.schema - with schema_file.open("r") as f: - schema = json.load(f) - - print_intro() - render_params_file(schema) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--schema", type=Path, default=DEFAULT_SCHEMA_PATH) - - args = parser.parse_args() - main(args) From bbdc3c64e24efffc1ddfd7fe015b5613c3e4a241 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 9 Aug 2023 13:33:58 +0200 Subject: [PATCH 061/112] fix: remove redundant params.input check --- lib/WorkflowPixelator.groovy | 10 ---------- workflows/pixelator.nf | 2 -- 2 files changed, 12 deletions(-) diff --git a/lib/WorkflowPixelator.groovy b/lib/WorkflowPixelator.groovy index 6056cca8..23218310 100755 --- a/lib/WorkflowPixelator.groovy +++ b/lib/WorkflowPixelator.groovy @@ -7,16 +7,6 @@ import groovy.text.SimpleTemplateEngine class WorkflowPixelator { - // - // Check and validate parameters - // - public static void initialise(params, log) { - // Check mandatory parameters - if (!params.input) { - Nextflow.error("Input samplesheet not specified!") - } - } - // // Get workflow summary for MultiQC // diff --git a/workflows/pixelator.nf b/workflows/pixelator.nf index 7a5b44e6..b8a287d8 100644 --- a/workflows/pixelator.nf +++ b/workflows/pixelator.nf @@ -14,8 +14,6 @@ def summary_params = paramsSummaryMap(workflow) // Print parameter summary log to screen log.info logo + paramsSummaryLog(workflow) + citation -WorkflowPixelator.initialise(params, log) - // Inject the samplesheet SHA-1 into the params object ch_input = file(params.input) params.samplesheet_sha = ch_input.bytes.digest('sha-1') From 7611bacd905434f404f10b73eca1276fca24bc63 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 9 Aug 2023 13:58:39 +0200 Subject: [PATCH 062/112] fix: sync with renamed parameter in pixelator --- assets/nf-params.yml | 404 +++++++++++++++++++++++++++++++++++++++++ assets/params-file.yml | 246 ------------------------- conf/modules.config | 2 +- nextflow.config | 2 +- nextflow_schema.json | 4 +- 5 files changed, 408 insertions(+), 250 deletions(-) create mode 100644 assets/nf-params.yml delete mode 100644 assets/params-file.yml diff --git a/assets/nf-params.yml b/assets/nf-params.yml new file mode 100644 index 00000000..6013c427 --- /dev/null +++ b/assets/nf-params.yml @@ -0,0 +1,404 @@ +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## nf-core/pixelator 1.0.0dev +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## This is an example parameter file to pass to the `-params-file` option +## of nextflow run with the nf-core/pixelator pipeline. +## +## Uncomment lines with a single '#' if you want to pass the parameter to +## the pipeline. +## ----------------------------------------------------------------------------- + +## ============================================================================= +## Input/output options +## ============================================================================= +## Define where the pipeline should find input data and save output data. + +## ----------------------------------------------------------------------------- +## input +## ----------------------------------------------------------------------------- +## Path to comma-separated file containing information about the samples +## in the experiment. +## Type: string +## ----------------------------------------------------------------------------- +# input = null + +## ----------------------------------------------------------------------------- +## outdir +## ----------------------------------------------------------------------------- +## The output directory where the results will be saved. You have to use +## absolute paths to storage on Cloud infrastructure. +## Type: string +## ----------------------------------------------------------------------------- +# outdir = "./results" + +## ----------------------------------------------------------------------------- +## email +## ----------------------------------------------------------------------------- +## Email address for completion summary. +## Type: string +## ----------------------------------------------------------------------------- +# email = null + + +## ============================================================================= +## QC/Filtering/Trimming options +## ============================================================================= + +## ----------------------------------------------------------------------------- +## trim_front +## ----------------------------------------------------------------------------- +## Trim N bases from the front of the reads +## Type: integer +## ----------------------------------------------------------------------------- +# trim_front = 0 + +## ----------------------------------------------------------------------------- +## trim_tail +## ----------------------------------------------------------------------------- +## Trim N bases from the tail of the reads +## Type: integer +## ----------------------------------------------------------------------------- +# trim_tail = 0 + +## ----------------------------------------------------------------------------- +## max_length +## ----------------------------------------------------------------------------- +## The maximum length of a read +## Type: integer +## ----------------------------------------------------------------------------- +# max_length = null + +## ----------------------------------------------------------------------------- +## min_length +## ----------------------------------------------------------------------------- +## The minimum length (bases) of a read +## Type: integer +## ----------------------------------------------------------------------------- +# min_length = null + +## ----------------------------------------------------------------------------- +## max_n_bases +## ----------------------------------------------------------------------------- +## The maximum number of Ns allowed in a read +## Type: integer +## ----------------------------------------------------------------------------- +# max_n_bases = 0 + +## ----------------------------------------------------------------------------- +## avg_qual +## ----------------------------------------------------------------------------- +## Minimum avg. quality a read must have (0 will disable the filter) +## Type: integer +## ----------------------------------------------------------------------------- +# avg_qual = 20 + +## ----------------------------------------------------------------------------- +## dedup +## ----------------------------------------------------------------------------- +## Remove duplicated reads (exact same sequence) +## Type: boolean +## ----------------------------------------------------------------------------- +# dedup = false + +## ----------------------------------------------------------------------------- +## remove_polyg +## ----------------------------------------------------------------------------- +## Remove PolyG sequences (length of 10 or more) +## Type: boolean +## ----------------------------------------------------------------------------- +# remove_polyg = false + + +## ============================================================================= +## Adapter QC Options +## ============================================================================= + +## (2 hidden parameters are not shown) + + +## ----------------------------------------------------------------------------- +## adapterqc_mismatches +## ----------------------------------------------------------------------------- +## The number of mismatches allowed (in percentage) [default: 0.1; +## 0.0<=x<=0.9] +## Type: number +## ----------------------------------------------------------------------------- +# adapterqc_mismatches = 0.1 + + +## ============================================================================= +## Demux options +## ============================================================================= + +## ----------------------------------------------------------------------------- +## demux_mismatches +## ----------------------------------------------------------------------------- +## The number of mismatches allowed (as a fraction) +## Type: number +## ----------------------------------------------------------------------------- +# demux_mismatches = 0.1 + +## ----------------------------------------------------------------------------- +## demux_min_length +## ----------------------------------------------------------------------------- +## The minimum length of the barcode that must overlap when matching +## Type: integer +## ----------------------------------------------------------------------------- +# demux_min_length = null + + +## ============================================================================= +## Collapse options +## ============================================================================= + +## (1 hidden parameters are not shown) + + +## ----------------------------------------------------------------------------- +## markers_ignore +## ----------------------------------------------------------------------------- +## A list of comma separated antibodies to discard +## Type: string +## ----------------------------------------------------------------------------- +# markers_ignore = null + +## ----------------------------------------------------------------------------- +## algorithm +## ----------------------------------------------------------------------------- +## The algorithm to use for collapsing (adjacency will peform error +## correction using the number of mismatches given) +## Type: string +## ----------------------------------------------------------------------------- +# algorithm = "adjacency" + +## ----------------------------------------------------------------------------- +## collapse_mismatches +## ----------------------------------------------------------------------------- +## The number of mismatches allowed when collapsing (adjacency) +## Type: integer +## ----------------------------------------------------------------------------- +# collapse_mismatches = 2 + +## ----------------------------------------------------------------------------- +## collapse_min_count +## ----------------------------------------------------------------------------- +## Discard molecules with with a count (reads) lower than this value +## Type: integer +## ----------------------------------------------------------------------------- +# collapse_min_count = 2 + +## ----------------------------------------------------------------------------- +## collapse_use_counts +## ----------------------------------------------------------------------------- +## Use counts when collapsing (the difference in counts between two +## molecules must be more than double in order to be collapsed) +## Type: boolean +## ----------------------------------------------------------------------------- +# collapse_use_counts = null + + +## ============================================================================= +## Options for pixelator graph command. +## ============================================================================= + +## (2 hidden parameters are not shown) + + +## ----------------------------------------------------------------------------- +## multiplet_recovery +## ----------------------------------------------------------------------------- +## Activate the multiplet recovery using leiden community detection +## Type: boolean +## ----------------------------------------------------------------------------- +# multiplet_recovery = true + + +## ============================================================================= +## Options for pixelator annotate command. +## ============================================================================= + +## ----------------------------------------------------------------------------- +## min_size +## ----------------------------------------------------------------------------- +## The minimum size (pixels) a component/cell can have (disabled by +## default) +## Type: integer +## ----------------------------------------------------------------------------- +# min_size = null + +## ----------------------------------------------------------------------------- +## max_size +## ----------------------------------------------------------------------------- +## The maximum size (pixels) a component/cell can have (disabled by +## default) +## Type: integer +## ----------------------------------------------------------------------------- +# max_size = null + +## ----------------------------------------------------------------------------- +## dynamic_filter +## ----------------------------------------------------------------------------- +## Enable the estimation of dynamic size filters using a log-rank +## approach both: estimate both min and max size, min: estimate min size +## (--min-size), max: estimate max size (--max-size) +## Type: string +## ----------------------------------------------------------------------------- +# dynamic_filter = "min" + +## ----------------------------------------------------------------------------- +## aggregate_calling +## ----------------------------------------------------------------------------- +## Enable aggregate calling, information on potential aggregates will be +## added to the output data +## Type: boolean +## ----------------------------------------------------------------------------- +# aggregate_calling = true + + +## ============================================================================= +## Options for pixelator analysis command. +## ============================================================================= + +## ----------------------------------------------------------------------------- +## skip_analysis +## ----------------------------------------------------------------------------- +## Skip analysis step +## Type: boolean +## ----------------------------------------------------------------------------- +# skip_analysis = false + +## ----------------------------------------------------------------------------- +## compute_polarization +## ----------------------------------------------------------------------------- +## Compute polarization scores matrix (clusters by markers) +## Type: boolean +## ----------------------------------------------------------------------------- +# compute_polarization = true + +## ----------------------------------------------------------------------------- +## compute_colocalization +## ----------------------------------------------------------------------------- +## Compute colocalization scores (marker by marker) for each component +## Type: boolean +## ----------------------------------------------------------------------------- +# compute_colocalization = true + +## ----------------------------------------------------------------------------- +## use_full_bipartite +## ----------------------------------------------------------------------------- +## Use the bipartite graph instead of the one-node projection when +## computing polarization, coabundance and colocalization scores +## Type: boolean +## ----------------------------------------------------------------------------- +# use_full_bipartite = false + +## ----------------------------------------------------------------------------- +## polarization_normalization +## ----------------------------------------------------------------------------- +## Which approach to use to normalize the antibody counts. +## Type: string +## ----------------------------------------------------------------------------- +# polarization_normalization = "clr" + +## ----------------------------------------------------------------------------- +## polarization_binarization +## ----------------------------------------------------------------------------- +## Transform the antibody counts to 0-1 (binarize) when computing +## polarization +## Type: boolean +## ----------------------------------------------------------------------------- +# polarization_binarization = false + +## ----------------------------------------------------------------------------- +## colocalization_transformation +## ----------------------------------------------------------------------------- +## Select the type of transformation to use on the node by antibody +## counts matrix when computing colocalization +## Type: string +## ----------------------------------------------------------------------------- +# colocalization_transformation = "log1p" + +## ----------------------------------------------------------------------------- +## colocalization_neighbourhood_size +## ----------------------------------------------------------------------------- +## Select the size of the neighborhood to use when computing +## colocalization metrics on each component +## Type: integer +## ----------------------------------------------------------------------------- +# colocalization_neighbourhood_size = 1 + +## ----------------------------------------------------------------------------- +## colocalization_n_permutations +## ----------------------------------------------------------------------------- +## Set the number of permutations use to compute the empirical p-value +## for the colocalization score +## Type: integer +## ----------------------------------------------------------------------------- +# colocalization_n_permutations = 50 + +## ----------------------------------------------------------------------------- +## colocalization_min_region_count +## ----------------------------------------------------------------------------- +## The minimum number of counts in a region for it to be concidered valid +## for computing colocalization +## Type: integer +## ----------------------------------------------------------------------------- +# colocalization_min_region_count = 5 + + +## ============================================================================= +## Options for pixelator report command. +## ============================================================================= + +## ----------------------------------------------------------------------------- +## skip_report +## ----------------------------------------------------------------------------- +## Skip report generation +## Type: boolean +## ----------------------------------------------------------------------------- +# skip_report = false + + +## ============================================================================= +## Global options +## ============================================================================= +## Global configuration options specific to nf-core/pixelator. + +## ----------------------------------------------------------------------------- +## pixelator_container +## ----------------------------------------------------------------------------- +## Override the container image reference to use for all steps using the +## `pixelator` command. +## Type: string +## ----------------------------------------------------------------------------- +# pixelator_container = null + + +## ============================================================================= +## Institutional config options +## ============================================================================= +## Parameters used to describe centralised config profiles. These should not +## be edited. + +## (6 hidden parameters are not shown) + + + +## ============================================================================= +## Max job request options +## ============================================================================= +## Set the top limit for requested resources for any single job. + +## (3 hidden parameters are not shown) + + + +## ============================================================================= +## Generic options +## ============================================================================= +## Less common options for the pipeline, typically set in a config file. + +## (12 hidden parameters are not shown) + + + diff --git a/assets/params-file.yml b/assets/params-file.yml deleted file mode 100644 index 683f4586..00000000 --- a/assets/params-file.yml +++ /dev/null @@ -1,246 +0,0 @@ -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## nf-core/pixelator parameter file -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## This is an example params-file.yaml for the `-params-file` option of -## nf-core/pixelator. -## Uncomment lines with a single '#' if you want to pass the parameter. -## ---------------------------------------------------------------------------------------- - -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## preqc_options -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -## ------------------------------------------------------------------------------------------ -## Trim N bases from the front of the reads -## ------------------------------------------------------------------------------------------ -# trim_front: 0 - -## ------------------------------------------------------------------------------------------ -## Trim N bases from the tail of the reads -## ------------------------------------------------------------------------------------------ -# trim_tail: 0 - -## ------------------------------------------------------------------------------------------ -## The maximum length of a read -## ------------------------------------------------------------------------------------------ -# max_length: null - -## ------------------------------------------------------------------------------------------ -## The minimum length (bases) of a read -## ------------------------------------------------------------------------------------------ -# min_length: null - -## ------------------------------------------------------------------------------------------ -## The maximum number of Ns allowed in a read -## ------------------------------------------------------------------------------------------ -# max_n_bases: 0 - -## ------------------------------------------------------------------------------------------ -## Minimum avg. quality a read must have (0 will disable the filter) -## ------------------------------------------------------------------------------------------ -# avg_qual: 20 - -## ------------------------------------------------------------------------------------------ -## Remove duplicated reads (exact same sequence) -## ------------------------------------------------------------------------------------------ -# dedup: false - -## ------------------------------------------------------------------------------------------ -## Remove PolyG sequences (length of 10 or more) -## ------------------------------------------------------------------------------------------ -# remove_polyg: false - -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## adapterqc_options -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -## ------------------------------------------------------------------------------------------ -## The number of mismatches allowed (in percentage) [default: 0.1; -## 0.0<=x<=0.9] -## ------------------------------------------------------------------------------------------ -# adapterqc_mismatches: 0.1 - -## ------------------------------------------------------------------------------------------ -## The PBS1 sequence that must be present in the reads. -## ------------------------------------------------------------------------------------------ -# pbs1: null - -## ------------------------------------------------------------------------------------------ -## The PBS2 sequence that must be present in the reads. -## ------------------------------------------------------------------------------------------ -# pbs2: null - -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## demux_options -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -## ------------------------------------------------------------------------------------------ -## The number of mismatches allowed (as a fraction) -## ------------------------------------------------------------------------------------------ -# demux_mismatches: 0.1 - -## ------------------------------------------------------------------------------------------ -## The minimum length of the barcode that must overlap when matching -## ------------------------------------------------------------------------------------------ -# demux_min_length: null - -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## collapse_options -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -## ------------------------------------------------------------------------------------------ -## A list of comma separated antibodies to discard -## ------------------------------------------------------------------------------------------ -# markers_ignore: null - -## ------------------------------------------------------------------------------------------ -## The algorithm to use for collapsing (adjacency will peform error -## correction using the number of mismatches given) -## ------------------------------------------------------------------------------------------ -# algorithm: adjacency - -## ------------------------------------------------------------------------------------------ -## The number of neighbours to use when searching for similar -## sequences (adjacency) This number depends on the sequence depth and -## the ratio of erroneous molecules expected. A high value can make the -## algorithm slower. -## ------------------------------------------------------------------------------------------ -# neighbours: 60 - -## ------------------------------------------------------------------------------------------ -## The number of mismatches allowed when collapsing (adjacency) -## ------------------------------------------------------------------------------------------ -# collapse_mismatches: 2 - -## ------------------------------------------------------------------------------------------ -## Discard molecules with with a count (reads) lower than this value -## ------------------------------------------------------------------------------------------ -# collapse_min_count: 2 - -## ------------------------------------------------------------------------------------------ -## Use counts when collapsing (the difference in counts between two -## molecules must be more than double in order to be collapsed) -## ------------------------------------------------------------------------------------------ -# collapse_use_counts: null - -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## graph_options -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -## ------------------------------------------------------------------------------------------ -## Activate the multiplet recovery using leiden community detection -## ------------------------------------------------------------------------------------------ -# multiplet_recovery: true - -## ------------------------------------------------------------------------------------------ -## Number of iterations for the leiden algorithm, high values will -## decrease the variance of the results but increase the runtime -## [default: 10; 1<=x<=100] -## ------------------------------------------------------------------------------------------ -# leiden_iterations: 10 - -## ------------------------------------------------------------------------------------------ -## Discard edges (pixels) with a count (reads) lower than this, use 1 -## to disable -## ------------------------------------------------------------------------------------------ -# graph_min_count: 2 - -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## annotate_options -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -## ------------------------------------------------------------------------------------------ -## The minimum size (pixels) a component/cell can have (disabled by -## default) -## ------------------------------------------------------------------------------------------ -# min_size: null - -## ------------------------------------------------------------------------------------------ -## The maximum size (pixels) a component/cell can have (disabled by -## default) -## ------------------------------------------------------------------------------------------ -# max_size: null - -## ------------------------------------------------------------------------------------------ -## Enable the estimation of dynamic size filters using a log-rank -## approach both: estimate both min and max size, min: estimate min size -## (--min-size), max: estimate max size (--max-size) -## ------------------------------------------------------------------------------------------ -# dynamic_filter: min - -## ------------------------------------------------------------------------------------------ -## Enable aggregate calling, information on potential aggregates will -## be added to the output data -## ------------------------------------------------------------------------------------------ -# aggregate_calling: true - -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## analysis_options -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -## ------------------------------------------------------------------------------------------ -## Skip analysis step -## ------------------------------------------------------------------------------------------ -# skip_analysis: false - -## ------------------------------------------------------------------------------------------ -## Compute polarization scores matrix (clusters by markers) -## ------------------------------------------------------------------------------------------ -# compute_polarization: true - -## ------------------------------------------------------------------------------------------ -## Compute colocalization scores (marker by marker) for each -## component -## ------------------------------------------------------------------------------------------ -# compute_colocalization: true - -## ------------------------------------------------------------------------------------------ -## Use the bipartite graph instead of the one-node projection when -## computing polarization, coabundance and colocalization scores -## ------------------------------------------------------------------------------------------ -# use_full_bipartite: false - -## ------------------------------------------------------------------------------------------ -## Which approach to use to normalize the antibody counts. -## ------------------------------------------------------------------------------------------ -# polarization_normalization: clr - -## ------------------------------------------------------------------------------------------ -## Transform the antibody counts to 0-1 (binarize) when computing -## polarization -## ------------------------------------------------------------------------------------------ -# polarization_binarization: false - -## ------------------------------------------------------------------------------------------ -## Select the type of transformation to use on the node by antibody -## counts matrix when computing colocalization -## ------------------------------------------------------------------------------------------ -# colocalization_transformation: log1p - -## ------------------------------------------------------------------------------------------ -## Select the size of the neighborhood to use when computing -## colocalization metrics on each component -## ------------------------------------------------------------------------------------------ -# colocalization_neighbourhood_size: 1 - -## ------------------------------------------------------------------------------------------ -## Set the number of permutations use to compute the empirical p-value -## for the colocalization score -## ------------------------------------------------------------------------------------------ -# colocalization_n_permutations: 50 - -## ------------------------------------------------------------------------------------------ -## The minimum number of counts in a region for it to be concidered -## valid for computing colocalization -## ------------------------------------------------------------------------------------------ -# colocalization_min_region_count: 5 - -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## report_options -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -## ------------------------------------------------------------------------------------------ -## Skip report generation -## ------------------------------------------------------------------------------------------ -# skip_report: false - diff --git a/conf/modules.config b/conf/modules.config index 8bb89ce8..411eab75 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -115,7 +115,7 @@ process { ext.args = [ params.markers_ignore ? "--markers_ignore ${markers_ignore}": params.algorithm ? "--algorithm ${params.algorithm}": '', - params.neighbours ? "--neighbours ${params.neighbours}": '', + params.max_neighbours ? "--max-neighbours ${params.max_neighbours}": '', params.collapse_mismatches ? "--mismatches ${params.collapse_mismatches}": '', params.collapse_min_count ? "--min-count ${params.collapse_min_count}": '', params.collapse_use_counts ? "--use-counts": '', diff --git a/nextflow.config b/nextflow.config index a340a73c..f19cf0ef 100644 --- a/nextflow.config +++ b/nextflow.config @@ -35,7 +35,7 @@ params { //collapse options markers_ignore = null algorithm = 'adjacency' - neighbours = 60 + max_neighbours = 60 collapse_mismatches = 2 collapse_min_count = 2 collapse_use_counts = false diff --git a/nextflow_schema.json b/nextflow_schema.json index 2f521c97..937b6c2f 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -155,9 +155,9 @@ "enum": ["adjacency", "unique"], "type": "string" }, - "neighbours": { + "max_neighbours": { "fa_icon": "fas circle-nodes", - "description": "The number of neighbours to use when searching for similar sequences (adjacency) This number depends on the sequence depth and the ratio of erroneous molecules expected. A high value can make the algorithm slower.", + "description": "The maximum number of neighbors to use when searching for similar sequences. This number depends on the sequence depth and the ratio of erroneous molecules expected. A high value can make the algorithm slower. This is only used when algorithm is set to 'adjacency'", "default": 60, "minimum": 1, "maximum": 250, From 502aeec3c2cffa1ef7ed371122c9847ee3008a39 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 9 Aug 2023 13:59:32 +0200 Subject: [PATCH 063/112] docs: fix duplicated part of pipeline summary --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 44a1d747..0ae1e855 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ to nf-core here, in 15-20 seconds. For an example, see https://github.com/nf-core/rnaseq/blob/master/README.md#introduction --> -**nf-core/pixelator** is a bioinformatics best-practice analysis pipeline for Pipeline for analysis of Molecular Pixelation assays. +**nf-core/pixelator** is a bioinformatics best-practice analysis pipeline for analysis of Molecular Pixelation assays. It takes a samplesheet as input and will process your data using `pixelator` to produce final antibody counts. - **nf-core/pixelator** is a bioinformatics best-practice analysis pipeline for analysis of Molecular Pixelation assays. It takes a samplesheet as input and will process your data using `pixelator` to produce final antibody counts. - - ![](./docs/images/nf_core_pixelator_metromap.svg) - - 1. Build amplicon from input reads ([`pixelator amplicon`](https://github.com/PixelgenTechnologies/pixelator)) 2. Read QC and filtering, correctness of the pixel binding sequence sequences ([`pixelator preqc | pixelator adapterqc`](https://github.com/PixelgenTechnologies/pixelator)) 3. Assign a marker (barcode) to each read ([`pixelator demux`](https://github.com/PixelgenTechnologies/pixelator)) @@ -44,22 +35,6 @@ It takes a samplesheet as input and will process your data using `pixelator` to > to set-up Nextflow. Make sure to [test your setup](https://nf-co.re/docs/usage/introduction#how-to-run-a-pipeline) > with `-profile test` before running the workflow on actual data. - - First, prepare a samplesheet with your input data that looks as follows: `samplesheet.csv`: @@ -95,7 +70,7 @@ For more details about the output files and reports, please refer to the ## Credits -nf-core/pixelator was originally written for [Pixelgen Technologies AB](https://www.pixelgen.tech/) by: +nf-core/pixelator was originally written for [Pixelgen Technologies AB](https://www.pixelgen.com/) by: - Florian De Temmerman - Johan Dahlberg @@ -114,8 +89,6 @@ For further information or help, don't hesitate to get in touch on the [Slack `# - - An extensive list of references for the tools used by the pipeline can be found in the [`CITATIONS.md`](CITATIONS.md) file. You can cite the `nf-core` publication as follows: From c3f5c31544cdfb789b43c338f69304cdce6f3279 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 19 Sep 2023 16:18:38 +0200 Subject: [PATCH 088/112] chore: remove TODOs --- .github/workflows/awsfulltest.yml | 3 --- .github/workflows/ci.yml | 3 --- 2 files changed, 6 deletions(-) diff --git a/.github/workflows/awsfulltest.yml b/.github/workflows/awsfulltest.yml index 33799cac..d8a09749 100644 --- a/.github/workflows/awsfulltest.yml +++ b/.github/workflows/awsfulltest.yml @@ -15,9 +15,6 @@ jobs: steps: - name: Launch workflow via tower uses: seqeralabs/action-tower-launch@v2 - # TODO nf-core: You can customise AWS full pipeline tests as required - # Add full size test data (but still relatively small datasets for few samples) - # on the `test_full.config` test runs with only one set of parameters with: workspace_id: ${{ secrets.TOWER_WORKSPACE_ID }} access_token: ${{ secrets.TOWER_ACCESS_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 33092acf..001aab7c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,8 +43,5 @@ jobs: password: ${{ secrets.GITHUB_TOKEN }} - name: Run pipeline with test data - # TODO nf-core: You can customise CI pipeline run tests as required - # For example: adding multiple test runs with different parameters - # Remember that you can parallelise this by using strategy.matrix run: | nextflow run ${GITHUB_WORKSPACE} -profile test,docker --outdir ./results From 36b1c9e523e0b4830dda3842decda3d6fc2a75a9 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 19 Sep 2023 16:23:26 +0200 Subject: [PATCH 089/112] chore: add mimetype to input --- nextflow_schema.json | 1 + 1 file changed, 1 insertion(+) diff --git a/nextflow_schema.json b/nextflow_schema.json index 71f0d8c0..c7b76423 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -16,6 +16,7 @@ "type": "string", "format": "file-path", "schema": "assets/schema_input.json", + "mimetype": "text/csv", "exists": true, "pattern": "^\\S+\\.(csv|tsv)$", "description": "Path to comma-separated file containing information about the samples in the experiment.", From 772489d218fba4fb35b8e8bbb071f5e5b4bc6832 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 19 Sep 2023 16:27:24 +0200 Subject: [PATCH 090/112] chore: remove unused multiqc config file --- assets/methods_description_template.yml | 29 ------------------------- 1 file changed, 29 deletions(-) delete mode 100644 assets/methods_description_template.yml diff --git a/assets/methods_description_template.yml b/assets/methods_description_template.yml deleted file mode 100644 index 2b6ed3cd..00000000 --- a/assets/methods_description_template.yml +++ /dev/null @@ -1,29 +0,0 @@ -id: "nf-core-pixelator-methods-description" -description: "Suggested text and references to use when describing pipeline usage within the methods section of a publication." -section_name: "nf-core/pixelator Methods Description" -section_href: "https://github.com/nf-core/pixelator" -plot_type: "html" -## TODO nf-core: Update the HTML below to your preferred methods description, e.g. add publication citation for this pipeline -## You inject any metadata in the Nextflow '${workflow}' object -data: | -

    Methods

    -

    Data was processed using nf-core/pixelator v${workflow.manifest.version} ${doi_text} of the nf-core collection of workflows (Ewels et al., 2020), utilising reproducible software environments from the Bioconda (Grüning et al., 2018) and Biocontainers (da Veiga Leprevost et al., 2017) projects.

    -

    The pipeline was executed with Nextflow v${workflow.nextflow.version} (Di Tommaso et al., 2017) with the following command:

    -
    ${workflow.commandLine}
    -

    ${tool_citations}

    -

    References

    -
      -
    • Di Tommaso, P., Chatzou, M., Floden, E. W., Barja, P. P., Palumbo, E., & Notredame, C. (2017). Nextflow enables reproducible computational workflows. Nature Biotechnology, 35(4), 316-319. doi: 10.1038/nbt.3820
    • -
    • Ewels, P. A., Peltzer, A., Fillinger, S., Patel, H., Alneberg, J., Wilm, A., Garcia, M. U., Di Tommaso, P., & Nahnsen, S. (2020). The nf-core framework for community-curated bioinformatics pipelines. Nature Biotechnology, 38(3), 276-278. doi: 10.1038/s41587-020-0439-x
    • -
    • Grüning, B., Dale, R., Sjödin, A., Chapman, B. A., Rowe, J., Tomkins-Tinch, C. H., Valieris, R., Köster, J., & Bioconda Team. (2018). Bioconda: sustainable and comprehensive software distribution for the life sciences. Nature Methods, 15(7), 475–476. doi: 10.1038/s41592-018-0046-7
    • -
    • da Veiga Leprevost, F., Grüning, B. A., Alves Aflitos, S., Röst, H. L., Uszkoreit, J., Barsnes, H., Vaudel, M., Moreno, P., Gatto, L., Weber, J., Bai, M., Jimenez, R. C., Sachsenberg, T., Pfeuffer, J., Vera Alvarez, R., Griss, J., Nesvizhskii, A. I., & Perez-Riverol, Y. (2017). BioContainers: an open-source and community-driven framework for software standardization. Bioinformatics (Oxford, England), 33(16), 2580–2582. doi: 10.1093/bioinformatics/btx192
    • - ${tool_bibliography} -
    -
    -
    Notes:
    -
      - ${nodoi_text} -
    • The command above does not include parameters contained in any configs or profiles that may have been used. Ensure the config file is also uploaded with your publication!
    • -
    • You should also cite all software used within this run. Check the "Software Versions" of this report to get version information.
    • -
    -
    From 35b7f9f0b72f493084c293758392eacce9fef03c Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 3 Oct 2023 15:58:39 +0200 Subject: [PATCH 091/112] fix: add pixelator report log output --- modules/local/pixelator/single-cell/report/main.nf | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/modules/local/pixelator/single-cell/report/main.nf b/modules/local/pixelator/single-cell/report/main.nf index aa89d9d7..b3a3505c 100644 --- a/modules/local/pixelator/single-cell/report/main.nf +++ b/modules/local/pixelator/single-cell/report/main.nf @@ -17,14 +17,17 @@ process PIXELATOR_REPORT { path annotate_data , stageAs: "results/annotate/*" path analysis_data , stageAs: "results/analysis/*" + output: - path "report/*.html", emit: reports - path "versions.yml", emit: versions + path "report/*.html" , emit: reports + path "versions.yml" , emit: versions + path "*pixelator-*.log" , emit: log when: task.ext.when == null || task.ext.when script: + def prefix = task.ext.prefix ?: "${meta.id}" def args = task.ext.args ?: '' def panelOpt = ( panel ? "--panel $panel" : @@ -34,6 +37,9 @@ process PIXELATOR_REPORT { """ pixelator \\ + --cores $task.cpus \\ + --log-file ${prefix}.pixelator-report.log \\ + --verbose \\ single-cell \\ report \\ --output . \\ From 131d1c3476fe0005616b7e258cb08120ad7e3e28 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 3 Oct 2023 15:58:52 +0200 Subject: [PATCH 092/112] fix: add missing params scope --- conf/modules.config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index c4c5131f..7ab9ba43 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -23,7 +23,7 @@ process { publishDir = [ [ path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, - mode: "${params.publish_dir_mode}", + mode: params.publish_dir_mode, saveAs: { filename -> (filename.endsWith('.log') || filename.equals('versions.yml')) ? null : filename } ], [ @@ -101,7 +101,7 @@ process { withName: PIXELATOR_COLLAPSE { ext.args = [ - params.markers_ignore ? "--markers_ignore ${markers_ignore}": + params.markers_ignore ? "--markers_ignore ${params.markers_ignore}": params.algorithm ? "--algorithm ${params.algorithm}": '', params.max_neighbours ? "--max-neighbours ${params.max_neighbours}": '', params.collapse_mismatches ? "--mismatches ${params.collapse_mismatches}": '', From e91c27f19aa45113185501b7dff71e125b64b8a2 Mon Sep 17 00:00:00 2001 From: nf-core-bot Date: Mon, 25 Sep 2023 15:17:19 +0000 Subject: [PATCH 093/112] Template update for nf-core/tools version 2.10 --- .devcontainer/devcontainer.json | 1 + .github/CONTRIBUTING.md | 4 +- .github/workflows/linting.yml | 2 +- .github/workflows/release-announcments.yml | 68 ++++++++++++++++++++++ README.md | 23 ++++---- docs/output.md | 1 + docs/usage.md | 16 +++-- lib/NfcoreTemplate.groovy | 16 +++++ lib/WorkflowPixelator.groovy | 2 +- nextflow.config | 7 ++- workflows/pixelator.nf | 1 + 11 files changed, 120 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/release-announcments.yml diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index ea27a584..4ecfbfe3 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -2,6 +2,7 @@ "name": "nfcore", "image": "nfcore/gitpod:latest", "remoteUser": "gitpod", + "runArgs": ["--privileged"], // Configure tool-specific properties. "customizations": { diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 4049c8ae..0065fc46 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -9,7 +9,9 @@ Please use the pre-filled template to save time. However, don't be put off by this template - other more general issues and suggestions are welcome! Contributions to the code are even more welcome ;) -> If you need help using or modifying nf-core/pixelator then the best place to ask is on the nf-core Slack [#pixelator](https://nfcore.slack.com/channels/pixelator) channel ([join our Slack here](https://nf-co.re/join/slack)). +:::info +If you need help using or modifying nf-core/pixelator then the best place to ask is on the nf-core Slack [#pixelator](https://nfcore.slack.com/channels/pixelator) channel ([join our Slack here](https://nf-co.re/join/slack)). +::: ## Contribution workflow diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 888cb4bc..b8bdd214 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -78,7 +78,7 @@ jobs: - uses: actions/setup-python@v4 with: - python-version: "3.8" + python-version: "3.11" architecture: "x64" - name: Install dependencies diff --git a/.github/workflows/release-announcments.yml b/.github/workflows/release-announcments.yml new file mode 100644 index 00000000..6ad33927 --- /dev/null +++ b/.github/workflows/release-announcments.yml @@ -0,0 +1,68 @@ +name: release-announcements +# Automatic release toot and tweet anouncements +on: + release: + types: [published] + workflow_dispatch: + +jobs: + toot: + runs-on: ubuntu-latest + steps: + - uses: rzr/fediverse-action@master + with: + access-token: ${{ secrets.MASTODON_ACCESS_TOKEN }} + host: "mstdn.science" # custom host if not "mastodon.social" (default) + # GitHub event payload + # https://docs.github.com/en/developers/webhooks-and-events/webhooks/webhook-events-and-payloads#release + message: | + Pipeline release! ${{ github.repository }} v${{ github.event.release.tag_name }} - ${{ github.event.release.name }}! + + Please see the changelog: ${{ github.event.release.html_url }} + + send-tweet: + runs-on: ubuntu-latest + + steps: + - uses: actions/setup-python@v4 + with: + python-version: "3.10" + - name: Install dependencies + run: pip install tweepy==4.14.0 + - name: Send tweet + shell: python + run: | + import os + import tweepy + + client = tweepy.Client( + access_token=os.getenv("TWITTER_ACCESS_TOKEN"), + access_token_secret=os.getenv("TWITTER_ACCESS_TOKEN_SECRET"), + consumer_key=os.getenv("TWITTER_CONSUMER_KEY"), + consumer_secret=os.getenv("TWITTER_CONSUMER_SECRET"), + ) + tweet = os.getenv("TWEET") + client.create_tweet(text=tweet) + env: + TWEET: | + Pipeline release! ${{ github.repository }} v${{ github.event.release.tag_name }} - ${{ github.event.release.name }}! + + Please see the changelog: ${{ github.event.release.html_url }} + TWITTER_CONSUMER_KEY: ${{ secrets.TWITTER_CONSUMER_KEY }} + TWITTER_CONSUMER_SECRET: ${{ secrets.TWITTER_CONSUMER_SECRET }} + TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }} + TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }} + + bsky-post: + runs-on: ubuntu-latest + steps: + - uses: zentered/bluesky-post-action@v0.0.2 + with: + post: | + Pipeline release! ${{ github.repository }} v${{ github.event.release.tag_name }} - ${{ github.event.release.name }}! + + Please see the changelog: ${{ github.event.release.html_url }} + env: + BSKY_IDENTIFIER: ${{ secrets.BSKY_IDENTIFIER }} + BSKY_PASSWORD: ${{ secrets.BSKY_PASSWORD }} + # diff --git a/README.md b/README.md index ef9e6f57..84677874 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,7 @@ # ![nf-core/pixelator](docs/images/nf-core-pixelator_logo_light.png#gh-light-mode-only) ![nf-core/pixelator](docs/images/nf-core-pixelator_logo_dark.png#gh-dark-mode-only) -[![nf-core CI](https://github.com/nf-core/pixelator/actions/workflows/ci.yml/badge.svg)](https://github.com/nf-core/pixelator/actions/workflows/ci.yml) -[![nf-core linting](https://github.com/nf-core/pixelator/actions/workflows/linting.yml/badge.svg)](https://github.com/nf-core/pixelator/actions/workflows/linting.yml) -[![AWS CI](https://img.shields.io/badge/CI%20tests-full%20size-FF9900?labelColor=000000&logo=Amazon%20AWS)](https://nf-co.re/pixelator/results)[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.XXXXXXX-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.XXXXXXX) +[![GitHub Actions CI Status](https://github.com/nf-core/pixelator/workflows/nf-core%20CI/badge.svg)](https://github.com/nf-core/pixelator/actions?query=workflow%3A%22nf-core+CI%22) +[![GitHub Actions Linting Status](https://github.com/nf-core/pixelator/workflows/nf-core%20linting/badge.svg)](https://github.com/nf-core/pixelator/actions?query=workflow%3A%22nf-core+linting%22)[![AWS CI](https://img.shields.io/badge/CI%20tests-full%20size-FF9900?labelColor=000000&logo=Amazon%20AWS)](https://nf-co.re/pixelator/results)[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.XXXXXXX-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.XXXXXXX) [![Nextflow](https://img.shields.io/badge/nextflow%20DSL2-%E2%89%A523.04.0-23aa62.svg)](https://www.nextflow.io/) [![run with conda](http://img.shields.io/badge/run%20with-conda-3EB049?labelColor=000000&logo=anaconda)](https://docs.conda.io/en/latest/) @@ -30,10 +29,11 @@ It takes a samplesheet as input and will process your data using `pixelator` to ## Usage -> **Note** -> If you are new to Nextflow and nf-core, please refer to [this page](https://nf-co.re/docs/usage/installation) on how -> to set-up Nextflow. Make sure to [test your setup](https://nf-co.re/docs/usage/introduction#how-to-run-a-pipeline) -> with `-profile test` before running the workflow on actual data. +:::note +If you are new to Nextflow and nf-core, please refer to [this page](https://nf-co.re/docs/usage/installation) on how +to set-up Nextflow. Make sure to [test your setup](https://nf-co.re/docs/usage/introduction#how-to-run-a-pipeline) +with `-profile test` before running the workflow on actual data. +::: First, prepare a samplesheet with your input data that looks as follows: @@ -55,10 +55,11 @@ nextflow run nf-core/pixelator \ --outdir ``` -> **Warning:** -> Please provide pipeline parameters via the CLI or Nextflow `-params-file` option. Custom config files including those -> provided by the `-c` Nextflow option can be used to provide any configuration _**except for parameters**_; -> see [docs](https://nf-co.re/usage/configuration#custom-configuration-files). +:::warning +Please provide pipeline parameters via the CLI or Nextflow `-params-file` option. Custom config files including those +provided by the `-c` Nextflow option can be used to provide any configuration _**except for parameters**_; +see [docs](https://nf-co.re/usage/configuration#custom-configuration-files). +::: For more details and further functionality, please refer to the [usage documentation](https://nf-co.re/pixelator/usage) and the [parameter documentation](https://nf-co.re/pixelator/parameters). diff --git a/docs/output.md b/docs/output.md index f8f52414..b64441bf 100644 --- a/docs/output.md +++ b/docs/output.md @@ -244,6 +244,7 @@ This step can be skipped using the `--skip_report` option. - Reports generated by the pipeline: `pipeline_report.html`, `pipeline_report.txt` and `software_versions.yml`. The `pipeline_report*` files will only be present if the `--email` / `--email_on_fail` parameter's are used when running the pipeline. - Reformatted samplesheet files used as input to the pipeline: `samplesheet.valid.csv`. - Metadata file with software versions, environment information and pipeline configuration for debugging: `metadata.json` + - Parameters used by the pipeline run: `params.json`. diff --git a/docs/usage.md b/docs/usage.md index 637d19c1..64f20d54 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -120,7 +120,9 @@ If you wish to repeatedly use the same parameters for multiple runs, rather than Pipeline settings can be provided in a `yaml` or `json` file via `-params-file `. -> ⚠️ Do not use `-c ` to specify parameters as this will result in errors. Custom config files specified with `-c` must only be used for [tuning process resource specifications](https://nf-co.re/docs/usage/configuration#tuning-workflow-resources), other infrastructural tweaks (such as output directories), or module arguments (args). +:::warning +Do not use `-c ` to specify parameters as this will result in errors. Custom config files specified with `-c` must only be used for [tuning process resource specifications](https://nf-co.re/docs/usage/configuration#tuning-workflow-resources), other infrastructural tweaks (such as output directories), or module arguments (args). +::: The above pipeline run specified with a params file in yaml format: @@ -158,11 +160,15 @@ This version number will be logged in reports when you run the pipeline, so that To further assist in reproducibility, you can use share and re-use [parameter files](#running-the-pipeline) to repeat pipeline runs with the same settings without having to write out a command with every single parameter. -> 💡 If you wish to share such profile (such as upload as supplementary material for academic publications), make sure to NOT include cluster specific paths to files, nor institutional specific profiles. +:::tip +If you wish to share such profile (such as upload as supplementary material for academic publications), make sure to NOT include cluster specific paths to files, nor institutional specific profiles. +::: ## Core Nextflow arguments -> **NB:** These options are part of Nextflow and use a _single_ hyphen (pipeline parameters use a double-hyphen). +:::note +These options are part of Nextflow and use a _single_ hyphen (pipeline parameters use a double-hyphen). +::: ### `-profile` @@ -170,7 +176,9 @@ Use this parameter to choose a configuration profile. Profiles can give configur Several generic profiles are bundled with the pipeline which instruct the pipeline to use software packaged using different methods (Docker, Singularity, Podman, Shifter, Charliecloud, Apptainer, Conda) - see below. -> We highly recommend the use of Docker or Singularity containers for full pipeline reproducibility, however when this is not possible, Conda is also supported. +:::info +We highly recommend the use of Docker or Singularity containers for full pipeline reproducibility, however when this is not possible, Conda is also supported. +::: The pipeline also dynamically loads configurations from [https://github.com/nf-core/configs](https://github.com/nf-core/configs) when it runs, making multiple config profiles for various institutional clusters available at run time. For more information and to see if your system is available in these configs please see the [nf-core/configs documentation](https://github.com/nf-core/configs#documentation). diff --git a/lib/NfcoreTemplate.groovy b/lib/NfcoreTemplate.groovy index 408951ae..01b8653d 100755 --- a/lib/NfcoreTemplate.groovy +++ b/lib/NfcoreTemplate.groovy @@ -3,6 +3,7 @@ // import org.yaml.snakeyaml.Yaml +import groovy.json.JsonOutput class NfcoreTemplate { @@ -222,6 +223,21 @@ class NfcoreTemplate { } } + // + // Dump pipeline parameters in a json file + // + public static void dump_parameters(workflow, params) { + def output_d = new File("${params.outdir}/pipeline_info/") + if (!output_d.exists()) { + output_d.mkdirs() + } + + def timestamp = new java.util.Date().format( 'yyyy-MM-dd_HH-mm-ss') + def output_pf = new File(output_d, "params_${timestamp}.json") + def jsonStr = JsonOutput.toJson(params) + output_pf.text = JsonOutput.prettyPrint(jsonStr) + } + // // Print pipeline summary on completion // diff --git a/lib/WorkflowPixelator.groovy b/lib/WorkflowPixelator.groovy index 23218310..96b8a3e3 100755 --- a/lib/WorkflowPixelator.groovy +++ b/lib/WorkflowPixelator.groovy @@ -40,7 +40,7 @@ class WorkflowPixelator { public static String toolCitationText(params) { - // TODO Optionally add in-text citation tools to this list. + // TODO nf-core: Optionally add in-text citation tools to this list. // Can use ternary operators to dynamically construct based conditions, e.g. params["run_xyz"] ? "Tool (Foo et al. 2023)" : "", // Uncomment function in methodsDescriptionText to render in MultiQC report def citation_text = [ diff --git a/nextflow.config b/nextflow.config index e692bf3b..d941a28c 100644 --- a/nextflow.config +++ b/nextflow.config @@ -98,7 +98,7 @@ params { // Schema validation default options validationFailUnrecognisedParams = false validationLenientMode = false - validationSchemaIgnoreParams = 'genomes' + validationSchemaIgnoreParams = 'genomes,igenomes_base' validationShowHiddenParams = false validate_params = true } @@ -198,6 +198,7 @@ profiles { } apptainer { apptainer.enabled = true + apptainer.autoMounts = true conda.enabled = false docker.enabled = false singularity.enabled = false @@ -207,8 +208,8 @@ profiles { } gitpod { executor.name = 'local' - executor.cpus = 16 - executor.memory = 60.GB + executor.cpus = 4 + executor.memory = 8.GB } test { includeConfig 'conf/test.config' } test_full { includeConfig 'conf/test_full.config' } diff --git a/workflows/pixelator.nf b/workflows/pixelator.nf index f03758e5..d6dc95c9 100644 --- a/workflows/pixelator.nf +++ b/workflows/pixelator.nf @@ -236,6 +236,7 @@ workflow.onComplete { if (params.email || params.email_on_fail) { NfcoreTemplate.email(workflow, params, summary_params, projectDir, log, multiqc_report) } + NfcoreTemplate.dump_parameters(workflow, params) NfcoreTemplate.summary(workflow, params, log) if (params.hook_url) { NfcoreTemplate.IM_notification(workflow, params, summary_params, projectDir, log) From b0daa692c010962541c31165d5c54bcbf28147d0 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 3 Oct 2023 16:12:39 +0200 Subject: [PATCH 094/112] chore: sync code of conduct with template --- CODE_OF_CONDUCT.md | 133 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 102 insertions(+), 31 deletions(-) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index f4fd052f..c089ec78 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,18 +1,20 @@ -# Code of Conduct at nf-core (v1.0) +# Code of Conduct at nf-core (v1.4) ## Our Pledge -In the interest of fostering an open, collaborative, and welcoming environment, we as contributors and maintainers of nf-core, pledge to making participation in our projects and community a harassment-free experience for everyone, regardless of: +In the interest of fostering an open, collaborative, and welcoming environment, we as contributors and maintainers of nf-core pledge to making participation in our projects and community a harassment-free experience for everyone, regardless of: - Age +- Ability - Body size +- Caste - Familial status - Gender identity and expression - Geographical location - Level of experience - Nationality and national origins - Native language -- Physical and neurological ability +- Neurodiversity - Race or ethnicity - Religion - Sexual identity and orientation @@ -22,80 +24,133 @@ Please note that the list above is alphabetised and is therefore not ranked in a ## Preamble -> Note: This Code of Conduct (CoC) has been drafted by the nf-core Safety Officer and been edited after input from members of the nf-core team and others. "We", in this document, refers to the Safety Officer and members of the nf-core core team, both of whom are deemed to be members of the nf-core community and are therefore required to abide by this Code of Conduct. This document will amended periodically to keep it up-to-date, and in case of any dispute, the most current version will apply. +:::note +This Code of Conduct (CoC) has been drafted by Renuka Kudva, Cris Tuñí, and Michael Heuer, with input from the nf-core Core Team and Susanna Marquez from the nf-core community. "We", in this document, refers to the Safety Officers and members of the nf-core Core Team, both of whom are deemed to be members of the nf-core community and are therefore required to abide by this Code of Conduct. This document will be amended periodically to keep it up-to-date. In case of any dispute, the most current version will apply. +::: -An up-to-date list of members of the nf-core core team can be found [here](https://nf-co.re/about). Our current safety officer is Renuka Kudva. +An up-to-date list of members of the nf-core core team can be found [here](https://nf-co.re/about). + +Our Safety Officers are Saba Nafees, Cris Tuñí, and Michael Heuer. nf-core is a young and growing community that welcomes contributions from anyone with a shared vision for [Open Science Policies](https://www.fosteropenscience.eu/taxonomy/term/8). Open science policies encompass inclusive behaviours and we strive to build and maintain a safe and inclusive environment for all individuals. -We have therefore adopted this code of conduct (CoC), which we require all members of our community and attendees in nf-core events to adhere to in all our workspaces at all times. Workspaces include but are not limited to Slack, meetings on Zoom, Jitsi, YouTube live etc. +We have therefore adopted this CoC, which we require all members of our community and attendees of nf-core events to adhere to in all our workspaces at all times. Workspaces include, but are not limited to, Slack, meetings on Zoom, gather.town, YouTube live etc. -Our CoC will be strictly enforced and the nf-core team reserve the right to exclude participants who do not comply with our guidelines from our workspaces and future nf-core activities. +Our CoC will be strictly enforced and the nf-core team reserves the right to exclude participants who do not comply with our guidelines from our workspaces and future nf-core activities. -We ask all members of our community to help maintain a supportive and productive workspace and to avoid behaviours that can make individuals feel unsafe or unwelcome. Please help us maintain and uphold this CoC. +We ask all members of our community to help maintain supportive and productive workspaces and to avoid behaviours that can make individuals feel unsafe or unwelcome. Please help us maintain and uphold this CoC. -Questions, concerns or ideas on what we can include? Contact safety [at] nf-co [dot] re +Questions, concerns, or ideas on what we can include? Contact members of the Safety Team on Slack or email safety [at] nf-co [dot] re. ## Our Responsibilities -The safety officer is responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behaviour. +Members of the Safety Team (the Safety Officers) are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behaviour. -The safety officer in consultation with the nf-core core team have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. +The Safety Team, in consultation with the nf-core core team, have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this CoC, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. -Members of the core team or the safety officer who violate the CoC will be required to recuse themselves pending investigation. They will not have access to any reports of the violations and be subject to the same actions as others in violation of the CoC. +Members of the core team or the Safety Team who violate the CoC will be required to recuse themselves pending investigation. They will not have access to any reports of the violations and will be subject to the same actions as others in violation of the CoC. -## When are where does this Code of Conduct apply? +## When and where does this Code of Conduct apply? -Participation in the nf-core community is contingent on following these guidelines in all our workspaces and events. This includes but is not limited to the following listed alphabetically and therefore in no order of preference: +Participation in the nf-core community is contingent on following these guidelines in all our workspaces and events, such as hackathons, workshops, bytesize, and collaborative workspaces on gather.town. These guidelines include, but are not limited to, the following (listed alphabetically and therefore in no order of preference): - Communicating with an official project email address. - Communicating with community members within the nf-core Slack channel. - Participating in hackathons organised by nf-core (both online and in-person events). -- Participating in collaborative work on GitHub, Google Suite, community calls, mentorship meetings, email correspondence. -- Participating in workshops, training, and seminar series organised by nf-core (both online and in-person events). This applies to events hosted on web-based platforms such as Zoom, Jitsi, YouTube live etc. +- Participating in collaborative work on GitHub, Google Suite, community calls, mentorship meetings, email correspondence, and on the nf-core gather.town workspace. +- Participating in workshops, training, and seminar series organised by nf-core (both online and in-person events). This applies to events hosted on web-based platforms such as Zoom, gather.town, Jitsi, YouTube live etc. - Representing nf-core on social media. This includes both official and personal accounts. ## nf-core cares 😊 -nf-core's CoC and expectations of respectful behaviours for all participants (including organisers and the nf-core team) include but are not limited to the following (listed in alphabetical order): +nf-core's CoC and expectations of respectful behaviours for all participants (including organisers and the nf-core team) include, but are not limited to, the following (listed in alphabetical order): - Ask for consent before sharing another community member’s personal information (including photographs) on social media. - Be respectful of differing viewpoints and experiences. We are all here to learn from one another and a difference in opinion can present a good learning opportunity. -- Celebrate your accomplishments at events! (Get creative with your use of emojis 🎉 🥳 💯 🙌 !) +- Celebrate your accomplishments! (Get creative with your use of emojis 🎉 🥳 💯 🙌 !) - Demonstrate empathy towards other community members. (We don’t all have the same amount of time to dedicate to nf-core. If tasks are pending, don’t hesitate to gently remind members of your team. If you are leading a task, ask for help if you feel overwhelmed.) - Engage with and enquire after others. (This is especially important given the geographically remote nature of the nf-core community, so let’s do this the best we can) - Focus on what is best for the team and the community. (When in doubt, ask) -- Graciously accept constructive criticism, yet be unafraid to question, deliberate, and learn. +- Accept feedback, yet be unafraid to question, deliberate, and learn. - Introduce yourself to members of the community. (We’ve all been outsiders and we know that talking to strangers can be hard for some, but remember we’re interested in getting to know you and your visions for open science!) -- Show appreciation and **provide clear feedback**. (This is especially important because we don’t see each other in person and it can be harder to interpret subtleties. Also remember that not everyone understands a certain language to the same extent as you do, so **be clear in your communications to be kind.**) +- Show appreciation and **provide clear feedback**. (This is especially important because we don’t see each other in person and it can be harder to interpret subtleties. Also remember that not everyone understands a certain language to the same extent as you do, so **be clear in your communication to be kind.**) - Take breaks when you feel like you need them. -- Using welcoming and inclusive language. (Participants are encouraged to display their chosen pronouns on Zoom or in communication on Slack.) +- Use welcoming and inclusive language. (Participants are encouraged to display their chosen pronouns on Zoom or in communication on Slack) ## nf-core frowns on 😕 -The following behaviours from any participants within the nf-core community (including the organisers) will be considered unacceptable under this code of conduct. Engaging or advocating for any of the following could result in expulsion from nf-core workspaces. +The following behaviours from any participants within the nf-core community (including the organisers) will be considered unacceptable under this CoC. Engaging or advocating for any of the following could result in expulsion from nf-core workspaces: - Deliberate intimidation, stalking or following and sustained disruption of communication among participants of the community. This includes hijacking shared screens through actions such as using the annotate tool in conferencing software such as Zoom. - “Doxing” i.e. posting (or threatening to post) another person’s personal identifying information online. - Spamming or trolling of individuals on social media. -- Use of sexual or discriminatory imagery, comments, or jokes and unwelcome sexual attention. -- Verbal and text comments that reinforce social structures of domination related to gender, gender identity and expression, sexual orientation, ability, physical appearance, body size, race, age, religion or work experience. +- Use of sexual or discriminatory imagery, comments, jokes, or unwelcome sexual attention. +- Verbal and text comments that reinforce social structures of domination related to gender, gender identity and expression, sexual orientation, ability, physical appearance, body size, race, age, religion, or work experience. ### Online Trolling -The majority of nf-core interactions and events are held online. Unfortunately, holding events online comes with the added issue of online trolling. This is unacceptable, reports of such behaviour will be taken very seriously, and perpetrators will be excluded from activities immediately. +The majority of nf-core interactions and events are held online. Unfortunately, holding events online comes with the risk of online trolling. This is unacceptable — reports of such behaviour will be taken very seriously and perpetrators will be excluded from activities immediately. -All community members are required to ask members of the group they are working within for explicit consent prior to taking screenshots of individuals during video calls. +All community members are **required** to ask members of the group they are working with for explicit consent prior to taking screenshots of individuals during video calls. -## Procedures for Reporting CoC violations +## Procedures for reporting CoC violations If someone makes you feel uncomfortable through their behaviours or actions, report it as soon as possible. -You can reach out to members of the [nf-core core team](https://nf-co.re/about) and they will forward your concerns to the safety officer(s). +You can reach out to members of the Safety Team (Saba Nafees, Cris Tuñí, and Michael Heuer) on Slack. Alternatively, contact a member of the nf-core core team [nf-core core team](https://nf-co.re/about), and they will forward your concerns to the Safety Team. + +Issues directly concerning members of the Core Team or the Safety Team will be dealt with by other members of the core team and the safety manager — possible conflicts of interest will be taken into account. nf-core is also in discussions about having an ombudsperson and details will be shared in due course. + +All reports will be handled with the utmost discretion and confidentiality. + +You can also report any CoC violations to safety [at] nf-co [dot] re. In your email report, please do your best to include: + +- Your contact information. +- Identifying information (e.g. names, nicknames, pseudonyms) of the participant who has violated the Code of Conduct. +- The behaviour that was in violation and the circumstances surrounding the incident. +- The approximate time of the behaviour (if different than the time the report was made). +- Other people involved in the incident, if applicable. +- If you believe the incident is ongoing. +- If there is a publicly available record (e.g. mailing list record, a screenshot). +- Any additional information. + +After you file a report, one or more members of our Safety Team will contact you to follow up on your report. + +## Who will read and handle reports + +All reports will be read and handled by the members of the Safety Team at nf-core. + +If members of the Safety Team are deemed to have a conflict of interest with a report, they will be required to recuse themselves as per our Code of Conduct and will not have access to any follow-ups. + +To keep this first report confidential from any of the Safety Team members, please submit your first report by direct messaging on Slack/direct email to any of the nf-core members you are comfortable disclosing the information to, and be explicit about which member(s) you do not consent to sharing the information with. + +## Reviewing reports + +After receiving the report, members of the Safety Team will review the incident report to determine whether immediate action is required, for example, whether there is immediate threat to participants’ safety. + +The Safety Team, in consultation with members of the nf-core core team, will assess the information to determine whether the report constitutes a Code of Conduct violation, for them to decide on a course of action. + +In the case of insufficient information, one or more members of the Safety Team may contact the reporter, the reportee, or any other attendees to obtain more information. -Issues directly concerning members of the core team will be dealt with by other members of the core team and the safety manager, and possible conflicts of interest will be taken into account. nf-core is also in discussions about having an ombudsperson, and details will be shared in due course. +Once additional information is gathered, the Safety Team will collectively review and decide on the best course of action to take, if any. The Safety Team reserves the right to not act on a report. -All reports will be handled with utmost discretion and confidentially. +## Confidentiality + +All reports, and any additional information included, are only shared with the team of safety officers (and possibly members of the core team, in case the safety officer is in violation of the CoC). We will respect confidentiality requests for the purpose of protecting victims of abuse. + +We will not name harassment victims, beyond discussions between the safety officer and members of the nf-core team, without the explicit consent of the individuals involved. + +## Enforcement + +Actions taken by the nf-core’s Safety Team may include, but are not limited to: + +- Asking anyone to stop a behaviour. +- Asking anyone to leave the event and online spaces either temporarily, for the remainder of the event, or permanently. +- Removing access to the gather.town and Slack, either temporarily or permanently. +- Communicating to all participants to reinforce our expectations for conduct and remind what is unacceptable behaviour; this may be public for practical reasons. +- Communicating to all participants that an incident has taken place and how we will act or have acted — this may be for the purpose of letting event participants know we are aware of and dealing with the incident. +- Banning anyone from participating in nf-core-managed spaces, future events, and activities, either temporarily or permanently. +- No action. ## Attribution and Acknowledgements @@ -106,6 +161,22 @@ All reports will be handled with utmost discretion and confidentially. ## Changelog -### v1.0 - March 12th, 2021 +### v1.4 - February 8th, 2022 + +- Included a new member of the Safety Team. Corrected a typographical error in the text. + +### v1.3 - December 10th, 2021 + +- Added a statement that the CoC applies to nf-core gather.town workspaces. Corrected typographical errors in the text. + +### v1.2 - November 12th, 2021 + +- Removed information specific to reporting CoC violations at the Hackathon in October 2021. + +### v1.1 - October 14th, 2021 + +- Updated with names of new Safety Officers and specific information for the hackathon in October 2021. + +### v1.0 - March 15th, 2021 - Complete rewrite from original [Contributor Covenant](http://contributor-covenant.org/) CoC. From a9dc80f1bc47d4dd318012d546168511025a6e4c Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 9 Oct 2023 14:22:28 +0200 Subject: [PATCH 095/112] feat: update to pixelator 0.14.0 --- modules/local/pixelator/collect_metadata.nf | 4 ++-- modules/local/pixelator/list_options.nf | 4 ++-- modules/local/pixelator/single-cell/amplicon/main.nf | 4 ++-- modules/local/pixelator/single-cell/analysis/main.nf | 4 ++-- modules/local/pixelator/single-cell/annotate/main.nf | 4 ++-- modules/local/pixelator/single-cell/collapse/main.nf | 4 ++-- modules/local/pixelator/single-cell/demux/main.nf | 4 ++-- modules/local/pixelator/single-cell/graph/main.nf | 4 ++-- modules/local/pixelator/single-cell/qc/main.nf | 4 ++-- modules/local/pixelator/single-cell/report/main.nf | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/modules/local/pixelator/collect_metadata.nf b/modules/local/pixelator/collect_metadata.nf index 09e48e60..1db77f93 100644 --- a/modules/local/pixelator/collect_metadata.nf +++ b/modules/local/pixelator/collect_metadata.nf @@ -8,8 +8,8 @@ process PIXELATOR_COLLECT_METADATA { label 'process_single' cache false - conda "bioconda::pixelator=0.13.1" - container "biocontainers/pixelator:0.13.1--pyh7cba7a3_0" + conda "bioconda::pixelator=0.14.0" + container "biocontainers/pixelator:0.14.0--pyh7cba7a3_0" input: diff --git a/modules/local/pixelator/list_options.nf b/modules/local/pixelator/list_options.nf index b7f59850..9515d172 100644 --- a/modules/local/pixelator/list_options.nf +++ b/modules/local/pixelator/list_options.nf @@ -2,8 +2,8 @@ process PIXELATOR_LIST_OPTIONS { label 'process_single' - conda "bioconda::pixelator=0.13.1" - container "biocontainers/pixelator:0.13.1--pyh7cba7a3_0" + conda "bioconda::pixelator=0.14.0" + container "biocontainers/pixelator:0.14.0--pyh7cba7a3_0" output: path "design_options.txt" , emit: designs diff --git a/modules/local/pixelator/single-cell/amplicon/main.nf b/modules/local/pixelator/single-cell/amplicon/main.nf index f22d3fb6..627b43a3 100644 --- a/modules/local/pixelator/single-cell/amplicon/main.nf +++ b/modules/local/pixelator/single-cell/amplicon/main.nf @@ -3,8 +3,8 @@ process PIXELATOR_AMPLICON { label 'process_low' - conda "bioconda::pixelator=0.13.1" - container "biocontainers/pixelator:0.13.1--pyh7cba7a3_0" + conda "bioconda::pixelator=0.14.0" + container "biocontainers/pixelator:0.14.0--pyh7cba7a3_0" input: tuple val(meta), path(reads) diff --git a/modules/local/pixelator/single-cell/analysis/main.nf b/modules/local/pixelator/single-cell/analysis/main.nf index 6d80647e..0efb3d5d 100644 --- a/modules/local/pixelator/single-cell/analysis/main.nf +++ b/modules/local/pixelator/single-cell/analysis/main.nf @@ -3,8 +3,8 @@ process PIXELATOR_ANALYSIS { label 'process_medium' - conda "bioconda::pixelator=0.13.1" - container "biocontainers/pixelator:0.13.1--pyh7cba7a3_0" + conda "bioconda::pixelator=0.14.0" + container "biocontainers/pixelator:0.14.0--pyh7cba7a3_0" input: tuple val(meta), path(data) diff --git a/modules/local/pixelator/single-cell/annotate/main.nf b/modules/local/pixelator/single-cell/annotate/main.nf index d8d508e6..4741aede 100644 --- a/modules/local/pixelator/single-cell/annotate/main.nf +++ b/modules/local/pixelator/single-cell/annotate/main.nf @@ -3,8 +3,8 @@ process PIXELATOR_ANNOTATE { label 'process_medium' - conda "bioconda::pixelator=0.13.1" - container "biocontainers/pixelator:0.13.1--pyh7cba7a3_0" + conda "bioconda::pixelator=0.14.0" + container "biocontainers/pixelator:0.14.0--pyh7cba7a3_0" input: tuple val(meta), path(dataset), path(panel_file), val(panel) diff --git a/modules/local/pixelator/single-cell/collapse/main.nf b/modules/local/pixelator/single-cell/collapse/main.nf index a29f2c5f..2e9fdbfb 100644 --- a/modules/local/pixelator/single-cell/collapse/main.nf +++ b/modules/local/pixelator/single-cell/collapse/main.nf @@ -2,8 +2,8 @@ process PIXELATOR_COLLAPSE { tag "$meta.id" label 'process_medium' - conda "bioconda::pixelator=0.13.1" - container "biocontainers/pixelator:0.13.1--pyh7cba7a3_0" + conda "bioconda::pixelator=0.14.0" + container "biocontainers/pixelator:0.14.0--pyh7cba7a3_0" input: tuple val(meta), path(reads), path(panel_file), val(panel) diff --git a/modules/local/pixelator/single-cell/demux/main.nf b/modules/local/pixelator/single-cell/demux/main.nf index 68529e90..a404caa7 100644 --- a/modules/local/pixelator/single-cell/demux/main.nf +++ b/modules/local/pixelator/single-cell/demux/main.nf @@ -3,8 +3,8 @@ process PIXELATOR_DEMUX { label 'process_medium' - conda "bioconda::pixelator=0.13.1" - container "biocontainers/pixelator:0.13.1--pyh7cba7a3_0" + conda "bioconda::pixelator=0.14.0" + container "biocontainers/pixelator:0.14.0--pyh7cba7a3_0" input: tuple val(meta), path(reads), path(panel_file), val(panel) diff --git a/modules/local/pixelator/single-cell/graph/main.nf b/modules/local/pixelator/single-cell/graph/main.nf index e1bef04d..22f9066f 100644 --- a/modules/local/pixelator/single-cell/graph/main.nf +++ b/modules/local/pixelator/single-cell/graph/main.nf @@ -3,8 +3,8 @@ process PIXELATOR_GRAPH { label 'process_medium' - conda "bioconda::pixelator=0.13.1" - container "biocontainers/pixelator:0.13.1--pyh7cba7a3_0" + conda "bioconda::pixelator=0.14.0" + container "biocontainers/pixelator:0.14.0--pyh7cba7a3_0" input: tuple val(meta), path(edge_list) diff --git a/modules/local/pixelator/single-cell/qc/main.nf b/modules/local/pixelator/single-cell/qc/main.nf index 642ad660..d6a22899 100644 --- a/modules/local/pixelator/single-cell/qc/main.nf +++ b/modules/local/pixelator/single-cell/qc/main.nf @@ -3,8 +3,8 @@ process PIXELATOR_QC { label 'process_medium' - conda "bioconda::pixelator=0.13.1" - container "biocontainers/pixelator:0.13.1--pyh7cba7a3_0" + conda "bioconda::pixelator=0.14.0" + container "biocontainers/pixelator:0.14.0--pyh7cba7a3_0" input: tuple val(meta), path(reads) diff --git a/modules/local/pixelator/single-cell/report/main.nf b/modules/local/pixelator/single-cell/report/main.nf index b3a3505c..91650e87 100644 --- a/modules/local/pixelator/single-cell/report/main.nf +++ b/modules/local/pixelator/single-cell/report/main.nf @@ -3,8 +3,8 @@ process PIXELATOR_REPORT { label 'process_low' - conda "bioconda::pixelator=0.13.1" - container "biocontainers/pixelator:0.13.1--pyh7cba7a3_0" + conda "bioconda::pixelator=0.14.0" + container "biocontainers/pixelator:0.14.0--pyh7cba7a3_0" input: tuple val(meta), path(panel_file), val(panel) From 626f93638c6f64863ac4fafb2770f043076e3418 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 9 Oct 2023 14:30:23 +0200 Subject: [PATCH 096/112] feat: support remote dump_parameters path --- .nf-core.yml | 2 ++ lib/NfcoreTemplate.groovy | 15 ++++++++------- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/.nf-core.yml b/.nf-core.yml index b80d615a..7fe39026 100644 --- a/.nf-core.yml +++ b/.nf-core.yml @@ -5,3 +5,5 @@ lint: files_exist: - assets/multiqc_config.yml - conf/igenomes.config + files_unchanged: + - lib/NfcoreTemplate.groovy diff --git a/lib/NfcoreTemplate.groovy b/lib/NfcoreTemplate.groovy index 01b8653d..0cccbf84 100755 --- a/lib/NfcoreTemplate.groovy +++ b/lib/NfcoreTemplate.groovy @@ -4,6 +4,7 @@ import org.yaml.snakeyaml.Yaml import groovy.json.JsonOutput +import nextflow.extension.FilesEx class NfcoreTemplate { @@ -227,15 +228,15 @@ class NfcoreTemplate { // Dump pipeline parameters in a json file // public static void dump_parameters(workflow, params) { - def output_d = new File("${params.outdir}/pipeline_info/") - if (!output_d.exists()) { - output_d.mkdirs() - } - def timestamp = new java.util.Date().format( 'yyyy-MM-dd_HH-mm-ss') - def output_pf = new File(output_d, "params_${timestamp}.json") + def filename = "params_${timestamp}.json" + def temp_pf = new File(workflow.launchDir.toString(), ".${filename}") def jsonStr = JsonOutput.toJson(params) - output_pf.text = JsonOutput.prettyPrint(jsonStr) + temp_pf.text = JsonOutput.prettyPrint(jsonStr) + + def destination = "${params.outdir}/pipeline_info/params_${timestamp}.json" + FilesEx.copyTo(temp_pf.toPath(), destination) + temp_pf.delete() } // From b078dff2c7defd675e0ac845b54b26486875790e Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 11 Oct 2023 14:35:28 +0200 Subject: [PATCH 097/112] fix: allow mail without multiqc report --- lib/NfcoreTemplate.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/NfcoreTemplate.groovy b/lib/NfcoreTemplate.groovy index 0cccbf84..0498726a 100755 --- a/lib/NfcoreTemplate.groovy +++ b/lib/NfcoreTemplate.groovy @@ -147,7 +147,7 @@ class NfcoreTemplate { } catch (all) { // Catch failures and try with plaintext def mail_cmd = [ 'mail', '-s', subject, '--content-type=text/html', email_address ] - if ( mqc_report.size() <= max_multiqc_email_size.toBytes() ) { + if ( mqc_report != null && mqc_report.size() <= max_multiqc_email_size.toBytes() ) { mail_cmd += [ '-A', mqc_report ] } mail_cmd.execute() << email_html From 65158914c157cb6264fd9443d64d5a424dd1b8fc Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 12 Oct 2023 14:32:13 +0200 Subject: [PATCH 098/112] add reports to tower.yml --- tower.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tower.yml b/tower.yml index 787aedfe..c67d4691 100644 --- a/tower.yml +++ b/tower.yml @@ -1,5 +1,3 @@ reports: - multiqc_report.html: - display: "MultiQC HTML report" - samplesheet.csv: - display: "Auto-created samplesheet with collated metadata and FASTQ paths" + "**/report/*_report.html": + display: "Pixelator HTML report" From 8a2320d34515e3a37209f4b4a43752acfa2fe611 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 16 Oct 2023 16:23:40 +0200 Subject: [PATCH 099/112] fix remote outdir in NfCoreTemplate.groovy See: https://github.com/nf-core/tools/pull/2465 --- lib/NfcoreTemplate.groovy | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/lib/NfcoreTemplate.groovy b/lib/NfcoreTemplate.groovy index 0498726a..66ad49d4 100755 --- a/lib/NfcoreTemplate.groovy +++ b/lib/NfcoreTemplate.groovy @@ -156,14 +156,16 @@ class NfcoreTemplate { } // Write summary e-mail HTML to a file - def output_d = new File("${params.outdir}/pipeline_info/") - if (!output_d.exists()) { - output_d.mkdirs() - } - def output_hf = new File(output_d, "pipeline_report.html") + def output_hf = new File(workflow.launchDir.toString(), ".pipeline_report.html") output_hf.withWriter { w -> w << email_html } - def output_tf = new File(output_d, "pipeline_report.txt") + FilesEx.copyTo(output_hf.toPath(), "${params.outdir}/pipeline_info/pipeline_report.html"); + output_hf.delete() + + // Write summary e-mail TXT to a file + def output_tf = new File(workflow.launchDir.toString(), ".pipeline_report.txt") output_tf.withWriter { w -> w << email_txt } + FilesEx.copyTo(output_tf.toPath(), "${params.outdir}/pipeline_info/pipeline_report.txt"); + output_tf.delete() } // @@ -234,8 +236,7 @@ class NfcoreTemplate { def jsonStr = JsonOutput.toJson(params) temp_pf.text = JsonOutput.prettyPrint(jsonStr) - def destination = "${params.outdir}/pipeline_info/params_${timestamp}.json" - FilesEx.copyTo(temp_pf.toPath(), destination) + FilesEx.copyTo(temp_pf.toPath(), "${params.outdir}/pipeline_info/params_${timestamp}.json") temp_pf.delete() } From 04f286ff13d16f789ac024ac5e2e5763c7bc8110 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 16 Oct 2023 16:24:00 +0200 Subject: [PATCH 100/112] update metromap --- README.md | 2 +- ...ixelator_metromap.svg => nf-core-pixelator-metromap.svg} | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) rename docs/images/{nf_core_pixelator_metromap.svg => nf-core-pixelator-metromap.svg} (93%) diff --git a/README.md b/README.md index 84677874..129a8386 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ **nf-core/pixelator** is a bioinformatics best-practice analysis pipeline for analysis of Molecular Pixelation assays. It takes a samplesheet as input and will process your data using `pixelator` to produce final antibody counts. -![](./docs/images/nf_core_pixelator_metromap.svg) +![](./docs/images/nf-core-pixelator-metromap.svg) 1. Build amplicon from input reads ([`pixelator amplicon`](https://github.com/PixelgenTechnologies/pixelator)) 2. Read QC and filtering, correctness of the pixel binding sequence sequences ([`pixelator preqc | pixelator adapterqc`](https://github.com/PixelgenTechnologies/pixelator)) diff --git a/docs/images/nf_core_pixelator_metromap.svg b/docs/images/nf-core-pixelator-metromap.svg similarity index 93% rename from docs/images/nf_core_pixelator_metromap.svg rename to docs/images/nf-core-pixelator-metromap.svg index 9232030d..dee01a7d 100644 --- a/docs/images/nf_core_pixelator_metromap.svg +++ b/docs/images/nf-core-pixelator-metromap.svg @@ -27,7 +27,7 @@ - + @@ -38,8 +38,8 @@ - - + + From 29ed89e48bb1ab5e23b972b6a99ab65d9a453191 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 16 Oct 2023 16:24:52 +0200 Subject: [PATCH 101/112] documentation tweaks --- docs/output.md | 118 ++++++++++++++++++++++++------------------------ docs/usage.md | 58 +++++++++++++++++++----- samplesheet.csv | 3 ++ 3 files changed, 109 insertions(+), 70 deletions(-) create mode 100644 samplesheet.csv diff --git a/docs/output.md b/docs/output.md index b64441bf..abde788a 100644 --- a/docs/output.md +++ b/docs/output.md @@ -23,16 +23,12 @@ The pipeline consists of the following steps: ### Preprocessing -The preprocessing step uses `pixelator single-cell concatenate` to create a full amplicon sequence from both single-end and paired-end data. -It returns a single fastq per sample containing fixed length amplicons. -This step will also calculate Q30 quality scores for different regions of the library. -
    Output files - `pixelator` - - `concatenate` + - `amplicon` - `.merged.fastq.gz`: Combine R1 and R2 reads into full amplicon reads and calculate Q30 scores for the amplicon regions. @@ -40,21 +36,15 @@ This step will also calculate Q30 quality scores for different regions of the li - `.meta.json`: Command invocation metadata. - `logs` - - `.pixelator-concatenate.log`: pixelator log output. + - `.pixelator-amplicon.log`: pixelator log output.
    -### Quality control - -Quality control is performed using `pixelator single-cell preqc` and `pixelator single-cell adapterqc`. - -The preqc stage performs QC and quality filtering of the raw sequencing data. -It also generates a QC report in HTML and JSON formats. It saves processed reads as well as reads that were -discarded (i.e. were too short, had too many Ns, or too low quality, etc.). Internally `preqc` -uses [Fastp](https://github.com/OpenGene/fastp), and `adapterqc` -uses [Cutadapt](https://cutadapt.readthedocs.io/en/stable/). +The preprocessing step uses `pixelator single-cell amplicon` to create full-length amplicon sequences from both single-end and paired-end data. +It returns a single fastq file per sample containing fixed length amplicons. +This step will also calculate Q30 quality scores for different regions of the library. -The `adapterqc` stage checks for the presence and correctness of the pixel binding sequences. It also generates a QC report in JSON format. It saves processed reads as well as discarded reads (i.e. reads that did not have a match for both pixel binding sequences). +### Quality control
    Output files @@ -78,11 +68,17 @@ The `adapterqc` stage checks for the presence and correctness of the pixel bindi
    -### Demultiplexing +Quality control is performed using `pixelator single-cell preqc` and `pixelator single-cell adapterqc`. -The `pixelator single-cell demux` command assigns a marker (barcode) to each read. It also generates QC report in -JSON format. It saves processed reads (one per antibody) as well as discarded reads with no match to the -given barcodes/antibodies. +The preqc stage performs QC and quality filtering of the raw sequencing data. +It also generates a QC report in HTML and JSON formats. It saves processed reads as well as reads that were +discarded (i.e. were too short, had too many Ns, or too low quality, etc.). Internally `preqc` +uses [Fastp](https://github.com/OpenGene/fastp), and `adapterqc` +uses [Cutadapt](https://cutadapt.readthedocs.io/en/stable/). + +The `adapterqc` stage checks for the presence and correctness of the pixel binding sequences. It also generates a QC report in JSON format. It saves processed reads as well as discarded reads (i.e. reads that did not have a match for both pixel binding sequences). + +### Demultiplexing
    Output files @@ -101,16 +97,11 @@ given barcodes/antibodies.
    -### Duplicate removal and error correction - -This step uses the `pixelator single-cell collapse` command. - -The `collapse` command removes duplicate reads and performs error correction. -This is achieved using the unique pixel identifier and unique molecular identifier sequences to check for -uniqueness, collapse and compute a read count. The command generates a QC report in JSON format. -Errors are allowed when collapsing reads if `--algorithm` is set to `adjacency` (this is the default option). +The `pixelator single-cell demux` command assigns a marker (barcode) to each read. It also generates QC report in +JSON format. It saves processed reads (one per antibody) as well as discarded reads with no match to the +given barcodes/antibodies. -The output format of this command is an edge list in CSV format. +### Duplicate removal and error correction
    Output files @@ -128,17 +119,16 @@ The output format of this command is an edge list in CSV format.
    -### Compute connected components +This step uses the `pixelator single-cell collapse` command. -This step uses the `pixelator single-cell graph` command. -The input is the edge list dataframe (CSV) generated in the collapse step and after filtering it -by count (`--graph_min_count`), the connected components of the graph (graphs) are computed and -added to the edge list in a column called "component". +The `collapse` command removes duplicate reads and performs error correction. +This is achieved using the unique pixel identifier and unique molecular identifier sequences to check for +uniqueness, collapse and compute a read count. The command generates a QC report in JSON format. +Errors are allowed when collapsing reads if `--algorithm` is set to `adjacency` (this is the default option). -The graph command has the option to recover components (technical multiplets) into smaller -components using community detection to find and remove problematic edges. -(See `--multiplet_recovery`). The information to keep track of the original and -new (recovered) components are stored in a file (components_recovered.csv). +The output format of this command is an edge list in CSV format. + +### Compute connected components
    Output files @@ -162,13 +152,17 @@ new (recovered) components are stored in a file (components_recovered.csv).
    -### Cell-calling, filtering, and annotation +This step uses the `pixelator single-cell graph` command. +The input is the edge list dataframe (CSV) generated in the collapse step and after filtering it +by count (`--graph_min_count`), the connected components of the graph (graphs) are computed and +added to the edge list in a column called "component". -This step uses the `pixelator single-cell annotate` command. +The graph command has the option to recover components (technical multiplets) into smaller +components using community detection to find and remove problematic edges. +(See `--multiplet_recovery`). The information to keep track of the original and +new (recovered) components are stored in a file (components_recovered.csv). -The annotate command takes as input the edge list (CSV) file generated in the graph command. It parses, and filters the -edgelist to find putative cells, and it will generate a pxl file containing the edgelist, and an -(AnnData object)[https://anndata.readthedocs.io/en/latest/] as well as some useful medatadata. +### Cell-calling, filtering, and annotation
    Output files @@ -186,18 +180,13 @@ edgelist to find putative cells, and it will generate a pxl file containing the - `.pixelator-annotate.log`: pixelator log output.
    -### Downstream analysis - -This step uses the `pixelator single-cell analysis` command. -Downstream analysis is performed on the `pxl` file generated by the previous stage. -The results of the analysis is added to the pxl file. - -Currently, the following analysis can be performed (if enabled): +This step uses the `pixelator single-cell annotate` command. -- polarization scores (enable with `--compute_polarization`) -- co-localization scores (enable with `--compute_colocalization`) +The annotate command takes as input the edge list (CSV) file generated in the graph command. It parses, and filters the +edgelist to find putative cells, and it will generate a pxl file containing the edgelist, and an +(AnnData object)[https://anndata.readthedocs.io/en/latest/] as well as some useful medatadata. -This step can be skipped using the `--skip_analysis` option. +### Downstream analysis
    Output files @@ -215,13 +204,18 @@ This step can be skipped using the `--skip_analysis` option.
    -### Generate reports +This step uses the `pixelator single-cell analysis` command. +Downstream analysis is performed on the `pxl` file generated by the previous stage. +The results of the analysis is added to the pxl file. -This step uses the `pixelator single-cell report` command. -This step will collect metrics and outputs generated by previous stages -and generate a report in HTML format for each sample. +Currently, the following analysis can be performed (if enabled): -This step can be skipped using the `--skip_report` option. +- polarization scores (enable with `--compute_polarization`) +- co-localization scores (enable with `--compute_colocalization`) + +This step can be skipped using the `--skip_analysis` option. + +### Generate reports
    Output files @@ -234,6 +228,14 @@ This step can be skipped using the `--skip_report` option.
    +This step uses the `pixelator single-cell report` command. +This step will collect metrics and outputs generated by previous stages +and generate a report in HTML format for each sample. + +This step can be skipped using the `--skip_report` option. + +More information on the report can be found in the pixelator documentation [here](https://software.pixelgen.com/pixelator/outputs/web-report/) + ### Pipeline information
    diff --git a/docs/usage.md b/docs/usage.md index 64f20d54..2ffee400 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -37,14 +37,13 @@ uropod_stimulated,D21,human-sc-immunology-spatial-proteomics,uropod_stimulated_S Columns not defined in the table below are ignored by the pipeline but can be useful to add extra information for downstream processing. -| Column | Description | -| ------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `sample` | Custom sample name. This entry will be identical for multiple sequencing libraries/runs from the same sample. Spaces in sample names are automatically converted to underscores (`_`). | -| `design` | The name of the pixelator design configuration. | -| `panel` | Name of the panel to use. | -| `panel_file` | Path to a CSV file containing a custom panel. | -| `fastq_1` | Path to FastQ file for Illumina short reads 1. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | -| `fastq_2` | Path to FastQ file for Illumina short reads 2. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | +| Column | Required | Description | +| ----------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `sample` | Yes | Custom sample name. This entry will be identical for multiple sequencing libraries/runs from the same sample. Spaces in sample names are automatically converted to underscores (`_`). | +| `design` | Yes | The name of the pixelator design configuration. | +| `panel`
    or
    `panel_file` | Yes | Name of the panel to use.
    or
    Path to a CSV file containing a custom panel. | +| `fastq_1` | Yes | Path to FastQ file for Illumina short reads 1. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". | +| `fastq_2` | No | Path to FastQ file for Illumina short reads 2. File has to be gzipped and have the extension ".fastq.gz" or ".fq.gz". Parameter only used if you are running paired-end. | The `panel` and `panel_file` options are mutually exclusive. If both are specified, the pipeline will throw an error. One of them has to be specified. @@ -56,10 +55,10 @@ The pipeline will auto-detect whether a sample is single- or paired-end based on The `sample` identifiers have to be the same when you have re-sequenced the same sample more than once e.g. to increase sequencing depth. The pipeline will concatenate the raw reads before performing any downstream analysis. Below is an example for the same sample sequenced across 3 lanes: ```csv -sample,design,panel,panel_file,fastq_1,fastq_2 -uropod_control_1,D21,human-sc-immunology-spatial-proteomics,,uropod_control_S1_L001_R1_001.fastq.gz,uropod_control_S1_L001_R2_001.fastq.gz -uropod_control_1,D21,human-sc-immunology-spatial-proteomics,,uropod_control_S1_L002_R1_001.fastq.gz,uropod_control_S1_L002_R2_001.fastq.gz -uropod_control_1,D21,human-sc-immunology-spatial-proteomics,,uropod_control_S1_L003_R1_001.fastq.gz,uropod_control_S1_L003_R2_001.fastq.gz +sample,design,panel,fastq_1,fastq_2 +uropod_control_1,D21,human-sc-immunology-spatial-proteomics,uropod_control_S1_L001_R1_001.fastq.gz,uropod_control_S1_L001_R2_001.fastq.gz +uropod_control_1,D21,human-sc-immunology-spatial-proteomics,uropod_control_S1_L002_R1_001.fastq.gz,uropod_control_S1_L002_R2_001.fastq.gz +uropod_control_1,D21,human-sc-immunology-spatial-proteomics,uropod_control_S1_L003_R1_001.fastq.gz,uropod_control_S1_L003_R2_001.fastq.gz ``` ### Relative paths @@ -97,6 +96,41 @@ For example, using the same samplesheet as above, but with the samplesheet on th nextflow run nf-core/pixelator --input samplesheet.csv --input_basedir s3://my-company-data/experiment-1/ ``` +### Design + +The `design` column specifies the name of the pixelator assay design configuration to use. + +A list of available designs can be listed by running following command: + +```shell +pixelator single-cell --list-designs +``` + +Currently, a single design is available: + +- `D21` + +### Panels + +The panel file contains all information used to link antibodies barcodes to their respective targets. +Panel files can be specified in two ways: + +- Using a predefined panel name to use the default build in panels. +- Passing a csv file with a customized panel. + +Predefined panels can be passed in the `panel` field. Custom panels can be passed in the `panel_file` field. +Every sample should have either `panel` or `panel_file` specified. + +A list of available panels can be listed by running following command: + +```shell +pixelator single-cell --list-panels +``` + +Currently, a single built-in panel is available: + +- `human-sc-immunology-spatial-proteomics` + ## Running the pipeline The typical command for running the pipeline is as follows: diff --git a/samplesheet.csv b/samplesheet.csv new file mode 100644 index 00000000..e336439f --- /dev/null +++ b/samplesheet.csv @@ -0,0 +1,3 @@ +sample,design,panel,fastq_1,fastq_2 +uropod_control,D21,human-sc-immunology-spatial-proteomics,uropod_control_300k_R1_001.fastq.gz,uropod_control_300k_R2_001.fastq.gz +pbmcs_unstimulated,D21,human-sc-immunology-spatial-proteomics,Sample01_human_pbmcs_unstimulated_200k_R1_001.fastq.gz,Sample01_human_pbmcs_unstimulated_200k_R2_001.fastq.gz From 2b6655325cd556233cdfc7ccb1b94ddc57462806 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 17 Oct 2023 09:47:06 +0200 Subject: [PATCH 102/112] update pixelator version to 0.15.0 --- modules/local/pixelator/collect_metadata.nf | 4 ++-- modules/local/pixelator/list_options.nf | 4 ++-- modules/local/pixelator/single-cell/amplicon/main.nf | 4 ++-- modules/local/pixelator/single-cell/analysis/main.nf | 4 ++-- modules/local/pixelator/single-cell/annotate/main.nf | 4 ++-- modules/local/pixelator/single-cell/collapse/main.nf | 4 ++-- modules/local/pixelator/single-cell/demux/main.nf | 4 ++-- modules/local/pixelator/single-cell/graph/main.nf | 4 ++-- modules/local/pixelator/single-cell/qc/main.nf | 4 ++-- modules/local/pixelator/single-cell/report/main.nf | 4 ++-- 10 files changed, 20 insertions(+), 20 deletions(-) diff --git a/modules/local/pixelator/collect_metadata.nf b/modules/local/pixelator/collect_metadata.nf index 1db77f93..7bf4d7ad 100644 --- a/modules/local/pixelator/collect_metadata.nf +++ b/modules/local/pixelator/collect_metadata.nf @@ -8,8 +8,8 @@ process PIXELATOR_COLLECT_METADATA { label 'process_single' cache false - conda "bioconda::pixelator=0.14.0" - container "biocontainers/pixelator:0.14.0--pyh7cba7a3_0" + conda "bioconda::pixelator=0.15.0" + container "biocontainers/pixelator:0.15.0--pyh7cba7a3_0" input: diff --git a/modules/local/pixelator/list_options.nf b/modules/local/pixelator/list_options.nf index 9515d172..0bcd4935 100644 --- a/modules/local/pixelator/list_options.nf +++ b/modules/local/pixelator/list_options.nf @@ -2,8 +2,8 @@ process PIXELATOR_LIST_OPTIONS { label 'process_single' - conda "bioconda::pixelator=0.14.0" - container "biocontainers/pixelator:0.14.0--pyh7cba7a3_0" + conda "bioconda::pixelator=0.15.0" + container "biocontainers/pixelator:0.15.0--pyh7cba7a3_0" output: path "design_options.txt" , emit: designs diff --git a/modules/local/pixelator/single-cell/amplicon/main.nf b/modules/local/pixelator/single-cell/amplicon/main.nf index 627b43a3..e27061b5 100644 --- a/modules/local/pixelator/single-cell/amplicon/main.nf +++ b/modules/local/pixelator/single-cell/amplicon/main.nf @@ -3,8 +3,8 @@ process PIXELATOR_AMPLICON { label 'process_low' - conda "bioconda::pixelator=0.14.0" - container "biocontainers/pixelator:0.14.0--pyh7cba7a3_0" + conda "bioconda::pixelator=0.15.0" + container "biocontainers/pixelator:0.15.0--pyh7cba7a3_0" input: tuple val(meta), path(reads) diff --git a/modules/local/pixelator/single-cell/analysis/main.nf b/modules/local/pixelator/single-cell/analysis/main.nf index 0efb3d5d..acd59e7a 100644 --- a/modules/local/pixelator/single-cell/analysis/main.nf +++ b/modules/local/pixelator/single-cell/analysis/main.nf @@ -3,8 +3,8 @@ process PIXELATOR_ANALYSIS { label 'process_medium' - conda "bioconda::pixelator=0.14.0" - container "biocontainers/pixelator:0.14.0--pyh7cba7a3_0" + conda "bioconda::pixelator=0.15.0" + container "biocontainers/pixelator:0.15.0--pyh7cba7a3_0" input: tuple val(meta), path(data) diff --git a/modules/local/pixelator/single-cell/annotate/main.nf b/modules/local/pixelator/single-cell/annotate/main.nf index 4741aede..61d01762 100644 --- a/modules/local/pixelator/single-cell/annotate/main.nf +++ b/modules/local/pixelator/single-cell/annotate/main.nf @@ -3,8 +3,8 @@ process PIXELATOR_ANNOTATE { label 'process_medium' - conda "bioconda::pixelator=0.14.0" - container "biocontainers/pixelator:0.14.0--pyh7cba7a3_0" + conda "bioconda::pixelator=0.15.0" + container "biocontainers/pixelator:0.15.0--pyh7cba7a3_0" input: tuple val(meta), path(dataset), path(panel_file), val(panel) diff --git a/modules/local/pixelator/single-cell/collapse/main.nf b/modules/local/pixelator/single-cell/collapse/main.nf index 2e9fdbfb..b4fe3404 100644 --- a/modules/local/pixelator/single-cell/collapse/main.nf +++ b/modules/local/pixelator/single-cell/collapse/main.nf @@ -2,8 +2,8 @@ process PIXELATOR_COLLAPSE { tag "$meta.id" label 'process_medium' - conda "bioconda::pixelator=0.14.0" - container "biocontainers/pixelator:0.14.0--pyh7cba7a3_0" + conda "bioconda::pixelator=0.15.0" + container "biocontainers/pixelator:0.15.0--pyh7cba7a3_0" input: tuple val(meta), path(reads), path(panel_file), val(panel) diff --git a/modules/local/pixelator/single-cell/demux/main.nf b/modules/local/pixelator/single-cell/demux/main.nf index a404caa7..b8ae8caf 100644 --- a/modules/local/pixelator/single-cell/demux/main.nf +++ b/modules/local/pixelator/single-cell/demux/main.nf @@ -3,8 +3,8 @@ process PIXELATOR_DEMUX { label 'process_medium' - conda "bioconda::pixelator=0.14.0" - container "biocontainers/pixelator:0.14.0--pyh7cba7a3_0" + conda "bioconda::pixelator=0.15.0" + container "biocontainers/pixelator:0.15.0--pyh7cba7a3_0" input: tuple val(meta), path(reads), path(panel_file), val(panel) diff --git a/modules/local/pixelator/single-cell/graph/main.nf b/modules/local/pixelator/single-cell/graph/main.nf index 22f9066f..52bf648e 100644 --- a/modules/local/pixelator/single-cell/graph/main.nf +++ b/modules/local/pixelator/single-cell/graph/main.nf @@ -3,8 +3,8 @@ process PIXELATOR_GRAPH { label 'process_medium' - conda "bioconda::pixelator=0.14.0" - container "biocontainers/pixelator:0.14.0--pyh7cba7a3_0" + conda "bioconda::pixelator=0.15.0" + container "biocontainers/pixelator:0.15.0--pyh7cba7a3_0" input: tuple val(meta), path(edge_list) diff --git a/modules/local/pixelator/single-cell/qc/main.nf b/modules/local/pixelator/single-cell/qc/main.nf index d6a22899..781663b8 100644 --- a/modules/local/pixelator/single-cell/qc/main.nf +++ b/modules/local/pixelator/single-cell/qc/main.nf @@ -3,8 +3,8 @@ process PIXELATOR_QC { label 'process_medium' - conda "bioconda::pixelator=0.14.0" - container "biocontainers/pixelator:0.14.0--pyh7cba7a3_0" + conda "bioconda::pixelator=0.15.0" + container "biocontainers/pixelator:0.15.0--pyh7cba7a3_0" input: tuple val(meta), path(reads) diff --git a/modules/local/pixelator/single-cell/report/main.nf b/modules/local/pixelator/single-cell/report/main.nf index 91650e87..61d06169 100644 --- a/modules/local/pixelator/single-cell/report/main.nf +++ b/modules/local/pixelator/single-cell/report/main.nf @@ -3,8 +3,8 @@ process PIXELATOR_REPORT { label 'process_low' - conda "bioconda::pixelator=0.14.0" - container "biocontainers/pixelator:0.14.0--pyh7cba7a3_0" + conda "bioconda::pixelator=0.15.0" + container "biocontainers/pixelator:0.15.0--pyh7cba7a3_0" input: tuple val(meta), path(panel_file), val(panel) From 2b20b04d01bc89b338226d8ed7d3c05c50b28a61 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 17 Oct 2023 10:03:00 +0200 Subject: [PATCH 103/112] update example samplesheet --- assets/samplesheet.csv | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/assets/samplesheet.csv b/assets/samplesheet.csv index f6644d80..e50611a7 100644 --- a/assets/samplesheet.csv +++ b/assets/samplesheet.csv @@ -1,3 +1,4 @@ sample,design,panel,fastq_1,fastq_2 -test_data_se,D12,/path/to/test_panel.csv,/path/to/test_data.fastq.gz, -uropod_control,D21,to/UNO_D21_conjV21.csv,/path/to/uropod_control_300k_S1_R1_001.fastq.gz,uropod_control_300k_S1_R2_001.fastq.gz +uropod_control_1,D21,human-sc-immunology-spatial-proteomics,uropod_control_S1_L001_R1_001.fastq.gz,uropod_control_S1_L001_R2_001.fastq.gz +uropod_control_1,D21,human-sc-immunology-spatial-proteomics,uropod_control_S1_L002_R1_001.fastq.gz,uropod_control_S1_L002_R2_001.fastq.gz +uropod_control_1,D21,human-sc-immunology-spatial-proteomics,uropod_control_S1_L003_R1_001.fastq.gz,uropod_control_S1_L003_R2_001.fastq.gz From c80fa77d5516e7e0235506350afbb267483728c9 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 17 Oct 2023 10:03:14 +0200 Subject: [PATCH 104/112] usage doc tweaks --- docs/output.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/output.md b/docs/output.md index abde788a..0013ed26 100644 --- a/docs/output.md +++ b/docs/output.md @@ -160,7 +160,7 @@ added to the edge list in a column called "component". The graph command has the option to recover components (technical multiplets) into smaller components using community detection to find and remove problematic edges. (See `--multiplet_recovery`). The information to keep track of the original and -new (recovered) components are stored in a file (components_recovered.csv). +newly recovered components are stored in a file (components_recovered.csv). ### Cell-calling, filtering, and annotation @@ -184,7 +184,7 @@ This step uses the `pixelator single-cell annotate` command. The annotate command takes as input the edge list (CSV) file generated in the graph command. It parses, and filters the edgelist to find putative cells, and it will generate a pxl file containing the edgelist, and an -(AnnData object)[https://anndata.readthedocs.io/en/latest/] as well as some useful medatadata. +(AnnData object)[https://anndata.readthedocs.io/en/latest/] as well as some useful metadata. ### Downstream analysis @@ -208,12 +208,13 @@ This step uses the `pixelator single-cell analysis` command. Downstream analysis is performed on the `pxl` file generated by the previous stage. The results of the analysis is added to the pxl file. -Currently, the following analysis can be performed (if enabled): +Currently, the following analysis are performed: - polarization scores (enable with `--compute_polarization`) - co-localization scores (enable with `--compute_colocalization`) -This step can be skipped using the `--skip_analysis` option. +Each analysis can be disabled by using respectively `--compute_polarization false` or `--compute_colocalization false`. +This entire step can also be skipped using the `--skip_analysis` option. ### Generate reports @@ -234,7 +235,7 @@ and generate a report in HTML format for each sample. This step can be skipped using the `--skip_report` option. -More information on the report can be found in the pixelator documentation [here](https://software.pixelgen.com/pixelator/outputs/web-report/) +More information on the report can be found in the [pixelator documentation](https://software.pixelgen.com/pixelator/outputs/web-report/) ### Pipeline information From c46c2c20a83e2e9dd61493d48029a584480f4123 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 17 Oct 2023 10:15:30 +0200 Subject: [PATCH 105/112] use published nf-core instead of dev version --- .pre-commit-config.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2327ab49..d507433b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,8 +15,7 @@ repos: name: Update nf-params.yml file with schema language: python additional_dependencies: - # TODO: Pin this to 2.10 after release - - git+https://github.com/nf-core/tools@df0c1bf9c18cb21e3a4da04c0de73f6ae24ff29f + - nf-core entry: nf-core args: [create-params-file, --output, assets/nf-params.yml, "--force", "."] pass_filenames: false From f86eb8386a4fa2aff8dce027ef18a745e4439d8d Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 17 Oct 2023 10:24:40 +0200 Subject: [PATCH 106/112] remove old TODOs --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 129a8386..911ea7a3 100644 --- a/README.md +++ b/README.md @@ -77,8 +77,6 @@ nf-core/pixelator was originally written for [Pixelgen Technologies AB](https:// - Johan Dahlberg - Alvaro Martinez Barrio - - ## Contributions and Support If you would like to contribute to this pipeline, please see the [contributing guidelines](.github/CONTRIBUTING.md). From 8d1c23a2cbce6e1ba65cc834cb9f2f761db2ae4a Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 17 Oct 2023 10:44:48 +0200 Subject: [PATCH 107/112] bump version, add changelog --- CHANGELOG.md | 12 ++---------- assets/nf-params.yml | 2 +- nextflow.config | 2 +- 3 files changed, 4 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 348c6fa8..671698c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,14 +3,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## v1.0.0dev - [date] +## 1.0.0 - [2023-10-17] -Initial release of nf-core/pixelator, created with the [nf-core](https://nf-co.re/) template. - -### `Added` - -### `Fixed` - -### `Dependencies` - -### `Deprecated` +Initial release of nf-core/pixelator. diff --git a/assets/nf-params.yml b/assets/nf-params.yml index 78f82d9d..523db8a4 100644 --- a/assets/nf-params.yml +++ b/assets/nf-params.yml @@ -1,5 +1,5 @@ ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## nf-core/pixelator 1.0.0dev +## nf-core/pixelator 1.0.0 ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## This is an example parameter file to pass to the `-params-file` option ## of nextflow run with the nf-core/pixelator pipeline. diff --git a/nextflow.config b/nextflow.config index d941a28c..4091690d 100644 --- a/nextflow.config +++ b/nextflow.config @@ -267,7 +267,7 @@ manifest { description = """Pipeline for analysis of Molecular Pixelation assays""" mainScript = 'main.nf' nextflowVersion = '!>=23.04.0' - version = '1.0.0dev' + version = '1.0.0' // TODO: Use zenodo DOI once available doi = '10.1101/2023.06.05.543770' } From a985aa132e458da3087dc7dff79d5b003d97eb54 Mon Sep 17 00:00:00 2001 From: Maxime U Garcia Date: Tue, 17 Oct 2023 15:16:00 +0200 Subject: [PATCH 108/112] Update nextflow.config --- nextflow.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextflow.config b/nextflow.config index 4091690d..6aabf162 100644 --- a/nextflow.config +++ b/nextflow.config @@ -69,7 +69,7 @@ params { pixelator_container = null // Boilerplate options - outdir = "./results" + outdir = null tracedir = "${params.outdir}/pipeline_info" publish_dir_mode = 'copy' email = null From 073a9b9b0f4c37c7c7f7e8d786cd2ffd69915793 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 17 Oct 2023 15:43:19 +0200 Subject: [PATCH 109/112] remove unneeded docker login action for ghcr.io --- .github/workflows/ci.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 001aab7c..5f0014a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,13 +35,6 @@ jobs: with: version: "${{ matrix.NXF_VER }}" - - name: Connect to Github Container Registry - uses: docker/login-action@v2.2.0 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - name: Run pipeline with test data run: | nextflow run ${GITHUB_WORKSPACE} -profile test,docker --outdir ./results From 08d5be6edbc648e6ddb954d2576082499528627e Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 17 Oct 2023 15:57:09 +0200 Subject: [PATCH 110/112] fix typo in container directive breaking singularity pulls --- modules/local/rename_reads.nf | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/modules/local/rename_reads.nf b/modules/local/rename_reads.nf index 8b61e63d..24a479f1 100644 --- a/modules/local/rename_reads.nf +++ b/modules/local/rename_reads.nf @@ -3,11 +3,10 @@ process RENAME_READS { label "process_single" conda "conda-forge::sed=4.7" - if (workflow.containerEngine == 'singularity' && !tast.ext.singularity_pull_docker_container) { - container "https://depot.galaxyproject.org/singularity/ubuntu:20.04" - } else { - container "registry.hub.docker.com/biocontainers/biocontainers:v1.2.0_cv2" - } + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/ubuntu:20.04' : + 'registry.hub.docker.com/biocontainers/biocontainers:v1.2.0_cv2' }" + input: tuple val(meta), path(reads) From 6bf093522d74b0424d3aa042ae5b8a5a30a4ab8f Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 17 Oct 2023 15:57:30 +0200 Subject: [PATCH 111/112] add native singularity image for pixelator --- modules/local/pixelator/collect_metadata.nf | 4 +++- modules/local/pixelator/list_options.nf | 4 +++- modules/local/pixelator/single-cell/amplicon/main.nf | 4 +++- modules/local/pixelator/single-cell/analysis/main.nf | 4 +++- modules/local/pixelator/single-cell/annotate/main.nf | 4 +++- modules/local/pixelator/single-cell/collapse/main.nf | 4 +++- modules/local/pixelator/single-cell/demux/main.nf | 4 +++- modules/local/pixelator/single-cell/graph/main.nf | 4 +++- modules/local/pixelator/single-cell/qc/main.nf | 4 +++- modules/local/pixelator/single-cell/report/main.nf | 4 +++- 10 files changed, 30 insertions(+), 10 deletions(-) diff --git a/modules/local/pixelator/collect_metadata.nf b/modules/local/pixelator/collect_metadata.nf index 7bf4d7ad..37badc0e 100644 --- a/modules/local/pixelator/collect_metadata.nf +++ b/modules/local/pixelator/collect_metadata.nf @@ -9,7 +9,9 @@ process PIXELATOR_COLLECT_METADATA { cache false conda "bioconda::pixelator=0.15.0" - container "biocontainers/pixelator:0.15.0--pyh7cba7a3_0" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.0--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.0--pyh7cba7a3_0' }" input: diff --git a/modules/local/pixelator/list_options.nf b/modules/local/pixelator/list_options.nf index 0bcd4935..9cbebb03 100644 --- a/modules/local/pixelator/list_options.nf +++ b/modules/local/pixelator/list_options.nf @@ -3,7 +3,9 @@ process PIXELATOR_LIST_OPTIONS { conda "bioconda::pixelator=0.15.0" - container "biocontainers/pixelator:0.15.0--pyh7cba7a3_0" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.0--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.0--pyh7cba7a3_0' }" output: path "design_options.txt" , emit: designs diff --git a/modules/local/pixelator/single-cell/amplicon/main.nf b/modules/local/pixelator/single-cell/amplicon/main.nf index e27061b5..e4a34027 100644 --- a/modules/local/pixelator/single-cell/amplicon/main.nf +++ b/modules/local/pixelator/single-cell/amplicon/main.nf @@ -4,7 +4,9 @@ process PIXELATOR_AMPLICON { conda "bioconda::pixelator=0.15.0" - container "biocontainers/pixelator:0.15.0--pyh7cba7a3_0" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.0--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.0--pyh7cba7a3_0' }" input: tuple val(meta), path(reads) diff --git a/modules/local/pixelator/single-cell/analysis/main.nf b/modules/local/pixelator/single-cell/analysis/main.nf index acd59e7a..ee694ca6 100644 --- a/modules/local/pixelator/single-cell/analysis/main.nf +++ b/modules/local/pixelator/single-cell/analysis/main.nf @@ -4,7 +4,9 @@ process PIXELATOR_ANALYSIS { conda "bioconda::pixelator=0.15.0" - container "biocontainers/pixelator:0.15.0--pyh7cba7a3_0" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.0--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.0--pyh7cba7a3_0' }" input: tuple val(meta), path(data) diff --git a/modules/local/pixelator/single-cell/annotate/main.nf b/modules/local/pixelator/single-cell/annotate/main.nf index 61d01762..4b439d4c 100644 --- a/modules/local/pixelator/single-cell/annotate/main.nf +++ b/modules/local/pixelator/single-cell/annotate/main.nf @@ -4,7 +4,9 @@ process PIXELATOR_ANNOTATE { conda "bioconda::pixelator=0.15.0" - container "biocontainers/pixelator:0.15.0--pyh7cba7a3_0" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.0--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.0--pyh7cba7a3_0' }" input: tuple val(meta), path(dataset), path(panel_file), val(panel) diff --git a/modules/local/pixelator/single-cell/collapse/main.nf b/modules/local/pixelator/single-cell/collapse/main.nf index b4fe3404..af45df07 100644 --- a/modules/local/pixelator/single-cell/collapse/main.nf +++ b/modules/local/pixelator/single-cell/collapse/main.nf @@ -3,7 +3,9 @@ process PIXELATOR_COLLAPSE { label 'process_medium' conda "bioconda::pixelator=0.15.0" - container "biocontainers/pixelator:0.15.0--pyh7cba7a3_0" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.0--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.0--pyh7cba7a3_0' }" input: tuple val(meta), path(reads), path(panel_file), val(panel) diff --git a/modules/local/pixelator/single-cell/demux/main.nf b/modules/local/pixelator/single-cell/demux/main.nf index b8ae8caf..5368cea0 100644 --- a/modules/local/pixelator/single-cell/demux/main.nf +++ b/modules/local/pixelator/single-cell/demux/main.nf @@ -4,7 +4,9 @@ process PIXELATOR_DEMUX { conda "bioconda::pixelator=0.15.0" - container "biocontainers/pixelator:0.15.0--pyh7cba7a3_0" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.0--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.0--pyh7cba7a3_0' }" input: tuple val(meta), path(reads), path(panel_file), val(panel) diff --git a/modules/local/pixelator/single-cell/graph/main.nf b/modules/local/pixelator/single-cell/graph/main.nf index 52bf648e..9a17b6d3 100644 --- a/modules/local/pixelator/single-cell/graph/main.nf +++ b/modules/local/pixelator/single-cell/graph/main.nf @@ -4,7 +4,9 @@ process PIXELATOR_GRAPH { conda "bioconda::pixelator=0.15.0" - container "biocontainers/pixelator:0.15.0--pyh7cba7a3_0" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.0--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.0--pyh7cba7a3_0' }" input: tuple val(meta), path(edge_list) diff --git a/modules/local/pixelator/single-cell/qc/main.nf b/modules/local/pixelator/single-cell/qc/main.nf index 781663b8..bdf7eff8 100644 --- a/modules/local/pixelator/single-cell/qc/main.nf +++ b/modules/local/pixelator/single-cell/qc/main.nf @@ -4,7 +4,9 @@ process PIXELATOR_QC { conda "bioconda::pixelator=0.15.0" - container "biocontainers/pixelator:0.15.0--pyh7cba7a3_0" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.0--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.0--pyh7cba7a3_0' }" input: tuple val(meta), path(reads) diff --git a/modules/local/pixelator/single-cell/report/main.nf b/modules/local/pixelator/single-cell/report/main.nf index 61d06169..88f4bfa6 100644 --- a/modules/local/pixelator/single-cell/report/main.nf +++ b/modules/local/pixelator/single-cell/report/main.nf @@ -4,7 +4,9 @@ process PIXELATOR_REPORT { conda "bioconda::pixelator=0.15.0" - container "biocontainers/pixelator:0.15.0--pyh7cba7a3_0" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.0--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.0--pyh7cba7a3_0' }" input: tuple val(meta), path(panel_file), val(panel) From 3e66dda8f7bf19450ab92d4abb6129af8880e4b6 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 17 Oct 2023 16:31:08 +0200 Subject: [PATCH 112/112] enable writable tmpfs for singularity/apptainer --- nextflow.config | 2 ++ 1 file changed, 2 insertions(+) diff --git a/nextflow.config b/nextflow.config index 6aabf162..946d7d02 100644 --- a/nextflow.config +++ b/nextflow.config @@ -161,6 +161,7 @@ profiles { singularity { singularity.enabled = true singularity.autoMounts = true + singularity.runOptions = '--writable-tmpfs' conda.enabled = false docker.enabled = false podman.enabled = false @@ -199,6 +200,7 @@ profiles { apptainer { apptainer.enabled = true apptainer.autoMounts = true + apptainer.runOptions = '--writable-tmpfs' conda.enabled = false docker.enabled = false singularity.enabled = false