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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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/260] 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 54d6b062677b8b8d6eb472927c3e0781ea1360d7 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 31 Jul 2023 10:22:02 +0200 Subject: [PATCH 043/260] 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 044/260] 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 045/260] 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 046/260] 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 047/260] 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 048/260] 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 049/260] 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 050/260] 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 051/260] 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 052/260] 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 053/260] 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 054/260] 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 055/260] 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 056/260] 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 057/260] 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 058/260] 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 059/260] 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 060/260] 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 061/260] 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 062/260] 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 087/260] 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 088/260] 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 089/260] 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 090/260] 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 091/260] 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 092/260] 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 093/260] 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 094/260] 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 095/260] 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 096/260] 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 097/260] 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 098/260] 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 099/260] 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 100/260] 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 101/260] 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 102/260] 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 103/260] 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 104/260] 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 105/260] 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 106/260] 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 107/260] 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 108/260] 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 109/260] 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 110/260] 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 111/260] 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 From 7420e8a2625937c68fff9609c30399063ad132d4 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 17 Oct 2023 17:21:11 +0200 Subject: [PATCH 112/260] bump version to 1.1.0dev --- CHANGELOG.md | 2 ++ nextflow.config | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 671698c9..ee08d2ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ 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). +## 1.1.0dev - [date] + ## 1.0.0 - [2023-10-17] Initial release of nf-core/pixelator. diff --git a/nextflow.config b/nextflow.config index 946d7d02..455e102e 100644 --- a/nextflow.config +++ b/nextflow.config @@ -269,7 +269,7 @@ manifest { description = """Pipeline for analysis of Molecular Pixelation assays""" mainScript = 'main.nf' nextflowVersion = '!>=23.04.0' - version = '1.0.0' + version = '1.1.0dev' // TODO: Use zenodo DOI once available doi = '10.1101/2023.06.05.543770' } From b0e45b92291a731605f98627ee21d142eb67d294 Mon Sep 17 00:00:00 2001 From: Friederike Hanssen Date: Wed, 18 Oct 2023 11:31:25 +0200 Subject: [PATCH 113/260] Add Zenodo ID --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 911ea7a3..3a18d7f5 100644 --- a/README.md +++ b/README.md @@ -1,7 +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) [![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) +[![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.10015112-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.10015112) [![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/) @@ -86,7 +86,7 @@ For further information or help, don't hesitate to get in touch on the [Slack `# ## Citations - +If you use nf-core/pixelator for your analysis, please cite it using the following doi: [10.5281/zenodo.10015112](https://doi.org/10.5281/zenodo.10015112) An extensive list of references for the tools used by the pipeline can be found in the [`CITATIONS.md`](CITATIONS.md) file. From 6df3f7c017aa8e76296dd1f2c55a13b0b1dfb043 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 25 Oct 2023 15:16:19 +0200 Subject: [PATCH 114/260] Remove singularity/apptainer `--writable-tmpfs` flag. This option does not fix the problem where the user home dir is not mounted with singularity/apptainer executor because the in-memory tmpfs partition is too small to store all data written. --- nextflow.config | 2 -- 1 file changed, 2 deletions(-) diff --git a/nextflow.config b/nextflow.config index 455e102e..1678983d 100644 --- a/nextflow.config +++ b/nextflow.config @@ -161,7 +161,6 @@ profiles { singularity { singularity.enabled = true singularity.autoMounts = true - singularity.runOptions = '--writable-tmpfs' conda.enabled = false docker.enabled = false podman.enabled = false @@ -200,7 +199,6 @@ profiles { apptainer { apptainer.enabled = true apptainer.autoMounts = true - apptainer.runOptions = '--writable-tmpfs' conda.enabled = false docker.enabled = false singularity.enabled = false From 8ba4a85952cc697082f2b58c3a443b48f0e8ed28 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 25 Oct 2023 15:23:23 +0200 Subject: [PATCH 115/260] Add warning for singularity/apptainer Add a warning and workaround to the docs ( both README and USAGE) for singularity/apptainer and Nextflow 23.10. This is needed because of the change in 23.07.0-edge to no longer mount the home directory when launching an Apptainer or singularity container. --- README.md | 8 +++++++- docs/usage.md | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3a18d7f5..5fbd1bfa 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,11 @@ It takes a samplesheet as input and will process your data using `pixelator` to 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)) +> **Warning** +> Since Nextflow 23.07.0-edge, Nextflow no longer mounts the host's home directory when using Apptainer or Singularity. +> This causes issues in some dependencies. As a workaround, you can revert tot the old behavior by setting the environment variable +> `NXF_APPTAINER_HOME_MOUNT` to `true` in the machine from which you launch the pipeline. + ## Usage :::note @@ -86,7 +91,8 @@ For further information or help, don't hesitate to get in touch on the [Slack `# ## Citations -If you use nf-core/pixelator for your analysis, please cite it using the following doi: [10.5281/zenodo.10015112](https://doi.org/10.5281/zenodo.10015112) + +If you use nf-core/pixelator for your analysis, please cite it using the following doi: [10.5281/zenodo.10015112](https://doi.org/10.5281/zenodo.10015112) An extensive list of references for the tools used by the pipeline can be found in the [`CITATIONS.md`](CITATIONS.md) file. diff --git a/docs/usage.md b/docs/usage.md index 2ffee400..8cba72ab 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -239,6 +239,12 @@ If `-profile` is not specified, the pipeline will run locally and expect all sof - `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. +:::warning +Since Nextflow 23.07.0-edge, Nextflow no longer mounts the host's home directory when using Apptainer or Singularity. +This causes issues in some dependencies. As a workaround, you can revert tot the old behavior by setting the environment variable +`NXF_APPTAINER_HOME_MOUNT` to `true` in the machine from which you launch the pipeline. +::: + ### `-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). From 2ed56e4311b9c8b685de0cf39a815c740c1a2c78 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 25 Oct 2023 15:34:14 +0200 Subject: [PATCH 116/260] Update CHANGELOG --- CHANGELOG.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ee08d2ab..ec782d48 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,8 +3,12 @@ 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). -## 1.1.0dev - [date] +## [1.0.1] - date -## 1.0.0 - [2023-10-17] +### Enhancements & fixes + +- [[PR #66](https://github.com/nf-core/pixelator/pull/66)] - Add a warning and workaround for singularity & apptainer + +## [1.0.0] - 2023-10-17 Initial release of nf-core/pixelator. From 842e902da5322df5ac16d4964ad0d5c30d65e718 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 25 Oct 2023 15:40:08 +0200 Subject: [PATCH 117/260] Update pixelator to 0.15.2 --- modules/local/pixelator/collect_metadata.nf | 6 +++--- modules/local/pixelator/list_options.nf | 6 +++--- modules/local/pixelator/single-cell/amplicon/main.nf | 6 +++--- modules/local/pixelator/single-cell/analysis/main.nf | 6 +++--- modules/local/pixelator/single-cell/annotate/main.nf | 6 +++--- modules/local/pixelator/single-cell/collapse/main.nf | 6 +++--- modules/local/pixelator/single-cell/demux/main.nf | 6 +++--- modules/local/pixelator/single-cell/graph/main.nf | 6 +++--- modules/local/pixelator/single-cell/qc/main.nf | 6 +++--- modules/local/pixelator/single-cell/report/main.nf | 6 +++--- 10 files changed, 30 insertions(+), 30 deletions(-) diff --git a/modules/local/pixelator/collect_metadata.nf b/modules/local/pixelator/collect_metadata.nf index 37badc0e..10601d75 100644 --- a/modules/local/pixelator/collect_metadata.nf +++ b/modules/local/pixelator/collect_metadata.nf @@ -8,10 +8,10 @@ process PIXELATOR_COLLECT_METADATA { label 'process_single' cache false - conda "bioconda::pixelator=0.15.0" + conda "bioconda::pixelator=0.15.2" 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' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.2--pyh7cba7a3_0' }" input: diff --git a/modules/local/pixelator/list_options.nf b/modules/local/pixelator/list_options.nf index 9cbebb03..200cf5b2 100644 --- a/modules/local/pixelator/list_options.nf +++ b/modules/local/pixelator/list_options.nf @@ -2,10 +2,10 @@ process PIXELATOR_LIST_OPTIONS { label 'process_single' - conda "bioconda::pixelator=0.15.0" + conda "bioconda::pixelator=0.15.2" 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' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.2--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 e4a34027..f5eb6ae5 100644 --- a/modules/local/pixelator/single-cell/amplicon/main.nf +++ b/modules/local/pixelator/single-cell/amplicon/main.nf @@ -3,10 +3,10 @@ process PIXELATOR_AMPLICON { label 'process_low' - conda "bioconda::pixelator=0.15.0" + conda "bioconda::pixelator=0.15.2" 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' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.2--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 ee694ca6..a30843b5 100644 --- a/modules/local/pixelator/single-cell/analysis/main.nf +++ b/modules/local/pixelator/single-cell/analysis/main.nf @@ -3,10 +3,10 @@ process PIXELATOR_ANALYSIS { label 'process_medium' - conda "bioconda::pixelator=0.15.0" + conda "bioconda::pixelator=0.15.2" 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' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.2--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 4b439d4c..16c17d8c 100644 --- a/modules/local/pixelator/single-cell/annotate/main.nf +++ b/modules/local/pixelator/single-cell/annotate/main.nf @@ -3,10 +3,10 @@ process PIXELATOR_ANNOTATE { label 'process_medium' - conda "bioconda::pixelator=0.15.0" + conda "bioconda::pixelator=0.15.2" 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' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.2--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 af45df07..893660a9 100644 --- a/modules/local/pixelator/single-cell/collapse/main.nf +++ b/modules/local/pixelator/single-cell/collapse/main.nf @@ -2,10 +2,10 @@ process PIXELATOR_COLLAPSE { tag "$meta.id" label 'process_medium' - conda "bioconda::pixelator=0.15.0" + conda "bioconda::pixelator=0.15.2" 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' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.2--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 5368cea0..fc50d42a 100644 --- a/modules/local/pixelator/single-cell/demux/main.nf +++ b/modules/local/pixelator/single-cell/demux/main.nf @@ -3,10 +3,10 @@ process PIXELATOR_DEMUX { label 'process_medium' - conda "bioconda::pixelator=0.15.0" + conda "bioconda::pixelator=0.15.2" 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' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.2--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 9a17b6d3..f15b07e5 100644 --- a/modules/local/pixelator/single-cell/graph/main.nf +++ b/modules/local/pixelator/single-cell/graph/main.nf @@ -3,10 +3,10 @@ process PIXELATOR_GRAPH { label 'process_medium' - conda "bioconda::pixelator=0.15.0" + conda "bioconda::pixelator=0.15.2" 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' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.2--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 bdf7eff8..e07ae1e2 100644 --- a/modules/local/pixelator/single-cell/qc/main.nf +++ b/modules/local/pixelator/single-cell/qc/main.nf @@ -3,10 +3,10 @@ process PIXELATOR_QC { label 'process_medium' - conda "bioconda::pixelator=0.15.0" + conda "bioconda::pixelator=0.15.2" 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' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.2--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 88f4bfa6..f39e7dda 100644 --- a/modules/local/pixelator/single-cell/report/main.nf +++ b/modules/local/pixelator/single-cell/report/main.nf @@ -3,10 +3,10 @@ process PIXELATOR_REPORT { label 'process_low' - conda "bioconda::pixelator=0.15.0" + conda "bioconda::pixelator=0.15.2" 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' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.2--pyh7cba7a3_0' }" input: tuple val(meta), path(panel_file), val(panel) From cf737e8c81eda0cb3c9a8b555556d634458396a2 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 25 Oct 2023 15:42:11 +0200 Subject: [PATCH 118/260] Update CHANGELOG --- CHANGELOG.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec782d48..51dc1a70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [[PR #66](https://github.com/nf-core/pixelator/pull/66)] - Add a warning and workaround for singularity & apptainer +### Software dependencies + +| Dependency | Old version | New version | +| ----------- | ----------- | ----------- | +| `pixelator` | 0.15.0 | 0.15.2 | + +> **NB:** Dependency has been **updated** if both old and new version information is present. +> +> **NB:** Dependency has been **added** if just the new version information is present. +> +> **NB:** Dependency has been **removed** if new version information isn't present. + ## [1.0.0] - 2023-10-17 Initial release of nf-core/pixelator. From a0350eb5400a87f1c6183b2ec080b0965c434846 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 26 Oct 2023 11:38:16 +0200 Subject: [PATCH 119/260] fix typo and missing NXF_SINGULARITY_HOME_MOUNT --- README.md | 4 ++-- docs/usage.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5fbd1bfa..5c3605c7 100644 --- a/README.md +++ b/README.md @@ -29,8 +29,8 @@ It takes a samplesheet as input and will process your data using `pixelator` to > **Warning** > Since Nextflow 23.07.0-edge, Nextflow no longer mounts the host's home directory when using Apptainer or Singularity. -> This causes issues in some dependencies. As a workaround, you can revert tot the old behavior by setting the environment variable -> `NXF_APPTAINER_HOME_MOUNT` to `true` in the machine from which you launch the pipeline. +> This causes issues in some dependencies. As a workaround, you can revert to the old behavior by setting the environment variable +> `NXF_APPTAINER_HOME_MOUNT` or `NXF_SINGULARITY_HOME_MOUNT` to `true` in the machine from which you launch the pipeline. ## Usage diff --git a/docs/usage.md b/docs/usage.md index 8cba72ab..cb2b1e0c 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -241,8 +241,8 @@ If `-profile` is not specified, the pipeline will run locally and expect all sof :::warning Since Nextflow 23.07.0-edge, Nextflow no longer mounts the host's home directory when using Apptainer or Singularity. -This causes issues in some dependencies. As a workaround, you can revert tot the old behavior by setting the environment variable -`NXF_APPTAINER_HOME_MOUNT` to `true` in the machine from which you launch the pipeline. +This causes issues in some dependencies. As a workaround, you can revert to the old behavior by setting the environment variable +`NXF_APPTAINER_HOME_MOUNT` or `NXF_SINGULARITY_HOME_MOUNT` to `true` in the machine from which you launch the pipeline. ::: ### `-resume` From 22135aee9e70a69b2c250161f4216889a8c28734 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 26 Oct 2023 13:18:01 +0200 Subject: [PATCH 120/260] update nf-core modules --- modules.json | 4 +- modules/nf-core/cat/fastq/environment.yml | 6 + modules/nf-core/cat/fastq/main.nf | 2 +- modules/nf-core/cat/fastq/meta.yml | 4 +- modules/nf-core/cat/fastq/tests/main.nf.test | 143 ++++++++++++++++++ .../nf-core/cat/fastq/tests/main.nf.test.snap | 78 ++++++++++ modules/nf-core/cat/fastq/tests/tags.yml | 2 + .../dumpsoftwareversions/environment.yml | 6 + .../custom/dumpsoftwareversions/main.nf | 2 +- .../custom/dumpsoftwareversions/meta.yml | 5 +- .../dumpsoftwareversions/tests/main.nf.test | 38 +++++ .../tests/main.nf.test.snap | 27 ++++ .../dumpsoftwareversions/tests/tags.yml | 2 + 13 files changed, 312 insertions(+), 7 deletions(-) create mode 100644 modules/nf-core/cat/fastq/environment.yml create mode 100644 modules/nf-core/cat/fastq/tests/main.nf.test create mode 100644 modules/nf-core/cat/fastq/tests/main.nf.test.snap create mode 100644 modules/nf-core/cat/fastq/tests/tags.yml create mode 100644 modules/nf-core/custom/dumpsoftwareversions/environment.yml create mode 100644 modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test create mode 100644 modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap create mode 100644 modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml diff --git a/modules.json b/modules.json index 300d51a7..06cde38b 100644 --- a/modules.json +++ b/modules.json @@ -7,12 +7,12 @@ "nf-core": { "cat/fastq": { "branch": "master", - "git_sha": "5c460c5a4736974abde2843294f35307ee2b0e5e", + "git_sha": "516189e968feb4ebdd9921806988b4c12b4ac2dc", "installed_by": ["modules"] }, "custom/dumpsoftwareversions": { "branch": "master", - "git_sha": "05c280924b6c768d484c7c443dad5e605c4ff4b4", + "git_sha": "516189e968feb4ebdd9921806988b4c12b4ac2dc", "installed_by": ["modules"] } } diff --git a/modules/nf-core/cat/fastq/environment.yml b/modules/nf-core/cat/fastq/environment.yml new file mode 100644 index 00000000..222b301f --- /dev/null +++ b/modules/nf-core/cat/fastq/environment.yml @@ -0,0 +1,6 @@ +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - conda-forge::sed=4.7 diff --git a/modules/nf-core/cat/fastq/main.nf b/modules/nf-core/cat/fastq/main.nf index 5021e6fc..b75a2e73 100644 --- a/modules/nf-core/cat/fastq/main.nf +++ b/modules/nf-core/cat/fastq/main.nf @@ -2,7 +2,7 @@ process CAT_FASTQ { tag "$meta.id" label 'process_single' - conda "conda-forge::sed=4.7" + conda 'modules/nf-core/cat/fastq/environment.yml' container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/ubuntu:20.04' : 'nf-core/ubuntu:20.04' }" diff --git a/modules/nf-core/cat/fastq/meta.yml b/modules/nf-core/cat/fastq/meta.yml index 8a39e309..db4ac3c7 100644 --- a/modules/nf-core/cat/fastq/meta.yml +++ b/modules/nf-core/cat/fastq/meta.yml @@ -34,7 +34,9 @@ output: type: file description: File containing software versions pattern: "versions.yml" - authors: - "@joseespinosa" - "@drpatelh" +maintainers: + - "@joseespinosa" + - "@drpatelh" diff --git a/modules/nf-core/cat/fastq/tests/main.nf.test b/modules/nf-core/cat/fastq/tests/main.nf.test new file mode 100644 index 00000000..f5f94182 --- /dev/null +++ b/modules/nf-core/cat/fastq/tests/main.nf.test @@ -0,0 +1,143 @@ +nextflow_process { + + name "Test Process CAT_FASTQ" + script "../main.nf" + process "CAT_FASTQ" + tag "modules" + tag "modules_nfcore" + tag "cat" + tag "cat/fastq" + + test("test_cat_fastq_single_end") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = [ + [ id:'test', single_end:true ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test2_1_fastq_gz'], checkIfExists: true) ] + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out.reads).match() }, + { assert path(process.out.versions.get(0)).getText().contains("cat") } + ) + } + } + + test("test_cat_fastq_paired_end") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = [ + [ id:'test', single_end:false ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test_2_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test2_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test2_2_fastq_gz'], checkIfExists: true) ] + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out.reads).match() }, + { assert path(process.out.versions.get(0)).getText().contains("cat") } + ) + } + } + + test("test_cat_fastq_single_end_same_name") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = [ + [ id:'test', single_end:true ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true) ] + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out.reads).match() }, + { assert path(process.out.versions.get(0)).getText().contains("cat") } + ) + } + } + + test("test_cat_fastq_paired_end_same_name") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = [ + [ id:'test', single_end:false ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test_2_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test_2_fastq_gz'], checkIfExists: true) ] + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out.reads).match() }, + { assert path(process.out.versions.get(0)).getText().contains("cat") } + ) + } + } + + test("test_cat_fastq_single_end_single_file") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = [ + [ id:'test', single_end:true ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true)] + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out.reads).match() }, + { assert path(process.out.versions.get(0)).getText().contains("cat") } + ) + } + } +} diff --git a/modules/nf-core/cat/fastq/tests/main.nf.test.snap b/modules/nf-core/cat/fastq/tests/main.nf.test.snap new file mode 100644 index 00000000..ec2342e5 --- /dev/null +++ b/modules/nf-core/cat/fastq/tests/main.nf.test.snap @@ -0,0 +1,78 @@ +{ + "test_cat_fastq_single_end": { + "content": [ + [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,f9cf5e375f7de81a406144a2c70cc64d" + ] + ] + ], + "timestamp": "2023-10-17T23:19:12.990284837" + }, + "test_cat_fastq_single_end_same_name": { + "content": [ + [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,63f817db7a29a03eb538104495556f66" + ] + ] + ], + "timestamp": "2023-10-17T23:19:31.554568147" + }, + "test_cat_fastq_single_end_single_file": { + "content": [ + [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,e325ef7deb4023447a1f074e285761af" + ] + ] + ], + "timestamp": "2023-10-17T23:19:49.629360033" + }, + "test_cat_fastq_paired_end_same_name": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test_1.merged.fastq.gz:md5,63f817db7a29a03eb538104495556f66", + "test_2.merged.fastq.gz:md5,fe9f266f43a6fc3dcab690a18419a56e" + ] + ] + ] + ], + "timestamp": "2023-10-17T23:19:40.711617539" + }, + "test_cat_fastq_paired_end": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test_1.merged.fastq.gz:md5,f9cf5e375f7de81a406144a2c70cc64d", + "test_2.merged.fastq.gz:md5,77c8e966e130d8c6b6ec9be52fcb2bda" + ] + ] + ] + ], + "timestamp": "2023-10-18T07:53:20.923560211" + } +} \ No newline at end of file diff --git a/modules/nf-core/cat/fastq/tests/tags.yml b/modules/nf-core/cat/fastq/tests/tags.yml new file mode 100644 index 00000000..6ac43614 --- /dev/null +++ b/modules/nf-core/cat/fastq/tests/tags.yml @@ -0,0 +1,2 @@ +cat/fastq: + - modules/nf-core/cat/fastq/** diff --git a/modules/nf-core/custom/dumpsoftwareversions/environment.yml b/modules/nf-core/custom/dumpsoftwareversions/environment.yml new file mode 100644 index 00000000..7ca22161 --- /dev/null +++ b/modules/nf-core/custom/dumpsoftwareversions/environment.yml @@ -0,0 +1,6 @@ +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - bioconda::multiqc=1.15 diff --git a/modules/nf-core/custom/dumpsoftwareversions/main.nf b/modules/nf-core/custom/dumpsoftwareversions/main.nf index c9d014b1..60a19e0e 100644 --- a/modules/nf-core/custom/dumpsoftwareversions/main.nf +++ b/modules/nf-core/custom/dumpsoftwareversions/main.nf @@ -2,7 +2,7 @@ process CUSTOM_DUMPSOFTWAREVERSIONS { label 'process_single' // Requires `pyyaml` which does not have a dedicated container but is in the MultiQC container - conda "bioconda::multiqc=1.15" + conda 'modules/nf-core/custom/dumpsoftwareversions/environment.yml' container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/multiqc:1.15--pyhdfd78af_0' : 'biocontainers/multiqc:1.15--pyhdfd78af_0' }" diff --git a/modules/nf-core/custom/dumpsoftwareversions/meta.yml b/modules/nf-core/custom/dumpsoftwareversions/meta.yml index c32657de..9414c32d 100644 --- a/modules/nf-core/custom/dumpsoftwareversions/meta.yml +++ b/modules/nf-core/custom/dumpsoftwareversions/meta.yml @@ -16,7 +16,6 @@ input: type: file description: YML file containing software versions pattern: "*.yml" - output: - yml: type: file @@ -30,7 +29,9 @@ output: type: file description: File containing software versions pattern: "versions.yml" - authors: - "@drpatelh" - "@grst" +maintainers: + - "@drpatelh" + - "@grst" diff --git a/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test new file mode 100644 index 00000000..eec1db10 --- /dev/null +++ b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test @@ -0,0 +1,38 @@ +nextflow_process { + + name "Test Process CUSTOM_DUMPSOFTWAREVERSIONS" + script "../main.nf" + process "CUSTOM_DUMPSOFTWAREVERSIONS" + tag "modules" + tag "modules_nfcore" + tag "custom" + tag "dumpsoftwareversions" + tag "custom/dumpsoftwareversions" + + test("Should run without failures") { + when { + process { + """ + def tool1_version = ''' + TOOL1: + tool1: 0.11.9 + '''.stripIndent() + + def tool2_version = ''' + TOOL2: + tool2: 1.9 + '''.stripIndent() + + input[0] = Channel.of(tool1_version, tool2_version).collectFile() + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } +} diff --git a/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap new file mode 100644 index 00000000..8713b921 --- /dev/null +++ b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap @@ -0,0 +1,27 @@ +{ + "Should run without failures": { + "content": [ + { + "0": [ + "software_versions.yml:md5,a027f820f30b8191a20ca16465daaf37" + ], + "1": [ + "software_versions_mqc.yml:md5,ee4a1d028ad29987f9ac511f4668f17c" + ], + "2": [ + "versions.yml:md5,f47ebd22aba1dd987b7e5d5247b766c3" + ], + "mqc_yml": [ + "software_versions_mqc.yml:md5,ee4a1d028ad29987f9ac511f4668f17c" + ], + "versions": [ + "versions.yml:md5,f47ebd22aba1dd987b7e5d5247b766c3" + ], + "yml": [ + "software_versions.yml:md5,a027f820f30b8191a20ca16465daaf37" + ] + } + ], + "timestamp": "2023-10-11T17:10:02.930699" + } +} diff --git a/modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml b/modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml new file mode 100644 index 00000000..405aa24a --- /dev/null +++ b/modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml @@ -0,0 +1,2 @@ +custom/dumpsoftwareversions: + - modules/nf-core/custom/dumpsoftwareversions/** From 7f657c3ffd3936d8f05dcb947577d7684184226c Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 26 Oct 2023 13:22:36 +0200 Subject: [PATCH 121/260] Add Zenodo id to pipeline citation summary --- lib/WorkflowMain.groovy | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/lib/WorkflowMain.groovy b/lib/WorkflowMain.groovy index ce9041a7..da063262 100755 --- a/lib/WorkflowMain.groovy +++ b/lib/WorkflowMain.groovy @@ -11,9 +11,8 @@ class WorkflowMain { // public static String citation(workflow) { return "If you use ${workflow.manifest.name} for your analysis please cite:\n\n" + - // TODO nf-core: Add Zenodo DOI for pipeline after first release - //"* The pipeline\n" + - //" https://doi.org/10.5281/zenodo.XXXXXXX\n\n" + + "* The pipeline\n" + + " https://doi.org/10.5281/zenodo.10015112\n\n" + "* The nf-core framework\n" + " https://doi.org/10.1038/s41587-020-0439-x\n\n" + "* Software dependencies\n" + From d350854ee5a300490d4c5aa9117556a33dd1403c Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 26 Oct 2023 13:22:56 +0200 Subject: [PATCH 122/260] Remove outdated TODO --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 5c3605c7..f0cf296e 100644 --- a/README.md +++ b/README.md @@ -90,8 +90,6 @@ For further information or help, don't hesitate to get in touch on the [Slack `# ## Citations - - If you use nf-core/pixelator for your analysis, please cite it using the following doi: [10.5281/zenodo.10015112](https://doi.org/10.5281/zenodo.10015112) An extensive list of references for the tools used by the pipeline can be found in the [`CITATIONS.md`](CITATIONS.md) file. From 902074e1bd16ce447910fd0bd0c5121b13579efb Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 26 Oct 2023 13:29:14 +0200 Subject: [PATCH 123/260] Update CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51dc1a70..9ad7ec83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ 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). -## [1.0.1] - date +## [1.0.1] - 2023-10-26 ### Enhancements & fixes From 61b9d3d21e706a025489ea9a0850b734808555cf Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 26 Oct 2023 13:56:12 +0200 Subject: [PATCH 124/260] Use nf-core/ubuntu:20.04 to match singularity img --- CHANGELOG.md | 2 ++ lib/WorkflowPixelator.groovy | 2 +- modules/local/rename_reads.nf | 4 ++-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9ad7ec83..c67bbf1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Enhancements & fixes - [[PR #66](https://github.com/nf-core/pixelator/pull/66)] - Add a warning and workaround for singularity & apptainer +- Cleanup some linting warnings +- Update docker image in RENAME_READS to match the singularity container ### Software dependencies diff --git a/lib/WorkflowPixelator.groovy b/lib/WorkflowPixelator.groovy index 96b8a3e3..0098f0bf 100755 --- a/lib/WorkflowPixelator.groovy +++ b/lib/WorkflowPixelator.groovy @@ -40,7 +40,7 @@ class WorkflowPixelator { public static String toolCitationText(params) { - // TODO nf-core: Optionally add in-text citation tools to this list. + // 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 = [ diff --git a/modules/local/rename_reads.nf b/modules/local/rename_reads.nf index 24a479f1..024bd2de 100644 --- a/modules/local/rename_reads.nf +++ b/modules/local/rename_reads.nf @@ -1,11 +1,11 @@ process RENAME_READS { tag "$meta.id" - label "process_single" + 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' : - 'registry.hub.docker.com/biocontainers/biocontainers:v1.2.0_cv2' }" + 'nf-core/ubuntu:20.04' }" input: From e4f282da67eb0999b5c5f7c79ad6b4652138b387 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 26 Oct 2023 13:10:45 +0200 Subject: [PATCH 125/260] bump version to 1.0.1 --- assets/nf-params.yml | 2 +- nextflow.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/nf-params.yml b/assets/nf-params.yml index 523db8a4..188b8022 100644 --- a/assets/nf-params.yml +++ b/assets/nf-params.yml @@ -1,5 +1,5 @@ ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## nf-core/pixelator 1.0.0 +## nf-core/pixelator 1.0.1 ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## 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 1678983d..213a01c6 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.1.0dev' + version = '1.0.1' // TODO: Use zenodo DOI once available doi = '10.1101/2023.06.05.543770' } From 0526a65711ef093f2d7a4ffc3bbd2f5090fadf70 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 7 Nov 2023 15:40:43 +0100 Subject: [PATCH 126/260] bump version to 1.0.2dev --- CHANGELOG.md | 4 +++- nextflow.config | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c67bbf1d..8f52aed9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,9 @@ 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). -## [1.0.1] - 2023-10-26 +## [1.0.2dev] - UNRELEASED + +## [1.0.1] - 2023-10-27 ### Enhancements & fixes diff --git a/nextflow.config b/nextflow.config index 213a01c6..30ba9076 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.1' + version = '1.0.2dev' // TODO: Use zenodo DOI once available doi = '10.1101/2023.06.05.543770' } From de4017901e07df586a0ad0d57e2a37c3548f9d6c Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 7 Nov 2023 15:36:36 +0100 Subject: [PATCH 127/260] Fix loading of absolute paths in samplesheet. Absolute local filepaths and remote resources are not loading properly due to a wrong return value when checking if paths are relative and need to be resolved or not. --- samplesheet.csv | 2 +- subworkflows/local/input_check.nf | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/samplesheet.csv b/samplesheet.csv index e336439f..aea927b2 100644 --- a/samplesheet.csv +++ b/samplesheet.csv @@ -1,3 +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 +uropod_control,D21,human-sc-immunology-spatial-proteomics,/home/fbdtemme/Documents/pixelgen/nf-core-pixelator-datasets/testdata/micro/uropod_control_300k_S1_R1_001.fastq.gz,/home/fbdtemme/Documents/pixelgen/nf-core-pixelator-datasets/testdata/micro/uropod_control_300k_S1_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 diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf index 573c692c..3af1adde 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -79,12 +79,12 @@ def resolve_relative_path(relative_path, URI samplesheet_path) { // If a scheme is given we keep it as given if (uri.getScheme() != null) { - return uri + return relative_path } - def path = new File(relative_path); + def path = new File(relative_path) if (path.isAbsolute()) { - return path + return relative_path } // Resolve relative paths agains the samplesheet_path From 7a22b05c602d6a5ca650394871b896c5ee9fdfc7 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 7 Nov 2023 15:38:55 +0100 Subject: [PATCH 128/260] update changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8f52aed9..12645db1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [1.0.2dev] - UNRELEASED +### Enhancements & fixes + +- [[PR #70](https://github.com/nf-core/pixelator/pull/70)] - Fix loading of absolute paths and urls in input samplesheet + ## [1.0.1] - 2023-10-27 ### Enhancements & fixes From a7d23ef2b5a870a8e2b26644e130e4d97d45fbf5 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 7 Nov 2023 15:52:49 +0100 Subject: [PATCH 129/260] remove stray samplesheet file --- samplesheet.csv | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 samplesheet.csv diff --git a/samplesheet.csv b/samplesheet.csv deleted file mode 100644 index aea927b2..00000000 --- a/samplesheet.csv +++ /dev/null @@ -1,3 +0,0 @@ -sample,design,panel,fastq_1,fastq_2 -uropod_control,D21,human-sc-immunology-spatial-proteomics,/home/fbdtemme/Documents/pixelgen/nf-core-pixelator-datasets/testdata/micro/uropod_control_300k_S1_R1_001.fastq.gz,/home/fbdtemme/Documents/pixelgen/nf-core-pixelator-datasets/testdata/micro/uropod_control_300k_S1_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 ce2aa6ecefa2b49a6ec75ac0ea66b1da61307bc2 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 20 Nov 2023 11:26:58 +0100 Subject: [PATCH 130/260] bump version to 1.0.2 --- CHANGELOG.md | 2 +- nextflow.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12645db1..3d0823e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ 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). -## [1.0.2dev] - UNRELEASED +## [1.0.2] - 2023-11-20 ### Enhancements & fixes diff --git a/nextflow.config b/nextflow.config index 30ba9076..c57f2859 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.2dev' + version = '1.0.2' // TODO: Use zenodo DOI once available doi = '10.1101/2023.06.05.543770' } From e2994e772d1da8f2894262abf673f536b92480bb Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 20 Nov 2023 13:13:31 +0100 Subject: [PATCH 131/260] bump version to 1.1.0dev --- nextflow.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextflow.config b/nextflow.config index c57f2859..cf20d386 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.2' + version = '1.1.0dev' // TODO: Use zenodo DOI once available doi = '10.1101/2023.06.05.543770' } From 99a2d806fe5e0b3eb161897d888167360178941c Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 11 Jan 2024 13:00:46 +0100 Subject: [PATCH 132/260] merge branch "dev" into nf-core-template-merge-2.11.1 --- .github/workflows/awsfulltest.yml | 3 - .github/workflows/ci.yml | 3 - .github/workflows/release-announcments.yml | 68 +++ .gitignore | 2 + .nf-core.yml | 8 + .pre-commit-config.yaml | 19 +- .prettierignore | 1 + CHANGELOG.md | 30 +- CITATIONS.md | 12 +- README.md | 54 +-- assets/methods_description_template.yml | 29 -- assets/multiqc_config.yml | 13 - assets/nf-params.yml | 410 ++++++++++++++++ assets/samplesheet.csv | 7 +- assets/schema_input.json | 30 +- assets/test_samplesheet.csv | 4 + bin/check_samplesheet.py | 259 ----------- bin/collect_metadata.py | 79 ++++ conf/base.config | 2 - conf/igenomes.config | 440 ------------------ conf/modules.config | 137 +++++- conf/test.config | 20 +- conf/test_full.config | 9 +- 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/images/nf-core-pixelator-metromap.svg | 246 ++++++++++ docs/output.md | 235 ++++++++-- docs/usage.md | 140 ++++-- lib/WorkflowMain.groovy | 5 +- lib/WorkflowPixelator.groovy | 15 +- main.nf | 4 - modules.json | 13 +- modules/local/pixelator/collect_metadata.nf | 83 ++++ modules/local/pixelator/list_options.nf | 31 ++ .../pixelator/single-cell/amplicon/main.nf | 45 ++ .../pixelator/single-cell/analysis/main.nf | 47 ++ .../pixelator/single-cell/annotate/main.nf | 53 +++ .../pixelator/single-cell/collapse/main.nf | 54 +++ .../local/pixelator/single-cell/demux/main.nf | 54 +++ .../local/pixelator/single-cell/graph/main.nf | 49 ++ .../local/pixelator/single-cell/qc/main.nf | 78 ++++ .../pixelator/single-cell/report/main.nf | 57 +++ modules/local/rename_reads.nf | 45 ++ modules/local/samplesheet_check.nf | 9 +- modules/nf-core/cat/fastq/environment.yml | 6 + modules/nf-core/cat/fastq/main.nf | 80 ++++ modules/nf-core/cat/fastq/meta.yml | 42 ++ modules/nf-core/cat/fastq/tests/main.nf.test | 143 ++++++ .../nf-core/cat/fastq/tests/main.nf.test.snap | 78 ++++ modules/nf-core/cat/fastq/tests/tags.yml | 2 + .../templates/dumpsoftwareversions.py | 3 +- .../tests/main.nf.test.snap | 14 +- modules/nf-core/fastqc/main.nf | 55 --- modules/nf-core/fastqc/meta.yml | 57 --- modules/nf-core/multiqc/main.nf | 55 --- modules/nf-core/multiqc/meta.yml | 59 --- nextflow.config | 85 +++- nextflow_schema.json | 352 +++++++++++--- subworkflows/local/generate_reports.nf | 113 +++++ subworkflows/local/input_check.nf | 196 +++++++- tower.yml | 6 +- workflows/pixelator.nf | 204 ++++++-- 63 files changed, 3161 insertions(+), 1291 deletions(-) create mode 100644 .github/workflows/release-announcments.yml delete mode 100644 assets/methods_description_template.yml delete mode 100644 assets/multiqc_config.yml create mode 100644 assets/nf-params.yml create mode 100644 assets/test_samplesheet.csv delete mode 100755 bin/check_samplesheet.py create mode 100755 bin/collect_metadata.py delete mode 100644 conf/igenomes.config 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 create mode 100644 docs/images/nf-core-pixelator-metromap.svg create mode 100644 modules/local/pixelator/collect_metadata.nf create mode 100644 modules/local/pixelator/list_options.nf create mode 100644 modules/local/pixelator/single-cell/amplicon/main.nf create mode 100644 modules/local/pixelator/single-cell/analysis/main.nf create mode 100644 modules/local/pixelator/single-cell/annotate/main.nf create mode 100644 modules/local/pixelator/single-cell/collapse/main.nf create mode 100644 modules/local/pixelator/single-cell/demux/main.nf create mode 100644 modules/local/pixelator/single-cell/graph/main.nf create mode 100644 modules/local/pixelator/single-cell/qc/main.nf create mode 100644 modules/local/pixelator/single-cell/report/main.nf create mode 100644 modules/local/rename_reads.nf create mode 100644 modules/nf-core/cat/fastq/environment.yml create mode 100644 modules/nf-core/cat/fastq/main.nf create mode 100644 modules/nf-core/cat/fastq/meta.yml create mode 100644 modules/nf-core/cat/fastq/tests/main.nf.test create mode 100644 modules/nf-core/cat/fastq/tests/main.nf.test.snap create mode 100644 modules/nf-core/cat/fastq/tests/tags.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 create mode 100644 subworkflows/local/generate_reports.nf 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 775a2210..c64c3e88 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,8 +36,5 @@ jobs: version: "${{ matrix.NXF_VER }}" - 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 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/.gitignore b/.gitignore index 5124c9ac..d0538e85 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ results/ testing/ testing* *.pyc +.idea +.vscode diff --git a/.nf-core.yml b/.nf-core.yml index 3805dc81..7fe39026 100644 --- a/.nf-core.yml +++ b/.nf-core.yml @@ -1 +1,9 @@ repository_type: pipeline +lint: + # No multiqc support for now + multiqc_config: false + files_exist: + - assets/multiqc_config.yml + - conf/igenomes.config + files_unchanged: + - lib/NfcoreTemplate.groovy diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 0c31cdb9..d507433b 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,5 +1,22 @@ 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 + + - repo: local + hooks: + - id: nf-core/tools parameters.yaml + name: Update nf-params.yml file with schema + language: python + additional_dependencies: + - nf-core + entry: nf-core + args: [create-params-file, --output, assets/nf-params.yml, "--force", "."] + pass_filenames: false + files: ^nextflow_schema.json$ diff --git a/.prettierignore b/.prettierignore index 437d763d..56fc6a49 100644 --- a/.prettierignore +++ b/.prettierignore @@ -10,3 +10,4 @@ testing/ testing* *.pyc bin/ +assets/nf-params.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index a5669d0b..3d0823e5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,14 +3,32 @@ 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.1.0dev - [date] +## [1.0.2] - 2023-11-20 -Initial release of nf-core/pixelator, created with the [nf-core](https://nf-co.re/) template. +### Enhancements & fixes -### `Added` +- [[PR #70](https://github.com/nf-core/pixelator/pull/70)] - Fix loading of absolute paths and urls in input samplesheet -### `Fixed` +## [1.0.1] - 2023-10-27 -### `Dependencies` +### Enhancements & fixes -### `Deprecated` +- [[PR #66](https://github.com/nf-core/pixelator/pull/66)] - Add a warning and workaround for singularity & apptainer +- Cleanup some linting warnings +- Update docker image in RENAME_READS to match the singularity container + +### Software dependencies + +| Dependency | Old version | New version | +| ----------- | ----------- | ----------- | +| `pixelator` | 0.15.0 | 0.15.2 | + +> **NB:** Dependency has been **updated** if both old and new version information is present. +> +> **NB:** Dependency has been **added** if just the new version information is present. +> +> **NB:** Dependency has been **removed** if new version information isn't present. + +## [1.0.0] - 2023-10-17 + +Initial release of nf-core/pixelator. diff --git a/CITATIONS.md b/CITATIONS.md index ca3444ff..49b37b62 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -10,13 +10,17 @@ ## Pipeline tools -- [FastQC](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/) +- [pixelator](https://doi.org/10.1101/2023.06.05.543770) - > Andrews, S. (2010). FastQC: A Quality Control Tool for High Throughput Sequence Data [Online]. + > 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. -- [MultiQC](https://pubmed.ncbi.nlm.nih.gov/27312411/) +- [cutadapt](http://dx.doi.org/10.14806/ej.17.1.200) - > 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. + > 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 diff --git a/README.md b/README.md index 84f3d1bb..ca3df6bd 100644 --- a/README.md +++ b/README.md @@ -1,7 +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) [![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) +[![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.10015112-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.10015112) [![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/) @@ -13,46 +13,43 @@ ## Introduction -**nf-core/pixelator** is a bioinformatics pipeline that ... +**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)) +4. Error correction, duplicate removal, compute read counts ([`pixelator collapse`](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)) -1. Read QC ([`FastQC`](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/)) -2. Present QC for raw reads ([`MultiQC`](http://multiqc.info/)) +> **Warning** +> Since Nextflow 23.07.0-edge, Nextflow no longer mounts the host's home directory when using Apptainer or Singularity. +> This causes issues in some dependencies. As a workaround, you can revert to the old behavior by setting the environment variable +> `NXF_APPTAINER_HOME_MOUNT` or `NXF_SINGULARITY_HOME_MOUNT` to `true` in the machine from which you launch the pipeline. ## 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. - +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 \ -profile \ @@ -74,11 +71,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.com/) by: -We thank the following people for their extensive assistance in the development of this pipeline: - - +- Florian De Temmerman +- Johan Dahlberg +- Alvaro Martinez Barrio ## Contributions and Support @@ -88,10 +85,7 @@ For further information or help, don't hesitate to get in touch on the [Slack `# ## Citations - - - - +If you use nf-core/pixelator for your analysis, please cite it using the following doi: [10.5281/zenodo.10015112](https://doi.org/10.5281/zenodo.10015112) An extensive list of references for the tools used by the pipeline can be found in the [`CITATIONS.md`](CITATIONS.md) file. 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.
  • -
-
diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml deleted file mode 100644 index 1e71e68e..00000000 --- a/assets/multiqc_config.yml +++ /dev/null @@ -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/nf-params.yml b/assets/nf-params.yml new file mode 100644 index 00000000..995f2130 --- /dev/null +++ b/assets/nf-params.yml @@ -0,0 +1,410 @@ +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## nf-core/pixelator 1.1.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 + +## ----------------------------------------------------------------------------- +## input_basedir +## ----------------------------------------------------------------------------- +## Path to a local or remote directory that is the "current working +## directory" for relative paths defined in the input samplesheet +## Type: string +## ----------------------------------------------------------------------------- +# input_basedir = 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 +## ============================================================================= + +## ----------------------------------------------------------------------------- +## 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/samplesheet.csv b/assets/samplesheet.csv index 5f653ab7..e50611a7 100644 --- a/assets/samplesheet.csv +++ b/assets/samplesheet.csv @@ -1,3 +1,4 @@ -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 +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 diff --git a/assets/schema_input.json b/assets/schema_input.json index c3a45df3..6647a8b7 100644 --- a/assets/schema_input.json +++ b/assets/schema_input.json @@ -6,11 +6,36 @@ "type": "array", "items": { "type": "object", + "required": ["sample", "design", "fastq_1"], "properties": { "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", + "meta": ["design"], + "errorMessage": "Design must be specified" + }, + "panel": { + "errorMessage": "Panel name must be specified", + "type": "string", + "meta": ["panel"] + }, + "panel_file": { + "errorMessage": "Panel file must either be left empty or cannot contain spaces and must have extension '.csv', '.tsv' or '.yaml'", + "anyOf": [ + { + "type": "string", + "pattern": "^\\S+.(csv|tsv|ya?ml)$" + }, + { + "type": "string", + "maxLength": 0 + } + ] }, "fastq_1": { "type": "string", @@ -30,7 +55,6 @@ } ] } - }, - "required": ["sample", "fastq_1"] + } } } 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/bin/check_samplesheet.py b/bin/check_samplesheet.py deleted file mode 100755 index 4a758fe0..00000000 --- a/bin/check_samplesheet.py +++ /dev/null @@ -1,259 +0,0 @@ -#!/usr/bin/env python - - -"""Provide a command line tool to validate and transform tabular samplesheets.""" - - -import argparse -import csv -import logging -import sys -from collections import Counter -from pathlib import Path - -logger = logging.getLogger() - - -class RowChecker: - """ - 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. - - """ - - VALID_FORMATS = ( - ".fq.gz", - ".fastq.gz", - ) - - def __init__( - self, - sample_col="sample", - first_col="fastq_1", - second_col="fastq_2", - single_col="single_end", - **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"). - first_col (str): The name of the column that contains the first (or only) - FASTQ file path (default "fastq_1"). - second_col (str): The name of the column that contains the second (if any) - FASTQ file path (default "fastq_2"). - single_col (str): The name of the new column that will be inserted and - records whether the sample contains single- or paired-end sequencing - reads (default "single_end"). - - """ - super().__init__(**kwargs) - self._sample_col = sample_col - self._first_col = first_col - self._second_col = second_col - self._single_col = single_col - self._seen = set() - self.modified = [] - - 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_first(row) - self._validate_second(row) - self._validate_pair(row) - self._seen.add((row[self._sample_col], row[self._first_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_first(self, row): - """Assert that the first FASTQ entry is non-empty and has the right format.""" - if len(row[self._first_col]) <= 0: - raise AssertionError("At least the first FASTQ file is required.") - self._validate_fastq_format(row[self._first_col]) - - def _validate_second(self, row): - """Assert that the second FASTQ entry has the right format if it exists.""" - if len(row[self._second_col]) > 0: - self._validate_fastq_format(row[self._second_col]) - - def _validate_pair(self, row): - """Assert that read pairs have the same file extension. Report pair status.""" - if row[self._first_col] and row[self._second_col]: - row[self._single_col] = False - first_col_suffix = Path(row[self._first_col]).suffixes[-2:] - second_col_suffix = Path(row[self._second_col]).suffixes[-2:] - if first_col_suffix != second_col_suffix: - raise AssertionError("FASTQ pairs must have the same file extensions.") - else: - row[self._single_col] = True - - def _validate_fastq_format(self, filename): - """Assert that a given filename has one of the expected FASTQ extensions.""" - if not any(filename.endswith(extension) for extension in self.VALID_FORMATS): - raise AssertionError( - f"The FASTQ 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 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]}" - - -def read_head(handle, num_lines=10): - """Read the specified number of lines from the current position in the file.""" - lines = [] - for idx, line in enumerate(handle): - if idx == num_lines: - break - lines.append(line) - return "".join(lines) - - -def sniff_format(handle): - """ - Detect the tabular format. - - Args: - handle (text file): A handle to a `text file`_ object. The read position is - expected to be at the beginning (index 0). - - Returns: - csv.Dialect: The detected tabular format. - - .. _text file: - https://docs.python.org/3/glossary.html#term-text-file - - """ - peek = read_head(handle) - handle.seek(0) - sniffer = csv.Sniffer() - dialect = sniffer.sniff(peek) - return dialect - - -def check_samplesheet(file_in, file_out): - """ - Check that the tabular samplesheet has the structure expected by nf-core pipelines. - - Validate the general shape of the table, expected columns, and each row. Also add - an additional column which records whether one or two FASTQ reads were found. - - Args: - file_in (pathlib.Path): The given tabular samplesheet. The format can be either - 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) - 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: - 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") - # 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=",") - writer.writeheader() - for row in checker.modified: - writer.writerow(row) - - -def parse_args(argv=None): - """Define and immediately parse command line arguments.""" - parser = argparse.ArgumentParser( - description="Validate and transform a tabular samplesheet.", - epilog="Example: python check_samplesheet.py samplesheet.csv samplesheet.valid.csv", - ) - parser.add_argument( - "file_in", - metavar="FILE_IN", - type=Path, - help="Tabular input samplesheet in CSV or TSV format.", - ) - parser.add_argument( - "file_out", - metavar="FILE_OUT", - type=Path, - help="Transformed output samplesheet in CSV format.", - ) - parser.add_argument( - "-l", - "--log-level", - help="The desired log level (default WARNING).", - choices=("CRITICAL", "ERROR", "WARNING", "INFO", "DEBUG"), - default="WARNING", - ) - return parser.parse_args(argv) - - -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) - - -if __name__ == "__main__": - sys.exit(main()) diff --git a/bin/collect_metadata.py b/bin/collect_metadata.py new file mode 100755 index 00000000..67df83b7 --- /dev/null +++ b/bin/collect_metadata.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python + +""" +Collect version information about the pixelator python environment. + +Written by Florian De Temmerman (https://github.com/fbdtemme) +Copyright (c) 2023 Pixelgen Technologies AB. +""" + +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/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 d91c6aba..7ab9ba43 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -10,6 +10,7 @@ ---------------------------------------------------------------------------------------- */ + process { publishDir = [ @@ -18,33 +19,139 @@ process { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] - withName: SAMPLESHEET_CHECK { + withName: "PIXELATOR.*" { publishDir = [ - path: { "${params.outdir}/pipeline_info" }, - mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + [ + 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" + ] ] + + if (params.pixelator_container) { + container = params.pixelator_container + } } - withName: FASTQC { - ext.args = '--quiet' + withName: PIXELATOR_LIST_OPTIONS { + publishDir = [ enabled: false ] } - withName: CUSTOM_DUMPSOFTWAREVERSIONS { + withName: "CAT_FASTQ" { + publishDir = [ enabled: false ] + } + + withName: RENAME_READS { publishDir = [ - path: { "${params.outdir}/pipeline_info" }, - mode: params.publish_dir_mode, - pattern: '*_versions.yml' + enabled: false, + path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, ] } - withName: 'MULTIQC' { - ext.args = { params.multiqc_title ? "--title \"$params.multiqc_title\"" : '' } + + // use explicit (params.my_option instanceof Integer) checks to avoid issues with 0 evaluating false + // since most pixelator flags do accept zero as a value + + withName: PIXELATOR_AMPLICON { + ext.args = { + [ + "--design ${meta.design}", + ].join(' ').trim() + } + } + + withName: PIXELATOR_QC { + // Options for pixelator preqc + ext.args = { + [ + "--design ${meta.design}", + (params.trim_front instanceof Integer)? "--trim-front ${params.trim_front}": '', + (params.trim_tail instanceof Integer) ? "--trim-tail ${params.trim_tail}": '', + (params.max_length instanceof Integer) ? "--max-length ${params.max_length}": '', + (params.min_length instanceof Integer) ? "--min-length ${params.min_length}": '', + (params.max_n_bases instanceof Integer) ? "--max-n-bases ${params.max_n_bases}": '', + (params.avg_qual instanceof Integer)? "--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}": '', + ].join(' ').trim() + } + } + + withName: PIXELATOR_DEMUX { + ext.args = { + [ + "--design ${meta.design}", + (params.demux_mismatches != null) ? "--mismatches ${params.demux_mismatches}": '', + (params.demux_min_length instanceof Integer) ? "--mismatches ${params.demux_min_length}": '', + ].join(' ').trim() + } + } + + withName: PIXELATOR_COLLAPSE { + ext.args = [ + 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}": '', + params.collapse_min_count ? "--min-count ${params.collapse_min_count}": '', + params.collapse_use_counts ? "--use-counts": '', + ].join(' ').trim() + } + + withName: PIXELATOR_GRAPH { + ext.args = [ + params.multiplet_recovery ? "--multiplet-recovery" : '', + params.leiden_iterations ? "--leiden-iterations ${params.leiden_iterations}" : '', + params.graph_min_count ? "--min-count ${params.graph_min_count}" : '', + ].join(' ').trim() + } + + withName: PIXELATOR_ANNOTATE { + ext.args = [ + (params.min_size instanceof Integer) ? "--min-size ${params.min_size}" : '', + (params.max_size instanceof Integer) ? "--max-size ${params.max_size}" : '', + params.dynamic_filter ? "--dynamic-filter ${params.dynamic_filter}" : '', + params.aggregate_calling ? "--aggregate-calling" : '', + ].join(' ').trim() + } + + 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.polarization_normalization ? "--polarization-normalization ${params.polarization_normalization}" : '', + params.polarization_binarization ? "--polarization-binarization" : '', + params.colocalization_transformation ? "--colocalization-transformation ${params.colocalization_transformation}" : '', + (params.colocalization_neighbourhood_size instanceof Integer) ? "--colocalization-neighbourhood-size ${params.colocalization_neighbourhood_size}" : '', + (params.colocalization_n_permutations instanceof Integer) ? "--colocalization-n-permutations ${params.colocalization_n_permutations}" : '', + (params.colocalization_min_region_count instanceof Integer) ? "--colocalization-min-region-count ${params.colocalization_min_region_count}" : '', + ].join(' ').trim() + } + + withName: PIXELATOR_REPORT { + ext.when = { !params.skip_report } + } + + + withName: CUSTOM_DUMPSOFTWAREVERSIONS { publishDir = [ - path: { "${params.outdir}/multiqc" }, + path: { "${params.outdir}/pipeline_info" }, mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + pattern: '*_versions.yml' ] } - } diff --git a/conf/test.config b/conf/test.config index bd0b4ab3..e30c9238 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,11 +23,15 @@ 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' + input = "https://raw.githubusercontent.com/nf-core/test-datasets/pixelator/samplesheet/samplesheet.csv" + input_basedir = "https://raw.githubusercontent.com/nf-core/test-datasets/pixelator/testdata" - // Genome references - genome = 'R64-1-1' + multiplet_recovery = true + min_size = 2 + max_size = 100000 + compute_polarization = true + use_full_bipartite = true + colocalization_min_region_count = 0 + colocalization_n_permutations = 10 + colocalization_neighbourhood_size = 1 } diff --git a/conf/test_full.config b/conf/test_full.config index 73c389cb..89e38ef4 100644 --- a/conf/test_full.config +++ b/conf/test_full.config @@ -14,11 +14,6 @@ params { config_profile_name = 'Full test profile' config_profile_description = 'Full test dataset to check pipeline function' - // Input data for full size test - // TODO nf-core: Specify the paths to your full test data ( on nf-core/test-datasets or directly in repositories, e.g. SRA) - // 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_full_illumina_amplicon.csv' - - // Genome references - genome = 'R64-1-1' + input = "https://raw.githubusercontent.com/nf-core/test-datasets/pixelator/samplesheet/samplesheet_full.csv" + input_basedir = "s3://pixelgen-technologies-datasets/nf-core-pixelator/testdata/full/" } 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 zgd3@mu<_@wnt>uVJydQ~oz|jKOy0;^`Z?+o2D0^+hp!@j_=nH5zG^AYBuV|wimv<8 zJ-BGiO^XI}T+0%OK+mPa+&L+!)PYa5H}wL${$XzJBCc;XV=Co{g^!)F^tz?jpNo4b zH_VuCMYaCaZVyd48bC?#x#Q0K4CK%<=X&Zv)V@IQ!g5ZVK?zTp+C(vj*rq zre0*ZTR%sn9`4BUqa`iQwuwP$!iTu9y z*^Aa8nvPt{NV`}cy5l$vTGknczicBgdPa#+$B~_lxB0^l39bW-wL`u?WXo>LbCrxs zHO}TPn@o1wSYvVPGZi62B3}9ADk9<9rEQFD-?ViCJHyk~ulRlQ*z07+ zmqT0+dAd*&o$#ah@3U!@BqPvJ}Ns=MjBuIqf9PCEedGznEA@4tG^@#xdHP z5}hhW*p9vTm8p^F2zoA2iJy%YoUT99TiNM^!6xPDkXY%@^R6F7n4GGx+4V!RemOu` z=Bso5M|O}5LA6BSOdLB#UmR7s1}UL!yoSsl_4aP{66T2X(LM*|9)bk2fjUQG@;XV5 za7g2iD)Klhxr?NUp}g%l7S(du@pSRzjsod24a*3J?<_x#8}8QdV|kf7grum zMHRS^M;MRa{Q64RKHpz0W`#~YUyQ#oG(l?D10Z|E)=~C)c9e1bRQzl_KE8L*d#S4H zGq*7)2eRPeh6YhjH3bvBj1tQl|SyY`C6lvas01T(9PNZJK6 zP3wxPDqmT-KbA4>ntJkBD=r{uh>P2dKe_5iem*i@&Qi7(JIJESfjBKGU&VlMgWXOZ z+grrgAg-ko&vt-qp3qk_{Jyj{S5C8tp_aWI-lcFeqdCorB>t+{;r}X*a{YZ_D7jsx@3ZLF5~Y0 zEmA^FHl-=O@oYTk=b{3)f#6wrVMR^aAFkWt`K!X;*hkOEJ}h?qih1@jUzl5Auc6L~ zxmKdYX`}A(wIiw@Nvhre3EN-J<9T?KI85Pa#lXhN0pxf~!g)YyRJC$%aOPVO z1|N}Vm(EBijEx+5zwlamO7S~iGl_`D(3_AYNv=Tp-B zLfLb!LWW&-P|dCrm$Sp?uU4-Z9Z(L)Y`Z^8vKv;BwSQutkP{9P7Ks==4@J%CYWj*9 zM}5&B_xX$_jmo8fH#TZaygRjP#vD;JIFLu_3CL=zp!gk|koyVmeEXBMat*taN>zb& zg&Kq-YKy~J*#7QCz^h^O!Y`}mn!;bvx)sw2>M`%V$C^-PmWPOs%LdR>R9a zjk<;fPnjUHaeQF}hq2MN56#UAxS3c@3Q9#gOvfR69IJ)f)#IIsnP!H1MzFJ+M~v3H zm2atRwZuz(u=p#QW$W$iOXDKnfSyYt`5~>Wm|Mz|({I|E$#NdL=fer>#3u1y5dSj4 zhbTlcNm<$ZXDm5+&{w;^Vnmq)aShdk!HJ)q1*3!J?c7eue z4Ayl-cd=DH3Kr87G6hlUw+4yt%YStriba0x#%6h8yWB{-wpg`bEXk>vAuT`8CMCZ= z-ET)=GS~U_weHAuj!N8$QxriRCC_$2*OZ)z1s7+y0Y=tKL9QtIwdQO;E))*V`;X)q z!yVh(pIlUb7qE?K#Tiudee6%#>#9!n7viM7$pyuCMEsl%le^k_Q@40@a~s%d)S`(E zEoa4Rt!`>1A*l{oFdqaZ%8$Gp!HH!0fyIoqj-0fBJZJCd=cuTUbI%~>YWI-?Xf_iU z;p(r4yd|!ntJP(HtQYRCvJmF3CM-fcN?4UOu~xNlO#K4l9UutOL;i*TcD40HZNfNZ z48=KpV`9#O&p~l1lqXnxeu_{R(_Fy18x?Do2vyIpfsMNi==h3*DeaW9KFeGKVIEUk zFA=1Sbsa>aOw&?cN(-LAsQGLQI*QKv_J(QxZW9@`w79A$t3iTm_8RU}= zPk1~jn1_ubHVP*Y=ty%DSKZCk_LL+S4BZt3ps?hcWV7U@v&+g|tce!uuT zoaf$auXWTi2^OKA6T^5VDK+&=LRZ zh}nwN4f|Wi2H;M29qxDsS1;ds?$L2%vs&=*`}(}x?fu@t5*h?7mkz7o7{o ziz|$({9mgQP|Q^QNr%LsNmqXDY%h(Z4D5=5G#s8mXc;bGXjqNhviHGjue>Uo%4SRF z*bqwj7Nod}m)P&L4UmIEG5T06`^F6ydHyGsz7w|bSdf}FmmV{OAIoAn zvSLZ+%SiQOM*3+%Bp+W1Lg$l}=r{Uk#**4isDECH=%jX5K&c!$Byp5BG?w8J;=YkIeXoqkj znKUFjOl-m^nECRn!;La!Lg$gJIgh_m;Fm}zxFr*;hzA!C9k~v(P>w8rpF(hXh1ovr zzA%Rm`6u4?vDUSNLT~;c9KJVF;WP;$)M+Y!vNGWDe8gda@!UuX;bF}B<-Nf*2T4sj z3>#r!`)cWpK08bL@-hHE@LQROyQGIdK{mv!k;3mAV~Y*& zSx9%5c6=H`R2c<5TZom~S)T3I8*R!KE9Z zGy!Hum?_Ifj#-ah^FhR$lt)QpLd z4Z=r(dZzP@l^;2su|VZMmnmOEH~2N&6&pO_5y1FY{2%~AEy}vnB0qX?;I+BeKcB&f z|5-n=5l=bT!BIq+;RyxX6beD)7x>UAtobc61SA?P_ozwGiB-Aj_c@!Lx0)r0&$Q*; z7-Q3p>Q8fJ@t8ETi=ab%YjAt}qA~>G@Vs;N-`I%rADs}msjm0>eWY*01Gn@It7Gr) zvfk|JHY~V9eI(H5^?}anqY4?%?)Xku8F<& z>_)a|3WD-J7>6{IyHJ7Ny`sr%kPEeFA5=8sz8I;*LW|uf$ijVCB$3K8y`x{FJORg-`CT zC}*oRScJZ^5!az4e_~k*L8Kie5o|%0U=n+}6MSoXJV^q{avZhx_N7Rh6~0qzf$Y&r zdu6)*)REIY#^T(0%7wuvlqQEMvE;#rG+58^o-`ukh`jLP##HQy1~6-E4c@rB3Pqh8 zDUnBX7mjDFaBO-{#bn&eWY$}&K#}-hW>rwhHS7<%)64c=7yoZj1-pKq1+iGlPBJuV zKWWI?fcdcbKl5WJrm2fffh~(~uvkVjp*vVr(~|$L=|8=URvWRpUf6Lsh5vzbQvm?> zx`zl(i*xr!4lxhdG3~Y`Q1gGiOqdro9<4s_DQ8>s)cb318F(RE9jSx=U_oa)!&<@6 zW>xI-V$Y4~$-l&cpIC)?eD<+JdcA$LeW$*9XCE(FnjzJSg_7=*jN^W1@WeUBcjDH4 zDPL7o!srDPfz9aXRG;qPXHjo@CM^=WfXt`E4qzoma*pJ40+uSL4biBj23qPqe)@#A-O+O882J9sS zx^ICqC-ENXg873a)hiL?Yz@}dc-2eO3P(wUqi2Mlig-`}Xn^2<>c-!c)nYA2ANpSM zuX$`hTok?gLtX^Ds38~f)saMV)hGjY49J#-6JXcd)fmPuT>MU&!;gXb^H(>&Zpei{ zD6$?;nhRf>Cl)J|l?%H+@7`H_THjT#q2NZFv}4$jI?{y^AFw)t(<3NOQOC{@uK$`a zoPZm>!1K=HBz(h-CC8)qCeFF)q=Y?4W0+Y>aYM_;Ck3GXj6bx#QiT@aGiN1BTVkl{ z$_soMv^o*z|IS*ibD=5ke1x4mH+90p^=6jL+vCqdmy>bpw>AThce8)=@3y`C^n)S` z2As*5mQq-ZofZMgl3aFv4EY~!kc=DVgPk4%_|XB9(t z&pkSvEgC-Fd2cJ<#I~D^+)wy<2|Dc}KteTsyumg~<4T`RTwO73uT1x6b7?Nz2m-zv zqyOe#?uynui^nat&s)saS#K051fD3HM8_dfRsv_4@!qD$rGwLBE5@Z2j9$ta(Iy%Q zyI?(ek&`*!o}zI)2_mMe+s^6{Ncvh8eAY-1@6{vYFcn>k8*Sfm zy$cr$g*55TbyE3$Y-}MsJmS0A>(>=$`3LA|Pq1!y36T*z%Y;3sBPxQ9<3LzLbMRC2 z^lI6cc)`I^f-xhbbhyc!6GZwVIRv`9)wSdf+(mLG-yGJyMG40l%UHu-3#%X;qlpQ4 zI#_zNF=lp0{;4(>6BbnpqPK82Py0fT!H1JSM(`6+d>88_BgyPd;`e|gGv!)&v8f|h zKFe}=GlJEsk%FxPR7!jXRBNR>!wcL`rav1Gca&M6@ZFqE% z`4Mh^%VfTB>88(OnS}XjA%!~1TgzdO3p7|7|926;mpc4??7wq26+B<|^nJ2fDzywu zFo?l1EdtXHOpk5ff@z1DS-<$rG(ZFiXuFs|}Y34Kpxiz9w9v)SYh`Qlsa!LK_OFPk$W_-wQcU; zqnMAG5Q$Prs$WQkS8`znPLX==kuQ7CiAW{Rl1k9zUL&)gL2Ky%RI6%ljx`3Lym78HOG_r#NWZ`h;UmT; z8Q;NB(OjT-ypxw`C{7rz=Ah6?Ilf*d)0!r@p+-^-rj8xi z_6SQ&${Rp@207;QK;#<376gviKcGm_O;|y6$pBqF&Tj(sX+L)PBhju%zN5&)Py{q84S1 z!u8GCK6^gp(|xu;h?PPKnUh7Lmhp+RzfjWm!UtOhw9(KveIW^uIn_ z_4XfElclN`*ZUd3r=6|g_*_mCYn{^noi)emliSaY^fz<49-|%;zdlvkVbJWlK+ewK zY*{HA(P$@!lXVkSTpg#-w&~WQVm=nA@QV~tjbwOd-7zb2C?(IOw{6?D(sBB$ncUFf zOE(5xIKJ9Pt&il#NG9BsH`1^QjnQt{9LJsje&!xuc&TL(@ zAuXdsJ#S?ulhXa4ohB~W21ju2HEmn9;Ale><}Dj~ZAt1pw2jd+HpPP}W)J-w1RDseHl7A;l`H-f zBR?QsBau>#e*U!E>9Dp@ArRa{F&#eiGa?C9X0D*u+HD^SnppyBly#h5H*jF%%7=!sw59c9vD zehhfcSO<-^K!2XtS}}-6ld)lbeq<@ttMA$#^BVn6O>T$3LxpcObE-NtEn)SH3DAgsjf%Hy@L@o z>)9|}Njhf6u=~m;LtCH0meC4`1j`X@*Usz5Oj(WAi)jVKP9?vMg6!#`W_aJeyzA9E z8Et=&jhAK;rplBlx~kENNni)V)@4o#6iK~r3DI>TTeDky--t|0k4HK@%pgO9xQ%UD zyh!gX7B7xtM3{)5K!6}U%CGpooZ#bwfJBA8TNJ|w2h=#+HMy)2qAkKu)x~cv^MTR5 zgRFZprT~ARVEa$0VJl_teYh6S_m})2e(B2S7D%gA2}!UY_BEL%&Tpl&tiC2nrB;xd z>BKo49MIQG#xbHH@XVM6HDxXHxI_x8HLWh^aO2<0Q|I4KOH9SCksvdzy{{R;Q_qkt zt6QqxbuiwIc%>4LsbH_z77CuZ(N3Eh{Hjl*tq**sjUxsbL00hB%O`K$_t@x|s{n4T zNd=a$$ae5z7;Rcbu!eQO`0qOBG$j8>tyuBKRunfzdwqI*M)DkXw4BTY9#k;h5lpSc zQ`n|Bngm4zP!!TzK$%?Z-G;AmCHO7HG zJ4a(MJnx8jrjb>P`5nQ+l}d5)GCk*Icu;gi*^oOINvafMb|ZIakvKmN9Bc9!zuX@| z8c!6fcJBtgI}cj%Z*hu}cIGcMT*eEDaRt3viG8Pz`YPlFCsx%E3 ze|0qp+oBM@_a-zIsY9^~(nq26QCP#uvzBLITT-Fz1pxTVGcnL9>X6Hfuvh0pCi`ERa%Md2+UxG~gfM-;9Wc)ekf>K{tXe9Mtf!(RFbeqz0o?=Tkh6Nvrj3gQ`mk*o^N zm!-*o=#C|``9cYa3e9*JN%R@qkelPrEPd#e)szjS?u45l-g~tSiv;RefFk~@$ll69Yelw0B?`5LzC;tmCJSyx_+HqT%Gc-2 zhqa7V;q8X$f6QtH%hylOT@X$Mzo#h71A{SUK$?cZ-d!_6boCTtWx6T|zRb+Ik5lZx zC5dG%G$-g=G*YM6F_`aAlH>GIDIqE;_y7oJh498JT}+&LXR4d;+c`H(r3h&!=?z9x z4Q9TKSxmY$n+qmpaZ(L5^RA7HmY@KNAqINP#5>dVozR%cDNn*ch4az#C??EvxggEz zsSOE4zWxw3&F#htFngbgdsT{RM~3V7uK!%; zSN!T%2CcRzG~5cBOfItKldRJy+p^9QA@i?}dZ znE+cDmfM=j?ciR(FH$XL?toJf-0P#?``x(7+V%+5_T&Q}4ryu>>On>|O2>w&hEpt* z5)Q%Yc&uncx(~56ht=CiOPu^_jEY%zk8Kpx8pu5Vbwy1^yuRo6Z{#hTke{V6p)&Tv=g`ZHv@IDp| z9-YRIOoK7?Vhu_H48|kcl8_9){<@Y7i_RF`qbV6-7s>n$_Pk7Q+O8Ny@3HclM47Ac z6zq|t>*>*jzQ1Q3l^j2@k0ZK+I`N0qp{^YV!oBYzZE5 zSvR>;F(^9oMiSA@_%a>wFdl#lN12STlFn`{Qmaf}rDn#9RS6j!Q3~}X zj=UMxLXAIWT*~kt-mDJCc)Cpz=ibFBQnyK#3pFG)Am4l|0PbQn#eT`Vij|AEU5G%h z$?8@IdZ=eNwR^{eh9<;Pjkqg_&CZ`Hvor z^fGvd$l6WXOdtBDp6J#m__((+#YK7r9MVZZf^jwc^VldYv>MnCwxEHmjCA-@!jTj?aPs5l^liizJ(^&FE1FpZ{Ym2#`r~ z3$WnCaEA?+aPxO%`B{1|`gSd*Ka{eb%NZ?ZKVE^@Xr40xBKY^cL=YK*9#^7FK>)h( zQSI76fgkV{B@bpHxC!faVCy9_0+fD8)Zyl>Oz5wZTeI&x21V>$btPM->8wm90k^yf zdoyGD<+a&Jz#pF3h!1alyPUX(tHDr~S87UyD+l>$24NU?oQO9D4|DnM<<{P-5v z0EfE~)@KAjemmaKTCM0`k3tG8krF!R2_~LbrBR2%teCVPh=veVmQB9mWCw` zRBgo9P5Zjdo9INN96~`85TLimeAWEwn27-7gW?#U5e%o(cE$*1-b}L?*H}@0i!8#D z>Uo|PP&r6F`v|C&?si$#j^150fj%x~5ONvfry{1>s%V^z?BIVI6%;awoqIAAE+1r% zr%okZN!tCI+p9joS~>M{6SzZ;3?!2Dhs9X!)6EG?W`;1=K2r-_=(Wi~M!Bb|OgmT_ z`2VC)SopD@PttM9_!%^JN0ir>nt%q^UFnwBe^6%XTT+3YDSb?Ycreb%B%%D&Nya3+ z2w8xJsD7FRj?pAvgW`tTb`Y4^yWJDg1&-?3wn>%6BsC2_CNkshL&e|3s0g6 zCp}stZhun&7%~}K)l7`s*HIU=ZT@Ig^~ciyxVAo{|#log(TGcqhFz2n>YD}PfA{!SqL*%27i3L zVt~5xwo(|dpyWNbTT%Xq90l-OjX0{cQ19gm4a+43;MeNTZ=^*pQErF466HVSl3n+B>}KhjI4M{vNuAyFoXS1WABDQ=ro#C9LHsinW@c$u zat7*s0VfDf|5M;;M0)rQl0tU8yk)AY$&F5i9w5cuIvS^~N4`8Er&8j=LloSD zIB@a!n7j^ZL*-A|ES~z_uESM3XAG>{e-s_b5@Y`0H<8?2V(vtNLcG>P#L70QDc=)3S59YTUZanCyxMgJ9IkJd@Js*GAR@QbFvEkyRt*ihX00jFbI`A{T@Hi7a>$ z9dv>9Zj5Nb)QrZRk2L02K06WlI?fU!y<7-R6wIRSDQm0??g)lKHj%zN!@_9%(a0V@-q0Y8JIgQw0k zW7KL3JY)7Dk5n5?r)jU5j0mN7vF}HdGu<)aLXMCHNd@t)OBd>dOcSQhVqu3=2eTsJ zgNs889adQocnYQEJQ%-no23VQ4pIz4bPKzPwc4-DLBR#uam?%N00hJ1njr|mOjTE{ zuR*ca{PW6n35vM9iK!*t8#DOOToBZaHj4?8k)~387a3NBLhj#R<;uK?z!bpJAS{wMPPYv6QFvJ; z1pm(5kCd0#WeWoFpwEhy?MR{TpwFJvXUtWgmeSGOP~>%i;$uC8L4s7CRaGSMz)fV7 zUH@X6>SJwD$y@wy2ft<@D9oe0{#fa=1O4+V;?Bu0XBj9@M&lTPmY1jKr%$u)t-%0H z3-xW%={G`|GW$M+@#1R2?cK`Es+e7a%3W&Y1={ajI{pp38a*BZf*cLMk@lcca%YXg zlb1((z53>tdl)5ewLO~{@W(aPGbV;*m_@yq z!qTY3JAN1dwSq6%J#P}Te0+5klVk5cW$!ppnl4pN5rBxnk}NjD;mr^O8WxI(tuyk`0_N-ZINriG=?|u0V*1~khV8VY1|dGfHsb!! z+(Ui-?Et=|dkl0Y1P6cph=LaS8TfA9T!yz?PpqW;y^36HLg)!o#r+qiEHMP~Vi977 z$7(}MP96Xy$AJ4j@)5S$ z2snd)MC1dM)y=FAI%aa~((I9!l;V~J2~%)Ps1pnWdtN_h)#4y1#Z|)Fy9R6MzFoTe zsG`5SF9Og>19#F$6A!2U5?$CmJUloKIWH2K!Pd!8Gl`-1B`tWbEj% zwiRkjD6ZDTM|sd?csJIOZSX&P3A_*kqq5%5i_x!yzuk!p2uJdXg!FMp@@_6aB7IoK zTfZ~n1_C0XsCgX-MJnqGCJnx&_GY%K+A@wwo}wu?zoJ5#%SCTshjddm*NlVOA60_o!t^8= zI0W__5IW`8Nk&UmI_i37>*#cFxlw+_lofMOq0LpPidbt%JRf+;51US0iZ2wkzhXBU z{sXo$ZRM!4y-fB)6GIa>mYK;(pHg%hKn`sr{vXS;Aw-_P)O1OwGV)Fmp4(3wz9Z;JL^LazLgBqs3c>31Ete zkvJ1G`mg2RFVoXBnbHFFXWG}DO5nA2ddz$^Q8rNcLw=sroH}ESu(vXg%7D4dr20c9 zVNbh2>kz^V5OkSK&mtMk#;7y~;;>bHPfBU~h1=K)Dez%9_oT_M9oq@hXPaCI-KAEa zu{h^qo^D~8_;yJU*(bQ2%Oy5pYPXS<8wW+^w*v_EnVFo=7Mxz0CO69%AvIkDua;ml zz0U!d&tone{&(zC2X!Ary4j(iv_c8}woL+hqX_34lAb%E5GR|RK3+PiU)tc&EO!lKt<)6Q?q{01?$TSpi z38`d+Wo9~JQFS7;L2m6=S4)!eGXEzn&)k-^*? zd1y`4oT}4%G%!z%}xCXHc>M$mhmTVAT336kckoBel%Bj z)&g8&jvAf@O!Xhv1y`%@vuHDzBU2eIKJHE-d^ihaG#+dinEZ??qTvKcSlIFl81&S% zoHEM=3Op{yn%GAlOe-^MQu7mA{UvC{^itXKzvVGn(In#i#7D#%-g`5-t%^txqr;ss zRa0U@3P+4G!CJk))@m4Yv!C;=t6-d2%gT=&k-LlU|HZLBjegiyu>*aHJ!<&T@twR$ z^k4HAr3$u8`D~&vUEwT~q%_-kU^k{QgYV^l6xU@aP~?)2R7Ni$;PRB>bq>wO4x z2Q47emNCk?Js?qGe-5jolGaEsMPNIPaN$dtXL$dp|N+K@#;;e$!}L;e9} z9|)HU8%z}N04-t!fy*cV-| z&}2yI^chFepYwSOh4h{7N6VIfD{fU8et0cv8q!pPWz}4dDhN9|6I4wEbU6S->l0aK z?`%!J%XqGI<%f9I^uH^v<41c29XWsR#SV7|oO?9xCy>;&NqxDJX*3)v0PF5mQe}Es z@{;McY=s=QsWN-j8l0i~VYxwu_RW_Ls(MO$M{F8D_^*6~WTdgNv!&mSpEEAgV7HKY zTz%Wg9D9(mFuZm&NL&x$k&5rqgW!Yx@a3u(zOIv;Ue;XgsP!R%QYvY);a(757zH9- zc4Ud;32BE97bj;-a`!?>KVi0llNL>XV{9ku{Qmt2^8w^JR*d2BdNFU}#jr1+?>tXidnE0BuK=S-> z=h>P=fbRnz5T;}T#2o|*n;igrz#sHq*Bq9%ys)H0F?pyPCv1_YM@pkxZGk0jT@WbQ z5KDokY=z2KTuDMU4aqZi^4=l86&mO^S~CWqFJ#i%2anIL^fydaUH znXJV@%IYSNofgsOQP}Cg&4d09K3VJd-5y#GZ}o0}XOvHnK&sdphlZ&~#{|6}+ePr)l?$_|NKwLRKN(BdZ3 zo#DJ@U=>sU752Y!1jPp&lbVL#t1ET51sA7t1e0$u;%X|Ct*=X&mew+NwOB)Prz=`#`&@WnIu3xwe)a~C4 zL3v7x3@n3V8V#$U@_G!`_`vmnCMluP{oO7rK%lLl3x8yU+u<%d=vI7RcD(rIYmub< zT~sKdn`Pe^#RKp{qrZlIH+Iz?rGH+&5V9Psbt{^s~I1Ml@4D2Us9a; zf4SJtwo@OBo~(qNojBF^%Gy!d?!UHHei#89mXzm%#QE2`WDj{{{~$+0LOqi*%6P%0 z%3*@i?u*OGyVk3B*A@ywsLuGBl2XYGDBy!kJtwQF*UaS`^K4pW=iof1FET}khs3Pk z`NJ&y!b>98;h~${_Too$)x{x$R6!8lWcpKg1iM0@TPL@5L~j{1C5nuVnU4R5xHDw3 zqy^a<2LKeQ&$;g-_YXS^u5A2l7-&=BGi7NvGn(RPbh&U4IM@v9x)hMm*~+kBFCBdP zu4W6LX$?j_MX-4Jo@9aOZxENUak7i;55J?NPMBy`KM7T5ki?o8-nY?+u$qaWER8=g zX0`0P5AGVR99*~Hw`{`*p!!-^knJK}Mz1=QZU%3}(R)yvgcrj?|fbhq#uk$67 zMp4}MhtDq#SrBar_6ynA{zL$l`8iMX#AmJRP2+R3}^5MRaqpmbj8GW4!Z$hLkza1`zr z@k1u&zx9zVlB`!`#B2Lg5tCAMDrTA+UfcW6Nk5kMr}E;uAB)ID3+Z}V$xKiXWLCGu zb&@@Pb=!WfDCLy2e{fUTg0SW%7c@zmHGmJkn5=1dILIl&6ZLKPV0MRz{m^T^tnU0UCMJ`aMmWMX6AQLqmL;?q?P zsbsx@f@LdX-&7D>Q*qjpw6tK(m1T$qYAVZXr#d;VCrG*3N1uYBJ$*>h8d-xGYpn=o zUXj?>QLCMN@Z(K7T^8!Pfq%bg=|gHJDV*VtQ|Rre}=?E(~;cSh>N0a!&!`UV$bA_ zrNERQ=kmQr#)YKfW1eZN?^ZaROvEf+Yg$8b;+I~$(Pc$u*9{X-G#3IEkEt*`$QSVIog6J# zA`y-Qp5M6VpbaKYFu}LMRK3jUvBOu0mF2z1`>m?1rp5!TB?KT<)b`${2^}{Z=Kap0 z{@V3UP2Cu&xngy8UO?MRAL3Ui;OO2=NV3gbgfYwkP86@NxCxSNd?D*Z;Zxl1p2TPq zrfV*YYx>zPG-*J6HTk{i<}%v5b&p^5)+`-ncA=7+ncNZE0?ZkE3V~-}!vX1E{LVMpgh3KmU##d}~-$~?0L z!|)PA9W6o#giPgsU|Bd3WY?@A&mz2kBdC8gH59E4D;y?C1g*@8X)44>)LvUB+KSRrZn=Pa@>glXfFN%iKv9F#NG)hABKjwmrQf`7$ zE^WH##}=w5_T5xu{lMbWSxb-&^K6pkh!Q&d0xdri^MFOgdH#*LE+|n)iWM|pweW{VTV9CFXr9w? zT@lQL5&`5YX#i=(c#8(v!80ed^u*m4}!_GKMeCmXy@wwvgds+K#6l{NU|Do5{(O1B!Z{bv(e>!|OAEauS zFeCzQ!T5<^)IA>Yesp68z2Lp{xE_t0@12s0l`&0uW2#aSd@}jt+iIPR$@|wAI{##s zO~&Eqz$0ku7AcgPbRy%=czUPh9_h?#Y7j1-_uwi+$vayFT~X+LPFx#MV3UgN7xq*W zdRE@0<>|@hX2qG>alJKa2Lf$fQ{-%T4DfS`J5Uf9P!LYt8I`KK-+Y^67+c?upqH?A zbu+jCX>IsTy&Mr$c#Z{Qw{IN)7_C$@ll$C^JjFaM4UaBV3d+sjB%0sMUs6dF*N}-xms`V{CaT%m*h#p@O z>BQbq6`f=qyyS0ry8-B=tf6jBpPis4XrLe+l{eb)ECZnKA49`I8v$CsCnT;z#CU*a z3rJ6pN9ZOU#7HD0wcJsit~-$nq-<+5xq1!z^C_`6szx(sQ!bfJfwoLDM^!hV!6YSJ z+0L#W|7eCMNd}#2)Rrn)R4P|t<_mHSDlSf8mDcyxcR%pilbomaJVaG_erwu*dH6n; zqfkc$7&t{y139)h%fUV|pyCnKR07)+)&mzNl~E!yFB_feQ(|~4lV8CVewB`IK~pJV z&M*5ev^{b(giYFsq`_n9ZtN>{C@9!j#P?p^RxU&>uHm3yb=kO%=F>&qmOf-m(WdU_ z|GyTDdlZ_dFE9Y<2rhwQ#LPA(L4NcFlH`}C(gvI9b*L6E0yhqi4ydqdDEI}QbYJ#w z6s3BOr4oJ1EEBU=s*~`r&>xDG?ao@fK z-5cUhSAgf=s%@m1wL)&1?g>1;v`GxC45skT;j)yN7-vDMotdI z3OSDKnsivlGMbhGKdZ2B)r5|NC4od58dXW%bW&>Fm^=Eey|!iZb?s;alW-ume{ME6 z^-@gBV6DY|joezuIF0uoWhvV7FGr*jd;7XXF#8r@)E{3E0EdqiKw}A+tfszOT1xAM zI@Yp=1WjEk8mu1Q_};EU1QG6i8p@7^)KpTH<|>_KzF@VKS?)}5?*^>Muh{Dbomv}C zZ)MM%Wl3xss_PQ69Hptk8=e64H@5$<)w6K{ka$v-q*jkReP%Hpze^vX@;;S^oiF#p zP^ZC<|BZbn$a_rk_ND!%!^nzsbP&HxMfr4&>`&zRfbmN4n7}mH0brX_P`(N#XNl#< zmlf3~Eab19m+!$p{M;v`C0hYbGa_hx+LXnSpxzr-XRM%bQN=*EL!~-s>=JoHgqoiD zmVUtXU2Q0#koE<;u(ea_d7+7=)KNo`nZe3H+js%Zapby%dzMdg8Q?dPc>0LC=XW%$ zA&94IY=F+HD-W#y=xdOp2alN6y9Fl0=p-sQ1-ZEslOzb)HC zFhk+y8%GUGuIY{$8=Ly=tk*N+t09D{jR&g)Q+MN9*#U%VFjBCoYKH{i_rn4lrfa>o z|Ip`>IH&N+O+v3&tywmNYXlqo#0uK=MYXTRWm&c7fih5AWF1K^{7`h}&tQ%WMSXlH zROqnOkl9@Ep_(hq0c+Lm%78cqD5!7Hhd0}Sm(MfNEQPfILeGVu3nP>A1{j(9C!*9% ze%Y-f92R*nz*5!ps^FtUL*f%R2QFQZ?qg>85EhKo2PkKZ?fG5MUQ(OS#3l1T7ru+F zj{*hHy1JjQSmy((?D|kgxB4pGy3VpoV$y(Rb%Ou@QQXk+LK+jk1>2b~=1%HZh4Dy`vziB=x^Yls~C#>020lv-;?LpQ~-2kH;EQQ~}+TdG)vi3@3};f$5i3CQ3^ zYuR*OoV=rykE7K;8F2*>kUmk|ppqG+Wg5r&D9;dTq!bzT=#>%e^-IZIqXezVLBrT& z@UWkNe@2~93z#=99oN6=eT_z!x91M{2FA`8&61U;EHu_+{`Z+zQ}A4Ix8FtM{{Ptf z%BU*4w@*+36#)eWk$R*XrKLqWr8}j&J5&UuyG!Xt>KwYeI}aeufkSuCMxXyXGi%M4 zS!>pOdOykWu6^(O>iAtNOJpgMtw<0u=ihwTrl^KTyoGbW!|`F5VD^;|{;*Ck`6BwK z;R!>C7GoQZuIm}L!o>aW6XTd5)NV}ssjS7%Bne6|c$O3=(!|DcO2obc5h<%vtQa7IKA^Y(eaz^nI_J}jXD6Qbc0+zw*m zGAIlpF_r2+duF^JU?lZXDB#CXv2-iSNV9zV=2n^iF}4MD^%w0|x+=}D5%*+(Z+p)n zGcHG)kIj}gk@-va5Iz_UmCi7B(sM-TG9gZ}QMBu+aG7*L>S^TK`ae}ldtf4`t3`*4 zS+Go=c!Y$kP>Ok=f!pk;I~OzWHnjn_M&IKy?9^)CuV?9YyHgdXu4(;7Bd5 zQBNYajdS@nDLd2>L`LZ_uqL%P^s?e#6x`!(UOu7E#8ZB2dT(B!9;#i)q>$wuuwA^h z1As!TH~iTQ%?dE+i+}q5Ts+rXiQ4Zbt;Os7rw1K@bJs%jRGxR}QP$xyB(hl|UGzI{ z_&}Bl{<|`5m=#psfJY=E?{IQ)LLo3%Td_LJuKal7>!>LA_aF(-0WAGk`b#2n8oQuR zBXSrK%_V)B-RXe|Lo6jl_-`$PR(VcOtlCKd8NuQV~m%VsU#5A;sxAif^%f2W!v zV6na%<#KXl>0(A?!t>d|Xs6GdrDS?=5%hQbgnWqO&}rE3oN3R2{281Vn#d2EoVz@B zFNsQTDcvkO^}5C)G@p3%M-UpQ=)qV!vgOej0_~u zxVm?()qPlQu+IR^jSYtx)EOOxcHyV4N>Mx8W1m86nCC2Aq}jL3u;Zzt0>tq%$*_Zg z&GV8S1T?JU?YpbxzgXO#7f|@|2zNjV06!N&KF*F8sq|(Fg7m&tlTDpz=v;hi6_F}?!{@{|?Ly{}xL_P%Q^5Mf!3Uv<6(a-(z0BoMwi+9SaqTkg#>?mqAtcx z7Vh2pH*2+T)_C~?zp_=^DTZ1|e#lm#W1_Vlgs`z7dTFc5)y!=)yBXI-q93sE$jN)W zci(K*?77VK`%s(xh#R+Q~3K z_SwGZ*lrDT=#Mw+#TV5Lh&{A|&l%X$hAv(%Jbc;)oh`WA`CHg`HO0zn^yJ?xXia%> zY$BfiLyFS#=9dCN5Pa)_=e%*kN9L;KaGTbp9fi%{(1NmOTlM$WOpd2na~su$2FzP8YrqpiD@lmitMf1)uah)UIlDowLgx;4CIVWA`=~L--eODx>>w0 zq42Eoza~BAJ$%bJ8Q@=ev~=X5hW6KsUuq+grCk-ylG{ChyStG|2W^?vp5IkS1!|R| zJSPJ+XDyG$!`L6Bm17Q=bH6bt)CN0vhdsU=$w}W%*ORs^itINANY8Cb2CVGrJspQ` zb)d7%O^4T_1pw(B^m`ENeE5N!-7XZc0m)L83yNq5Ii!L#^uAxITrXC#pbdEI`eu*v z#E0BJaTx@Uo~e9t8hIOS_`46)_Yv|b{mzas8ou{kUhRy)ro0!yLl7r4i6TRolRV}n zz-b$y`%$$Iokcs&O|=MfK(P&vM=x10xL%c2mnubaFlTN1%ctRr)FX*W-I!^U`wo+i zI-^egAkap=9LUdqa}}h(l>NB8Yf;Z7cl&ARwr@Ayo=ud*FQ^{V<~}t`@2c&7K7)kz zyBVdYim}v8y6~A}!9RB7>w@1h#(aCtmq=hdK;2j1FUGnr_YR@HWSDx=ZKq)<6Hr6Q_OlXKN8P8$@+TzJM)aIEAUWv3 zRqdt7&kapo0e$O~MVW5fCL9lD+K$`%mK__~j;r%g3SKioa1-)p~6CIl7WCx&<1X52k`&E#vUN_LjxZ=#tYs}e7C}f@Xbwd?wN6I)TQcH2O z@5phbWfo`MPTKAqrfOkfq9=v|)5=zU=+cfCgud1f%5fmbfuHk`W((P-W)v1iwI)-# zTTw^evY{)a)4mqLo2YoA7YM3Gxm#068=i-tQ=<$RvO;o68E$ctQBJ1Sa@yiRVIdk} zL=b9xV0Un+?$XP$2Q1o(0S4>|1Npxj?(l%Ge|wek#Dct)dyLE%#oYoGJE@PoZ|C<; z@)J&;GVmBE7WbN<@i=`{Eg{7Dbq{hzio)Y-6WX=!z)WCDZV)D?Ctnk;_MI}L>ZwtX zq3*g$rM9E=EZfxURP~agWyVx(C)$<#uvSu-H&`7L~=IWbY`erWU!GmxK~32z&7iUb+4*)M{62<(fbyUL}X z;gLm}Me|4C>eTss;;XQP>xoXUeV5lBizj>0%{g1R)I0IYWtBK63}X;0EhH7hLQ8V% z&Om<@Nl(RSGmZ4NM3d2HhT)ech{7#I(Uv79d#if5Ql5nb4U;ciMlm(CS+y)@o4N&_ z{#9|!`p$5O@O?)9JeGu3iqbtzYq7Wpi&>&;f(%-8*3}2kD_Px)daZ;a znk{{2M~%;IcIhlz@B$u?f|ir$Ee}Uwu6A6X!*;bG+>FQSp%Jg5dz~>OjdfER!Hgc2 zT^048Zs#3gx&VRG(F35LS%gfHvX}iqLC+*XDfZHS&(dK__!}bD{u5%5pkn z7n#LZcQwzs7b~;B)y6MFzNeECGlF>$ce|L_o+43@7eQsrt6(qxD|?McH8|!+ zi~&PUPFv{vaG(@l1+Ui{n-B=zCyWgUsRQv~->GuKGC1xZjYvO^bI=im)K{aT(C@qA z#}k2~RC=rwBn4zh)Cy?h$VQQ>9B05SnMGgDWEh*k-}&|hnc&GufLcy76!=D+pO()y zOV6e(>{dC4K*$4dzk9CM>Y`JxWx|WBFFz^D&<{W;$)#;>9HC)^Y0^bktoQ4W>w!j6(8#7d2(>HFoYbWxPa;=9VaWbohWgh0wIqJUyA;R;LdJ;Q%B>TbjyysI8lR36tBt z*F(=XO&(Q%$)4OFQXseJpCeeXN$>+qW61gL^>!B8eBL!fr#{c7gZUD!vgLgBYtI!S zXjja|Ll6cT2_qA}pijQTowea`BG`{%3k?X@5@b$NY`xD?3ST+0FjMxUZ$JJg8^G?S zw~Ia13HUvWu(o;x88d}GgT)xtGEhbJ3XN_Og2@`3`$~T3kNiRX{E+Q^ne~<{-`lqr z{HS=iS}K7}2@P4>3@Yq8rqv9HtLpvr)HJtwVkF;*rWtefVj9t?7M#iwaZ`?h@=sv4 zwfFU}Ei5Trm~;xVn}N$)fwy;pv`aaXfTUMiW{s*NVx5xmAPT3tJHUh9NSUd%+&HY# zxTMlL&3Kp3e3wt5wzgX|WBPF24sXDiDOohs$f4-v{q{2Yiuo^+g*TFgl8lZVV-vqJ z7Tfl^6QX?fo4Z#GSaGz9l`X#EdP{n1-QLt(U$$Iw`J@aC(U!xf4@(c%m)9e7zU!zC z4}7VdAlTeSKR)(VGCPJQzMyDAKe6#Rvp^scd|8b3jk6U-jeLDjbz0~5vRKWi&9lSw=8yHd5Ypk-r=N=*>&*L`*@5vnFxto1Bx7H98)pfdGR2n=eWjXGX?eq@pEG%q4pLag@G(l6N7amC4vea^al|i&J zo8DR}R@#f7i!z1mpj9l$6W7y3u_#7*Ctk;1O@MHwe38G#PD zXK4WD6J!+7$M8do`F=p4;H%MORtoN>AL4I6m)cIUrudR*Z*#v^Lk%)SC<6O8lf z=qF5psNO-g+DoF4qNl#1s1Lt+F2)K-O6F$0n}TiVFnd0FZQuw7DND&}`x&?2VW+be zzom_~X4GoV_&^Em=ntJ`SqcO3YRfQCKr@#(V3pLi*Rls#8-&yhpP@}JOnGZ{I=Vbv zd}nWmSOJEUkv$!{Z0u}J-TA?XZU4QlmL)iRbc%RTHQM_$e?g0-YfP9o(q!~+csQI$ zK)aoBALEJpAlRWN8Ja5%5zs;@9Z@%L=!8y9IRmRQ-hL{9+*0rKv)e7a!eJVPt$%h8 zvxlwXPV%n=toc+k6kgGB)4uzZ16)oi(Els1D|9?|dNg+I;Kvyr2u66}yDMNz{W9!-8T&0< z9`tLV5LKyQC`jb%NvOiU<7S9Zx%z-+2|nS_vTw@MU-zVdrvN5Yxqn*2m`yO0H5hc< zo?Mjk8+8TMg;C2?Dz5B1Aqd_vuUx41yZq#^ROedQSyiDr%6|oXUUOqQldf`eBe+=* z1TPO#@lWWV%VIh;asl>;g0>-AZY#M92GUD^P`#CM{+3l=v?B??h9y~ zMbgEK3L|ktg{6D<(H}cSKkutKzK<>;y{_P=omYFkncFbMmzW3essXsRB-@|bErFiYvPPVZ!)vc1PQ;Jo_0&@kl0D?z9*FXtQcPj ztMzyy*Xeb2Z>yFNa}rRlp@L4rW1|zNHFNrboj@s2ULkLv-tte{ciH$CTWz48mk9vt z>3;gh*>45~RB=G?or>l4@9C)bya_rZli4?X!4%^{8G0Xra}r?vb}LqHx4`-lEfi1u z*B0crsH33Mi*5^f(#Zkxv0M=zRWJ)NKuSM`p!~TuZ)JF-ZpEN_Mx$H@R^oUJwq&PF zXqpF@7wo>n&Vy0BRkahDEeT^h_1*B*3BF1nqd!9mt0btk=9%&sqL0g78^dK&I$Un0 z)}&%VO>sHP=(L831;_M%{%hVcQo`WDr-<*=OcL+ER{NuA&u}OEo}J0LFz=b4z>`&#jB*MLq2J&h!&9@o{VO zwYu({G*vbgPE=Qxu5zJ}!VmFiJOnOx$?15~i*MoiUoSoRKq;xb{iFVkFColaGzrqN z@>(D)dGes>A7c6{*LM4&*F#VDg(nJR*}x2?IR?4DvV@+1ON zfuGxXg4k8DO-p573F@$PwK^6%qc6$Ol*>RS%d^KeDH`{ncFrpoa#ww_LfVm-dbo)! zN}KX_*Qg-eJhvCZzLrP|Y|~@X&Xq*6>Jb)Mo#-kBQwo)OzFd&Ne^R?l_YJ8F!jZ!` z7u8U~7G8(S~@urM;F z7b4B;``hMIlP^ua4Uc16d>O9n8Jv5w0y1}`4c~8jHO&SJHBd24L8k6Hn4Rr{AV|=S3HYCloaak< z`wC}VdCjdWA7_6SXq0pqgE?Y@A$+F?N4>(LU#-ufDpwli9}@v=&6tBABSl$mx6eSm zYym_5K>|URD$7U9KPr9aJq8;WH-ac_UusZI!9EqfaS+c$7YR^V5$QyFWeg$jR{B*H z4a?hwrRGJqS|j>0NanjXQn4K*Pu6f{_|1i_xjrH?!!ws9Lj9w`_=A z@pXIADP9D)JMFL(*+HgIoweJ3Hw*{pgB4)VKkK zdwNC9X6lE|b^zGsSGab(>>#KT*`tn^kqRQ~OSE#1W7Bc^u#Qo{gLZI!WnNyALdg9t z=FQ>IVr*mnYCcH#iPx>m$foh}*%2;;9_(sg*SPIRPiq)yx{(?5Y%xorkii72G zv$3bKYY4;r{q~+Yw0drlXJiJaPo;(TrJ7Pe-(pJ?vLR0#;$v0IykGro{+7<-2}dv8m)YC4 zsesa{czQQjDu9Ldmh99J%9}1_5ulTe#mTnV;5*2{f=w9Wn*A+_xGPUfk`r4GB;`aEQkpd)ZSj8EYN`#wd6z05IlD;7Z|)jhM^WA ztus>Vv$o>r%7U#>)(htR(8rRRcRmV^{mk*()>Zd;3{J*--*OC~DdMH*YW91nUu$@P zY3I@%DnXG!TGKa7Q{{)wyDpS`Z@6vP-JITVZ3N>4f7*HIjIf4zi!W0YT*=5h%tP6G zevw9YYww^pMsHrTRb!24C}pXeA&L8W{u3Av1j!`P!q8dIANx%jT=QRzea8yLL-H7O zg)YnEQE+IX6Mv1Rr)9RV=|VQvMQ)BwUXCSh{`?g`#N!jE`E{jFp(jq8Z$-5dcG%X>nL1+YPd`8n>(p}-c@!<}9T(=L#1zT=fIv`13~G>80;F0BH6%20Ep=KO z0GZ3ZQBrTNe&fA}fKA)muLqLW{dQM!iR-v7NV5DEzKtTAdi(B*e^7KV$q>Wpkf7E| zb50UPwrE`>jhn@}gT7YNGlI_}pRK~_pY0h14X1m5V~>LQq1Za8oiPYIDa-f;sd#Y zcDUVzqhptwmjsumY>2I*T{fjxgzSjoa(m+-%2-VIR*7s=SYwXYpqp_z#WxF#s#Rd< zcmwlq{S(??Ak?uDAm$*K*I~PSOeW-Zb-SpbcjKMsE~&Ebf96|>O94G0T`GR?Co%9X zoT16tY0BM7k%kE`yzlA7YUZW8;uPL99k*HO?e?$6l$-oT9@^m_*(*^F_^g*M=v=>eI2o^n9%Pr5?lmlmp>E{s5Nj~x!};_dDqpH0koFDG0kXL zOWPnD#(!R|Bc>!zdfifZ0}bhnRv_su>9P?TJUn@xx&A&>MiT@u~uqLW{da5j3+G9YU>3JeCn1OS>p0UCopmL8 z3)Va5{Yq;o;M3uCTO0t}RY&%wMoh~Sh?-)n+8XMApiyATWal=`dP8w(gb=MsFVnoT zyPj>(f0(eoiiNac<1>?3RvTWUwe8gK{6LVn$3CVkXcye|KCU}O{9@BW9FhXOr@k92 z$DPX>kV3QT=cdV|v-k;`e6-VCJzeysOfh3f5$LtUOm+$KsZ4Lu_Fgr*(a(bkX&MW& z3X`J>3-`@I8^j(6nA*G)9+5S!viDxTQ!GibBAY}ZA^OYq_C2zqW>#B`MNA`9hJs>6 zU#L0`aR$>~az_kgNyiXVAFZ8m=*&88qt1<*S&_>P2MZ-82E|DJjZ|l5+vKpI>~DZ=Kxi@a-b-h5%ME5J4XTS`&6 zZoq&RFO}Z-dwWjt-9z>F7N3>6E$oEZazGU>9TTV+`7({1d45!fbtSnpsc-`1EC1JqGzR>|7byEk!PP2vt36DJ<{bj?GRJu-Ds4qfdx1-m^^NoE`-XN2CT6~CW{)68e>}wpg-DpXx=y;3)#Prr zT?F!FlC3wq&qTT@3`8Rb*LA=^E4-!hi~CT z-&zk1$K0(dGS9I03{T=eGr=1MEJS;SNgMh)qtDWPFfIo|U5w&fjHgyMTYI*0Nyn<)KQ&tm=LitCT53i%K7fgfu<3Wf@sP2)f1t* zMJYz^w2-9yd&E#<*)YPk4EL-j=I2 zp{YK3I)Bny-&{u7csL1VgBG)wR{T;j>y`KvU}i=5tm*Iwk>8Vs|k+7eXO0ndvY&uPPR?yvQV4#3s%v-inRcYoC_suE5G3pt*+;hn$H zUP&!JAzC@W8O-vFiXzLSiHW3@U7<~Gdgub%`9&4qzrIwxBv2PSJ4#?u0{uE{apj@^ zwyKYp7pg^U6s;-fMC;QXaLcvNuN{V!VA$VW)3C7H&`%$o-Qa4SnWgNZG4^B#^g0ut zjn39cPK=@ctIinZ5ArI+us~YqRc}Z!Az|An>^FQ%xd;7#SBo)ivT$l~WqmCManNy& zX!1q)K2z9gBHGiqbT7K^UU)55pY62%CMtnMS~}=~&pi<2&`+t-D*n-#X1^L0nkQw! zb=}{k;epXO=~*xa0J<2L;R#e!Vf_5JeritDJ6o3mvOmV@qkm+B$RL*Y(Z+oG&ktt0 z!_{P!Yjgjmtqh!X+v1vsVJO?@%x~+zt_O8)!%dXRBz58{{hr&O1_%#~T7aO2s(yX8a?l*)v6m#lqT zDX6HNHn|CZ(<7;KDvZ5H5jTh#YJi3sGuS)bd?jf66en(W8*X(PcwqNqP^(eFCnh*6 zTPHBZ-E|Qrpidq*m@tD~HB2F8`%H3BJbFCsI-{NhaRA*g6YSdgN)|x-^{*HH5P+?C zXp^t?t{mAd&k{X0TNMs_H#56kT>DZ#d#!^qWye=gyiIiR@haS)Jc=Ys#TFSR^5OQGeh)Gwp3p0MdYBY7OnJZB0jKGQeSC zNcN<0+8LknO^1iTe#OM*nFr4bb`@uxjKvZm|JCkK%VZ7$6i>!k;5rTAu5d?%tWw6g zt=b*h-Jd>Ijf09>^zqdp15Zd-73lirKx>XCbE{klcSS4ZxEBN8*+EP7Xz5`_o~eRT z)AET}A0FWCGV}k10K~FZJ_Q_g$1yj0=ygBu&-E{Ra{O+|K_d|j^yd7TjDFJYZ+ZGBG0$k9r!7sDI7{D8-G?mk-p+JcU(&G z!QapOtm(dwXu}N}8*Y{FzXUM-rn)=fsJwB2=TzUyXh3n%mz(fN+kMD+E(Qn=vw@_b zXUSDXb-Ch|af_yA;SXyiT;Uchm29$HX|4?HE?iDGljz24%o1`JV+~l9myD4}yx+nd z3^ zuvtE%$N_pOfkL z=U^?Ts`-NT6!z?2f>=qXit4W0OMHwt*u>A-_zk#3%QUpP9B zBT#hpp_x_2jrPJ%Ivy?Vj&@(IL-Bd{tf1qKqMf7lFrp{%Jwb`WtE+t|Ig?=_Ia$M_v!=(6YVI{W z?lmyvMz!}3U(ZU12zQTf2GZc!o@_f~#$m^Qs6{*?l}_b&u{r5$SpyXz%DuVOtz1u%iCx0XpHy*s>u=Yz`Y6ztlGP zP#8gf893Kf%1AwWn}P%>vHCu zf@Snh=Wv6Gv{AYLHTxA6XNW|G2x z!x&&kMEPoT@6`rN#ph?aBoag)jEutJ!t;w(!SOHfcwJSjB!YlIEXNbE`;bA0>S0?w zmkKe;k~(&RCoiGD&g>b>y(^pHzu03^`gwVRM(iSMDcq&>pS!aOSh?_U^TZM)bYX_9 z`gI(lzb)6N*|GVE!V2F$a&T6yCrUlRE!W2jPl_MF2r(QCGZ@6m2$wA;Z}@KiG||L5 z%-EXa@g2MvZ5HJiZdOs%&h-UJylPb|zsK({o#+u7W(qbx|D=>b9xu$p;Wal;s)DK1 zi;ir~>SVR`rtMQ8_t*}^^4_Er)l$#wv?)5-up0B+2|^fO+AEt1Xy?qV<@T1X=w{zz z!G|K`@y($20XwMgiMTG{06`lW;-NzRlTDCNpm0 zYznetu>CM{(X4iP63P%pvt??2qFrEsXCB6xzDvohwz_BMMV@mMw+LGa&U5})TF}quF=FDk_9~}1H!*++63B)oqR6uKBMi^jtx;&0q5a!%L z)9^DTb;1vsL&x<&$PVTpN%3d5SJEldB#gCP80E0I$Lq3$t1l%fxT~ZboJi5zGZUeG|2~}-vVCAX*hvN3qS~h zMehJS4r3iR-s>y6={U6H#IM{Nr`onn?#G4`FVHx@ib%H?`4M6CT8L&(tUjK*zC9s^ zwL9Uwu6>!$@Z$YnKjs^P`2g;4vWiSmTX*Efw`#Mx=T;xLd#G(+eVQ)`dwpR`U1scG zw(e)=^Qjr@s>FmuLGt0WG$?y~_#a_58QE>5?L~HYMVAn#ql2w9xm=2gi0BT6MQ|yI zgEfP3OaJw>a0~Xs9(?euGxeL>h57pS4#)LVWd6DhtC?7aX_j;;joJpwIz}gf5`+;> z#v?nL4Iu}1VYv+PFA(Z(l)#gp+mdqM$bJZa{2}YQfjOR&ju{}8v_6cVtk+#RUx zmRN|<8#@_jD9!>gkYu-1!;2iXH^TJ)AW=cFD%=0_=v)A4&~UBK=7x*KzTxWD`<96@ zli-t<++b7ad?)edwFZ{6HJd224P7Ke6VDVK38^B%b87=}>u!J2pT-!Vm7eR~$y?8V z_`9Z)I2dn48VUM2G>0K(#3V10vBUt*Bdqq1B{I_I-u_AB1y?5c_CW{t@nBqE1gzfD ze0LeE^VaQRSDFJER#(hs3AZY~kAy@&IX8Z}cb~xfP{r!fd1034;B=DrxTtuRo#V7G zjn95x7Axhl{`TbD`-%yV^44PK+RUCCsZ@zrT#+WE;bNsttbk0i&TFH)(9t3QK6?)d zNyT_)V}E)wO!J~!<5-qYl7r1*!PR|ccJ+n`PWd^hz4F8oPJJdnfu!98X-05cRc5OB&^lXja+EC#W7c^H>wi%$U2Lz zfGaZBsW6t2p|r&a2}u_N4sUdBExCckdLM^Duadl9F;zUS>PtI6TDm>oufDzF=f9jA z@xAtDc0O{6KFUF>@+~x*i6rP!>Rm{)AZS)g@z^hr*Z}WrE^!Je+VbAd>%U!sT3{Z%lE!-mbJ#Mc^u55O4I@4XN(QPDEuWK0M`aec5DA4mo z$*M35&fy{omtLyG4rY@Rd1iWTd^X4$DG^)I$k@xZ<;yjFBoCC78yy1+T7-n_86kmYk+H5-72Z}ir-B<=&(2iZeqiNL;rD)B-+blaxpsISMKVzDcrX(p0r{mq0s9yb;o}a5Mf_L1wG4rdzcyi#FUt{Vlsj=)l?Y4FH=DHDf zP;%Ryy+Eve8zg(|wY;U}3^|T$WaW0Qb28ne!t1%c)P$e%U#2WvUOAt7?(5wCZn?c^ zEVr&>xgDN9GD6~jZHAIx>~%KYQmv<+abt;!YI~hWiF#iL6n8IqyPcOe8{baru2Ftr zk9>%PRF-Gno4w<{v*T%_I|pqjy;)EDetXP!AmDskKL=fy7@yO+UGiY%U#K&@zVba+ zFkTBKPP^`Hjl*nkg8x23M4YbipHT-|ms@E~W{31AA!`;$g^-(tQm9YFQSjG6Iin?2 z%38!ok&sj~HjmF0NCs78+0aP(mG}$257cVR^NOVjYMtk2N7Jsh<`cFWwhEY%krK-| z?mJkPacaxZtujhUMZfz)LTco^nxWoroJr3)yz3w%;pxR8TeZ8rr-(iZHaB0UrnsK} z(D`plC4O()8zIZ$h(-^!voco&S#RvxOkN$xeCiHTm+H(&VidL3Amg3Xg}sX0TXnfR zlYFtaGcA)lR-z>?MH~_NjcK2M5gj(e90RG4y-K$Hvjz%^*3fxtUnY{iG_}_r(-o!b zUv5Gcu2+j^ttB~-p^?EMHJD*0AQAx&!@c%%qqMl{<;rs$aM?NQ-0&|r z^yG-|#-`>TOoEvs(quYV2xGbcO!o$ok1^^S(=JtMFYI!>*s-4A7L=b%9A{sC*66Ox zW|-@DL_$J}h0j!!o-U$I+_pp|-3*r#q+PPfq1(jt0Sp>z@JdL(?s)=kM?&I)qbhbY zsEo$oI^O;M%tof*sgWPG(8yy3o`h7DP;`+jB)4`^su^%c&`3>>na817dn>v%55O;* zAk{hAYTt;`T*c(VtOD>qNF4RQ$pRvWKg2k=Qsl1y34~D5uTSj#CsNe0LX)^6~hn zT=`cFp75@pEvn27)RKMTcgrvQhs+-PZZ)uUZe}|)=6`VEXYMy5$dAzdJCNd7sGqZC3$#y8`^$&>> zX274XAfxfY6wHQgOk7}rA^PRHOC4YzKlQ+8#C-z5)t@nYy<%Y5naWm{vZZHI>g3Qe z>k5bTdXt?40?j11`ipsUI5Rj;AW0fJXTJ`)9Epjk9Eqt6hm27MEw93+gbKb&7P|dV zO`fTbhiJmtCw09VE}GH)y=XpY9lCHkUfTUiLPL3@BC?H6q4pHlKQT)qQbTx>2tw|u zftiT>3Ou0d>ntkj1*%m({tw9**xttKvX9+|R-f^M8zU{)=1NeEviRM%`i$A*vJjiu z+cOg2_t=t1H9u;(-OfHWy}2|XqVfGy`d@BaI z{-KzM;&=KC>1kvI3i#(A@;_$@h~4oV(&z9yMnXb*E&hk71tTGMzrK>RQ)@v5_Dg`ufZviPSX%1&>B?v&`<+Pgu47RqDZjZR`I_<_;2tLBUS2mlH#ZK3hD8pBMcE7? zE{0~O^GhGg!Gvj6^}u3o3-OWINo~ovJ7G6tQL~=Py<5wqr8Yeys}YI+g8;c#tgeXb zUFwko4WGSlKzfNpy*97Qo4+@=pKTIYXcDL?D^sp1^Vtl{k`}7^?@>F3bN>xf-KNc6W!Fa|*OeI{8D1d27rki`TN*e*RIUS}^Wt z>*C43`W0|&crRQ2;N$}5fnJSZtY*Hmv*>YZ@rpOi^jnSH&?Ez`Nsk&Cqqc2qsEq7n z9W}3cU6SF1Ca)LM)`4HFv`n%^;A|FMpj!&tG!93%W<9r6V%3+f#Et-k-DAJlx8=uG z;>9QCP1%malZ{T+e>qcmG*+aJxzgR*Hdn1C3s^hClLQcP$w;BT}X=w$Mm+Z%xTLvOmRww&?h!p7Y38yLZ8p60diT$X}+62y(V7n-P9fWSb zuNGAtMPY1Y1hqh@?Y4Et4>rUHmAvAxK4SaF-e`R*&4b!1nD?5w#xnY)1J3l`h3sIPwc+dzEWS7j zpCpA>hxfXjg9Mfc7U}J{vYc{iRlRkB0q2_D+u4_$JU)TN%|?PV*9Qh0T#pb?;_6x| zxR(%w@ZAY~Erj>_l+(5>%k2Wzw;o5_a2x8t`|VE7WmL9^*`5iRvdYn)h6SkKkrTb@ zC{e<}2X`uYajZXf%>awV6L8@F&K42Oc64^kl584>&(<+&kxEXSUNrR=A8%F2h*)Ya zL@^?(bWS35g%-Qj6W?;W9c>hA)g~r^ryx}+7dZ&e2>K~vJrBAp*cbG=GyWQ?OYyo`5ss3_VGD*ZV_mbtXwQTA6Jy zd#YnjpXy=ivEqzLKi5xNKz!y^ARGx%H3^Q-h8J#r*$?pTP@Q1iFOJy1Ki*-d!D8z} zu`XPAJvPKjY+b+6y*{us z4ptt$GOq2iidT{HUNXtFdy@^SK&SQgV*;W;ra`rP7vG99sA=_2eL5c|o@(-t1)X9{%$!Bf5wnAB<&)?;)41Iew<|Ie(j}@j>7L}M2>34Yp7#VrO%BV9;4+se zC*-d>V?i1`S5fWcR+T1?QslWOHougZmSvWeD5_m)mJlXd-A=>|o{Em=1!5f%&^0(| z)={ecFlCkmi#Rr5=-FmuEfI(v0*~W;Be!E+Ut*dVDye-ak;j?f!D0SDZ;<^^LV8pW zNIV_Hl>lG9Qk2mMEB?sC_8C6sNTYm0GtC}y6;_`h@2RC4v)A(F4 zPW?Se;W38>;0=uSn}ZFL!x9Y#?Zd&wNyU#L1Qh%gP}dQu;N!TUB1yM0-5Q6D+5Qe1 z%yrtV6VBi#-%DO*@MgdtJ}mnQoGZ@C+ISC+g4j;cppHxfp$uJHNAFU6VvEU%g|G~`=rPM9as(*y&Vi++ENO&a$J#4ne8d41GsHj$DnvW2UN78N5gd-+ue zbL^3Y^v#JpEUIKDP3&eT-Ly=1aaXUjl&EtFRZJc1tN2K1u2#mnoRw%@>9Ag-)=0^! z+W~N>65{9(14=pB8giZ^)5VrmWE_IW0=A3Gbs^c^#Vt`j+iVVz|Ijzq+H9vi(@cX{ ztCpS}yyeiexEf={&oHFP*s$ULJ^k^Kl!tq)<`fd@4%-P50%>_(L#KNl-HA0 z+K)U(%AGBC1tD&nBE}b)okXFDO{ao;`FI4k%v$`*My6GlKFvp~?*_?E$7T9yZvnei zcFPwG+Q@TzzTKup;19^gjeZf9?8zV1OQhs}<(rEu>1m#b8PvGM82ipddp2j($s}<= za&t*%5sNl4yZqID&r&dZ$kIRPlY!uZM4V!V=RAOXBMDv+Yi_)pKZBX}SJpVxY z2tL|0A5|)uTqY3>Bc7`?SFy)&P|RXYjE>b*-u)r>HuHR;{w-!%X?srG^VwQI(?l6{kK>ZP3$Q+O^AzCBPCPjUZzLBo znE2u`)HHD*UmCZw7kyzQ*6Z02Ys%P(mD4$gf%NFJ?q2O$1WJiaC|+;>p852;j61iM zlkLT-Iy~^NZ~IxfM*pu*@c-Gp70?~OpVh5i_Hmkni;GXq(xT2RW~4!)<{?s{G;p;4 z(a1*&%#e&O=6BDP?&wtCztL$ptpP$Y?~5R#R;`oo;>|&B6AIGAoeLlS-nTR$yHrq- zM$7&*90iEg<);`iBO50B0<#gZ2#hRw+Ht=|j%Znx649H4#TEw|k0%e1VAOZd>3!Vl zejvB4`bl%()kofs#Vby?7+ermibluP_O1SSq|Y)@z{58e{e&3&N|C}p(@DbMq^m|q zr%1!*rF=@oA!+@~gIsRp-0*#=noE}H&nt;7RJvpCJmu{C^EuyDA`RTMlO;U@Sx&xz zB_9Y0YaN3V^==&$s(GSm0g;w_s6MDwlHhxk?rGzv~s}vT<7f6k#!$Pyr zN@9W*!bAxCi3kc~J7>dQ@tYjR?~|?3WkJ4E0WUGX)4>Y)bLE|{YM=t*$mzMfrltuFev!U8<`6GHijVw!)&De8So2^o7;`?4a>x1fhe|5@$d?j?;mO z+|(~{x8RSL$wDewZ$|2DD|z_bSftW43ntQgQ7Mp-%)bGeR>fi5vKWcaGcgsPA1L{*R_Z=pk5kU7ucPZ%>U!a{-r#U1D<447=)Na`FF~eFg%5S|*TatjGp@5B*BEU9R7%jwSX9z3V@IDVlbo(R76 zyC787atv<4HhaNH#YoC#_sodKJtXshyG4=NeQ2+5mHYH~UDdSa4Z9qn+1fMHggBux z&!4p0^5;KyG1kpj&u)SggqX~p7pBOBDZofDcI!9gq%0%HjHdhgeLiIj3mxXJnw08W zeb7V9`oF48Y?RqTrdz!pH?q`4(q-7ppWNCH%McCQnW-$OeuVUSO9kY~IDfG!Re#<5 zqMw1f_kuLVU@~AaAi^BW9qDtZSr**|AixJoFX?vpAervHm3h&^3`oB^?tJNcz5Fb( zn6@>Cn9<%fd{|L>w+|9iyYPe@eGpX#*UuC99Objq6NG-bPg zb=>|e%QL1(JTo?C4}-(3v|N*s*83bU`NuDj+Q%o^?< zncUo8ASQ_u0kymrgVYxoJ!9Xz6Bb^9t(SE8pJudq-Hr zd)39HpZH#qG+Nt}d7HqNeHeVO*svOZ!MDRQf`*9}zVD7tC4b-5 z_TrzMiiB-$uVoOX!cH@)n``I2ZW?b5=6-(|9`WZqJ#nxc%e9NBQvOavW;pF$ILz&U=hg#^G!(p`jrmEV7o+YyB(~ zLIp*<)@QL+jLhLYI0}u5p*yCiKFkxmIFcbL?0e#|y;&1%AxpAe8?sQp`nY6#PUF&O zpiPwjYNxy5l0+@>M3d!Dv=?^d^nBza8NQGGL5%1B*hcZV`7b0aukwwq0Er}f<#pt=s&-;&I!&RFpNhjn=13e}f^lf1lE%(44X zb1U%a%egOgr+NQsTe5Cd!kcfqC)X)0x9fUW|Ky_Er=lN^XUfL!o>g79(p~@AV&=?R~j!`T6hP`EI3K;1p0={86)cK~BzX=kN3X zf8?K(wPoXyS8o@W$5vFox|;I$(pzi0s`OQXOUiElVXy!Acx4*r?Z$TYbN>GWtNM@K zJIlPYRkyg-+HUWTOwXxzj%?fcDqiMhz>ljx949-=-i-Kh_1KBUKX&esw4a``^RJ>* zXwhtT%ei{n#FzEH|C;yZ>+$!u_x#*+`=L8{b9SH^9&27u3G_Gxqxe`L2UJtdxghk z&-wzDFvLvW{chK5u3{n6GSKKy!P&C6w^IFpbD0bcp^A{{2lcLh_DXj@ybtYvc^;(2 M)78&qol`;+0Fu7JivR!s diff --git a/docs/images/nf-core-pixelator-metromap.svg b/docs/images/nf-core-pixelator-metromap.svg new file mode 100644 index 00000000..dee01a7d --- /dev/null +++ b/docs/images/nf-core-pixelator-metromap.svg @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/output.md b/docs/output.md index dc8682df..0013ed26 100644 --- a/docs/output.md +++ b/docs/output.md @@ -2,58 +2,240 @@ ## 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 [`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) +- [Compute connected components](#compute-connected-components) +- [Filtering, annotation, cell-calling](#cell-calling-filtering-and-annotation) +- [Downstream analysis](#downstream-analysis) +- [Generate reports](#generate-reports) + +### Preprocessing + +
+Output files + +- `pixelator` + + - `amplicon` + + - `.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-amplicon.log`: pixelator log output. + +
+ +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. + +### Quality control + +
+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. + - `.meta.json`: Command invocation metadata. + + - `logs` + - `.pixelator-preqc.log`: pixelator log output. + +
+ +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 `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 + +- `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. + +
-- [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 +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. -### FastQC +### Duplicate removal and error correction
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` + + - `collapse` + + - `.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.
-[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/). +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). -![MultiQC - FastQC sequence counts plot](images/mqc_fastqc_counts.png) +The output format of this command is an edge list in CSV format. -![MultiQC - FastQC mean quality scores plot](images/mqc_fastqc_quality.png) +### Compute connected components -![MultiQC - FastQC adapter content plot](images/mqc_fastqc_adapter.png) +
+Output files + +- `pixelator` -:::note -The FastQC plots displayed in the MultiQC report shows _untrimmed_ reads. They may contain adapter sequence and potentially regions with low quality. -::: + - `graph` -### MultiQC + - `.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`: Metrics with useful information about the clustering. + - `*.meta.json`: Command invocation metadata. + + - `logs` + - `.pixelator-cluster.log`: pixelator log output. + +
+ +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 find and remove problematic edges. +(See `--multiplet_recovery`). The information to keep track of the original and +newly recovered components are stored in a file (components_recovered.csv). + +### Cell-calling, filtering, and annotation
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` + + - `annotate` + - `.dataset.pxl` + - `.meta.json`: Command invocation metadata. + - `.rank_vs_size.png` + - `.raw_components_metrics.csv` + - `.report.json`: Statistics for the analysis step. + - `.umap.png` + - `logs` + - `.pixelator-annotate.log`: pixelator log output. +
+ +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 metadata. + +### Downstream analysis + +
+Output files + +- `pixelator` + + - `analysis` + + - `.dataset.pxl`: PXL file with the analysis results added to it. + - `.meta.json`: Command invocation metadata. + - `.report.json`: Statistics for the analysis step. + + - `logs` + - `.pixelator-analysis.log`: pixelator log output. + +
+ +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 are performed: + +- polarization scores (enable with `--compute_polarization`) +- co-localization scores (enable with `--compute_colocalization`) + +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 + +
+Output files + +- `pixelator` + - `report` + - `_report.html`: Pixelator summary report. + - `logs` + - `.pixelator-report.log`: Pixelator 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. +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. -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 . +More information on the report can be found in the [pixelator documentation](https://software.pixelgen.com/pixelator/outputs/web-report/) ### Pipeline information @@ -64,6 +246,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` - Parameters used by the pipeline run: `params.json`.
diff --git a/docs/usage.md b/docs/usage.md index 7de8300a..9a6bc55f 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -6,58 +6,137 @@ ## 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. +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. ```bash --input '[path to samplesheet file]' ``` +An [example samplesheet](../assets/samplesheet.csv) has been provided with the pipeline. + +### Format + +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. + +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. + +Below is an example of a simple samplesheet with two samples. + +```csv +sample,design,panel,fastq_1,fastq_2 +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 | 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. + +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 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 title="samplesheet.csv" -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 +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 ``` -### Full samplesheet +### Relative paths + +Using relative paths in a samplesheet is supported. +This make it easier to relocate data since you do not have to edit the paths to files in the samplesheet. + +The default behavior is to resolve relative paths based on the directory the samplesheet file is located in. -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. +Given following directory structure: -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. +- data + - samplesheet.csv + - fastq + - sample1_R1.fq.gz + - sample1_R2.fq.gz + +You can use following samplesheet: ```csv title="samplesheet.csv" -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, +sample,design,panel,panel_file,fastq_1,fastq_2 +sample1,D21,human-sc-immunology-spatial-proteomics,,fastq/sample1_R1.fq.gz,fastq/sample1_R2.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 (`_`). | -| `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". | +Using the `--input_basedir` option you can specify a different location that will be used to resolve relative paths. +This location can be a local or a remote path. -An [example samplesheet](../assets/samplesheet.csv) has been provided with the pipeline. +For example, using the same samplesheet as above, but with the samplesheet on the local machine and the input data located on an AWS S3 bucket: + +- s3://my-company-data/experiment-1/fastq + - sample1_R1.fq.gz + - sample1_R2.fq.gz + +```shell +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: ```bash -nextflow run nf-core/pixelator --input ./samplesheet.csv --outdir ./results --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. @@ -90,10 +169,11 @@ with `params.yaml` containing: ```yaml input: './samplesheet.csv' outdir: './results/' -genome: 'GRCh37' <...> ``` +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 @@ -112,7 +192,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. :::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. @@ -159,6 +239,12 @@ If `-profile` is not specified, the pipeline will run locally and expect all sof - `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. +:::warning +Since Nextflow 23.07.0-edge, Nextflow no longer mounts the host's home directory when using Apptainer or Singularity. +This causes issues in some dependencies. As a workaround, you can revert to the old behavior by setting the environment variable +`NXF_APPTAINER_HOME_MOUNT` or `NXF_SINGULARITY_HOME_MOUNT` to `true` in the machine from which you launch the pipeline. +::: + ### `-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). diff --git a/lib/WorkflowMain.groovy b/lib/WorkflowMain.groovy index ce9041a7..da063262 100755 --- a/lib/WorkflowMain.groovy +++ b/lib/WorkflowMain.groovy @@ -11,9 +11,8 @@ class WorkflowMain { // public static String citation(workflow) { return "If you use ${workflow.manifest.name} for your analysis please cite:\n\n" + - // TODO nf-core: Add Zenodo DOI for pipeline after first release - //"* The pipeline\n" + - //" https://doi.org/10.5281/zenodo.XXXXXXX\n\n" + + "* The pipeline\n" + + " https://doi.org/10.5281/zenodo.10015112\n\n" + "* The nf-core framework\n" + " https://doi.org/10.1038/s41587-020-0439-x\n\n" + "* Software dependencies\n" + diff --git a/lib/WorkflowPixelator.groovy b/lib/WorkflowPixelator.groovy index 70e024ae..0098f0bf 100755 --- a/lib/WorkflowPixelator.groovy +++ b/lib/WorkflowPixelator.groovy @@ -7,19 +7,6 @@ import groovy.text.SimpleTemplateEngine 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." - } - } - // // Get workflow summary for MultiQC // @@ -53,7 +40,7 @@ class WorkflowPixelator { public static String toolCitationText(params) { - // TODO nf-core: Optionally add in-text citation tools to this list. + // 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 = [ diff --git a/main.nf b/main.nf index f0450fce..c1fe9417 100644 --- a/main.nf +++ b/main.nf @@ -17,10 +17,6 @@ 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') /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/modules.json b/modules.json index 8cdee47f..06cde38b 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": "bba7e362e4afead70653f84d8700588ea28d0f9e", + "git_sha": "516189e968feb4ebdd9921806988b4c12b4ac2dc", "installed_by": ["modules"] }, - "fastqc": { - "branch": "master", - "git_sha": "65ad3e0b9a4099592e1102e92e10455dc661cf53", - "installed_by": ["modules"] - }, - "multiqc": { + "custom/dumpsoftwareversions": { "branch": "master", - "git_sha": "4ab13872435962dadc239979554d13709e20bf29", + "git_sha": "516189e968feb4ebdd9921806988b4c12b4ac2dc", "installed_by": ["modules"] } } diff --git a/modules/local/pixelator/collect_metadata.nf b/modules/local/pixelator/collect_metadata.nf new file mode 100644 index 00000000..10601d75 --- /dev/null +++ b/modules/local/pixelator/collect_metadata.nf @@ -0,0 +1,83 @@ +import org.json.JSONObject +import org.json.JSONTokener +import org.json.JSONArray +import groovy.json.JsonSlurper +import groovy.json.JsonBuilder + +process PIXELATOR_COLLECT_METADATA { + label 'process_single' + cache false + + conda "bioconda::pixelator=0.15.2" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.2--pyh7cba7a3_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/list_options.nf b/modules/local/pixelator/list_options.nf new file mode 100644 index 00000000..200cf5b2 --- /dev/null +++ b/modules/local/pixelator/list_options.nf @@ -0,0 +1,31 @@ +process PIXELATOR_LIST_OPTIONS { + label 'process_single' + + + conda "bioconda::pixelator=0.15.2" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.2--pyh7cba7a3_0' }" + + output: + path "design_options.txt" , emit: designs + path "panel_options.txt" , emit: panels + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def args2 = task.ext.args2 ?: '' + + """ + pixelator single-cell --list-designs $args > design_options.txt + pixelator single-cell --list-panels $args2 > panel_options.txt + + 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/single-cell/amplicon/main.nf b/modules/local/pixelator/single-cell/amplicon/main.nf new file mode 100644 index 00000000..f5eb6ae5 --- /dev/null +++ b/modules/local/pixelator/single-cell/amplicon/main.nf @@ -0,0 +1,45 @@ +process PIXELATOR_AMPLICON { + tag "$meta.id" + label 'process_low' + + + conda "bioconda::pixelator=0.15.2" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.2--pyh7cba7a3_0' }" + + input: + tuple val(meta), path(reads) + + output: + 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 + + 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-amplicon.log \\ + --verbose \\ + single-cell \\ + amplicon \\ + --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/single-cell/analysis/main.nf b/modules/local/pixelator/single-cell/analysis/main.nf new file mode 100644 index 00000000..a30843b5 --- /dev/null +++ b/modules/local/pixelator/single-cell/analysis/main.nf @@ -0,0 +1,47 @@ +process PIXELATOR_ANALYSIS { + tag "$meta.id" + label 'process_medium' + + + conda "bioconda::pixelator=0.15.2" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.2--pyh7cba7a3_0' }" + + input: + tuple val(meta), path(data) + + 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 \\ + $data + + 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/single-cell/annotate/main.nf b/modules/local/pixelator/single-cell/annotate/main.nf new file mode 100644 index 00000000..16c17d8c --- /dev/null +++ b/modules/local/pixelator/single-cell/annotate/main.nf @@ -0,0 +1,53 @@ +process PIXELATOR_ANNOTATE { + tag "$meta.id" + label 'process_medium' + + + conda "bioconda::pixelator=0.15.2" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.2--pyh7cba7a3_0' }" + + input: + tuple val(meta), path(dataset), path(panel_file), val(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/*.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 ?: '' + def panelOpt = ( + panel ? "--panel $panel" : + panel_file ? "--panel $panel_file" : + "" + ) + + """ + pixelator \\ + --cores $task.cpus \\ + --log-file ${prefix}.pixelator-annotate.log \\ + --verbose \\ + single-cell \\ + annotate \\ + --output . \\ + $panelOpt \\ + $args \\ + $dataset \\ + + 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/single-cell/collapse/main.nf b/modules/local/pixelator/single-cell/collapse/main.nf new file mode 100644 index 00000000..893660a9 --- /dev/null +++ b/modules/local/pixelator/single-cell/collapse/main.nf @@ -0,0 +1,54 @@ +process PIXELATOR_COLLAPSE { + tag "$meta.id" + label 'process_medium' + + conda "bioconda::pixelator=0.15.2" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.2--pyh7cba7a3_0' }" + + input: + tuple val(meta), path(reads), path(panel_file), val(panel) + + 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(' ') + def panelOpt = ( + panel ? "--panel $panel" : + panel_file ? "--panel $panel_file" : + "" + ) + + """ + pixelator \\ + --cores $task.cpus \\ + --log-file ${prefix}.pixelator-collapse.log \\ + --verbose \\ + single-cell \\ + collapse \\ + --output . \\ + --design ${meta.design} \\ + $panelOpt \\ + $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/single-cell/demux/main.nf b/modules/local/pixelator/single-cell/demux/main.nf new file mode 100644 index 00000000..fc50d42a --- /dev/null +++ b/modules/local/pixelator/single-cell/demux/main.nf @@ -0,0 +1,54 @@ +process PIXELATOR_DEMUX { + tag "$meta.id" + label 'process_medium' + + + conda "bioconda::pixelator=0.15.2" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.2--pyh7cba7a3_0' }" + + input: + tuple val(meta), path(reads), path(panel_file), val(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 ?: '' + def panelOpt = ( + panel ? "--panel $panel" : + panel_file ? "--panel $panel_file" : + "" + ) + + """ + pixelator \\ + --cores $task.cpus \\ + --log-file ${prefix}.pixelator-demux.log \\ + --verbose \\ + single-cell \\ + demux \\ + --output . \\ + $panelOpt \\ + $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/single-cell/graph/main.nf b/modules/local/pixelator/single-cell/graph/main.nf new file mode 100644 index 00000000..f15b07e5 --- /dev/null +++ b/modules/local/pixelator/single-cell/graph/main.nf @@ -0,0 +1,49 @@ +process PIXELATOR_GRAPH { + tag "$meta.id" + label 'process_medium' + + + conda "bioconda::pixelator=0.15.2" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.2--pyh7cba7a3_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/single-cell/qc/main.nf b/modules/local/pixelator/single-cell/qc/main.nf new file mode 100644 index 00000000..e07ae1e2 --- /dev/null +++ b/modules/local/pixelator/single-cell/qc/main.nf @@ -0,0 +1,78 @@ +process PIXELATOR_QC { + tag "$meta.id" + label 'process_medium' + + + conda "bioconda::pixelator=0.15.2" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.2--pyh7cba7a3_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/single-cell/report/main.nf b/modules/local/pixelator/single-cell/report/main.nf new file mode 100644 index 00000000..f39e7dda --- /dev/null +++ b/modules/local/pixelator/single-cell/report/main.nf @@ -0,0 +1,57 @@ +process PIXELATOR_REPORT { + tag "$meta.id" + label 'process_low' + + + conda "bioconda::pixelator=0.15.2" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : + 'biocontainers/pixelator:0.15.2--pyh7cba7a3_0' }" + + input: + tuple val(meta), path(panel_file), val(panel) + path amplicon_data , stageAs: "results/amplicon/*" + 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 + 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" : + panel_file ? "--panel $panel_file" : + "" + ) + + """ + pixelator \\ + --cores $task.cpus \\ + --log-file ${prefix}.pixelator-report.log \\ + --verbose \\ + single-cell \\ + report \\ + --output . \\ + $panelOpt \\ + $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..024bd2de --- /dev/null +++ b/modules/local/rename_reads.nf @@ -0,0 +1,45 @@ +process RENAME_READS { + 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) + + 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..fc1d17dd 100644 --- a/modules/local/samplesheet_check.nf +++ b/modules/local/samplesheet_check.nf @@ -9,6 +9,8 @@ process SAMPLESHEET_CHECK { input: path samplesheet + path design_options + val samplesheet_path output: path '*.csv' , emit: csv @@ -18,10 +20,15 @@ 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 ?: '' + """ check_samplesheet.py \\ $samplesheet \\ - samplesheet.valid.csv + samplesheet.valid.csv \\ + --samplesheet-path $samplesheet_path \\ + --design-options $design_options \\ + $args cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/nf-core/cat/fastq/environment.yml b/modules/nf-core/cat/fastq/environment.yml new file mode 100644 index 00000000..222b301f --- /dev/null +++ b/modules/nf-core/cat/fastq/environment.yml @@ -0,0 +1,6 @@ +channels: + - conda-forge + - bioconda + - defaults +dependencies: + - conda-forge::sed=4.7 diff --git a/modules/nf-core/cat/fastq/main.nf b/modules/nf-core/cat/fastq/main.nf new file mode 100644 index 00000000..b75a2e73 --- /dev/null +++ b/modules/nf-core/cat/fastq/main.nf @@ -0,0 +1,80 @@ +process CAT_FASTQ { + tag "$meta.id" + label 'process_single' + + conda 'modules/nf-core/cat/fastq/environment.yml' + 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..db4ac3c7 --- /dev/null +++ b/modules/nf-core/cat/fastq/meta.yml @@ -0,0 +1,42 @@ +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" +maintainers: + - "@joseespinosa" + - "@drpatelh" diff --git a/modules/nf-core/cat/fastq/tests/main.nf.test b/modules/nf-core/cat/fastq/tests/main.nf.test new file mode 100644 index 00000000..f5f94182 --- /dev/null +++ b/modules/nf-core/cat/fastq/tests/main.nf.test @@ -0,0 +1,143 @@ +nextflow_process { + + name "Test Process CAT_FASTQ" + script "../main.nf" + process "CAT_FASTQ" + tag "modules" + tag "modules_nfcore" + tag "cat" + tag "cat/fastq" + + test("test_cat_fastq_single_end") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = [ + [ id:'test', single_end:true ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test2_1_fastq_gz'], checkIfExists: true) ] + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out.reads).match() }, + { assert path(process.out.versions.get(0)).getText().contains("cat") } + ) + } + } + + test("test_cat_fastq_paired_end") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = [ + [ id:'test', single_end:false ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test_2_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test2_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test2_2_fastq_gz'], checkIfExists: true) ] + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out.reads).match() }, + { assert path(process.out.versions.get(0)).getText().contains("cat") } + ) + } + } + + test("test_cat_fastq_single_end_same_name") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = [ + [ id:'test', single_end:true ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true) ] + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out.reads).match() }, + { assert path(process.out.versions.get(0)).getText().contains("cat") } + ) + } + } + + test("test_cat_fastq_paired_end_same_name") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = [ + [ id:'test', single_end:false ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test_2_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), + file(params.test_data['sarscov2']['illumina']['test_2_fastq_gz'], checkIfExists: true) ] + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out.reads).match() }, + { assert path(process.out.versions.get(0)).getText().contains("cat") } + ) + } + } + + test("test_cat_fastq_single_end_single_file") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = [ + [ id:'test', single_end:true ], // meta map + [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true)] + ] + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out.reads).match() }, + { assert path(process.out.versions.get(0)).getText().contains("cat") } + ) + } + } +} diff --git a/modules/nf-core/cat/fastq/tests/main.nf.test.snap b/modules/nf-core/cat/fastq/tests/main.nf.test.snap new file mode 100644 index 00000000..ec2342e5 --- /dev/null +++ b/modules/nf-core/cat/fastq/tests/main.nf.test.snap @@ -0,0 +1,78 @@ +{ + "test_cat_fastq_single_end": { + "content": [ + [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,f9cf5e375f7de81a406144a2c70cc64d" + ] + ] + ], + "timestamp": "2023-10-17T23:19:12.990284837" + }, + "test_cat_fastq_single_end_same_name": { + "content": [ + [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,63f817db7a29a03eb538104495556f66" + ] + ] + ], + "timestamp": "2023-10-17T23:19:31.554568147" + }, + "test_cat_fastq_single_end_single_file": { + "content": [ + [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,e325ef7deb4023447a1f074e285761af" + ] + ] + ], + "timestamp": "2023-10-17T23:19:49.629360033" + }, + "test_cat_fastq_paired_end_same_name": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test_1.merged.fastq.gz:md5,63f817db7a29a03eb538104495556f66", + "test_2.merged.fastq.gz:md5,fe9f266f43a6fc3dcab690a18419a56e" + ] + ] + ] + ], + "timestamp": "2023-10-17T23:19:40.711617539" + }, + "test_cat_fastq_paired_end": { + "content": [ + [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test_1.merged.fastq.gz:md5,f9cf5e375f7de81a406144a2c70cc64d", + "test_2.merged.fastq.gz:md5,77c8e966e130d8c6b6ec9be52fcb2bda" + ] + ] + ] + ], + "timestamp": "2023-10-18T07:53:20.923560211" + } +} \ No newline at end of file diff --git a/modules/nf-core/cat/fastq/tests/tags.yml b/modules/nf-core/cat/fastq/tests/tags.yml new file mode 100644 index 00000000..6ac43614 --- /dev/null +++ b/modules/nf-core/cat/fastq/tests/tags.yml @@ -0,0 +1,2 @@ +cat/fastq: + - modules/nf-core/cat/fastq/** 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/custom/dumpsoftwareversions/tests/main.nf.test.snap b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap index 4274ed57..8713b921 100644 --- a/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap +++ b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap @@ -3,25 +3,25 @@ "content": [ { "0": [ - "software_versions.yml:md5,1c851188476409cda5752ce971b20b58" + "software_versions.yml:md5,a027f820f30b8191a20ca16465daaf37" ], "1": [ - "software_versions_mqc.yml:md5,2570f4ba271ad08357b0d3d32a9cf84d" + "software_versions_mqc.yml:md5,ee4a1d028ad29987f9ac511f4668f17c" ], "2": [ - "versions.yml:md5,3843ac526e762117eedf8825b40683df" + "versions.yml:md5,f47ebd22aba1dd987b7e5d5247b766c3" ], "mqc_yml": [ - "software_versions_mqc.yml:md5,2570f4ba271ad08357b0d3d32a9cf84d" + "software_versions_mqc.yml:md5,ee4a1d028ad29987f9ac511f4668f17c" ], "versions": [ - "versions.yml:md5,3843ac526e762117eedf8825b40683df" + "versions.yml:md5,f47ebd22aba1dd987b7e5d5247b766c3" ], "yml": [ - "software_versions.yml:md5,1c851188476409cda5752ce971b20b58" + "software_versions.yml:md5,a027f820f30b8191a20ca16465daaf37" ] } ], - "timestamp": "2023-11-03T14:43:22.157011" + "timestamp": "2023-10-11T17:10:02.930699" } } diff --git a/modules/nf-core/fastqc/main.nf b/modules/nf-core/fastqc/main.nf deleted file mode 100644 index 9e19a74c..00000000 --- a/modules/nf-core/fastqc/main.nf +++ /dev/null @@ -1,55 +0,0 @@ -process FASTQC { - tag "$meta.id" - label 'process_medium' - - conda "${moduleDir}/environment.yml" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/fastqc:0.12.1--hdfd78af_0' : - 'biocontainers/fastqc:0.12.1--hdfd78af_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 '/FastQC v/!d; s/.*v//' ) - 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 '/FastQC v/!d; s/.*v//' ) - END_VERSIONS - """ -} diff --git a/modules/nf-core/fastqc/meta.yml b/modules/nf-core/fastqc/meta.yml deleted file mode 100644 index ee5507e0..00000000 --- a/modules/nf-core/fastqc/meta.yml +++ /dev/null @@ -1,57 +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" -maintainers: - - "@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 00cc48d2..00000000 --- a/modules/nf-core/multiqc/main.nf +++ /dev/null @@ -1,55 +0,0 @@ -process MULTIQC { - label 'process_single' - - conda "${moduleDir}/environment.yml" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/multiqc:1.18--pyhdfd78af_0' : - 'biocontainers/multiqc:1.18--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" : '' - def logo = multiqc_logo ? /--cl-config 'custom_logo: "${multiqc_logo}"'/ : '' - """ - multiqc \\ - --force \\ - $args \\ - $config \\ - $extra_config \\ - $logo \\ - . - - 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 f1aa660e..00000000 --- a/modules/nf-core/multiqc/meta.yml +++ /dev/null @@ -1,59 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/meta-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" -maintainers: - - "@abhi18av" - - "@bunop" - - "@drpatelh" - - "@jfy133" diff --git a/nextflow.config b/nextflow.config index cef6e274..d3384f50 100644 --- a/nextflow.config +++ b/nextflow.config @@ -6,27 +6,71 @@ ---------------------------------------------------------------------------------------- */ + // Global default params, used in configs 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 - + input = null + input_basedir = 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 + + // demux options + demux_mismatches = 0.1 + demux_min_length = null + + // collapse options + markers_ignore = null + algorithm = 'adjacency' + max_neighbours = 60 + collapse_mismatches = 2 + collapse_min_count = 2 + collapse_use_counts = false + + // graph options + multiplet_recovery = true + leiden_iterations = 10 + graph_min_count = 2 - // MultiQC options - multiqc_config = null - multiqc_title = null - multiqc_logo = null - max_multiqc_email_size = '25.MB' - multiqc_methods_description = null + // annotate options + min_size = null + max_size = null + dynamic_filter = 'min' + aggregate_calling = true + + // analysis options + compute_polarization = true + compute_colocalization = true + use_full_bipartite = 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 + skip_analysis = false + + // Main pixelator container override + pixelator_container = null // Boilerplate options outdir = null + tracedir = "${params.outdir}/pipeline_info" publish_dir_mode = 'copy' email = null email_on_fail = null @@ -43,7 +87,7 @@ params { custom_config_base = "https://raw.githubusercontent.com/nf-core/configs/${params.custom_config_version}" config_profile_contact = null config_profile_url = null - + // Max resource options // Defaults only, expecting to be overwritten @@ -57,7 +101,6 @@ params { validationSchemaIgnoreParams = 'genomes,igenomes_base' validationShowHiddenParams = false validate_params = true - } // Load base.config by default for all pipelines @@ -134,6 +177,7 @@ profiles { shifter.enabled = false charliecloud.enabled = false apptainer.enabled = false + podman.runOptions = '--userns=keep-id' } shifter { shifter.enabled = true @@ -185,12 +229,6 @@ plugins { id 'nf-validation@1.1.3' // Validation of pipeline parameters and creation of an input channel from a sample sheet } -// 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. // 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. @@ -233,8 +271,9 @@ manifest { description = """Pipeline for analysis of Molecular Pixelation assays""" mainScript = 'main.nf' nextflowVersion = '!>=23.04.0' - version = '1.1.0dev' - doi = '' + version = '1.1.0dev' + // TODO: Use zenodo DOI once available + 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 4914162f..3210d63f 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -15,16 +15,24 @@ "input": { "type": "string", "format": "file-path", - "exists": true, + "schema": "assets/schema_input.json", "mimetype": "text/csv", - "pattern": "^\\S+\\.csv$", + "pattern": "^\\S+\\.(csv|tsv)$", "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" }, + "input_basedir": { + "type": "string", + "format": "directory-path", + "exists": false, + "description": "Path to a local or remote directory that is the \"current working directory\" for relative paths defined in the input samplesheet", + "fa_icon": "fas fa-folder" + }, "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" }, @@ -34,42 +42,277 @@ "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})$" + } + } + }, + "preqc_options": { + "title": "QC/Filtering/Trimming options", + "type": "object", + "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 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", + "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 + }, + "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, + "minimum": 0.0, + "maximum": 0.9 + } + } + }, + "demux_options": { + "title": "Demux options", + "properties": { + "demux_mismatches": { + "fa_icon": "fas not-equal", + "description": "The number of mismatches allowed (as a fraction)", + "type": "number", + "default": 0.1, + "minimum": 0.0, + "maximum": 0.9 }, - "multiqc_title": { + "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", + "help_text": "If you set this argument it will overrule the value from the chosen design", + "type": "integer" + } + } + }, + "collapse_options": { + "title": "Collapse options", + "properties": { + "markers_ignore": { + "fa-icon": "fas fa-list", + "description": "A list of comma separated antibodies to discard", "type": "string", - "description": "MultiQC report title. Printed as page header, used for filename if not otherwise specified.", - "fa_icon": "fas fa-file-signature" + "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", + "enum": ["adjacency", "unique"], + "type": "string" + }, + "max_neighbours": { + "fa_icon": "fas circle-nodes", + "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, + "type": "integer", + "hidden": true + }, + "collapse_mismatches": { + "fa_icon": "fas not-equal", + "description": "The number of mismatches allowed when collapsing (adjacency)", + "type": "integer", + "default": 2, + "minimum": 0, + "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, + "minimum": 1, + "type": "integer" + }, + "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" } } }, - "reference_genome_options": { - "title": "Reference genome options", - "type": "object", - "fa_icon": "fas fa-dna", - "description": "Reference genome related files and options required for the workflow.", + "graph_options": { + "title": "Options for pixelator graph command.", "properties": { - "genome": { + "multiplet_recovery": { + "description": "Activate the multiplet recovery using leiden community detection", + "type": "boolean", + "default": 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", + "minimum": 1, + "maximum": 100, + "default": 10, + "hidden": true + }, + "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", + "default": 2, + "minimum": 1, + "maximum": 50, + "hidden": true + } + } + }, + "annotate_options": { + "title": "Options for pixelator annotate command.", + "properties": { + "min_size": { + "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 (disabled by default)", + "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", - "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": ["both", "min", "max"], + "default": "min" }, - "fasta": { + "aggregate_calling": { + "description": "Enable aggregate calling, information on potential aggregates will be added to the output data", + "type": "boolean", + "default": true + } + } + }, + "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 + }, + "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", - "format": "file-path", - "exists": true, - "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" - }, - "igenomes_ignore": { + "enum": ["raw", "clr", "denoise"], + "default": "clr" + }, + "polarization_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 + }, + "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 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 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 concidered valid for computing colocalization", + "default": 5, + "minimum": 0 + } + } + }, + "report_options": { + "title": "Options for pixelator report command.", + "properties": { + "skip_report": { + "description": "Skip report generation", + "type": "boolean", + "default": false + } + } + }, + "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." } } }, @@ -198,14 +441,6 @@ "fa_icon": "fas fa-remove-format", "hidden": true }, - "max_multiqc_email_size": { - "type": "string", - "description": "File size limit when attaching MultiQC 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.", @@ -219,24 +454,13 @@ "help_text": "Incoming hook URL for messaging service. Currently, MS Teams and Slack are supported.", "hidden": true }, - "multiqc_config": { + "tracedir": { "type": "string", - "format": "file-path", - "description": "Custom config file to supply to MultiQC.", - "fa_icon": "fas fa-cog", + "description": "Directory to keep pipeline Nextflow logs and reports.", + "default": "${params.outdir}/pipeline_info", + "fa_icon": "fas fa-cogs", "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" - }, "validate_params": { "type": "boolean", "description": "Boolean whether to validate parameters against the schema at runtime", @@ -273,7 +497,31 @@ "$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/global_config_options" }, { "$ref": "#/definitions/institutional_config_options" diff --git a/subworkflows/local/generate_reports.nf b/subworkflows/local/generate_reports.nf new file mode 100644 index 00000000..f6a484d9 --- /dev/null +++ b/subworkflows/local/generate_reports.nf @@ -0,0 +1,113 @@ +include { PIXELATOR_REPORT } from '../../modules/local/pixelator/single-cell/report/main' + + +workflow GENERATE_REPORTS { + take: + panel_files // [meta, panel_file] or [meta, []] + 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() + + ch_meta_col = panel_files + .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] + } + + ch_panel_col = panel_files + .map { meta, data -> [ meta.id, data] } + + // 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_report_data = ch_meta_col + .concat ( ch_panel_col ) + .concat ( ch_amplicon_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 (size: 10) + + // Split up everything per stage so we can recreate the expected directory structure for + // `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. + + // If no `panel_file` (data[1]) is given we need to pass in `panel` from the samplesheet instead + ch_panel_files_grouped = ch_report_data.map { id, data -> [ data[0], data[1], data[1] ? null : data[0].panel ] } + 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() : [] } + 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_panel_files_grouped, + ch_amplicon_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 0aecf87f..3af1adde 100644 --- a/subworkflows/local/input_check.nf +++ b/subworkflows/local/input_check.nf @@ -2,43 +2,189 @@ // Check input samplesheet and get read channels // -include { SAMPLESHEET_CHECK } from '../../modules/local/samplesheet_check' +include { fromSamplesheet } from 'plugin/nf-validation' +include { SAMPLESHEET_CHECK } from '../../modules/local/samplesheet_check' +include { PIXELATOR_LIST_OPTIONS } from '../../modules/local/pixelator/list_options.nf' workflow INPUT_CHECK { take: - samplesheet // file: /path/to/samplesheet.csv + samplesheet // file: /path/to/samplesheet.csv + input_basedir // string | null main: - SAMPLESHEET_CHECK ( samplesheet ) - .csv - .splitCsv ( header:true, sep:',' ) - .map { create_fastq_channel(it) } - .set { reads } + + // Create a new channel of metadata from a sample sheet + // NB: `input` corresponds to `params.input` and associated sample sheet schema + def inputBaseDir = get_data_basedir(samplesheet.toUri(), input_basedir) + + log.info "Resolving relative paths in samplesheet relative to: ${inputBaseDir}" + + ch_input = Channel.fromSamplesheet("input") + .map { check_channels(inputBaseDir, *it) } + + PIXELATOR_LIST_OPTIONS() + + // Create a set of valid pixelator options to pass to --design + ch_design_options = PIXELATOR_LIST_OPTIONS.out.designs + .splitText() + .map( text -> text.trim()) + .reduce( new HashSet() ) { prev, curr -> prev << curr } + + // Create a set of valid pixelator panel keys to pass using --panel + ch_panel_options = PIXELATOR_LIST_OPTIONS.out.panels + .splitText() + .map( text -> text.trim()) + .reduce( new HashSet() ) { prev, curr -> prev << curr } + + ch_checked_input = ch_input + .map { it -> it[0] } + .combine(ch_panel_options) + .combine(ch_design_options) + .map { + meta, panel_options, design_options -> + validate_panel(meta, panel_options) + validate_design(meta, design_options) + return [meta, []] + } + // Combine a dummy output after validation with the main input and strip the dummy value again + // This adds a dependency to make sure all jobs wait untill the validation is complete + .join(ch_input) + .map { it -> [ it[0] ] + it[2..-1] } + + reads = ch_checked_input.map { it -> [it[0]] + it[2..-1] } + panels = ch_checked_input.map { it -> [it[0], it[1]] } 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 = PIXELATOR_LIST_OPTIONS.out.versions // channel: [ versions.yml ] +} + + +// Resolve relative paths relative to the samplesheet parent directory. +def resolve_relative_path(relative_path, URI samplesheet_path) { + if (!(relative_path instanceof String)) { + return relative_path + } + + // Try to create a java.net.UR object out of it. If it is not a proper URL, a MalformedURLException will be t + URI uri; + + try { + uri = new URI(relative_path) + } catch (URISyntaxException exc) { + return relative_path + } + + // If a scheme is given we keep it as given + if (uri.getScheme() != null) { + return relative_path + } + + def path = new File(relative_path) + if (path.isAbsolute()) { + return relative_path + } + + // Resolve relative paths agains the samplesheet_path + def resolvedPath = samplesheet_path.resolve(relative_path); + + def stringPath = resolvedPath.toString() + return stringPath +} + + +// Validate a given panel key if present against the (dynamic) set of panel options retrieved from pixelator +def validate_panel(LinkedHashMap meta, HashSet options) { + if (meta.panel == null) { + return + } + + if (!options.contains(meta.panel)) { + exit 1, "ERROR: Please check input samplesheet -> panel field does not contains a valid key!\n${meta.panel}\nValid options:\n${options}" + } +} + + +// Validate a given design key if present against the (dynamic) set of design options retrieved from pixelator +def validate_design(LinkedHashMap meta, HashSet options) { + if (meta.design == null) { + return + } + + if (!options.contains(meta.design)) { + exit 1, "ERROR: Please check input samplesheet -> design field does contains a valid key!\n${meta.design}\nValid options:\n${options}" + } } -// 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() +// Determine the path/url that will be used as the root for relative paths in the samplesheet +def get_data_basedir(URI samplesheet, String input_basedir) { + + URI uri; + + // nothing given to --input_data so we use the samplesheet as root directory + // for resolving relative paths + if (!input_basedir) { + return samplesheet + } + + try { + uri = new URI(input_basedir) + } catch (URISyntaxException exc) { + return samplesheet + } + + // If a scheme is given we keep check that it is a directory (trailing-slash) + if (uri.getScheme() != null) { + if (!uri.path.endsWith('/')) { + def newUrl = new URI( + uri.getScheme(), uri.getUserInfo(), uri.getHost(), + uri.getPort(), uri.getPath() + '/', uri.getQuery(), uri.getFragment() + ) + return newUrl + } + return uri + } - // 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}" + f = file(input_basedir) + if (!f.exists()) { + exit 1, "ERROR: data path passed with --input_basedir does not exist!" } - if (meta.single_end) { - fastq_meta = [ meta, [ file(row.fastq_1) ] ] + if (f.isDirectory()) { + data_root = new URI(f.toString() + '/') } 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}" + data_root = new URI(f.toString()) + } + + return data_root +} + +// Resolve relative paths and check that all files exist. +def check_channels(URI samplesheetUrl, Map meta, panel_file, ...fq) { + def paired_end = fq.size() == 2 + def panel_file_abs = resolve_relative_path(panel_file, samplesheetUrl) + def fq1_abs = resolve_relative_path(fq[0], samplesheetUrl) + + if (panel_file_abs && !file(panel_file_abs).exists()) { + exit 1, "ERROR: Please check input samplesheet -> panel_file does not exist!\n${panel_file_abs}" + } + + if (!file(fq1_abs).exists()) { + exit 1, "ERROR: Please check input samplesheet -> fastq_1 does not exist!\n${fq1_abs}" + } + + def reads = [ fq1_abs ] + + if (paired_end) { + def fq2_abs = resolve_relative_path(fq[1], samplesheetUrl) + + if (fq2_abs && !file(fq2_abs).exists()) { + exit 1, "ERROR: Please check input samplesheet -> fastq_2 does not exist!\n${fq2_abs}" } - fastq_meta = [ meta, [ file(row.fastq_1), file(row.fastq_2) ] ] + + reads += [ fq2_abs] } - return fastq_meta + + return [ meta, panel_file_abs, reads ] } 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" diff --git a/workflows/pixelator.nf b/workflows/pixelator.nf index 518a97b6..d6dc95c9 100644 --- a/workflows/pixelator.nf +++ b/workflows/pixelator.nf @@ -6,6 +6,7 @@ include { paramsSummaryLog; paramsSummaryMap } from 'plugin/nf-validation' + def logo = NfcoreTemplate.logo(workflow, params.monochrome_logs) def citation = '\n' + WorkflowMain.citation(workflow) + '\n' def summary_params = paramsSummaryMap(workflow) @@ -13,7 +14,9 @@ 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') /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -21,11 +24,6 @@ WorkflowPixelator.initialise(params, log) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -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 @@ -35,7 +33,8 @@ 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' +include { GENERATE_REPORTS } from '../subworkflows/local/generate_reports' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -46,12 +45,29 @@ 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 { RENAME_READS } from '../modules/local/rename_reads' +include { PIXELATOR_COLLECT_METADATA } from '../modules/local/pixelator/collect_metadata' +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' +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' /* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +======================================================================================== RUN MAIN WORKFLOW ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ @@ -60,54 +76,154 @@ include { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/custom/dumpsoft def multiqc_report = [] workflow PIXELATOR { - ch_versions = Channel.empty() // // SUBWORKFLOW: Read in samplesheet, validate and stage input files // - INPUT_CHECK ( - 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 + // Create a new channel of metadata from a sample sheet + // NB: `input` corresponds to `params.input` and associated sample sheet schema + INPUT_CHECK ( ch_input, params.input_basedir ) + + ch_reads = INPUT_CHECK.out.reads + ch_panel_files = INPUT_CHECK.out.panels + + ch_fastq_split = ch_reads + .groupTuple() + .branch { + meta, fastq -> + single : fastq.size() == 1 + return [ meta, fastq.flatten() ] + multiple: fastq.size() > 1 + return [ meta, fastq.flatten() ] + } + + PIXELATOR_COLLECT_METADATA () + ch_versions = ch_versions.mix(PIXELATOR_COLLECT_METADATA.out.versions) // - // MODULE: Run FastQC + // MODULE: Concatenate FastQ files from same sample if required // - FASTQC ( - INPUT_CHECK.out.reads + ch_cat_fastq = CAT_FASTQ ( ch_fastq_split.multiple ) + .reads + .mix(ch_fastq_split.single) + + // 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 + RENAME_READS ( ch_cat_fastq ) + ch_renamed_reads = RENAME_READS.out.reads + ch_versions = ch_versions.mix(RENAME_READS.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 + + PIXELATOR_QC ( ch_input_reads ) + ch_qc = PIXELATOR_QC.out.processed + ch_versions = ch_versions.mix(PIXELATOR_QC.out.versions.first()) + + ch_fq_and_panel = ch_qc + .join(ch_cat_panel_files, failOnMismatch:true, failOnDuplicate:true) + .map { meta, fq, panel_file -> [meta, fq, panel_file, panel_file ? null : meta.panel ] } + + PIXELATOR_DEMUX ( ch_fq_and_panel ) + ch_demuxed = PIXELATOR_DEMUX.out.processed + ch_versions = ch_versions.mix(PIXELATOR_DEMUX.out.versions.first()) + + ch_demuxed_and_panel = ch_demuxed + .join(ch_cat_panel_files, failOnMismatch:true, failOnDuplicate:true) + .map { meta, demuxed, panel_file -> [meta, demuxed, panel_file, panel_file ? null : meta.panel ] } + + PIXELATOR_COLLAPSE ( ch_demuxed_and_panel ) + 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_cat_panel_files, failOnMismatch:true, failOnDuplicate:true) + .map { meta, clustered, panel_file -> [meta, clustered, panel_file, panel_file ? null : meta.panel ] } + + PIXELATOR_ANNOTATE ( ch_clustered_and_panel ) + ch_annotated = PIXELATOR_ANNOTATE.out.dataset + 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()) + + + // 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_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_cat_panel_files, + ch_amplicon_data, + ch_preqc_data, + ch_adapterqc_data, + ch_demux_data, + ch_collapse_data, + ch_cluster_data, + ch_annotate_data, + ch_analysis_data ) - ch_versions = ch_versions.mix(FASTQC.out.versions.first()) + + ch_versions = ch_versions.mix(GENERATE_REPORTS.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, params) - 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 implemented } /* From e196431842b039cbf5c299c7a3e568f6a3e30e33 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 11 Jan 2024 13:24:36 +0100 Subject: [PATCH 133/260] disable docker.runOptions -u and -g to workaround container issue --- nextflow.config | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/nextflow.config b/nextflow.config index d3384f50..f6193e5a 100644 --- a/nextflow.config +++ b/nextflow.config @@ -154,7 +154,8 @@ profiles { shifter.enabled = false charliecloud.enabled = false apptainer.enabled = false - docker.runOptions = '-u $(id -u):$(id -g)' + // Disabled this to deal with libpysal temporary directory issue + // docker.runOptions = '-u $(id -u):$(id -g)' } arm { docker.runOptions = '-u $(id -u):$(id -g) --platform=linux/amd64' @@ -271,7 +272,7 @@ manifest { description = """Pipeline for analysis of Molecular Pixelation assays""" mainScript = 'main.nf' nextflowVersion = '!>=23.04.0' - version = '1.1.0dev' + version = '1.1.0dev' // TODO: Use zenodo DOI once available doi = '10.1101/2023.06.05.543770' } From 0d91247761f6f2dc18ffe6d2002ba1a9932a0207 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 11 Jan 2024 13:29:46 +0100 Subject: [PATCH 134/260] update nf-core modules, sync github templates --- .github/CONTRIBUTING.md | 3 -- .github/PULL_REQUEST_TEMPLATE.md | 1 - .github/workflows/linting.yml | 12 ++--- modules.json | 4 +- modules/nf-core/cat/fastq/environment.yml | 1 + modules/nf-core/cat/fastq/main.nf | 2 +- .../dumpsoftwareversions/environment.yml | 2 +- .../custom/dumpsoftwareversions/main.nf | 4 +- .../dumpsoftwareversions/tests/main.nf.test | 7 ++- .../tests/main.nf.test.snap | 50 +++++++++++-------- 10 files changed, 47 insertions(+), 39 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index b78bb9d3..0065fc46 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -27,9 +27,6 @@ If you're not used to this workflow with git, you can start with some [docs from ## Tests -You can optionally test your changes by running the pipeline locally. Then it is recommended to use the `debug` profile to -receive warnings about process selectors and other debug info. Example: `nextflow run . -profile debug,test,docker --outdir `. - When you create a pull request with changes, [GitHub Actions](https://github.com/features/actions) will run automatic tests. Typically, pull-requests are only fully reviewed when these tests are passing, though of course we can help out before then. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 9d93ee87..71151c23 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -19,7 +19,6 @@ Learn more about contributing: [CONTRIBUTING.md](https://github.com/nf-core/pixe - [ ] If necessary, also make a PR on the nf-core/pixelator _branch_ on the [nf-core/test-datasets](https://github.com/nf-core/test-datasets) repository. - [ ] Make sure your code lints (`nf-core lint`). - [ ] Ensure the test suite passes (`nextflow run . -profile test,docker --outdir `). -- [ ] Check for unexpected warnings in debug mode (`nextflow run . -profile debug,test,docker --outdir `). - [ ] Usage Documentation in `docs/usage.md` is updated. - [ ] Output Documentation in `docs/output.md` is updated. - [ ] `CHANGELOG.md` is updated. diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 905c58e4..b8bdd214 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -14,9 +14,9 @@ jobs: EditorConfig: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v3 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v3 - name: Install editorconfig-checker run: npm install -g editorconfig-checker @@ -27,9 +27,9 @@ jobs: Prettier: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v3 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v3 - name: Install Prettier run: npm install -g prettier @@ -40,7 +40,7 @@ jobs: PythonBlack: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v3 - name: Check code lints with Black uses: psf/black@stable @@ -71,7 +71,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Check out pipeline code - uses: actions/checkout@v4 + uses: actions/checkout@v3 - name: Install Nextflow uses: nf-core/setup-nextflow@v1 diff --git a/modules.json b/modules.json index 06cde38b..b4bc6ee4 100644 --- a/modules.json +++ b/modules.json @@ -7,12 +7,12 @@ "nf-core": { "cat/fastq": { "branch": "master", - "git_sha": "516189e968feb4ebdd9921806988b4c12b4ac2dc", + "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", "installed_by": ["modules"] }, "custom/dumpsoftwareversions": { "branch": "master", - "git_sha": "516189e968feb4ebdd9921806988b4c12b4ac2dc", + "git_sha": "8ec825f465b9c17f9d83000022995b4f7de6fe93", "installed_by": ["modules"] } } diff --git a/modules/nf-core/cat/fastq/environment.yml b/modules/nf-core/cat/fastq/environment.yml index 222b301f..bff93add 100644 --- a/modules/nf-core/cat/fastq/environment.yml +++ b/modules/nf-core/cat/fastq/environment.yml @@ -1,3 +1,4 @@ +name: cat_fastq channels: - conda-forge - bioconda diff --git a/modules/nf-core/cat/fastq/main.nf b/modules/nf-core/cat/fastq/main.nf index b75a2e73..3d963784 100644 --- a/modules/nf-core/cat/fastq/main.nf +++ b/modules/nf-core/cat/fastq/main.nf @@ -2,7 +2,7 @@ process CAT_FASTQ { tag "$meta.id" label 'process_single' - conda 'modules/nf-core/cat/fastq/environment.yml' + conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/ubuntu:20.04' : 'nf-core/ubuntu:20.04' }" diff --git a/modules/nf-core/custom/dumpsoftwareversions/environment.yml b/modules/nf-core/custom/dumpsoftwareversions/environment.yml index f0c63f69..9b3272bc 100644 --- a/modules/nf-core/custom/dumpsoftwareversions/environment.yml +++ b/modules/nf-core/custom/dumpsoftwareversions/environment.yml @@ -4,4 +4,4 @@ channels: - bioconda - defaults dependencies: - - bioconda::multiqc=1.17 + - bioconda::multiqc=1.19 diff --git a/modules/nf-core/custom/dumpsoftwareversions/main.nf b/modules/nf-core/custom/dumpsoftwareversions/main.nf index 7685b33c..f2187611 100644 --- a/modules/nf-core/custom/dumpsoftwareversions/main.nf +++ b/modules/nf-core/custom/dumpsoftwareversions/main.nf @@ -4,8 +4,8 @@ process CUSTOM_DUMPSOFTWAREVERSIONS { // Requires `pyyaml` which does not have a dedicated container but is in the MultiQC container conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/multiqc:1.17--pyhdfd78af_0' : - 'biocontainers/multiqc:1.17--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/multiqc:1.19--pyhdfd78af_0' : + 'biocontainers/multiqc:1.19--pyhdfd78af_0' }" input: path versions diff --git a/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test index eec1db10..b1e1630b 100644 --- a/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test +++ b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test @@ -31,7 +31,12 @@ nextflow_process { then { assertAll( { assert process.success }, - { assert snapshot(process.out).match() } + { assert snapshot( + process.out.versions, + file(process.out.mqc_yml[0]).readLines()[0..10], + file(process.out.yml[0]).readLines()[0..7] + ).match() + } ) } } diff --git a/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap index 8713b921..5f59a936 100644 --- a/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap +++ b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap @@ -1,27 +1,33 @@ { "Should run without failures": { "content": [ - { - "0": [ - "software_versions.yml:md5,a027f820f30b8191a20ca16465daaf37" - ], - "1": [ - "software_versions_mqc.yml:md5,ee4a1d028ad29987f9ac511f4668f17c" - ], - "2": [ - "versions.yml:md5,f47ebd22aba1dd987b7e5d5247b766c3" - ], - "mqc_yml": [ - "software_versions_mqc.yml:md5,ee4a1d028ad29987f9ac511f4668f17c" - ], - "versions": [ - "versions.yml:md5,f47ebd22aba1dd987b7e5d5247b766c3" - ], - "yml": [ - "software_versions.yml:md5,a027f820f30b8191a20ca16465daaf37" - ] - } + [ + "versions.yml:md5,76d454d92244589d32455833f7c1ba6d" + ], + [ + "data: \"\\n\\n \\n \\n \\n \\n \\n \\n \\n\\", + " \\n\\n\\n \\n \\n\\", + " \\ \\n\\n\\n\\n \\n \\", + " \\ \\n \\n\\n\\n\\n\\", + " \\n\\n \\n \\n\\", + " \\ \\n\\n\\n\\n\\n\\n \\n\\", + " \\ \\n \\n\\n\\n\\n\\", + " \\n\\n \\n \\n\\" + ], + [ + "CUSTOM_DUMPSOFTWAREVERSIONS:", + " python: 3.11.7", + " yaml: 5.4.1", + "TOOL1:", + " tool1: 0.11.9", + "TOOL2:", + " tool2: '1.9'", + "Workflow:" + ] ], - "timestamp": "2023-10-11T17:10:02.930699" + "timestamp": "2024-01-09T23:01:18.710682" } -} +} \ No newline at end of file From b9ccf5b9324ddd3bf39eddb3b4ab1eb55fb2819a Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 19 Jan 2024 09:43:45 +0100 Subject: [PATCH 135/260] fix: use adapterqc output as main output of PIXELATOR_QC --- modules/local/pixelator/single-cell/qc/main.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/local/pixelator/single-cell/qc/main.nf b/modules/local/pixelator/single-cell/qc/main.nf index e07ae1e2..141ed303 100644 --- a/modules/local/pixelator/single-cell/qc/main.nf +++ b/modules/local/pixelator/single-cell/qc/main.nf @@ -12,7 +12,7 @@ process PIXELATOR_QC { 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: 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 From 5426ddc57d350034caf8885394424d6aac6dab8c Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 19 Jan 2024 10:06:53 +0100 Subject: [PATCH 136/260] ignore devcontainer.json in prettier --- .nf-core.yml | 1 + .prettierignore | 1 + 2 files changed, 2 insertions(+) diff --git a/.nf-core.yml b/.nf-core.yml index 7fe39026..2714bdba 100644 --- a/.nf-core.yml +++ b/.nf-core.yml @@ -7,3 +7,4 @@ lint: - conf/igenomes.config files_unchanged: - lib/NfcoreTemplate.groovy + - .prettierignore diff --git a/.prettierignore b/.prettierignore index 56fc6a49..490a9c25 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,6 +1,7 @@ email_template.html adaptivecard.json slackreport.json +.devcontainer/devcontainer.json .nextflow* work/ data/ From 2c7bb5cd481b8cf363366dffb137f013696d0324 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 19 Jan 2024 13:39:46 +0100 Subject: [PATCH 137/260] cleanup nextflow_schema.json --- assets/nf-params.yml | 12 ++++++------ nextflow_schema.json | 28 ++++++++++++++-------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/assets/nf-params.yml b/assets/nf-params.yml index 995f2130..51a7ab6b 100644 --- a/assets/nf-params.yml +++ b/assets/nf-params.yml @@ -107,7 +107,7 @@ ## Remove duplicated reads (exact same sequence) ## Type: boolean ## ----------------------------------------------------------------------------- -# dedup = false +# dedup = null ## ----------------------------------------------------------------------------- ## remove_polyg @@ -115,7 +115,7 @@ ## Remove PolyG sequences (length of 10 or more) ## Type: boolean ## ----------------------------------------------------------------------------- -# remove_polyg = false +# remove_polyg = null ## ============================================================================= @@ -271,7 +271,7 @@ ## Skip analysis step ## Type: boolean ## ----------------------------------------------------------------------------- -# skip_analysis = false +# skip_analysis = null ## ----------------------------------------------------------------------------- ## compute_polarization @@ -296,7 +296,7 @@ ## computing polarization, coabundance and colocalization scores ## Type: boolean ## ----------------------------------------------------------------------------- -# use_full_bipartite = false +# use_full_bipartite = null ## ----------------------------------------------------------------------------- ## polarization_normalization @@ -313,7 +313,7 @@ ## polarization ## Type: boolean ## ----------------------------------------------------------------------------- -# polarization_binarization = false +# polarization_binarization = null ## ----------------------------------------------------------------------------- ## colocalization_transformation @@ -362,7 +362,7 @@ ## Skip report generation ## Type: boolean ## ----------------------------------------------------------------------------- -# skip_report = false +# skip_report = null ## ============================================================================= diff --git a/nextflow_schema.json b/nextflow_schema.json index 3210d63f..60ac0bc3 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -90,19 +90,18 @@ "dedup": { "fa_icon": "fas clone", "description": "Remove duplicated reads (exact same sequence)", - "type": "boolean", - "default": false + "type": "boolean" }, "remove_polyg": { "fa_icon": "fas g", "description": "Remove PolyG sequences (length of 10 or more)", - "type": "boolean", - "default": false + "type": "boolean" } } }, "adapterqc_options": { "title": "Adapter QC Options", + "type": "object", "properties": { "adapterqc_mismatches": { "fa_icon": "fas not-equal", @@ -116,6 +115,7 @@ }, "demux_options": { "title": "Demux options", + "type": "object", "properties": { "demux_mismatches": { "fa_icon": "fas not-equal", @@ -135,9 +135,10 @@ }, "collapse_options": { "title": "Collapse options", + "type": "object", "properties": { "markers_ignore": { - "fa-icon": "fas fa-list", + "fa_icon": "fas fa-list", "description": "A list of comma separated antibodies to discard", "type": "string", "pattern": "(\\S+)?(,\\S+)*" @@ -181,6 +182,7 @@ }, "graph_options": { "title": "Options for pixelator graph command.", + "type": "object", "properties": { "multiplet_recovery": { "description": "Activate the multiplet recovery using leiden community detection", @@ -209,6 +211,7 @@ }, "annotate_options": { "title": "Options for pixelator annotate command.", + "type": "object", "properties": { "min_size": { "description": "The minimum size (pixels) a component/cell can have (disabled by default)", @@ -233,11 +236,11 @@ }, "analysis_options": { "title": "Options for pixelator analysis command.", + "type": "object", "properties": { "skip_analysis": { "description": "Skip analysis step", - "type": "boolean", - "default": false + "type": "boolean" }, "compute_polarization": { "description": "Compute polarization scores matrix (clusters by markers)", @@ -251,8 +254,7 @@ }, "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 + "type": "boolean" }, "polarization_normalization": { "description": "Which approach to use to normalize the antibody counts.", @@ -264,8 +266,7 @@ "polarization_binarization": { "fa_icon": "fas binary", "description": "Transform the antibody counts to 0-1 (binarize) when computing polarization", - "type": "boolean", - "default": false + "type": "boolean" }, "colocalization_transformation": { "type": "string", @@ -295,11 +296,11 @@ }, "report_options": { "title": "Options for pixelator report command.", + "type": "object", "properties": { "skip_report": { "description": "Skip report generation", - "type": "boolean", - "default": false + "type": "boolean" } } }, @@ -310,7 +311,6 @@ "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." } From 0f01be74944a97e89f6522011975223f29ca1611 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 19 Jan 2024 16:31:31 +0100 Subject: [PATCH 138/260] bump version to 1.0.3 for release --- nextflow.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextflow.config b/nextflow.config index 4e7e8a68..fee82590 100644 --- a/nextflow.config +++ b/nextflow.config @@ -272,7 +272,7 @@ manifest { description = """Pipeline for analysis of Molecular Pixelation assays""" mainScript = 'main.nf' nextflowVersion = '!>=23.04.0' - version = '1.1.0dev' + version = '1.0.3' doi = '10.1101/2023.06.05.543770' } From 6393be719cba20c99717458f8981fb19963ea620 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 19 Jan 2024 16:51:05 +0100 Subject: [PATCH 139/260] remove leftover files from template sync --- modules/nf-core/fastqc/environment.yml | 7 -- modules/nf-core/fastqc/tests/main.nf.test | 109 ------------------ .../nf-core/fastqc/tests/main.nf.test.snap | 10 -- modules/nf-core/fastqc/tests/tags.yml | 2 - modules/nf-core/multiqc/environment.yml | 7 -- modules/nf-core/multiqc/tests/main.nf.test | 63 ---------- modules/nf-core/multiqc/tests/tags.yml | 2 - 7 files changed, 200 deletions(-) delete mode 100644 modules/nf-core/fastqc/environment.yml delete mode 100644 modules/nf-core/fastqc/tests/main.nf.test delete mode 100644 modules/nf-core/fastqc/tests/main.nf.test.snap delete mode 100644 modules/nf-core/fastqc/tests/tags.yml delete mode 100644 modules/nf-core/multiqc/environment.yml delete mode 100644 modules/nf-core/multiqc/tests/main.nf.test delete mode 100644 modules/nf-core/multiqc/tests/tags.yml diff --git a/modules/nf-core/fastqc/environment.yml b/modules/nf-core/fastqc/environment.yml deleted file mode 100644 index 1787b38a..00000000 --- a/modules/nf-core/fastqc/environment.yml +++ /dev/null @@ -1,7 +0,0 @@ -name: fastqc -channels: - - conda-forge - - bioconda - - defaults -dependencies: - - bioconda::fastqc=0.12.1 diff --git a/modules/nf-core/fastqc/tests/main.nf.test b/modules/nf-core/fastqc/tests/main.nf.test deleted file mode 100644 index b9e8f926..00000000 --- a/modules/nf-core/fastqc/tests/main.nf.test +++ /dev/null @@ -1,109 +0,0 @@ -nextflow_process { - - name "Test Process FASTQC" - script "../main.nf" - process "FASTQC" - tag "modules" - tag "modules_nfcore" - tag "fastqc" - - test("Single-Read") { - - when { - params { - outdir = "$outputDir" - } - process { - """ - input[0] = [ - [ id: 'test', single_end:true ], - [ - file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true) - ] - ] - """ - } - } - - then { - assertAll ( - { assert process.success }, - // NOTE The report contains the date inside it, which means that the md5sum is stable per day, but not longer than that. So you can't md5sum it. - // looks like this:
Mon 2 Oct 2023
test.gz
- // https://github.com/nf-core/modules/pull/3903#issuecomment-1743620039 - { assert process.out.html.get(0).get(1) ==~ ".*/test_fastqc.html" }, - { assert path(process.out.html.get(0).get(1)).getText().contains("
") }, - { assert snapshot(process.out.versions).match("versions") }, - { assert process.out.zip.get(0).get(1) ==~ ".*/test_fastqc.zip" } - ) - } - } -// TODO -// // -// // Test with paired-end data -// // -// workflow test_fastqc_paired_end { -// input = [ -// [id: 'test', single_end: false], // meta map -// [ -// file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), -// file(params.test_data['sarscov2']['illumina']['test_2_fastq_gz'], checkIfExists: true) -// ] -// ] - -// FASTQC ( input ) -// } - -// // -// // Test with interleaved data -// // -// workflow test_fastqc_interleaved { -// input = [ -// [id: 'test', single_end: false], // meta map -// file(params.test_data['sarscov2']['illumina']['test_interleaved_fastq_gz'], checkIfExists: true) -// ] - -// FASTQC ( input ) -// } - -// // -// // Test with bam data -// // -// workflow test_fastqc_bam { -// input = [ -// [id: 'test', single_end: false], // meta map -// file(params.test_data['sarscov2']['illumina']['test_paired_end_sorted_bam'], checkIfExists: true) -// ] - -// FASTQC ( input ) -// } - -// // -// // Test with multiple samples -// // -// workflow test_fastqc_multiple { -// input = [ -// [id: 'test', single_end: false], // meta map -// [ -// file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), -// file(params.test_data['sarscov2']['illumina']['test_2_fastq_gz'], checkIfExists: true), -// file(params.test_data['sarscov2']['illumina']['test2_1_fastq_gz'], checkIfExists: true), -// file(params.test_data['sarscov2']['illumina']['test2_2_fastq_gz'], checkIfExists: true) -// ] -// ] - -// FASTQC ( input ) -// } - -// // -// // Test with custom prefix -// // -// workflow test_fastqc_custom_prefix { -// input = [ -// [ id:'mysample', single_end:true ], // meta map -// file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true) -// ] - -// FASTQC ( input ) -// } -} diff --git a/modules/nf-core/fastqc/tests/main.nf.test.snap b/modules/nf-core/fastqc/tests/main.nf.test.snap deleted file mode 100644 index 636a32ce..00000000 --- a/modules/nf-core/fastqc/tests/main.nf.test.snap +++ /dev/null @@ -1,10 +0,0 @@ -{ - "versions": { - "content": [ - [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" - ] - ], - "timestamp": "2023-10-09T23:40:54+0000" - } -} \ No newline at end of file diff --git a/modules/nf-core/fastqc/tests/tags.yml b/modules/nf-core/fastqc/tests/tags.yml deleted file mode 100644 index 7834294b..00000000 --- a/modules/nf-core/fastqc/tests/tags.yml +++ /dev/null @@ -1,2 +0,0 @@ -fastqc: - - modules/nf-core/fastqc/** diff --git a/modules/nf-core/multiqc/environment.yml b/modules/nf-core/multiqc/environment.yml deleted file mode 100644 index bc0bdb5b..00000000 --- a/modules/nf-core/multiqc/environment.yml +++ /dev/null @@ -1,7 +0,0 @@ -name: multiqc -channels: - - conda-forge - - bioconda - - defaults -dependencies: - - bioconda::multiqc=1.18 diff --git a/modules/nf-core/multiqc/tests/main.nf.test b/modules/nf-core/multiqc/tests/main.nf.test deleted file mode 100644 index c2dad217..00000000 --- a/modules/nf-core/multiqc/tests/main.nf.test +++ /dev/null @@ -1,63 +0,0 @@ -nextflow_process { - - name "Test Process MULTIQC" - script "../main.nf" - process "MULTIQC" - tag "modules" - tag "modules_nfcore" - tag "multiqc" - - test("MULTIQC: FASTQC") { - - when { - params { - outdir = "$outputDir" - } - process { - """ - input[0] = Channel.of([file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz_fastqc_zip'], checkIfExists: true)]) - input[1] = [] - input[2] = [] - input[3] = [] - """ - } - } - - then { - assertAll( - { assert process.success }, - { assert path(process.out.report.get(0)).exists() }, - { assert path(process.out.data.get(0)).exists() }, - { assert path(process.out.versions.get(0)).getText().contains("multiqc") } - ) - } - - } - - test("MULTIQC: FASTQC and a config file") { - - when { - params { - outdir = "$outputDir" - } - process { - """ - input[0] = Channel.of([file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz_fastqc_zip'], checkIfExists: true)]) - input[1] = Channel.of(file("https://github.com/nf-core/tools/raw/dev/nf_core/pipeline-template/assets/multiqc_config.yml", checkIfExists: true)) - input[2] = [] - input[3] = [] - """ - } - } - - then { - assertAll( - { assert process.success }, - { assert path(process.out.report.get(0)).exists() }, - { assert path(process.out.data.get(0)).exists() }, - { assert path(process.out.versions.get(0)).getText().contains("multiqc") } - ) - } - - } -} diff --git a/modules/nf-core/multiqc/tests/tags.yml b/modules/nf-core/multiqc/tests/tags.yml deleted file mode 100644 index bea6c0d3..00000000 --- a/modules/nf-core/multiqc/tests/tags.yml +++ /dev/null @@ -1,2 +0,0 @@ -multiqc: - - modules/nf-core/multiqc/** From 5038aca32866e7c6f94981cf9692dd9f7039ef7b Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 19 Jan 2024 16:51:29 +0100 Subject: [PATCH 140/260] update CHANGELOG --- CHANGELOG.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3d0823e5..4fd9204d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,13 +3,22 @@ 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). -## [1.0.2] - 2023-11-20 +## [[1.0.3](https://github.com/nf-core/pixelator/releases/tag/1.0.3)] - 2024-01-19 + +### Enhancements & fixes + +- [[PR #74](https://github.com/nf-core/pixelator/pull/74)] - Template update for nf-core/tools v2.11 +- [[e196431](https://github.com/nf-core/pixelator/commit/e196431842b039cbf5c299c7a3e568f6a3e30e33)] - Workaround a tool issue by removing `docker.runOptions` user and group flags +- [[PR #76](https://github.com/nf-core/pixelator/pull/76)] - Use `adapterqc` output as main output of PIXELATOR_QC +- [[PR #77](https://github.com/nf-core/pixelator/pull/77)] - Fix some style issues in nextflow_schema.json + +## [[1.0.2](https://github.com/nf-core/pixelator/releases/tag/1.0.2)] - 2023-11-20 ### Enhancements & fixes - [[PR #70](https://github.com/nf-core/pixelator/pull/70)] - Fix loading of absolute paths and urls in input samplesheet -## [1.0.1] - 2023-10-27 +## [[1.0.1](https://github.com/nf-core/pixelator/releases/tag/1.0.1)] - 2023-10-27 ### Enhancements & fixes @@ -29,6 +38,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 > > **NB:** Dependency has been **removed** if new version information isn't present. -## [1.0.0] - 2023-10-17 +## [[1.0.0](https://github.com/nf-core/pixelator/releases/tag/1.0.0)] - 2023-10-17 Initial release of nf-core/pixelator. From f217f053a46cdc2ac332de78f95082220df9a7ad Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 19 Jan 2024 17:18:46 +0100 Subject: [PATCH 141/260] fix admonition syntax --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ca3df6bd..03176261 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ It takes a samplesheet as input and will process your data using `pixelator` to 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)) -> **Warning** +> [!WARNING] > Since Nextflow 23.07.0-edge, Nextflow no longer mounts the host's home directory when using Apptainer or Singularity. > This causes issues in some dependencies. As a workaround, you can revert to the old behavior by setting the environment variable > `NXF_APPTAINER_HOME_MOUNT` or `NXF_SINGULARITY_HOME_MOUNT` to `true` in the machine from which you launch the pipeline. From 35481f0e83b06d7f67a4b1c614138cf797f07471 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 19 Jan 2024 17:25:29 +0100 Subject: [PATCH 142/260] update cat/fastq module --- modules.json | 2 +- modules/nf-core/cat/fastq/tests/main.nf.test | 63 +++--- .../nf-core/cat/fastq/tests/main.nf.test.snap | 185 +++++++++++++----- 3 files changed, 168 insertions(+), 82 deletions(-) diff --git a/modules.json b/modules.json index b4bc6ee4..83f6490f 100644 --- a/modules.json +++ b/modules.json @@ -7,7 +7,7 @@ "nf-core": { "cat/fastq": { "branch": "master", - "git_sha": "3f5420aa22e00bd030a2556dfdffc9e164ec0ec5", + "git_sha": "02fd5bd7275abad27aad32d5c852e0a9b1b98882", "installed_by": ["modules"] }, "custom/dumpsoftwareversions": { diff --git a/modules/nf-core/cat/fastq/tests/main.nf.test b/modules/nf-core/cat/fastq/tests/main.nf.test index f5f94182..dab2e14c 100644 --- a/modules/nf-core/cat/fastq/tests/main.nf.test +++ b/modules/nf-core/cat/fastq/tests/main.nf.test @@ -16,11 +16,11 @@ nextflow_process { } process { """ - input[0] = [ - [ id:'test', single_end:true ], // meta map - [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), - file(params.test_data['sarscov2']['illumina']['test2_1_fastq_gz'], checkIfExists: true) ] - ] + input[0] = Channel.of([ + [ id:'test', single_end:true ], // meta map + [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true)] + ]) """ } } @@ -28,8 +28,7 @@ nextflow_process { then { assertAll( { assert process.success }, - { assert snapshot(process.out.reads).match() }, - { assert path(process.out.versions.get(0)).getText().contains("cat") } + { assert snapshot(process.out).match() } ) } } @@ -42,13 +41,13 @@ nextflow_process { } process { """ - input[0] = [ + input[0] = Channel.of([ [ id:'test', single_end:false ], // meta map - [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), - file(params.test_data['sarscov2']['illumina']['test_2_fastq_gz'], checkIfExists: true), - file(params.test_data['sarscov2']['illumina']['test2_1_fastq_gz'], checkIfExists: true), - file(params.test_data['sarscov2']['illumina']['test2_2_fastq_gz'], checkIfExists: true) ] - ] + [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test2_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test2_2.fastq.gz', checkIfExists: true)] + ]) """ } } @@ -56,8 +55,7 @@ nextflow_process { then { assertAll( { assert process.success }, - { assert snapshot(process.out.reads).match() }, - { assert path(process.out.versions.get(0)).getText().contains("cat") } + { assert snapshot(process.out).match() } ) } } @@ -70,11 +68,11 @@ nextflow_process { } process { """ - input[0] = [ + input[0] = Channel.of([ [ id:'test', single_end:true ], // meta map - [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), - file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true) ] - ] + [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true)] + ]) """ } } @@ -82,8 +80,7 @@ nextflow_process { then { assertAll( { assert process.success }, - { assert snapshot(process.out.reads).match() }, - { assert path(process.out.versions.get(0)).getText().contains("cat") } + { assert snapshot(process.out).match() } ) } } @@ -96,13 +93,13 @@ nextflow_process { } process { """ - input[0] = [ + input[0] = Channel.of([ [ id:'test', single_end:false ], // meta map - [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), - file(params.test_data['sarscov2']['illumina']['test_2_fastq_gz'], checkIfExists: true), - file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true), - file(params.test_data['sarscov2']['illumina']['test_2_fastq_gz'], checkIfExists: true) ] - ] + [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true)] + ]) """ } } @@ -110,8 +107,7 @@ nextflow_process { then { assertAll( { assert process.success }, - { assert snapshot(process.out.reads).match() }, - { assert path(process.out.versions.get(0)).getText().contains("cat") } + { assert snapshot(process.out).match() } ) } } @@ -124,10 +120,10 @@ nextflow_process { } process { """ - input[0] = [ + input[0] = Channel.of([ [ id:'test', single_end:true ], // meta map - [ file(params.test_data['sarscov2']['illumina']['test_1_fastq_gz'], checkIfExists: true)] - ] + [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true)] + ]) """ } } @@ -135,8 +131,7 @@ nextflow_process { then { assertAll( { assert process.success }, - { assert snapshot(process.out.reads).match() }, - { assert path(process.out.versions.get(0)).getText().contains("cat") } + { assert snapshot(process.out).match() } ) } } diff --git a/modules/nf-core/cat/fastq/tests/main.nf.test.snap b/modules/nf-core/cat/fastq/tests/main.nf.test.snap index ec2342e5..43dfe28f 100644 --- a/modules/nf-core/cat/fastq/tests/main.nf.test.snap +++ b/modules/nf-core/cat/fastq/tests/main.nf.test.snap @@ -1,78 +1,169 @@ { "test_cat_fastq_single_end": { "content": [ - [ - [ - { - "id": "test", - "single_end": true - }, - "test.merged.fastq.gz:md5,f9cf5e375f7de81a406144a2c70cc64d" + { + "0": [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,ee314a9bd568d06617171b0c85f508da" + ] + ], + "1": [ + "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + ], + "reads": [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,ee314a9bd568d06617171b0c85f508da" + ] + ], + "versions": [ + "versions.yml:md5,d42d6e24d67004608495883e00bd501b" ] - ] + } ], - "timestamp": "2023-10-17T23:19:12.990284837" + "timestamp": "2024-01-17T17:30:39.816981" }, "test_cat_fastq_single_end_same_name": { "content": [ - [ - [ - { - "id": "test", - "single_end": true - }, - "test.merged.fastq.gz:md5,63f817db7a29a03eb538104495556f66" + { + "0": [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,3ad9406595fafec8172368f9cd0b6a22" + ] + ], + "1": [ + "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + ], + "reads": [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,3ad9406595fafec8172368f9cd0b6a22" + ] + ], + "versions": [ + "versions.yml:md5,d42d6e24d67004608495883e00bd501b" ] - ] + } ], - "timestamp": "2023-10-17T23:19:31.554568147" + "timestamp": "2024-01-17T17:32:35.229332" }, "test_cat_fastq_single_end_single_file": { "content": [ - [ - [ - { - "id": "test", - "single_end": true - }, - "test.merged.fastq.gz:md5,e325ef7deb4023447a1f074e285761af" + { + "0": [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,4161df271f9bfcd25d5845a1e220dbec" + ] + ], + "1": [ + "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + ], + "reads": [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,4161df271f9bfcd25d5845a1e220dbec" + ] + ], + "versions": [ + "versions.yml:md5,d42d6e24d67004608495883e00bd501b" ] - ] + } ], - "timestamp": "2023-10-17T23:19:49.629360033" + "timestamp": "2024-01-17T17:34:00.058829" }, "test_cat_fastq_paired_end_same_name": { "content": [ - [ - [ - { - "id": "test", - "single_end": false - }, + { + "0": [ [ - "test_1.merged.fastq.gz:md5,63f817db7a29a03eb538104495556f66", - "test_2.merged.fastq.gz:md5,fe9f266f43a6fc3dcab690a18419a56e" + { + "id": "test", + "single_end": false + }, + [ + "test_1.merged.fastq.gz:md5,3ad9406595fafec8172368f9cd0b6a22", + "test_2.merged.fastq.gz:md5,a52cab0b840c7178b0ea83df1fdbe8d5" + ] ] + ], + "1": [ + "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + ], + "reads": [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test_1.merged.fastq.gz:md5,3ad9406595fafec8172368f9cd0b6a22", + "test_2.merged.fastq.gz:md5,a52cab0b840c7178b0ea83df1fdbe8d5" + ] + ] + ], + "versions": [ + "versions.yml:md5,d42d6e24d67004608495883e00bd501b" ] - ] + } ], - "timestamp": "2023-10-17T23:19:40.711617539" + "timestamp": "2024-01-17T17:33:33.031555" }, "test_cat_fastq_paired_end": { "content": [ - [ - [ - { - "id": "test", - "single_end": false - }, + { + "0": [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test_1.merged.fastq.gz:md5,3ad9406595fafec8172368f9cd0b6a22", + "test_2.merged.fastq.gz:md5,a52cab0b840c7178b0ea83df1fdbe8d5" + ] + ] + ], + "1": [ + "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + ], + "reads": [ [ - "test_1.merged.fastq.gz:md5,f9cf5e375f7de81a406144a2c70cc64d", - "test_2.merged.fastq.gz:md5,77c8e966e130d8c6b6ec9be52fcb2bda" + { + "id": "test", + "single_end": false + }, + [ + "test_1.merged.fastq.gz:md5,3ad9406595fafec8172368f9cd0b6a22", + "test_2.merged.fastq.gz:md5,a52cab0b840c7178b0ea83df1fdbe8d5" + ] ] + ], + "versions": [ + "versions.yml:md5,d42d6e24d67004608495883e00bd501b" ] - ] + } ], - "timestamp": "2023-10-18T07:53:20.923560211" + "timestamp": "2024-01-17T17:32:02.270935" } } \ No newline at end of file From db874fb1c6196b5cb99b766798538f98df711128 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 19 Jan 2024 17:30:17 +0100 Subject: [PATCH 143/260] fix some typos --- assets/nf-params.yml | 6 +++--- nextflow_schema.json | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/assets/nf-params.yml b/assets/nf-params.yml index 51a7ab6b..4816169f 100644 --- a/assets/nf-params.yml +++ b/assets/nf-params.yml @@ -1,5 +1,5 @@ ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## nf-core/pixelator 1.1.0dev +## nf-core/pixelator 1.0.3 ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## This is an example parameter file to pass to the `-params-file` option ## of nextflow run with the nf-core/pixelator pipeline. @@ -171,7 +171,7 @@ ## ----------------------------------------------------------------------------- ## algorithm ## ----------------------------------------------------------------------------- -## The algorithm to use for collapsing (adjacency will peform error +## The algorithm to use for collapsing (adjacency will perform error ## correction using the number of mismatches given) ## Type: string ## ----------------------------------------------------------------------------- @@ -345,7 +345,7 @@ ## ----------------------------------------------------------------------------- ## colocalization_min_region_count ## ----------------------------------------------------------------------------- -## The minimum number of counts in a region for it to be concidered valid +## The minimum number of counts in a region for it to be considered valid ## for computing colocalization ## Type: integer ## ----------------------------------------------------------------------------- diff --git a/nextflow_schema.json b/nextflow_schema.json index 60ac0bc3..a67f3604 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -145,7 +145,7 @@ }, "algorithm": { "fa_icon": "fas code-fork", - "description": "The algorithm to use for collapsing (adjacency will peform error correction using the number of mismatches given)", + "description": "The algorithm to use for collapsing (adjacency will perform error correction using the number of mismatches given)", "default": "adjacency", "enum": ["adjacency", "unique"], "type": "string" @@ -288,7 +288,7 @@ }, "colocalization_min_region_count": { "type": "integer", - "description": "The minimum number of counts in a region for it to be concidered valid for computing colocalization", + "description": "The minimum number of counts in a region for it to be considered valid for computing colocalization", "default": 5, "minimum": 0 } @@ -312,7 +312,7 @@ "pixelator_container": { "type": "string", "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." + "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 guaranteed to work when using different pixelator versions." } } }, @@ -480,7 +480,7 @@ "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." + "help_text": "By default, when an unrecognised parameter is found, it returns a warning." }, "validationLenientMode": { "type": "boolean", From 7c9eaa3e3001ddaef6fe30cc09c9c5d33e33a586 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 29 Feb 2024 22:03:44 +0100 Subject: [PATCH 144/260] Complete refactoring to 2.13.1 template --- assets/schema_input.json | 4 - main.nf | 17 +- modules.json | 4 +- modules/local/pixelator/collect_metadata.nf | 12 +- modules/nf-core/cat/fastq/environment.yml | 2 +- modules/nf-core/cat/fastq/main.nf | 1 - .../dumpsoftwareversions/environment.yml | 2 +- .../custom/dumpsoftwareversions/main.nf | 4 +- .../custom/dumpsoftwareversions/meta.yml | 37 +++ .../templates/dumpsoftwareversions.py | 101 +++++++++ .../dumpsoftwareversions/tests/main.nf.test | 43 ++++ .../tests/main.nf.test.snap | 33 +++ .../dumpsoftwareversions/tests/tags.yml | 2 + nextflow.config | 3 +- nextflow_schema.json | 8 - samplesheet.csv | 3 + subworkflows/local/generate_reports.nf | 43 +++- subworkflows/local/input_check.nf | 190 ---------------- .../utils_nfcore_pixelator_pipeline/main.nf | 213 ++++++++++++++++-- workflows/pixelator.nf | 62 ++--- 20 files changed, 498 insertions(+), 286 deletions(-) create mode 100644 modules/nf-core/custom/dumpsoftwareversions/meta.yml create mode 100755 modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py create mode 100644 modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test create mode 100644 modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap create mode 100644 modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml create mode 100644 samplesheet.csv delete mode 100644 subworkflows/local/input_check.nf diff --git a/assets/schema_input.json b/assets/schema_input.json index c63ce7b2..463396c3 100644 --- a/assets/schema_input.json +++ b/assets/schema_input.json @@ -39,15 +39,11 @@ }, "fastq_1": { "type": "string", - "format": "file-path", - "exists": true, "pattern": "^\\S+\\.f(ast)?q\\.gz$", "errorMessage": "FastQ file for reads 1 must be provided, cannot contain spaces and must have extension '.fq.gz' or '.fastq.gz'" }, "fastq_2": { "type": "string", - "format": "file-path", - "exists": true, "pattern": "^\\S+\\.f(ast)?q\\.gz$", "errorMessage": "FastQ file for reads 2 cannot contain spaces and must have extension '.fq.gz' or '.fastq.gz'" } diff --git a/main.nf b/main.nf index dda7d284..b8a63871 100644 --- a/main.nf +++ b/main.nf @@ -21,15 +21,6 @@ include { PIXELATOR } from './workflows/pixelator' include { PIPELINE_INITIALISATION } from './subworkflows/local/utils_nfcore_pixelator_pipeline' include { PIPELINE_COMPLETION } from './subworkflows/local/utils_nfcore_pixelator_pipeline' -include { getGenomeAttribute } from './subworkflows/local/utils_nfcore_pixelator_pipeline' - -/* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - GENOME PARAMETER VALUES -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -*/ - - /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ NAMED WORKFLOWS FOR PIPELINE @@ -53,9 +44,6 @@ workflow NFCORE_PIXELATOR { samplesheet ) - emit: - multiqc_report = PIXELATOR.out.multiqc_report // channel: /path/to/multiqc_report.html - } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -77,7 +65,8 @@ workflow { params.monochrome_logs, args, params.outdir, - params.input + params.input, + params.input_basedir ) // @@ -97,7 +86,7 @@ workflow { params.outdir, params.monochrome_logs, params.hook_url, - NFCORE_PIXELATOR.out.multiqc_report + [] ) } diff --git a/modules.json b/modules.json index bf42ce1b..040af087 100644 --- a/modules.json +++ b/modules.json @@ -7,12 +7,12 @@ "nf-core": { "cat/fastq": { "branch": "master", - "git_sha": "02fd5bd7275abad27aad32d5c852e0a9b1b98882", + "git_sha": "0997b47c93c06b49aa7b3fefda87e728312cf2ca", "installed_by": ["modules"] }, "custom/dumpsoftwareversions": { "branch": "master", - "git_sha": "8ec825f465b9c17f9d83000022995b4f7de6fe93", + "git_sha": "de45447d060b8c8b98575bc637a4a575fd0638e1", "installed_by": ["modules"] } } diff --git a/modules/local/pixelator/collect_metadata.nf b/modules/local/pixelator/collect_metadata.nf index 10601d75..469e3651 100644 --- a/modules/local/pixelator/collect_metadata.nf +++ b/modules/local/pixelator/collect_metadata.nf @@ -1,8 +1,6 @@ -import org.json.JSONObject -import org.json.JSONTokener -import org.json.JSONArray -import groovy.json.JsonSlurper -import groovy.json.JsonBuilder +import groovy.json.JsonOutput + + process PIXELATOR_COLLECT_METADATA { label 'process_single' @@ -72,12 +70,10 @@ process PIXELATOR_COLLECT_METADATA { parameters: params ] - def builder = new JsonBuilder(metadata) - def nextflowJson = builder.toPrettyString() + def nextflowJson = JsonOutput.toJson(metadata) """ echo '${nextflowJson}' > nextflow-metadata.json collect_metadata.py --process-name ${task.process} --workflow-data "nextflow-metadata.json" """ - } diff --git a/modules/nf-core/cat/fastq/environment.yml b/modules/nf-core/cat/fastq/environment.yml index f7d8933b..8c69b121 100644 --- a/modules/nf-core/cat/fastq/environment.yml +++ b/modules/nf-core/cat/fastq/environment.yml @@ -4,4 +4,4 @@ channels: - bioconda - defaults dependencies: - - bioconda::multiqc=1.21 + - conda-forge::coreutils=8.30 diff --git a/modules/nf-core/cat/fastq/main.nf b/modules/nf-core/cat/fastq/main.nf index 3d963784..f132b2ad 100644 --- a/modules/nf-core/cat/fastq/main.nf +++ b/modules/nf-core/cat/fastq/main.nf @@ -76,5 +76,4 @@ process CAT_FASTQ { """ } } - } diff --git a/modules/nf-core/custom/dumpsoftwareversions/environment.yml b/modules/nf-core/custom/dumpsoftwareversions/environment.yml index 9b3272bc..b48ced26 100644 --- a/modules/nf-core/custom/dumpsoftwareversions/environment.yml +++ b/modules/nf-core/custom/dumpsoftwareversions/environment.yml @@ -4,4 +4,4 @@ channels: - bioconda - defaults dependencies: - - bioconda::multiqc=1.19 + - bioconda::multiqc=1.20 diff --git a/modules/nf-core/custom/dumpsoftwareversions/main.nf b/modules/nf-core/custom/dumpsoftwareversions/main.nf index f2187611..105f9265 100644 --- a/modules/nf-core/custom/dumpsoftwareversions/main.nf +++ b/modules/nf-core/custom/dumpsoftwareversions/main.nf @@ -4,8 +4,8 @@ process CUSTOM_DUMPSOFTWAREVERSIONS { // Requires `pyyaml` which does not have a dedicated container but is in the MultiQC container conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/multiqc:1.19--pyhdfd78af_0' : - 'biocontainers/multiqc:1.19--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/multiqc:1.20--pyhdfd78af_0' : + 'biocontainers/multiqc:1.20--pyhdfd78af_0' }" input: path versions diff --git a/modules/nf-core/custom/dumpsoftwareversions/meta.yml b/modules/nf-core/custom/dumpsoftwareversions/meta.yml new file mode 100644 index 00000000..5f15a5fd --- /dev/null +++ b/modules/nf-core/custom/dumpsoftwareversions/meta.yml @@ -0,0 +1,37 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/meta-schema.json +name: custom_dumpsoftwareversions +description: Custom module used to dump software versions within the nf-core pipeline template +keywords: + - custom + - dump + - version +tools: + - custom: + description: Custom module used to dump software versions within the nf-core pipeline template + homepage: https://github.com/nf-core/tools + documentation: https://github.com/nf-core/tools + licence: ["MIT"] +input: + - versions: + type: file + description: YML file containing software versions + pattern: "*.yml" +output: + - yml: + type: file + description: Standard YML file containing software versions + pattern: "software_versions.yml" + - mqc_yml: + type: file + description: MultiQC custom content YML file containing software versions + pattern: "software_versions_mqc.yml" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" +authors: + - "@drpatelh" + - "@grst" +maintainers: + - "@drpatelh" + - "@grst" diff --git a/modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py b/modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py new file mode 100755 index 00000000..da033408 --- /dev/null +++ b/modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python + + +"""Provide functions to merge multiple versions.yml files.""" + + +import yaml +import platform +from textwrap import dedent + + +def _make_versions_html(versions): + """Generate a tabular HTML output of all versions for MultiQC.""" + html = [ + dedent( + """\\ + +
Process Name \\", + " \\ Software Version
CUSTOM_DUMPSOFTWAREVERSIONSpython3.11.7
yaml5.4.1
TOOL1tool10.11.9
TOOL2tool21.9
WorkflowNextflow
File typeConventional base calls
+ + + + + + + + """ + ) + ] + for process, tmp_versions in sorted(versions.items()): + html.append("") + for i, (tool, version) in enumerate(sorted(tmp_versions.items())): + html.append( + dedent( + f"""\\ + + + + + + """ + ) + ) + html.append("") + html.append("
Process Name Software Version
{process if (i == 0) else ''}{tool}{version}
") + return "\\n".join(html) + + +def main(): + """Load all version files and generate merged output.""" + versions_this_module = {} + versions_this_module["${task.process}"] = { + "python": platform.python_version(), + "yaml": yaml.__version__, + } + + with open("$versions") as f: + versions_by_process = yaml.load(f, Loader=yaml.BaseLoader) | versions_this_module + + # aggregate versions by the module name (derived from fully-qualified process name) + versions_by_module = {} + for process, process_versions in versions_by_process.items(): + module = process.split(":")[-1] + try: + if versions_by_module[module] != process_versions: + raise AssertionError( + "We assume that software versions are the same between all modules. " + "If you see this error-message it means you discovered an edge-case " + "and should open an issue in nf-core/tools. " + ) + except KeyError: + versions_by_module[module] = process_versions + + versions_by_module["Workflow"] = { + "Nextflow": "$workflow.nextflow.version", + "$workflow.manifest.name": "$workflow.manifest.version", + } + + versions_mqc = { + "id": "software_versions", + "section_name": "${workflow.manifest.name} Software Versions", + "section_href": "https://github.com/${workflow.manifest.name}", + "plot_type": "html", + "description": "are collected at run time from the software output.", + "data": _make_versions_html(versions_by_module), + } + + with open("software_versions.yml", "w") as f: + yaml.dump(versions_by_module, f, default_flow_style=False) + with open("software_versions_mqc.yml", "w") as f: + yaml.dump(versions_mqc, f, default_flow_style=False) + + with open("versions.yml", "w") as f: + yaml.dump(versions_this_module, f, default_flow_style=False) + + +if __name__ == "__main__": + main() diff --git a/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test new file mode 100644 index 00000000..b1e1630b --- /dev/null +++ b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test @@ -0,0 +1,43 @@ +nextflow_process { + + name "Test Process CUSTOM_DUMPSOFTWAREVERSIONS" + script "../main.nf" + process "CUSTOM_DUMPSOFTWAREVERSIONS" + tag "modules" + tag "modules_nfcore" + tag "custom" + tag "dumpsoftwareversions" + tag "custom/dumpsoftwareversions" + + test("Should run without failures") { + when { + process { + """ + def tool1_version = ''' + TOOL1: + tool1: 0.11.9 + '''.stripIndent() + + def tool2_version = ''' + TOOL2: + tool2: 1.9 + '''.stripIndent() + + input[0] = Channel.of(tool1_version, tool2_version).collectFile() + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + process.out.versions, + file(process.out.mqc_yml[0]).readLines()[0..10], + file(process.out.yml[0]).readLines()[0..7] + ).match() + } + ) + } + } +} diff --git a/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap new file mode 100644 index 00000000..5f59a936 --- /dev/null +++ b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap @@ -0,0 +1,33 @@ +{ + "Should run without failures": { + "content": [ + [ + "versions.yml:md5,76d454d92244589d32455833f7c1ba6d" + ], + [ + "data: \"\\n\\n \\n \\n \\n \\n \\n \\n \\n\\", + " \\n\\n\\n \\n \\n\\", + " \\ \\n\\n\\n\\n \\n \\", + " \\ \\n \\n\\n\\n\\n\\", + " \\n\\n \\n \\n\\", + " \\ \\n\\n\\n\\n\\n\\n \\n\\", + " \\ \\n \\n\\n\\n\\n\\", + " \\n\\n \\n \\n\\" + ], + [ + "CUSTOM_DUMPSOFTWAREVERSIONS:", + " python: 3.11.7", + " yaml: 5.4.1", + "TOOL1:", + " tool1: 0.11.9", + "TOOL2:", + " tool2: '1.9'", + "Workflow:" + ] + ], + "timestamp": "2024-01-09T23:01:18.710682" + } +} \ No newline at end of file diff --git a/modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml b/modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml new file mode 100644 index 00000000..405aa24a --- /dev/null +++ b/modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml @@ -0,0 +1,2 @@ +custom/dumpsoftwareversions: + - modules/nf-core/custom/dumpsoftwareversions/** diff --git a/nextflow.config b/nextflow.config index 81c22747..aee3d7bc 100644 --- a/nextflow.config +++ b/nextflow.config @@ -70,7 +70,6 @@ params { // Boilerplate options outdir = null - tracedir = "${params.outdir}/pipeline_info" publish_dir_mode = 'copy' email = null email_on_fail = null @@ -272,7 +271,7 @@ manifest { description = """Pipeline for analysis of Molecular Pixelation assays""" mainScript = 'main.nf' nextflowVersion = '!>=23.04.0' - version = '1.0.3' + version = '1.1.0dev' doi = '10.1101/2023.06.05.543770' } diff --git a/nextflow_schema.json b/nextflow_schema.json index 51319b9e..1659e2d6 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -33,7 +33,6 @@ "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" }, @@ -455,13 +454,6 @@ "help_text": "Incoming hook URL for messaging service. Currently, MS Teams and Slack are supported.", "hidden": true }, - "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", diff --git a/samplesheet.csv b/samplesheet.csv new file mode 100644 index 00000000..da37578e --- /dev/null +++ b/samplesheet.csv @@ -0,0 +1,3 @@ +sample,design,panel,panel_file,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 diff --git a/subworkflows/local/generate_reports.nf b/subworkflows/local/generate_reports.nf index f6a484d9..dc5fb42c 100644 --- a/subworkflows/local/generate_reports.nf +++ b/subworkflows/local/generate_reports.nf @@ -1,17 +1,29 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + IMPORT MODULES / SUBWORKFLOWS / FUNCTIONS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + include { PIXELATOR_REPORT } from '../../modules/local/pixelator/single-cell/report/main' +/* +======================================================================================== + SUBWORKFLOW TO GENERATE PIXELATOR REPORTS +======================================================================================== +*/ + workflow GENERATE_REPORTS { take: - panel_files // [meta, panel_file] or [meta, []] - 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]] + panel_files // channel: [meta, path(panel_file) | []] + amplicon_data // channel: [meta, [path, ...]] + preqc_data // channel: [meta, [path, ...]] + adapterqc_data // channel: [meta, [path, ...]] + demux_data // channel: [meta, [path, ...]] + collapse_data // channel: [meta, [path, ...]] + graph_data // channel: [meta, [path, ...]] + annotate_data // channel: [meta, [path, ...]] + analysis_data // channel: [meta, [path, ...]] main: ch_versions = Channel.empty() @@ -33,9 +45,10 @@ workflow GENERATE_REPORTS { ch_panel_col = panel_files .map { meta, data -> [ meta.id, data] } + // // 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] } @@ -46,7 +59,8 @@ workflow GENERATE_REPORTS { 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 + // Combine all inputs and group them, then split them up again. + // This is neded to have the per subcommand outputs in the sample order // // ch_report_data: [ // [ @@ -75,6 +89,7 @@ workflow GENERATE_REPORTS { .concat ( ch_analysis_col ) .groupTuple (size: 10) + // // Split up everything per stage so we can recreate the expected directory structure for // `pixelator single-cell report` using stageAs for each stage // @@ -83,6 +98,7 @@ workflow GENERATE_REPORTS { // channel will match the same sample from the samplesheet. // If no `panel_file` (data[1]) is given we need to pass in `panel` from the samplesheet instead + // ch_panel_files_grouped = ch_report_data.map { id, data -> [ data[0], data[1], data[1] ? null : data[0].panel ] } 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() : [] } @@ -93,6 +109,11 @@ 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() : [] } + // + // MODULE: Run pixelator single-cell report for each samples + // + // NB: These channels need to be split per stage to allow PIXELATOR_REPORT to + // use stageAs directives to reorder the inputs and prevent filename collisions PIXELATOR_REPORT ( ch_panel_files_grouped, ch_amplicon_grouped, diff --git a/subworkflows/local/input_check.nf b/subworkflows/local/input_check.nf deleted file mode 100644 index 3af1adde..00000000 --- a/subworkflows/local/input_check.nf +++ /dev/null @@ -1,190 +0,0 @@ -// -// Check input samplesheet and get read channels -// - -include { fromSamplesheet } from 'plugin/nf-validation' -include { SAMPLESHEET_CHECK } from '../../modules/local/samplesheet_check' -include { PIXELATOR_LIST_OPTIONS } from '../../modules/local/pixelator/list_options.nf' - -workflow INPUT_CHECK { - take: - samplesheet // file: /path/to/samplesheet.csv - input_basedir // string | null - - main: - - // Create a new channel of metadata from a sample sheet - // NB: `input` corresponds to `params.input` and associated sample sheet schema - def inputBaseDir = get_data_basedir(samplesheet.toUri(), input_basedir) - - log.info "Resolving relative paths in samplesheet relative to: ${inputBaseDir}" - - ch_input = Channel.fromSamplesheet("input") - .map { check_channels(inputBaseDir, *it) } - - PIXELATOR_LIST_OPTIONS() - - // Create a set of valid pixelator options to pass to --design - ch_design_options = PIXELATOR_LIST_OPTIONS.out.designs - .splitText() - .map( text -> text.trim()) - .reduce( new HashSet() ) { prev, curr -> prev << curr } - - // Create a set of valid pixelator panel keys to pass using --panel - ch_panel_options = PIXELATOR_LIST_OPTIONS.out.panels - .splitText() - .map( text -> text.trim()) - .reduce( new HashSet() ) { prev, curr -> prev << curr } - - ch_checked_input = ch_input - .map { it -> it[0] } - .combine(ch_panel_options) - .combine(ch_design_options) - .map { - meta, panel_options, design_options -> - validate_panel(meta, panel_options) - validate_design(meta, design_options) - return [meta, []] - } - // Combine a dummy output after validation with the main input and strip the dummy value again - // This adds a dependency to make sure all jobs wait untill the validation is complete - .join(ch_input) - .map { it -> [ it[0] ] + it[2..-1] } - - reads = ch_checked_input.map { it -> [it[0]] + it[2..-1] } - panels = ch_checked_input.map { it -> [it[0], it[1]] } - - emit: - reads // channel: [ val(meta), [ reads ] ] - panels // channel: [ val(meta), panel ] - - versions = PIXELATOR_LIST_OPTIONS.out.versions // channel: [ versions.yml ] -} - - -// Resolve relative paths relative to the samplesheet parent directory. -def resolve_relative_path(relative_path, URI samplesheet_path) { - if (!(relative_path instanceof String)) { - return relative_path - } - - // Try to create a java.net.UR object out of it. If it is not a proper URL, a MalformedURLException will be t - URI uri; - - try { - uri = new URI(relative_path) - } catch (URISyntaxException exc) { - return relative_path - } - - // If a scheme is given we keep it as given - if (uri.getScheme() != null) { - return relative_path - } - - def path = new File(relative_path) - if (path.isAbsolute()) { - return relative_path - } - - // Resolve relative paths agains the samplesheet_path - def resolvedPath = samplesheet_path.resolve(relative_path); - - def stringPath = resolvedPath.toString() - return stringPath -} - - -// Validate a given panel key if present against the (dynamic) set of panel options retrieved from pixelator -def validate_panel(LinkedHashMap meta, HashSet options) { - if (meta.panel == null) { - return - } - - if (!options.contains(meta.panel)) { - exit 1, "ERROR: Please check input samplesheet -> panel field does not contains a valid key!\n${meta.panel}\nValid options:\n${options}" - } -} - - -// Validate a given design key if present against the (dynamic) set of design options retrieved from pixelator -def validate_design(LinkedHashMap meta, HashSet options) { - if (meta.design == null) { - return - } - - if (!options.contains(meta.design)) { - exit 1, "ERROR: Please check input samplesheet -> design field does contains a valid key!\n${meta.design}\nValid options:\n${options}" - } -} - -// Determine the path/url that will be used as the root for relative paths in the samplesheet -def get_data_basedir(URI samplesheet, String input_basedir) { - - URI uri; - - // nothing given to --input_data so we use the samplesheet as root directory - // for resolving relative paths - if (!input_basedir) { - return samplesheet - } - - try { - uri = new URI(input_basedir) - } catch (URISyntaxException exc) { - return samplesheet - } - - // If a scheme is given we keep check that it is a directory (trailing-slash) - if (uri.getScheme() != null) { - if (!uri.path.endsWith('/')) { - def newUrl = new URI( - uri.getScheme(), uri.getUserInfo(), uri.getHost(), - uri.getPort(), uri.getPath() + '/', uri.getQuery(), uri.getFragment() - ) - return newUrl - } - return uri - } - - f = file(input_basedir) - if (!f.exists()) { - exit 1, "ERROR: data path passed with --input_basedir does not exist!" - } - if (f.isDirectory()) { - data_root = new URI(f.toString() + '/') - } else { - data_root = new URI(f.toString()) - } - - return data_root -} - -// Resolve relative paths and check that all files exist. -def check_channels(URI samplesheetUrl, Map meta, panel_file, ...fq) { - def paired_end = fq.size() == 2 - def panel_file_abs = resolve_relative_path(panel_file, samplesheetUrl) - def fq1_abs = resolve_relative_path(fq[0], samplesheetUrl) - - if (panel_file_abs && !file(panel_file_abs).exists()) { - exit 1, "ERROR: Please check input samplesheet -> panel_file does not exist!\n${panel_file_abs}" - } - - if (!file(fq1_abs).exists()) { - exit 1, "ERROR: Please check input samplesheet -> fastq_1 does not exist!\n${fq1_abs}" - } - - def reads = [ fq1_abs ] - - if (paired_end) { - def fq2_abs = resolve_relative_path(fq[1], samplesheetUrl) - - if (fq2_abs && !file(fq2_abs).exists()) { - exit 1, "ERROR: Please check input samplesheet -> fastq_2 does not exist!\n${fq2_abs}" - } - - reads += [ fq2_abs] - } - - return [ meta, panel_file_abs, reads ] -} diff --git a/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf b/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf index f11ecddf..58fa645a 100644 --- a/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf @@ -20,6 +20,8 @@ include { imNotification } from '../../nf-core/utils_nfcore_pipeline' include { UTILS_NFCORE_PIPELINE } from '../../nf-core/utils_nfcore_pipeline' include { workflowCitation } from '../../nf-core/utils_nfcore_pipeline' +include { PIXELATOR_LIST_OPTIONS } from '../../../modules/local/pixelator/list_options.nf' + /* ======================================================================================== SUBWORKFLOW TO INITIALISE PIPELINE @@ -36,6 +38,7 @@ workflow PIPELINE_INITIALISATION { nextflow_cli_args // array: List of positional nextflow CLI args outdir // string: The output directory where the results will be saved input // string: Path to input samplesheet + input_basedir // string: Path to the base directory for relative paths resolving main: @@ -80,25 +83,57 @@ workflow PIPELINE_INITIALISATION { // // Create channel from input file provided through params.input // - Channel - .fromSamplesheet("input") - .map { - meta, fastq_1, fastq_2 -> - if (!fastq_2) { - return [ meta.id, meta + [ single_end:true ], [ fastq_1 ] ] - } else { - return [ meta.id, meta + [ single_end:false ], [ fastq_1, fastq_2 ] ] - } - } - .groupTuple() + ch_versions = Channel.empty() + + // + // Resolve relative paths and validate fastq files existence + // + def samplesheet_uri = file(input).toUri() + def inputBaseDir = get_data_basedir(samplesheet_uri, input_basedir) + + log.info "Resolving relative paths in samplesheet relative to: ${inputBaseDir}" + + ch_input = Channel.fromSamplesheet("input") .map { - validateInputSamplesheet(it) + validate_input_samplesheet(inputBaseDir, it) } + + + // + // Validate design and panel samplesheet fields agains a dynamic set of allowed values + // + PIXELATOR_LIST_OPTIONS() + ch_versions = ch_versions.mix(PIXELATOR_LIST_OPTIONS.out.versions) + + // Create a set of valid pixelator options to pass to --design + ch_design_options = PIXELATOR_LIST_OPTIONS.out.designs + .splitText() + .map( text -> text.trim()) + .reduce( new HashSet() ) { prev, curr -> prev << curr } + + // Create a set of valid pixelator panel keys to pass using --panel + ch_panel_options = PIXELATOR_LIST_OPTIONS.out.panels + .splitText() + .map( text -> text.trim()) + .reduce( new HashSet() ) { prev, curr -> prev << curr } + + + + // + // Combine the validated inputs again in a single channel + // + ch_samplesheet = ch_input + .map { it -> it[0] } + .combine(ch_panel_options) + .combine(ch_design_options) .map { - meta, fastqs -> - return [ meta, fastqs.flatten() ] + meta, panel_options, design_options -> { + meta = validate_panel(meta, panel_options) + meta = validate_design(meta, design_options) + return meta + } } - .set { ch_samplesheet } + .join(ch_input) emit: samplesheet = ch_samplesheet @@ -249,3 +284,151 @@ def methodsDescriptionText(mqc_methods_yaml) { return description_html.toString() } + + + +// +// Resolve relative paths relative to the samplesheet parent directory. +// +def resolve_relative_path(relative_path, URI samplesheet_path) { + if (!(relative_path instanceof String)) { + return relative_path + } + + // Try to create a java.net.UR object out of it. If it is not a proper URL, a MalformedURLException will be t + URI uri; + + try { + uri = new URI(relative_path) + } catch (URISyntaxException exc) { + return relative_path + } + + // If a scheme is given we keep it as given + if (uri.getScheme() != null) { + return relative_path + } + + def path = new File(relative_path) + if (path.isAbsolute()) { + return relative_path + } + + // Resolve relative paths agains the samplesheet_path + def resolvedPath = samplesheet_path.resolve(relative_path); + + def stringPath = resolvedPath.toString() + return stringPath +} + +// +// Validate a given panel key if present against the (dynamic) set of panel options retrieved from pixelator +// +def validate_panel(LinkedHashMap meta, HashSet options) { + if (meta.panel == null) { + return + } + + if (!options.contains(meta.panel)) { + options_list_str = " - ${options.join("\n - ")}" + exit 1, "Please check input samplesheet -> panel field does not contains a valid key!\n\nInput: ${meta.panel}\nValid options:\n${options_list_str}" + } + + return meta +} + + +// +// Validate a given design key if present against the (dynamic) set of design options retrieved from pixelator +// +def validate_design(LinkedHashMap meta, HashSet options) { + if (meta.design == null) { + return + } + + if (!options.contains(meta.design)) { + options_list_str = " - ${options.join("\n - ")}" + exit 1, "Please check input samplesheet -> design field does not contains a valid key!\n\nInput: ${meta.design}\nValid options:\n${options_list_str}" + } + + return meta +} + +// +// Determine the path/url that will be used as the root for relative paths in the samplesheet +// +def get_data_basedir(URI samplesheet, String input_basedir) { + + URI uri; + + // nothing given to --input_data so we use the samplesheet as root directory + // for resolving relative paths + if (!input_basedir) { + return samplesheet + } + + try { + uri = new URI(input_basedir) + } catch (URISyntaxException exc) { + return samplesheet + } + + // If a scheme is given we keep check that it is a directory (trailing-slash) + if (uri.getScheme() != null) { + if (!uri.path.endsWith('/')) { + def newUrl = new URI( + uri.getScheme(), uri.getUserInfo(), uri.getHost(), + uri.getPort(), uri.getPath() + '/', uri.getQuery(), uri.getFragment() + ) + return newUrl + } + return uri + } + + f = file(input_basedir) + if (!f.exists()) { + exit 1, "ERROR: data path passed with --input_basedir does not exist!" + } + if (f.isDirectory()) { + data_root = new URI(f.toString() + '/') + } else { + data_root = new URI(f.toString()) + } + + return data_root +} + +// +// Resolve relative paths and check that all files exist. +// +def validate_input_samplesheet(URI samplesheetUrl, items) { + def meta = items[0] + def panel_file = items[1] + def fq = items[2..-1] + + def paired_end = fq.size() == 2 + def panel_file_abs = resolve_relative_path(panel_file, samplesheetUrl) + def fq1_abs = resolve_relative_path(fq[0], samplesheetUrl) + + if (panel_file_abs && !file(panel_file_abs).exists()) { + exit 1, "ERROR: Please check input samplesheet -> panel_file does not exist!\n${panel_file_abs}" + } + + if (!file(fq1_abs).exists()) { + exit 1, "ERROR: Please check input samplesheet -> fastq_1 does not exist!\n${fq1_abs}" + } + + def reads = [ fq1_abs ] + + if (paired_end) { + def fq2_abs = resolve_relative_path(fq[1], samplesheetUrl) + + if (fq2_abs && !file(fq2_abs).exists()) { + exit 1, "ERROR: Please check input samplesheet -> fastq_2 does not exist!\n${fq2_abs}" + } + + reads += [ fq2_abs] + } + + return [ meta, panel_file_abs, reads ] +} diff --git a/workflows/pixelator.nf b/workflows/pixelator.nf index b225c244..ec8735f3 100644 --- a/workflows/pixelator.nf +++ b/workflows/pixelator.nf @@ -8,16 +8,8 @@ include { paramsSummaryMap } from 'plugin/nf-validation' include { paramsSummaryMultiqc } from '../subworkflows/nf-core/utils_nfcore_pipeline' include { softwareVersionsToYAML } from '../subworkflows/nf-core/utils_nfcore_pipeline' include { methodsDescriptionText } from '../subworkflows/local/utils_nfcore_pixelator_pipeline' -include { paramsSummaryLog; paramsSummaryMap } from 'plugin/nf-validation' -def logo = NfcoreTemplate.logo(workflow, params.monochrome_logs) -def citation = '\n' + WorkflowMain.citation(workflow) + '\n' -def summary_params = paramsSummaryMap(workflow) - -// Print parameter summary log to screen -log.info logo + paramsSummaryLog(workflow) + citation - // Inject the samplesheet SHA-1 into the params object ch_input = file(params.input) params.samplesheet_sha = ch_input.bytes.digest('sha-1') @@ -37,7 +29,6 @@ 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' /* @@ -73,25 +64,22 @@ include { PIXELATOR_ANNOTATE } from '../modules/local/pixelator/singl /* ======================================================================================== RUN MAIN WORKFLOW -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +======================================================================================== */ workflow PIXELATOR { take: - ch_samplesheet // channel: samplesheet read in from --input + ch_samplesheet // channel: [ meta, path(panel_file | []), path(sample_1.fq), path(sample_2.fq) ] main: ch_versions = Channel.empty() + // - // SUBWORKFLOW: Read in samplesheet, validate and stage input files + // Split the samplesheet channel in reads and panel_files // - // Create a new channel of metadata from a sample sheet - // NB: `input` corresponds to `params.input` and associated sample sheet schema - INPUT_CHECK ( ch_input, params.input_basedir ) - - ch_reads = INPUT_CHECK.out.reads - ch_panel_files = INPUT_CHECK.out.panels + ch_reads = ch_samplesheet.map { [it[0]] + it[2..-1] } + ch_panel_files = ch_samplesheet.map { [it[0], it[1]] } ch_fastq_split = ch_reads .groupTuple() @@ -103,11 +91,14 @@ workflow PIXELATOR { return [ meta, fastq.flatten() ] } + // + // MODULE: Dump pixelaor and pipeline information + // PIXELATOR_COLLECT_METADATA () ch_versions = ch_versions.mix(PIXELATOR_COLLECT_METADATA.out.versions) // - // MODULE: Concatenate FastQ files from same sample if required + // MODULE: Concatenate FastQ files from the same sample if required // ch_cat_fastq = CAT_FASTQ ( ch_fastq_split.multiple ) .reads @@ -133,26 +124,28 @@ workflow PIXELATOR { .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)) + ch_versions = ch_versions.mix(CAT_FASTQ.out.versions.first()) - // 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 ) - ch_renamed_reads = RENAME_READS.out.reads - ch_versions = ch_versions.mix(RENAME_READS.out.versions.first()) - // We need to rename to make all reads match the sample name, - // since pixelator extracts sample_names from read names + // + // MODULE: Rename input reads to match the sample ids from the samplesheet + // RENAME_READS ( ch_cat_fastq ) ch_renamed_reads = RENAME_READS.out.reads ch_versions = ch_versions.mix(RENAME_READS.out.versions.first()) + // + // MODULE: Run pixelator single-cell amplicon + // 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 + // + // MODULE: Run pixelator single-cell preqc & pixelator single-cell adapterqc + // PIXELATOR_QC ( ch_input_reads ) ch_qc = PIXELATOR_QC.out.processed ch_versions = ch_versions.mix(PIXELATOR_QC.out.versions.first()) @@ -161,6 +154,9 @@ workflow PIXELATOR { .join(ch_cat_panel_files, failOnMismatch:true, failOnDuplicate:true) .map { meta, fq, panel_file -> [meta, fq, panel_file, panel_file ? null : meta.panel ] } + // + // MODULE: Run pixelator single-cell demux + // PIXELATOR_DEMUX ( ch_fq_and_panel ) ch_demuxed = PIXELATOR_DEMUX.out.processed ch_versions = ch_versions.mix(PIXELATOR_DEMUX.out.versions.first()) @@ -169,10 +165,16 @@ workflow PIXELATOR { .join(ch_cat_panel_files, failOnMismatch:true, failOnDuplicate:true) .map { meta, demuxed, panel_file -> [meta, demuxed, panel_file, panel_file ? null : meta.panel ] } + // + // MODULE: Run pixelator single-cell collapse + // PIXELATOR_COLLAPSE ( ch_demuxed_and_panel ) ch_collapsed = PIXELATOR_COLLAPSE.out.collapsed ch_versions = ch_versions.mix( PIXELATOR_COLLAPSE.out.versions.first()) + // + // MODULE: Run pixelator single-cell graph + // PIXELATOR_GRAPH ( ch_collapsed ) ch_clustered = PIXELATOR_GRAPH.out.edgelist ch_versions = ch_versions.mix(PIXELATOR_GRAPH.out.versions.first()) @@ -181,10 +183,16 @@ workflow PIXELATOR { .join(ch_cat_panel_files, failOnMismatch:true, failOnDuplicate:true) .map { meta, clustered, panel_file -> [meta, clustered, panel_file, panel_file ? null : meta.panel ] } + // + // MODULE: Run pixelator single-cell annotate + // PIXELATOR_ANNOTATE ( ch_clustered_and_panel ) ch_annotated = PIXELATOR_ANNOTATE.out.dataset ch_versions = ch_versions.mix( PIXELATOR_ANNOTATE.out.versions.first() ) + // + // MODULE: Run pixelator single-cell analysis + // PIXELATOR_ANALYSIS ( ch_annotated ) ch_analysed = PIXELATOR_ANALYSIS.out.dataset ch_versions = ch_versions.mix(PIXELATOR_ANALYSIS.out.versions.first()) From 834a7f25c0a453e4597400e508d1dcd9c7664a74 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 29 Feb 2024 22:18:38 +0100 Subject: [PATCH 145/260] Reformat and fix linting errors --- .pre-commit-config.yaml | 27 ++++++++++--------- assets/nf-params.yml | 8 +++--- bin/collect_metadata.py | 8 ++++-- .../templates/dumpsoftwareversions.py | 4 ++- 4 files changed, 29 insertions(+), 18 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bce64572..801e349a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,18 @@ +exclude: ^(.*.test.snap)|(assets/nf-params.yml)$ + repos: + - repo: local + hooks: + - id: nf-core/tools parameters.yaml + name: Update nf-params.yml file with schema + language: python + additional_dependencies: + - nf-core + entry: nf-core + args: [create-params-file, --output, assets/nf-params.yml, "--force", "."] + pass_filenames: false + files: ^nextflow_schema.json$ + - repo: https://github.com/pre-commit/mirrors-prettier rev: "v3.1.0" hooks: @@ -8,20 +22,9 @@ repos: hooks: - id: editorconfig-checker alias: ec + exclude: assets/nf-params.yml - repo: https://github.com/psf/black 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: - - nf-core - entry: nf-core - args: [create-params-file, --output, assets/nf-params.yml, "--force", "."] - pass_filenames: false - files: ^nextflow_schema.json$ diff --git a/assets/nf-params.yml b/assets/nf-params.yml index 4816169f..5e6c9928 100644 --- a/assets/nf-params.yml +++ b/assets/nf-params.yml @@ -1,5 +1,5 @@ ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## nf-core/pixelator 1.0.3 +## nf-core/pixelator 1.1.0dev ## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ## This is an example parameter file to pass to the `-params-file` option ## of nextflow run with the nf-core/pixelator pipeline. @@ -19,6 +19,7 @@ ## Path to comma-separated file containing information about the samples ## in the experiment. ## Type: string +## Required ## ----------------------------------------------------------------------------- # input = null @@ -37,8 +38,9 @@ ## The output directory where the results will be saved. You have to use ## absolute paths to storage on Cloud infrastructure. ## Type: string +## Required ## ----------------------------------------------------------------------------- -# outdir = "./results" +# outdir = null ## ----------------------------------------------------------------------------- ## email @@ -404,7 +406,7 @@ ## ============================================================================= ## Less common options for the pipeline, typically set in a config file. -## (12 hidden parameters are not shown) +## (11 hidden parameters are not shown) diff --git a/bin/collect_metadata.py b/bin/collect_metadata.py index 67df83b7..8eef3d74 100755 --- a/bin/collect_metadata.py +++ b/bin/collect_metadata.py @@ -20,7 +20,9 @@ def subtool_versions(): - cutadapt_proc = subprocess.run(["cutadapt", "--version"], capture_output=True, text=True) + 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") @@ -73,7 +75,9 @@ def main(args): 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) + parser.add_argument( + "--workflow-data", dest="workflow_data", type=Path, default=None + ) args = parser.parse_args() main(args) diff --git a/modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py b/modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py index da033408..4a993608 100755 --- a/modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py +++ b/modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py @@ -58,7 +58,9 @@ def main(): } with open("$versions") as f: - versions_by_process = yaml.load(f, Loader=yaml.BaseLoader) | versions_this_module + versions_by_process = ( + yaml.load(f, Loader=yaml.BaseLoader) | versions_this_module + ) # aggregate versions by the module name (derived from fully-qualified process name) versions_by_module = {} From cb4e13279d3ad57b1bdc57d3873d7e6bc8020ee8 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 1 Mar 2024 10:26:10 +0100 Subject: [PATCH 146/260] Remove auto generated nf-params.yml file --- .pre-commit-config.yaml | 18 +- .prettierignore | 1 - assets/nf-params.yml | 412 ---------------------------------------- 3 files changed, 3 insertions(+), 428 deletions(-) delete mode 100644 assets/nf-params.yml diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 801e349a..c277a296 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,30 +1,18 @@ -exclude: ^(.*.test.snap)|(assets/nf-params.yml)$ +exclude: ^(.*.test.snap)$ repos: - - repo: local - hooks: - - id: nf-core/tools parameters.yaml - name: Update nf-params.yml file with schema - language: python - additional_dependencies: - - nf-core - entry: nf-core - args: [create-params-file, --output, assets/nf-params.yml, "--force", "."] - pass_filenames: false - files: ^nextflow_schema.json$ - - repo: https://github.com/pre-commit/mirrors-prettier rev: "v3.1.0" hooks: - id: prettier + - repo: https://github.com/editorconfig-checker/editorconfig-checker.python rev: "2.7.3" hooks: - id: editorconfig-checker alias: ec - exclude: assets/nf-params.yml - repo: https://github.com/psf/black - rev: 23.3.0 + rev: 24.2.0 hooks: - id: black diff --git a/.prettierignore b/.prettierignore index 490a9c25..651a8862 100644 --- a/.prettierignore +++ b/.prettierignore @@ -11,4 +11,3 @@ testing/ testing* *.pyc bin/ -assets/nf-params.yml diff --git a/assets/nf-params.yml b/assets/nf-params.yml deleted file mode 100644 index 5e6c9928..00000000 --- a/assets/nf-params.yml +++ /dev/null @@ -1,412 +0,0 @@ -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## nf-core/pixelator 1.1.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 -## Required -## ----------------------------------------------------------------------------- -# input = null - -## ----------------------------------------------------------------------------- -## input_basedir -## ----------------------------------------------------------------------------- -## Path to a local or remote directory that is the "current working -## directory" for relative paths defined in the input samplesheet -## Type: string -## ----------------------------------------------------------------------------- -# input_basedir = null - -## ----------------------------------------------------------------------------- -## outdir -## ----------------------------------------------------------------------------- -## The output directory where the results will be saved. You have to use -## absolute paths to storage on Cloud infrastructure. -## Type: string -## Required -## ----------------------------------------------------------------------------- -# outdir = null - -## ----------------------------------------------------------------------------- -## 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 = null - -## ----------------------------------------------------------------------------- -## remove_polyg -## ----------------------------------------------------------------------------- -## Remove PolyG sequences (length of 10 or more) -## Type: boolean -## ----------------------------------------------------------------------------- -# remove_polyg = null - - -## ============================================================================= -## Adapter QC Options -## ============================================================================= - -## ----------------------------------------------------------------------------- -## 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 perform 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 = null - -## ----------------------------------------------------------------------------- -## 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 = null - -## ----------------------------------------------------------------------------- -## 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 = null - -## ----------------------------------------------------------------------------- -## 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 considered 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 = null - - -## ============================================================================= -## 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. - -## (11 hidden parameters are not shown) - - - From 9be31c261074a5441ef959c653e889e92f0e495d Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Thu, 7 Dec 2023 15:13:00 +0100 Subject: [PATCH 147/260] Using parquet as io format in collapse and graph --- modules/local/pixelator/single-cell/collapse/main.nf | 10 +++++----- modules/local/pixelator/single-cell/graph/main.nf | 3 +-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/modules/local/pixelator/single-cell/collapse/main.nf b/modules/local/pixelator/single-cell/collapse/main.nf index 893660a9..c5124955 100644 --- a/modules/local/pixelator/single-cell/collapse/main.nf +++ b/modules/local/pixelator/single-cell/collapse/main.nf @@ -11,12 +11,12 @@ process PIXELATOR_COLLAPSE { tuple val(meta), path(reads), path(panel_file), val(panel) 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 + tuple val(meta), path("collapse/*.collapsed.parquet"), 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 + path "versions.yml" , emit: versions when: task.ext.when == null || task.ext.when diff --git a/modules/local/pixelator/single-cell/graph/main.nf b/modules/local/pixelator/single-cell/graph/main.nf index f15b07e5..d7410741 100644 --- a/modules/local/pixelator/single-cell/graph/main.nf +++ b/modules/local/pixelator/single-cell/graph/main.nf @@ -12,8 +12,7 @@ process PIXELATOR_GRAPH { 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/*.edgelist.parquet") , emit: 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 From 9b558df1da9aee2b581ab73960359f17269935f2 Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Thu, 7 Dec 2023 15:13:35 +0100 Subject: [PATCH 148/260] Fixing typos in schema file --- nextflow_schema.json | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index 1659e2d6..85773657 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,7 +10,10 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": ["input", "outdir"], + "required": [ + "input", + "outdir" + ], "properties": { "input": { "type": "string", @@ -147,7 +150,10 @@ "fa_icon": "fas code-fork", "description": "The algorithm to use for collapsing (adjacency will perform error correction using the number of mismatches given)", "default": "adjacency", - "enum": ["adjacency", "unique"], + "enum": [ + "adjacency", + "unique" + ], "type": "string" }, "max_neighbours": { @@ -224,7 +230,11 @@ "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", - "enum": ["both", "min", "max"], + "enum": [ + "both", + "min", + "max" + ], "default": "min" }, "aggregate_calling": { @@ -260,7 +270,11 @@ "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"], + "enum": [ + "raw", + "clr", + "denoise" + ], "default": "clr" }, "polarization_binarization": { @@ -270,7 +284,12 @@ }, "colocalization_transformation": { "type": "string", - "enum": ["raw", "clr", "log1p", "relative"], + "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" }, @@ -424,7 +443,14 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], + "enum": [ + "symlink", + "rellink", + "link", + "copy", + "copyNoFollow", + "move" + ], "hidden": true }, "email_on_fail": { @@ -526,4 +552,4 @@ "$ref": "#/definitions/generic_options" } ] -} +} \ No newline at end of file From 7aa829a3ef2c57b266d280edb931687ddd47b57b Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Thu, 7 Dec 2023 15:13:47 +0100 Subject: [PATCH 149/260] Custom config to switch graph backend --- custom.config | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 custom.config diff --git a/custom.config b/custom.config new file mode 100644 index 00000000..47b2b03f --- /dev/null +++ b/custom.config @@ -0,0 +1,5 @@ + +// Change which pixelator backend is used. +env { + PIXELATOR_GRAPH_BACKEND = "NetworkXGraphBackend" +} From 6fdb731240cf861071c59df02465d90a8fbe4287 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 7 Dec 2023 15:30:44 +0100 Subject: [PATCH 150/260] update docs output section for switch to parquet --- docs/output.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/output.md b/docs/output.md index 0013ed26..5590fee4 100644 --- a/docs/output.md +++ b/docs/output.md @@ -110,7 +110,7 @@ given barcodes/antibodies. - `collapse` - - `.collapsed.csv.gz`: Edgelist of the graph. + - `.collapsed.parquet`: Edgelist of the graph. - `.report.json`: Statistics for the collapse step. - `.meta.json`: Command invocation metadata. @@ -137,10 +137,8 @@ The output format of this command is an edge list in CSV format. - `graph` - - `.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. + - `.edgelist.parquet`: + Edge list dataframe after recovering technical multiplets. - `.components_recovered.csv`: List of new components recovered (when using `--multiple-recovery`) - `.meta.json`: Command invocation metadata. From 80d1935fbfc70a9c4578a42d5ef43e8f8c9c1c0c Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Thu, 11 Jan 2024 09:59:33 +0100 Subject: [PATCH 151/260] Remove uneccessary custom config file --- custom.config | 5 ----- 1 file changed, 5 deletions(-) delete mode 100644 custom.config diff --git a/custom.config b/custom.config deleted file mode 100644 index 47b2b03f..00000000 --- a/custom.config +++ /dev/null @@ -1,5 +0,0 @@ - -// Change which pixelator backend is used. -env { - PIXELATOR_GRAPH_BACKEND = "NetworkXGraphBackend" -} From 7c33b9175fda4dd0011cf1d04129d01f6eb6cbbe Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Thu, 11 Jan 2024 10:11:44 +0100 Subject: [PATCH 152/260] Update changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4fd9204d..2575cec0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ 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). + +## [UNRELEASED] - YYYY-MM-DD + +### Changed + +- `collapse` and `graph` steps output parquet files + ## [[1.0.3](https://github.com/nf-core/pixelator/releases/tag/1.0.3)] - 2024-01-19 ### Enhancements & fixes From 8c3debd8d0d61e1248cc8602158f1f4bf3876614 Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Wed, 20 Mar 2024 15:33:33 +0100 Subject: [PATCH 153/260] Update to pixelator 0.16.2 --- modules/local/pixelator/collect_metadata.nf | 6 +++--- modules/local/pixelator/list_options.nf | 6 +++--- modules/local/pixelator/single-cell/amplicon/main.nf | 6 +++--- modules/local/pixelator/single-cell/analysis/main.nf | 6 +++--- modules/local/pixelator/single-cell/annotate/main.nf | 6 +++--- modules/local/pixelator/single-cell/collapse/main.nf | 6 +++--- modules/local/pixelator/single-cell/demux/main.nf | 6 +++--- modules/local/pixelator/single-cell/graph/main.nf | 6 +++--- modules/local/pixelator/single-cell/qc/main.nf | 6 +++--- modules/local/pixelator/single-cell/report/main.nf | 6 +++--- 10 files changed, 30 insertions(+), 30 deletions(-) diff --git a/modules/local/pixelator/collect_metadata.nf b/modules/local/pixelator/collect_metadata.nf index 469e3651..6f0f0ac1 100644 --- a/modules/local/pixelator/collect_metadata.nf +++ b/modules/local/pixelator/collect_metadata.nf @@ -6,10 +6,10 @@ process PIXELATOR_COLLECT_METADATA { label 'process_single' cache false - conda "bioconda::pixelator=0.15.2" + conda "bioconda::pixelator=0.16.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : - 'biocontainers/pixelator:0.15.2--pyh7cba7a3_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.16.2--pyhdfd78af_0' }" input: diff --git a/modules/local/pixelator/list_options.nf b/modules/local/pixelator/list_options.nf index 200cf5b2..fe59c369 100644 --- a/modules/local/pixelator/list_options.nf +++ b/modules/local/pixelator/list_options.nf @@ -2,10 +2,10 @@ process PIXELATOR_LIST_OPTIONS { label 'process_single' - conda "bioconda::pixelator=0.15.2" + conda "bioconda::pixelator=0.16.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : - 'biocontainers/pixelator:0.15.2--pyh7cba7a3_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.16.2--pyhdfd78af_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 f5eb6ae5..bdd60d2a 100644 --- a/modules/local/pixelator/single-cell/amplicon/main.nf +++ b/modules/local/pixelator/single-cell/amplicon/main.nf @@ -3,10 +3,10 @@ process PIXELATOR_AMPLICON { label 'process_low' - conda "bioconda::pixelator=0.15.2" + conda "bioconda::pixelator=0.16.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : - 'biocontainers/pixelator:0.15.2--pyh7cba7a3_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.16.2--pyhdfd78af_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 a30843b5..0ad705e0 100644 --- a/modules/local/pixelator/single-cell/analysis/main.nf +++ b/modules/local/pixelator/single-cell/analysis/main.nf @@ -3,10 +3,10 @@ process PIXELATOR_ANALYSIS { label 'process_medium' - conda "bioconda::pixelator=0.15.2" + conda "bioconda::pixelator=0.16.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : - 'biocontainers/pixelator:0.15.2--pyh7cba7a3_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.16.2--pyhdfd78af_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 16c17d8c..c7a44e82 100644 --- a/modules/local/pixelator/single-cell/annotate/main.nf +++ b/modules/local/pixelator/single-cell/annotate/main.nf @@ -3,10 +3,10 @@ process PIXELATOR_ANNOTATE { label 'process_medium' - conda "bioconda::pixelator=0.15.2" + conda "bioconda::pixelator=0.16.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : - 'biocontainers/pixelator:0.15.2--pyh7cba7a3_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.16.2--pyhdfd78af_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 c5124955..17918e76 100644 --- a/modules/local/pixelator/single-cell/collapse/main.nf +++ b/modules/local/pixelator/single-cell/collapse/main.nf @@ -2,10 +2,10 @@ process PIXELATOR_COLLAPSE { tag "$meta.id" label 'process_medium' - conda "bioconda::pixelator=0.15.2" + conda "bioconda::pixelator=0.16.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : - 'biocontainers/pixelator:0.15.2--pyh7cba7a3_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.16.2--pyhdfd78af_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 fc50d42a..6a02b0ac 100644 --- a/modules/local/pixelator/single-cell/demux/main.nf +++ b/modules/local/pixelator/single-cell/demux/main.nf @@ -3,10 +3,10 @@ process PIXELATOR_DEMUX { label 'process_medium' - conda "bioconda::pixelator=0.15.2" + conda "bioconda::pixelator=0.16.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : - 'biocontainers/pixelator:0.15.2--pyh7cba7a3_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.16.2--pyhdfd78af_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 d7410741..96e56324 100644 --- a/modules/local/pixelator/single-cell/graph/main.nf +++ b/modules/local/pixelator/single-cell/graph/main.nf @@ -3,10 +3,10 @@ process PIXELATOR_GRAPH { label 'process_medium' - conda "bioconda::pixelator=0.15.2" + conda "bioconda::pixelator=0.16.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : - 'biocontainers/pixelator:0.15.2--pyh7cba7a3_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.16.2--pyhdfd78af_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 141ed303..2650e5c4 100644 --- a/modules/local/pixelator/single-cell/qc/main.nf +++ b/modules/local/pixelator/single-cell/qc/main.nf @@ -3,10 +3,10 @@ process PIXELATOR_QC { label 'process_medium' - conda "bioconda::pixelator=0.15.2" + conda "bioconda::pixelator=0.16.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : - 'biocontainers/pixelator:0.15.2--pyh7cba7a3_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.16.2--pyhdfd78af_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 f39e7dda..c71b9749 100644 --- a/modules/local/pixelator/single-cell/report/main.nf +++ b/modules/local/pixelator/single-cell/report/main.nf @@ -3,10 +3,10 @@ process PIXELATOR_REPORT { label 'process_low' - conda "bioconda::pixelator=0.15.2" + conda "bioconda::pixelator=0.16.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.15.2--pyh7cba7a3_0' : - 'biocontainers/pixelator:0.15.2--pyh7cba7a3_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.16.2--pyhdfd78af_0' }" input: tuple val(meta), path(panel_file), val(panel) From 579c06bf131a82b06ec1c0b6ee4c1436b640088a Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Wed, 20 Mar 2024 16:14:06 +0100 Subject: [PATCH 154/260] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2575cec0..1dd28b2f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - `collapse` and `graph` steps output parquet files +- Update pixelator to 0.16.2 ## [[1.0.3](https://github.com/nf-core/pixelator/releases/tag/1.0.3)] - 2024-01-19 From ab7ae89fd96e7a2dcc31c163f24882c3ab781d54 Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Wed, 20 Mar 2024 16:41:02 +0100 Subject: [PATCH 155/260] Fix formatting --- CHANGELOG.md | 1 - assets/nf-params.yml | 394 +++++++++++++++++++++++++++++++++++++++++++ nextflow_schema.json | 40 +---- 3 files changed, 401 insertions(+), 34 deletions(-) create mode 100644 assets/nf-params.yml diff --git a/CHANGELOG.md b/CHANGELOG.md index 1dd28b2f..367d1626 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +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). - ## [UNRELEASED] - YYYY-MM-DD ### Changed diff --git a/assets/nf-params.yml b/assets/nf-params.yml new file mode 100644 index 00000000..f43a625b --- /dev/null +++ b/assets/nf-params.yml @@ -0,0 +1,394 @@ +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## nf-core/pixelator 1.0.3 +## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## 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 +## Required +## ----------------------------------------------------------------------------- +# input = null + +## ----------------------------------------------------------------------------- +## input_basedir +## ----------------------------------------------------------------------------- +## Path to a local or remote directory that is the "current working +## directory" for relative paths defined in the input samplesheet +## Type: string +## ----------------------------------------------------------------------------- +# input_basedir = null + +## ----------------------------------------------------------------------------- +## outdir +## ----------------------------------------------------------------------------- +## The output directory where the results will be saved. You have to use +## absolute paths to storage on Cloud infrastructure. +## Type: string +## Required +## ----------------------------------------------------------------------------- +# 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 = null + +## ----------------------------------------------------------------------------- +## remove_polyg +## ----------------------------------------------------------------------------- +## Remove PolyG sequences (length of 10 or more) +## Type: boolean +## ----------------------------------------------------------------------------- +# remove_polyg = null + +## ============================================================================= +## Adapter QC Options +## ============================================================================= + +## ----------------------------------------------------------------------------- +## 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 perform 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 = null + +## ----------------------------------------------------------------------------- +## 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 = null + +## ----------------------------------------------------------------------------- +## 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 = null + +## ----------------------------------------------------------------------------- +## 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 considered 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 = null + +## ============================================================================= +## 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/nextflow_schema.json b/nextflow_schema.json index 85773657..1659e2d6 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -10,10 +10,7 @@ "type": "object", "fa_icon": "fas fa-terminal", "description": "Define where the pipeline should find input data and save output data.", - "required": [ - "input", - "outdir" - ], + "required": ["input", "outdir"], "properties": { "input": { "type": "string", @@ -150,10 +147,7 @@ "fa_icon": "fas code-fork", "description": "The algorithm to use for collapsing (adjacency will perform error correction using the number of mismatches given)", "default": "adjacency", - "enum": [ - "adjacency", - "unique" - ], + "enum": ["adjacency", "unique"], "type": "string" }, "max_neighbours": { @@ -230,11 +224,7 @@ "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", - "enum": [ - "both", - "min", - "max" - ], + "enum": ["both", "min", "max"], "default": "min" }, "aggregate_calling": { @@ -270,11 +260,7 @@ "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" - ], + "enum": ["raw", "clr", "denoise"], "default": "clr" }, "polarization_binarization": { @@ -284,12 +270,7 @@ }, "colocalization_transformation": { "type": "string", - "enum": [ - "raw", - "clr", - "log1p", - "relative" - ], + "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" }, @@ -443,14 +424,7 @@ "description": "Method used to save pipeline results to output directory.", "help_text": "The Nextflow `publishDir` option specifies which intermediate files should be saved to the output directory. This option tells the pipeline what method should be used to move these files. See [Nextflow docs](https://www.nextflow.io/docs/latest/process.html#publishdir) for details.", "fa_icon": "fas fa-copy", - "enum": [ - "symlink", - "rellink", - "link", - "copy", - "copyNoFollow", - "move" - ], + "enum": ["symlink", "rellink", "link", "copy", "copyNoFollow", "move"], "hidden": true }, "email_on_fail": { @@ -552,4 +526,4 @@ "$ref": "#/definitions/generic_options" } ] -} \ No newline at end of file +} From a0514252ab41cae41e39e9cb40a96d10095f95ed Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Thu, 21 Mar 2024 09:29:15 +0100 Subject: [PATCH 156/260] Remove nf-params.yml --- assets/nf-params.yml | 394 ------------------------------------------- 1 file changed, 394 deletions(-) delete mode 100644 assets/nf-params.yml diff --git a/assets/nf-params.yml b/assets/nf-params.yml deleted file mode 100644 index f43a625b..00000000 --- a/assets/nf-params.yml +++ /dev/null @@ -1,394 +0,0 @@ -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## nf-core/pixelator 1.0.3 -## ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -## 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 -## Required -## ----------------------------------------------------------------------------- -# input = null - -## ----------------------------------------------------------------------------- -## input_basedir -## ----------------------------------------------------------------------------- -## Path to a local or remote directory that is the "current working -## directory" for relative paths defined in the input samplesheet -## Type: string -## ----------------------------------------------------------------------------- -# input_basedir = null - -## ----------------------------------------------------------------------------- -## outdir -## ----------------------------------------------------------------------------- -## The output directory where the results will be saved. You have to use -## absolute paths to storage on Cloud infrastructure. -## Type: string -## Required -## ----------------------------------------------------------------------------- -# 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 = null - -## ----------------------------------------------------------------------------- -## remove_polyg -## ----------------------------------------------------------------------------- -## Remove PolyG sequences (length of 10 or more) -## Type: boolean -## ----------------------------------------------------------------------------- -# remove_polyg = null - -## ============================================================================= -## Adapter QC Options -## ============================================================================= - -## ----------------------------------------------------------------------------- -## 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 perform 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 = null - -## ----------------------------------------------------------------------------- -## 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 = null - -## ----------------------------------------------------------------------------- -## 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 = null - -## ----------------------------------------------------------------------------- -## 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 considered 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 = null - -## ============================================================================= -## 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) - From 12c1383665d77452adc69fb0c7be980c5daabde9 Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Thu, 21 Mar 2024 10:33:42 +0100 Subject: [PATCH 157/260] Update metromap and add software dependency table --- CHANGELOG.md | 12 +++ docs/images/nf-core-pixelator-metromap.svg | 98 +++++++++++----------- 2 files changed, 61 insertions(+), 49 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 367d1626..6e5443dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `collapse` and `graph` steps output parquet files - Update pixelator to 0.16.2 +### Software dependencies + +| Dependency | Old version | New version | +| ----------- | ----------- | ----------- | +| `pixelator` | 0.15.2 | 0.16.2 | + +> **NB:** Dependency has been **updated** if both old and new version information is present. +> +> **NB:** Dependency has been **added** if just the new version information is present. +> +> **NB:** Dependency has been **removed** if new version information isn't present. + ## [[1.0.3](https://github.com/nf-core/pixelator/releases/tag/1.0.3)] - 2024-01-19 ### Enhancements & fixes diff --git a/docs/images/nf-core-pixelator-metromap.svg b/docs/images/nf-core-pixelator-metromap.svg index dee01a7d..a6c3e37e 100644 --- a/docs/images/nf-core-pixelator-metromap.svg +++ b/docs/images/nf-core-pixelator-metromap.svg @@ -1,13 +1,13 @@ - + - - - - + + + + @@ -17,7 +17,7 @@ - + @@ -25,7 +25,7 @@ - + @@ -79,29 +79,29 @@ - + - + - + - + - + - + @@ -112,10 +112,10 @@ - + - + @@ -124,7 +124,7 @@ - + @@ -133,113 +133,113 @@ - - + + - + - + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - + - + - + - + - + - + - + From d85364f3bd1b78415dbc2834f2dc60beed905c2d Mon Sep 17 00:00:00 2001 From: Pixelgen Technologies Date: Thu, 22 Feb 2024 22:44:30 +0100 Subject: [PATCH 158/260] fix: max memory requirements accoring to spec in documentation --- conf/base.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/base.config b/conf/base.config index 9861f0c5..0d55af3b 100644 --- a/conf/base.config +++ b/conf/base.config @@ -36,7 +36,7 @@ process { } withLabel:process_medium { cpus = { check_max( 6 * task.attempt, 'cpus' ) } - memory = { check_max( 36.GB * task.attempt, 'memory' ) } + memory = { check_max( 32.GB * task.attempt, 'memory' ) } time = { check_max( 8.h * task.attempt, 'time' ) } } withLabel:process_high { From 29eb3bbf61db74c1b491861c7c33d391967d2f3d Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 21 Mar 2024 16:52:58 +0100 Subject: [PATCH 159/260] Silence unused params.genome warning --- subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf b/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf index 58fa645a..e2f9d958 100644 --- a/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf @@ -186,7 +186,8 @@ workflow PIPELINE_COMPLETION { // Check and validate pipeline parameters // def validateInputParameters() { - genomeExistsError() + // Keep this commented here to closely follow the template + // genomeExistsError() } // From a6502567b6c4dff58f99a1f783005142e303bba0 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 21 Mar 2024 17:22:25 +0100 Subject: [PATCH 160/260] Add env options for numba and matplotlib cache with container based profiles --- nextflow.config | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/nextflow.config b/nextflow.config index aee3d7bc..1251c1f9 100644 --- a/nextflow.config +++ b/nextflow.config @@ -118,6 +118,13 @@ try { // } catch (Exception e) { // System.err.println("WARNING: Could not load nf-core/config/pixelator profiles: ${params.custom_config_base}/pipeline/pixelator.config") // } + + +def container_env_options = [ + "MPLCONFIGDIR": '/tmp/.config/matplotlib', + "NUMBA_CACHE_DIR": "/tmp/.numba_cache", +] + profiles { debug { dumpHashes = true @@ -153,8 +160,8 @@ profiles { shifter.enabled = false charliecloud.enabled = false apptainer.enabled = false - // Disabled this to deal with libpysal temporary directory issue - // docker.runOptions = '-u $(id -u):$(id -g)' + docker.runOptions = '-u $(id -u):$(id -g)' + env = container_env_options } arm { docker.runOptions = '-u $(id -u):$(id -g) --platform=linux/amd64' @@ -168,6 +175,7 @@ profiles { shifter.enabled = false charliecloud.enabled = false apptainer.enabled = false + env = container_env_options } podman { podman.enabled = true @@ -178,6 +186,7 @@ profiles { charliecloud.enabled = false apptainer.enabled = false podman.runOptions = '--userns=keep-id' + env = container_env_options } shifter { shifter.enabled = true @@ -187,6 +196,7 @@ profiles { podman.enabled = false charliecloud.enabled = false apptainer.enabled = false + env = container_env_options } charliecloud { charliecloud.enabled = true @@ -196,6 +206,7 @@ profiles { podman.enabled = false shifter.enabled = false apptainer.enabled = false + env = container_env_options } apptainer { apptainer.enabled = true @@ -206,6 +217,7 @@ profiles { podman.enabled = false shifter.enabled = false charliecloud.enabled = false + env = container_env_options } gitpod { executor.name = 'local' From f8c889669c5cb79b99176912c803bdf4f03a5f48 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 21 Mar 2024 17:25:52 +0100 Subject: [PATCH 161/260] Update CHANGELOG.md --- CHANGELOG.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6e5443dc..23bc5c33 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [UNRELEASED] - YYYY-MM-DD -### Changed +### Enhancements & fixes -- `collapse` and `graph` steps output parquet files -- Update pixelator to 0.16.2 +- `collapse` and `graph` step now output parquet files +- Update `process_medium` label to use 32 GiB of memory by default +- Fix a warning from an unused parameter from the nf-core template ### Software dependencies From f49632d7f765a79ba880a2748e5f80b108d5ac99 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 21 Mar 2024 17:28:08 +0100 Subject: [PATCH 162/260] bump version to 1.1.0 --- CHANGELOG.md | 2 +- nextflow.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 23bc5c33..a63d9271 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ 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). -## [UNRELEASED] - YYYY-MM-DD +## [[1.1.0](https://github.com/nf-core/pixelator/releases/tag/1.1.0)] - 2024-03-22 ### Enhancements & fixes diff --git a/nextflow.config b/nextflow.config index 1251c1f9..6af56176 100644 --- a/nextflow.config +++ b/nextflow.config @@ -283,7 +283,7 @@ manifest { description = """Pipeline for analysis of Molecular Pixelation assays""" mainScript = 'main.nf' nextflowVersion = '!>=23.04.0' - version = '1.1.0dev' + version = '1.1.0' doi = '10.1101/2023.06.05.543770' } From 55933e6cbbeb2c70b367285157cf9db5da3936de Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 26 Mar 2024 10:20:42 +0100 Subject: [PATCH 163/260] Update CHANGELOG --- CHANGELOG.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a63d9271..9405d163 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,13 +3,13 @@ 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). -## [[1.1.0](https://github.com/nf-core/pixelator/releases/tag/1.1.0)] - 2024-03-22 +## [[1.1.0](https://github.com/nf-core/pixelator/releases/tag/1.1.0)] - 2024-03-26 ### Enhancements & fixes -- `collapse` and `graph` step now output parquet files -- Update `process_medium` label to use 32 GiB of memory by default -- Fix a warning from an unused parameter from the nf-core template +- [[PR #83](https://github.com/nf-core/pixelator/pull/83)] - Template update for nf-core/tools v2.13 +- [[PR #84](https://github.com/nf-core/pixelator/pull/84)] - Update pixelator to 0.16.2, collapse`and`graph` step now return parquet files +- [[PR #85](https://github.com/nf-core/pixelator/pull/85)] - Remove a workaround for container issues, silence some warnings, update default resources ### Software dependencies From dba549235cd00e141c87d7523573d731d55bf72e Mon Sep 17 00:00:00 2001 From: Florian De Temmerman <69114541+fbdtemme@users.noreply.github.com> Date: Tue, 26 Mar 2024 13:33:28 +0100 Subject: [PATCH 164/260] Update alignment in main.nf MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matthias Hörtenhuber --- main.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/main.nf b/main.nf index b8a63871..759cf0a2 100644 --- a/main.nf +++ b/main.nf @@ -17,7 +17,7 @@ nextflow.enable.dsl = 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -include { PIXELATOR } from './workflows/pixelator' +include { PIXELATOR } from './workflows/pixelator' include { PIPELINE_INITIALISATION } from './subworkflows/local/utils_nfcore_pixelator_pipeline' include { PIPELINE_COMPLETION } from './subworkflows/local/utils_nfcore_pixelator_pipeline' From a0de2d5caa72b0494b589cc1411f8e8c6b614423 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 26 Mar 2024 13:41:22 +0100 Subject: [PATCH 165/260] Revert nf-test to test profile in PR template. --- .github/PULL_REQUEST_TEMPLATE.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 2457900b..9d93ee87 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -18,7 +18,7 @@ Learn more about contributing: [CONTRIBUTING.md](https://github.com/nf-core/pixe - [ ] If you've added a new tool - have you followed the pipeline conventions in the [contribution docs](https://github.com/nf-core/pixelator/tree/master/.github/CONTRIBUTING.md) - [ ] If necessary, also make a PR on the nf-core/pixelator _branch_ on the [nf-core/test-datasets](https://github.com/nf-core/test-datasets) repository. - [ ] Make sure your code lints (`nf-core lint`). -- [ ] Ensure the test suite passes (`nf-test test main.nf.test -profile test,docker`). +- [ ] Ensure the test suite passes (`nextflow run . -profile test,docker --outdir `). - [ ] Check for unexpected warnings in debug mode (`nextflow run . -profile debug,test,docker --outdir `). - [ ] Usage Documentation in `docs/usage.md` is updated. - [ ] Output Documentation in `docs/output.md` is updated. From 417bc49cf4f207efb9cc75ba5ee1ee2113256a9a Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 26 Mar 2024 13:45:35 +0100 Subject: [PATCH 166/260] Skip linting of unchanged PULL_REQUEST_TEMPLATE --- .nf-core.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.nf-core.yml b/.nf-core.yml index 2714bdba..25ae468e 100644 --- a/.nf-core.yml +++ b/.nf-core.yml @@ -8,3 +8,5 @@ lint: files_unchanged: - lib/NfcoreTemplate.groovy - .prettierignore + # Skip warning for a removed a reference to nf-test + - .github/PULL_REQUEST_TEMPLATE.md From b8068dfe88b0dfd793eb5e6102822afb56e11025 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 29 Mar 2024 09:17:21 +0100 Subject: [PATCH 167/260] Update 1.1.0 release date in CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9405d163..92ef4f0e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ 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). -## [[1.1.0](https://github.com/nf-core/pixelator/releases/tag/1.1.0)] - 2024-03-26 +## [[1.1.0](https://github.com/nf-core/pixelator/releases/tag/1.1.0)] - 2024-03-29 ### Enhancements & fixes From 0ab9f89d76d4bba72f7b630ec98d2269fad6647e Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 15 May 2024 10:07:30 +0200 Subject: [PATCH 168/260] Merge branch 'dev' into nf-core-template-merge-2.14.1 --- .github/workflows/awsfulltest.yml | 3 - .github/workflows/ci.yml | 3 - .github/workflows/release-announcments.yml | 68 +++ .gitignore | 2 + .nf-core.yml | 11 + .pre-commit-config.yaml | 7 + .prettierignore | 1 + CHANGELOG.md | 65 ++- CITATIONS.md | 12 +- README.md | 57 +-- assets/methods_description_template.yml | 29 -- assets/multiqc_config.yml | 17 - assets/samplesheet.csv | 7 +- assets/schema_input.json | 31 +- assets/test_samplesheet.csv | 4 + bin/collect_metadata.py | 83 ++++ conf/base.config | 4 +- conf/igenomes.config | 440 ------------------ conf/modules.config | 129 ++++- conf/test.config | 19 +- conf/test_full.config | 9 +- 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/images/nf-core-pixelator-metromap.svg | 246 ++++++++++ docs/output.md | 233 ++++++++-- docs/usage.md | 140 ++++-- main.nf | 23 +- modules.json | 6 +- modules/local/pixelator/collect_metadata.nf | 79 ++++ modules/local/pixelator/list_options.nf | 31 ++ .../pixelator/single-cell/amplicon/main.nf | 45 ++ .../pixelator/single-cell/analysis/main.nf | 47 ++ .../pixelator/single-cell/annotate/main.nf | 53 +++ .../pixelator/single-cell/collapse/main.nf | 54 +++ .../local/pixelator/single-cell/demux/main.nf | 54 +++ .../local/pixelator/single-cell/graph/main.nf | 48 ++ .../local/pixelator/single-cell/qc/main.nf | 78 ++++ .../pixelator/single-cell/report/main.nf | 57 +++ modules/local/rename_reads.nf | 45 ++ .../{fastqc => cat/fastq}/environment.yml | 4 +- modules/nf-core/cat/fastq/main.nf | 79 ++++ modules/nf-core/cat/fastq/meta.yml | 42 ++ modules/nf-core/cat/fastq/tests/main.nf.test | 138 ++++++ .../nf-core/cat/fastq/tests/main.nf.test.snap | 169 +++++++ modules/nf-core/cat/fastq/tests/tags.yml | 2 + .../dumpsoftwareversions}/environment.yml | 4 +- .../custom/dumpsoftwareversions/main.nf | 24 + .../custom/dumpsoftwareversions/meta.yml | 37 ++ .../templates/dumpsoftwareversions.py | 103 ++++ .../dumpsoftwareversions/tests/main.nf.test | 43 ++ .../tests/main.nf.test.snap | 33 ++ .../dumpsoftwareversions/tests/tags.yml | 2 + modules/nf-core/fastqc/main.nf | 61 --- modules/nf-core/fastqc/meta.yml | 57 --- modules/nf-core/fastqc/tests/main.nf.test | 212 --------- .../nf-core/fastqc/tests/main.nf.test.snap | 88 ---- modules/nf-core/fastqc/tests/tags.yml | 2 - modules/nf-core/multiqc/main.nf | 55 --- modules/nf-core/multiqc/meta.yml | 58 --- modules/nf-core/multiqc/tests/main.nf.test | 84 ---- modules/nf-core/multiqc/tests/tags.yml | 2 - nextflow.config | 167 ++++--- nextflow_schema.json | 348 +++++++++++--- samplesheet.csv | 3 + subworkflows/local/generate_reports.nf | 134 ++++++ .../utils_nfcore_pixelator_pipeline/main.nf | 216 ++++++++- tower.yml | 6 +- workflows/pixelator.nf | 263 +++++++++-- 69 files changed, 3232 insertions(+), 1444 deletions(-) create mode 100644 .github/workflows/release-announcments.yml delete mode 100644 assets/methods_description_template.yml delete mode 100644 assets/multiqc_config.yml create mode 100644 assets/test_samplesheet.csv create mode 100755 bin/collect_metadata.py delete mode 100644 conf/igenomes.config 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 create mode 100644 docs/images/nf-core-pixelator-metromap.svg create mode 100644 modules/local/pixelator/collect_metadata.nf create mode 100644 modules/local/pixelator/list_options.nf create mode 100644 modules/local/pixelator/single-cell/amplicon/main.nf create mode 100644 modules/local/pixelator/single-cell/analysis/main.nf create mode 100644 modules/local/pixelator/single-cell/annotate/main.nf create mode 100644 modules/local/pixelator/single-cell/collapse/main.nf create mode 100644 modules/local/pixelator/single-cell/demux/main.nf create mode 100644 modules/local/pixelator/single-cell/graph/main.nf create mode 100644 modules/local/pixelator/single-cell/qc/main.nf create mode 100644 modules/local/pixelator/single-cell/report/main.nf create mode 100644 modules/local/rename_reads.nf rename modules/nf-core/{fastqc => cat/fastq}/environment.yml (57%) create mode 100644 modules/nf-core/cat/fastq/main.nf create mode 100644 modules/nf-core/cat/fastq/meta.yml create mode 100644 modules/nf-core/cat/fastq/tests/main.nf.test create mode 100644 modules/nf-core/cat/fastq/tests/main.nf.test.snap create mode 100644 modules/nf-core/cat/fastq/tests/tags.yml rename modules/nf-core/{multiqc => custom/dumpsoftwareversions}/environment.yml (51%) create mode 100644 modules/nf-core/custom/dumpsoftwareversions/main.nf create mode 100644 modules/nf-core/custom/dumpsoftwareversions/meta.yml create mode 100755 modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py create mode 100644 modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test create mode 100644 modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap create mode 100644 modules/nf-core/custom/dumpsoftwareversions/tests/tags.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/fastqc/tests/main.nf.test delete mode 100644 modules/nf-core/fastqc/tests/main.nf.test.snap delete mode 100644 modules/nf-core/fastqc/tests/tags.yml delete mode 100644 modules/nf-core/multiqc/main.nf delete mode 100644 modules/nf-core/multiqc/meta.yml delete mode 100644 modules/nf-core/multiqc/tests/main.nf.test delete mode 100644 modules/nf-core/multiqc/tests/tags.yml create mode 100644 samplesheet.csv create mode 100644 subworkflows/local/generate_reports.nf diff --git a/.github/workflows/awsfulltest.yml b/.github/workflows/awsfulltest.yml index bd9c15db..831c60e8 100644 --- a/.github/workflows/awsfulltest.yml +++ b/.github/workflows/awsfulltest.yml @@ -15,9 +15,6 @@ jobs: steps: - name: Launch workflow via Seqera Platform 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 a7db22d2..4168fb0f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,8 +39,5 @@ jobs: uses: jlumbroso/free-disk-space@54081f138730dfa15788a46383842cd2f914a1be # v1.3.1 - 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 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/.gitignore b/.gitignore index 5124c9ac..d0538e85 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ results/ testing/ testing* *.pyc +.idea +.vscode diff --git a/.nf-core.yml b/.nf-core.yml index e0b85a77..f447682f 100644 --- a/.nf-core.yml +++ b/.nf-core.yml @@ -1,2 +1,13 @@ repository_type: pipeline nf_core_version: "2.14.1" +lint: + # No multiqc support for now + multiqc_config: false + files_exist: + - assets/multiqc_config.yml + - conf/igenomes.config + files_unchanged: + - lib/NfcoreTemplate.groovy + - .prettierignore + # Skip warning for a removed a reference to nf-test + - .github/PULL_REQUEST_TEMPLATE.md diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4dc0f1dc..b92f13dc 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,3 +1,5 @@ +exclude: ^(.*.test.snap)$ + repos: - repo: https://github.com/pre-commit/mirrors-prettier rev: "v3.1.0" @@ -11,3 +13,8 @@ repos: hooks: - id: editorconfig-checker alias: ec + + - repo: https://github.com/psf/black + rev: 24.2.0 + hooks: + - id: black diff --git a/.prettierignore b/.prettierignore index 437d763d..651a8862 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,6 +1,7 @@ email_template.html adaptivecard.json slackreport.json +.devcontainer/devcontainer.json .nextflow* work/ data/ diff --git a/CHANGELOG.md b/CHANGELOG.md index f7f10319..bf5d953d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,14 +3,67 @@ 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.1.0 - [date] +## [[1.2.0](https://github.com/nf-core/pixelator/releases/tag/1.2.0)] - UNRELEASED -Initial release of nf-core/pixelator, created with the [nf-core](https://nf-co.re/) template. +### Enhancements & fixes -### `Added` +- [[PR #89](https://github.com/nf-core/pixelator/pull/89)] - Template update for nf-core/tools v2.14.1 -### `Fixed` +## [[1.1.0](https://github.com/nf-core/pixelator/releases/tag/1.1.0)] - 2024-03-29 -### `Dependencies` +### Enhancements & fixes -### `Deprecated` +- [[PR #83](https://github.com/nf-core/pixelator/pull/83)] - Template update for nf-core/tools v2.13 +- [[PR #84](https://github.com/nf-core/pixelator/pull/84)] - Update pixelator to 0.16.2, collapse`and`graph` step now return parquet files +- [[PR #85](https://github.com/nf-core/pixelator/pull/85)] - Remove a workaround for container issues, silence some warnings, update default resources + +### Software dependencies + +| Dependency | Old version | New version | +| ----------- | ----------- | ----------- | +| `pixelator` | 0.15.2 | 0.16.2 | + +> **NB:** Dependency has been **updated** if both old and new version information is present. +> +> **NB:** Dependency has been **added** if just the new version information is present. +> +> **NB:** Dependency has been **removed** if new version information isn't present. + +## [[1.0.3](https://github.com/nf-core/pixelator/releases/tag/1.0.3)] - 2024-01-19 + +### Enhancements & fixes + +- [[PR #74](https://github.com/nf-core/pixelator/pull/74)] - Template update for nf-core/tools v2.11 +- [[e196431](https://github.com/nf-core/pixelator/commit/e196431842b039cbf5c299c7a3e568f6a3e30e33)] - Workaround a tool issue by removing `docker.runOptions` user and group flags +- [[PR #76](https://github.com/nf-core/pixelator/pull/76)] - Use `adapterqc` output as main output of PIXELATOR_QC +- [[PR #77](https://github.com/nf-core/pixelator/pull/77)] - Fix some style issues in nextflow_schema.json + +## [[1.0.2](https://github.com/nf-core/pixelator/releases/tag/1.0.2)] - 2023-11-20 + +### Enhancements & fixes + +- [[PR #70](https://github.com/nf-core/pixelator/pull/70)] - Fix loading of absolute paths and urls in input samplesheet + +## [[1.0.1](https://github.com/nf-core/pixelator/releases/tag/1.0.1)] - 2023-10-27 + +### Enhancements & fixes + +- [[PR #66](https://github.com/nf-core/pixelator/pull/66)] - Add a warning and workaround for singularity & apptainer +- Cleanup some linting warnings +- Update docker image in RENAME_READS to match the singularity container + +### Software dependencies + +| Dependency | Old version | New version | +| ----------- | ----------- | ----------- | +| `pixelator` | 0.15.0 | 0.15.2 | + +> **NB:** Dependency has been **updated** if both old and new version information is present. +> +> **NB:** Dependency has been **added** if just the new version information is present. +> +> **NB:** Dependency has been **removed** if new version information isn't present. + +## [[1.0.0](https://github.com/nf-core/pixelator/releases/tag/1.0.0)] - 2023-10-17 + +Initial release of nf-core/pixelator. diff --git a/CITATIONS.md b/CITATIONS.md index ca3444ff..49b37b62 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -10,13 +10,17 @@ ## Pipeline tools -- [FastQC](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/) +- [pixelator](https://doi.org/10.1101/2023.06.05.543770) - > Andrews, S. (2010). FastQC: A Quality Control Tool for High Throughput Sequence Data [Online]. + > 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. -- [MultiQC](https://pubmed.ncbi.nlm.nih.gov/27312411/) +- [cutadapt](http://dx.doi.org/10.14806/ej.17.1.200) - > 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. + > 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 diff --git a/README.md b/README.md index fdbb131a..1c8d8fe5 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,8 @@ -[![GitHub Actions CI Status](https://github.com/nf-core/pixelator/actions/workflows/ci.yml/badge.svg)](https://github.com/nf-core/pixelator/actions/workflows/ci.yml) -[![GitHub Actions Linting Status](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) -[![nf-test](https://img.shields.io/badge/unit_tests-nf--test-337ab7.svg)](https://www.nf-test.com) +[![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.10015112-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.10015112) [![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/) @@ -19,46 +18,43 @@ ## Introduction -**nf-core/pixelator** is a bioinformatics pipeline that ... +**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)) +4. Error correction, duplicate removal, compute read counts ([`pixelator collapse`](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)) -1. Read QC ([`FastQC`](https://www.bioinformatics.babraham.ac.uk/projects/fastqc/)) -2. Present QC for raw reads ([`MultiQC`](http://multiqc.info/)) +> [!WARNING] +> Since Nextflow 23.07.0-edge, Nextflow no longer mounts the host's home directory when using Apptainer or Singularity. +> This causes issues in some dependencies. As a workaround, you can revert to the old behavior by setting the environment variable +> `NXF_APPTAINER_HOME_MOUNT` or `NXF_SINGULARITY_HOME_MOUNT` to `true` in the machine from which you launch the pipeline. ## 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. - +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 \ -profile \ @@ -80,11 +76,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. - -We thank the following people for their extensive assistance in the development of this pipeline: +nf-core/pixelator was originally written for [Pixelgen Technologies AB](https://www.pixelgen.com/) by: - +- Florian De Temmerman +- Johan Dahlberg +- Alvaro Martinez Barrio ## Contributions and Support @@ -94,10 +90,7 @@ For further information or help, don't hesitate to get in touch on the [Slack `# ## Citations - - - - +If you use nf-core/pixelator for your analysis, please cite it using the following doi: [10.5281/zenodo.10015112](https://doi.org/10.5281/zenodo.10015112) An extensive list of references for the tools used by the pipeline can be found in the [`CITATIONS.md`](CITATIONS.md) file. 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.
  • -
-
diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml deleted file mode 100644 index a67694de..00000000 --- a/assets/multiqc_config.yml +++ /dev/null @@ -1,17 +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 - -disable_version_detection: true diff --git a/assets/samplesheet.csv b/assets/samplesheet.csv index 5f653ab7..e50611a7 100644 --- a/assets/samplesheet.csv +++ b/assets/samplesheet.csv @@ -1,3 +1,4 @@ -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 +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 diff --git a/assets/schema_input.json b/assets/schema_input.json index 0b6b7dd1..463396c3 100644 --- a/assets/schema_input.json +++ b/assets/schema_input.json @@ -6,6 +6,7 @@ "type": "array", "items": { "type": "object", + "required": ["sample", "design", "fastq_1"], "properties": { "sample": { "type": "string", @@ -13,21 +14,39 @@ "errorMessage": "Sample name must be provided and cannot contain spaces", "meta": ["id"] }, + "design": { + "type": "string", + "meta": ["design"], + "errorMessage": "Design must be specified" + }, + "panel": { + "errorMessage": "Panel name must be specified", + "type": "string", + "meta": ["panel"] + }, + "panel_file": { + "errorMessage": "Panel file must either be left empty or cannot contain spaces and must have extension '.csv', '.tsv' or '.yaml'", + "anyOf": [ + { + "type": "string", + "pattern": "^\\S+.(csv|tsv|ya?ml)$" + }, + { + "type": "string", + "maxLength": 0 + } + ] + }, "fastq_1": { "type": "string", - "format": "file-path", - "exists": true, "pattern": "^\\S+\\.f(ast)?q\\.gz$", "errorMessage": "FastQ file for reads 1 must be provided, cannot contain spaces and must have extension '.fq.gz' or '.fastq.gz'" }, "fastq_2": { "type": "string", - "format": "file-path", - "exists": true, "pattern": "^\\S+\\.f(ast)?q\\.gz$", "errorMessage": "FastQ file for reads 2 cannot contain spaces and must have extension '.fq.gz' or '.fastq.gz'" } - }, - "required": ["sample", "fastq_1"] + } } } 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/bin/collect_metadata.py b/bin/collect_metadata.py new file mode 100755 index 00000000..8eef3d74 --- /dev/null +++ b/bin/collect_metadata.py @@ -0,0 +1,83 @@ +#!/usr/bin/env python + +""" +Collect version information about the pixelator python environment. + +Written by Florian De Temmerman (https://github.com/fbdtemme) +Copyright (c) 2023 Pixelgen Technologies AB. +""" + +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/base.config b/conf/base.config index 4d00103e..c3596e9c 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' ) } @@ -38,7 +36,7 @@ process { } withLabel:process_medium { cpus = { check_max( 6 * task.attempt, 'cpus' ) } - memory = { check_max( 36.GB * task.attempt, 'memory' ) } + memory = { check_max( 32.GB * task.attempt, 'memory' ) } time = { check_max( 8.h * task.attempt, 'time' ) } } withLabel:process_high { 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 d203d2b6..b1872182 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -10,6 +10,7 @@ ---------------------------------------------------------------------------------------- */ + process { publishDir = [ @@ -18,17 +19,131 @@ process { saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] - withName: FASTQC { - ext.args = '--quiet' + withName: "PIXELATOR.*" { + 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" + ] + ] + + if (params.pixelator_container) { + container = params.pixelator_container + } + } + + withName: PIXELATOR_LIST_OPTIONS { + publishDir = [ enabled: false ] + } + + withName: "CAT_FASTQ" { + publishDir = [ enabled: false ] } - withName: 'MULTIQC' { - ext.args = { params.multiqc_title ? "--title \"$params.multiqc_title\"" : '' } + withName: RENAME_READS { publishDir = [ - path: { "${params.outdir}/multiqc" }, - mode: params.publish_dir_mode, - saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + enabled: false, + path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, ] } + + // use explicit (params.my_option instanceof Integer) checks to avoid issues with 0 evaluating false + // since most pixelator flags do accept zero as a value + + withName: PIXELATOR_AMPLICON { + ext.args = { + [ + "--design ${meta.design}", + ].join(' ').trim() + } + } + + withName: PIXELATOR_QC { + // Options for pixelator preqc + ext.args = { + [ + "--design ${meta.design}", + (params.trim_front instanceof Integer)? "--trim-front ${params.trim_front}": '', + (params.trim_tail instanceof Integer) ? "--trim-tail ${params.trim_tail}": '', + (params.max_length instanceof Integer) ? "--max-length ${params.max_length}": '', + (params.min_length instanceof Integer) ? "--min-length ${params.min_length}": '', + (params.max_n_bases instanceof Integer) ? "--max-n-bases ${params.max_n_bases}": '', + (params.avg_qual instanceof Integer)? "--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}": '', + ].join(' ').trim() + } + } + + withName: PIXELATOR_DEMUX { + ext.args = { + [ + "--design ${meta.design}", + (params.demux_mismatches != null) ? "--mismatches ${params.demux_mismatches}": '', + (params.demux_min_length instanceof Integer) ? "--mismatches ${params.demux_min_length}": '', + ].join(' ').trim() + } + } + + withName: PIXELATOR_COLLAPSE { + ext.args = [ + 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}": '', + params.collapse_min_count ? "--min-count ${params.collapse_min_count}": '', + params.collapse_use_counts ? "--use-counts": '', + ].join(' ').trim() + } + + withName: PIXELATOR_GRAPH { + ext.args = [ + params.multiplet_recovery ? "--multiplet-recovery" : '', + params.leiden_iterations ? "--leiden-iterations ${params.leiden_iterations}" : '', + params.graph_min_count ? "--min-count ${params.graph_min_count}" : '', + ].join(' ').trim() + } + + withName: PIXELATOR_ANNOTATE { + ext.args = [ + (params.min_size instanceof Integer) ? "--min-size ${params.min_size}" : '', + (params.max_size instanceof Integer) ? "--max-size ${params.max_size}" : '', + params.dynamic_filter ? "--dynamic-filter ${params.dynamic_filter}" : '', + params.aggregate_calling ? "--aggregate-calling" : '', + ].join(' ').trim() + } + + 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.polarization_normalization ? "--polarization-normalization ${params.polarization_normalization}" : '', + params.polarization_binarization ? "--polarization-binarization" : '', + params.colocalization_transformation ? "--colocalization-transformation ${params.colocalization_transformation}" : '', + (params.colocalization_neighbourhood_size instanceof Integer) ? "--colocalization-neighbourhood-size ${params.colocalization_neighbourhood_size}" : '', + (params.colocalization_n_permutations instanceof Integer) ? "--colocalization-n-permutations ${params.colocalization_n_permutations}" : '', + (params.colocalization_min_region_count instanceof Integer) ? "--colocalization-min-region-count ${params.colocalization_min_region_count}" : '', + ].join(' ').trim() + } + + withName: PIXELATOR_REPORT { + ext.when = { !params.skip_report } + } + } diff --git a/conf/test.config b/conf/test.config index 8f96a616..5bd688e8 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' @@ -20,10 +24,15 @@ params { 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 = params.pipelines_testdata_base_path + 'viralrecon/samplesheet/samplesheet_test_illumina_amplicon.csv' + input = params.pipelines_testdata_base_path + 'pixelator/samplesheet/samplesheet.csv' + input_basedir = "https://raw.githubusercontent.com/nf-core/test-datasets/pixelator/testdata" - // Genome references - genome = 'R64-1-1' + multiplet_recovery = true + min_size = 2 + max_size = 100000 + compute_polarization = true + use_full_bipartite = true + colocalization_min_region_count = 0 + colocalization_n_permutations = 10 + colocalization_neighbourhood_size = 1 } diff --git a/conf/test_full.config b/conf/test_full.config index b8f7cf2a..2690a944 100644 --- a/conf/test_full.config +++ b/conf/test_full.config @@ -14,11 +14,6 @@ params { config_profile_name = 'Full test profile' config_profile_description = 'Full test dataset to check pipeline function' - // Input data for full size test - // TODO nf-core: Specify the paths to your full test data ( on nf-core/test-datasets or directly in repositories, e.g. SRA) - // TODO nf-core: Give any required params for the test so that command line flags are not needed - input = params.pipelines_testdata_base_path + 'viralrecon/samplesheet/samplesheet_full_illumina_amplicon.csv' - - // Genome references - genome = 'R64-1-1' + input = params.pipelines_testdata_base_path + "pixelator/samplesheet/samplesheet_full.csv" + input_basedir = "s3://pixelgen-technologies-datasets/nf-core-pixelator/testdata/full/" } 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 zcmeFZRal$t)-Fn+z*nS{Vx>rm6qiDAOL2F1cMtAuDNvx0;#Q!zyE_zjcbDMqmSlzR zn{)pEI@tSUUwdu2)&Y>bJb7fuJ?=5a1EER^lGqq;F_4guu%)HMRFIHRN0E?_z5hZ+ zJaJ}X&O!Wm=At4gf>b&}x`%l4+)`Lx7zwEYjQMDcig^FRNlM!V3F)=#)7P^V3xFpQ z(!7JTn6R3s!6EcTteK|QPPjx@DDOv5T2*CXB}Z%z@|SP-DsObzPh`FaVcdV&m0)j; zcZ>LN@}*RhsyUw6to^1IV&KrBgSL*D84<+V=b92tLUGmkCzrla{Dr!*h^X~IGAQjM zyD9lfz=>mTe@ql{QdCq_QdAt=(BA&2YBUsY=dfzD{{p(Xxaz)h;YCF8?Ul%1e}5}@ zO@0yZuh)nND%kn8|Na%lH#NLM=KqYOnC|MbCw}whr}=*yP7H-Y`-r9qwQ2rq9Dz|0 zBdN65Kl4A$DgS>m=QkV7|7=EzGh^Yu&HaDh$NCi3wnS$c$@$FVUp#HFss7?l0LJ~{ z!`SL7tNPPP=8^Kq8)3(i@(qbit!IaRj$Duu3h(VXaI4Sdu3~_@H&ak|A1shtFJP;$ z&Ff|ziaT$FS{aiU@Te#m;Cp!+I*IbJ@XxAqIeeeH<$>FQ&-YdyTH@a_&X?%>7*prF zp2!e%;=M(CLssc(k6U1h(+Z6N7fk4b1$pU zx+k}@k}uu*?&UWT+g}Y#gV?3_XQkIe!hs%Suq9Q))|Tlh`Wr-J#)v6)bNt9IQZ-?zd%Hw*=ZrCzD^f-D3r^0KBi$+ip$`A6Mk<3rtrZFNxAf zKk90T99Gb#t7ndaGJ(*jcpaOR-2zFV|0MH`0H4>cX|8kH-A>yB@PzO5QPgAAeG<9~ z(7IdVikhJ^RFhx&6*~Cd*30U>;FKs>ES%nYuI$%8RM=1({ChUX}X7!Wu zAA=&In$O5ezi+pM8LtJ8`oW`oa28+E!&*f>9{W97;k4XXkIS^H4+UAGvZx7D{UOIK zH$}ZEkpj2NC%)GxA>My-R{)`xdTyO1fcg{J)!T^@lJhkw=vrQzj&$^Qa(I7Cu2xl- zg5af(2k=sEQGeBmBNF1c9B_MFCIG7eR|`T^)>Jws({-d$>S9rNoIs$o1qKW1U(s7gPai5(qrX(&Um zwy;AI@AZ}{%d9#&PBP>zwc8=%jgWWGH2jQp`DWYPw4k^T`^Nvelzg_m4tOygvshAx zSic)*_56B2$iwR{sdtKA-$NW8Cffewvz4#abf1JwCg*y2X*Lu~6edkmydt&um&!Yh;0Fgz!I z8S zXW#cIlDgIR7Kgd*mV>IL1+VdR*KujmVe6Bnrwi2`nyj5h(N`umHB#h26X zt}BBFa)TAfq5C^R?mPC5nk4!GljuO$+PG#|*B4a_2>^!?m-qb{I`I10^!40&Ah?Xo z5pt;rAZdrM_}>Q86li@(J8)D#f?(9Br`@U}FA1>Jx%%}~}bmH|q8K|Y!jaNAu?dYM~6 zRZJc^eBV;Y!Mnx?kn&2<<#2q|Pp)+P>ZBPmqA2KkX?Et2s&9LqBzZimIWVsmGYatA zRXt~RY=fjB;A5x~rSrZ2e#S!_7>vCGqC{9lj*|V8LTb}g!H@mpp{+Rn_v>x&(6H+J z7}nKf@B4Ld%Z-a7|M0=og<;D>XSx@Y&lV$4Ekin}o2SXK^<>^M{r+%K-I&?XE$nJSn(xJK4qrH|bnqfPU>4jm=e=x!oc#?Jke&g(g- zUucQtw<$SVY?d~P}!t-c2Lo8mx6d`@70 zvP5TBSUX%%C7-WOwciMN4WbKqP5B%ow3f{Z-jx6kgNKYV|^tpbL^<*qZ-A^30n?FBY*Hn_q~jp%0Mg-<>UCF!!;rL{!Y{b z*3Cv>f1?;licgf`G`bG-zLl-3R|wc#Q538g0z$S#C86oCbHSjNy?ANChiOIVH2rMI zG5nGlT3Axtm$CYA3AoOV^jpuMy|ROZ?T(T^1UI_*!$t2I@DM>^@!2%tQ*2Px;zGGh z02fo5-BK-N3cz|cST76mXYkO_egPK}#MwY7cUixalk{5k7n=LGIBj3hTJKhyeXzl~ zGo3fkBcT7$3Q6oSx65M@pbZ+YC;(b=HY>1%!!mZp6Fqznq0rpI#0pXZU|dVnIlk9-%u>~`h}VhYjz zmPod{6t5ndj-zKD=!WOo(!>9dq!*2ld8_8dca!LG1x9m|yPCUXkoxbbV)V`B^QlP* z2QLUMxOI2m3%(x6c>7K);Oa-%C(!K#N~N9Ef%3qRq9J)~x4KpV>itdW?%7A43LDIa z8X^^jrZk!ojDyDSMXww70zLApJntoe%=xcBD#D>RDy64nfaU_M6Z)d7V4v3O7+UfM zI23&xL2-PqOi$oj<6nQBorePGYWBHH+x}3PF;m>1({p~`Te}(*tYP8JcKw|ZaIa3W z5|KeaW+a1}*~V9jOh9(L$~YKYYcNd}*`l$FOU6yA(HR-(cSZ&9*~&v1R}oErionDF zkmE|SIb~(H=VJ$DZ4b&-CQ)fO@a_a4)*zSnmv493+6k&S(%z0p_QJ>psX^O_V9lhrb>BAr9 z#!w93wGILaXkvaRP39@H;n)|GB8ih{1e-l>kB{FBn1qGHL%+#NzbvY3$Xf&5Ir5z2 zPG9!I*3-qPiSN%$8O#PHBV)1VD}P1)O~7Dhj2?72@pBcduzphsN8H)`k=p3Wh%;_$ zOeXLMp7o@Qaw@rwstN}`?{)X08s5C`DQlRw*eDrX7{@P}7d8#NUz6uvKJSkcQF?Ne z6pViyWiT|=e=Doa?LjcWpUG)555Bnx)chgcgWJ97&2EQZf!xal z)p2nI02nbGF^RF>u>$hlk&33=WQ-^JoI>Si0u8 zV07Zbz#>r^qAXD{lBu!00RKml^p=Cv64=~UMF`M+kogAK za9tvbFb_5Czmu~*!Wcf7X4}nlOhFn>z@2UYs5e8zXiDYQ=Ox))S3>&zy2o(u2h5!JvYvSsLq$lAJ%%c;J%Lb@e5mEkCW z?eZ|Dux0i&Si?wGLD+e^#G`KKbCx{u6gsr?6jUM?pE*3wAGiPuHc1MIvY4|WVosn|)%172v_ zuJ9qyLTdW=-$|n#8!G@V$$7Z3oifYzxs!m`vv;S}RV*&e|L#YrvkJalcR(jP&|ivp zdX?VXKmoSP&tSH<4&P*Xc=vJz77}8-1B8!d0cW#BxWLd8o=iJfUfU`0+(QVsx$4{8 zM%dD+!cq1`U^-K(q~!|)T~eLAZia5FB+I+)`mCM=ATeKEa>FyeeU0P0N(2$?H5_a% z1c?1K;t}s!d86fx%Dsml&FIN>)%>u!tJSay-_BD*KV3b8rOY0MRDF}8&W3rMO8Cvd zq4No{`UQOiAyeW&=;8TZg&{D6<%2^Z z!|qE6iY8+BPguq9y#O>n~H+h-giBAsF%%~f&;2z zHSJ9+elB|j$&@GebI=dtreMMQ&ghri{%!G?7SS%=%2G0KqHH#RkD(za3ny=Hi$(=p zLGvS3B|d!WGOoC}J8#If=~Y0uQMxBB0Dao47Ri8W79ysyRyY66Fcmx+Tm-DB zhy25cx=95+#qc?ToUlOnSSf2{HM2o=*VzYQSjU+-RrVoQq-g{FF4Zg zE~D2d*8doXY~?Q)$%+d%R^R5T*Ja|j(efj$qMbfNU$|`D4f(?#^kdi{t)k*vJRUdL zlxcwb4m#}66CTp`2n9CPSQhv#x;!Mn5l~6yO6GGaT9+UCvj-#Cg^PfUgy(9?6bFXL zpNb`ZMW&HB#=RloUUl{4T*WAYN0#{>9S=giO>#Fy+5dV^K*r~FnE~_`y9;cG`R|Z< zoOm=C`0i!|j9q)!?A~%82Uz7BM!4{L-9s2&lDz;lp6G%f*Hh2|EjuF*ZTdWkb~fij z6_P^E5528|&KH1y9o-vpP$5xCn_I}+iK{MC;6&BY+8Fs=m!-n;b%SD?b{UHjMD=vl z=|HehRp36=l!l{Nb=j)%E)c-p>$yu+7f<0NCv?~F0Cqtaf)`7bVV&u>BhZse9N&i(A3$x{)K4e9C)`q;|M{`52%Ol-Fg#F@RhIVC{{nI!7gqddBASWD!btp-(BBw zy3b`l5s_nR2<)6q^Y+vd*eWbZ{zSIO{;S}l*pU8|lJn$|PvBuKUqx7+=-R09e`&ej zfx{|HP3Z%AGj5jsR!`dCO19@yQ~>yvW;*!(X7#4zWHpB}1(BEfJf?t!{10!5-z-JJ zQX-eGqE>l9_7%!}cZXT{YORv&H@6?!P^VBI%uu6V6=U2bfK z-nUhXzIRgAtSRD^1sRqBr@J>`*yP8cp7G0o-9a4q`1%ZFqkHR25(W(nc!>F8Rev?+ z2p#E#0X>$-*t{U__3WWm|LRC(^ku5R)_I#q+`)twhDXu$zH2tK)}SV;F#zE0@2 zg?0JR?v@D90Hrb{11&%10Dztc$r&o2>~^QX>Hg!vk;( z#!o$oW+d2aJ3E!HTRLmi#ku04&fiTkl>~TQ=DSMO6nU&V@0^f&T|`G#xX*^A`Jd~q zJ}%Ne)$q(Ccl0IwAN0|Wt_{zb<)PfG{R#-xbxpIXTB^TSg|zin6u zSh5q{v1O+fzBxjo@#?QW1SARF$04v2_)CFv*=aWK_yOuc#x(QJ=Ett;&FUqs;sfxq zCIB|&O^N=5HrZJJV02Sr(xjsQLk19jeTIiI@V|PQ~{$B-zwT*x3pGviT$60%8 zCF!>divF-$D){m87X$&aRcy6G_WdbycC+L(o9?%>1B5-W24q|AHU&J)RiTV0+o^D# zT@WW6EHpXfOd)pp&5q{s?`;3C`S)0Y*FJT?+vbC9;6s04-B?QK(}F_(bAgv9`a9z3 z6M28iWc~@r|2+7AU-9?vZT>GSHUD2*%^6Xwe{?i5`rX!MSZEWDhZAtQj+cwo7%6a? zSLc=zv`#AoZy(3i_dRGaga;nDKI!IPS|BN(j!XSr`)E`qYOKB0Wf*X2oba7V#{I5) zk=%1laIo%)G5j-l9>dPfyf>2it=GmbYZG{h1;(^o*K*Rh-V5gQHTu_th|#qnsfD#z z@N=S0eaEKKL8ivW8}}v!0nvu1qUJx#E)FXw=}JTjohk=?^dIb7E2n>IU)7z^yXKN5>F_agCUG}=!;#J&CZeBX*c`T6-#zh=YC zndemokzv74zo3(!G~OKC6xP?%!8h!~ZNg_vh8nM8JRn4`F)hCQXDep(R~_D}48xI{ zy4B6+;dRhGlsf5MLde2Kp_-kt&0xj4>3R zhquhEz2pj?@1^q#2>W9fj)Lo|e>Qu;f1NoyY^u>Q{MwRUOwH>_4=8z=h;cgr9=^=* z?xGoVzo&BQKig6XySlGE%#IRELH|3M`R8%$1||7_>z7ob{BH;Pi(>l!kOxD5aw~vz80WD^z{{}CSKKBaMsdz*X zg6)>mlPEl1p-B3iKpQu{PzB-uPdhWO{u5Cs7TY70bf2c^q^bito#+l%nrww;wH*q9 z9^AY$9%^s&xgT$p@9X{}TC>IZXEuYUIBot@Zd+L=dt8Ib>xM9s`UCq}w*sdfH-c>$0J>4`lZ*J!KJWf!Y{KJ18 zO*eu+eRMMb1qB7s`&Lme!UCS%p^vnj9Q2HvZ-t@@!T%j}87W(a>}+UdXigJcB$4Fw!o$e+tk>*3^i~SJOF4C(3^hQo`+k zUHc7b-*l>D~O}$@DWtwNsB+WB=I-1wY3B z)aL(26^f6bcMLQ!gU#$v8OoT`dO;}%ZkQ@+oL)F*{Gtk~zA0_h*@O(Wo!zyFkK)04I`B2uMsXC_I zU!z7c!RhYhJk8D~`gE!0=iP>pQ1&?a zB!)_?vR+2ekCH#{3X(;%F)T=$KuNw;e-z^P__rCKy7~zHo4Nd6PA>hsiCK;Rkg$~!x* z1oZ}mhF_&o*#{n_Gl6O4`E5MaZ`8*?L(y-2KH65;x&P}1M}c~Nt(r)Z&EUbuGWgb` zq7h*-WJ2sQ%Gao%mg#yU&%gCFZGLyHw3wSiqxS1=ra7 zhfVM<(E_q=xL(ERoMH|F6v6KtK8Lk~#`=qi2h8)gZN zpyUxJ+PA&F!GFW~&t>#~6y)_7(HpW8GA#0Jj)JnO8cp|o$d$>=w7`eLBf~3W4w@?I z3W{(h>8dd`6ru&FGa6{(H&J8WF#<6i9@Pa!~XE?j?N_|er(s~ zoQnPL+2qvYPfp!VWX_=|XJ`LT_K`)B)Hpg6`5Jj1h*XuWGaakV^^5GAL8 z1<+W`_)7+Y9;rgWz7UMAb3^H0$qF~P}9YX$|(l68N)eOTs+-Qe#c_pox#H>9Hd=PVCb?037 zc_zYv+uwJQsXssy&e|r6osX(3gtZO%F+;}1ED_{DN(OKVGEW(OEgOHy`z;Y7edqUg zys_WA|GWh3p==edvj;U(>@0s)K za$RXeodzH`gT9(d)4eY`^}kKtGx+twpn!(!VK&>E+`yXpuh(v|Wpi(xTH=d7h;v5M zR!OVLI0!YPL@|EdV)~92GWb13R$pt`GEOT?Qb3x8FL#*Qs?^3PjDp30bwiH;|K&TnmI{XS_VTuIA^Xnk) zsnw>~BEwGBj$xwjGp_8r=GxpTbLY>4v$JC!E~~?Hz8N?^Ndu^6cq%-o7f>+JKkXTPIu#nTp1%Bf8oJEn+~#k zN$lGfo=h(}gTm<=NmRx#HWubhurWa9!z_j0mirhQKozcX)o-MCKS+U+)JmbYr=O&@ zqxm_+j`#c2m5$2FzBZCB1j*|si#Xvy3^!Fg04#vUxMh?he_JB87X1Pu^@Js}Al%lvRC}tTS?07wM`*eC|2fyacbu0nu1^PZ>k4AuS6p2pa8h}3!lXb z7r_gjW1#8@siJi4P7|_X)OLVfrXKQ1D=O4MjItz#=B=8o?40SD-1vq-P6EOgSr>U~Z9S?C>u(HvJCbLw4qC ztop8mY8GXcZ~_~n((s%NJy11JVUEbad`sQH;>i#eZ%GutbswFi`1%Pt)KH$zcr%DNDbV>DfG#DbOi8HOuFJpN&gT2;Iw>eOv}O#o z4R?4w{O&%K5Vb8@eB}{yeS>?T6RABQWkJM`{;QZIfGnGhyGq@IV*-6knvpw|-p9>L z8_Al3s`00QS`2aOB3S!KJ6PoClJHk*^e<9Ad|2h$i@?&-W7MU;?%kal^yz-r<+G^1 z3ePEaFu4kt4B8S>_b4Tog*3~bz8YIp2aKD9eM`&~kMoKBWiRy9>3*ex{3JikcJ}Fb z%F|>X-1Il#2ykyN?PknmKS5VQ>R)oG6|@i!HKt@e_*{`e6InENts%!y^}F{k;`8W< zOrqN3znhy>Y9D=`Y^b~%VAL%YTfa)04G_FL@T75=u?EDHHkKYcahGyN8oqe$#fkN- zL8ZX;gEHG~1>0NUj1-Y$rY3Fo=O%*5W=W@_?&iwRXu`HWXo{>Xyp@Hhxe!iZ?z&aD z4#nffwZ_Qzzrns#X;7I)Zjo{zoMhLa+xqy$Lg_DE<4d}V4`)a2&!Cd8UrIb`$7hQ~ z=rk3pL_>uShe-#nDQLLow4nimpL(^LXX95){J{Vs+#}lAx7hhMZKMAmM z@F@}Uj3|<`r$;{V-DHE@vA-qpGrh)EZ5nLHWL(KsXXqLi6M2tSeldQ*-*^A#+2(TN zh$e0D&p8p<0o2}CZ?Hhg*9_EEM8poNPOG1Aa2MN4ah2O+F;TTtw>uGr!H)Gh>J2rH zXFLlZh85r9yE4=+UxGnHePi3;6^A7(&UUa7E_@yVU?4Y_-Fl<@d%Quv-C`T%DQ|3``&(L^MPUn-q&sCZ zIsW1CvgOQcUB>3?@6N76^$4n~f@AH|@$r9Ikk}0E6n$%+>4bIhw}NC?o0k^zHGQCq zxp%a2gBW2V&eD+hK-KcNgv_rD{9j9$3M3nTudV&qOyVhqdTQ*bNTlgAZR#YREPi=I zfkqQU1+uZ!r~ zapTZw$fVK7r9vJg-B@Ml62+w5DO-4xdbOHw%~CT+&0R2hKK6+*aN;}#xCcXC8`-rj z#;6lm-Bt>#;*zI)V_WakvCNkFRBe|M;i6nIt8_Sqf)GD$y4Ebet;_EQ-h36+-}Hwi z*G}Fgdp~G<3==(#xp-|EIBy&Mupf-xtXVY1eM0f9a^eqffibJ*| zFeh(6S1byR5ldEw}h82UX3!s5W0g3eUd%q+f2x+?Q9?AJ$OF(NzRM^O0ul)+F&srRw4rpP9NNM zC+6g5Exi}AgJU;t`_6WH(mrCoZ3b*c%ri})d9Ihd2^NoS7gwNk za5jd{cQ*6X&O$wBl|Mpu%G zfG|V3AiCEMp;(0hIdu;xI$DRF-Q+5CzoEklgGPL8%wa`qXo-C(ae{e2;oprIn(;Y@Rg$=FML#BVB8#k+Rsl+tItuyeq~L*%@f2v&d2@{8TD zM4U=vKs?;y0D1T4AlMAjt@pZ4y~b5b@2%c%N=e{S-}#nshr*)&pdIT`hWpYx&!zQe zjQd!}?*!y1TmKrsOhSFkV0&vQpSUeJ3^??Yn_vhJE!C@OqdrT8p(8U?oK zh4%j8J@{vmM&n5g*a{t_Z9=H#&%@^O?8k?dY_{BgDp+AGs7eel>=}gdqYj%0RVi$( zsT+LAc6Q%axVf$PzQhzC+57B3hfK@;tUU~41cfVo{!Kj}NUffe)J3ZeQ!*z(w z>Yf&dPaI1$fq6}(4-q#NuR(Tjuk+8QT?>!Z%}?WO-j#B?w@`gzPQ`$y$X_?XzFGTR zq4hP-)!S%(Z9A9kK-iSIk7=8q-+i=TuFWi-ym*_>eUoPt=U@$W&Du0xolIbxFcuds z4|Sb9PnETL$71WkID^fx}bZ->Qs>AzZ!# z)c%0bGRnt2(({R^w`7S zQ7`JPVihS~JElzLcg&Jdd}{iZFO;O*+4PfZg117qLHd0iCL@#g)Gf`g%DXKUr@=Yy zaQwqceMb;fi5;K|T|B z`ANT$P7xM#`E`EtzTje-z>i*~rOcq&w0y=+5+UNB=7_ZR+xavh$!gMiy9+D2V)I5) zXmTO4S339dDqho((|)vpY7L~`^o1fNL?K(C>SAW7+0tP}5O6WnD~RdrArPuwYBrFn z0t9YDTYbmUanM0m#&K`|H1tT-76<{b^1V|*ZWLDqsJ;U0k+kIi?txp3rqAApczcKB zo-dSweIHV#%4W#2=aTn${B1Sv+UK<<0kN}qKR$ZB4bCuBx0k6_9x~vVoKV+ z&(}WQ=Jfd5nXXxN3SCvQlpXd}JoI-|b2eC!WgJd}PGeu$0!A_7d^#zIInYxi2_?*Ae@&^G z$PDnH`PPs*7BM*M79tWQTA8;<+CjnjahNS z)TAw}dr@;mwFV9luiSC7%1XKG3xtoE5sB2~ygqfPHmK?D`3S&-UbuAZDCpu%&f(5$ zZ=tm6>C+h!4NRlD7~_9!xK|Rw7kh7$EdN8&O|Q*;*ZCaD z4jJd=S~Xv{DiBm!zi9n!b0}i$`%OoeZgb9z_M07f<{%w$=I`(F7_&6GM`$zITB8MB8N6Ln8`vU|&v^H% zzlI7CK3Iehb#r8caRv?DU*F)1A3F@2*T^{A{zQd`>S=|uUQsZ&KA$%6(}JuU$Osz{88r^rp+Wi2e{`0T9QV1?p4 za~L#5T~1-Vhe|5^Tiu~ICc2J`73V*Tefm#B~4=bveHUwyMjMBL|;cX%8)=8 zoFo#i&)!T+)w-21=sR3;km9s1*flcnP%RDC*F=Tm+O94aEg_pD%leF8vta2*Az+P5 zADCIRacf?WQ5yN&B7R1q%5=w5DPM1NI*8FkNSjOkOD-biO1n=>Yb5tgEnr6RP3U8p z5Y3K}dS=;@c)-P$KCeSaK>{xIyvtA`@hFg}FUHmS*FTS48)2aw_y`Ge$ znPdOp^4YsOOpB;eHiXpO*`L}sIyT{J3b~>{{`Hm*>q&-6fwqLN*}Hm*SJZr0npYDr z?=PMOu;BO2GP-?w@jR;0&XjsqFWugHNL(Ya_7gUH7>j4_c5%P9E#H1=OZjV-#{l0u_)~I>-0fUVyiYkdf9XWUa zM1Xd3e6i;hJ1jx+30m4J7u2Est`0T%J8*(f$K%%KjgCZsHvMO3bvqCnPh3H|?xQma z4rSbdWu=z(`9a-Vy*y?Xf&ekh=h1@{dte9L4d-_~uQ60YMb*`Oc8Afv+%Yp?VF6=U zBVxaZSM8}7nHB{T5Ec5;B(df4+%q?_-G3OE5S=3EkUl8VV4L_ckv;LF(c9jrKJ0u# zcUAY~BU|YBk+VVlfiscRFj_~_Mj8R6yWmfL^BTYEytrmUr|}&luY{yq2gBhj`^c5Z z^S(cSkrU0?2?&(}>)0c{^rSVWrQMSY%$yc?UR!hrcSNmq+0&B!svJ0?5C~GA8}c>6 zj3N{*t4OCfKpu_^evK+tV7fprL3p;sL9(|iBI7Pia)v6MwpCc}&x=Mz?g403Xl<e;viOll%5G z0F13z2bFa2Hzg%Djq*8s(f={4DAR z_VYbC*mT3k8^YwXI%jshm2GBx>{5ieUdx1_gq9OvdT$5b@dmgLq=((RU{ZK6<-f+T zm}DK>i(S6*_7hf2xOTX|1-7HO4%Lop@E&^79{! z@9zg?%&B$Nbb{u$4&`iUl7ECne{W^Zt*<`qAxIkdiPu5@9OKNSobC�)v~C(0C)c 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 M)78&qol`;+0Fu7JivR!s diff --git a/docs/images/nf-core-pixelator-metromap.svg b/docs/images/nf-core-pixelator-metromap.svg new file mode 100644 index 00000000..a6c3e37e --- /dev/null +++ b/docs/images/nf-core-pixelator-metromap.svg @@ -0,0 +1,246 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/output.md b/docs/output.md index dc8682df..5590fee4 100644 --- a/docs/output.md +++ b/docs/output.md @@ -2,58 +2,238 @@ ## 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 [`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) +- [Compute connected components](#compute-connected-components) +- [Filtering, annotation, cell-calling](#cell-calling-filtering-and-annotation) +- [Downstream analysis](#downstream-analysis) +- [Generate reports](#generate-reports) + +### Preprocessing + +
+Output files + +- `pixelator` + + - `amplicon` + + - `.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-amplicon.log`: pixelator log output. + +
+ +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. + +### Quality control + +
+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. + - `.meta.json`: Command invocation metadata. + + - `logs` + - `.pixelator-preqc.log`: pixelator log output. + +
+ +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 `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 + +- `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. + +
-- [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 +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. -### FastQC +### Duplicate removal and error correction
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` + + - `collapse` + + - `.collapsed.parquet`: Edgelist of the graph. + - `.report.json`: Statistics for the collapse step. + - `.meta.json`: Command invocation metadata. + + - `logs` + - `.pixelator-collapse.log`: pixelator 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/). +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). -![MultiQC - FastQC sequence counts plot](images/mqc_fastqc_counts.png) +The output format of this command is an edge list in CSV format. -![MultiQC - FastQC mean quality scores plot](images/mqc_fastqc_quality.png) +### Compute connected components -![MultiQC - FastQC adapter content plot](images/mqc_fastqc_adapter.png) +
+Output files + +- `pixelator` -:::note -The FastQC plots displayed in the MultiQC report shows _untrimmed_ reads. They may contain adapter sequence and potentially regions with low quality. -::: + - `graph` -### MultiQC + - `.edgelist.parquet`: + Edge list dataframe after recovering technical multiplets. + - `.components_recovered.csv`: + List of new components recovered (when using `--multiple-recovery`) + - `.meta.json`: Command invocation metadata. + - `.report.json`: Metrics with useful information about the clustering. + - `*.meta.json`: Command invocation metadata. + + - `logs` + - `.pixelator-cluster.log`: pixelator log output. + +
+ +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 find and remove problematic edges. +(See `--multiplet_recovery`). The information to keep track of the original and +newly recovered components are stored in a file (components_recovered.csv). + +### Cell-calling, filtering, and annotation
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` + + - `annotate` + - `.dataset.pxl` + - `.meta.json`: Command invocation metadata. + - `.rank_vs_size.png` + - `.raw_components_metrics.csv` + - `.report.json`: Statistics for the analysis step. + - `.umap.png` + - `logs` + - `.pixelator-annotate.log`: pixelator log output. +
+ +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 metadata. + +### Downstream analysis + +
+Output files + +- `pixelator` + + - `analysis` + + - `.dataset.pxl`: PXL file with the analysis results added to it. + - `.meta.json`: Command invocation metadata. + - `.report.json`: Statistics for the analysis step. + + - `logs` + - `.pixelator-analysis.log`: pixelator log output. + +
+ +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 are performed: + +- polarization scores (enable with `--compute_polarization`) +- co-localization scores (enable with `--compute_colocalization`) + +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 + +
+Output files + +- `pixelator` + - `report` + - `_report.html`: Pixelator summary report. + - `logs` + - `.pixelator-report.log`: Pixelator 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. +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. -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 . +More information on the report can be found in the [pixelator documentation](https://software.pixelgen.com/pixelator/outputs/web-report/) ### Pipeline information @@ -64,6 +244,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` - Parameters used by the pipeline run: `params.json`. diff --git a/docs/usage.md b/docs/usage.md index 20bcc59d..f6337e55 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -6,58 +6,137 @@ ## 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. +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. ```bash --input '[path to samplesheet file]' ``` +An [example samplesheet](../assets/samplesheet.csv) has been provided with the pipeline. + +### Format + +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. + +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. + +Below is an example of a simple samplesheet with two samples. + +```csv +sample,design,panel,fastq_1,fastq_2 +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 | 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. + +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 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 title="samplesheet.csv" -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 +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 ``` -### Full samplesheet +### Relative paths + +Using relative paths in a samplesheet is supported. +This make it easier to relocate data since you do not have to edit the paths to files in the samplesheet. + +The default behavior is to resolve relative paths based on the directory the samplesheet file is located in. -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. +Given following directory structure: -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. +- data + - samplesheet.csv + - fastq + - sample1_R1.fq.gz + - sample1_R2.fq.gz + +You can use following samplesheet: ```csv title="samplesheet.csv" -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, +sample,design,panel,panel_file,fastq_1,fastq_2 +sample1,D21,human-sc-immunology-spatial-proteomics,,fastq/sample1_R1.fq.gz,fastq/sample1_R2.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 (`_`). | -| `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". | +Using the `--input_basedir` option you can specify a different location that will be used to resolve relative paths. +This location can be a local or a remote path. -An [example samplesheet](../assets/samplesheet.csv) has been provided with the pipeline. +For example, using the same samplesheet as above, but with the samplesheet on the local machine and the input data located on an AWS S3 bucket: + +- s3://my-company-data/experiment-1/fastq + - sample1_R1.fq.gz + - sample1_R2.fq.gz + +```shell +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: ```bash -nextflow run nf-core/pixelator --input ./samplesheet.csv --outdir ./results --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. @@ -90,10 +169,11 @@ with `params.yaml` containing: ```yaml input: './samplesheet.csv' outdir: './results/' -genome: 'GRCh37' <...> ``` +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 @@ -112,7 +192,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. :::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. @@ -161,6 +241,12 @@ If `-profile` is not specified, the pipeline will run locally and expect all sof - `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. +:::warning +Since Nextflow 23.07.0-edge, Nextflow no longer mounts the host's home directory when using Apptainer or Singularity. +This causes issues in some dependencies. As a workaround, you can revert to the old behavior by setting the environment variable +`NXF_APPTAINER_HOME_MOUNT` or `NXF_SINGULARITY_HOME_MOUNT` to `true` in the machine from which you launch the pipeline. +::: + ### `-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). diff --git a/main.nf b/main.nf index 268be7c8..759cf0a2 100644 --- a/main.nf +++ b/main.nf @@ -17,23 +17,10 @@ nextflow.enable.dsl = 2 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -include { PIXELATOR } from './workflows/pixelator' +include { PIXELATOR } from './workflows/pixelator' include { PIPELINE_INITIALISATION } from './subworkflows/local/utils_nfcore_pixelator_pipeline' include { PIPELINE_COMPLETION } from './subworkflows/local/utils_nfcore_pixelator_pipeline' -include { getGenomeAttribute } from './subworkflows/local/utils_nfcore_pixelator_pipeline' - -/* -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - GENOME PARAMETER VALUES -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -*/ - -// 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 = getGenomeAttribute('fasta') - /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ NAMED WORKFLOWS FOR PIPELINE @@ -57,9 +44,6 @@ workflow NFCORE_PIXELATOR { samplesheet ) - emit: - multiqc_report = PIXELATOR.out.multiqc_report // channel: /path/to/multiqc_report.html - } /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -81,7 +65,8 @@ workflow { params.monochrome_logs, args, params.outdir, - params.input + params.input, + params.input_basedir ) // @@ -101,7 +86,7 @@ workflow { params.outdir, params.monochrome_logs, params.hook_url, - NFCORE_PIXELATOR.out.multiqc_report + [] ) } diff --git a/modules.json b/modules.json index 6cb9bbd1..5cf0d6b3 100644 --- a/modules.json +++ b/modules.json @@ -5,14 +5,14 @@ "https://github.com/nf-core/modules.git": { "modules": { "nf-core": { - "fastqc": { + "cat/fastq": { "branch": "master", "git_sha": "285a50500f9e02578d90b3ce6382ea3c30216acd", "installed_by": ["modules"] }, - "multiqc": { + "custom/dumpsoftwareversions": { "branch": "master", - "git_sha": "b7ebe95761cd389603f9cc0e0dc384c0f663815a", + "git_sha": "de45447d060b8c8b98575bc637a4a575fd0638e1", "installed_by": ["modules"] } } diff --git a/modules/local/pixelator/collect_metadata.nf b/modules/local/pixelator/collect_metadata.nf new file mode 100644 index 00000000..6f0f0ac1 --- /dev/null +++ b/modules/local/pixelator/collect_metadata.nf @@ -0,0 +1,79 @@ +import groovy.json.JsonOutput + + + +process PIXELATOR_COLLECT_METADATA { + label 'process_single' + cache false + + conda "bioconda::pixelator=0.16.2" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.16.2--pyhdfd78af_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 nextflowJson = JsonOutput.toJson(metadata) + + """ + echo '${nextflowJson}' > nextflow-metadata.json + collect_metadata.py --process-name ${task.process} --workflow-data "nextflow-metadata.json" + """ +} diff --git a/modules/local/pixelator/list_options.nf b/modules/local/pixelator/list_options.nf new file mode 100644 index 00000000..fe59c369 --- /dev/null +++ b/modules/local/pixelator/list_options.nf @@ -0,0 +1,31 @@ +process PIXELATOR_LIST_OPTIONS { + label 'process_single' + + + conda "bioconda::pixelator=0.16.2" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.16.2--pyhdfd78af_0' }" + + output: + path "design_options.txt" , emit: designs + path "panel_options.txt" , emit: panels + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + def args2 = task.ext.args2 ?: '' + + """ + pixelator single-cell --list-designs $args > design_options.txt + pixelator single-cell --list-panels $args2 > panel_options.txt + + 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/single-cell/amplicon/main.nf b/modules/local/pixelator/single-cell/amplicon/main.nf new file mode 100644 index 00000000..bdd60d2a --- /dev/null +++ b/modules/local/pixelator/single-cell/amplicon/main.nf @@ -0,0 +1,45 @@ +process PIXELATOR_AMPLICON { + tag "$meta.id" + label 'process_low' + + + conda "bioconda::pixelator=0.16.2" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.16.2--pyhdfd78af_0' }" + + input: + tuple val(meta), path(reads) + + output: + 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 + + 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-amplicon.log \\ + --verbose \\ + single-cell \\ + amplicon \\ + --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/single-cell/analysis/main.nf b/modules/local/pixelator/single-cell/analysis/main.nf new file mode 100644 index 00000000..0ad705e0 --- /dev/null +++ b/modules/local/pixelator/single-cell/analysis/main.nf @@ -0,0 +1,47 @@ +process PIXELATOR_ANALYSIS { + tag "$meta.id" + label 'process_medium' + + + conda "bioconda::pixelator=0.16.2" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.16.2--pyhdfd78af_0' }" + + input: + tuple val(meta), path(data) + + 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 \\ + $data + + 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/single-cell/annotate/main.nf b/modules/local/pixelator/single-cell/annotate/main.nf new file mode 100644 index 00000000..c7a44e82 --- /dev/null +++ b/modules/local/pixelator/single-cell/annotate/main.nf @@ -0,0 +1,53 @@ +process PIXELATOR_ANNOTATE { + tag "$meta.id" + label 'process_medium' + + + conda "bioconda::pixelator=0.16.2" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.16.2--pyhdfd78af_0' }" + + input: + tuple val(meta), path(dataset), path(panel_file), val(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/*.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 ?: '' + def panelOpt = ( + panel ? "--panel $panel" : + panel_file ? "--panel $panel_file" : + "" + ) + + """ + pixelator \\ + --cores $task.cpus \\ + --log-file ${prefix}.pixelator-annotate.log \\ + --verbose \\ + single-cell \\ + annotate \\ + --output . \\ + $panelOpt \\ + $args \\ + $dataset \\ + + 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/single-cell/collapse/main.nf b/modules/local/pixelator/single-cell/collapse/main.nf new file mode 100644 index 00000000..17918e76 --- /dev/null +++ b/modules/local/pixelator/single-cell/collapse/main.nf @@ -0,0 +1,54 @@ +process PIXELATOR_COLLAPSE { + tag "$meta.id" + label 'process_medium' + + conda "bioconda::pixelator=0.16.2" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.16.2--pyhdfd78af_0' }" + + input: + tuple val(meta), path(reads), path(panel_file), val(panel) + + output: + tuple val(meta), path("collapse/*.collapsed.parquet"), 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(' ') + def panelOpt = ( + panel ? "--panel $panel" : + panel_file ? "--panel $panel_file" : + "" + ) + + """ + pixelator \\ + --cores $task.cpus \\ + --log-file ${prefix}.pixelator-collapse.log \\ + --verbose \\ + single-cell \\ + collapse \\ + --output . \\ + --design ${meta.design} \\ + $panelOpt \\ + $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/single-cell/demux/main.nf b/modules/local/pixelator/single-cell/demux/main.nf new file mode 100644 index 00000000..6a02b0ac --- /dev/null +++ b/modules/local/pixelator/single-cell/demux/main.nf @@ -0,0 +1,54 @@ +process PIXELATOR_DEMUX { + tag "$meta.id" + label 'process_medium' + + + conda "bioconda::pixelator=0.16.2" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.16.2--pyhdfd78af_0' }" + + input: + tuple val(meta), path(reads), path(panel_file), val(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 ?: '' + def panelOpt = ( + panel ? "--panel $panel" : + panel_file ? "--panel $panel_file" : + "" + ) + + """ + pixelator \\ + --cores $task.cpus \\ + --log-file ${prefix}.pixelator-demux.log \\ + --verbose \\ + single-cell \\ + demux \\ + --output . \\ + $panelOpt \\ + $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/single-cell/graph/main.nf b/modules/local/pixelator/single-cell/graph/main.nf new file mode 100644 index 00000000..96e56324 --- /dev/null +++ b/modules/local/pixelator/single-cell/graph/main.nf @@ -0,0 +1,48 @@ +process PIXELATOR_GRAPH { + tag "$meta.id" + label 'process_medium' + + + conda "bioconda::pixelator=0.16.2" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.16.2--pyhdfd78af_0' }" + + input: + tuple val(meta), path(edge_list) + + output: + tuple val(meta), path("graph/*.edgelist.parquet") , emit: 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/single-cell/qc/main.nf b/modules/local/pixelator/single-cell/qc/main.nf new file mode 100644 index 00000000..2650e5c4 --- /dev/null +++ b/modules/local/pixelator/single-cell/qc/main.nf @@ -0,0 +1,78 @@ +process PIXELATOR_QC { + tag "$meta.id" + label 'process_medium' + + + conda "bioconda::pixelator=0.16.2" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.16.2--pyhdfd78af_0' }" + + input: + tuple val(meta), path(reads) + + output: + tuple val(meta), path("adapterqc/*.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/single-cell/report/main.nf b/modules/local/pixelator/single-cell/report/main.nf new file mode 100644 index 00000000..c71b9749 --- /dev/null +++ b/modules/local/pixelator/single-cell/report/main.nf @@ -0,0 +1,57 @@ +process PIXELATOR_REPORT { + tag "$meta.id" + label 'process_low' + + + conda "bioconda::pixelator=0.16.2" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.16.2--pyhdfd78af_0' }" + + input: + tuple val(meta), path(panel_file), val(panel) + path amplicon_data , stageAs: "results/amplicon/*" + 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 + 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" : + panel_file ? "--panel $panel_file" : + "" + ) + + """ + pixelator \\ + --cores $task.cpus \\ + --log-file ${prefix}.pixelator-report.log \\ + --verbose \\ + single-cell \\ + report \\ + --output . \\ + $panelOpt \\ + $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..024bd2de --- /dev/null +++ b/modules/local/rename_reads.nf @@ -0,0 +1,45 @@ +process RENAME_READS { + 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) + + 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/nf-core/fastqc/environment.yml b/modules/nf-core/cat/fastq/environment.yml similarity index 57% rename from modules/nf-core/fastqc/environment.yml rename to modules/nf-core/cat/fastq/environment.yml index 1787b38a..8c69b121 100644 --- a/modules/nf-core/fastqc/environment.yml +++ b/modules/nf-core/cat/fastq/environment.yml @@ -1,7 +1,7 @@ -name: fastqc +name: cat_fastq channels: - conda-forge - bioconda - defaults dependencies: - - bioconda::fastqc=0.12.1 + - conda-forge::coreutils=8.30 diff --git a/modules/nf-core/cat/fastq/main.nf b/modules/nf-core/cat/fastq/main.nf new file mode 100644 index 00000000..f132b2ad --- /dev/null +++ b/modules/nf-core/cat/fastq/main.nf @@ -0,0 +1,79 @@ +process CAT_FASTQ { + tag "$meta.id" + label 'process_single' + + conda "${moduleDir}/environment.yml" + 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..db4ac3c7 --- /dev/null +++ b/modules/nf-core/cat/fastq/meta.yml @@ -0,0 +1,42 @@ +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" +maintainers: + - "@joseespinosa" + - "@drpatelh" diff --git a/modules/nf-core/cat/fastq/tests/main.nf.test b/modules/nf-core/cat/fastq/tests/main.nf.test new file mode 100644 index 00000000..dab2e14c --- /dev/null +++ b/modules/nf-core/cat/fastq/tests/main.nf.test @@ -0,0 +1,138 @@ +nextflow_process { + + name "Test Process CAT_FASTQ" + script "../main.nf" + process "CAT_FASTQ" + tag "modules" + tag "modules_nfcore" + tag "cat" + tag "cat/fastq" + + test("test_cat_fastq_single_end") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:true ], // meta map + [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true)] + ]) + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("test_cat_fastq_paired_end") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test2_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test2_2.fastq.gz', checkIfExists: true)] + ]) + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("test_cat_fastq_single_end_same_name") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:true ], // meta map + [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true)] + ]) + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("test_cat_fastq_paired_end_same_name") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true)] + ]) + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("test_cat_fastq_single_end_single_file") { + + when { + params { + outdir = "$outputDir" + } + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:true ], // meta map + [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true)] + ]) + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } +} diff --git a/modules/nf-core/cat/fastq/tests/main.nf.test.snap b/modules/nf-core/cat/fastq/tests/main.nf.test.snap new file mode 100644 index 00000000..43dfe28f --- /dev/null +++ b/modules/nf-core/cat/fastq/tests/main.nf.test.snap @@ -0,0 +1,169 @@ +{ + "test_cat_fastq_single_end": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,ee314a9bd568d06617171b0c85f508da" + ] + ], + "1": [ + "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + ], + "reads": [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,ee314a9bd568d06617171b0c85f508da" + ] + ], + "versions": [ + "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + ] + } + ], + "timestamp": "2024-01-17T17:30:39.816981" + }, + "test_cat_fastq_single_end_same_name": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,3ad9406595fafec8172368f9cd0b6a22" + ] + ], + "1": [ + "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + ], + "reads": [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,3ad9406595fafec8172368f9cd0b6a22" + ] + ], + "versions": [ + "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + ] + } + ], + "timestamp": "2024-01-17T17:32:35.229332" + }, + "test_cat_fastq_single_end_single_file": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,4161df271f9bfcd25d5845a1e220dbec" + ] + ], + "1": [ + "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + ], + "reads": [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,4161df271f9bfcd25d5845a1e220dbec" + ] + ], + "versions": [ + "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + ] + } + ], + "timestamp": "2024-01-17T17:34:00.058829" + }, + "test_cat_fastq_paired_end_same_name": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test_1.merged.fastq.gz:md5,3ad9406595fafec8172368f9cd0b6a22", + "test_2.merged.fastq.gz:md5,a52cab0b840c7178b0ea83df1fdbe8d5" + ] + ] + ], + "1": [ + "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + ], + "reads": [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test_1.merged.fastq.gz:md5,3ad9406595fafec8172368f9cd0b6a22", + "test_2.merged.fastq.gz:md5,a52cab0b840c7178b0ea83df1fdbe8d5" + ] + ] + ], + "versions": [ + "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + ] + } + ], + "timestamp": "2024-01-17T17:33:33.031555" + }, + "test_cat_fastq_paired_end": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test_1.merged.fastq.gz:md5,3ad9406595fafec8172368f9cd0b6a22", + "test_2.merged.fastq.gz:md5,a52cab0b840c7178b0ea83df1fdbe8d5" + ] + ] + ], + "1": [ + "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + ], + "reads": [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test_1.merged.fastq.gz:md5,3ad9406595fafec8172368f9cd0b6a22", + "test_2.merged.fastq.gz:md5,a52cab0b840c7178b0ea83df1fdbe8d5" + ] + ] + ], + "versions": [ + "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + ] + } + ], + "timestamp": "2024-01-17T17:32:02.270935" + } +} \ No newline at end of file diff --git a/modules/nf-core/cat/fastq/tests/tags.yml b/modules/nf-core/cat/fastq/tests/tags.yml new file mode 100644 index 00000000..6ac43614 --- /dev/null +++ b/modules/nf-core/cat/fastq/tests/tags.yml @@ -0,0 +1,2 @@ +cat/fastq: + - modules/nf-core/cat/fastq/** diff --git a/modules/nf-core/multiqc/environment.yml b/modules/nf-core/custom/dumpsoftwareversions/environment.yml similarity index 51% rename from modules/nf-core/multiqc/environment.yml rename to modules/nf-core/custom/dumpsoftwareversions/environment.yml index ca39fb67..b48ced26 100644 --- a/modules/nf-core/multiqc/environment.yml +++ b/modules/nf-core/custom/dumpsoftwareversions/environment.yml @@ -1,7 +1,7 @@ -name: multiqc +name: custom_dumpsoftwareversions channels: - conda-forge - bioconda - defaults dependencies: - - bioconda::multiqc=1.21 + - bioconda::multiqc=1.20 diff --git a/modules/nf-core/custom/dumpsoftwareversions/main.nf b/modules/nf-core/custom/dumpsoftwareversions/main.nf new file mode 100644 index 00000000..105f9265 --- /dev/null +++ b/modules/nf-core/custom/dumpsoftwareversions/main.nf @@ -0,0 +1,24 @@ +process CUSTOM_DUMPSOFTWAREVERSIONS { + label 'process_single' + + // Requires `pyyaml` which does not have a dedicated container but is in the MultiQC container + conda "${moduleDir}/environment.yml" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/multiqc:1.20--pyhdfd78af_0' : + 'biocontainers/multiqc:1.20--pyhdfd78af_0' }" + + input: + path versions + + output: + path "software_versions.yml" , emit: yml + path "software_versions_mqc.yml", emit: mqc_yml + path "versions.yml" , emit: versions + + when: + task.ext.when == null || task.ext.when + + script: + def args = task.ext.args ?: '' + template 'dumpsoftwareversions.py' +} diff --git a/modules/nf-core/custom/dumpsoftwareversions/meta.yml b/modules/nf-core/custom/dumpsoftwareversions/meta.yml new file mode 100644 index 00000000..5f15a5fd --- /dev/null +++ b/modules/nf-core/custom/dumpsoftwareversions/meta.yml @@ -0,0 +1,37 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/meta-schema.json +name: custom_dumpsoftwareversions +description: Custom module used to dump software versions within the nf-core pipeline template +keywords: + - custom + - dump + - version +tools: + - custom: + description: Custom module used to dump software versions within the nf-core pipeline template + homepage: https://github.com/nf-core/tools + documentation: https://github.com/nf-core/tools + licence: ["MIT"] +input: + - versions: + type: file + description: YML file containing software versions + pattern: "*.yml" +output: + - yml: + type: file + description: Standard YML file containing software versions + pattern: "software_versions.yml" + - mqc_yml: + type: file + description: MultiQC custom content YML file containing software versions + pattern: "software_versions_mqc.yml" + - versions: + type: file + description: File containing software versions + pattern: "versions.yml" +authors: + - "@drpatelh" + - "@grst" +maintainers: + - "@drpatelh" + - "@grst" diff --git a/modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py b/modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py new file mode 100755 index 00000000..4a993608 --- /dev/null +++ b/modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py @@ -0,0 +1,103 @@ +#!/usr/bin/env python + + +"""Provide functions to merge multiple versions.yml files.""" + + +import yaml +import platform +from textwrap import dedent + + +def _make_versions_html(versions): + """Generate a tabular HTML output of all versions for MultiQC.""" + html = [ + dedent( + """\\ + +
Process Name \\", + " \\ Software Version
CUSTOM_DUMPSOFTWAREVERSIONSpython3.11.7
yaml5.4.1
TOOL1tool10.11.9
TOOL2tool21.9
WorkflowNextflow
+ + + + + + + + """ + ) + ] + for process, tmp_versions in sorted(versions.items()): + html.append("") + for i, (tool, version) in enumerate(sorted(tmp_versions.items())): + html.append( + dedent( + f"""\\ + + + + + + """ + ) + ) + html.append("") + html.append("
Process Name Software Version
{process if (i == 0) else ''}{tool}{version}
") + return "\\n".join(html) + + +def main(): + """Load all version files and generate merged output.""" + versions_this_module = {} + versions_this_module["${task.process}"] = { + "python": platform.python_version(), + "yaml": yaml.__version__, + } + + with open("$versions") as f: + versions_by_process = ( + yaml.load(f, Loader=yaml.BaseLoader) | versions_this_module + ) + + # aggregate versions by the module name (derived from fully-qualified process name) + versions_by_module = {} + for process, process_versions in versions_by_process.items(): + module = process.split(":")[-1] + try: + if versions_by_module[module] != process_versions: + raise AssertionError( + "We assume that software versions are the same between all modules. " + "If you see this error-message it means you discovered an edge-case " + "and should open an issue in nf-core/tools. " + ) + except KeyError: + versions_by_module[module] = process_versions + + versions_by_module["Workflow"] = { + "Nextflow": "$workflow.nextflow.version", + "$workflow.manifest.name": "$workflow.manifest.version", + } + + versions_mqc = { + "id": "software_versions", + "section_name": "${workflow.manifest.name} Software Versions", + "section_href": "https://github.com/${workflow.manifest.name}", + "plot_type": "html", + "description": "are collected at run time from the software output.", + "data": _make_versions_html(versions_by_module), + } + + with open("software_versions.yml", "w") as f: + yaml.dump(versions_by_module, f, default_flow_style=False) + with open("software_versions_mqc.yml", "w") as f: + yaml.dump(versions_mqc, f, default_flow_style=False) + + with open("versions.yml", "w") as f: + yaml.dump(versions_this_module, f, default_flow_style=False) + + +if __name__ == "__main__": + main() diff --git a/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test new file mode 100644 index 00000000..b1e1630b --- /dev/null +++ b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test @@ -0,0 +1,43 @@ +nextflow_process { + + name "Test Process CUSTOM_DUMPSOFTWAREVERSIONS" + script "../main.nf" + process "CUSTOM_DUMPSOFTWAREVERSIONS" + tag "modules" + tag "modules_nfcore" + tag "custom" + tag "dumpsoftwareversions" + tag "custom/dumpsoftwareversions" + + test("Should run without failures") { + when { + process { + """ + def tool1_version = ''' + TOOL1: + tool1: 0.11.9 + '''.stripIndent() + + def tool2_version = ''' + TOOL2: + tool2: 1.9 + '''.stripIndent() + + input[0] = Channel.of(tool1_version, tool2_version).collectFile() + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot( + process.out.versions, + file(process.out.mqc_yml[0]).readLines()[0..10], + file(process.out.yml[0]).readLines()[0..7] + ).match() + } + ) + } + } +} diff --git a/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap new file mode 100644 index 00000000..5f59a936 --- /dev/null +++ b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap @@ -0,0 +1,33 @@ +{ + "Should run without failures": { + "content": [ + [ + "versions.yml:md5,76d454d92244589d32455833f7c1ba6d" + ], + [ + "data: \"\\n\\n \\n \\n \\n \\n \\n \\n \\n\\", + " \\n\\n\\n \\n \\n\\", + " \\ \\n\\n\\n\\n \\n \\", + " \\ \\n \\n\\n\\n\\n\\", + " \\n\\n \\n \\n\\", + " \\ \\n\\n\\n\\n\\n\\n \\n\\", + " \\ \\n \\n\\n\\n\\n\\", + " \\n\\n \\n \\n\\" + ], + [ + "CUSTOM_DUMPSOFTWAREVERSIONS:", + " python: 3.11.7", + " yaml: 5.4.1", + "TOOL1:", + " tool1: 0.11.9", + "TOOL2:", + " tool2: '1.9'", + "Workflow:" + ] + ], + "timestamp": "2024-01-09T23:01:18.710682" + } +} \ No newline at end of file diff --git a/modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml b/modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml new file mode 100644 index 00000000..405aa24a --- /dev/null +++ b/modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml @@ -0,0 +1,2 @@ +custom/dumpsoftwareversions: + - modules/nf-core/custom/dumpsoftwareversions/** diff --git a/modules/nf-core/fastqc/main.nf b/modules/nf-core/fastqc/main.nf deleted file mode 100644 index d79f1c86..00000000 --- a/modules/nf-core/fastqc/main.nf +++ /dev/null @@ -1,61 +0,0 @@ -process FASTQC { - tag "$meta.id" - label 'process_medium' - - conda "${moduleDir}/environment.yml" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/fastqc:0.12.1--hdfd78af_0' : - 'biocontainers/fastqc:0.12.1--hdfd78af_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(' ') - - def memory_in_mb = MemoryUnit.of("${task.memory}").toUnit('MB') - // FastQC memory value allowed range (100 - 10000) - def fastqc_memory = memory_in_mb > 10000 ? 10000 : (memory_in_mb < 100 ? 100 : memory_in_mb) - - """ - 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 \\ - --memory $fastqc_memory \\ - $renamed_files - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - fastqc: \$( fastqc --version | sed '/FastQC v/!d; s/.*v//' ) - 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 '/FastQC v/!d; s/.*v//' ) - END_VERSIONS - """ -} diff --git a/modules/nf-core/fastqc/meta.yml b/modules/nf-core/fastqc/meta.yml deleted file mode 100644 index ee5507e0..00000000 --- a/modules/nf-core/fastqc/meta.yml +++ /dev/null @@ -1,57 +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" -maintainers: - - "@drpatelh" - - "@grst" - - "@ewels" - - "@FelixKrueger" diff --git a/modules/nf-core/fastqc/tests/main.nf.test b/modules/nf-core/fastqc/tests/main.nf.test deleted file mode 100644 index 70edae4d..00000000 --- a/modules/nf-core/fastqc/tests/main.nf.test +++ /dev/null @@ -1,212 +0,0 @@ -nextflow_process { - - name "Test Process FASTQC" - script "../main.nf" - process "FASTQC" - - tag "modules" - tag "modules_nfcore" - tag "fastqc" - - test("sarscov2 single-end [fastq]") { - - when { - process { - """ - input[0] = Channel.of([ - [ id: 'test', single_end:true ], - [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true) ] - ]) - """ - } - } - - then { - assertAll ( - { assert process.success }, - - // NOTE The report contains the date inside it, which means that the md5sum is stable per day, but not longer than that. So you can't md5sum it. - // looks like this:
Mon 2 Oct 2023
test.gz
- // https://github.com/nf-core/modules/pull/3903#issuecomment-1743620039 - - { assert process.out.html[0][1] ==~ ".*/test_fastqc.html" }, - { assert process.out.zip[0][1] ==~ ".*/test_fastqc.zip" }, - { assert path(process.out.html[0][1]).text.contains("") }, - - { assert snapshot(process.out.versions).match("fastqc_versions_single") } - ) - } - } - - test("sarscov2 paired-end [fastq]") { - - when { - process { - """ - input[0] = Channel.of([ - [id: 'test', single_end: false], // meta map - [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true) ] - ]) - """ - } - } - - then { - assertAll ( - { assert process.success }, - - { assert process.out.html[0][1][0] ==~ ".*/test_1_fastqc.html" }, - { assert process.out.html[0][1][1] ==~ ".*/test_2_fastqc.html" }, - { assert process.out.zip[0][1][0] ==~ ".*/test_1_fastqc.zip" }, - { assert process.out.zip[0][1][1] ==~ ".*/test_2_fastqc.zip" }, - { assert path(process.out.html[0][1][0]).text.contains("") }, - { assert path(process.out.html[0][1][1]).text.contains("") }, - - { assert snapshot(process.out.versions).match("fastqc_versions_paired") } - ) - } - } - - test("sarscov2 interleaved [fastq]") { - - when { - process { - """ - input[0] = Channel.of([ - [id: 'test', single_end: false], // meta map - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_interleaved.fastq.gz', checkIfExists: true) - ]) - """ - } - } - - then { - assertAll ( - { assert process.success }, - - { assert process.out.html[0][1] ==~ ".*/test_fastqc.html" }, - { assert process.out.zip[0][1] ==~ ".*/test_fastqc.zip" }, - { assert path(process.out.html[0][1]).text.contains("") }, - - { assert snapshot(process.out.versions).match("fastqc_versions_interleaved") } - ) - } - } - - test("sarscov2 paired-end [bam]") { - - when { - process { - """ - input[0] = Channel.of([ - [id: 'test', single_end: false], // meta map - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/bam/test.paired_end.sorted.bam', checkIfExists: true) - ]) - """ - } - } - - then { - assertAll ( - { assert process.success }, - - { assert process.out.html[0][1] ==~ ".*/test_fastqc.html" }, - { assert process.out.zip[0][1] ==~ ".*/test_fastqc.zip" }, - { assert path(process.out.html[0][1]).text.contains("") }, - - { assert snapshot(process.out.versions).match("fastqc_versions_bam") } - ) - } - } - - test("sarscov2 multiple [fastq]") { - - when { - process { - """ - input[0] = Channel.of([ - [id: 'test', single_end: false], // meta map - [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true), - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test2_1.fastq.gz', checkIfExists: true), - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test2_2.fastq.gz', checkIfExists: true) ] - ]) - """ - } - } - - then { - assertAll ( - { assert process.success }, - - { assert process.out.html[0][1][0] ==~ ".*/test_1_fastqc.html" }, - { assert process.out.html[0][1][1] ==~ ".*/test_2_fastqc.html" }, - { assert process.out.html[0][1][2] ==~ ".*/test_3_fastqc.html" }, - { assert process.out.html[0][1][3] ==~ ".*/test_4_fastqc.html" }, - { assert process.out.zip[0][1][0] ==~ ".*/test_1_fastqc.zip" }, - { assert process.out.zip[0][1][1] ==~ ".*/test_2_fastqc.zip" }, - { assert process.out.zip[0][1][2] ==~ ".*/test_3_fastqc.zip" }, - { assert process.out.zip[0][1][3] ==~ ".*/test_4_fastqc.zip" }, - { assert path(process.out.html[0][1][0]).text.contains("") }, - { assert path(process.out.html[0][1][1]).text.contains("") }, - { assert path(process.out.html[0][1][2]).text.contains("") }, - { assert path(process.out.html[0][1][3]).text.contains("") }, - - { assert snapshot(process.out.versions).match("fastqc_versions_multiple") } - ) - } - } - - test("sarscov2 custom_prefix") { - - when { - process { - """ - input[0] = Channel.of([ - [ id:'mysample', single_end:true ], // meta map - file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true) - ]) - """ - } - } - - then { - assertAll ( - { assert process.success }, - - { assert process.out.html[0][1] ==~ ".*/mysample_fastqc.html" }, - { assert process.out.zip[0][1] ==~ ".*/mysample_fastqc.zip" }, - { assert path(process.out.html[0][1]).text.contains("") }, - - { assert snapshot(process.out.versions).match("fastqc_versions_custom_prefix") } - ) - } - } - - test("sarscov2 single-end [fastq] - stub") { - - options "-stub" - - when { - process { - """ - input[0] = Channel.of([ - [ id: 'test', single_end:true ], - [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true) ] - ]) - """ - } - } - - then { - assertAll ( - { assert process.success }, - { assert snapshot(process.out.html.collect { file(it[1]).getName() } + - process.out.zip.collect { file(it[1]).getName() } + - process.out.versions ).match("fastqc_stub") } - ) - } - } - -} diff --git a/modules/nf-core/fastqc/tests/main.nf.test.snap b/modules/nf-core/fastqc/tests/main.nf.test.snap deleted file mode 100644 index 86f7c311..00000000 --- a/modules/nf-core/fastqc/tests/main.nf.test.snap +++ /dev/null @@ -1,88 +0,0 @@ -{ - "fastqc_versions_interleaved": { - "content": [ - [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" - ] - ], - "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" - }, - "timestamp": "2024-01-31T17:40:07.293713" - }, - "fastqc_stub": { - "content": [ - [ - "test.html", - "test.zip", - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" - ] - ], - "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" - }, - "timestamp": "2024-01-31T17:31:01.425198" - }, - "fastqc_versions_multiple": { - "content": [ - [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" - ] - ], - "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" - }, - "timestamp": "2024-01-31T17:40:55.797907" - }, - "fastqc_versions_bam": { - "content": [ - [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" - ] - ], - "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" - }, - "timestamp": "2024-01-31T17:40:26.795862" - }, - "fastqc_versions_single": { - "content": [ - [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" - ] - ], - "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" - }, - "timestamp": "2024-01-31T17:39:27.043675" - }, - "fastqc_versions_paired": { - "content": [ - [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" - ] - ], - "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" - }, - "timestamp": "2024-01-31T17:39:47.584191" - }, - "fastqc_versions_custom_prefix": { - "content": [ - [ - "versions.yml:md5,e1cc25ca8af856014824abd842e93978" - ] - ], - "meta": { - "nf-test": "0.8.4", - "nextflow": "23.10.1" - }, - "timestamp": "2024-01-31T17:41:14.576531" - } -} \ No newline at end of file diff --git a/modules/nf-core/fastqc/tests/tags.yml b/modules/nf-core/fastqc/tests/tags.yml deleted file mode 100644 index 7834294b..00000000 --- a/modules/nf-core/fastqc/tests/tags.yml +++ /dev/null @@ -1,2 +0,0 @@ -fastqc: - - modules/nf-core/fastqc/** diff --git a/modules/nf-core/multiqc/main.nf b/modules/nf-core/multiqc/main.nf deleted file mode 100644 index 47ac352f..00000000 --- a/modules/nf-core/multiqc/main.nf +++ /dev/null @@ -1,55 +0,0 @@ -process MULTIQC { - label 'process_single' - - conda "${moduleDir}/environment.yml" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/multiqc:1.21--pyhdfd78af_0' : - 'biocontainers/multiqc:1.21--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" : '' - def logo = multiqc_logo ? /--cl-config 'custom_logo: "${multiqc_logo}"'/ : '' - """ - multiqc \\ - --force \\ - $args \\ - $config \\ - $extra_config \\ - $logo \\ - . - - cat <<-END_VERSIONS > versions.yml - "${task.process}": - multiqc: \$( multiqc --version | sed -e "s/multiqc, version //g" ) - END_VERSIONS - """ - - stub: - """ - mkdir 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 45a9bc35..00000000 --- a/modules/nf-core/multiqc/meta.yml +++ /dev/null @@ -1,58 +0,0 @@ -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" -maintainers: - - "@abhi18av" - - "@bunop" - - "@drpatelh" - - "@jfy133" diff --git a/modules/nf-core/multiqc/tests/main.nf.test b/modules/nf-core/multiqc/tests/main.nf.test deleted file mode 100644 index f1c4242e..00000000 --- a/modules/nf-core/multiqc/tests/main.nf.test +++ /dev/null @@ -1,84 +0,0 @@ -nextflow_process { - - name "Test Process MULTIQC" - script "../main.nf" - process "MULTIQC" - - tag "modules" - tag "modules_nfcore" - tag "multiqc" - - test("sarscov2 single-end [fastqc]") { - - when { - process { - """ - input[0] = Channel.of(file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastqc/test_fastqc.zip', checkIfExists: true)) - input[1] = [] - input[2] = [] - input[3] = [] - """ - } - } - - then { - assertAll( - { assert process.success }, - { assert process.out.report[0] ==~ ".*/multiqc_report.html" }, - { assert process.out.data[0] ==~ ".*/multiqc_data" }, - { assert snapshot(process.out.versions).match("multiqc_versions_single") } - ) - } - - } - - test("sarscov2 single-end [fastqc] [config]") { - - when { - process { - """ - input[0] = Channel.of(file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastqc/test_fastqc.zip', checkIfExists: true)) - input[1] = Channel.of(file("https://github.com/nf-core/tools/raw/dev/nf_core/pipeline-template/assets/multiqc_config.yml", checkIfExists: true)) - input[2] = [] - input[3] = [] - """ - } - } - - then { - assertAll( - { assert process.success }, - { assert process.out.report[0] ==~ ".*/multiqc_report.html" }, - { assert process.out.data[0] ==~ ".*/multiqc_data" }, - { assert snapshot(process.out.versions).match("multiqc_versions_config") } - ) - } - } - - test("sarscov2 single-end [fastqc] - stub") { - - options "-stub" - - when { - process { - """ - input[0] = Channel.of(file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastqc/test_fastqc.zip', checkIfExists: true)) - input[1] = [] - input[2] = [] - input[3] = [] - """ - } - } - - then { - assertAll( - { assert process.success }, - { assert snapshot(process.out.report.collect { file(it).getName() } + - process.out.data.collect { file(it).getName() } + - process.out.plots.collect { file(it).getName() } + - process.out.versions ).match("multiqc_stub") } - ) - } - - } -} diff --git a/modules/nf-core/multiqc/tests/tags.yml b/modules/nf-core/multiqc/tests/tags.yml deleted file mode 100644 index bea6c0d3..00000000 --- a/modules/nf-core/multiqc/tests/tags.yml +++ /dev/null @@ -1,2 +0,0 @@ -multiqc: - - modules/nf-core/multiqc/** diff --git a/nextflow.config b/nextflow.config index ebfdbf55..eb09e930 100644 --- a/nextflow.config +++ b/nextflow.config @@ -6,23 +6,67 @@ ---------------------------------------------------------------------------------------- */ + // Global default params, used in configs 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 - multiqc_logo = null - max_multiqc_email_size = '25.MB' - multiqc_methods_description = null + input = null + input_basedir = 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 + + // demux options + demux_mismatches = 0.1 + demux_min_length = null + + // collapse options + markers_ignore = null + algorithm = 'adjacency' + max_neighbours = 60 + collapse_mismatches = 2 + collapse_min_count = 2 + collapse_use_counts = false + + // graph options + multiplet_recovery = true + leiden_iterations = 10 + graph_min_count = 2 + + // annotate options + min_size = null + max_size = null + dynamic_filter = 'min' + aggregate_calling = true + + // analysis options + compute_polarization = true + compute_colocalization = true + use_full_bipartite = 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 + skip_analysis = false + + // Main pixelator container override + pixelator_container = null // Boilerplate options outdir = null @@ -56,7 +100,6 @@ params { validationSchemaIgnoreParams = 'genomes,igenomes_base' validationShowHiddenParams = false validate_params = true - } // Load base.config by default for all pipelines @@ -70,11 +113,19 @@ try { } // Load nf-core/pixelator custom profiles from different institutions. -try { - includeConfig "${params.custom_config_base}/pipeline/pixelator.config" -} catch (Exception e) { - System.err.println("WARNING: Could not load nf-core/config/pixelator profiles: ${params.custom_config_base}/pipeline/pixelator.config") -} +// Warning: Uncomment only if a pipeline-specific institutional config already exists on nf-core/configs! +// try { +// includeConfig "${params.custom_config_base}/pipeline/pixelator.config" +// } catch (Exception e) { +// System.err.println("WARNING: Could not load nf-core/config/pixelator profiles: ${params.custom_config_base}/pipeline/pixelator.config") +// } + + +def container_env_options = [ + "MPLCONFIGDIR": '/tmp/.config/matplotlib', + "NUMBA_CACHE_DIR": "/tmp/.numba_cache", +] + profiles { debug { dumpHashes = true @@ -103,36 +154,40 @@ profiles { apptainer.enabled = false } docker { - docker.enabled = true - conda.enabled = false - singularity.enabled = false - podman.enabled = false - shifter.enabled = false - charliecloud.enabled = false - apptainer.enabled = false - docker.runOptions = '-u $(id -u):$(id -g)' + docker.enabled = true + conda.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + apptainer.enabled = false + docker.runOptions = '-u $(id -u):$(id -g)' + env = container_env_options } arm { docker.runOptions = '-u $(id -u):$(id -g) --platform=linux/amd64' } singularity { - singularity.enabled = true - singularity.autoMounts = true - conda.enabled = false - docker.enabled = false - podman.enabled = false - shifter.enabled = false - charliecloud.enabled = false - apptainer.enabled = false + singularity.enabled = true + singularity.autoMounts = true + conda.enabled = false + docker.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + apptainer.enabled = false + env = container_env_options } podman { - podman.enabled = true - conda.enabled = false - docker.enabled = false - singularity.enabled = false - shifter.enabled = false - charliecloud.enabled = false - apptainer.enabled = false + podman.enabled = true + conda.enabled = false + docker.enabled = false + singularity.enabled = false + shifter.enabled = false + charliecloud.enabled = false + apptainer.enabled = false + podman.runOptions = '--userns=keep-id' + env = container_env_options } shifter { shifter.enabled = true @@ -142,6 +197,7 @@ profiles { podman.enabled = false charliecloud.enabled = false apptainer.enabled = false + env = container_env_options } charliecloud { charliecloud.enabled = true @@ -153,14 +209,15 @@ profiles { apptainer.enabled = false } apptainer { - apptainer.enabled = true - apptainer.autoMounts = true - conda.enabled = false - docker.enabled = false - singularity.enabled = false - podman.enabled = false - shifter.enabled = false - charliecloud.enabled = false + apptainer.enabled = true + apptainer.autoMounts = true + conda.enabled = false + docker.enabled = false + singularity.enabled = false + podman.enabled = false + shifter.enabled = false + charliecloud.enabled = false + env = container_env_options } wave { apptainer.ociAutoPull = true @@ -191,12 +248,6 @@ plugins { id 'nf-validation@1.1.3' // Validation of pipeline parameters and creation of an input channel from a sample sheet } -// 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. // 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. @@ -239,8 +290,8 @@ manifest { description = """Pipeline for analysis of Molecular Pixelation assays""" mainScript = 'main.nf' nextflowVersion = '!>=23.04.0' - version = '1.1.0' - doi = '' + version = '1.2.0' + 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 77b2a8e1..e58178a9 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -18,11 +18,18 @@ "exists": true, "schema": "assets/schema_input.json", "mimetype": "text/csv", - "pattern": "^\\S+\\.csv$", + "pattern": "^\\S+\\.(csv|tsv)$", "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" }, + "input_basedir": { + "type": "string", + "format": "directory-path", + "exists": false, + "description": "Path to a local or remote directory that is the \"current working directory\" for relative paths defined in the input samplesheet", + "fa_icon": "fas fa-folder" + }, "outdir": { "type": "string", "format": "directory-path", @@ -35,42 +42,277 @@ "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})$" + } + } + }, + "preqc_options": { + "title": "QC/Filtering/Trimming options", + "type": "object", + "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 }, - "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" + "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 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", + "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 + }, + "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" + }, + "remove_polyg": { + "fa_icon": "fas g", + "description": "Remove PolyG sequences (length of 10 or more)", + "type": "boolean" + } + } + }, + "adapterqc_options": { + "title": "Adapter QC Options", + "type": "object", + "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, + "minimum": 0.0, + "maximum": 0.9 + } + } + }, + "demux_options": { + "title": "Demux options", + "type": "object", + "properties": { + "demux_mismatches": { + "fa_icon": "fas not-equal", + "description": "The number of mismatches allowed (as a fraction)", + "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", + "help_text": "If you set this argument it will overrule the value from the chosen design", + "type": "integer" } } }, - "reference_genome_options": { - "title": "Reference genome options", + "collapse_options": { + "title": "Collapse options", "type": "object", - "fa_icon": "fas fa-dna", - "description": "Reference genome related files and options required for the workflow.", "properties": { - "genome": { + "markers_ignore": { + "fa_icon": "fas fa-list", + "description": "A list of comma separated antibodies to discard", "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." + "pattern": "(\\S+)?(,\\S+)*" + }, + "algorithm": { + "fa_icon": "fas code-fork", + "description": "The algorithm to use for collapsing (adjacency will perform error correction using the number of mismatches given)", + "default": "adjacency", + "enum": ["adjacency", "unique"], + "type": "string" + }, + "max_neighbours": { + "fa_icon": "fas circle-nodes", + "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, + "type": "integer", + "hidden": true + }, + "collapse_mismatches": { + "fa_icon": "fas not-equal", + "description": "The number of mismatches allowed when collapsing (adjacency)", + "type": "integer", + "default": 2, + "minimum": 0, + "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, + "minimum": 1, + "type": "integer" + }, + "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" + } + } + }, + "graph_options": { + "title": "Options for pixelator graph command.", + "type": "object", + "properties": { + "multiplet_recovery": { + "description": "Activate the multiplet recovery using leiden community detection", + "type": "boolean", + "default": true }, - "fasta": { + "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", + "minimum": 1, + "maximum": 100, + "default": 10, + "hidden": true + }, + "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", + "default": 2, + "minimum": 1, + "maximum": 50, + "hidden": true + } + } + }, + "annotate_options": { + "title": "Options for pixelator annotate command.", + "type": "object", + "properties": { + "min_size": { + "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 (disabled by default)", + "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", - "exists": true, - "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_ignore": { + "aggregate_calling": { + "description": "Enable aggregate calling, information on potential aggregates will be added to the output data", "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": true + } + } + }, + "analysis_options": { + "title": "Options for pixelator analysis command.", + "type": "object", + "properties": { + "skip_analysis": { + "description": "Skip analysis step", + "type": "boolean" + }, + "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" + }, + "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" + }, + "polarization_binarization": { + "fa_icon": "fas binary", + "description": "Transform the antibody counts to 0-1 (binarize) when computing polarization", + "type": "boolean" + }, + "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 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 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 considered valid for computing colocalization", + "default": 5, + "minimum": 0 + } + } + }, + "report_options": { + "title": "Options for pixelator report command.", + "type": "object", + "properties": { + "skip_report": { + "description": "Skip report generation", + "type": "boolean" + } + } + }, + "global_config_options": { + "title": "Global options", + "type": "object", + "description": "Global configuration options specific to nf-core/pixelator.", + "properties": { + "pixelator_container": { + "type": "string", + "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 guaranteed to work when using different pixelator versions." } } }, @@ -199,14 +441,6 @@ "fa_icon": "fas fa-remove-format", "hidden": true }, - "max_multiqc_email_size": { - "type": "string", - "description": "File size limit when attaching MultiQC 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.", @@ -220,24 +454,6 @@ "help_text": "Incoming hook URL for messaging service. Currently, MS Teams and Slack are supported.", "hidden": true }, - "multiqc_config": { - "type": "string", - "format": "file-path", - "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" - }, "validate_params": { "type": "boolean", "description": "Boolean whether to validate parameters against the schema at runtime", @@ -257,7 +473,7 @@ "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." + "help_text": "By default, when an unrecognised parameter is found, it returns a warning." }, "validationLenientMode": { "type": "boolean", @@ -281,7 +497,31 @@ "$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/global_config_options" }, { "$ref": "#/definitions/institutional_config_options" diff --git a/samplesheet.csv b/samplesheet.csv new file mode 100644 index 00000000..da37578e --- /dev/null +++ b/samplesheet.csv @@ -0,0 +1,3 @@ +sample,design,panel,panel_file,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 diff --git a/subworkflows/local/generate_reports.nf b/subworkflows/local/generate_reports.nf new file mode 100644 index 00000000..dc5fb42c --- /dev/null +++ b/subworkflows/local/generate_reports.nf @@ -0,0 +1,134 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + IMPORT MODULES / SUBWORKFLOWS / FUNCTIONS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +include { PIXELATOR_REPORT } from '../../modules/local/pixelator/single-cell/report/main' + + +/* +======================================================================================== + SUBWORKFLOW TO GENERATE PIXELATOR REPORTS +======================================================================================== +*/ + +workflow GENERATE_REPORTS { + take: + panel_files // channel: [meta, path(panel_file) | []] + amplicon_data // channel: [meta, [path, ...]] + preqc_data // channel: [meta, [path, ...]] + adapterqc_data // channel: [meta, [path, ...]] + demux_data // channel: [meta, [path, ...]] + collapse_data // channel: [meta, [path, ...]] + graph_data // channel: [meta, [path, ...]] + annotate_data // channel: [meta, [path, ...]] + analysis_data // channel: [meta, [path, ...]] + + main: + ch_versions = Channel.empty() + + ch_meta_col = panel_files + .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] + } + + ch_panel_col = panel_files + .map { meta, data -> [ meta.id, data] } + + // + // 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 is neded to have the per subcommand outputs in 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_report_data = ch_meta_col + .concat ( ch_panel_col ) + .concat ( ch_amplicon_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 (size: 10) + + // + // Split up everything per stage so we can recreate the expected directory structure for + // `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. + + // If no `panel_file` (data[1]) is given we need to pass in `panel` from the samplesheet instead + // + ch_panel_files_grouped = ch_report_data.map { id, data -> [ data[0], data[1], data[1] ? null : data[0].panel ] } + 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() : [] } + 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() : [] } + + // + // MODULE: Run pixelator single-cell report for each samples + // + // NB: These channels need to be split per stage to allow PIXELATOR_REPORT to + // use stageAs directives to reorder the inputs and prevent filename collisions + PIXELATOR_REPORT ( + ch_panel_files_grouped, + ch_amplicon_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/utils_nfcore_pixelator_pipeline/main.nf b/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf index 5d96f1f8..3a61820a 100644 --- a/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf @@ -20,6 +20,8 @@ include { imNotification } from '../../nf-core/utils_nfcore_pipeline' include { UTILS_NFCORE_PIPELINE } from '../../nf-core/utils_nfcore_pipeline' include { workflowCitation } from '../../nf-core/utils_nfcore_pipeline' +include { PIXELATOR_LIST_OPTIONS } from '../../../modules/local/pixelator/list_options.nf' + /* ======================================================================================== SUBWORKFLOW TO INITIALISE PIPELINE @@ -36,6 +38,7 @@ workflow PIPELINE_INITIALISATION { nextflow_cli_args // array: List of positional nextflow CLI args outdir // string: The output directory where the results will be saved input // string: Path to input samplesheet + input_basedir // string: Path to the base directory for relative paths resolving main: @@ -80,25 +83,57 @@ workflow PIPELINE_INITIALISATION { // // Create channel from input file provided through params.input // - Channel - .fromSamplesheet("input") - .map { - meta, fastq_1, fastq_2 -> - if (!fastq_2) { - return [ meta.id, meta + [ single_end:true ], [ fastq_1 ] ] - } else { - return [ meta.id, meta + [ single_end:false ], [ fastq_1, fastq_2 ] ] - } - } - .groupTuple() + ch_versions = Channel.empty() + + // + // Resolve relative paths and validate fastq files existence + // + def samplesheet_uri = file(input).toUri() + def inputBaseDir = get_data_basedir(samplesheet_uri, input_basedir) + + log.info "Resolving relative paths in samplesheet relative to: ${inputBaseDir}" + + ch_input = Channel.fromSamplesheet("input") .map { - validateInputSamplesheet(it) + validate_input_samplesheet(inputBaseDir, it) } + + + // + // Validate design and panel samplesheet fields agains a dynamic set of allowed values + // + PIXELATOR_LIST_OPTIONS() + ch_versions = ch_versions.mix(PIXELATOR_LIST_OPTIONS.out.versions) + + // Create a set of valid pixelator options to pass to --design + ch_design_options = PIXELATOR_LIST_OPTIONS.out.designs + .splitText() + .map( text -> text.trim()) + .reduce( new HashSet() ) { prev, curr -> prev << curr } + + // Create a set of valid pixelator panel keys to pass using --panel + ch_panel_options = PIXELATOR_LIST_OPTIONS.out.panels + .splitText() + .map( text -> text.trim()) + .reduce( new HashSet() ) { prev, curr -> prev << curr } + + + + // + // Combine the validated inputs again in a single channel + // + ch_samplesheet = ch_input + .map { it -> it[0] } + .combine(ch_panel_options) + .combine(ch_design_options) .map { - meta, fastqs -> - return [ meta, fastqs.flatten() ] + meta, panel_options, design_options -> { + meta = validate_panel(meta, panel_options) + meta = validate_design(meta, design_options) + return meta + } } - .set { ch_samplesheet } + .join(ch_input) emit: samplesheet = ch_samplesheet @@ -155,7 +190,8 @@ workflow PIPELINE_COMPLETION { // Check and validate pipeline parameters // def validateInputParameters() { - genomeExistsError() + // Keep this commented here to closely follow the template + // genomeExistsError() } // @@ -261,3 +297,151 @@ def methodsDescriptionText(mqc_methods_yaml) { return description_html.toString() } + + + +// +// Resolve relative paths relative to the samplesheet parent directory. +// +def resolve_relative_path(relative_path, URI samplesheet_path) { + if (!(relative_path instanceof String)) { + return relative_path + } + + // Try to create a java.net.UR object out of it. If it is not a proper URL, a MalformedURLException will be t + URI uri; + + try { + uri = new URI(relative_path) + } catch (URISyntaxException exc) { + return relative_path + } + + // If a scheme is given we keep it as given + if (uri.getScheme() != null) { + return relative_path + } + + def path = new File(relative_path) + if (path.isAbsolute()) { + return relative_path + } + + // Resolve relative paths agains the samplesheet_path + def resolvedPath = samplesheet_path.resolve(relative_path); + + def stringPath = resolvedPath.toString() + return stringPath +} + +// +// Validate a given panel key if present against the (dynamic) set of panel options retrieved from pixelator +// +def validate_panel(LinkedHashMap meta, HashSet options) { + if (meta.panel == null) { + return + } + + if (!options.contains(meta.panel)) { + options_list_str = " - ${options.join("\n - ")}" + exit 1, "Please check input samplesheet -> panel field does not contains a valid key!\n\nInput: ${meta.panel}\nValid options:\n${options_list_str}" + } + + return meta +} + + +// +// Validate a given design key if present against the (dynamic) set of design options retrieved from pixelator +// +def validate_design(LinkedHashMap meta, HashSet options) { + if (meta.design == null) { + return + } + + if (!options.contains(meta.design)) { + options_list_str = " - ${options.join("\n - ")}" + exit 1, "Please check input samplesheet -> design field does not contains a valid key!\n\nInput: ${meta.design}\nValid options:\n${options_list_str}" + } + + return meta +} + +// +// Determine the path/url that will be used as the root for relative paths in the samplesheet +// +def get_data_basedir(URI samplesheet, String input_basedir) { + + URI uri; + + // nothing given to --input_data so we use the samplesheet as root directory + // for resolving relative paths + if (!input_basedir) { + return samplesheet + } + + try { + uri = new URI(input_basedir) + } catch (URISyntaxException exc) { + return samplesheet + } + + // If a scheme is given we keep check that it is a directory (trailing-slash) + if (uri.getScheme() != null) { + if (!uri.path.endsWith('/')) { + def newUrl = new URI( + uri.getScheme(), uri.getUserInfo(), uri.getHost(), + uri.getPort(), uri.getPath() + '/', uri.getQuery(), uri.getFragment() + ) + return newUrl + } + return uri + } + + f = file(input_basedir) + if (!f.exists()) { + exit 1, "ERROR: data path passed with --input_basedir does not exist!" + } + if (f.isDirectory()) { + data_root = new URI(f.toString() + '/') + } else { + data_root = new URI(f.toString()) + } + + return data_root +} + +// +// Resolve relative paths and check that all files exist. +// +def validate_input_samplesheet(URI samplesheetUrl, items) { + def meta = items[0] + def panel_file = items[1] + def fq = items[2..-1] + + def paired_end = fq.size() == 2 + def panel_file_abs = resolve_relative_path(panel_file, samplesheetUrl) + def fq1_abs = resolve_relative_path(fq[0], samplesheetUrl) + + if (panel_file_abs && !file(panel_file_abs).exists()) { + exit 1, "ERROR: Please check input samplesheet -> panel_file does not exist!\n${panel_file_abs}" + } + + if (!file(fq1_abs).exists()) { + exit 1, "ERROR: Please check input samplesheet -> fastq_1 does not exist!\n${fq1_abs}" + } + + def reads = [ fq1_abs ] + + if (paired_end) { + def fq2_abs = resolve_relative_path(fq[1], samplesheetUrl) + + if (fq2_abs && !file(fq2_abs).exists()) { + exit 1, "ERROR: Please check input samplesheet -> fastq_2 does not exist!\n${fq2_abs}" + } + + reads += [ fq2_abs] + } + + return [ meta, panel_file_abs, reads ] +} 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" diff --git a/workflows/pixelator.nf b/workflows/pixelator.nf index afb6838b..c7db0457 100644 --- a/workflows/pixelator.nf +++ b/workflows/pixelator.nf @@ -4,37 +4,239 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -include { FASTQC } from '../modules/nf-core/fastqc/main' -include { MULTIQC } from '../modules/nf-core/multiqc/main' include { paramsSummaryMap } from 'plugin/nf-validation' include { paramsSummaryMultiqc } from '../subworkflows/nf-core/utils_nfcore_pipeline' include { softwareVersionsToYAML } from '../subworkflows/nf-core/utils_nfcore_pipeline' include { methodsDescriptionText } from '../subworkflows/local/utils_nfcore_pixelator_pipeline' + +// Inject the samplesheet SHA-1 into the params object +ch_input = file(params.input) +params.samplesheet_sha = ch_input.bytes.digest('sha-1') + /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - RUN MAIN WORKFLOW + CONFIG FILES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -workflow PIXELATOR { +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + IMPORT LOCAL MODULES/SUBWORKFLOWS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +// +// SUBWORKFLOW: Consisting of a mix of local and nf-core/modules +// +include { GENERATE_REPORTS } from '../subworkflows/local/generate_reports' + +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + IMPORT NF-CORE MODULES/SUBWORKFLOWS +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +*/ + +// +// MODULE: Installed directly from nf-core/modules +// +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 { RENAME_READS } from '../modules/local/rename_reads' +include { PIXELATOR_COLLECT_METADATA } from '../modules/local/pixelator/collect_metadata' +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' +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' + +/* +======================================================================================== + RUN MAIN WORKFLOW +======================================================================================== +*/ +workflow PIXELATOR { take: - ch_samplesheet // channel: samplesheet read in from --input + ch_samplesheet // channel: [ meta, path(panel_file | []), path(sample_1.fq), path(sample_2.fq) ] main: ch_versions = Channel.empty() - ch_multiqc_files = Channel.empty() // - // MODULE: Run FastQC + // Split the samplesheet channel in reads and panel_files + // + ch_reads = ch_samplesheet.map { [it[0]] + it[2..-1] } + ch_panel_files = ch_samplesheet.map { [it[0], it[1]] } + + ch_fastq_split = ch_reads + .groupTuple() + .branch { + meta, fastq -> + single : fastq.size() == 1 + return [ meta, fastq.flatten() ] + multiple: fastq.size() > 1 + return [ meta, fastq.flatten() ] + } + + // + // MODULE: Dump pixelaor and pipeline information + // + PIXELATOR_COLLECT_METADATA () + ch_versions = ch_versions.mix(PIXELATOR_COLLECT_METADATA.out.versions) + // - FASTQC ( - ch_samplesheet + // MODULE: Concatenate FastQ files from the same sample if required + // + ch_cat_fastq = CAT_FASTQ ( ch_fastq_split.multiple ) + .reads + .mix(ch_fastq_split.single) + + // 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()) + + + // + // MODULE: Rename input reads to match the sample ids from the samplesheet + // + RENAME_READS ( ch_cat_fastq ) + ch_renamed_reads = RENAME_READS.out.reads + ch_versions = ch_versions.mix(RENAME_READS.out.versions.first()) + + // + // MODULE: Run pixelator single-cell amplicon + // + 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 + + // + // MODULE: Run pixelator single-cell preqc & pixelator single-cell adapterqc + // + PIXELATOR_QC ( ch_input_reads ) + ch_qc = PIXELATOR_QC.out.processed + ch_versions = ch_versions.mix(PIXELATOR_QC.out.versions.first()) + + ch_fq_and_panel = ch_qc + .join(ch_cat_panel_files, failOnMismatch:true, failOnDuplicate:true) + .map { meta, fq, panel_file -> [meta, fq, panel_file, panel_file ? null : meta.panel ] } + + // + // MODULE: Run pixelator single-cell demux + // + PIXELATOR_DEMUX ( ch_fq_and_panel ) + ch_demuxed = PIXELATOR_DEMUX.out.processed + ch_versions = ch_versions.mix(PIXELATOR_DEMUX.out.versions.first()) + + ch_demuxed_and_panel = ch_demuxed + .join(ch_cat_panel_files, failOnMismatch:true, failOnDuplicate:true) + .map { meta, demuxed, panel_file -> [meta, demuxed, panel_file, panel_file ? null : meta.panel ] } + + // + // MODULE: Run pixelator single-cell collapse + // + PIXELATOR_COLLAPSE ( ch_demuxed_and_panel ) + ch_collapsed = PIXELATOR_COLLAPSE.out.collapsed + ch_versions = ch_versions.mix( PIXELATOR_COLLAPSE.out.versions.first()) + + // + // MODULE: Run pixelator single-cell graph + // + 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_cat_panel_files, failOnMismatch:true, failOnDuplicate:true) + .map { meta, clustered, panel_file -> [meta, clustered, panel_file, panel_file ? null : meta.panel ] } + + // + // MODULE: Run pixelator single-cell annotate + // + PIXELATOR_ANNOTATE ( ch_clustered_and_panel ) + ch_annotated = PIXELATOR_ANNOTATE.out.dataset + ch_versions = ch_versions.mix( PIXELATOR_ANNOTATE.out.versions.first() ) + + // + // MODULE: Run pixelator single-cell analysis + // + PIXELATOR_ANALYSIS ( ch_annotated ) + ch_analysed = PIXELATOR_ANALYSIS.out.dataset + ch_versions = ch_versions.mix(PIXELATOR_ANALYSIS.out.versions.first()) + + + // 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_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_cat_panel_files, + ch_amplicon_data, + ch_preqc_data, + ch_adapterqc_data, + ch_demux_data, + ch_collapse_data, + ch_cluster_data, + ch_annotate_data, + ch_analysis_data ) - ch_multiqc_files = ch_multiqc_files.mix(FASTQC.out.zip.collect{it[1]}) - ch_versions = ch_versions.mix(FASTQC.out.versions.first()) + + ch_versions = ch_versions.mix(GENERATE_REPORTS.out.versions) // // Collate and save software versions @@ -47,47 +249,10 @@ workflow PIXELATOR { newLine: true ).set { ch_collated_versions } - // - // MODULE: MultiQC - // - 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() - - summary_params = paramsSummaryMap( - workflow, parameters_schema: "nextflow_schema.json") - ch_workflow_summary = Channel.value(paramsSummaryMultiqc(summary_params)) - - 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) - ch_methods_description = Channel.value( - methodsDescriptionText(ch_multiqc_custom_methods_description)) - - ch_multiqc_files = ch_multiqc_files.mix( - ch_workflow_summary.collectFile(name: 'workflow_summary_mqc.yaml')) - ch_multiqc_files = ch_multiqc_files.mix(ch_collated_versions) - ch_multiqc_files = ch_multiqc_files.mix( - ch_methods_description.collectFile( - name: 'methods_description_mqc.yaml', - sort: true - ) - ) + // TODO: Add MultiQC when plugins are ready - MULTIQC ( - ch_multiqc_files.collect(), - ch_multiqc_config.toList(), - ch_multiqc_custom_config.toList(), - ch_multiqc_logo.toList() - ) emit: - multiqc_report = MULTIQC.out.report.toList() // channel: /path/to/multiqc_report.html versions = ch_versions // channel: [ path(versions.yml) ] } From 70fe1957e184b90f1f9fa6c7e789bd1f982c03e7 Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Fri, 24 May 2024 08:16:50 +0200 Subject: [PATCH 169/260] Update to use pixelator 0.17.1, add layout command --- conf/modules.config | 14 +++++- conf/test.config | 3 ++ docs/output.md | 28 ++++++++++- modules/local/pixelator/collect_metadata.nf | 6 +-- modules/local/pixelator/list_options.nf | 6 +-- .../pixelator/single-cell/amplicon/main.nf | 6 +-- .../pixelator/single-cell/analysis/main.nf | 7 ++- .../pixelator/single-cell/annotate/main.nf | 6 +-- .../pixelator/single-cell/collapse/main.nf | 6 +-- .../local/pixelator/single-cell/demux/main.nf | 6 +-- .../local/pixelator/single-cell/graph/main.nf | 6 +-- .../pixelator/single-cell/layout/main.nf | 47 ++++++++++++++++++ .../local/pixelator/single-cell/qc/main.nf | 6 +-- .../pixelator/single-cell/report/main.nf | 6 +-- nextflow.config | 10 +++- nextflow_schema.json | 48 +++++++++++++++---- workflows/pixelator.nf | 7 +++ 17 files changed, 171 insertions(+), 47 deletions(-) create mode 100644 modules/local/pixelator/single-cell/layout/main.nf diff --git a/conf/modules.config b/conf/modules.config index 7ab9ba43..6841052b 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -133,15 +133,25 @@ process { params.compute_polarization ? "--compute-polarization" : '', params.compute_colocalization ? "--compute-colocalization" : '', params.use_full_bipartite ? "--use-full-bipartite " : '', - params.polarization_normalization ? "--polarization-normalization ${params.polarization_normalization}" : '', - params.polarization_binarization ? "--polarization-binarization" : '', + params.polarization_min_marker_count ? "--polarization-min-marker-count ${params.polarization_min_marker_count}" : '', + params.polarization_transformation ? "--polarization-transformation ${params.polarization_transformation}" : '', params.colocalization_transformation ? "--colocalization-transformation ${params.colocalization_transformation}" : '', + params.polarization_n_permutations ? "--polarization-n-permutations ${params.polarization_n_permutations}" : '', (params.colocalization_neighbourhood_size instanceof Integer) ? "--colocalization-neighbourhood-size ${params.colocalization_neighbourhood_size}" : '', (params.colocalization_n_permutations instanceof Integer) ? "--colocalization-n-permutations ${params.colocalization_n_permutations}" : '', (params.colocalization_min_region_count instanceof Integer) ? "--colocalization-min-region-count ${params.colocalization_min_region_count}" : '', ].join(' ').trim() } + withName: PIXELATOR_LAYOUT { + ext.when = { !params.skip_layout } + ext.args = [ + params.no_node_marker_counts ? "--no-node-marker-counts" : '', + params.layout_algorithm ? "--layout-algorithm ${params.layout_algorithm} " : '', + ].join(' ').trim() + } + + withName: PIXELATOR_REPORT { ext.when = { !params.skip_report } } diff --git a/conf/test.config b/conf/test.config index e30c9238..5577d4e8 100644 --- a/conf/test.config +++ b/conf/test.config @@ -29,9 +29,12 @@ params { multiplet_recovery = true min_size = 2 max_size = 100000 + // dynamic_filter = false compute_polarization = true use_full_bipartite = true colocalization_min_region_count = 0 colocalization_n_permutations = 10 colocalization_neighbourhood_size = 1 + // using this since the default pmds_3d does not work on very small graphs + layout_algorithm = "fruchterman_reingold_3d" } diff --git a/docs/output.md b/docs/output.md index 5590fee4..36c88bc8 100644 --- a/docs/output.md +++ b/docs/output.md @@ -168,7 +168,7 @@ newly recovered components are stored in a file (components_recovered.csv). - `pixelator` - `annotate` - - `.dataset.pxl` + - `.annotated.dataset.pxl` - `.meta.json`: Command invocation metadata. - `.rank_vs_size.png` - `.raw_components_metrics.csv` @@ -193,7 +193,7 @@ edgelist to find putative cells, and it will generate a pxl file containing the - `analysis` - - `.dataset.pxl`: PXL file with the analysis results added to it. + - `.analysis.dataset.pxl`: PXL file with the analysis results added to it. - `.meta.json`: Command invocation metadata. - `.report.json`: Statistics for the analysis step. @@ -214,6 +214,30 @@ Currently, the following analysis are performed: 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. +### Compute layouts for visualization + +
+Output files + +- `pixelator` + + - `layout` + + - `.layout.dataset.pxl`: PXL file with the layout results added to it. + - `.meta.json`: Command invocation metadata. + - `.report.json`: Statistics for the layout step. + + - `logs` + - `.pixelator-layout.log`: pixelator log output. + +
+ +This step uses the `pixelator single-cell layout` command. +It will generate precomputed layouts that can be used to visualize cells +as part of the downstream analysis. + +This entire step can also be skipped using the `--skip_layout` option. + ### Generate reports
diff --git a/modules/local/pixelator/collect_metadata.nf b/modules/local/pixelator/collect_metadata.nf index 6f0f0ac1..1867abc0 100644 --- a/modules/local/pixelator/collect_metadata.nf +++ b/modules/local/pixelator/collect_metadata.nf @@ -6,10 +6,10 @@ process PIXELATOR_COLLECT_METADATA { label 'process_single' cache false - conda "bioconda::pixelator=0.16.2" + conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : - 'biocontainers/pixelator:0.16.2--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.17.1--pyhdfd78af_0' }" input: diff --git a/modules/local/pixelator/list_options.nf b/modules/local/pixelator/list_options.nf index fe59c369..70854636 100644 --- a/modules/local/pixelator/list_options.nf +++ b/modules/local/pixelator/list_options.nf @@ -2,10 +2,10 @@ process PIXELATOR_LIST_OPTIONS { label 'process_single' - conda "bioconda::pixelator=0.16.2" + conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : - 'biocontainers/pixelator:0.16.2--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.17.1--pyhdfd78af_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 bdd60d2a..0555312b 100644 --- a/modules/local/pixelator/single-cell/amplicon/main.nf +++ b/modules/local/pixelator/single-cell/amplicon/main.nf @@ -3,10 +3,10 @@ process PIXELATOR_AMPLICON { label 'process_low' - conda "bioconda::pixelator=0.16.2" + conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : - 'biocontainers/pixelator:0.16.2--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.17.1--pyhdfd78af_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 0ad705e0..4cab8af7 100644 --- a/modules/local/pixelator/single-cell/analysis/main.nf +++ b/modules/local/pixelator/single-cell/analysis/main.nf @@ -2,11 +2,10 @@ process PIXELATOR_ANALYSIS { tag "$meta.id" label 'process_medium' - - conda "bioconda::pixelator=0.16.2" + conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : - 'biocontainers/pixelator:0.16.2--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.17.1--pyhdfd78af_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 c7a44e82..38397dc1 100644 --- a/modules/local/pixelator/single-cell/annotate/main.nf +++ b/modules/local/pixelator/single-cell/annotate/main.nf @@ -3,10 +3,10 @@ process PIXELATOR_ANNOTATE { label 'process_medium' - conda "bioconda::pixelator=0.16.2" + conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : - 'biocontainers/pixelator:0.16.2--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.17.1--pyhdfd78af_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 17918e76..22ad72a5 100644 --- a/modules/local/pixelator/single-cell/collapse/main.nf +++ b/modules/local/pixelator/single-cell/collapse/main.nf @@ -2,10 +2,10 @@ process PIXELATOR_COLLAPSE { tag "$meta.id" label 'process_medium' - conda "bioconda::pixelator=0.16.2" + conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : - 'biocontainers/pixelator:0.16.2--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.17.1--pyhdfd78af_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 6a02b0ac..41d6d99f 100644 --- a/modules/local/pixelator/single-cell/demux/main.nf +++ b/modules/local/pixelator/single-cell/demux/main.nf @@ -3,10 +3,10 @@ process PIXELATOR_DEMUX { label 'process_medium' - conda "bioconda::pixelator=0.16.2" + conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : - 'biocontainers/pixelator:0.16.2--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.17.1--pyhdfd78af_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 96e56324..9c700e33 100644 --- a/modules/local/pixelator/single-cell/graph/main.nf +++ b/modules/local/pixelator/single-cell/graph/main.nf @@ -3,10 +3,10 @@ process PIXELATOR_GRAPH { label 'process_medium' - conda "bioconda::pixelator=0.16.2" + conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : - 'biocontainers/pixelator:0.16.2--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.17.1--pyhdfd78af_0' }" input: tuple val(meta), path(edge_list) diff --git a/modules/local/pixelator/single-cell/layout/main.nf b/modules/local/pixelator/single-cell/layout/main.nf new file mode 100644 index 00000000..331db7e9 --- /dev/null +++ b/modules/local/pixelator/single-cell/layout/main.nf @@ -0,0 +1,47 @@ +process PIXELATOR_LAYOUT { + tag "$meta.id" + label 'process_medium' + + + conda "bioconda::pixelator=0.17.1" + container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? + 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.17.1--pyhdfd78af_0' }" + + input: + tuple val(meta), path(data) + + output: + tuple val(meta), path("layout/*dataset.pxl") , emit: dataset + tuple val(meta), path("layout/*report.json") , emit: report_json + tuple val(meta), path("layout/*.meta.json") , emit: metadata + tuple val(meta), path("layout/*") , emit: all_results + tuple val(meta), path("*pixelator-layout.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-layout.log \\ + --verbose \\ + single-cell \\ + layout \\ + --output . \\ + $args \\ + $data + + 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/single-cell/qc/main.nf b/modules/local/pixelator/single-cell/qc/main.nf index 2650e5c4..fe9dbce3 100644 --- a/modules/local/pixelator/single-cell/qc/main.nf +++ b/modules/local/pixelator/single-cell/qc/main.nf @@ -3,10 +3,10 @@ process PIXELATOR_QC { label 'process_medium' - conda "bioconda::pixelator=0.16.2" + conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : - 'biocontainers/pixelator:0.16.2--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.17.1--pyhdfd78af_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 c71b9749..93e1b5d4 100644 --- a/modules/local/pixelator/single-cell/report/main.nf +++ b/modules/local/pixelator/single-cell/report/main.nf @@ -3,10 +3,10 @@ process PIXELATOR_REPORT { label 'process_low' - conda "bioconda::pixelator=0.16.2" + conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.16.2--pyhdfd78af_0' : - 'biocontainers/pixelator:0.16.2--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.17.1--pyhdfd78af_0' }" input: tuple val(meta), path(panel_file), val(panel) diff --git a/nextflow.config b/nextflow.config index 6af56176..6ff12860 100644 --- a/nextflow.config +++ b/nextflow.config @@ -54,16 +54,22 @@ params { compute_polarization = true compute_colocalization = true use_full_bipartite = false - polarization_normalization = "clr" - polarization_binarization = false + polarization_transformation = "log1p" + polarization_min_marker_count = 5 + polarization_n_permutations = 50 colocalization_transformation = "log1p" colocalization_neighbourhood_size = 1 colocalization_n_permutations = 50 colocalization_min_region_count = 5 + // layout options + no_node_marker_counts = false + layout_algorithm = "pmds_3d" + // skip options skip_report = false skip_analysis = false + skip_layout = false // Main pixelator container override pixelator_container = null diff --git a/nextflow_schema.json b/nextflow_schema.json index 1659e2d6..0ccaaf87 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -256,21 +256,28 @@ "description": "Use the bipartite graph instead of the one-node projection when computing polarization, coabundance and colocalization scores", "type": "boolean" }, - "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", + "polarization_transformation": { + "description": "Which transformation to use for the antibody counts when calculating polarity scores.", + "help_text": "- `raw`: use the raw counts.\n- `log1p`: use the log1p-transformed counts.\n", "type": "string", - "enum": ["raw", "clr", "denoise"], - "default": "clr" + "enum": ["raw", "log1p"], + "default": "log1p" }, - "polarization_binarization": { - "fa_icon": "fas binary", - "description": "Transform the antibody counts to 0-1 (binarize) when computing polarization", - "type": "boolean" + "polarization_n_permutations": { + "type": "integer", + "description": "Set the number of permutations use to compute the empirical z- and p-values for the polarity score", + "default": 50, + "minimum": 5 + }, + "polarization_min_marker_count": { + "type": "integer", + "description": "The minimum number of counts of a marker to calculate the polarity score in the component", + "default": 5, + "minimum": 2 }, "colocalization_transformation": { "type": "string", - "enum": ["raw", "clr", "log1p", "relative"], + "enum": ["raw", "log1p", "rate-diff"], "default": "log1p", "description": "Select the type of transformation to use on the node by antibody counts matrix when computing colocalization" }, @@ -294,6 +301,27 @@ } } }, + "layout_options": { + "title": "Options for pixelator layout command.", + "type": "object", + "properties": { + "skip_layout": { + "description": "Skip layout step", + "type": "boolean" + }, + "no_node_marker_counts": { + "description": "Skip adding marker counts to the layout.", + "type": "boolean", + "default": false + }, + "layout_algorithm": { + "description": "Select a layout algorithm to use. This can be specified as a comma separated list to compute multiple layouts. Possible values are: fruchterman_reingold, fruchterman_reingold_3d, kamada_kawai, kamada_kawai_3d, pmds, pmds_3d", + "type": "string", + "pattern": "(\\S+)?(,\\S+)*", + "default": "pmds_3d" + } + } + }, "report_options": { "title": "Options for pixelator report command.", "type": "object", diff --git a/workflows/pixelator.nf b/workflows/pixelator.nf index ec8735f3..5091a141 100644 --- a/workflows/pixelator.nf +++ b/workflows/pixelator.nf @@ -60,6 +60,7 @@ 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_LAYOUT } from '../modules/local/pixelator/single-cell/layout/main' /* ======================================================================================== @@ -198,6 +199,12 @@ workflow PIXELATOR { ch_versions = ch_versions.mix(PIXELATOR_ANALYSIS.out.versions.first()) + // + // MODULE: Run pixelator single-cell layout + PIXELATOR_LAYOUT ( ch_analysed ) + ch_layout = PIXELATOR_LAYOUT.out.dataset + ch_versions = ch_versions.mix(PIXELATOR_LAYOUT.out.versions.first()) + // Prepare all data needed by reporting for each pixelator step ch_amplicon_data = PIXELATOR_AMPLICON.out.report_json From e7d1d58861bc6a3b9c36d765ae7cb157d2d4cd5e Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Mon, 27 May 2024 14:41:50 +0200 Subject: [PATCH 170/260] Make graph step use process_high resource --- modules/local/pixelator/single-cell/graph/main.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/local/pixelator/single-cell/graph/main.nf b/modules/local/pixelator/single-cell/graph/main.nf index 9c700e33..35d9fb2a 100644 --- a/modules/local/pixelator/single-cell/graph/main.nf +++ b/modules/local/pixelator/single-cell/graph/main.nf @@ -1,6 +1,6 @@ process PIXELATOR_GRAPH { tag "$meta.id" - label 'process_medium' + label 'process_high' conda "bioconda::pixelator=0.17.1" From 99275c6675b6f2647393d617840a407878ffca38 Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Mon, 27 May 2024 14:42:34 +0200 Subject: [PATCH 171/260] Formatting fix --- modules/local/pixelator/single-cell/layout/main.nf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/local/pixelator/single-cell/layout/main.nf b/modules/local/pixelator/single-cell/layout/main.nf index 331db7e9..9b534c1a 100644 --- a/modules/local/pixelator/single-cell/layout/main.nf +++ b/modules/local/pixelator/single-cell/layout/main.nf @@ -18,7 +18,7 @@ process PIXELATOR_LAYOUT { tuple val(meta), path("layout/*") , emit: all_results tuple val(meta), path("*pixelator-layout.log"), emit: log - path "versions.yml" , emit: versions + path "versions.yml" , emit: versions when: task.ext.when == null || task.ext.when From edab0ddf6b57aabaa48bdbffa2517d8db4f2ed15 Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Mon, 27 May 2024 14:43:10 +0200 Subject: [PATCH 172/260] Skip layout in small test config --- conf/test.config | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/conf/test.config b/conf/test.config index 5577d4e8..d9ee29df 100644 --- a/conf/test.config +++ b/conf/test.config @@ -35,6 +35,10 @@ params { colocalization_min_region_count = 0 colocalization_n_permutations = 10 colocalization_neighbourhood_size = 1 + + // For now skip the layout step since it is very slow on these + // small test datasets + skip_layout = true // using this since the default pmds_3d does not work on very small graphs layout_algorithm = "fruchterman_reingold_3d" } From 4a28425f9a3730c7ff0023d3236a5c3dcde70aef Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Mon, 27 May 2024 14:47:52 +0200 Subject: [PATCH 173/260] Clean-up --- conf/test.config | 1 - 1 file changed, 1 deletion(-) diff --git a/conf/test.config b/conf/test.config index d9ee29df..3c1692b2 100644 --- a/conf/test.config +++ b/conf/test.config @@ -29,7 +29,6 @@ params { multiplet_recovery = true min_size = 2 max_size = 100000 - // dynamic_filter = false compute_polarization = true use_full_bipartite = true colocalization_min_region_count = 0 From 07b05009088414e1171efd3b837c71c2e729cb8b Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Mon, 27 May 2024 15:08:09 +0200 Subject: [PATCH 174/260] Update changelog --- CHANGELOG.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92ef4f0e..cf29e65e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,26 @@ 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). +## [[1.2.0](https://github.com/nf-core/pixelator/releases/tag/1.1.0)] - 2024-05-?? + +### Enhancements & fixes + +- [[PR #90](https://github.com/nf-core/pixelator/pull/90)] - Update pixelator to 0.17.1 +- [[PR #90](https://github.com/nf-core/pixelator/pull/90)] - Add `pixelator single-cell layout` command +- [[PR #90](https://github.com/nf-core/pixelator/pull/90)] - `graph` step is now using `process_high` as it's resource tag + +### Software dependencies + +| Dependency | Old version | New version | +| ----------- | ----------- | ----------- | +| `pixelator` | 0.16.2 | 0.17.1 | + +> **NB:** Dependency has been **updated** if both old and new version information is present. +> +> **NB:** Dependency has been **added** if just the new version information is present. +> +> **NB:** Dependency has been **removed** if new version information isn't present. + ## [[1.1.0](https://github.com/nf-core/pixelator/releases/tag/1.1.0)] - 2024-03-29 ### Enhancements & fixes From 1d2fcde58482e9aa233a0b713cfa6d3c18984ab0 Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Mon, 27 May 2024 15:44:59 +0200 Subject: [PATCH 175/260] Fix linting issue --- nextflow_schema.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/nextflow_schema.json b/nextflow_schema.json index 0ccaaf87..2a87c891 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -538,6 +538,9 @@ { "$ref": "#/definitions/analysis_options" }, + { + "$ref": "#/definitions/layout_options" + }, { "$ref": "#/definitions/report_options" }, From bd29d2c156ed654d7bb9cb4953c374539f3500b2 Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Mon, 27 May 2024 15:54:21 +0200 Subject: [PATCH 176/260] Pass layout to the report stage --- modules/local/pixelator/single-cell/report/main.nf | 1 + subworkflows/local/generate_reports.nf | 5 +++++ workflows/pixelator.nf | 5 ++++- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/local/pixelator/single-cell/report/main.nf b/modules/local/pixelator/single-cell/report/main.nf index 93e1b5d4..6d891a62 100644 --- a/modules/local/pixelator/single-cell/report/main.nf +++ b/modules/local/pixelator/single-cell/report/main.nf @@ -18,6 +18,7 @@ process PIXELATOR_REPORT { path graph_data , stageAs: "results/graph/*" path annotate_data , stageAs: "results/annotate/*" path analysis_data , stageAs: "results/analysis/*" + path layout_data , stageAs: "results/layout/*" output: diff --git a/subworkflows/local/generate_reports.nf b/subworkflows/local/generate_reports.nf index dc5fb42c..73c599b7 100644 --- a/subworkflows/local/generate_reports.nf +++ b/subworkflows/local/generate_reports.nf @@ -24,6 +24,7 @@ workflow GENERATE_REPORTS { graph_data // channel: [meta, [path, ...]] annotate_data // channel: [meta, [path, ...]] analysis_data // channel: [meta, [path, ...]] + layout_data // channel: [meta, [path, ...]] main: ch_versions = Channel.empty() @@ -57,6 +58,7 @@ workflow GENERATE_REPORTS { 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] } + ch_layout_col = layout_data.map { meta, data -> [meta.id, data] } // // Combine all inputs and group them, then split them up again. @@ -87,6 +89,7 @@ workflow GENERATE_REPORTS { .concat ( ch_graph_col ) .concat ( ch_annotate_col ) .concat ( ch_analysis_col ) + .concat ( ch_layout_col ) .groupTuple (size: 10) // @@ -108,6 +111,7 @@ workflow GENERATE_REPORTS { 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() : [] } + ch_layout_grouped = ch_report_data.map { id, data -> data[10] ? data[10].flatten() : [] } // // MODULE: Run pixelator single-cell report for each samples @@ -124,6 +128,7 @@ workflow GENERATE_REPORTS { ch_graph_grouped, ch_annotate_grouped, ch_analysis_grouped, + ch_layout_grouped, ) ch_versions = ch_versions.mix(PIXELATOR_REPORT.out.versions.first()) diff --git a/workflows/pixelator.nf b/workflows/pixelator.nf index 5091a141..ab03c7ad 100644 --- a/workflows/pixelator.nf +++ b/workflows/pixelator.nf @@ -201,6 +201,7 @@ workflow PIXELATOR { // // MODULE: Run pixelator single-cell layout + // PIXELATOR_LAYOUT ( ch_analysed ) ch_layout = PIXELATOR_LAYOUT.out.dataset ch_versions = ch_versions.mix(PIXELATOR_LAYOUT.out.versions.first()) @@ -230,6 +231,7 @@ workflow PIXELATOR { 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_layout_data = PIXELATOR_ANALYSIS.out.all_results GENERATE_REPORTS( ch_cat_panel_files, @@ -240,7 +242,8 @@ workflow PIXELATOR { ch_collapse_data, ch_cluster_data, ch_annotate_data, - ch_analysis_data + ch_analysis_data, + ch_layout_data ) ch_versions = ch_versions.mix(GENERATE_REPORTS.out.versions) From 1db4263b0497c9599be0e33ee10896d913bbf571 Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Mon, 27 May 2024 16:01:53 +0200 Subject: [PATCH 177/260] Fix copy-paste-error --- workflows/pixelator.nf | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/workflows/pixelator.nf b/workflows/pixelator.nf index ab03c7ad..018ef55e 100644 --- a/workflows/pixelator.nf +++ b/workflows/pixelator.nf @@ -231,7 +231,9 @@ workflow PIXELATOR { 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_layout_data = PIXELATOR_ANALYSIS.out.all_results + ch_layout_data = PIXELATOR_LAYOUT.out.report_json + .concat(PIXELATOR_LAYOUT.out.metadata) + .groupTuple(size: 2) GENERATE_REPORTS( ch_cat_panel_files, From ce90f8bd2ac2d9f91ed1d6c3fe3999f5d00fbb3b Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Mon, 27 May 2024 16:08:18 +0200 Subject: [PATCH 178/260] annotate step use `process_high` resources --- CHANGELOG.md | 1 + modules/local/pixelator/single-cell/annotate/main.nf | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cf29e65e..7de97f08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [[PR #90](https://github.com/nf-core/pixelator/pull/90)] - Update pixelator to 0.17.1 - [[PR #90](https://github.com/nf-core/pixelator/pull/90)] - Add `pixelator single-cell layout` command - [[PR #90](https://github.com/nf-core/pixelator/pull/90)] - `graph` step is now using `process_high` as it's resource tag +- [[PR #90](https://github.com/nf-core/pixelator/pull/90)] - `annotate` step is now using `process_high` as it's resource tag ### Software dependencies diff --git a/modules/local/pixelator/single-cell/annotate/main.nf b/modules/local/pixelator/single-cell/annotate/main.nf index 38397dc1..0d3dac4e 100644 --- a/modules/local/pixelator/single-cell/annotate/main.nf +++ b/modules/local/pixelator/single-cell/annotate/main.nf @@ -1,6 +1,6 @@ process PIXELATOR_ANNOTATE { tag "$meta.id" - label 'process_medium' + label 'process_high' conda "bioconda::pixelator=0.17.1" From 1e557b2cba77b18e48cc1cf515e218e024dd7667 Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Mon, 27 May 2024 16:31:03 +0200 Subject: [PATCH 179/260] Clean-up the changelog a bit --- CHANGELOG.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7de97f08..9cded24b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,8 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [[PR #90](https://github.com/nf-core/pixelator/pull/90)] - Update pixelator to 0.17.1 - [[PR #90](https://github.com/nf-core/pixelator/pull/90)] - Add `pixelator single-cell layout` command -- [[PR #90](https://github.com/nf-core/pixelator/pull/90)] - `graph` step is now using `process_high` as it's resource tag -- [[PR #90](https://github.com/nf-core/pixelator/pull/90)] - `annotate` step is now using `process_high` as it's resource tag +- [[PR #90](https://github.com/nf-core/pixelator/pull/90)] - The `graph` and `annotate` steps are now using `process_high` as their resource tags ### Software dependencies From c41065b1daf9c7884ad2efdf1facae6002181406 Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Mon, 27 May 2024 16:56:21 +0200 Subject: [PATCH 180/260] process_high->64GB ram/process_high_memory->128GB --- conf/base.config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/conf/base.config b/conf/base.config index 0d55af3b..38505d1e 100644 --- a/conf/base.config +++ b/conf/base.config @@ -41,14 +41,14 @@ process { } withLabel:process_high { cpus = { check_max( 12 * task.attempt, 'cpus' ) } - memory = { check_max( 72.GB * task.attempt, 'memory' ) } + memory = { check_max( 64.GB * task.attempt, 'memory' ) } time = { check_max( 16.h * task.attempt, 'time' ) } } withLabel:process_long { time = { check_max( 20.h * task.attempt, 'time' ) } } withLabel:process_high_memory { - memory = { check_max( 200.GB * task.attempt, 'memory' ) } + memory = { check_max( 128.GB * task.attempt, 'memory' ) } } withLabel:error_ignore { errorStrategy = 'ignore' From 71b413c647bc2ccabefe07d0f34592d57247c7a7 Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Mon, 27 May 2024 17:00:37 +0200 Subject: [PATCH 181/260] Update changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9cded24b..61bcab27 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [[PR #90](https://github.com/nf-core/pixelator/pull/90)] - Update pixelator to 0.17.1 - [[PR #90](https://github.com/nf-core/pixelator/pull/90)] - Add `pixelator single-cell layout` command - [[PR #90](https://github.com/nf-core/pixelator/pull/90)] - The `graph` and `annotate` steps are now using `process_high` as their resource tags +- [[PR #91](https://github.com/nf-core/pixelator/pull/91)] - Set `process_high` to use 64GB of RAM and `process_high_memory` to use 128GB of RAM ### Software dependencies From f1b288c7f64f6e07915fe397f2b5c21bd2aafc91 Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Tue, 28 May 2024 08:17:26 +0200 Subject: [PATCH 182/260] Minor documenation fixes --- CHANGELOG.md | 3 ++- docs/output.md | 6 ++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 61bcab27..d6d2e040 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ 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). -## [[1.2.0](https://github.com/nf-core/pixelator/releases/tag/1.1.0)] - 2024-05-?? +## [[1.2.0](https://github.com/nf-core/pixelator/releases/tag/1.2.0)] - 2024-05-?? ### Enhancements & fixes @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [[PR #90](https://github.com/nf-core/pixelator/pull/90)] - Add `pixelator single-cell layout` command - [[PR #90](https://github.com/nf-core/pixelator/pull/90)] - The `graph` and `annotate` steps are now using `process_high` as their resource tags - [[PR #91](https://github.com/nf-core/pixelator/pull/91)] - Set `process_high` to use 64GB of RAM and `process_high_memory` to use 128GB of RAM +- [[PR #92](https://github.com/nf-core/pixelator/pull/92)] - Minor touch-ups to the documentation ### Software dependencies diff --git a/docs/output.md b/docs/output.md index 36c88bc8..cda70ef8 100644 --- a/docs/output.md +++ b/docs/output.md @@ -168,12 +168,10 @@ newly recovered components are stored in a file (components_recovered.csv). - `pixelator` - `annotate` - - `.annotated.dataset.pxl` + - `.annotate.dataset.pxl` - `.meta.json`: Command invocation metadata. - - `.rank_vs_size.png` - - `.raw_components_metrics.csv` + - `.raw_components_metrics.csv.gz` - `.report.json`: Statistics for the analysis step. - - `.umap.png` - `logs` - `.pixelator-annotate.log`: pixelator log output.
From 48dbe6299f4040a3167c46f2c48c9b519739ec9b Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 28 May 2024 09:20:22 +0200 Subject: [PATCH 183/260] Fix template merge issues --- conf/modules.config | 109 ------------------ conf/test_full.config | 4 - modules.json | 5 - .../dumpsoftwareversions/environment.yml | 7 -- .../custom/dumpsoftwareversions/main.nf | 24 ---- .../custom/dumpsoftwareversions/meta.yml | 37 ------ .../templates/dumpsoftwareversions.py | 103 ----------------- .../dumpsoftwareversions/tests/main.nf.test | 43 ------- .../tests/main.nf.test.snap | 33 ------ .../dumpsoftwareversions/tests/tags.yml | 2 - workflows/pixelator.nf | 3 +- 11 files changed, 1 insertion(+), 369 deletions(-) delete mode 100644 modules/nf-core/custom/dumpsoftwareversions/environment.yml delete mode 100644 modules/nf-core/custom/dumpsoftwareversions/main.nf delete mode 100644 modules/nf-core/custom/dumpsoftwareversions/meta.yml delete mode 100755 modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py delete mode 100644 modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test delete mode 100644 modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap delete mode 100644 modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml diff --git a/conf/modules.config b/conf/modules.config index 065b241c..97c0a4b3 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -47,8 +47,6 @@ process { } withName: RENAME_READS { -<<<<<<< HEAD -======= publishDir = [ enabled: false, path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, @@ -153,114 +151,7 @@ process { ].join(' ').trim() } - - withName: PIXELATOR_REPORT { - ext.when = { !params.skip_report } - } - - - withName: CUSTOM_DUMPSOFTWAREVERSIONS { ->>>>>>> dev - publishDir = [ - enabled: false, - path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, - ] - } -<<<<<<< HEAD - - - // use explicit (params.my_option instanceof Integer) checks to avoid issues with 0 evaluating false - // since most pixelator flags do accept zero as a value - - withName: PIXELATOR_AMPLICON { - ext.args = { - [ - "--design ${meta.design}", - ].join(' ').trim() - } - } - - withName: PIXELATOR_QC { - // Options for pixelator preqc - ext.args = { - [ - "--design ${meta.design}", - (params.trim_front instanceof Integer)? "--trim-front ${params.trim_front}": '', - (params.trim_tail instanceof Integer) ? "--trim-tail ${params.trim_tail}": '', - (params.max_length instanceof Integer) ? "--max-length ${params.max_length}": '', - (params.min_length instanceof Integer) ? "--min-length ${params.min_length}": '', - (params.max_n_bases instanceof Integer) ? "--max-n-bases ${params.max_n_bases}": '', - (params.avg_qual instanceof Integer)? "--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}": '', - ].join(' ').trim() - } - } - - withName: PIXELATOR_DEMUX { - ext.args = { - [ - "--design ${meta.design}", - (params.demux_mismatches != null) ? "--mismatches ${params.demux_mismatches}": '', - (params.demux_min_length instanceof Integer) ? "--mismatches ${params.demux_min_length}": '', - ].join(' ').trim() - } - } - - withName: PIXELATOR_COLLAPSE { - ext.args = [ - 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}": '', - params.collapse_min_count ? "--min-count ${params.collapse_min_count}": '', - params.collapse_use_counts ? "--use-counts": '', - ].join(' ').trim() - } - - withName: PIXELATOR_GRAPH { - ext.args = [ - params.multiplet_recovery ? "--multiplet-recovery" : '', - params.leiden_iterations ? "--leiden-iterations ${params.leiden_iterations}" : '', - params.graph_min_count ? "--min-count ${params.graph_min_count}" : '', - ].join(' ').trim() - } - - withName: PIXELATOR_ANNOTATE { - ext.args = [ - (params.min_size instanceof Integer) ? "--min-size ${params.min_size}" : '', - (params.max_size instanceof Integer) ? "--max-size ${params.max_size}" : '', - params.dynamic_filter ? "--dynamic-filter ${params.dynamic_filter}" : '', - params.aggregate_calling ? "--aggregate-calling" : '', - ].join(' ').trim() - } - - 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.polarization_normalization ? "--polarization-normalization ${params.polarization_normalization}" : '', - params.polarization_binarization ? "--polarization-binarization" : '', - params.colocalization_transformation ? "--colocalization-transformation ${params.colocalization_transformation}" : '', - (params.colocalization_neighbourhood_size instanceof Integer) ? "--colocalization-neighbourhood-size ${params.colocalization_neighbourhood_size}" : '', - (params.colocalization_n_permutations instanceof Integer) ? "--colocalization-n-permutations ${params.colocalization_n_permutations}" : '', - (params.colocalization_min_region_count instanceof Integer) ? "--colocalization-min-region-count ${params.colocalization_min_region_count}" : '', - ].join(' ').trim() - } - withName: PIXELATOR_REPORT { ext.when = { !params.skip_report } } - -======= ->>>>>>> dev } diff --git a/conf/test_full.config b/conf/test_full.config index b0352580..2690a944 100644 --- a/conf/test_full.config +++ b/conf/test_full.config @@ -14,10 +14,6 @@ params { config_profile_name = 'Full test profile' config_profile_description = 'Full test dataset to check pipeline function' -<<<<<<< HEAD input = params.pipelines_testdata_base_path + "pixelator/samplesheet/samplesheet_full.csv" -======= - input = "https://raw.githubusercontent.com/nf-core/test-datasets/pixelator/samplesheet/samplesheet_full.csv" ->>>>>>> dev input_basedir = "s3://pixelgen-technologies-datasets/nf-core-pixelator/testdata/full/" } diff --git a/modules.json b/modules.json index 5cf0d6b3..785cce21 100644 --- a/modules.json +++ b/modules.json @@ -9,11 +9,6 @@ "branch": "master", "git_sha": "285a50500f9e02578d90b3ce6382ea3c30216acd", "installed_by": ["modules"] - }, - "custom/dumpsoftwareversions": { - "branch": "master", - "git_sha": "de45447d060b8c8b98575bc637a4a575fd0638e1", - "installed_by": ["modules"] } } }, diff --git a/modules/nf-core/custom/dumpsoftwareversions/environment.yml b/modules/nf-core/custom/dumpsoftwareversions/environment.yml deleted file mode 100644 index b48ced26..00000000 --- a/modules/nf-core/custom/dumpsoftwareversions/environment.yml +++ /dev/null @@ -1,7 +0,0 @@ -name: custom_dumpsoftwareversions -channels: - - conda-forge - - bioconda - - defaults -dependencies: - - bioconda::multiqc=1.20 diff --git a/modules/nf-core/custom/dumpsoftwareversions/main.nf b/modules/nf-core/custom/dumpsoftwareversions/main.nf deleted file mode 100644 index 105f9265..00000000 --- a/modules/nf-core/custom/dumpsoftwareversions/main.nf +++ /dev/null @@ -1,24 +0,0 @@ -process CUSTOM_DUMPSOFTWAREVERSIONS { - label 'process_single' - - // Requires `pyyaml` which does not have a dedicated container but is in the MultiQC container - conda "${moduleDir}/environment.yml" - container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/multiqc:1.20--pyhdfd78af_0' : - 'biocontainers/multiqc:1.20--pyhdfd78af_0' }" - - input: - path versions - - output: - path "software_versions.yml" , emit: yml - path "software_versions_mqc.yml", emit: mqc_yml - path "versions.yml" , emit: versions - - when: - task.ext.when == null || task.ext.when - - script: - def args = task.ext.args ?: '' - template 'dumpsoftwareversions.py' -} diff --git a/modules/nf-core/custom/dumpsoftwareversions/meta.yml b/modules/nf-core/custom/dumpsoftwareversions/meta.yml deleted file mode 100644 index 5f15a5fd..00000000 --- a/modules/nf-core/custom/dumpsoftwareversions/meta.yml +++ /dev/null @@ -1,37 +0,0 @@ -# yaml-language-server: $schema=https://raw.githubusercontent.com/nf-core/modules/master/modules/meta-schema.json -name: custom_dumpsoftwareversions -description: Custom module used to dump software versions within the nf-core pipeline template -keywords: - - custom - - dump - - version -tools: - - custom: - description: Custom module used to dump software versions within the nf-core pipeline template - homepage: https://github.com/nf-core/tools - documentation: https://github.com/nf-core/tools - licence: ["MIT"] -input: - - versions: - type: file - description: YML file containing software versions - pattern: "*.yml" -output: - - yml: - type: file - description: Standard YML file containing software versions - pattern: "software_versions.yml" - - mqc_yml: - type: file - description: MultiQC custom content YML file containing software versions - pattern: "software_versions_mqc.yml" - - versions: - type: file - description: File containing software versions - pattern: "versions.yml" -authors: - - "@drpatelh" - - "@grst" -maintainers: - - "@drpatelh" - - "@grst" diff --git a/modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py b/modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py deleted file mode 100755 index 4a993608..00000000 --- a/modules/nf-core/custom/dumpsoftwareversions/templates/dumpsoftwareversions.py +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env python - - -"""Provide functions to merge multiple versions.yml files.""" - - -import yaml -import platform -from textwrap import dedent - - -def _make_versions_html(versions): - """Generate a tabular HTML output of all versions for MultiQC.""" - html = [ - dedent( - """\\ - -
Process Name \\", + " \\ Software Version
CUSTOM_DUMPSOFTWAREVERSIONSpython3.11.7
yaml5.4.1
TOOL1tool10.11.9
TOOL2tool21.9
WorkflowNextflow
File typeConventional base calls
File typeConventional base calls
File typeConventional base calls
File typeConventional base calls
File typeConventional base calls
File typeConventional base calls
File typeConventional base calls
File typeConventional base calls
File typeConventional base calls
File typeConventional base calls
- - - - - - - - """ - ) - ] - for process, tmp_versions in sorted(versions.items()): - html.append("") - for i, (tool, version) in enumerate(sorted(tmp_versions.items())): - html.append( - dedent( - f"""\\ - - - - - - """ - ) - ) - html.append("") - html.append("
Process Name Software Version
{process if (i == 0) else ''}{tool}{version}
") - return "\\n".join(html) - - -def main(): - """Load all version files and generate merged output.""" - versions_this_module = {} - versions_this_module["${task.process}"] = { - "python": platform.python_version(), - "yaml": yaml.__version__, - } - - with open("$versions") as f: - versions_by_process = ( - yaml.load(f, Loader=yaml.BaseLoader) | versions_this_module - ) - - # aggregate versions by the module name (derived from fully-qualified process name) - versions_by_module = {} - for process, process_versions in versions_by_process.items(): - module = process.split(":")[-1] - try: - if versions_by_module[module] != process_versions: - raise AssertionError( - "We assume that software versions are the same between all modules. " - "If you see this error-message it means you discovered an edge-case " - "and should open an issue in nf-core/tools. " - ) - except KeyError: - versions_by_module[module] = process_versions - - versions_by_module["Workflow"] = { - "Nextflow": "$workflow.nextflow.version", - "$workflow.manifest.name": "$workflow.manifest.version", - } - - versions_mqc = { - "id": "software_versions", - "section_name": "${workflow.manifest.name} Software Versions", - "section_href": "https://github.com/${workflow.manifest.name}", - "plot_type": "html", - "description": "are collected at run time from the software output.", - "data": _make_versions_html(versions_by_module), - } - - with open("software_versions.yml", "w") as f: - yaml.dump(versions_by_module, f, default_flow_style=False) - with open("software_versions_mqc.yml", "w") as f: - yaml.dump(versions_mqc, f, default_flow_style=False) - - with open("versions.yml", "w") as f: - yaml.dump(versions_this_module, f, default_flow_style=False) - - -if __name__ == "__main__": - main() diff --git a/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test deleted file mode 100644 index b1e1630b..00000000 --- a/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test +++ /dev/null @@ -1,43 +0,0 @@ -nextflow_process { - - name "Test Process CUSTOM_DUMPSOFTWAREVERSIONS" - script "../main.nf" - process "CUSTOM_DUMPSOFTWAREVERSIONS" - tag "modules" - tag "modules_nfcore" - tag "custom" - tag "dumpsoftwareversions" - tag "custom/dumpsoftwareversions" - - test("Should run without failures") { - when { - process { - """ - def tool1_version = ''' - TOOL1: - tool1: 0.11.9 - '''.stripIndent() - - def tool2_version = ''' - TOOL2: - tool2: 1.9 - '''.stripIndent() - - input[0] = Channel.of(tool1_version, tool2_version).collectFile() - """ - } - } - - then { - assertAll( - { assert process.success }, - { assert snapshot( - process.out.versions, - file(process.out.mqc_yml[0]).readLines()[0..10], - file(process.out.yml[0]).readLines()[0..7] - ).match() - } - ) - } - } -} diff --git a/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap b/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap deleted file mode 100644 index 5f59a936..00000000 --- a/modules/nf-core/custom/dumpsoftwareversions/tests/main.nf.test.snap +++ /dev/null @@ -1,33 +0,0 @@ -{ - "Should run without failures": { - "content": [ - [ - "versions.yml:md5,76d454d92244589d32455833f7c1ba6d" - ], - [ - "data: \"\\n\\n \\n \\n \\n \\n \\n \\n \\n\\", - " \\n\\n\\n \\n \\n\\", - " \\ \\n\\n\\n\\n \\n \\", - " \\ \\n \\n\\n\\n\\n\\", - " \\n\\n \\n \\n\\", - " \\ \\n\\n\\n\\n\\n\\n \\n\\", - " \\ \\n \\n\\n\\n\\n\\", - " \\n\\n \\n \\n\\" - ], - [ - "CUSTOM_DUMPSOFTWAREVERSIONS:", - " python: 3.11.7", - " yaml: 5.4.1", - "TOOL1:", - " tool1: 0.11.9", - "TOOL2:", - " tool2: '1.9'", - "Workflow:" - ] - ], - "timestamp": "2024-01-09T23:01:18.710682" - } -} \ No newline at end of file diff --git a/modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml b/modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml deleted file mode 100644 index 405aa24a..00000000 --- a/modules/nf-core/custom/dumpsoftwareversions/tests/tags.yml +++ /dev/null @@ -1,2 +0,0 @@ -custom/dumpsoftwareversions: - - modules/nf-core/custom/dumpsoftwareversions/** diff --git a/workflows/pixelator.nf b/workflows/pixelator.nf index 63b1b014..0058bf3b 100644 --- a/workflows/pixelator.nf +++ b/workflows/pixelator.nf @@ -40,7 +40,6 @@ include { GENERATE_REPORTS } from '../subworkflows/local/generate_rep // // MODULE: Installed directly from nf-core/modules // -include { CUSTOM_DUMPSOFTWAREVERSIONS } from '../modules/nf-core/custom/dumpsoftwareversions/main' include { CAT_FASTQ } from '../modules/nf-core/cat/fastq/main' /* ======================================================================================== @@ -93,7 +92,7 @@ workflow PIXELATOR { } // - // MODULE: Dump pixelaor and pipeline information + // MODULE: Dump pixelator and pipeline information // PIXELATOR_COLLECT_METADATA () ch_versions = ch_versions.mix(PIXELATOR_COLLECT_METADATA.out.versions) From 64375e92a9e5a1f4ce6453db8074390a7b5c7d85 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 28 May 2024 09:27:08 +0200 Subject: [PATCH 184/260] Remove some old nf-core lint opt-outs --- .nf-core.yml | 5 ----- .prettierignore | 1 - 2 files changed, 6 deletions(-) diff --git a/.nf-core.yml b/.nf-core.yml index f447682f..15b6e700 100644 --- a/.nf-core.yml +++ b/.nf-core.yml @@ -6,8 +6,3 @@ lint: files_exist: - assets/multiqc_config.yml - conf/igenomes.config - files_unchanged: - - lib/NfcoreTemplate.groovy - - .prettierignore - # Skip warning for a removed a reference to nf-test - - .github/PULL_REQUEST_TEMPLATE.md diff --git a/.prettierignore b/.prettierignore index 651a8862..437d763d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -1,7 +1,6 @@ email_template.html adaptivecard.json slackreport.json -.devcontainer/devcontainer.json .nextflow* work/ data/ From ea9548cd2c4250891772ea915f020f01f24f57df Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 28 May 2024 09:26:10 +0200 Subject: [PATCH 185/260] Merge RENAME_READS module functionality into PIXELATOR_AMPLICON --- .../pixelator/single-cell/amplicon/main.nf | 22 ++++++++- modules/local/rename_reads.nf | 45 ------------------- workflows/pixelator.nf | 11 +---- 3 files changed, 22 insertions(+), 56 deletions(-) delete mode 100644 modules/local/rename_reads.nf diff --git a/modules/local/pixelator/single-cell/amplicon/main.nf b/modules/local/pixelator/single-cell/amplicon/main.nf index 0555312b..b73f696a 100644 --- a/modules/local/pixelator/single-cell/amplicon/main.nf +++ b/modules/local/pixelator/single-cell/amplicon/main.nf @@ -26,7 +26,19 @@ process PIXELATOR_AMPLICON { def prefix = task.ext.prefix ?: "${meta.id}" def args = task.ext.args ?: '' + // 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}${getFileSuffix(reads)}" ]] + : reads.withIndex().collect { entry, index -> [ entry, "${prefix}_${index + 1}${getFileSuffix(entry)}" ] } + + def rename_to = old_new_pairs*.join(' ').join(' ') + def renamed_reads = 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 + pixelator \\ --cores $task.cpus \\ --log-file ${prefix}.pixelator-amplicon.log \\ @@ -35,7 +47,7 @@ process PIXELATOR_AMPLICON { amplicon \\ --output . \\ $args \\ - ${reads} + ${renamed_reads} cat <<-END_VERSIONS > versions.yml "${task.process}": @@ -43,3 +55,11 @@ process PIXELATOR_AMPLICON { END_VERSIONS """ } + + +// for .gz files also include the second to last extension if it is present. E.g., .fasta.gz +// Source: nf-core/modules/cat/cat +def getFileSuffix(filename) { + def match = filename =~ /^.*?((\.\w{1,5})?(\.\w{1,5}\.gz$))/ + return match ? match[0][1] : filename.substring(filename.lastIndexOf('.')) +} diff --git a/modules/local/rename_reads.nf b/modules/local/rename_reads.nf deleted file mode 100644 index 024bd2de..00000000 --- a/modules/local/rename_reads.nf +++ /dev/null @@ -1,45 +0,0 @@ -process RENAME_READS { - 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) - - 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/workflows/pixelator.nf b/workflows/pixelator.nf index 0058bf3b..f7ee3408 100644 --- a/workflows/pixelator.nf +++ b/workflows/pixelator.nf @@ -50,7 +50,6 @@ include { CAT_FASTQ } from '../modules/nf-core/cat/fastq/main' // // MODULE: Defined locally // -include { RENAME_READS } from '../modules/local/rename_reads' include { PIXELATOR_COLLECT_METADATA } from '../modules/local/pixelator/collect_metadata' include { PIXELATOR_AMPLICON } from '../modules/local/pixelator/single-cell/amplicon/main' include { PIXELATOR_QC } from '../modules/local/pixelator/single-cell/qc/main' @@ -126,18 +125,10 @@ workflow PIXELATOR { ch_versions = ch_versions.mix(CAT_FASTQ.out.versions.first()) - - // - // MODULE: Rename input reads to match the sample ids from the samplesheet - // - RENAME_READS ( ch_cat_fastq ) - ch_renamed_reads = RENAME_READS.out.reads - ch_versions = ch_versions.mix(RENAME_READS.out.versions.first()) - // // MODULE: Run pixelator single-cell amplicon // - PIXELATOR_AMPLICON ( ch_renamed_reads ) + PIXELATOR_AMPLICON ( ch_cat_fastq ) ch_merged = PIXELATOR_AMPLICON.out.merged ch_versions = ch_versions.mix(PIXELATOR_AMPLICON.out.versions.first()) From ca85ee44b52622aa88ad7d98120805a073304a94 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 28 May 2024 09:55:26 +0200 Subject: [PATCH 186/260] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b3018409..6a02d146 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [[PR #90](https://github.com/nf-core/pixelator/pull/90)] - The `graph` and `annotate` steps are now using `process_high` as their resource tags - [[PR #91](https://github.com/nf-core/pixelator/pull/91)] - Set `process_high` to use 64GB of RAM and `process_high_memory` to use 128GB of RAM - [[PR #92](https://github.com/nf-core/pixelator/pull/92)] - Minor touch-ups to the documentation +- [[PR #93](https://github.com/nf-core/pixelator/pull/93)] - Merge RENAME_READS functionality into PIXELATOR_AMPLICON ### Software dependencies From 6407896a646db8d2466e1e6b7deebb10fb577102 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 28 May 2024 10:02:28 +0200 Subject: [PATCH 187/260] Remove RENAME_READS from modules.config --- conf/modules.config | 7 ------- 1 file changed, 7 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 97c0a4b3..94912907 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -46,13 +46,6 @@ process { publishDir = [ enabled: false ] } - withName: RENAME_READS { - publishDir = [ - enabled: false, - path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, - ] - } - // use explicit (params.my_option instanceof Integer) checks to avoid issues with 0 evaluating false // since most pixelator flags do accept zero as a value From a2b315e96b7dd4ed01c12ea01aeac6cad4976c0a Mon Sep 17 00:00:00 2001 From: Florian De Temmerman <69114541+fbdtemme@users.noreply.github.com> Date: Tue, 28 May 2024 13:08:12 +0200 Subject: [PATCH 188/260] Update conf/test.config Co-authored-by: Maxime U Garcia --- conf/test.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/test.config b/conf/test.config index 17d1000c..12b8cb81 100644 --- a/conf/test.config +++ b/conf/test.config @@ -25,7 +25,7 @@ params { // Input data input = params.pipelines_testdata_base_path + 'pixelator/samplesheet/samplesheet.csv' - input_basedir = "https://raw.githubusercontent.com/nf-core/test-datasets/pixelator/testdata" + input_basedir = params.pipelines_testdata_base_path + 'pixelator/testdata' multiplet_recovery = true min_size = 2 From 9bf3bf1d9ed97dc87b58b3ee00cf4dfa158737d5 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 28 May 2024 13:13:37 +0200 Subject: [PATCH 189/260] Update release date in CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a02d146..a461cdd2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ 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). -## [[1.2.0](https://github.com/nf-core/pixelator/releases/tag/1.2.0)] - 2024-05-?? +## [[1.2.0](https://github.com/nf-core/pixelator/releases/tag/1.2.0)] - 2024-05-28 ### Enhancements & fixes From a13c0df974cdf67a2b75c25066fd8e11f1746a8d Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 29 May 2024 17:40:30 +0200 Subject: [PATCH 190/260] chore: bump to 1.3.0dev --- nextflow.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextflow.config b/nextflow.config index 48220fca..e268fb93 100644 --- a/nextflow.config +++ b/nextflow.config @@ -297,7 +297,7 @@ manifest { description = """Pipeline for analysis of Molecular Pixelation assays""" mainScript = 'main.nf' nextflowVersion = '!>=23.04.0' - version = '1.2.0' + version = '1.3.0dev' doi = '10.1101/2023.06.05.543770' } From 559d95a11db289474f7dd0131042153d13f34128 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 29 May 2024 17:47:47 +0200 Subject: [PATCH 191/260] Add new changelog entry for dev version --- CHANGELOG.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a461cdd2..6da32747 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,22 @@ 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). +## [[UNRELEASED](https://github.com/nf-core/pixelator/releases/tag/?.?.?)] - 2024-??-?? + +### Enhancements & fixes + +### Software dependencies + +| Dependency | Old version | New version | +| ----------- | ----------- | ----------- | +| `pixelator` | 0.17.1 | 0.17.1 | + +> **NB:** Dependency has been **updated** if both old and new version information is present. +> +> **NB:** Dependency has been **added** if just the new version information is present. +> +> **NB:** Dependency has been **removed** if new version information isn't present. + ## [[1.2.0](https://github.com/nf-core/pixelator/releases/tag/1.2.0)] - 2024-05-28 ### Enhancements & fixes From 56da1fdcc7fdd595f342928f87cbac945b6a61c3 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 14 Jun 2024 16:06:06 +0200 Subject: [PATCH 192/260] Make all ext.args assignments closures --- conf/modules.config | 80 +++++++++++++++++++++++++-------------------- 1 file changed, 45 insertions(+), 35 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 94912907..2e66b1e2 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -93,55 +93,65 @@ process { } withName: PIXELATOR_COLLAPSE { - ext.args = [ - 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}": '', - params.collapse_min_count ? "--min-count ${params.collapse_min_count}": '', - params.collapse_use_counts ? "--use-counts": '', - ].join(' ').trim() + ext.args = { + [ + 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}": '', + params.collapse_min_count ? "--min-count ${params.collapse_min_count}": '', + params.collapse_use_counts ? "--use-counts": '', + ].join(' ').trim() + } } withName: PIXELATOR_GRAPH { - ext.args = [ - params.multiplet_recovery ? "--multiplet-recovery" : '', - params.leiden_iterations ? "--leiden-iterations ${params.leiden_iterations}" : '', - params.graph_min_count ? "--min-count ${params.graph_min_count}" : '', - ].join(' ').trim() + ext.args = { + [ + params.multiplet_recovery ? "--multiplet-recovery" : '', + params.leiden_iterations ? "--leiden-iterations ${params.leiden_iterations}" : '', + params.graph_min_count ? "--min-count ${params.graph_min_count}" : '', + ].join(' ').trim() + } } withName: PIXELATOR_ANNOTATE { - ext.args = [ - (params.min_size instanceof Integer) ? "--min-size ${params.min_size}" : '', - (params.max_size instanceof Integer) ? "--max-size ${params.max_size}" : '', - params.dynamic_filter ? "--dynamic-filter ${params.dynamic_filter}" : '', - params.aggregate_calling ? "--aggregate-calling" : '', - ].join(' ').trim() + ext.args = { + [ + (params.min_size instanceof Integer) ? "--min-size ${params.min_size}" : '', + (params.max_size instanceof Integer) ? "--max-size ${params.max_size}" : '', + params.dynamic_filter ? "--dynamic-filter ${params.dynamic_filter}" : '', + params.aggregate_calling ? "--aggregate-calling" : '', + ].join(' ').trim() + } } 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.polarization_min_marker_count ? "--polarization-min-marker-count ${params.polarization_min_marker_count}" : '', - params.polarization_transformation ? "--polarization-transformation ${params.polarization_transformation}" : '', - params.colocalization_transformation ? "--colocalization-transformation ${params.colocalization_transformation}" : '', - params.polarization_n_permutations ? "--polarization-n-permutations ${params.polarization_n_permutations}" : '', - (params.colocalization_neighbourhood_size instanceof Integer) ? "--colocalization-neighbourhood-size ${params.colocalization_neighbourhood_size}" : '', - (params.colocalization_n_permutations instanceof Integer) ? "--colocalization-n-permutations ${params.colocalization_n_permutations}" : '', - (params.colocalization_min_region_count instanceof Integer) ? "--colocalization-min-region-count ${params.colocalization_min_region_count}" : '', - ].join(' ').trim() + ext.args = { + [ + params.compute_polarization ? "--compute-polarization" : '', + params.compute_colocalization ? "--compute-colocalization" : '', + params.use_full_bipartite ? "--use-full-bipartite " : '', + params.polarization_min_marker_count ? "--polarization-min-marker-count ${params.polarization_min_marker_count}" : '', + params.polarization_transformation ? "--polarization-transformation ${params.polarization_transformation}" : '', + params.colocalization_transformation ? "--colocalization-transformation ${params.colocalization_transformation}" : '', + params.polarization_n_permutations ? "--polarization-n-permutations ${params.polarization_n_permutations}" : '', + (params.colocalization_neighbourhood_size instanceof Integer) ? "--colocalization-neighbourhood-size ${params.colocalization_neighbourhood_size}" : '', + (params.colocalization_n_permutations instanceof Integer) ? "--colocalization-n-permutations ${params.colocalization_n_permutations}" : '', + (params.colocalization_min_region_count instanceof Integer) ? "--colocalization-min-region-count ${params.colocalization_min_region_count}" : '', + ].join(' ').trim() + } } withName: PIXELATOR_LAYOUT { ext.when = { !params.skip_layout } - ext.args = [ - params.no_node_marker_counts ? "--no-node-marker-counts" : '', - params.layout_algorithm ? "--layout-algorithm ${params.layout_algorithm} " : '', - ].join(' ').trim() + ext.args = { + [ + params.no_node_marker_counts ? "--no-node-marker-counts" : '', + params.layout_algorithm ? "--layout-algorithm ${params.layout_algorithm} " : '', + ].join(' ').trim() + } } withName: PIXELATOR_REPORT { From ccb35d0e6b8c0dca86cb9503d0d84afe3cbd81ae Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 14 Jun 2024 16:08:34 +0200 Subject: [PATCH 193/260] Update CHANGELOG --- CHANGELOG.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6da32747..92ec252e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,10 +3,12 @@ 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). -## [[UNRELEASED](https://github.com/nf-core/pixelator/releases/tag/?.?.?)] - 2024-??-?? +## [[1.3.0dev](https://github.com/nf-core/pixelator/releases/tag/?.?.?)] - 2024-??-?? ### Enhancements & fixes +- [[PR #96](https://github.com/nf-core/pixelator/pull/96)] - Make all ext.args assignments closures + ### Software dependencies | Dependency | Old version | New version | From f920319b3cc33c4b85c1b1b69d3361db3bc17042 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 17 Jun 2024 10:26:54 +0200 Subject: [PATCH 194/260] Update citations --- CITATIONS.md | 4 ++-- README.md | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/CITATIONS.md b/CITATIONS.md index 49b37b62..4d7355ef 100644 --- a/CITATIONS.md +++ b/CITATIONS.md @@ -10,9 +10,9 @@ ## Pipeline tools -- [pixelator](https://doi.org/10.1101/2023.06.05.543770) +- [pixelator](https://doi.org/10.1038/s41592-024-02268-9) - > 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. + > Karlsson, F., Kallas, T., Thiagarajan, D. et al. “Molecular pixelation: spatial proteomics of single cells by sequencing.“ Nat Methods 21, 1044–1052 (2024). https://doi.org/10.1038/s41592-024-02268-9 - [cutadapt](http://dx.doi.org/10.14806/ej.17.1.200) diff --git a/README.md b/README.md index 1c8d8fe5..496efb1a 100644 --- a/README.md +++ b/README.md @@ -101,3 +101,11 @@ You can cite the `nf-core` publication as follows: > Philip Ewels, Alexander Peltzer, Sven Fillinger, Harshil Patel, Johannes Alneberg, Andreas Wilm, Maxime Ulysse Garcia, Paolo Di Tommaso & Sven Nahnsen. > > _Nat Biotechnol._ 2020 Feb 13. doi: [10.1038/s41587-020-0439-x](https://dx.doi.org/10.1038/s41587-020-0439-x). + +You can cite the Molecular pixelation technology as follows: + +> **Molecular pixelation: spatial proteomics of single cells by sequencing.** +> +> Filip Karlsson, Tomasz Kallas, Divya Thiagarajan, Max Karlsson, Maud Schweitzer, Jose Fernandez Navarro, Louise Leijonancker, Sylvain Geny, Erik Pettersson, Jan Rhomberg-Kauert, Ludvig Larsson, Hanna van Ooijen, Stefan Petkov, Marcela González-Granillo, Jessica Bunz, Johan Dahlberg, Michele Simonetti, Prajakta Sathe, Petter Brodin, Alvaro Martinez Barrio & Simon Fredriksson +> +> _Nat Methods._ 2024 May 08. doi: [10.1038/s41592-024-02268-9](https://doi.org/10.1038/s41592-024-02268-9) From 1cb639de3d132f5cfc4c1d636dfc73c9fb7aaa8f Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 17 Jun 2024 10:34:04 +0200 Subject: [PATCH 195/260] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92ec252e..b2fe3ef4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Enhancements & fixes +- [[PR #97](https://github.com/nf-core/pixelator/pull/97)] - Update citations - [[PR #96](https://github.com/nf-core/pixelator/pull/96)] - Make all ext.args assignments closures ### Software dependencies From c3203076a41a617058e0f9e6adf4e4e6dae02f2e Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 18 Jun 2024 13:24:52 +0200 Subject: [PATCH 196/260] Update metromap to include layout step --- docs/images/nf-core-pixelator-metromap.svg | 361 +++++++++++---------- 1 file changed, 196 insertions(+), 165 deletions(-) diff --git a/docs/images/nf-core-pixelator-metromap.svg b/docs/images/nf-core-pixelator-metromap.svg index a6c3e37e..0b66bdc0 100644 --- a/docs/images/nf-core-pixelator-metromap.svg +++ b/docs/images/nf-core-pixelator-metromap.svg @@ -1,246 +1,277 @@ - - - - - - - - - - - + + + + + + + + + + + - + - - - - - + + + + + - - - - - - - + + + + + + + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - - + + + + + + + + - + + + + + + + - - - - - - - - + - - - - - - - - + + + + + + + + + + - - - - - + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - - + + - - + + - - + + - - + + - - + + - - + + From 20496c14793d52f77da18776725a8cc4d6bcf4ca Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 18 Jun 2024 13:26:39 +0200 Subject: [PATCH 197/260] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 92ec252e..b33a1d1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Enhancements & fixes - [[PR #96](https://github.com/nf-core/pixelator/pull/96)] - Make all ext.args assignments closures +- [[PR #98](https://github.com/nf-core/pixelator/pull/98)] - Update metromap to include layout step ### Software dependencies From 98238c8f437b1e4c74a70c5aa66b00bc54c71fc8 Mon Sep 17 00:00:00 2001 From: Pixelgen Technologies Date: Thu, 20 Jun 2024 15:47:26 +0200 Subject: [PATCH 198/260] fix: add layour docs to readme --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 1c8d8fe5..fb11b3d5 100644 --- a/README.md +++ b/README.md @@ -30,7 +30,8 @@ It takes a samplesheet as input and will process your data using `pixelator` to 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)) +8. Generate 3D graph layouts for visualization of cells ([`pixelator layout`](https://github.com/PixelgenTechnologies/pixelator)) +9. Report generation ([`pixelator report`](https://github.com/PixelgenTechnologies/pixelator)) > [!WARNING] > Since Nextflow 23.07.0-edge, Nextflow no longer mounts the host's home directory when using Apptainer or Singularity. From 141642ad7150e4f5a9b14f2db598a4dd26ccee18 Mon Sep 17 00:00:00 2001 From: Pixelgen Technologies Date: Thu, 20 Jun 2024 15:59:12 +0200 Subject: [PATCH 199/260] fix: changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index b33a1d1f..1212eb43 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [[PR #96](https://github.com/nf-core/pixelator/pull/96)] - Make all ext.args assignments closures - [[PR #98](https://github.com/nf-core/pixelator/pull/98)] - Update metromap to include layout step +- [[PR #99](https://github.com/nf-core/pixelator/pull/99)] - Update README to include layout step ### Software dependencies From bfe93c44e047887f4b0d7b8884dca2f4f0dd8168 Mon Sep 17 00:00:00 2001 From: Pixelgen Technologies Date: Thu, 20 Jun 2024 16:10:21 +0200 Subject: [PATCH 200/260] fix: update output layout step --- docs/output.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/output.md b/docs/output.md index cda70ef8..78f6f12d 100644 --- a/docs/output.md +++ b/docs/output.md @@ -19,6 +19,7 @@ The pipeline consists of the following steps: - [Compute connected components](#compute-connected-components) - [Filtering, annotation, cell-calling](#cell-calling-filtering-and-annotation) - [Downstream analysis](#downstream-analysis) +- [Generate layouts for visualization](#compute-layouts-for-visualization) - [Generate reports](#generate-reports) ### Preprocessing From ca32765cfe3e4091adebe97056cda9ac2d24481c Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 27 Jun 2024 11:54:49 +0200 Subject: [PATCH 201/260] Use R1/R2 suffixes in amplicon input fastq file renaming This ensures limited backward compatibility when using the pipeline with older pixelator containers (<0.17) using the `--pixelator_container` flag. --- CHANGELOG.md | 1 + modules/local/pixelator/single-cell/amplicon/main.nf | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 562d2c91..6aa2a89a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [[PR #96](https://github.com/nf-core/pixelator/pull/96)] - Make all ext.args assignments closures - [[PR #98](https://github.com/nf-core/pixelator/pull/98)] - Update metromap to include layout step - [[PR #99](https://github.com/nf-core/pixelator/pull/99)] - Update README to include layout step +- [[PR #100](https://github.com/nf-core/pixelator/pull/100)] - Use R1/R2 suffixes in amplicon input fastq file renaming ### Software dependencies diff --git a/modules/local/pixelator/single-cell/amplicon/main.nf b/modules/local/pixelator/single-cell/amplicon/main.nf index b73f696a..bfc9bacf 100644 --- a/modules/local/pixelator/single-cell/amplicon/main.nf +++ b/modules/local/pixelator/single-cell/amplicon/main.nf @@ -26,10 +26,11 @@ process PIXELATOR_AMPLICON { def prefix = task.ext.prefix ?: "${meta.id}" def args = task.ext.args ?: '' - // Make list of old name and new name pairs to use for renaming in the bash while loop + // Make list of old name and new name pairs to use for renaming + // Use R1/R2 style suffixes for limited backward compatibility with pixelator<0.17 def old_new_pairs = (reads instanceof Path || reads.size() == 1) ? [[ reads, "${prefix}${getFileSuffix(reads)}" ]] - : reads.withIndex().collect { entry, index -> [ entry, "${prefix}_${index + 1}${getFileSuffix(entry)}" ] } + : reads.withIndex().collect { entry, index -> [ entry, "${prefix}_R${index + 1}${getFileSuffix(entry)}" ] } def rename_to = old_new_pairs*.join(' ').join(' ') def renamed_reads = old_new_pairs.collect { old_name, new_name -> new_name }.join(' ') From dd34553b5f27480d7c2848b16c7a8aa0a2f46358 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 27 Jun 2024 12:06:19 +0200 Subject: [PATCH 202/260] Fix validation issue when using panel_file instead of panel A map access error occurs because the meta map gets overwritten with null in validate_panel when no panel key is present. We need to return the meta map unmodified instead. --- CHANGELOG.md | 1 + subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6aa2a89a..5c6ddb1d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [[PR #98](https://github.com/nf-core/pixelator/pull/98)] - Update metromap to include layout step - [[PR #99](https://github.com/nf-core/pixelator/pull/99)] - Update README to include layout step - [[PR #100](https://github.com/nf-core/pixelator/pull/100)] - Use R1/R2 suffixes in amplicon input fastq file renaming +- [[PR #101](https://github.com/nf-core/pixelator/pull/101)] - Fix validation issue when using panel_file instead of panel ### Software dependencies diff --git a/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf b/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf index 3a61820a..7cfab179 100644 --- a/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf @@ -339,7 +339,7 @@ def resolve_relative_path(relative_path, URI samplesheet_path) { // def validate_panel(LinkedHashMap meta, HashSet options) { if (meta.panel == null) { - return + return meta } if (!options.contains(meta.panel)) { @@ -356,7 +356,7 @@ def validate_panel(LinkedHashMap meta, HashSet options) { // def validate_design(LinkedHashMap meta, HashSet options) { if (meta.design == null) { - return + return meta } if (!options.contains(meta.design)) { From 97bd99d4fe254fd5807dfd2dfedbef807f4bbecd Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 14 Jun 2024 17:05:29 +0200 Subject: [PATCH 203/260] Restructure output directory --- conf/modules.config | 257 +++++++++++++++++++++++++++++++++++++++-- docs/output.md | 187 ++++++++++++++++++++---------- nextflow.config | 15 +++ nextflow_schema.json | 105 +++++++++++++++-- workflows/pixelator.nf | 3 +- 5 files changed, 485 insertions(+), 82 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index 2e66b1e2..44aae83f 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -14,7 +14,7 @@ process { publishDir = [ - path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + path: { "${params.outdir}/pixelator" }, mode: params.publish_dir_mode, saveAs: { filename -> filename.equals('versions.yml') ? null : filename } ] @@ -22,14 +22,14 @@ process { withName: "PIXELATOR.*" { publishDir = [ [ - path: { "${params.outdir}/${task.process.tokenize(':')[-1].tokenize('_')[0].toLowerCase()}" }, + path: { "${params.outdir}/pixelator" }, 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" }, + path: { "${params.outdir}/pixelator/logs" }, mode: params.publish_dir_mode, - pattern: "*.log" + pattern: '*.log' ] ] @@ -47,15 +47,50 @@ process { } + withName: PIXELATOR_COLLECT_METADATA { + publishDir = [ + [ + path: { "${params.outdir}/pipeline_info" }, + mode: params.publish_dir_mode, + pattern: 'metadata.json', + saveAs: { filename -> + def timestamp = new java.util.Date().format( 'yyyy-MM-dd_HH-mm-ss') + "metadata_${timestamp}.json" + } + ], + [ + path: { "${params.outdir}/pixelator/logs" }, + mode: params.publish_dir_mode, + pattern: '*.log' + ] + ] + } + // use explicit (params.my_option instanceof Integer) checks to avoid issues with 0 evaluating false // since most pixelator flags do accept zero as a value withName: PIXELATOR_AMPLICON { - ext.args = { + ext.args = { ["--design ${meta.design}"].join(' ').trim() } + + publishDir = [ [ - "--design ${meta.design}", - ].join(' ').trim() - } + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: 'amplicon/*.merged.{fq,fastq}.gz', + saveAs: { (params.save_amplicon_reads || params.save_all) ? it : null } + ], + [ + path: { "${params.outdir}/pixelator/logs/${meta.id}" }, + mode: params.publish_dir_mode, + pattern: "*.log" + ], + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: '**/*.{report,meta}.json', + saveAs: { params.save_all ? it : null } + ] + ] } withName: PIXELATOR_QC { @@ -80,6 +115,32 @@ process { params.adapterqc_mismatches ? "--mismatches ${params.adapterqc_mismatches}": '', ].join(' ').trim() } + + publishDir = [ + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: '{preqc,adapterqc}/*.processed.{fq,fastq}.gz', + saveAs: { (params.save_qc_passed_reads || params.save_all) ? it : null } + ], + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: '{preqc,adapterqc}/*.failed.{fq,fastq}.gz', + saveAs: { (params.save_qc_failed_reads || params.save_all) ? it : null } + ], + [ + path: { "${params.outdir}/pixelator/logs/${meta.id}" }, + mode: params.publish_dir_mode, + pattern: '*.log' + ], + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: '**/*.{report,meta}.json', + saveAs: { (params.save_all) ? it : null } + ] + ] } withName: PIXELATOR_DEMUX { @@ -90,6 +151,32 @@ process { (params.demux_min_length instanceof Integer) ? "--mismatches ${params.demux_min_length}": '', ].join(' ').trim() } + + publishDir = [ + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: 'demux/*.processed-*.{fq,fastq}.gz', + saveAs: { (params.save_demux_processed_reads || params.save_all) ? it : null } + ], + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: 'demux/*failed.{fq,fastq}.gz', + saveAs: { (params.save_demux_failed_reads || params.save_all) ? it : null } + ], + [ + path: { "${params.outdir}/pixelator/logs/${meta.id}" }, + mode: params.publish_dir_mode, + pattern: '*.log' + ], + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: '**/*.{report,meta}.json', + saveAs: { (params.save_all) ? it : null } + ] + ] } withName: PIXELATOR_COLLAPSE { @@ -103,6 +190,26 @@ process { params.collapse_use_counts ? "--use-counts": '', ].join(' ').trim() } + + publishDir = [ + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: 'collapse/*.collapsed.parquet', + saveAs: { (params.save_collapsed_reads || params.save_all) ? it : null } + ], + [ + path: { "${params.outdir}/pixelator/logs/${meta.id}" }, + mode: params.publish_dir_mode, + pattern: '*.log' + ], + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: '**/*.{report,meta}.json', + saveAs: { (params.save_all) ? it : null } + ] + ] } withName: PIXELATOR_GRAPH { @@ -113,6 +220,32 @@ process { params.graph_min_count ? "--min-count ${params.graph_min_count}" : '', ].join(' ').trim() } + + publishDir = [ + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: 'graph/*.components_recovered.csv', + saveAs: { (params.save_recovered_components || params.save_all) ? it : null } + ], + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: 'graph/*.components_recovered.csv', + saveAs: { (params.save_edgelist || params.save_all) ? it : null } + ], + [ + path: { "${params.outdir}/pixelator/logs/${meta.id}" }, + mode: params.publish_dir_mode, + pattern: '*.log' + ], + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: '**/*.{report,meta}.json', + saveAs: { (params.save_all) ? it : null } + ] + ] } withName: PIXELATOR_ANNOTATE { @@ -124,6 +257,41 @@ process { params.aggregate_calling ? "--aggregate-calling" : '', ].join(' ').trim() } + + publishDir = [ + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: 'annotate/*.dataset.pxl', + saveAs: { + if (params.skip_layout && params.skip_analysis) { + // Trim the annotate directory prefix from the output name + return new File(it).name + } + else if (params.save_annotate_dataset || params.save_all) { + return it + } + return null + } + ], + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: '**/*.{report,meta}.json', + saveAs: { (params.save_raw_component_metrics || params.save_all) ? it : null } + ], + [ + path: { "${params.outdir}/pixelator/logs/${meta.id}" }, + mode: params.publish_dir_mode, + pattern: '*.log' + ], + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: '**/*.{report,meta}.json', + saveAs: { (params.save_all) ? it : null } + ] + ] } withName: PIXELATOR_ANALYSIS { @@ -142,6 +310,34 @@ process { (params.colocalization_min_region_count instanceof Integer) ? "--colocalization-min-region-count ${params.colocalization_min_region_count}" : '', ].join(' ').trim() } + + publishDir = [ + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: 'analysis/*.dataset.pxl', + saveAs: { + if (params.skip_layout) { + // Trim the annotate directory prefix from the output name + return new File(it).name + } + else if (params.save_analysis_dataset || params.save_all) { + return it + } + } + ], + [ + path: { "${params.outdir}/pixelator/logs/${meta.id}" }, + mode: params.publish_dir_mode, + pattern: '*.log' + ], + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: '**/*.{report,meta}.json', + saveAs: { (params.save_all) ? it : null } + ] + ] } withName: PIXELATOR_LAYOUT { @@ -152,9 +348,54 @@ process { params.layout_algorithm ? "--layout-algorithm ${params.layout_algorithm} " : '', ].join(' ').trim() } + + publishDir = [ + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: 'layout/*.dataset.pxl', + saveAs: { + // Trim the annotate directory prefix from the output name + new File(it).name + } + ], + [ + path: { "${params.outdir}/pixelator/logs/${meta.id}" }, + mode: params.publish_dir_mode, + pattern: '*.log' + ], + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: '**/*.{report,meta}.json', + saveAs: { (params.save_all) ? it : null } + ] + ] } withName: PIXELATOR_REPORT { ext.when = { !params.skip_report } + publishDir = [ + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: 'report/*.html', + saveAs: { + // Trim the annotate directory prefix from the output name + new File(it).name + } + ], + [ + path: { "${params.outdir}/pixelator/logs/${meta.id}" }, + mode: params.publish_dir_mode, + pattern: '*.log' + ], + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: '**/*.{report,meta}.json', + saveAs: { (params.save_all) ? it : null } + ] + ] } } diff --git a/docs/output.md b/docs/output.md index 78f6f12d..d8be0096 100644 --- a/docs/output.md +++ b/docs/output.md @@ -24,6 +24,13 @@ The pipeline consists of the following steps: ### Preprocessing +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. + +These amplicon FASTQ files are intermediate and by default not placed in the output folder kept in the final files delivered to users. +Set `--save_amplicon_reads` or `--save_all` to enable publishing of these files to: +
Output files @@ -41,12 +48,23 @@ The pipeline consists of the following steps:
-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. - ### 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 `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). + +These processed and discarded FASTQ reads are intermediate and by default not placed in the output folder kept in the final files delivered to users. +Set `--save_qc_passed_reads` and/or `--save_qc_passed_reads` to enable publishing of these files. +Alternatively, set `--save_all` to keep all intermediary outputs of all steps. +
Output files @@ -69,17 +87,15 @@ This step will also calculate Q30 quality scores for different regions of the li
-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/). +### Demultiplexing -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). +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. -### Demultiplexing +These processed and discarded FASTQ reads are intermediate and by default not placed in the output folder kept in the final files delivered to users. +Set `--save_demux_failed_reads` and/or `--save_demux_processed_reads` to enable publishing of these files. +Alternatively, set `--save_all` to keep all intermediary outputs of all steps.
Output files @@ -98,12 +114,21 @@ The `adapterqc` stage checks for the presence and correctness of the pixel bindi
-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. - ### 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 output format of this command is a parquet file containing deduplicated and error-corrected reads. + +The collapsed reads are intermediate and by default not placed in the output folder kept in the final files delivered to users. +Set `--save_collapsed_reads` to enable publishing of these files. +Alternatively, set `--save_all` to keep all intermediary outputs of all steps. +
Output files @@ -120,16 +145,23 @@ given barcodes/antibodies.
-This step uses the `pixelator single-cell collapse` command. +### Compute connected components -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). +This step uses the `pixelator single-cell graph` command. +The input is the edge list parquet file 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 find and remove problematic edges. +(See `--multiplet_recovery`). The information to keep track of the original and +newly recovered components are stored in a file (components_recovered.csv). +This file is not included in the output folder by default, but can be included by passing `--save_recovered_components`. -The output format of this command is an edge list in CSV format. +The edgelist is intermediate and by default not placed in the output folder kept in the final files delivered to users. +Set `--save_edgelist` to enable publishing of these file. -### Compute connected components +Alternatively, set `--save_all` to keep all intermediary outputs of all steps.
Output files @@ -151,17 +183,19 @@ The output format of this command is an edge list in CSV format.
-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". +### Cell-calling, filtering, and annotation -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 -newly recovered components are stored in a file (components_recovered.csv). +This step uses the `pixelator single-cell annotate` command. -### Cell-calling, filtering, and annotation +The annotate command takes as input the edge list 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 metadata. + +Some summary statistics before filtering are stored in `raw_components_metrics.csv.gz`. +This file is not included in the output folder by default, but can be included by passing `--save_raw_component_metrics`. + +By default, the PXL file after annotate will not be saved to the results directory unless `--skip_analysis` and `--skip_layout` is passed. +Set `--save_annotate_dataset` to include these files.
Output files @@ -169,7 +203,7 @@ newly recovered components are stored in a file (components_recovered.csv). - `pixelator` - `annotate` - - `.annotate.dataset.pxl` + - `.annotate.dataset.pxl`: The procesed PXL dataset, - `.meta.json`: Command invocation metadata. - `.raw_components_metrics.csv.gz` - `.report.json`: Statistics for the analysis step. @@ -177,13 +211,24 @@ newly recovered components are stored in a file (components_recovered.csv). - `.pixelator-annotate.log`: pixelator log output.
-This step uses the `pixelator single-cell annotate` command. +### Downstream analysis -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 metadata. +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 are added to the pxl file. -### Downstream analysis +Currently, the following analysis are performed: + +- polarization scores (enable with `--compute_polarization`) +- co-localization scores (enable with `--compute_colocalization`) + +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. + +By default, the PXL file after analysis will not be saved to the results directory unless `--skip_layout` is passed. +Set `--save_analysis_dataset` to include these files. + +Alternatively, set `--save_all` to keep all intermediary outputs of all steps.
Output files @@ -201,19 +246,15 @@ edgelist to find putative cells, and it will generate a pxl file containing the
-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 are performed: +### Compute layouts for visualization -- polarization scores (enable with `--compute_polarization`) -- co-localization scores (enable with `--compute_colocalization`) +This step uses the `pixelator single-cell layout` command. +It will generate precomputed layouts that can be used to visualize cells +as part of the downstream analysis. This data will be appended to a PXL file. -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. +This entire step can also be skipped using the `--skip_layout` option. -### Compute layouts for visualization +Set `--save_all` to keep all intermediary outputs of all steps.
Output files @@ -231,13 +272,15 @@ This entire step can also be skipped using the `--skip_analysis` option.
-This step uses the `pixelator single-cell layout` command. -It will generate precomputed layouts that can be used to visualize cells -as part of the downstream analysis. +### Generate reports -This entire step can also be skipped using the `--skip_layout` 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. -### Generate reports +This step can be skipped using the `--skip_report` option. + +More information on the report can be found in the [pixelator documentation](https://software.pixelgen.com/pixelator/outputs/web-report/)
Output files @@ -250,14 +293,6 @@ This entire step can also be skipped using the `--skip_layout` 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](https://software.pixelgen.com/pixelator/outputs/web-report/) - ### Pipeline information
@@ -266,10 +301,36 @@ More information on the report can be found in the [pixelator documentation](htt - `pipeline_info/` - 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` - Parameters used by the pipeline run: `params.json`.
[Nextflow](https://www.nextflow.io/docs/latest/tracing.html) provides excellent functionality for generating various reports relevant to the running and execution of the pipeline. This will allow you to troubleshoot errors with the running of the pipeline, and also provide you with other information such as launch commands, run times and resource usage. + +## Output directory structure + +With default parameters, the pixelator pipeline output directory will only include the most "complete" PXL file +generated by the pipeline and an interactive HTML report per sample. +The PXL dataset files can be from either the `annotate`, `analysis` or `layout` step. + +With default parameters, the `.layout.datasets.pxl` will be copied to the output directory. +If the `layout` stage is skipped (using `--skip_layout`) the `.analysis.datasets.pxl` files will be included and +if the `analysis` stage is skipped (using `--skip_analysis`) the `.annotate.datasets.pxl` will be copied. + +Various flags are available to store intermediate files and are described in the input parameter documentation. Alternatively, you can keep all intermediate files using `--save_all`. + +Below is an example output structure for a pipeline run using the default settings. + +- `pipeline_info/` +- `pixelator/` + + - `logs/` + + - `/`: + - `*.log` + + - `pbmcs_unstimulated.layout.dataset.pxl` + - `pbmcs_unstimulated.qc-report.html` + - `uropod_control.layout.dataset.pxl` + - `uropod_control.qc-report.html` diff --git a/nextflow.config b/nextflow.config index e268fb93..dba8a423 100644 --- a/nextflow.config +++ b/nextflow.config @@ -62,6 +62,21 @@ params { colocalization_n_permutations = 50 colocalization_min_region_count = 5 + // Output options + save_amplicon_reads = false + save_qc_passed_reads = false + save_qc_failed_reads = false + save_demux_processed_reads = false + save_demux_failed_reads = false + save_collapsed_reads = false + save_recovered_components = false + save_edgelist = false + save_annotate_dataset = false + save_raw_component_metrics = false + save_analysis_dataset = false + + save_all = false + // layout options no_node_marker_counts = false layout_algorithm = "pmds_3d" diff --git a/nextflow_schema.json b/nextflow_schema.json index 7faa19b5..9c353a70 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -45,7 +45,21 @@ } } }, - "preqc_options": { + "amplicon_options": { + "title": "Amplicon generation options", + "type": "object", + "fa_icon": "fas fa-circle", + "properties": { + "save_amplicon_reads": { + "fa_icon": "fas fa-save", + "type": "boolean", + "default": false, + "description": "Save intermediate amplicon reads generated from the raw input reads.", + "help": "By default, generated amplicon FastQ files will not be saved to the results directory. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." + } + } + }, + "qc_options": { "title": "QC/Filtering/Trimming options", "type": "object", "fa_icon": "fas fa-terminal", @@ -96,13 +110,7 @@ "fa_icon": "fas g", "description": "Remove PolyG sequences (length of 10 or more)", "type": "boolean" - } - } - }, - "adapterqc_options": { - "title": "Adapter QC Options", - "type": "object", - "properties": { + }, "adapterqc_mismatches": { "fa_icon": "fas not-equal", "description": "The number of mismatches allowed (in percentage) [default: 0.1; 0.0<=x<=0.9]", @@ -110,6 +118,20 @@ "default": 0.1, "minimum": 0.0, "maximum": 0.9 + }, + "save_qc_passed_reads": { + "fa_icon": "fas fa-save", + "type": "boolean", + "default": false, + "description": "Save intermediate qc read files containing all reads that passed the filters.", + "help": "By default, filtered read FastQ files after QC will not be saved to the results directory. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." + }, + "save_qc_failed_reads": { + "fa_icon": "fas fa-save", + "type": "boolean", + "default": false, + "description": "Save intermediate qc read files containing all reads that failed the filters.", + "help": "By default, FastQ files with reads that failed QC will not be saved to the results directory. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." } } }, @@ -130,6 +152,20 @@ "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" + }, + "save_demux_processed_reads": { + "fa_icon": "fas fa-save", + "type": "boolean", + "default": false, + "description": "Save intermediate qc read files containing all reads that containt valid antibody barcodes.", + "help": "By default, FastQ files containing reads with valid antibody barcodes will not be saved to the results directory. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." + }, + "save_demux_failed_reads": { + "fa_icon": "fas fa-save", + "type": "boolean", + "default": false, + "description": "Save intermediate qc read files containing all reads that failed the filters.", + "help": "By default, FastQ files containing reads without valid antibody barcodes will not be saved to the results directory. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." } } }, @@ -177,6 +213,13 @@ "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" + }, + "save_collapsed_reads": { + "fa_icon": "fas fa-save", + "type": "boolean", + "default": false, + "description": "Save an intermediate parquet file containing collapsed read information.", + "help": "By default, intermediate collapsed reads will not be saved to the results directory. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." } } }, @@ -206,6 +249,20 @@ "minimum": 1, "maximum": 50, "hidden": true + }, + "save_edgelist": { + "fa_icon": "fas fa-save", + "type": "boolean", + "default": false, + "description": "Save an intermediate csv file containing the unfiltered graph edgelist.", + "help": "By default, the unfiltered edgelist will not be saved to the results directory. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." + }, + "save_recovered_components": { + "fa_icon": "fas fa-save", + "type": "boolean", + "default": false, + "description": "Save an intermediate csv file containing the recovered components after multiplet recovery.", + "help": "By default, the recovered component will not be saved to the results directory. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." } } }, @@ -231,6 +288,20 @@ "description": "Enable aggregate calling, information on potential aggregates will be added to the output data", "type": "boolean", "default": true + }, + "save_raw_component_metrics": { + "fa_icon": "fas fa-save", + "type": "boolean", + "default": false, + "description": "Save the raw_component_metrics.csv file from the annotate stage.", + "help": "By default, the raw_component_metrics CSV file after annotate will not be saved to the results directory. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." + }, + "save_annotate_dataset": { + "fa_icon": "fas fa-save", + "type": "boolean", + "default": false, + "description": "Save the PXL dataset after the annotate stage.", + "help": "By default, the PXL file after annotate will not be saved to the results directory unless `--skip_analysis` and `--skip_layout` is passed. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." } } }, @@ -298,6 +369,13 @@ "description": "The minimum number of counts in a region for it to be considered valid for computing colocalization", "default": 5, "minimum": 0 + }, + "save_analysis_dataset": { + "fa_icon": "fas fa-save", + "type": "boolean", + "default": false, + "description": "Save the PXL dataset after the analysis stage.", + "help": "By default, the PXL dataset after the analysis stage will only be saved be saved when `--skip_layout` is passed. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." } } }, @@ -341,6 +419,13 @@ "type": "string", "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 guaranteed to work when using different pixelator versions." + }, + "save_all": { + "fa_icon": "fas fa-save", + "type": "boolean", + "default": false, + "description": "Save all intermediate results.", + "help": "This option is equivalent to passing:\n`--save_amplicon_reads --save_qc_passed_reads --save_qc_failed_reads --save_demux_processed_reads --save_demux_failed_reads --save_collapsed_reads --save_edgelist --save_recovered_components --save_annotate_dataset --save_analysis_dataset`" } } }, @@ -525,10 +610,10 @@ "$ref": "#/definitions/input_output_options" }, { - "$ref": "#/definitions/preqc_options" + "$ref": "#/definitions/amplicon_options" }, { - "$ref": "#/definitions/adapterqc_options" + "$ref": "#/definitions/qc_options" }, { "$ref": "#/definitions/demux_options" diff --git a/workflows/pixelator.nf b/workflows/pixelator.nf index f7ee3408..870f8285 100644 --- a/workflows/pixelator.nf +++ b/workflows/pixelator.nf @@ -192,7 +192,8 @@ workflow PIXELATOR { // // MODULE: Run pixelator single-cell layout // - PIXELATOR_LAYOUT ( ch_analysed ) + ch_layout_input = params.skip_analysis ? ch_annotated : ch_analysed + PIXELATOR_LAYOUT ( ch_layout_input ) ch_layout = PIXELATOR_LAYOUT.out.dataset ch_versions = ch_versions.mix(PIXELATOR_LAYOUT.out.versions.first()) From 6ddf209b056889331e6b58b878ed090232fdd8ed Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 27 Jun 2024 15:13:01 +0200 Subject: [PATCH 204/260] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c6ddb1d..9da0bd1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [[PR #99](https://github.com/nf-core/pixelator/pull/99)] - Update README to include layout step - [[PR #100](https://github.com/nf-core/pixelator/pull/100)] - Use R1/R2 suffixes in amplicon input fastq file renaming - [[PR #101](https://github.com/nf-core/pixelator/pull/101)] - Fix validation issue when using panel_file instead of panel +- [[PR #102](https://github.com/nf-core/pixelator/pull/101)] - Restructure output directory ### Software dependencies From d16b018a6d5fb16f9288dedfb2c031546b4a63be Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 1 Jul 2024 14:38:19 +0200 Subject: [PATCH 205/260] Fix bad pattern for graph/*edgelist.parquet' --- conf/modules.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/modules.config b/conf/modules.config index 44aae83f..0ae12300 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -231,7 +231,7 @@ process { [ path: { "${params.outdir}/pixelator" }, mode: params.publish_dir_mode, - pattern: 'graph/*.components_recovered.csv', + pattern: 'graph/*edgelist.parquet', saveAs: { (params.save_edgelist || params.save_all) ? it : null } ], [ From bead999c75e9314902bcfb1a0516b390c03d5631 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 1 Jul 2024 14:38:42 +0200 Subject: [PATCH 206/260] Use explicit return null in saveAs closures --- conf/modules.config | 1 + 1 file changed, 1 insertion(+) diff --git a/conf/modules.config b/conf/modules.config index 0ae12300..7e14768e 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -324,6 +324,7 @@ process { else if (params.save_analysis_dataset || params.save_all) { return it } + return null } ], [ From d38af5c3e319c60d5b4a7dde37dd59c4ebabcd6f Mon Sep 17 00:00:00 2001 From: Florian De Temmerman <69114541+fbdtemme@users.noreply.github.com> Date: Mon, 1 Jul 2024 14:43:38 +0200 Subject: [PATCH 207/260] Update docs/output.md Co-authored-by: Alvaro Martinez Barrio Update docs/output.md Co-authored-by: Alvaro Martinez Barrio Update docs/output.md Co-authored-by: Alvaro Martinez Barrio Update docs/output.md Co-authored-by: Alvaro Martinez Barrio Update docs/output.md Co-authored-by: Alvaro Martinez Barrio Update docs/output.md Co-authored-by: Alvaro Martinez Barrio Apply suggestions from code review Co-authored-by: Alvaro Martinez Barrio --- docs/output.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/output.md b/docs/output.md index d8be0096..c1846b83 100644 --- a/docs/output.md +++ b/docs/output.md @@ -28,7 +28,7 @@ The preprocessing step uses `pixelator single-cell amplicon` to create full-leng 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. -These amplicon FASTQ files are intermediate and by default not placed in the output folder kept in the final files delivered to users. +These amplicon FASTQ files are intermediate and by default not placed in the output folder with the final files delivered to users. Set `--save_amplicon_reads` or `--save_all` to enable publishing of these files to:
@@ -61,7 +61,7 @@ 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). -These processed and discarded FASTQ reads are intermediate and by default not placed in the output folder kept in the final files delivered to users. +These processed and discarded FASTQ reads are intermediate and by default not placed in the output folder with the final files delivered to users. Set `--save_qc_passed_reads` and/or `--save_qc_passed_reads` to enable publishing of these files. Alternatively, set `--save_all` to keep all intermediary outputs of all steps. @@ -89,11 +89,11 @@ Alternatively, set `--save_all` to keep all intermediary outputs of all steps. ### Demultiplexing -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 +The `pixelator single-cell demux` command assigns each read to a marker (with a certain barcode) file. It also generates QC report in +JSON format. It saves processed reads (one file per antibody) as well as discarded reads (in a different file) with no match to the given barcodes/antibodies. -These processed and discarded FASTQ reads are intermediate and by default not placed in the output folder kept in the final files delivered to users. +These processed and discarded FASTQ reads are intermediate and by default not placed in the output folder with the final files delivered to users. Set `--save_demux_failed_reads` and/or `--save_demux_processed_reads` to enable publishing of these files. Alternatively, set `--save_all` to keep all intermediary outputs of all steps. @@ -123,9 +123,9 @@ This is achieved using the unique pixel identifier and unique molecular identifi 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 output format of this command is a parquet file containing deduplicated and error-corrected reads. +The output format of this command is a parquet file containing deduplicated and error-corrected molecules. -The collapsed reads are intermediate and by default not placed in the output folder kept in the final files delivered to users. +The collapsed reads are intermediate and by default not placed in the output folder with the final files delivered to users. Set `--save_collapsed_reads` to enable publishing of these files. Alternatively, set `--save_all` to keep all intermediary outputs of all steps. @@ -153,12 +153,12 @@ 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 find and remove problematic edges. -(See `--multiplet_recovery`). The information to keep track of the original and +components using community detection to find and remove problematic edges +(see `--multiplet_recovery`). These new component IDs are then stored in the "component" column. The information to keep track of the original and newly recovered components are stored in a file (components_recovered.csv). This file is not included in the output folder by default, but can be included by passing `--save_recovered_components`. -The edgelist is intermediate and by default not placed in the output folder kept in the final files delivered to users. +The edgelist is intermediate and by default not placed in the output folder with the final files delivered to users. Set `--save_edgelist` to enable publishing of these file. Alternatively, set `--save_all` to keep all intermediary outputs of all steps. @@ -187,8 +187,8 @@ Alternatively, set `--save_all` to keep all intermediary outputs of all steps. This step uses the `pixelator single-cell annotate` command. -The annotate command takes as input the edge list 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 +The annotate command takes as input the molecule list file generated in the graph command. It parses, and filters the +molecules grouped by "component" ID to find putative cells, and it will generate a PXL file containing the edges of the graphs in an edgelist, and an (AnnData object)[https://anndata.readthedocs.io/en/latest/] as well as some useful metadata. Some summary statistics before filtering are stored in `raw_components_metrics.csv.gz`. @@ -203,7 +203,7 @@ Set `--save_annotate_dataset` to include these files. - `pixelator` - `annotate` - - `.annotate.dataset.pxl`: The procesed PXL dataset, + - `.annotate.dataset.pxl`: The annotated PXL dataset, - `.meta.json`: Command invocation metadata. - `.raw_components_metrics.csv.gz` - `.report.json`: Statistics for the analysis step. @@ -214,8 +214,8 @@ Set `--save_annotate_dataset` to include these files. ### 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 are added to the pxl file. +Downstream analyses are performed on the PXL file generated by the previous stage. +The results of the analysis are added to the PXL file produced in this stage. Currently, the following analysis are performed: @@ -280,7 +280,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](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/qc-report/)
Output files @@ -310,8 +310,8 @@ More information on the report can be found in the [pixelator documentation](htt ## Output directory structure -With default parameters, the pixelator pipeline output directory will only include the most "complete" PXL file -generated by the pipeline and an interactive HTML report per sample. +With default parameters, the pixelator pipeline output directory will only include the latest PXL file +generated by the pipeline (with the most "complete" information) and an interactive HTML report per sample. The PXL dataset files can be from either the `annotate`, `analysis` or `layout` step. With default parameters, the `.layout.datasets.pxl` will be copied to the output directory. From 4f1de61a69799fca98a0894988a39624a65e35f2 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 1 Jul 2024 16:27:54 +0200 Subject: [PATCH 208/260] Make fastq file reference all caps --- docs/output.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/output.md b/docs/output.md index c1846b83..9facd47e 100644 --- a/docs/output.md +++ b/docs/output.md @@ -25,7 +25,7 @@ The pipeline consists of the following steps: ### Preprocessing 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. +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. These amplicon FASTQ files are intermediate and by default not placed in the output folder with the final files delivered to users. From af797e5be34b443769cd9443f43424adcf5fe244 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 1 Jul 2024 16:28:18 +0200 Subject: [PATCH 209/260] Tweak graph stage output description --- docs/output.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/output.md b/docs/output.md index 9facd47e..7aa3643e 100644 --- a/docs/output.md +++ b/docs/output.md @@ -148,9 +148,9 @@ Alternatively, set `--save_all` to keep all intermediary outputs of all steps. ### Compute connected components This step uses the `pixelator single-cell graph` command. -The input is the edge list parquet file 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 parquet file generated in the collapse step. +The molecules from edge list are filtered by count (`--graph_min_count`) to form the edges of the connected components of the graph. +When graphs are computed and identified, their ID names are added back 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 From 35160316f8baeae49148aeb493a7be0ba8b7c114 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 1 Jul 2024 17:00:58 +0200 Subject: [PATCH 210/260] Tweak collapse output description --- docs/output.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/output.md b/docs/output.md index 7aa3643e..997b62c3 100644 --- a/docs/output.md +++ b/docs/output.md @@ -118,9 +118,9 @@ Alternatively, set `--save_all` to keep all intermediary outputs of all steps. 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. +The `collapse` command quantifies molecules by performing error correction and detecting PCR duplicates. +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 output format of this command is a parquet file containing deduplicated and error-corrected molecules. From 115c87dedfc788501e599d91e52cd2b4b8b8900e Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Mon, 8 Jul 2024 13:41:40 +0200 Subject: [PATCH 211/260] rate-diff is now default colocalization transform --- nextflow.config | 2 +- nextflow_schema.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nextflow.config b/nextflow.config index dba8a423..a2e9762f 100644 --- a/nextflow.config +++ b/nextflow.config @@ -57,7 +57,7 @@ params { polarization_transformation = "log1p" polarization_min_marker_count = 5 polarization_n_permutations = 50 - colocalization_transformation = "log1p" + colocalization_transformation = "rate-diff" colocalization_neighbourhood_size = 1 colocalization_n_permutations = 50 colocalization_min_region_count = 5 diff --git a/nextflow_schema.json b/nextflow_schema.json index 9c353a70..30410ba2 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -349,7 +349,7 @@ "colocalization_transformation": { "type": "string", "enum": ["raw", "log1p", "rate-diff"], - "default": "log1p", + "default": "rate-diff", "description": "Select the type of transformation to use on the node by antibody counts matrix when computing colocalization" }, "colocalization_neighbourhood_size": { From d7d1e7ec6408d0daeaec737b35f1a14e83aeb986 Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Mon, 8 Jul 2024 13:44:00 +0200 Subject: [PATCH 212/260] Update the changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9da0bd1f..47522e78 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [[PR #100](https://github.com/nf-core/pixelator/pull/100)] - Use R1/R2 suffixes in amplicon input fastq file renaming - [[PR #101](https://github.com/nf-core/pixelator/pull/101)] - Fix validation issue when using panel_file instead of panel - [[PR #102](https://github.com/nf-core/pixelator/pull/101)] - Restructure output directory +- [[PR #103](https://github.com/nf-core/pixelator/pull/103)] - Make rate-diff the default transformation method when computing colocalization ### Software dependencies From cbe7fc7fae536fa5f4cad15f3aef9167fff7f33a Mon Sep 17 00:00:00 2001 From: Johan Dahlberg Date: Mon, 8 Jul 2024 13:45:24 +0200 Subject: [PATCH 213/260] Empty commit to retrigger CI From faec0f4af5fcb654dc42dda9175c02bd95ab9caf Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 11 Jul 2024 13:59:22 +0200 Subject: [PATCH 214/260] Add colocalization_min_marker_count parameter --- conf/modules.config | 1 + nextflow.config | 1 + nextflow_schema.json | 6 ++++++ 3 files changed, 8 insertions(+) diff --git a/conf/modules.config b/conf/modules.config index 7e14768e..438abfe4 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -308,6 +308,7 @@ process { (params.colocalization_neighbourhood_size instanceof Integer) ? "--colocalization-neighbourhood-size ${params.colocalization_neighbourhood_size}" : '', (params.colocalization_n_permutations instanceof Integer) ? "--colocalization-n-permutations ${params.colocalization_n_permutations}" : '', (params.colocalization_min_region_count instanceof Integer) ? "--colocalization-min-region-count ${params.colocalization_min_region_count}" : '', + (params.colocalization_min_marker_count instanceof Integer) ? "--colocalization-min-marker-count ${params.colocalization_min_marker_count}" : '' ].join(' ').trim() } diff --git a/nextflow.config b/nextflow.config index a2e9762f..acfb185d 100644 --- a/nextflow.config +++ b/nextflow.config @@ -61,6 +61,7 @@ params { colocalization_neighbourhood_size = 1 colocalization_n_permutations = 50 colocalization_min_region_count = 5 + colocalization_min_marker_count = 5 // Output options save_amplicon_reads = false diff --git a/nextflow_schema.json b/nextflow_schema.json index 30410ba2..b83c2789 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -370,6 +370,12 @@ "default": 5, "minimum": 0 }, + "colocalization_min_marker_count": { + "type": "integer", + "description": "The minimum number of counts in a component for for it to be considered valid for computing colocalization", + "default": 5, + "minimum": 0 + }, "save_analysis_dataset": { "fa_icon": "fas fa-save", "type": "boolean", From 6acc037b8cbc21de6a870682f0563809a9dbea66 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Thu, 11 Jul 2024 14:00:35 +0200 Subject: [PATCH 215/260] Change default layout algorithm to wpmds_3d --- nextflow.config | 2 +- nextflow_schema.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nextflow.config b/nextflow.config index acfb185d..174b3d1f 100644 --- a/nextflow.config +++ b/nextflow.config @@ -80,7 +80,7 @@ params { // layout options no_node_marker_counts = false - layout_algorithm = "pmds_3d" + layout_algorithm = "wpmds_3d" // skip options skip_report = false diff --git a/nextflow_schema.json b/nextflow_schema.json index b83c2789..a3bb084a 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -402,7 +402,7 @@ "description": "Select a layout algorithm to use. This can be specified as a comma separated list to compute multiple layouts. Possible values are: fruchterman_reingold, fruchterman_reingold_3d, kamada_kawai, kamada_kawai_3d, pmds, pmds_3d", "type": "string", "pattern": "(\\S+)?(,\\S+)*", - "default": "pmds_3d" + "default": "wpmds_3d" } } }, From a136ed00e9b5bda3262773eb06c4594dc21d4095 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 12 Jul 2024 10:21:59 +0200 Subject: [PATCH 216/260] Update pixelator container to 0.18 using override param --- nextflow.config | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/nextflow.config b/nextflow.config index 174b3d1f..11e38651 100644 --- a/nextflow.config +++ b/nextflow.config @@ -88,7 +88,8 @@ params { skip_layout = false // Main pixelator container override - pixelator_container = null + // TODO: remove this and use biocontainers image before release + pixelator_container = "ghcr.io/pixelgentechnologies/pixelator:0.18" // Boilerplate options outdir = null From cadf011987a7c79ba85a9d5a59f5f1ad3dd9f906 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Fri, 12 Jul 2024 12:21:35 +0200 Subject: [PATCH 217/260] Add v2 panel small test profile --- conf/test_v2.config | 49 +++++++++++++++++++++++++++++++++++++++++++++ nextflow.config | 1 + 2 files changed, 50 insertions(+) create mode 100644 conf/test_v2.config diff --git a/conf/test_v2.config b/conf/test_v2.config new file mode 100644 index 00000000..0c107c18 --- /dev/null +++ b/conf/test_v2.config @@ -0,0 +1,49 @@ +/* +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Nextflow config file for running minimal tests +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + Defines input files and everything required to run a fast and simple pipeline test. + + Use as follows: + nextflow run nf-core/pixelator -profile test, --outdir + +---------------------------------------------------------------------------------------- +*/ + + +aws.client.downloadParallel = true + + +params { + config_profile_name = 'Test profile' + config_profile_description = 'Minimal test dataset to check pipeline function' + + // Limit resources so that this can run on GitHub Actions + max_cpus = 2 + max_memory = '6.GB' + max_time = '6.h' + + // Input data + // input = params.pipelines_testdata_base_path + 'pixelator/samplesheet/samplesheet_v2.csv' + // input_basedir = params.pipelines_testdata_base_path + 'pixelator/testdata/' + input = params.pipelines_testdata_base_path + 'samplesheet/samplesheet_v2.csv' + input_basedir = params.pipelines_testdata_base_path + 'testdata/' + + multiplet_recovery = true + min_size = 2 + max_size = 100000 + compute_polarization = true + use_full_bipartite = true + colocalization_min_region_count = 0 + colocalization_n_permutations = 10 + colocalization_neighbourhood_size = 1 + + // Number of pivots is larger than the number of components in the graph for this small testdata + compute_svd_var_explained = false + + // For now skip the layout step since it is very slow on these + // small test datasets + skip_layout = true + // using this since the default pmds_3d does not work on very small graphs + layout_algorithm = "fruchterman_reingold_3d" +} diff --git a/nextflow.config b/nextflow.config index 11e38651..98f85f48 100644 --- a/nextflow.config +++ b/nextflow.config @@ -256,6 +256,7 @@ profiles { executor.memory = 8.GB } test { includeConfig 'conf/test.config' } + test_v2 { includeConfig 'conf/test_v2.config' } test_full { includeConfig 'conf/test_full.config' } } From 96bd92769ae0e8d085566a90cea7354d10e7c292 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 15 Jul 2024 10:09:11 +0200 Subject: [PATCH 218/260] Update pixelator container to 0.18.1 --- 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/layout/main.nf | 4 ++-- modules/local/pixelator/single-cell/qc/main.nf | 4 ++-- modules/local/pixelator/single-cell/report/main.nf | 4 ++-- nextflow.config | 3 +-- 12 files changed, 23 insertions(+), 24 deletions(-) diff --git a/modules/local/pixelator/collect_metadata.nf b/modules/local/pixelator/collect_metadata.nf index 1867abc0..e93cc12c 100644 --- a/modules/local/pixelator/collect_metadata.nf +++ b/modules/local/pixelator/collect_metadata.nf @@ -8,8 +8,8 @@ process PIXELATOR_COLLECT_METADATA { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.17.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.1--pyhdfd78af_0' }" input: diff --git a/modules/local/pixelator/list_options.nf b/modules/local/pixelator/list_options.nf index 70854636..2d225503 100644 --- a/modules/local/pixelator/list_options.nf +++ b/modules/local/pixelator/list_options.nf @@ -4,8 +4,8 @@ process PIXELATOR_LIST_OPTIONS { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.17.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.1--pyhdfd78af_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 bfc9bacf..98b62b6a 100644 --- a/modules/local/pixelator/single-cell/amplicon/main.nf +++ b/modules/local/pixelator/single-cell/amplicon/main.nf @@ -5,8 +5,8 @@ process PIXELATOR_AMPLICON { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.17.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.1--pyhdfd78af_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 4cab8af7..bac03de7 100644 --- a/modules/local/pixelator/single-cell/analysis/main.nf +++ b/modules/local/pixelator/single-cell/analysis/main.nf @@ -4,8 +4,8 @@ process PIXELATOR_ANALYSIS { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.17.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.1--pyhdfd78af_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 0d3dac4e..caaf5bf9 100644 --- a/modules/local/pixelator/single-cell/annotate/main.nf +++ b/modules/local/pixelator/single-cell/annotate/main.nf @@ -5,8 +5,8 @@ process PIXELATOR_ANNOTATE { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.17.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.1--pyhdfd78af_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 22ad72a5..ac3ba189 100644 --- a/modules/local/pixelator/single-cell/collapse/main.nf +++ b/modules/local/pixelator/single-cell/collapse/main.nf @@ -4,8 +4,8 @@ process PIXELATOR_COLLAPSE { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.17.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.1--pyhdfd78af_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 41d6d99f..06596076 100644 --- a/modules/local/pixelator/single-cell/demux/main.nf +++ b/modules/local/pixelator/single-cell/demux/main.nf @@ -5,8 +5,8 @@ process PIXELATOR_DEMUX { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.17.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.1--pyhdfd78af_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 35d9fb2a..34a2ae28 100644 --- a/modules/local/pixelator/single-cell/graph/main.nf +++ b/modules/local/pixelator/single-cell/graph/main.nf @@ -5,8 +5,8 @@ process PIXELATOR_GRAPH { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.17.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.1--pyhdfd78af_0' }" input: tuple val(meta), path(edge_list) diff --git a/modules/local/pixelator/single-cell/layout/main.nf b/modules/local/pixelator/single-cell/layout/main.nf index 9b534c1a..6e25fb50 100644 --- a/modules/local/pixelator/single-cell/layout/main.nf +++ b/modules/local/pixelator/single-cell/layout/main.nf @@ -5,8 +5,8 @@ process PIXELATOR_LAYOUT { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.17.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.1--pyhdfd78af_0' }" input: tuple val(meta), path(data) diff --git a/modules/local/pixelator/single-cell/qc/main.nf b/modules/local/pixelator/single-cell/qc/main.nf index fe9dbce3..568ba212 100644 --- a/modules/local/pixelator/single-cell/qc/main.nf +++ b/modules/local/pixelator/single-cell/qc/main.nf @@ -5,8 +5,8 @@ process PIXELATOR_QC { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.17.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.1--pyhdfd78af_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 6d891a62..2a10dbcb 100644 --- a/modules/local/pixelator/single-cell/report/main.nf +++ b/modules/local/pixelator/single-cell/report/main.nf @@ -5,8 +5,8 @@ process PIXELATOR_REPORT { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.17.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.17.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.1--pyhdfd78af_0' }" input: tuple val(meta), path(panel_file), val(panel) diff --git a/nextflow.config b/nextflow.config index 98f85f48..abbb8d59 100644 --- a/nextflow.config +++ b/nextflow.config @@ -88,8 +88,7 @@ params { skip_layout = false // Main pixelator container override - // TODO: remove this and use biocontainers image before release - pixelator_container = "ghcr.io/pixelgentechnologies/pixelator:0.18" + pixelator_container = null // Boilerplate options outdir = null From e9e9035eab7ebf73741fc1f8f04efecf41008149 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 15 Jul 2024 10:25:23 +0200 Subject: [PATCH 219/260] Update CHANGELOG.md --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47522e78..64c5ff3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,12 +15,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [[PR #101](https://github.com/nf-core/pixelator/pull/101)] - Fix validation issue when using panel_file instead of panel - [[PR #102](https://github.com/nf-core/pixelator/pull/101)] - Restructure output directory - [[PR #103](https://github.com/nf-core/pixelator/pull/103)] - Make rate-diff the default transformation method when computing colocalization +- [[PR #104](https://github.com/nf-core/pixelator/pull/104)] - Update to pixelator 0.18.1 ### Software dependencies | Dependency | Old version | New version | | ----------- | ----------- | ----------- | -| `pixelator` | 0.17.1 | 0.17.1 | +| `pixelator` | 0.17.1 | 0.18.1 | > **NB:** Dependency has been **updated** if both old and new version information is present. > From b47a4bde06ab32788b0e079ad91eb008a20b536f Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 15 Jul 2024 10:36:56 +0200 Subject: [PATCH 220/260] Remove unused parameter from test_v2.config --- conf/test_v2.config | 3 --- 1 file changed, 3 deletions(-) diff --git a/conf/test_v2.config b/conf/test_v2.config index 0c107c18..58ce2ba0 100644 --- a/conf/test_v2.config +++ b/conf/test_v2.config @@ -38,9 +38,6 @@ params { colocalization_n_permutations = 10 colocalization_neighbourhood_size = 1 - // Number of pivots is larger than the number of components in the graph for this small testdata - compute_svd_var_explained = false - // For now skip the layout step since it is very slow on these // small test datasets skip_layout = true From 3790c92d6dca4e237ba886e76c05c4e2b0284194 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 15 Jul 2024 10:47:28 +0200 Subject: [PATCH 221/260] Add `human-sc-immunology-spatial-proteomics-2` panel in docs --- docs/usage.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index f6337e55..778114ae 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -127,9 +127,12 @@ A list of available panels can be listed by running following command: pixelator single-cell --list-panels ``` -Currently, a single built-in panel is available: +Currently, two built-in panels are available: -- `human-sc-immunology-spatial-proteomics` +- `human-sc-immunology-spatial-proteomics-1` +- `human-sc-immunology-spatial-proteomics-2` + +`human-sc-immunology-spatial-proteomics` is also an allowed value and is an alias to `human-sc-immunology-spatial-proteomics-1`. ## Running the pipeline From 2fd30b3bf67b04a46eba8281e6cd195a6ddb6c75 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 15 Jul 2024 11:16:04 +0200 Subject: [PATCH 222/260] Bump version to 1.3.0 --- CHANGELOG.md | 2 +- nextflow.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 64c5ff3b..0189780e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ 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). -## [[1.3.0dev](https://github.com/nf-core/pixelator/releases/tag/?.?.?)] - 2024-??-?? +## [[1.3.0](https://github.com/nf-core/pixelator/releases/tag/1.3.0)] - 2024-07-15 ### Enhancements & fixes diff --git a/nextflow.config b/nextflow.config index abbb8d59..7b6d9280 100644 --- a/nextflow.config +++ b/nextflow.config @@ -314,7 +314,7 @@ manifest { description = """Pipeline for analysis of Molecular Pixelation assays""" mainScript = 'main.nf' nextflowVersion = '!>=23.04.0' - version = '1.3.0dev' + version = '1.3.0' doi = '10.1101/2023.06.05.543770' } From ed4f50f548af07f030c4d67e8b9fbd7823395b44 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman <69114541+fbdtemme@users.noreply.github.com> Date: Mon, 15 Jul 2024 14:35:59 +0200 Subject: [PATCH 223/260] Apply suggestions from code review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Matthias Hörtenhuber --- CHANGELOG.md | 9 ++++----- README.md | 2 +- conf/test_v2.config | 1 - docs/output.md | 13 ++++++------- nextflow_schema.json | 8 ++++---- 5 files changed, 15 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0189780e..78008145 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,11 +23,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | ----------- | ----------- | ----------- | | `pixelator` | 0.17.1 | 0.18.1 | -> **NB:** Dependency has been **updated** if both old and new version information is present. -> -> **NB:** Dependency has been **added** if just the new version information is present. -> -> **NB:** Dependency has been **removed** if new version information isn't present. +> [!NOTE] +> Dependency has been **updated** if both old and new version information is present. +> Dependency has been **added** if just the new version information is present. +> Dependency has been **removed** if new version information isn't present. ## [[1.2.0](https://github.com/nf-core/pixelator/releases/tag/1.2.0)] - 2024-05-28 diff --git a/README.md b/README.md index 21a86492..ea095574 100644 --- a/README.md +++ b/README.md @@ -103,7 +103,7 @@ You can cite the `nf-core` publication as follows: > > _Nat Biotechnol._ 2020 Feb 13. doi: [10.1038/s41587-020-0439-x](https://dx.doi.org/10.1038/s41587-020-0439-x). -You can cite the Molecular pixelation technology as follows: +You can cite the molecular pixelation technology as follows: > **Molecular pixelation: spatial proteomics of single cells by sequencing.** > diff --git a/conf/test_v2.config b/conf/test_v2.config index 58ce2ba0..cf50d83d 100644 --- a/conf/test_v2.config +++ b/conf/test_v2.config @@ -13,7 +13,6 @@ aws.client.downloadParallel = true - params { config_profile_name = 'Test profile' config_profile_description = 'Minimal test dataset to check pipeline function' diff --git a/docs/output.md b/docs/output.md index 997b62c3..e7bcbb88 100644 --- a/docs/output.md +++ b/docs/output.md @@ -52,13 +52,12 @@ Set `--save_amplicon_reads` or `--save_all` to enable publishing of these files 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 +The preqc step performs QC and quality filtering of the raw sequencing data using [Fastp](https://github.com/OpenGene/fastp) internally. +It 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. +The `adapterqc` stage checks for the presence and correctness of the pixel binding sequences, +using [Cutadapt](https://cutadapt.readthedocs.io/en/stable/) internally. 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). These processed and discarded FASTQ reads are intermediate and by default not placed in the output folder with the final files delivered to users. @@ -158,7 +157,7 @@ components using community detection to find and remove problematic edges newly recovered components are stored in a file (components_recovered.csv). This file is not included in the output folder by default, but can be included by passing `--save_recovered_components`. -The edgelist is intermediate and by default not placed in the output folder with the final files delivered to users. +The edge list is intermediate and by default not placed in the output folder with the final files delivered to users. Set `--save_edgelist` to enable publishing of these file. Alternatively, set `--save_all` to keep all intermediary outputs of all steps. @@ -217,7 +216,7 @@ This step uses the `pixelator single-cell analysis` command. Downstream analyses are performed on the PXL file generated by the previous stage. The results of the analysis are added to the PXL file produced in this stage. -Currently, the following analysis are performed: +Currently, the following analyses are performed: - polarization scores (enable with `--compute_polarization`) - co-localization scores (enable with `--compute_colocalization`) diff --git a/nextflow_schema.json b/nextflow_schema.json index a3bb084a..534655cd 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -55,7 +55,7 @@ "type": "boolean", "default": false, "description": "Save intermediate amplicon reads generated from the raw input reads.", - "help": "By default, generated amplicon FastQ files will not be saved to the results directory. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." + "help": "By default, generated amplicon FastQ files will not be saved to the results directory. Specify this flag (or set it to `true` in your config file) to copy these files to the results directory when complete." } } }, @@ -123,14 +123,14 @@ "fa_icon": "fas fa-save", "type": "boolean", "default": false, - "description": "Save intermediate qc read files containing all reads that passed the filters.", - "help": "By default, filtered read FastQ files after QC will not be saved to the results directory. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." + "description": "Save intermediate QC read files containing all reads that passed the filters.", + "help": "By default, filtered read FastQ files after QC will not be saved to the results directory. Specify this flag (or set it to `true` in your config file) to copy these files to the results directory when complete." }, "save_qc_failed_reads": { "fa_icon": "fas fa-save", "type": "boolean", "default": false, - "description": "Save intermediate qc read files containing all reads that failed the filters.", + "description": "Save intermediate QC read files containing all reads that failed the filters.", "help": "By default, FastQ files with reads that failed QC will not be saved to the results directory. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." } } From b262018cef9144ce866a1bf1e0e1723a7d4e8801 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 15 Jul 2024 14:39:43 +0200 Subject: [PATCH 224/260] Rename `test_v2` to `test_panel_v2` and align --- conf/test.config | 24 +++++++++---------- conf/{test_v2.config => test_panel_v2.config} | 22 ++++++++--------- nextflow.config | 6 ++--- 3 files changed, 26 insertions(+), 26 deletions(-) rename conf/{test_v2.config => test_panel_v2.config} (72%) diff --git a/conf/test.config b/conf/test.config index 12b8cb81..90071e09 100644 --- a/conf/test.config +++ b/conf/test.config @@ -24,21 +24,21 @@ params { max_time = '6.h' // Input data - input = params.pipelines_testdata_base_path + 'pixelator/samplesheet/samplesheet.csv' - input_basedir = params.pipelines_testdata_base_path + 'pixelator/testdata' - - multiplet_recovery = true - min_size = 2 - max_size = 100000 - compute_polarization = true - use_full_bipartite = true - colocalization_min_region_count = 0 - colocalization_n_permutations = 10 + input = params.pipelines_testdata_base_path + 'pixelator/samplesheet/samplesheet.csv' + input_basedir = params.pipelines_testdata_base_path + 'pixelator/testdata' + + multiplet_recovery = true + min_size = 2 + max_size = 100000 + compute_polarization = true + use_full_bipartite = true + colocalization_min_region_count = 0 + colocalization_n_permutations = 10 colocalization_neighbourhood_size = 1 // For now skip the layout step since it is very slow on these // small test datasets - skip_layout = true + skip_layout = true // using this since the default pmds_3d does not work on very small graphs - layout_algorithm = "fruchterman_reingold_3d" + layout_algorithm = "fruchterman_reingold_3d" } diff --git a/conf/test_v2.config b/conf/test_panel_v2.config similarity index 72% rename from conf/test_v2.config rename to conf/test_panel_v2.config index cf50d83d..a9b365bd 100644 --- a/conf/test_v2.config +++ b/conf/test_panel_v2.config @@ -25,21 +25,21 @@ params { // Input data // input = params.pipelines_testdata_base_path + 'pixelator/samplesheet/samplesheet_v2.csv' // input_basedir = params.pipelines_testdata_base_path + 'pixelator/testdata/' - input = params.pipelines_testdata_base_path + 'samplesheet/samplesheet_v2.csv' - input_basedir = params.pipelines_testdata_base_path + 'testdata/' - - multiplet_recovery = true - min_size = 2 - max_size = 100000 - compute_polarization = true - use_full_bipartite = true - colocalization_min_region_count = 0 - colocalization_n_permutations = 10 + input = params.pipelines_testdata_base_path + 'samplesheet/samplesheet_v2.csv' + input_basedir = params.pipelines_testdata_base_path + 'testdata/' + + multiplet_recovery = true + min_size = 2 + max_size = 100000 + compute_polarization = true + use_full_bipartite = true + colocalization_min_region_count = 0 + colocalization_n_permutations = 10 colocalization_neighbourhood_size = 1 // For now skip the layout step since it is very slow on these // small test datasets - skip_layout = true + skip_layout = true // using this since the default pmds_3d does not work on very small graphs layout_algorithm = "fruchterman_reingold_3d" } diff --git a/nextflow.config b/nextflow.config index 7b6d9280..3ffb3a49 100644 --- a/nextflow.config +++ b/nextflow.config @@ -254,9 +254,9 @@ profiles { executor.cpus = 4 executor.memory = 8.GB } - test { includeConfig 'conf/test.config' } - test_v2 { includeConfig 'conf/test_v2.config' } - test_full { includeConfig 'conf/test_full.config' } + test { includeConfig 'conf/test.config' } + test_panel_v2 { includeConfig 'conf/test_panel_v2.config' } + test_full { includeConfig 'conf/test_full.config' } } // Set default registry for Apptainer, Docker, Podman and Singularity independent of -profile From 3de2f54063a751eaae40dfe71d6a612198b2277f Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 15 Jul 2024 14:44:19 +0200 Subject: [PATCH 225/260] Use `edge list` instead of `edgelist` in docs --- docs/output.md | 6 +++--- nextflow_schema.json | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/output.md b/docs/output.md index e7bcbb88..7f33e175 100644 --- a/docs/output.md +++ b/docs/output.md @@ -56,7 +56,7 @@ The preqc step performs QC and quality filtering of the raw sequencing data usin It 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` -The `adapterqc` stage checks for the presence and correctness of the pixel binding sequences, +The `adapterqc` stage checks for the presence and correctness of the pixel binding sequences, using [Cutadapt](https://cutadapt.readthedocs.io/en/stable/) internally. 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). @@ -135,7 +135,7 @@ Alternatively, set `--save_all` to keep all intermediary outputs of all steps. - `collapse` - - `.collapsed.parquet`: Edgelist of the graph. + - `.collapsed.parquet`: Edge list of the graph. - `.report.json`: Statistics for the collapse step. - `.meta.json`: Command invocation metadata. @@ -187,7 +187,7 @@ Alternatively, set `--save_all` to keep all intermediary outputs of all steps. This step uses the `pixelator single-cell annotate` command. The annotate command takes as input the molecule list file generated in the graph command. It parses, and filters the -molecules grouped by "component" ID to find putative cells, and it will generate a PXL file containing the edges of the graphs in an edgelist, and an +molecules grouped by "component" ID to find putative cells, and it will generate a PXL file containing the edges of the graphs in an edge list, and an (AnnData object)[https://anndata.readthedocs.io/en/latest/] as well as some useful metadata. Some summary statistics before filtering are stored in `raw_components_metrics.csv.gz`. diff --git a/nextflow_schema.json b/nextflow_schema.json index 534655cd..87d799a2 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -254,8 +254,8 @@ "fa_icon": "fas fa-save", "type": "boolean", "default": false, - "description": "Save an intermediate csv file containing the unfiltered graph edgelist.", - "help": "By default, the unfiltered edgelist will not be saved to the results directory. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." + "description": "Save an intermediate csv file containing the unfiltered graph edge list.", + "help": "By default, the unfiltered edge list will not be saved to the results directory. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." }, "save_recovered_components": { "fa_icon": "fas fa-save", From 12a2aa9d12380412c91246261b0c24eba5a6efca Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 15 Jul 2024 15:06:07 +0200 Subject: [PATCH 226/260] Output preqc html reports with `--save_all` --- conf/modules.config | 6 ++++++ docs/output.md | 3 +++ modules/local/pixelator/single-cell/qc/main.nf | 2 ++ 3 files changed, 11 insertions(+) diff --git a/conf/modules.config b/conf/modules.config index 438abfe4..c8350e70 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -139,6 +139,12 @@ process { mode: params.publish_dir_mode, pattern: '**/*.{report,meta}.json', saveAs: { (params.save_all) ? it : null } + ], + [ + path: { "${params.outdir}/pixelator" }, + mode: params.publish_dir_mode, + pattern: '**/*.qc-report.html', + saveAs: { (params.save_all) ? it : null } ] ] } diff --git a/docs/output.md b/docs/output.md index 7f33e175..7d3f5d09 100644 --- a/docs/output.md +++ b/docs/output.md @@ -70,10 +70,13 @@ Alternatively, set `--save_all` to keep all intermediary outputs of all steps. - `pixelator` - `preqc` + - `.processed.fastq.gz`: Processed reads. - `.failed.fastq.gz`: Discarded reads. - `.report.json`: Fastp json report. + - `.qc-report.html`: Fastp html report. - `.meta.json`: Command invocation metadata. + - `adapterqc` - `.processed.fastq.gz`: Processed reads. diff --git a/modules/local/pixelator/single-cell/qc/main.nf b/modules/local/pixelator/single-cell/qc/main.nf index 568ba212..d3a6d997 100644 --- a/modules/local/pixelator/single-cell/qc/main.nf +++ b/modules/local/pixelator/single-cell/qc/main.nf @@ -25,6 +25,8 @@ process PIXELATOR_QC { 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("preqc/*.qc-report.html") , emit: preqc_report_html + 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 From 42c0653a9354081350262378e55caeb3215d89e0 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 15 Jul 2024 15:11:59 +0200 Subject: [PATCH 227/260] Align code in nextflow.config --- nextflow.config | 159 ++++++++++++++++++++++++------------------------ 1 file changed, 79 insertions(+), 80 deletions(-) diff --git a/nextflow.config b/nextflow.config index 3ffb3a49..63de05a6 100644 --- a/nextflow.config +++ b/nextflow.config @@ -11,117 +11,116 @@ params { // Input options - input = null - input_basedir = null + input = null + input_basedir = 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 + 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 + adapterqc_mismatches = 0.1 // demux options - demux_mismatches = 0.1 - demux_min_length = null + demux_mismatches = 0.1 + demux_min_length = null // collapse options - markers_ignore = null - algorithm = 'adjacency' - max_neighbours = 60 - collapse_mismatches = 2 - collapse_min_count = 2 - collapse_use_counts = false + markers_ignore = null + algorithm = 'adjacency' + max_neighbours = 60 + collapse_mismatches = 2 + collapse_min_count = 2 + collapse_use_counts = false // graph options - multiplet_recovery = true - leiden_iterations = 10 - graph_min_count = 2 + multiplet_recovery = true + leiden_iterations = 10 + graph_min_count = 2 // annotate options - min_size = null - max_size = null - dynamic_filter = 'min' - aggregate_calling = true + min_size = null + max_size = null + dynamic_filter = 'min' + aggregate_calling = true // analysis options - compute_polarization = true - compute_colocalization = true - use_full_bipartite = false - polarization_transformation = "log1p" - polarization_min_marker_count = 5 - polarization_n_permutations = 50 - colocalization_transformation = "rate-diff" - colocalization_neighbourhood_size = 1 - colocalization_n_permutations = 50 - colocalization_min_region_count = 5 - colocalization_min_marker_count = 5 + compute_polarization = true + compute_colocalization = true + use_full_bipartite = false + polarization_transformation = "log1p" + polarization_min_marker_count = 5 + polarization_n_permutations = 50 + colocalization_transformation = "rate-diff" + colocalization_neighbourhood_size = 1 + colocalization_n_permutations = 50 + colocalization_min_region_count = 5 + colocalization_min_marker_count = 5 // Output options - save_amplicon_reads = false - save_qc_passed_reads = false - save_qc_failed_reads = false - save_demux_processed_reads = false - save_demux_failed_reads = false - save_collapsed_reads = false - save_recovered_components = false - save_edgelist = false - save_annotate_dataset = false - save_raw_component_metrics = false - save_analysis_dataset = false - - save_all = false + save_amplicon_reads = false + save_qc_passed_reads = false + save_qc_failed_reads = false + save_demux_processed_reads = false + save_demux_failed_reads = false + save_collapsed_reads = false + save_recovered_components = false + save_edgelist = false + save_annotate_dataset = false + save_raw_component_metrics = false + save_analysis_dataset = false + save_all = false // layout options - no_node_marker_counts = false - layout_algorithm = "wpmds_3d" + no_node_marker_counts = false + layout_algorithm = "wpmds_3d" // skip options - skip_report = false - skip_analysis = false - skip_layout = false + skip_report = false + skip_analysis = false + skip_layout = false // Main pixelator container override - pixelator_container = null + pixelator_container = null // Boilerplate options - outdir = null - publish_dir_mode = 'copy' - email = null - email_on_fail = null - plaintext_email = false - monochrome_logs = false - hook_url = null - help = false - version = false - pipelines_testdata_base_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/' + outdir = null + publish_dir_mode = 'copy' + email = null + email_on_fail = null + plaintext_email = false + monochrome_logs = false + hook_url = null + help = false + version = false + pipelines_testdata_base_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/' // 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_contact = null - config_profile_url = null + 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_contact = null + config_profile_url = null // Max resource options // Defaults only, expecting to be overwritten - max_memory = '128.GB' - max_cpus = 16 - max_time = '240.h' + max_memory = '128.GB' + max_cpus = 16 + max_time = '240.h' // Schema validation default options - validationFailUnrecognisedParams = false - validationLenientMode = false - validationSchemaIgnoreParams = 'genomes,igenomes_base' - validationShowHiddenParams = false - validate_params = true + validationFailUnrecognisedParams = false + validationLenientMode = false + validationSchemaIgnoreParams = 'genomes,igenomes_base' + validationShowHiddenParams = false + validate_params = true } // Load base.config by default for all pipelines From d701a5931601cb9cfbc4d5771b67253f340fcc92 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 15 Jul 2024 15:13:39 +0200 Subject: [PATCH 228/260] Update test_panel_v2 usage comment --- conf/test_panel_v2.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/test_panel_v2.config b/conf/test_panel_v2.config index a9b365bd..68b2552f 100644 --- a/conf/test_panel_v2.config +++ b/conf/test_panel_v2.config @@ -5,7 +5,7 @@ Defines input files and everything required to run a fast and simple pipeline test. Use as follows: - nextflow run nf-core/pixelator -profile test, --outdir + nextflow run nf-core/pixelator -profile test_panel_v2, --outdir ---------------------------------------------------------------------------------------- */ From 44fc31daf37429aab5900f78e883b56f47d92de8 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 15 Jul 2024 15:32:36 +0200 Subject: [PATCH 229/260] Fix tubemap icon alignment, add pipeline version --- docs/images/nf-core-pixelator-metromap.svg | 74 +++++++++++----------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/docs/images/nf-core-pixelator-metromap.svg b/docs/images/nf-core-pixelator-metromap.svg index 0b66bdc0..9a1b4188 100644 --- a/docs/images/nf-core-pixelator-metromap.svg +++ b/docs/images/nf-core-pixelator-metromap.svg @@ -117,53 +117,53 @@ - + - + - + - + - + - + - + - - - - - - - + + + + + + + - - - - + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + @@ -235,7 +235,7 @@ - + @@ -245,7 +245,7 @@ - + @@ -262,16 +262,16 @@ - + - + - + - + From bc8526a075d4aef0cfc0a8b5be81aee8c13e051b Mon Sep 17 00:00:00 2001 From: Florian De Temmerman <69114541+fbdtemme@users.noreply.github.com> Date: Tue, 16 Jul 2024 14:29:01 +0200 Subject: [PATCH 230/260] Apply suggestions from code review Fix some typos and inconsistencies in the docs Co-authored-by: avani-bhojwani <68935109+avani-bhojwani@users.noreply.github.com> --- conf/test_panel_v2.config | 3 ++- nextflow_schema.json | 26 +++++++++++++------------- 2 files changed, 15 insertions(+), 14 deletions(-) diff --git a/conf/test_panel_v2.config b/conf/test_panel_v2.config index 68b2552f..c36519f7 100644 --- a/conf/test_panel_v2.config +++ b/conf/test_panel_v2.config @@ -2,7 +2,8 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Nextflow config file for running minimal tests ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - Defines input files and everything required to run a fast and simple pipeline test. + Defines input files and everything required to run a fast and simple pipeline test + using the v2 panel. Use as follows: nextflow run nf-core/pixelator -profile test_panel_v2, --outdir diff --git a/nextflow_schema.json b/nextflow_schema.json index 87d799a2..60ad6bca 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -157,15 +157,15 @@ "fa_icon": "fas fa-save", "type": "boolean", "default": false, - "description": "Save intermediate qc read files containing all reads that containt valid antibody barcodes.", - "help": "By default, FastQ files containing reads with valid antibody barcodes will not be saved to the results directory. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." + "description": "Save intermediate QC read files containing all reads that contain valid antibody barcodes.", + "help": "By default, FastQ files containing reads with valid antibody barcodes will not be saved to the results directory. Specify this flag (or set to `true` in your config file) to copy these files to the results directory when complete." }, "save_demux_failed_reads": { "fa_icon": "fas fa-save", "type": "boolean", "default": false, - "description": "Save intermediate qc read files containing all reads that failed the filters.", - "help": "By default, FastQ files containing reads without valid antibody barcodes will not be saved to the results directory. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." + "description": "Save intermediate QC read files containing all reads that failed the filters.", + "help": "By default, FastQ files containing reads without valid antibody barcodes will not be saved to the results directory. Specify this flag (or set to `true` in your config file) to copy these files to the results directory when complete." } } }, @@ -219,7 +219,7 @@ "type": "boolean", "default": false, "description": "Save an intermediate parquet file containing collapsed read information.", - "help": "By default, intermediate collapsed reads will not be saved to the results directory. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." + "help": "By default, intermediate collapsed reads will not be saved to the results directory. Specify this flag (or set to `true` in your config file) to copy these files to the results directory when complete." } } }, @@ -254,15 +254,15 @@ "fa_icon": "fas fa-save", "type": "boolean", "default": false, - "description": "Save an intermediate csv file containing the unfiltered graph edge list.", - "help": "By default, the unfiltered edge list will not be saved to the results directory. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." + "description": "Save an intermediate CSV file containing the unfiltered graph edge list.", + "help": "By default, the unfiltered edge list will not be saved to the results directory. Specify this flag (or set to `true` in your config file) to copy these files to the results directory when complete." }, "save_recovered_components": { "fa_icon": "fas fa-save", "type": "boolean", "default": false, - "description": "Save an intermediate csv file containing the recovered components after multiplet recovery.", - "help": "By default, the recovered component will not be saved to the results directory. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." + "description": "Save an intermediate CSV file containing the recovered components after multiplet recovery.", + "help": "By default, the recovered component will not be saved to the results directory. Specify this flag (or set to `true` in your config file) to copy these files to the results directory when complete." } } }, @@ -294,14 +294,14 @@ "type": "boolean", "default": false, "description": "Save the raw_component_metrics.csv file from the annotate stage.", - "help": "By default, the raw_component_metrics CSV file after annotate will not be saved to the results directory. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." + "help": "By default, the raw_component_metrics CSV file after annotate will not be saved to the results directory. Specify this flag (or set to `true` in your config file) to copy these files to the results directory when complete." }, "save_annotate_dataset": { "fa_icon": "fas fa-save", "type": "boolean", "default": false, "description": "Save the PXL dataset after the annotate stage.", - "help": "By default, the PXL file after annotate will not be saved to the results directory unless `--skip_analysis` and `--skip_layout` is passed. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." + "help": "By default, the PXL file after annotate will not be saved to the results directory unless `--skip_analysis` and `--skip_layout` is passed. Specify this flag (or set to `true` in your config file) to copy these files to the results directory when complete." } } }, @@ -372,7 +372,7 @@ }, "colocalization_min_marker_count": { "type": "integer", - "description": "The minimum number of counts in a component for for it to be considered valid for computing colocalization", + "description": "The minimum number of counts in a component for it to be considered valid for computing colocalization", "default": 5, "minimum": 0 }, @@ -381,7 +381,7 @@ "type": "boolean", "default": false, "description": "Save the PXL dataset after the analysis stage.", - "help": "By default, the PXL dataset after the analysis stage will only be saved be saved when `--skip_layout` is passed. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." + "help": "By default, the PXL dataset after the analysis stage will only be saved be saved when `--skip_layout` is passed. Specify this flag (or set to `true` in your config file) to copy these files to the results directory when complete." } } }, From ff3ab07cbf3314a214b94374897219699221fc3a Mon Sep 17 00:00:00 2001 From: Florian De Temmerman <69114541+fbdtemme@users.noreply.github.com> Date: Tue, 16 Jul 2024 14:31:17 +0200 Subject: [PATCH 231/260] Update nextflow_schema.json Co-authored-by: avani-bhojwani <68935109+avani-bhojwani@users.noreply.github.com> --- nextflow_schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index 60ad6bca..cf0673c5 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -131,7 +131,7 @@ "type": "boolean", "default": false, "description": "Save intermediate QC read files containing all reads that failed the filters.", - "help": "By default, FastQ files with reads that failed QC will not be saved to the results directory. Specify this flag (or set to true in your config file) to copy these files to the results directory when complete." + "help": "By default, FastQ files with reads that failed QC will not be saved to the results directory. Specify this flag (or set to `true` in your config file) to copy these files to the results directory when complete." } } }, From fdbd34d62b2b9a9c75d7a7121fc32793c7988009 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 16 Jul 2024 14:27:03 +0200 Subject: [PATCH 232/260] Fix input paths to testdata in `test_panel_v2` config --- conf/test_panel_v2.config | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/conf/test_panel_v2.config b/conf/test_panel_v2.config index c36519f7..db784f0f 100644 --- a/conf/test_panel_v2.config +++ b/conf/test_panel_v2.config @@ -24,10 +24,8 @@ params { max_time = '6.h' // Input data - // input = params.pipelines_testdata_base_path + 'pixelator/samplesheet/samplesheet_v2.csv' - // input_basedir = params.pipelines_testdata_base_path + 'pixelator/testdata/' - input = params.pipelines_testdata_base_path + 'samplesheet/samplesheet_v2.csv' - input_basedir = params.pipelines_testdata_base_path + 'testdata/' + input = params.pipelines_testdata_base_path + 'pixelator/samplesheet/samplesheet_v2.csv' + input_basedir = params.pipelines_testdata_base_path + 'pixelator/testdata/' multiplet_recovery = true min_size = 2 From 3ffd206c921724bae57d83cf9621b82b33d5851d Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 16 Jul 2024 16:52:08 +0200 Subject: [PATCH 233/260] Update to pixelator 0.18.2 --- CHANGELOG.md | 3 ++- conf/test_panel_v2.config | 2 +- 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/layout/main.nf | 4 ++-- modules/local/pixelator/single-cell/qc/main.nf | 4 ++-- modules/local/pixelator/single-cell/report/main.nf | 4 ++-- 13 files changed, 25 insertions(+), 24 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 78008145..bd1bee8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,12 +16,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [[PR #102](https://github.com/nf-core/pixelator/pull/101)] - Restructure output directory - [[PR #103](https://github.com/nf-core/pixelator/pull/103)] - Make rate-diff the default transformation method when computing colocalization - [[PR #104](https://github.com/nf-core/pixelator/pull/104)] - Update to pixelator 0.18.1 +- [[PR #106](https://github.com/nf-core/pixelator/pull/106)] - Update to pixelator 0.18.2 ### Software dependencies | Dependency | Old version | New version | | ----------- | ----------- | ----------- | -| `pixelator` | 0.17.1 | 0.18.1 | +| `pixelator` | 0.17.1 | 0.18.2 | > [!NOTE] > Dependency has been **updated** if both old and new version information is present. diff --git a/conf/test_panel_v2.config b/conf/test_panel_v2.config index db784f0f..19d84845 100644 --- a/conf/test_panel_v2.config +++ b/conf/test_panel_v2.config @@ -38,7 +38,7 @@ params { // For now skip the layout step since it is very slow on these // small test datasets - skip_layout = true + skip_layout = false // using this since the default pmds_3d does not work on very small graphs layout_algorithm = "fruchterman_reingold_3d" } diff --git a/modules/local/pixelator/collect_metadata.nf b/modules/local/pixelator/collect_metadata.nf index e93cc12c..a6b58729 100644 --- a/modules/local/pixelator/collect_metadata.nf +++ b/modules/local/pixelator/collect_metadata.nf @@ -8,8 +8,8 @@ process PIXELATOR_COLLECT_METADATA { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" input: diff --git a/modules/local/pixelator/list_options.nf b/modules/local/pixelator/list_options.nf index 2d225503..86a03402 100644 --- a/modules/local/pixelator/list_options.nf +++ b/modules/local/pixelator/list_options.nf @@ -4,8 +4,8 @@ process PIXELATOR_LIST_OPTIONS { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.2--pyhdfd78af_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 98b62b6a..d7b5bcf4 100644 --- a/modules/local/pixelator/single-cell/amplicon/main.nf +++ b/modules/local/pixelator/single-cell/amplicon/main.nf @@ -5,8 +5,8 @@ process PIXELATOR_AMPLICON { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.2--pyhdfd78af_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 bac03de7..ede72a1e 100644 --- a/modules/local/pixelator/single-cell/analysis/main.nf +++ b/modules/local/pixelator/single-cell/analysis/main.nf @@ -4,8 +4,8 @@ process PIXELATOR_ANALYSIS { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.2--pyhdfd78af_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 caaf5bf9..b66cf872 100644 --- a/modules/local/pixelator/single-cell/annotate/main.nf +++ b/modules/local/pixelator/single-cell/annotate/main.nf @@ -5,8 +5,8 @@ process PIXELATOR_ANNOTATE { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.2--pyhdfd78af_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 ac3ba189..a34ecd15 100644 --- a/modules/local/pixelator/single-cell/collapse/main.nf +++ b/modules/local/pixelator/single-cell/collapse/main.nf @@ -4,8 +4,8 @@ process PIXELATOR_COLLAPSE { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.2--pyhdfd78af_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 06596076..d1bda817 100644 --- a/modules/local/pixelator/single-cell/demux/main.nf +++ b/modules/local/pixelator/single-cell/demux/main.nf @@ -5,8 +5,8 @@ process PIXELATOR_DEMUX { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.2--pyhdfd78af_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 34a2ae28..c4e4bb61 100644 --- a/modules/local/pixelator/single-cell/graph/main.nf +++ b/modules/local/pixelator/single-cell/graph/main.nf @@ -5,8 +5,8 @@ process PIXELATOR_GRAPH { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" input: tuple val(meta), path(edge_list) diff --git a/modules/local/pixelator/single-cell/layout/main.nf b/modules/local/pixelator/single-cell/layout/main.nf index 6e25fb50..8173aab0 100644 --- a/modules/local/pixelator/single-cell/layout/main.nf +++ b/modules/local/pixelator/single-cell/layout/main.nf @@ -5,8 +5,8 @@ process PIXELATOR_LAYOUT { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" input: tuple val(meta), path(data) diff --git a/modules/local/pixelator/single-cell/qc/main.nf b/modules/local/pixelator/single-cell/qc/main.nf index d3a6d997..f2082c81 100644 --- a/modules/local/pixelator/single-cell/qc/main.nf +++ b/modules/local/pixelator/single-cell/qc/main.nf @@ -5,8 +5,8 @@ process PIXELATOR_QC { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.2--pyhdfd78af_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 2a10dbcb..1a6bbbae 100644 --- a/modules/local/pixelator/single-cell/report/main.nf +++ b/modules/local/pixelator/single-cell/report/main.nf @@ -5,8 +5,8 @@ process PIXELATOR_REPORT { conda "bioconda::pixelator=0.17.1" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.1--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.1--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : + 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" input: tuple val(meta), path(panel_file), val(panel) From a5247ba7aa8f33835e7c924402505c130b60e01f Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 17 Jul 2024 10:50:08 +0200 Subject: [PATCH 234/260] Update release date in CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bd1bee8f..fc8ae895 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ 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). -## [[1.3.0](https://github.com/nf-core/pixelator/releases/tag/1.3.0)] - 2024-07-15 +## [[1.3.0](https://github.com/nf-core/pixelator/releases/tag/1.3.0)] - 2024-07-17 ### Enhancements & fixes From aec3a6650c0e14e5d310c61949e5db560fc26634 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 30 Jul 2024 14:19:55 +0200 Subject: [PATCH 235/260] Fix conda version tag to use pixelator 0.18.2 --- CHANGELOG.md | 6 ++++++ modules/local/pixelator/collect_metadata.nf | 2 +- modules/local/pixelator/list_options.nf | 2 +- modules/local/pixelator/single-cell/amplicon/main.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/demux/main.nf | 2 +- modules/local/pixelator/single-cell/graph/main.nf | 2 +- modules/local/pixelator/single-cell/layout/main.nf | 2 +- modules/local/pixelator/single-cell/qc/main.nf | 2 +- modules/local/pixelator/single-cell/report/main.nf | 2 +- 12 files changed, 17 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc8ae895..12c487cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ 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). +## [[1.3.1](https://github.com/nf-core/pixelator/releases/tag/1.3.0)] - 2024-07-17 + +### Enhancements & fixes + +- [[PR #107](https://github.com/nf-core/pixelator/pull/107)] - Fix conda version tag to use pixelator 0.18.2 + ## [[1.3.0](https://github.com/nf-core/pixelator/releases/tag/1.3.0)] - 2024-07-17 ### Enhancements & fixes diff --git a/modules/local/pixelator/collect_metadata.nf b/modules/local/pixelator/collect_metadata.nf index a6b58729..0817d806 100644 --- a/modules/local/pixelator/collect_metadata.nf +++ b/modules/local/pixelator/collect_metadata.nf @@ -6,7 +6,7 @@ process PIXELATOR_COLLECT_METADATA { label 'process_single' cache false - conda "bioconda::pixelator=0.17.1" + conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" diff --git a/modules/local/pixelator/list_options.nf b/modules/local/pixelator/list_options.nf index 86a03402..880be7cc 100644 --- a/modules/local/pixelator/list_options.nf +++ b/modules/local/pixelator/list_options.nf @@ -2,7 +2,7 @@ process PIXELATOR_LIST_OPTIONS { label 'process_single' - conda "bioconda::pixelator=0.17.1" + conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" diff --git a/modules/local/pixelator/single-cell/amplicon/main.nf b/modules/local/pixelator/single-cell/amplicon/main.nf index d7b5bcf4..87b5095b 100644 --- a/modules/local/pixelator/single-cell/amplicon/main.nf +++ b/modules/local/pixelator/single-cell/amplicon/main.nf @@ -3,7 +3,7 @@ process PIXELATOR_AMPLICON { label 'process_low' - conda "bioconda::pixelator=0.17.1" + conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" diff --git a/modules/local/pixelator/single-cell/analysis/main.nf b/modules/local/pixelator/single-cell/analysis/main.nf index ede72a1e..077fc1f4 100644 --- a/modules/local/pixelator/single-cell/analysis/main.nf +++ b/modules/local/pixelator/single-cell/analysis/main.nf @@ -2,7 +2,7 @@ process PIXELATOR_ANALYSIS { tag "$meta.id" label 'process_medium' - conda "bioconda::pixelator=0.17.1" + conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" diff --git a/modules/local/pixelator/single-cell/annotate/main.nf b/modules/local/pixelator/single-cell/annotate/main.nf index b66cf872..48e97194 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 { label 'process_high' - conda "bioconda::pixelator=0.17.1" + conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" diff --git a/modules/local/pixelator/single-cell/collapse/main.nf b/modules/local/pixelator/single-cell/collapse/main.nf index a34ecd15..0fbe600c 100644 --- a/modules/local/pixelator/single-cell/collapse/main.nf +++ b/modules/local/pixelator/single-cell/collapse/main.nf @@ -2,7 +2,7 @@ process PIXELATOR_COLLAPSE { tag "$meta.id" label 'process_medium' - conda "bioconda::pixelator=0.17.1" + conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" diff --git a/modules/local/pixelator/single-cell/demux/main.nf b/modules/local/pixelator/single-cell/demux/main.nf index d1bda817..29e9e769 100644 --- a/modules/local/pixelator/single-cell/demux/main.nf +++ b/modules/local/pixelator/single-cell/demux/main.nf @@ -3,7 +3,7 @@ process PIXELATOR_DEMUX { label 'process_medium' - conda "bioconda::pixelator=0.17.1" + conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" diff --git a/modules/local/pixelator/single-cell/graph/main.nf b/modules/local/pixelator/single-cell/graph/main.nf index c4e4bb61..b26b8e4d 100644 --- a/modules/local/pixelator/single-cell/graph/main.nf +++ b/modules/local/pixelator/single-cell/graph/main.nf @@ -3,7 +3,7 @@ process PIXELATOR_GRAPH { label 'process_high' - conda "bioconda::pixelator=0.17.1" + conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" diff --git a/modules/local/pixelator/single-cell/layout/main.nf b/modules/local/pixelator/single-cell/layout/main.nf index 8173aab0..ad2524ba 100644 --- a/modules/local/pixelator/single-cell/layout/main.nf +++ b/modules/local/pixelator/single-cell/layout/main.nf @@ -3,7 +3,7 @@ process PIXELATOR_LAYOUT { label 'process_medium' - conda "bioconda::pixelator=0.17.1" + conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" diff --git a/modules/local/pixelator/single-cell/qc/main.nf b/modules/local/pixelator/single-cell/qc/main.nf index f2082c81..2184363f 100644 --- a/modules/local/pixelator/single-cell/qc/main.nf +++ b/modules/local/pixelator/single-cell/qc/main.nf @@ -3,7 +3,7 @@ process PIXELATOR_QC { label 'process_medium' - conda "bioconda::pixelator=0.17.1" + conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" diff --git a/modules/local/pixelator/single-cell/report/main.nf b/modules/local/pixelator/single-cell/report/main.nf index 1a6bbbae..11d73c21 100644 --- a/modules/local/pixelator/single-cell/report/main.nf +++ b/modules/local/pixelator/single-cell/report/main.nf @@ -3,7 +3,7 @@ process PIXELATOR_REPORT { label 'process_low' - conda "bioconda::pixelator=0.17.1" + conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" From 934e555828d2dda60534ea770e7e487eca0413ec Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 30 Jul 2024 14:32:59 +0200 Subject: [PATCH 236/260] Bump version and update CHANGELOG --- CHANGELOG.md | 2 +- nextflow.config | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 12c487cc..846c82be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ 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). -## [[1.3.1](https://github.com/nf-core/pixelator/releases/tag/1.3.0)] - 2024-07-17 +## [[1.3.1](https://github.com/nf-core/pixelator/releases/tag/1.3.1)] - 2024-07-30 ### Enhancements & fixes diff --git a/nextflow.config b/nextflow.config index 63de05a6..62852458 100644 --- a/nextflow.config +++ b/nextflow.config @@ -313,7 +313,7 @@ manifest { description = """Pipeline for analysis of Molecular Pixelation assays""" mainScript = 'main.nf' nextflowVersion = '!>=23.04.0' - version = '1.3.0' + version = '1.3.1' doi = '10.1101/2023.06.05.543770' } From 5e1851199e40a78645b7474632e103c4eb41a4c9 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 31 Jul 2024 11:53:54 +0200 Subject: [PATCH 237/260] Update release date in CHANGELOG --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 846c82be..090e2ff1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ 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). -## [[1.3.1](https://github.com/nf-core/pixelator/releases/tag/1.3.1)] - 2024-07-30 +## [[1.3.1](https://github.com/nf-core/pixelator/releases/tag/1.3.1)] - 2024-07-31 ### Enhancements & fixes From f82e485812e1d7e695f2a0f61ef6363fda74ccc7 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 21 Oct 2024 13:56:57 +0200 Subject: [PATCH 238/260] Update cat/fastq module --- modules.json | 2 +- modules/nf-core/cat/fastq/environment.yml | 2 +- modules/nf-core/cat/fastq/main.nf | 14 +- modules/nf-core/cat/fastq/meta.yml | 43 ++-- modules/nf-core/cat/fastq/tests/main.nf.test | 138 ++++++++-- .../nf-core/cat/fastq/tests/main.nf.test.snap | 237 ++++++++++++++++-- 6 files changed, 378 insertions(+), 58 deletions(-) diff --git a/modules.json b/modules.json index c005251e..7c89a4f9 100644 --- a/modules.json +++ b/modules.json @@ -7,7 +7,7 @@ "nf-core": { "cat/fastq": { "branch": "master", - "git_sha": "666652151335353eef2fcd58880bcef5bc2928e1", + "git_sha": "a1abf90966a2a4016d3c3e41e228bfcbd4811ccc", "installed_by": ["modules"] } } diff --git a/modules/nf-core/cat/fastq/environment.yml b/modules/nf-core/cat/fastq/environment.yml index c7eb9bd1..71e04c3d 100644 --- a/modules/nf-core/cat/fastq/environment.yml +++ b/modules/nf-core/cat/fastq/environment.yml @@ -2,4 +2,4 @@ channels: - conda-forge - bioconda dependencies: - - conda-forge::coreutils=8.30 + - conda-forge::coreutils=9.5 diff --git a/modules/nf-core/cat/fastq/main.nf b/modules/nf-core/cat/fastq/main.nf index f132b2ad..4364a389 100644 --- a/modules/nf-core/cat/fastq/main.nf +++ b/modules/nf-core/cat/fastq/main.nf @@ -4,8 +4,8 @@ process CAT_FASTQ { conda "${moduleDir}/environment.yml" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/ubuntu:20.04' : - 'nf-core/ubuntu:20.04' }" + 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/c2/c262fc09eca59edb5a724080eeceb00fb06396f510aefb229c2d2c6897e63975/data' : + 'community.wave.seqera.io/library/coreutils:9.5--ae99c88a9b28c264' }" input: tuple val(meta), path(reads, stageAs: "input*/*") @@ -53,9 +53,9 @@ process CAT_FASTQ { 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) { + if (readList.size >= 1) { """ - touch ${prefix}.merged.fastq.gz + echo '' | gzip > ${prefix}.merged.fastq.gz cat <<-END_VERSIONS > versions.yml "${task.process}": @@ -64,10 +64,10 @@ process CAT_FASTQ { """ } } else { - if (readList.size > 2) { + if (readList.size >= 2) { """ - touch ${prefix}_1.merged.fastq.gz - touch ${prefix}_2.merged.fastq.gz + echo '' | gzip > ${prefix}_1.merged.fastq.gz + echo '' | gzip > ${prefix}_2.merged.fastq.gz cat <<-END_VERSIONS > versions.yml "${task.process}": diff --git a/modules/nf-core/cat/fastq/meta.yml b/modules/nf-core/cat/fastq/meta.yml index db4ac3c7..91ff2fb5 100644 --- a/modules/nf-core/cat/fastq/meta.yml +++ b/modules/nf-core/cat/fastq/meta.yml @@ -10,30 +10,33 @@ tools: 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"] + identifier: "" 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. + - - 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}" + - meta: + type: map + description: | + Groovy Map containing sample information + e.g. [ id:'test', single_end:false ] + - "*.merged.fastq.gz": + type: file + description: Merged fastq file + pattern: "*.{merged.fastq.gz}" - versions: - type: file - description: File containing software versions - pattern: "versions.yml" + - versions.yml: + type: file + description: File containing software versions + pattern: "versions.yml" authors: - "@joseespinosa" - "@drpatelh" diff --git a/modules/nf-core/cat/fastq/tests/main.nf.test b/modules/nf-core/cat/fastq/tests/main.nf.test index dab2e14c..f88a78b6 100644 --- a/modules/nf-core/cat/fastq/tests/main.nf.test +++ b/modules/nf-core/cat/fastq/tests/main.nf.test @@ -1,3 +1,5 @@ +// NOTE The version snaps may not be consistant +// https://github.com/nf-core/modules/pull/4087#issuecomment-1767948035 nextflow_process { name "Test Process CAT_FASTQ" @@ -11,9 +13,6 @@ nextflow_process { test("test_cat_fastq_single_end") { when { - params { - outdir = "$outputDir" - } process { """ input[0] = Channel.of([ @@ -36,9 +35,6 @@ nextflow_process { test("test_cat_fastq_paired_end") { when { - params { - outdir = "$outputDir" - } process { """ input[0] = Channel.of([ @@ -63,9 +59,6 @@ nextflow_process { test("test_cat_fastq_single_end_same_name") { when { - params { - outdir = "$outputDir" - } process { """ input[0] = Channel.of([ @@ -88,9 +81,6 @@ nextflow_process { test("test_cat_fastq_paired_end_same_name") { when { - params { - outdir = "$outputDir" - } process { """ input[0] = Channel.of([ @@ -115,9 +105,129 @@ nextflow_process { test("test_cat_fastq_single_end_single_file") { when { - params { - outdir = "$outputDir" + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:true ], // meta map + [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true)] + ]) + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("test_cat_fastq_single_end - stub") { + + options "-stub" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:true ], // meta map + [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true)] + ]) + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("test_cat_fastq_paired_end - stub") { + + options "-stub" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test2_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test2_2.fastq.gz', checkIfExists: true)] + ]) + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("test_cat_fastq_single_end_same_name - stub") { + + options "-stub" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:true ], // meta map + [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true)] + ]) + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("test_cat_fastq_paired_end_same_name - stub") { + + options "-stub" + + when { + process { + """ + input[0] = Channel.of([ + [ id:'test', single_end:false ], // meta map + [ file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_1.fastq.gz', checkIfExists: true), + file(params.modules_testdata_base_path + 'genomics/sarscov2/illumina/fastq/test_2.fastq.gz', checkIfExists: true)] + ]) + """ } + } + + then { + assertAll( + { assert process.success }, + { assert snapshot(process.out).match() } + ) + } + } + + test("test_cat_fastq_single_end_single_file - stub") { + + options "-stub" + + when { process { """ input[0] = Channel.of([ diff --git a/modules/nf-core/cat/fastq/tests/main.nf.test.snap b/modules/nf-core/cat/fastq/tests/main.nf.test.snap index 43dfe28f..f8689a1c 100644 --- a/modules/nf-core/cat/fastq/tests/main.nf.test.snap +++ b/modules/nf-core/cat/fastq/tests/main.nf.test.snap @@ -12,7 +12,7 @@ ] ], "1": [ - "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + "versions.yml:md5,6ef4fd28546a005865b9454bbedbf81a" ], "reads": [ [ @@ -24,11 +24,15 @@ ] ], "versions": [ - "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + "versions.yml:md5,6ef4fd28546a005865b9454bbedbf81a" ] } ], - "timestamp": "2024-01-17T17:30:39.816981" + "meta": { + "nf-test": "0.9.0", + "nextflow": "24.04.4" + }, + "timestamp": "2024-10-19T20:02:07.519211144" }, "test_cat_fastq_single_end_same_name": { "content": [ @@ -43,7 +47,7 @@ ] ], "1": [ - "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + "versions.yml:md5,6ef4fd28546a005865b9454bbedbf81a" ], "reads": [ [ @@ -55,11 +59,15 @@ ] ], "versions": [ - "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + "versions.yml:md5,6ef4fd28546a005865b9454bbedbf81a" ] } ], - "timestamp": "2024-01-17T17:32:35.229332" + "meta": { + "nf-test": "0.9.0", + "nextflow": "24.04.4" + }, + "timestamp": "2024-10-19T20:02:31.618628921" }, "test_cat_fastq_single_end_single_file": { "content": [ @@ -74,7 +82,7 @@ ] ], "1": [ - "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + "versions.yml:md5,6ef4fd28546a005865b9454bbedbf81a" ], "reads": [ [ @@ -86,11 +94,15 @@ ] ], "versions": [ - "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + "versions.yml:md5,6ef4fd28546a005865b9454bbedbf81a" ] } ], - "timestamp": "2024-01-17T17:34:00.058829" + "meta": { + "nf-test": "0.9.0", + "nextflow": "24.04.4" + }, + "timestamp": "2024-10-19T20:02:57.904149581" }, "test_cat_fastq_paired_end_same_name": { "content": [ @@ -108,7 +120,7 @@ ] ], "1": [ - "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + "versions.yml:md5,6ef4fd28546a005865b9454bbedbf81a" ], "reads": [ [ @@ -123,11 +135,126 @@ ] ], "versions": [ - "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + "versions.yml:md5,6ef4fd28546a005865b9454bbedbf81a" ] } ], - "timestamp": "2024-01-17T17:33:33.031555" + "meta": { + "nf-test": "0.9.0", + "nextflow": "24.04.4" + }, + "timestamp": "2024-10-19T20:02:44.577183829" + }, + "test_cat_fastq_single_end - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,68b329da9893e34099c7d8ad5cb9c940" + ] + ], + "1": [ + "versions.yml:md5,6ef4fd28546a005865b9454bbedbf81a" + ], + "reads": [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,68b329da9893e34099c7d8ad5cb9c940" + ] + ], + "versions": [ + "versions.yml:md5,6ef4fd28546a005865b9454bbedbf81a" + ] + } + ], + "meta": { + "nf-test": "0.9.0", + "nextflow": "24.04.4" + }, + "timestamp": "2024-10-19T20:03:10.603734777" + }, + "test_cat_fastq_paired_end_same_name - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test_1.merged.fastq.gz:md5,68b329da9893e34099c7d8ad5cb9c940", + "test_2.merged.fastq.gz:md5,68b329da9893e34099c7d8ad5cb9c940" + ] + ] + ], + "1": [ + "versions.yml:md5,6ef4fd28546a005865b9454bbedbf81a" + ], + "reads": [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test_1.merged.fastq.gz:md5,68b329da9893e34099c7d8ad5cb9c940", + "test_2.merged.fastq.gz:md5,68b329da9893e34099c7d8ad5cb9c940" + ] + ] + ], + "versions": [ + "versions.yml:md5,6ef4fd28546a005865b9454bbedbf81a" + ] + } + ], + "meta": { + "nf-test": "0.9.0", + "nextflow": "24.04.4" + }, + "timestamp": "2024-10-19T20:03:46.041808828" + }, + "test_cat_fastq_single_end_same_name - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,68b329da9893e34099c7d8ad5cb9c940" + ] + ], + "1": [ + "versions.yml:md5,6ef4fd28546a005865b9454bbedbf81a" + ], + "reads": [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,68b329da9893e34099c7d8ad5cb9c940" + ] + ], + "versions": [ + "versions.yml:md5,6ef4fd28546a005865b9454bbedbf81a" + ] + } + ], + "meta": { + "nf-test": "0.9.0", + "nextflow": "24.04.4" + }, + "timestamp": "2024-10-19T20:03:34.13865402" }, "test_cat_fastq_paired_end": { "content": [ @@ -145,7 +272,7 @@ ] ], "1": [ - "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + "versions.yml:md5,6ef4fd28546a005865b9454bbedbf81a" ], "reads": [ [ @@ -160,10 +287,90 @@ ] ], "versions": [ - "versions.yml:md5,d42d6e24d67004608495883e00bd501b" + "versions.yml:md5,6ef4fd28546a005865b9454bbedbf81a" + ] + } + ], + "meta": { + "nf-test": "0.9.0", + "nextflow": "24.04.4" + }, + "timestamp": "2024-10-19T20:02:19.64383573" + }, + "test_cat_fastq_paired_end - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test_1.merged.fastq.gz:md5,68b329da9893e34099c7d8ad5cb9c940", + "test_2.merged.fastq.gz:md5,68b329da9893e34099c7d8ad5cb9c940" + ] + ] + ], + "1": [ + "versions.yml:md5,6ef4fd28546a005865b9454bbedbf81a" + ], + "reads": [ + [ + { + "id": "test", + "single_end": false + }, + [ + "test_1.merged.fastq.gz:md5,68b329da9893e34099c7d8ad5cb9c940", + "test_2.merged.fastq.gz:md5,68b329da9893e34099c7d8ad5cb9c940" + ] + ] + ], + "versions": [ + "versions.yml:md5,6ef4fd28546a005865b9454bbedbf81a" + ] + } + ], + "meta": { + "nf-test": "0.9.0", + "nextflow": "24.04.4" + }, + "timestamp": "2024-10-19T20:03:22.597246066" + }, + "test_cat_fastq_single_end_single_file - stub": { + "content": [ + { + "0": [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,68b329da9893e34099c7d8ad5cb9c940" + ] + ], + "1": [ + "versions.yml:md5,6ef4fd28546a005865b9454bbedbf81a" + ], + "reads": [ + [ + { + "id": "test", + "single_end": true + }, + "test.merged.fastq.gz:md5,68b329da9893e34099c7d8ad5cb9c940" + ] + ], + "versions": [ + "versions.yml:md5,6ef4fd28546a005865b9454bbedbf81a" ] } ], - "timestamp": "2024-01-17T17:32:02.270935" + "meta": { + "nf-test": "0.9.0", + "nextflow": "24.04.4" + }, + "timestamp": "2024-10-19T20:03:58.44849001" } } \ No newline at end of file From 769d9ab89cc7fbac9db51e1e45e550751d291e00 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 21 Oct 2024 13:57:30 +0200 Subject: [PATCH 239/260] Remove duplicated lines form old template --- nextflow.config | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/nextflow.config b/nextflow.config index 663b4e2f..20d752e6 100644 --- a/nextflow.config +++ b/nextflow.config @@ -119,21 +119,6 @@ params { // Load base.config by default for all pipelines includeConfig 'conf/base.config' -// Load nf-core custom profiles from different Institutions -try { - includeConfig "${params.custom_config_base}/nfcore_custom.config" -} catch (Exception e) { - System.err.println("WARNING: Could not load nf-core/config profiles: ${params.custom_config_base}/nfcore_custom.config") -} - -// Load nf-core/pixelator custom profiles from different institutions. -// Warning: Uncomment only if a pipeline-specific institutional config already exists on nf-core/configs! -// try { -// includeConfig "${params.custom_config_base}/pipeline/pixelator.config" -// } catch (Exception e) { -// System.err.println("WARNING: Could not load nf-core/config/pixelator profiles: ${params.custom_config_base}/pipeline/pixelator.config") -// } - def container_env_options = [ "MPLCONFIGDIR": '/tmp/.config/matplotlib', @@ -315,7 +300,7 @@ manifest { homePage = 'https://github.com/nf-core/pixelator' description = """Pipeline for analysis of Molecular Pixelation assays""" mainScript = 'main.nf' - nextflowVersion = '!>=23.04.2' + nextflowVersion = '!>=24.04.2' version = '1.4.0dev' doi = '10.1101/2023.06.05.543770' } From 6c9accaace163af57c06123048a9bf4056b96fc9 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 21 Oct 2024 13:57:36 +0200 Subject: [PATCH 240/260] Update logo --- assets/nf-core-pixelator_logo_light.png | Bin 80637 -> 79808 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/assets/nf-core-pixelator_logo_light.png b/assets/nf-core-pixelator_logo_light.png index 9b643424a35ba9d3c7dd653522220ea377485add..a3ccb46351ce4346149fc660d11551ea03893548 100644 GIT binary patch literal 79808 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

);ZzjCK~k0> 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 !_^*Q)(Gm_l3{IhD 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 literal 80637 zcmeEui93|*|NktQ%2p{POG2_GTh>fbF$vKkWvL`7S+eglWvLVyq^!vx5wgoR)sd*| zWoJr4wy}@>_kPs*ocb4j*Y~ijuVhOY*KysvQ|q$y*J-S zk72oMpYe0q?VAbaq#*9EMu$IdcoXp^so>_-$xz2=@*S~&`he~jv*V1qmOg7gB~|ku z4iN>q9{kdyZ|YpwtFN7@xuSZ(Q#H%X?NoYKwa=aXj}v2~+N64J89ds7jyopzTE&lL zTX=gIP6(U()S#j$694J$cS-q-%;cBHV%V6wo@O1Lq5Km$NZ4lj<2c zwqe?icigw3j2(aYEkjNIUZPcw=4bC$%q+7b?p1P^!$01``3%CuY^87 z;+7(ucG8n%c1WY$)*4{X-}BJ>bo;<}^v>;*r&@E0IG1l!Tp!}0zq857k7xHop;0Kp zp@Szq;zqjNA6dD3R!l5jDHM2N_C;`RXROn?GDR!4N3Rr{H;HXrSGA?_^#Q)^>x3(} z=<43q$8GY`M>pzW^wyWZeG~I-r(IWRLDQGU@i1q_WC4ffOiFfW-Ev8m^QVz}l&O0c zE-N-A&)rvx=OBv`{`)UCX`>qP&krcRHeYrA`@JZkb|WkP&!3Hh&M?CNd_(@9H(`JN z94XB0_vgv}e;Qn^msPPxe=j4ZZ3tyk36Qt*{xj$1vfpf zfBH3w+1b*|GaJs!(AUwvQtw>gauUf&pe#+DqG^ev2_0MidbaBfJliPBoT!80#N55W zHQE^UG1`B{W;y77G;^JZ?XkZ+q<%geg@+i7lHDn>$mVspb@xY%5NgE@d77ner^MdB z*1nVP_UY#-Y6+E<6!9i?+oS8$-pt;d3D=}sx*MwhbhNflGtKiBcGI#+HhX)dN@uIz zwmP&i0vm)y17C`|p#E^MEEm>zX-KMMI?WQQU^kBA;1tj@<~8`!k6U+iC}prxPmuhX zLf7-#M5camXj9p)7C>oq{&QD}yf0a%zRENK>b9gjroGHS>5cb)J>cAwuIbORgq`;p zTlA*RBIgOO7V`Q-GHmC7ec{waQgA@s=7!!EAVMzKXzyQ9fV>qPdH^Ma;^2%Idm@BQ zM2@WTS7c%30FP{ds&OGjve=0;e@c)!{5Qs~o)&OAOBL&2h3C z+(CYm`)5ePckWtirUj;j=#14_e|4Rpj0@3N8b)$~sm-A@5Q%3?LyG%gU8O9hH~ z8A^^977D%Xm^qkJdCK;0QQ7W6MLAC=Yv;Zj_Q%9qHa)tdCeXU@hbZ2C04`h6DoMfY zv>$5J0irT?7{pe5eUoP@Q5MB1cp$;gj9tSTbi-0$!c*+N++WJ^G8dF%Gu@Qtx9EAI z2(`*l<_b^pu(Eh_HU}9)^Sqh{~|U>-3rmZFc!^%_ZbeD=flc&OM@tp z?svck6BIj*j_#cr!gBffi`+ED7@O#-v_4>H7vHWV z3c>16#ozJ|(2YlFyr|FcUGLF?NR3^%$qz9+`SUEr7eQ9a!GKiqe+#+`-V#Fjq||3D zuPa`Kw_>$#ixM7R_;YDRW5ifEmiSdt!ZNxsaTTCTAA%!}l5>;)N+l%RS8lG9h?`u}UeiCe>ZJV(mp_czII|xVk zvx(}wP}Y$Fk9aihozyix{WmSOp47XVdsg*mH-)XkDIf2u38W?bXA$Ryrt>*3-i2J>&i0gGs^9OpXxyy zb;$Dde`29+TOBjUaNlGlPE#>%H#ySxFD|^Doz|3Htw#j7FH)4Oi2jQQL6_lt3e@h! z;-jn#>Vt2(O^0&+I$A1zY-%#m4D8{BHen}$>&;&~?*{!7K>Ifhe2yCvA$b2SL1}lU z({oPqQlxA)AWNU^FWz48ypb5aN(bS)vx(gi)&DE~qmV#_TuCdecp<(c+=Lj#zbSIu z=jbC8UCGCoe~=3Y{wwd}3-U`_^sxd?#V3h|Q`Y~buZqWq)UxqEr>^$$Bg-E8YuQ_F zYK~_TTanck|ME4q4P(s%{SGXCa`$$S74Q7TKb9B0Z|aM{i4|MzCWzW%{!$M1kD%xx zvtrh&lQ9AzK{24tOKN zR6~TB^!H)X;V`T0Vu7OiIbuoJ|0TP&(?_d3*5_+__{X{WkEQb_qPxUhLN()s)QA+{rg1e{M9F;f{?oKbylL#% zlmcyglD2M#c-pMz{0~qK2Z5uT2{GsY;J^*a$$Md_6%}MLAVeK`JP!a*XBz*tdPtL; zzwVERC;R8AxedY#jxzL@5Fm_3h>)-gWuahRXPN$xg{qO{yALFufV##scg1s~pVp4B zY%qt0XYzOdxnAEL>ZT;^=?mNGNWd|gcY?=B0D0)oO>aCoc~68^)}XpDJ#jh1*LrXx zdA9xHt|c=$`WA8|_Rrb_M=hKw1^jm*3RO zU=4){s^|a1N!vG3cPA#OeOd#9&CDS24tGG!P~zmyEIxL%5Yc|pe^5fHncI0@5=3go z;v?^}j^L|`I#Y3bcs{N`ROTjL<9~j9*O_$QY^LW)&eKCOpH6#w!}%9(wC&6uaQ*^> zuFxa@6@HAJA@k`#j>cea`WSFy<`byT{f0Hq#;4QoRe<8(z|YRllsTySjd|6A!wwxo zO=Wg)*tHGsb0XPz_}*;BgSDlMd!p_-F_kHTNPVBQU+vw9Pz&}z*@Q$-wJA5sU$LpM z7h@gy2`|&U`LX=W(gHLI2Z`1UcAD{%#aUyT3Dtowsv{Jgp!X~M`{}jOaS`M`oeGSV zxeGkt69zF+?wrmt5g~7`7vSMflk4ccg(F9XjOB#X7krrcSx-t#tu`cDDoPf7dEfL4 z!EKG#)Vrd^u0T16L)g{`w7O{>VygOaH_E*fYowwD<0|&c+dVD6ODav%X|*e{SVq(P zrx_N>#+}?d<9@L!H~r*2Ll(hJYC4H~)(ISFdio!6Wv~p&(UN?#{#H%T?QiB1do3{w z*JxjWWhD*O_s=XT>z|oLqiM8AShW5u26nJ)v$8Nd{t&y6Wb0=sFMb4gz z5S6O@wK=6*a%HJx3bc?(RL7(Ovl%dvK};6ACw7!?8t5XUP_oTE#pbg4I%yo zzYBGWLkY~0TET;F^{F_m;0O7Z9OmC=LbqMnK&V}haPy@}mWkehsP6~wDv|nrV~y2| zllRiz_o!vNE1w&^jHtd|+52Gu^#)LVE=0GjGR(3C*~)l5R2_BO6n~Lsd|6|&irgC5 zI3OtEcdqdLFv6q52ukp&_j}FkrCEzwCt}~G{w@j150o{a7p7}2XLPSyd!ypdH@3qY z8)>JgQ>2SDmEU{(h`c)?Kq^4g`X*=vdjOZ8sBHiOQ#YkrGd9r`X_llRs?%?#NqgH4 zqI?(R=>7l(e7CE({Wwg)BRPRvc|l4Aw}OO!TTYf(n6sC$7yHw8|K24P%u$gpzP1aT z4WZ_Mv|9=fRgUT~r+i=m(-%b*9(wtFV7Vtu0JWvb>gwyxK%q{yuKbX`AIz&2n~GD7 z(&rWQn{<)zl-VfD#ELv)?vIvc=l@+TjbW;1J{$vER1BCntSY*=CRKy=ff23LK!3be5YuWtf>8^w6 zyt2#|TR=^Fdh7~(F=jC+1Wnpj-*6T!GD@V}r7gWMMKUukA^Rs*!~|YNQMU~>UMn=q zw)l8GC3x*AMa4x5hKjp;4ke5|P= zh9X2JQ{=#yL#UlZn}E8Vzsyzt$&?muuvsVe#q4K9Y>)aET}SovGS2^`8l=5#5wceq zc_d}OwETsOv+`TVWcVEK@d~GcEgt8+mZTh=z;rbT=)r6EkG>F)kE2X*yQ*08#U?r21@$V}!yT`uX=J6ZJ z>9s8PY)@(BZ)%r2w^L$X8uyUrvV`{I|mNyUav)P$WM4jW= z-!FdmY5Oep=%$UMlT#laE`9&eDO8zT@{@Ri+dFi9PWbn#&ki73lOPTe2@S;VlK^=+ z(VCxDLMKnk!Ut<66LUiHT{S8iqleKMs_p{`hyycnDQ4O4sH3EaN3x^*Rdr$sZ;5Yo zZ@H?ebCfK+OUy1_dUY-&v0}ERy}fQzNXg?jxo`AeEM{tWWb;VBV0?+PiSs#P^L(T1 z4-Kx1+v#$&r6Vb`eGL~x1Cf|caSQ17L8^`Jk(*Z?5$+(zur)~MUENQA4$jw6!qGVI zbI&HV+lryJGe0-eE}hr+>Js+#-dy_FPhp>$fI+E8bD&=3a&Sf`Pp@OkNTI-@#(kTb z3m^PC%K9eN$3IvnYkmE;eaz=#n(uMj%Z;Yu)yhPFtY-7GmZcKO{(HdxCTi61XB;X~ ztbIJ=_5V_R)X^#x7C+8o!F5f+_-ln7t9)t!ihJM;U#Y&NMf4oT0QZoYU1>-EwIVZ5 zdQtX*`Jzc{W>nC_hz+wl*To9d=A|R{BNsS z#hvL7{eE<_Eh*7wkE9gtci|C7O6+|MznJ?YD)NOdWtf2jB zr3$EHBoM!)hA9z`5j>U~Bh~t}Zsg0*hpgqArVrV&Io6%#ZJ)j;`nnyT*lrT%H0g_r zu)nyp#p43FKLaSD1R?b(+a0h5iE2pf46Kv?`men27|U6(`Y0j6;1`IK1c1?YwlxDi zR4!6ARwqDTu_Cq5uJpn}Q^r2&l7&9jRwh``Wl?I(ucO_h*LmiC z{1CQ6@NlEjvYCI4b!)tDgRAoLjWhG|BO7V)RCnN(xmIwv;#w(dF~ux9fu&5$h2s}c zO6XXy9YUa3gpLP7Zk^0BNrMo`64F$2sx3)G|DJ%87D%`Hv7NQ3e#<;jU-3gQt^1$at-TmG_0LZoM`h;Bob-m98dE+{Oi+n3 ztr@srj-9p>h;nfQ#{~5zC0Tw^KoB`D`Hq>Eg7|gwPp3<_ss-eq>GyK=ga80%woc=Y z-HYZ}mCnBYU`wYj>2t68>xvfj^Id-0l$NyRYWw@u=qG=SG8#bjf@Y-!!6r^?n+z9F z?}95_+k?XI$?JGyaHDSS$XWsKT3o9I-M&Q4X)d2XoyDjOOq5!l(H9?-PehZ0+mBa&p8c8}fiY&m@~=9|56iK*SQp8n3f>*{!CfR;o^Ire`QW zar+O=#vZRPx%#o$>v(1KHc(@Mz!E4ocerbsN)dkQl~|PX&>uCZ{$4!Jb1ecx)8iY2 zDZb!@O)x^tb+~Uiw)woR0^V(=Cf$AcrVnM7FDxXznReK-Xx?zVeQFFsg=fIh(d~$* z@eiP%Meq+sv+URJ(>t=uW#v;-I>^~N||;g z4*r-mPZr3F)gGvQAVI^!f!mP-t0QasbxH02byU&&b^zQJ+2y!}y?qT=GR7RAHpg-g z@X|hjUaLV-x)ee5{Op8dh3{B?=T#9j>m=;-&|3h&4vr#U_LNzOQz;plku7t{yX<^*lZ(Z5-6a z<#7tq1^W*O)@K7P=A_bqI^o}Me)L54y$1UZ0A7`nANp$Ew-&OMvoOyxsYmjmIz;DZ ztDLoPe~3Da=~tZqiVpcw2D9O_om=RyUBnV7a!AsX4>_tUGd=PTjnm9gMDk;d&pt4~ zq#NaY=y7I2ezlNDr!>?ml7xFADU7$X5RLjjvn|`bwXdJbiX`B%5CcMJOEG8Y(Fkx( zu5VTSUY{r96nCwi?nm6eMPEvs=VnI_u=OkCW5>ha9WAl&Wuk{AIqP!$D2GEKj97c4 z4w$jV0mYpu7x~e$tYB&5w&2bo@Sg#kOG68xpEj6M);qJ&IblO21nIbiWTrX0{%80) zW9(gGK$Z*f-`8+xR~nUSL?N64%o7jf5bp0@%i66*g_E$Cjnr7f>bN7#4 zpBmd^2Hpj*DHy9jwIXhCr03i^y0)b466F2cJnTRp)bK8-bOe%c9;XNSjX5;Z&9WP!6sRcX&n?J*n6(TuBu8;wm~)ed(9msKa#y10dz*YHf;1{EB{A$P=&qYTaPfUJGr zv+dVUUOh*W#aK$WX83Pd!qT;N@U{GgCB+q}q=_A8M<5SMMFy~Hm@=vwiUa-cZ-L@t zA;&V&!i7)n){}7}G+pUdcOl9(?%KBMH2}eD(!qzm+*eOtRfe#h2RkP!!TU?UCGZJH z3Qt#Ba{55DlnxoP=IxQF9~1(8;YRt-kVh$~H4Y4fCSmW9?o+#=*5M8NythbidDc>a zi(9|Myf^>m?=0?}b~>*PRZkoAQ9bsBo4U)o^-s*Q_b~Txs|81%u)%ObgR52q!HDjX z6N4y{?<2~{)Q64Y5aM^j!rO_?#8~Z1zpSX~y$0_Y0kuOyg;ph6ef{m!8c;ITStwUg zG!R{TbsuUEOe*_bLWvbj8p%fK*Ea}IjM87=#+a8hUATV{$NuR9l9l;G?ExRaA&ds- z*-Y97dW`VCRDnQX14?1d?vKq?`yELobQ`Z=usrN8F$x zjYNB3(k)OgSBBJWJEGKsI5z6Cde|i!mkPT)2n33vAnFzh`kfJj8=fZI#P6i*#1C7 zTw0#7W7si)U!~xtbx`++CoTp*L>~-nIUh)2#Zm@`Oc+V55Smrl0CSjTA%m0F4 zq?D^tOJo*y z8T#yQ2dVN39<|@KugRp7_oC6VY+&aO_JtjJzjz>}4P`JBDaV-vMHj>qu=ZbnQ_l$} zSmT+oy#`tZDX?~7%9O#5tGR#^`SnouBPtFd0^+Xi3`msv@9{XKcHYx$SAaPAn0&JApT!FRLqGilW52->f|m0cK>ptlo}JscXOz^4e`mG0yLyny;#lrf;QIfsTCxD{V@b@1L~qzpPE8N{*oViDLYQda!s zswOPf9YdpAjNvw3{Zw$pMN>uswfLs(drp=56)NJ6zQp|Ry(l0tt_~x6JXrt zAIqDfw#R@D(sy^=W0P+Z1FManFx&m~?A+f0Rr-Ag>?AUNC3RmrAxy#nBg!F)3a6$c zx-KgYc3*6iUZ9N&-vwxE%LerK;HJ`uFR0w-&m|jA9o)(u0Toe#m4xrbtIRdy&tDv9 z7LSdzPPtgdq{6Slr?Shpl)n7-k?f4L5N*gtud4VQ>MGgQ3@hXT1_g zlcb2rMB5)BTUkGNbr(5y-Z(stR5&l&8nUKTnuMZjsq0yL1X(jLRW#&Xj8@JaAGoSu zi7bWT!Sc+Ml|O@}^6>-Vxmkd)ObG%Yw79Wkr|)XA5{o-&qBI_eaULnT~4DUljXs_o@Olo~VvZ zi_jSIVyo$8OZsBR^T@wx?l?>i*_W0&rt>O4mc=L$6VZ5PzaJ4LwaEZe#P6qmh(|sO0Yhtkwv&?YdM{U?rk5{gl&hXpu{|JYFuC(~<`rxKgf)SXBr$c#0qkPKQ+h*;FP~u; z*@N+Fx0QTs-fwVGUg%gm&k0e>RRUGyl@LU%Jeb~5RX>OxdoUMJeP`-lqTUAuurqDZr&03Wzd=(+Fa0XX|AYIxi=F~3C0@fc+LH_m#+#jbdt z0Oe+>$w1?wGMlw->oKw@yCK-sA!9oawi>CEoEu`V*twXL^n|$_^M~^AO&GURl1AwV zxWw0AC6AMwiHjJin?bc%QPan6Kr&|;@z9@`tzLET?L$9&FWQ5*W^Q^vssEt%{V*{q zwyAGvdagaKxi1(&-Al!UQd|2l@J3NT0&;SgETU4fQe6t8rI@jit#l^H_p^! zjvq-ee51+gA?fHtQ}JwIsn}2^#5f7jCZ9^!u0v>A?4|o0$awJ61J=92vv016+n4It0M|(Gs@I!+5_)% zOJ&Y^^+%*}u8ek@L~&Re-R4$G-*@-vcG+v!=ReO!TRKlKd^%^y?{cL79n!UuK-@ZD zzWfWb?5v#gby=BRg2#}L|JcH|Myq|6r)Ab0=*DmN$?`rZ8Qpfqzq z^5%em3zakB#=^q;Ciax_d34P!S)pquufAxwkP9~-W+A+#kU+0C)5WPFx(PczUs=9+ z>3NL~QK&d!=RGJMi_q<8w$$fTr~JHZv=WUgYpH2WrOM{JJ@X2;3OMNTbpn^;Ame7! zzL9>(R8#razlm5jmV76Um1Rm;kJPd@SMGd#butMVl%Pp-WuLrff-w|+{KNS) zO{JrByY82w7GQ9;R>bZ)U~wK|8qs)s--Rqu!S!axq;INpGukPJ`;pVWf*z+J z`?MY0!55$Z-Pwd}D-l-3gSj8_i8=+e;}yFdGS3;p(VG2}#eE#sB>mxgC#EFgkHCu}I=-W{x;ygcnl`A_qBnbyEWh>A8dY{=bw`X^&KKUZMK zk--DMbIwwk#Va4@=Q=G;LM8gMWxU69ZTju=)!q?e zjR_2m97UKAQZxom9H0q{8eY`k7k~7nd-BU2~74k2KbdtBo?GR+ozWi@#rsEsEn+t5;Fa+<3iY zYSKD7vGC&jj6&7cSWe5rfRw;zIZrnp)=R!pL)KC(5S5f}-=|*c+8o;cqR77YWnb#3 zdhvzf**B-o6}?~(u1y*B_7;w|wcAxFLXmlmM=ro4QCj$rljV8awnKahVa$S%V)o*e z^r2p&;V4KT%S)Me&nVKyo%(A;OR)58Djr^x*lAZ#DX4aD z*V!+ri!Fe?DFE%>Ujq`&Rh-+&QnD)Z#j&TiJY5W|%RP<`_3D$T?qSUFm`{8#w7aUF z!gXfT;XF>6J(cu>860J^)7Wp=qk5^Kl2jw=F|Uz>ByG2;$Gh+DD<~Q-{(e`c(|*a- zS;6wv(3if-66jnrgwZ2Y zG@-MvDk>%BGiiH61}zvb7)UjJXAJdg4iDv=eJY~t9~1pJzfES9>H0F(XJ9h}kk3Pz0@~?aApCg3T}@67{=*w~#cwob z?$N!GXgu${w}xxN9|BiH=x_6|)Fqqinz-!`qU?3zc*qll9$Ls6h&Xm!W49x}j0Mw@ z1U+YZ?w>GiT+<;>&Gwk9t6a2L5c}c|*5;!}ew+$^vGAoY)XY_RP+AY4k1giijn>&~ zG54YmzklLA*Phi+*3o{5*VGE$7Fa5KZXtMp-*a$$%;)&TcP-=7>gXrU+subA6SjPc zb2{$ytPi_w z;(q=%sKZ${JZo7Ifz~$posrtMudbp}WlyP2l=DsFf$kAY*LTLRUxMz@D#qhl-sc2% z0bOvJ)ntuEzdZz1z1jc(woMF`nhsU{piXQr;B+Ok1vEN~f;lL5dLJ_D0kaKxiUrSX zbJ7|_^_@Sw@!Qq8I^2tU1dF}P&C`tj>5s2Ip<2zSUrEp$$)}8TB9@B0F&phh zP12fG2Q3XWXMc!0*mX^pj;eETL|HxJkUrFz;7cdry{0$eWYryOTDDQ;k`!FG(%PCi zZ-?z)CO&dK)M}P*I~AB%K9-8b`wXY5_fv%K0yJevD6< zS@vln57ef&!^nxKtsU3^7s(x!0TV)6FhnMfcBmd_9<^hp@1iw==4XP{n+_iEgiuc$ z?N5+z6xWO36a^!=Kn@;Y8@- zyojt_r2XU3V;`NH=1eOxC%Te3%BN>>TgFlG-T`%a+pp8)X_L3>`o&+E`OSYgE$B2~ z`ys3Rr+{+zf-Y?$;IYNfRBaRSa5BGG>IfLVH7#BAaN-F}wb9BFw@R8(y7^|c_lPBA z)O@+c%olI7PqJVR3TqMocbdE&taNcw^6oON1k0#%lJA014q~1+4|hbwl6cTUHB)mR zpMli~5M8MeyxE^Ie3_gOsiXyS$X!XfsKH#u!nhXq^bxKSE!bgK;5_vxf<@e1YSD^T zM@pXVa{xrV2GQhfDm3gdOKL8m)GVAJBy$y|mCBI@C@&QK7%hx<;=t|Rq9VWH^*rW& z*#g^ELq1;44&G+~O z(E;*AZJ@0l0=XYu!FEx;h2h8UM&;51pz>V0#p6n{@pj!IiFnjBp1Nx0-Z;F%%s@FV z?6CK?YZELmkt15WxlQr?tS})7Yy=xOs{Bbis!*|0@C3?hqR3dYQ-2OcK z4;28Ch9jWZdxI7?0JYkg1i-)@2 zDi34NL*Gav#O2dPq!+(8hEtWBZkD%%{cjxY6ash8_|*;^z@x`hpXJtTfv)?pb#3+7 zpVO&y;g(ykHfQm8h=~AW*(ly&<5<2+jgnAhcVn;5Kzg-dBGBmoqr0gstwbDF0bZ?( zO)#CqFGu=%DM=Q{Kr2k2%0^c^KSjIP^X2u46Iosl5flFb8q5W>#*$cS+Ag|BUhCpV z`6NMRs0!^}@(~E<#@G1>F;19CB=PI9GW)amFwtKN%becEKsqe~YVSpLD`kb4ePSt* zKSLBE2}7&ViL5>$KKPPH?l@?V0VD)*Jhg+Qgo}8T#=DGkb<;VkgE=<7k0vd`$&wyU zrP_SC{y4$3h@)7U-gzM*LudMAgYn#HV$+JBq31^2Xiw|-V<8C zw1uJQ7?xv8sRH0$=OIEjIB!Z*+2F8^{t{Mz*@xNyp70j~CKc^4W5f>y>R@dx+&bDV zYBIE!UL>o11>_!okh#le^|pcOQtaboRvh=!Q4uxE-h?xf_Kd{N#0C}bQ$v0*?BQv= z%HHJ)7Wo!P^S$;+9J+zgfVehjcMODS&IG=D(%r(JiC~i6ppo?`JT9S@aqnrwY}Gjk z&_w_nL~R=&op^roo-C^8o}q@s0Q|&EcY@)Ryem&LQ7fe<@A+;zmeH+AI zQ@V>HGK*!~JWOf3`D8cIfio+M4F?q~;9W6#91I_pom2;1Uo00&PFF zlVlifSGlg=m8t-Qelbe)@Vacy>anlJKO*TI-(m6P=Eh+5g8W=wg%g3XtKL5ztuB7x z3NyXH@L4PmQ=(1HSLAzRKuo{ecCEi#2ZtvQM6ISn2clMUUNP^sQq`95<4R7*L{ z>>E+sFmucbx10uo>z=-xc@8j?3i+Od%f|aDX$ocXZ}u)FP(JRku6d1p6@N{)_Gj){G3@yQ9}gl8)2F8zTld58Gs8?+*{^^K=kjgaA>(2L7Xy1)QHvGmhHe z>91n-WM6&*$9%1lpVG$Goq2a%fubBGX{R={#Z2!a7>3NG9d`G6~Y49oMbgVHe`{>=Q~ zS?Zz8(g{%y6sCQ+YcdiIH^d+23&Err zR(-fzA8om8#gK4X+dmTkvAtCa401au8*2$vuc9Ggb{c5b1lNIi9V^nSEUhrmBpzz8#zbNl29{|^PV?HA1sC>%+?^T020RsErFzQYD;HGF&z^!2nLCXNp( z;AchgbCgU~k4>#Z502%%!aE)Tx%uO*6Oo2*A$T`S(5DsAuvWGk%~XBrt?HDKW~Kdk zW87pV10K#rvx$=~3%!DJ0(M+Ax4!jPE`I;Xm8I&_-sBxro5Q-w)MXu4t62_9tzz7F zJl2H93j|Gy>!BI#U1Guhg$V}TUV`Pw(62v%=@3LuWzm`}FlE8rZxDEie9QktPV>=n z%m^lyW>EX)o+S=mCWe|UGoDe2Y~yGzh8S_H3oAG|_{qoYuCH&fYql@g=$Q?O<0 zTz=s-Irv8yu@t?E`ZzZ5$(*GN@Q+!lbfg9hDg$OILiXs5X5a+fXpiU46i!cGNVG{Z zG00w+#mBpRlsmCWJanO}^yM~>H}JTLL49T?zuLzH5b|ko>L+MM#7aww9MmkDC?4|k zYB&VB1z7LLM;Bu$inXZC%)c_Ri?%5#Z+8(4B1ya4mYdi~gUM~(TV(E|aM@4Bh1rNZDgc z)EqR`EBVZFUn6d8JFWMK|OKJ6I#K?Q@df%VG|p*TjnHfK~QjZDRr+YR+6p7VVp zCcY)Q*QRaY=i5N){&%i{tvzsNJ(4%pftCMotRoDePThanP0UiiHoIsH`Aiq~%X%O8#EHeIeoT_hv=awRd+eVx5!YOaxe_I?o}TEGmfj2N-LkLmn=i`#m1W13GA6{ z(1z#1SaJwxou%AzpLl6X8>LEsp~l0UQ1um&(X(L0vUJh&oq~I29jGUe?Heh4&*o5g zp;-c?w+9`{y5+F46`A**3J9rueU^KV~X}stP({&n9r`IJ~GsDn)yn znYg#lj-Jzr(5>Z6y!1fv4iXebLsbvv3IIA;=(g0}6PuAlF5Jg3b6x!5v*-C3TEJpH zm}jkmST~tui29&q)A*049()Qzt4Dz3G$PO1m_y79H*u&bhI3fZ$AumooA!ixbtENEGVVEk4mN;N#18$##)-R&knk(j>EqHRF@rnI7hhDVVRMlt5 z{QVC2R37a9K{R92aDpfQ*=h-N9HB{*d0a=EWDBqNYQ8#Ttcd{89Rp?yIE*Nv98B;M zErg1O0H<$+IHKia@6hG0}o~!hxMvFc7e_>jxba6w1bcDf#czT`E^uZ7+iSEL2gQp zv=#!+4M1RVO$#>!u5i%_b~%%5d`?L11MxFyyN=V-H2(d*jJvzfwrt2Z5h(n5u3qKx zf<3hfUCkFz3A~%DX%IdFxVg-$f@Sy&N;iqZWQr}hJQKPe0A=JzOQfjHK!*FcZOteV zJUviCR}0AxyG!*p8x^}>384?C!gN-FmQ6z)5|FcK zD&BS0KioC8wHfE1);nDO_1W8siDD6x?vM%iI)xBwZ!pIuEl}q>my~`~GKKEc&vaT! z5+~fc!9>qTptfZ8EUgDtwfbR5)PbR{nV||QGFFoBopRBPsRt*$p8xQ?@$1hPTp5rE z;u1DP7rzdf(fDU!&M1EHrB0Tl(3#tXqaUo(c6tmr>G&+8n{oGs6$?#i^>9F$t5+LE zft%_e3+UGh(bBqTrLDER-cA7sU?41jjqVtJ!b*0HPt&3lK!9f}Bcc z&eGhHw0B3FjVhJE01zu64<1tiBUh9fMTi4zMDaF3S*YrjstdtB?%B~HG;OGy-&f$Y z@T@fYewH!^%CZPIH~XF#rI!m8`w#LrQD6$iwke)X1JMKH3P^l~sW^1?qF%a7cU33x z6qsPhmcFa9w$h+C=0uZ#x}+{OCJ(E@jeMEV>1HY0i=g)JE2}YAL}DkW8s2hh&fM0@ z_jFIRgu>=bMCkGtiIlB3zliXmu(pPQBs23G1Z8J zdt99w9BDnrVgc7nD4w7hOdiTK7t6ru@1O?Aw$JdGT*ZBC{$#jvBUn)eqt#ybM!*=#APwtEk_E! zWHb+DdVOwe#{XRLzWm&A%0J<3#do4uVCg-{w|TAl>y{u9%6hhwFTdhoqYC$$=iuXe@HKaSgyDdFEj77O@*b%$02BI+z;g(+Au$*pkfG;*G))FKo-G5aLr2OT>B#NH{%VNqIOvT=@!8Eokv%tR zos_>A$jDkkQWo~JY;i~l0J{TNRf}9XpWKO&gnyHFLG+M_S@HyBE_QkqwyR6w7ph5w zt!07PNzTU}2vC)%u2egkbOcob-#)9gc4643y#4B`B0}a=pizR|Vd1mv@u!kC2K*Re zbP~Wpe>F0}WE+q`T+DhtA(kGjymUM9KYS3viZ~jAt_7?tXv)3@e}&_Jfs2S zp4}8oOhxMT;_R7njo)lbX@$6^6NX_ZOLW3|f@dyVMigv%)?&X7k_|=GrtoFBh@j1< zJj4~`&!tw)R7(0z=ot3uu`rx;U%61Ka`(bTgR(RTxA0J-0~Oxce|RCFEMn{2SQ<6D zsShTpygyW!Nyw~pJrd*@{V5OV#xj;Dc#Smamlm(#4@LdCtH?cqgDuf`VToevDlw`J z7+WMB_+My=)Uj|L7YC)SQqew=z`uF;!a3uFV`v*}B0F-xZ7_9#>4BkA#Z*bEfL8GP zoMDm!wioU9wyr;6J0x#sZ)Ugr-dP*tfs+PKCF!fK(qEE1=~(53oeE#uvm z)Hn574$iN_C`RCEBpm9HFL$tY6f~SXUqR_S3*(9J>{v8)ZdlHoqmHwvZiU;~4m(YU zng-|Tl~_Q)*P{3jg1VE$QKwsK4h9-2Pzy_fN7w`o#kj!0I@W)aS~c&h0E!PIe52tC zGI~g=gq&9yC5yAbL>`H^Vk<|n;!P(}IbbMtELObr=bU3m9Ll>>!;>Q_ux9dUhD^D z&qmj=#fxAIv5B|@7JiugOKgCg4LG$&pPEM7j)a?65(;x}XB8 z;m&}Slh+`4J0%)cxqBiUPzJ#@34i#u51Bd+n!D)X1k04_6zV5Y&dXhFb`U9gOHV_U z?)LYshPWC2V6;2S*h9h4bivF#nEQe0?yjUggbvGkxTPEXo)khFCvWkz$Jmc}b}p_U zm-CC}%A4+>g2(z*$P~(r_j@ZI#CgJyqLsY@eGe@WPAjApJQU?8v@AsThg@Od2W-EHKjgy<;C<@~fa!9EHMC zv>D@ap#5bS#)k&gcmKH2l&HwG=2o?jTEHGJni8zOi-so_kVFFInd4ZOtZ4ilAplwH zJuV>*0Vd7KhX4rnBx&EfQv~zX5{b}+@}BdiEsmIt&BIOi&fH4nxQ7Pz`e7fuHD@Fq zNihlNP5#sT_+vDYqo>z-pzSrahU$PBXp0?3XJF#y>?W0#M=z3bLwVn00DWM}M(9xP zzJs?u?=0z^F{i0h-GTfAeq%6pzOQEa#fLeU5-2YaPw0Nx-py5v%L(#X_Un>GO7a?T ze^Bl-riWz-T00ezE|1Eg?sAUgFT1sG4r_?9V9w--y=wP@DFIa`>&P*hI4bHdWcF@p z{9a)7DUgA8PQaqD%tw9>{=C?}w&~Hk$G6RWYbQ1vlXIt#i?Alzxb|334se)>+VVi2BzYaLI2l=YInnS96BbfN^buoIwkJp+?9#lrY9{ZTDVfUp7V(dAqb%943S ze7_&hRlZ|hJs+C-B2X~;xQ)(BPRDSvglCnK;GolA^ z!NdTsvwO+Ih_2Pz(h#B!*bU5S98rGp6-Ib#)MIw(Kr=bR$ z4_UC!j+$}0v?|;JAsqD$zSMU(A>l+@lrqdbOH_iM)JQQZ-uk>A2!<5J&hiD66$&w{ z>z8a``hA#3;j zXv)3o=qo+V+BjZ?B2+f2CxW|HUP@-A44H0VF56ygNYm~6Q2~kp!yB?21P=LjLZc>| zP2eIw1)pZG8B`Xps>yVL=RT0^zq3jtl4yT1<9J06X_0s$Ed;I=5 z@ApH8`*q*fcwXbauIr@(rXD$|aN8lsx&}foMuP9_9pHR!a4*k6KKnMsm6S?v_dqLd zH&U&v(O8m=dI`34M3wRhZmxK>6{$AaB8o_O;I+4*F@s4a;D)(bsISG?;cLIu1aw+& zrVo}P9B>AnZwhn;3@MfxUu7APB=o|QSrH0B(%p&kz5SFEMgUaZ#yQr?C1F9V=h}7VrzJ0rUhtEk-%2qFekR{ zfHxFstX+vX3)JvC)PQZNTHWE|)YGH@eVc2HBx|IYzDeEc?2Es1E>{JSYJGx1)A7hu zUbwbsm!eoAUXwKM1O1fs{Vum5f`hB6waWlc?AZ1f5IRml06MLHWLM=We9CDL^>Qos zGxr7TPtz{G{ix*Yaj>Dkm;-da7Tk$JPHWisGuRpxK0>4)0u1q6bp1B5k&h;-_CaGA zF=T5RY+_q<{^F`KI0-+!DD?1jj*)}VCoykMG3B@tufV%(F}9cASj$@O#oxO~1*MM( zn0O$u2J<>9o)!9KB#_k3ws!%g4c@a2K;k5bBa!Z@(ChDdSAsGg75_r4<&Bsw|HXW* z+3=}Y*qAv~m0M|^tc2MnNWnM_SqqBz-bYLKh?7vpiA^>Tm0j7ipw;OYm zU`jz+USgoQ`XlhYi0(0e-|<)54S*XyQb?oVX*`z(`}h-}N+*=Jf#WmW3GM|HxJiKHxcjd90x4ty@Kq=GbUo@`jH zsPc7zCXb$%v2Tw+(Iy>^9+EGgcWrRA1MsP6!@bx5$Q<1(0S0pZvce1*OqKnf+4DcX z!d$B__kwvvguwnV2Gz0=uRTIKpT0c~u`$J`1)`692k>OVT;B&~APM+pPB308TvRt7t#THdL6)% zqWZbBp4MAW50&xi*x;g zrY0HBZLC1r#lQ&=n7S`luyB|7Jo62dD>f#;5C>MI>FtYRiitM#io3C$;=+DD@O_6| z6&#WuA6*3$l7-ET!jm%y92!|tQIYc+dA<)@+3RZoaG8}u4w!)8%aY8MK$t+x)ek=V z`gQ?<>N13>sbF58aFL)(couFdr&UX7Wln!%+{Mb&?DHmZqAoX4kC*onKcLXY0lo_(mjx}wC{ zvL$n1Xvvqc?l~ni(4zA9}Lc9}ri2*m7 zHm4pPHdsJ?>8*j=D4{Rs^)(H9YBA5_l_l}DwP6Pq`7_Y6+5Yiu+ZAXLXQgzfxt(xv zz6x0p`jQ~X89bri+9k(W|Lg4icVKS8R5|a#s5HKi)2?s)gHJ7l8h&UGz_CK7)H8Ok z^E2h`e0N1*j@NdTA+&kLEOsCScrr|13w_Hm6(Aog3y0vphicvl=*-{H933}*|F?2~ zM_=i+S|=eqdNo*yS*SiLVtKyGQ)3g z+mLT%NVB>eq_)`Ct^<-l8uuE-x?bZ4(0&xq~bw>Zp@toqWUMW2q{o#FBm8(S#rTx~xnrq_DI*lj?Gz8=- z53A_kL({b4?D>}}{nnoRBF%pq?f~C7hEauV7~37%bjBfUPn0F-o*~_wMMy0&lFspe ze+*AxUr7bQ&V3H3IJ_x{Cy@dj~b$Hj1bY1GOzYdVC@S^UwS%?o?sj4DiR8 z45*sALEL1AOBNyS_>O5UM5*#aWR%+XhQghq!FW6VZi}GP1__^aMQN4D~p zuPogW<{V*CHTwm$LIIqM!8>td=_`WL3+O+5Ag};tlqw0R zfBlErzUXKEHBv9$wyonvB5+idR2ecAm-(D(I>FGaHO*IDuYDs?sbcny6Kbzp*%QAP z(fPBQy?*R1IU0(Y?nb% z=>h?$ogt>UCh>TfzYJx~j5&s2J}*H6kd?&g@)Z(|`H&$&2LaTcw%t02oQOB3J0X_V ztbaPC8JGFx^6K!)_He-waLPhN4#o(!@hUMMcsmW)slf~h)cJ0xse-=Dqasy(cR(b1 zHpJHG8XiTIpV`y!Ir-yye*tX=CO-9d*M?`BU4e1$I-Yo4zunMgQ;w+g^r>&BtzOW| zJIiW@WK_psV`2H@h5V&NYla?iG`aNFH)`S9c;T^2Cf+R@(kGgWHWN=h1{?Llthn1l z<;`Lp6^J=g2N(#t^Jo>COHt*@;8r{wOwAO2z8?n$80_k4Z8)+xNm^<2K+q?`FXfQ> zur(AkTc__SLro(P$xRJ!r+)|qOqp+>N58QgI=4g`8l&Xyeti7YO}0?0*NgSVWU))! z{_0rQii@d3OB%)=>=&uGi+;Nicg2e~7tD?id8E-5Q%9X8$=SaBv=U-@+tTIDv@!~s zsAMyGPfsU{Ci&K)hN1DHov!E77H1TNiS@D1f=2{bIhov^ zqemj-5hxD|Eq{EGGbXXaqNY#ySYqOqALm6kh795A0HN|9CHRt~!w&$l4}g=dcC-kh zc5go=D}T^;)JoD!F?v~PY_BQYot2?@nTY&!LhI^lFizqqE;to3<)^5f=UN^^`1W55 zac4mM?>H;-pw#Wa0)%MCOdOKZeOhWgaQI3eV|;29M@KiAQUG^0l2MG%B7fR;7|gHq1GXowsfaf& z3tDtt>iSE1IN+xs1Le^JX}smFcX~mToC(9=5~8%157z9DEf`f)_|?XsaZtK}BOm}P zU);Jd6!S&<8EsPYPzsbOs(0o%)Rt=8*$75M>?pL*%;lR-)6B>B>-6_iH~d0!>}*@t zSMzgpU$kR-INY6OWpq5#b!Jz7OEFWKif9AnVL1DYTTVAj+Qy4e#Q8ZY=EGULrE8cN z4JBD7`Gb|#*3wOb_|%eh$>^<^Yom`q1-$*i3xlTpGyIPysuVC-(vD^B*Dza^U3M1? zWZfl#o-N&Mr7HM73)!DdOwhjkZ7;O>?)K9DuRdI(Unf@XpQ9ZUsH>h1)v2uOqR)^L=Px%T)GDn`JN*z8Z))D}iC7%yZHn;)t_*+Y2qVzNv87rxC@ zJwk~%f0rmcZn|;~N4AsFWKxs$F^+O)YPj`6`{|J5iRRHHF+MKt>xnF$NW?KgcpS`@ z1|8TLH81Z}&&~+Yl%Ussk){o!DB>k*`isHrokB4p(*Z8Owu@euZVk7l0Qvj2$$tRs z{S-2O(=rl~rub6J-s@c?0LkI$gdvU#Lc}piYZQtp`1f-Jm$0pz%~&+s7WEJ7l_b#^@|HWbSH>(J9YQAlo+qFKxU+0YZ#J&m4Qt+50aj z9hkqKnv?y#%ivX*Pc}7^ccOe&`*?Cr*#m?4)Je%CE4%Q_d4n+#Mt2h*dOQz~jM}T> zc7xX^YgxsBTJai$ZpqC}rjx2vEblEFe5-IW_VUR@iIwJ)&GHxgp;^XCdsFcFB;C*v z5FR1tVWCte%+-6p-^~1iTfymB!{wF#0;Bk*OFu@N+vx?r$siHS{0|=rKLjqFbR}RJ z(l0^Gk!cBLI(MQ1Imdz0J?|&clX898Wy^03sM+Q@VIzVY?cxK`&s@?&wbOj#ZqXdG zV=s>-B#L!4Klk;SuHs^nJ?G(Mz>-*L=J{6F6Qw6TVqu@7$a!%xzG-K1@Ghp{mCJgc zCkIv1h_&2=+y=9WcRQ~@#1`uR7D8><{#z&reomJMYWt=L;70`5ah${p761m%cOC)V z)~76{xjl*&<6A`h6Lp0Elk-39&Tr4Gng$@4LIO@YLHE1!+=9M7Pcm8Hz9P2?27w8- zE>0vmm~Bl2J@ZdbL1_ySeK;04-O`=XZjPoYPzRi3Lh%bGcR$d~3UXvca?IN`hCMDx z1=AwAI>Qw3xZ&a>C#tRZ!b1t#X8j4~(+nyG!R7Zl9G?0GsPT!5a4}JDbPa@$JXX^A zkh}MI896~rQ0p7Hu@a<+^i_#bcchuF31XqsUp@Eq6alf{birL3#0s4UWEtwamO_J5 z{bNH!aCUG)xV4Uj4gwwigqc{bfuJ%3YVV(dio0hGCpOM@7HP$NX;qPza#@mn@xe+qg_EBK9b1NPX8ZEUo5uWX^&@DfnXiJXxc5G2@z>IOdVRd`uo#^I z$2l@11#n8$L2L#tI-klw0q-eW@!8Wiu0EYLUN%J~gRgY<3(~e4YP#Gf4a&k96ssH( ze@cQC0~5vIUZn`BPS|;CU$2iPbn6OCAy@U5Fdzfw$n`p>uXHmrnlFZVSQT_z4s=t+ zCo-q!d#uy(tUZR`h`UtUR9+BqQUeCfR8Ak`o9?pmZH?va{>E z#91!Nk4}xKYib4)d;Hyx#ZQ_3=_ZbJS)zNw(laLwgxmQNOX94&#nzFDBEt~#o*Au& zGfkGY7!R1tvG#lcAU=2_sD`M$dV{BowrZA>4@_XzJQ1N`)vK ztmSpca4byp2^hq_{V59${u-k81R2P{s6sbp=1DO7ja3+F2ivYyzBvDi;Dq^MGF8DX z7J?rYcw-nI=MDiZGRew|9=K8dyG zyY=IGnp2;hnoWSFo{;5*o}h9tMl`I9>{cfXzf%ys^TJA1OA&k^R7prQQg(so6a5KOd5REa(RV-x~J0eepb zZ4*EDyvsb?=lV%d3eXmt#0W85>@)wE28Fr2*M#y;?Wa~z<+~L$yzK%+wawVZxt)%M zj_C>uh!rzYe;Y zKF+H1B)Pou^Ph~aw?0!+26yz^HFQ2j?8(9St zg*MxXtsKX1#FsBv*r#P1s0*&)FUdr6`!Y2+YP~G$s{E4IZkg8IB`KP@MdR#zXG3*t zjti@}k$iT}!86W;&$cxC=(n(=qMU@#zWEI&*Rc80tj@Tr#FQduQSlvG?k3(BIo>tq zO~Aw=)k(E9v~cl>^;>(TkSR3K7;ove8aGSxfV@nEX<@$!TmbB+D8T55G}dpBPR!&> zfJSPbGXjwX4Zh*3F#>>MPkkJEx!8HZ^ZJxJFFwy7$^? zJsBJswwGhv1o5IE!Q2ZsKhwrB zMO@E6MZcpe@IpcYxi{8OJ`)z4T7WpMw96x1FfH5ur(>Of4wC(gFO?J|-@_wsp? z^}%P4DAbfdr%qkZ+d6!a4{9yD-n(CN2?QF3coXu$LFXq3AtjPfws%xR`D|D) zugNLR|4BSc-XyxYpgp{T|E{{wQuD&?^ztBCBJB^@-7q8z>r}4BwFzrU{KyuQg zWLb?^;+h;){NEGM&+T zOM4|NpIwQ{tL(_@k|csg{ut->X9`Y<(c#pKACF=gm-6umSD@B1lke(&OOlZBj1eI& za~z*2uXxU<2?jYG-jXERtV4eYMy8drXQH&T-1qB3@vA8!MuARkOY z00#sRmHXZUtgyQkJjWwI!izC%TA3U2ZZl^fJZdTm`ain}xkyK&MVxy8-xy zLiAehmt>e*dAZV8Q2XZO({j2JlEyed7hfJ^qK+4qOYI}aSyk7i3R@0ywG`0vCoWSJ z)QY>Bv%);R|J!vT3+AdmKquQw2nY^@_W#@Wg34R>;rTMs{%r+KD%p)2fLkJW{?#DUzYenQo!#0__U=ye>E5N>VuYAk7c$ zTNwCh-4K%=8hVRX>a(U8oKtC>KX7(D1(%p){Eax@l~y)spq@ql(%J!%1i>g^qH!D- zQv+*u?7ggt*hs<3N%V@@C!|_`d=hSabH>Z%C-8JPj1e!Vct+B~du`hHDtHY@ z@80p>Me3qcjbR}6?R}6HgxUV$q&4JfjxXu+n!7m?D99tzwqm~YmnNB&)ZXPIF1l=% zj$77sW2R#{?_4?cS12JdmZerB;(UkS8^LiAbc=`c^OZ1JH-r1}*2CQ##~O5nGdTiB zgxFdNhR0LHsx`CA1io4B?$OY^5IldE+@R3lC{&l?l+{P;jTsToyi*%9C&-YRD=aSe zM-7sAV3Tac5i>MvtbXuamDaK;GCu7ln~+5JUHdjRh)M7N3ko2YR0g9=G?bscJQuOn z2%*52#6VqW-G1|KAxo;j2J*r3c~wkvb;-YD=U_v|*B7)WHHN2}^1IS0$wJ6nt%KduSY z1omRO53`^p9DM{CvvD&BieUIO_>sYQswZ$d!|E}am-~DSk`_&*K1PF8s;E2Csh|TX z;M*K6{x!aRPV_l@FXDNmgM}?~xmt7R7$sX!{_wtk-ZLPi`43XC5ZfOZ;K|@x^}jWP7M7#4ff~zImC+28eh2u zaoLYq8tY!FROxvXEMq44x_H#NZTm25`{jF=cSD=e8eh}Ud7n;Gj_!Qt&AIH#-L zq)61(Ukvjp>>{Ghma*k~GU=U_w#kw8&QaF+n|%s*@fK~!D$VYgu4kjJXT=^wZh8}w zi%EX%gMi-z_Hnu-sKdVbr{HyTnNnj84C2!nstL@jmJvpL{42|SFhj#)djQRL;c<-W zXXN!NwwJaC#E!q)7?Dca^gMeb^*66<(GSg`MI?Fq7at)-Ou4IjhF&P4unUIUlEW?- zP`?t&!4u9*QJVgpx)NuNFJg4N^9~!VbmmFOioFuC#BfJ*!Pa=qo>S)Wsh3MK+AVh* ztM@*BSytQSj#Ui$7?VBjl>{KKck9*5V@M{=SnS8a?`U55&leMk#{p4lsbACeislRM`DV8s_MP zHx&3TDN^Q<28Vxd{jq<5yld;<9vRA}Mx>_rp8KZ{rE9glD#$6DM^j&kj~H7ma1*fd zcizQJH1h@vmwy}_?P|hHKj7*d2ufM%YFl{b8hW)fDGv;vlHrRoxLZt3u~RF{|JGt8 zd=tK0IZ(0g%hxDQ7a`Sxn*EJVK0Qy4F;f9`@L6*$ELKN^%w@v zf+xK?zle>HqG2F(<(SU&Jkd!5I{2vVlI4u?S`yEn z`giW(uE8O7D;Lt9@u5Gr!eWIeoso(TDz5TUOPn zPwjc3Cob-o6lMxfx25MPwER$*cjfOZ6N#QXkybq4b?>LYaGA=pVh(5J;<|W&o`LnX z;gj}|gI)%Uo34U4(YqT2W#?q=TIV+WTJ4n7JyM>wv?kBVPKyPb`TP>Ewn;T#Ta^q~_Y2M@BXi zKh1KPM|2D*IJ7p7ZY_~w$MGK;J_I_3VheQj%WR(RayS2xt!;fL)BVTjgOV6D&U3Q6 zgI;nz&LexRZ4Bu3qUAtquZOvWRS9YCR*Ys-5WJU~)0P@p<}#NXIJNeA&&+gMyol;% z^|3wt{Fk+OeS(Y_14o*v1(RKCu3J4ki$A2yJvMyl>{=a-SrQdKOWMUbk`5fSc`!7m z!?P=~MzcLUYryHxyQ;E}?T#T2joIkQH{);BO@Bz1sgCpaQPy<3dYiV(phon8Pr*#F zH=ZEsctD3z5QfHWjFKK3I=lO9950AOV?d_QoSTxFebX3WpD$l~cvO6lb(t(#GJ{{hp7hXFwuo!6=aRmC`Hlnujqz*= zbZzyNXKVG$xcrDyaf63LvBq$Um$tL8UAXP*8+Gk0)jyI0i>l_PjohxRZ17h*zRzOq z=NzW7HayB-cQt4?zY!9+hdJ`p>@#3{?p@ANfh`@`Ax&hI)~)0GvA zCf_z|hXo_NIB&wIS@!UP&#K{$)gzIl`ugOdqP%69xvYXCXS?sD46L2!=lrBqd{xAy zYtk=gulu2^bl)?(9h&cWR$R(ZHq>zU;iZnMZ*@;Cz1L2~bym1g)XW;V0*o`@cV7Ao zcw;b_qZ;%N-Gg}#&};-XxJJVSHDbO=$Lg#O9w!|yTjSOFX?MTq2~C%1yku%4N$DTk zWz;F&L683Sh%F~blw5%0spz2BZ#X~0U+|mMHZFhUyExW%=Sh0ObbbL5(y2Y`?;)4e zd3O&Z%rebQLF3F-7<1l^pPey4gg;4@k>v_eArPdW^teQyY>crqQB!4XZWprDFw)nO z0q~_iygdhX-209F+!&paF>xpN;bm%V1uL>}$t!Hl&e)#M-OJ8!ph4#I!WGwcVo9zY z>@l3bojU0Ke?PPOPiF*nKT~1c0mq-n(?;WxR2aW0k{`YrpI_lGU^e*>ZJfx1(N!`s zCyzex$et)|h#4QJA%D+=`+tA$^oVWO>x-ZO6Fp-D`d0&|G#^wH%>06lze-9+XYO+i zPPI2{`65O7(Sebie=|m>5&qh%@*c80@@c7Kg@lYO<~8kH0j$Gf->LPo!Cc7>d*%VOi=uT{a(MXa?&59T(x7 zGvm_p@bDfJ;?4k)WA$o*6B_5fw*I6iD=P1V=AARm`!($lNno^M|4%3NU)T(_GFw0& zK^Q{W7y+(vj}Hw&u#*bb6X$2TtlfE*T_L4lRQQjXnmP=$!pkm)0Ur%fk!_l0DggmY!`XCikPl%_U1eLJHz5ZEl7XT| zWUD}pJCf4a7du{+RVS(5@x4(ze`qJqbq&P7M zn!1~(U5_0j@QpIz^sCDeNi%bv=)NsQgBDtlv1jPiP$0?GO7vU?|4tHDfIu8z_x5o- z(71w|8Eit=&#pN|u^o6_QJ^S!3#gPk(0j<<5vXQtG!ev0`C~Y-M1d*ro@W3b?{(YA zN)RyV=>LzdmAhnl-`~MM5T&Mqx*3-RJ-sj>h`mZG3}uVf{1OCgdPnZe>vnKpsX9?{ z?O{|H9An>m6FiA9$qqW52eJXuX84{Ddz2&(N766>W&7URLI3F${L#e?T1=H3R#EgI z0HBePy?auh$ARucJ2v@e^1$qf1~I~K*IRYa$CO~*1JKCJ6tqmrxB^}rSYGjgO=Sbu zeAO6P-eB1<75@}*(+p*?50ln;D2y!>jDaeFKSTEBSiDEJEEiDx2}1FEKqJmZ#Dodt z>V9R`cCY$g$m+>;>Z1Qib(xtioV)3&fAAd;?t%_>{f2L#Q7nViWa`M+-dvRpsWZ$ZVrNLBK7N3uQ1q=FJ{v(c4eHw+U zEmdU-FC;w(^5HakoNOfvbP*(>)A=)Fa{I>>zG6R%X(;*|F9rR~%9aNiqn7?p!VfR^ zz3XJ;~(n^$0I0z|=9~PLZnGQfh zDFpzk|L?@vT$i$ZRRBS9|9w+j*zNF0w^qCwk&5YiV+1--Z?Wp|+m2kmcPqQ9MUho( zYXNQCC~Eptz*t0SThUqR{HXJwG60%V-=av|9qeci0478I(Tj{byj=47ZmFoLcK{Y; zkuIepJOC^by-ONbaPd*L$p)QK~2SzW*R1ZR}n&%EL73Y_%_JZS5?#g2{+`lj&~sRlJ&xEu1a-9tH)Hej2?W zJq+8++}9_8J*vp@T?B}h1@wZ2%yJ9SxDd7NW!|@cjP)rlh(hC@t1zk`cR$@88MSMM zU|jsh8~=cFtbUAea8(7wg&Pqr{jXdIrV7M8+E(gLO?S+4UoFVM5s(2H9QG(+PfHA7 zH=$C^ljZxLDea}iv0b^3KyvR=iaId#BXo?aYU=E8)*;e0U;*VO#SP%kkUL_(`1gma z)!F7B5UTy$*3pvAIz?IRv3i>h`d3jN*}n2F_V}-aDGtmjl_R^)rt|Nd%par6{oNYL zwre(hj83HfPCEML7;|-RZA|9JPe4CF^IXG!((2>m8K25QX8%27Tznd|Q8RZS{7MMu z@)J8%#vSLyK;z5j($V+ia^W=W7(Q10yuR;DURL<8EdBroF`6Un9rQu4mZ9jjsnN)L zHL&*+lP_vbRV_zIjU<2U=A1xXDsAO0xR|vSf`(G!m8w|+PM7u-E0u)Z-35*M2hgA|S z9vZE;&X7e_zbgBac6M^x>|FzaFho@4GO=vV%w`(&&^&XB#A%!N5>^mz+#r&8z?=+1&N$kP!oW00p4XoZ7_GR=pM5n{mc(zpKyVB{f@ z4*Cxxbg7Q#c;?cT4Sm3su%}~GQ!{M87NiBxc7m}rEGqgHK$F#uWF9j6WLLq>Yn8Cy zJyCS}_|GhU8DwgYcaoik8AsWi=%737dk!eE`=16FkFtQ58hUk!(TWSKQQpMuD^3&V z+$17Edw9ywPi|(?^41*~{e}vv&t3)vRV6iT+bIMilR%&z0q%)4qdg*`q6J{rAO;mg zU@?oy3cZ&O98LzS^rxGKFG^3zTbFxRb}qQ*LSEl_Ixjq4Et(eqj%$t#k%X6AH&0YF ztzI%nwAyJ^ekKVKurzssteV=>P{eToUk8vbvwJN7uejR*<@cuB;r-<|wEEl*diq18 zKovE^dRauZg!I)EVX?vyZ8PZv)V3c)@AH=)J@Y4K|K8a&%AC0i$^s zyq;!eZ8871W%?l~kkQJL#7G^KM5vQ#8kt%eG_2yeL*^|wFB}*>+Yp2Nea0rZM{Loe z<7%YNvCtSV5Irx@wh{&4YAa|yz^*Yxa{C->bOWktPn)d(eucYQuEgB_=Y-g|Xos^2 zKUpD_!JlkoD39kzkIjitcu8V}w~l}HdL64v=@d9Ida6KqbIRCQ)Kwf9o>w_!>JZ)f zv5E`YR(Rfy%T`)-^Lss+p z9g1DS&0nOk=n8NeWMPGCK^LJ{jx)JLa2pu|K%#`P=?|jPmZdWaH@}0p0eFc{Zl$9& zGk5!y0TU&$DtQ3J1MtVvKq`ZD^TsS74a(5*66G2=lDVG`FPm0ZXQ2OkJT=PyH1@@H zDF$u(b3Bh9vmLw%XDaD-@JqD4n946~d_}Z)7%hEKZO1Aeg#0`<{REJNgJ1`)ft##^ z{ipW@;OveX4ven;G)kfW zwv=4a0I>F&6&%bquWy;nWp~~5#w{0PF1f_pj;?pvo%n?{JU>rW^*9s(;LJv*0SJ_M z2&phW{XQ2|Y~J#PzJt#QOpM2!b39+^_tQzF^?QZ~a$GrrVV|4>nc3`MN4yFP$|Vvx z!(5XB(P||q`5{zlU6I(Gy8?9-cYf^k0}G|b{`*}{*HQX7{>rUhHUzyxLa-Z%GN^(Q z9H2kKL)No}Ku;vu*$@GucWbBXb_W&=og~@AW8|&8%qoEZdT@^0(@uYx3HuGEF7VQU z2y?w}Yzv>Fd4#eZ%pB^$@TR{%D!OceC!A&3mT&2FS?257_Mb&_pq> ze*W^T6s7fpX&lH6ZKvExh@$#QVCiBEZI?(zA)eQt1Nb^{V)$~0{=mX3-~#!FhQEBEh(g4<>Zh*$1NHdCa9IY7Ss@QL^+nkOf)Q5Edop zO9?QhHY_8&(8=+EFjirC=^YS%Sp)a0cv}nVB_Q6a#m9a1)Du*Cz z;PAoK2}q{ueBebXEKh_rlL&4>k^~bv^E%^&?j0`I@G84udbJTKnK(8x)#tvyHJ#Ow zWPL$!chx^-lBp^5$=qZ;`o|2daEfWLkx43FpPD+?kJ8162cD+OCO8p)c}8F zX6}te9gIgQ96>DYD5R;cNx^#~2atCTa{!EgW&B0iW~eSvNPQM>%#zw9p2W@oMK1`?y4CG;>{Hp_nI|D;tUB005mC|$D+b- z)5BXs(CaU|9jf3nY2Gbf>w{MC1=#!xPrXPv1}~gH|Ju~S^1U@W0*v}~1IlOk;?I!r z{m$w8$cfb#loXQH> zC<%0Fgm_-CkZ!)y6|PZTmLQFoo@M}<7TrDTkF*~RGcU`}L=UVNRX)~!)Vs0%+>j|w zuKXkzC5F|#ujR1mKWX$;AwM5{s0EqbS}fh@7kPgt(yT>wyg+;YA<%-}f;E}ZkK60W zsY87xf?(L0Ojb;Zp9lzGUCZ5oB z?)2Z@I*<>Jpf1x5nIiV9!ymXc%!uCLqC@5=4$!+6(7#PD|93iT*}0kKA#lZDaDURz zpDW?l^niR)^*JCl2WO6kZ;bOQy)pdz<-SACdzegoQSE8F@QgpD#JJeiu(C^D9(&8t z=f2l8D&?^>EBVQjK& zLoR*EYxMgu*od!z!las{%Ywr=6wJ3?-r_XqLWaX$Q~`!5Wu zNnG?N!@iEV>~=X`$IaTsTeJfvH&kat7=dXy zSx4yb;Pmt_qJ9thc`yDG>qIU%%7eG10yJIuw97n?&P;$nS;G4%rW>VBbb={CTd`F! zg^+tju#^YjMekH#tvYxM;4uvC%Sh4!Av!_P>57^I{fv1b1rp$QRf3{W{cEWD>r_T! zi7G_x&Fx5(AdRFWXyCka6$>e!nUTbv9zxa7$+=?!mIF2lo|K&Y?=avVS@qqPQrNZb ziNBoJn=vW|ZiWOk{HRQ8<;j1Z7(B=pIl8^raot(RT)kn5=ZaEx{}a=dOTt&GuXT;>brqfA2`}it ze_c#J5ba|PXashi+!ll@l_kI2@cU_Ro&hhpiLyPVJwX$t`~pWi(vQ=Nq41;huCRA| zEF?gV$}lLizvnNWE+N0T5|?{=>ibjM?_Y6W$9+X!>mhz5GA{@{1poj6Cvt9Do4@@mEOK&ljpR(-&0k_MzHqyKubO#Q|;8a^AiDI3JA zT4q>(n9@;=enAE?VNIrA=UCofM`#HD%G2Y1BN)kJSO&@ae3ggp(aM$)i5CxtdTDrP zkm7N$!AKk)#b|YyeC)-6w?kQ;IX8U<3agJH@dx5oF}b*>CJaboulh%7!_<1`^zTl7 z{>F7R`yqdj_Masm-cK+=FYTs9H-fJ|?P9&O(2ZiFVe(=DKO92{Sp+Q~oM+uHW0G)T z=BWWN`WzU(?sguLQsu#uSd~}h+4QoiYR#l+kcR1P<{e&6D2GHjU|SiX4Xraw*i*cJ zCa~B*z~28~kXeuoj!{1N$$OUN>q;48vZw{KbHk)P>E;g&Q6A%%Lsg#O51D81XI4NE z)eH8YU!?SKH0LOi2*P5qiuzk{931m|ue%OEoHNW{-GV4{DeKCci;~4_kOMCry1A55 z7{+F50(Q0%J#71Ft4gevm|*BWL>2;IbtJ$Ao*gA;j4k)GcG~wC%|n7)bl{%wb$>S@ zOoMam4gu6+wwc&@D>grVtWu}qBzhf>w_OkG;Nx0=j4#(Yl-wDtttIS&_pRsq#ZEi(B2<+-M>^5Fnd1_tuvj6+R zTXY<-(;^w#UAo{F3dB15bdIi-KJgH zU>NWmJeP-I(Xe46s2ma-i(~NYrye8+07JKMqcb?tWGkZsfJsfui2ykb$=}=7|12{+ z^#-XaT{iQ~c){nH#W6PfXNUWE=AUv-|2nTL%GxhR0Dn$$<|Xj?JkXCc>6f$>Kwrmg zJ-}FE_yu7Rz12Rlo~ta{Ga~8=5ww~SZHLyG?lO4a_?YuwO4j}vn2;iRAr;SUbqS>N zldn+jS4;&c!bL_!8-hYL5nQjD;v)AW7@u=a{@LRd zdzXuVTzR)WE9~?f`uK9U*wB#nf-nm9<_6Nv+8mX?QZcGcDpC{rvi9zy#tkH_lBTeB zV3dD`BE+;cFM&PiQS3;v)Gay4-sm6whg+tA0nR&87~hj4@I2t7q>?6ppbMuMsvx;i znxQOE&?77Ia8_VJ1GyHxLZ7aQsPtLgGIHxg=^>H{JP;=ZoM||Euh9t#j zEN8)Vt(95o?*{#2b5X&Eoc(=-ZBPtS`Ghmi%eT|9t<}OC1^!L;oYKZDNuDQxG8v&lM~DZ8{Xm)K zNG609#^rAi=P>+B5sp9hMA<=V_C^<=BLPN2sgl9pLd7h_wWsoPp_y8Axgn)QOmPAjP~Ay|tS-HNt4KQ_A41C(;l z!Mid4DE4jtl(saw?L*p5st3@n?!02!?JL@E;!K3FDxFyGb2G(|N%;=8kW7FC+)auq zWf{@7vOhlS$@~O2kUCz;fzw?Edg)x56(LSD(KaeRjKT zJ7~_;fn5y?gI9Tod@QbAh%gM~yz@5=@D53GO=L;CY#373$GGk5s}3Cq13_+sd_WQ+ z$b+ke--;z&8UblsK|=BnqW(VeZI-hoL~o6*cC$y=X!oQLdI zuueVhD{ud_WS^{9=SwaBJ&q&Q*WSz%`Cdx;ii8qnMMPK(Ik+|W1#Jea3v!aW5p9^< zNB{I~6+m*6(+m6hM8!qXSR|F_b(-ySZdEJY5F~-^Z^Cih;lfi~?@Px9I$QNhKwjDc(){h|-H(ae&E3Zkc^d>N-xc!JKs+qLj zBkS4lcHzkC@+7PD)LL_`RdQj4d&BENm%mCB_l;!|tmLboe|zQN)wuwigpBISMA3qW>;U4}q!kJySO{6O#p|)EYI!OXkypHy6K$e>TF0W-h>{ zbr@6NUyV#5ZKV8$XcBjr19McQ*?~={lpt-uvRQQpWUk3{{X3t}z$FaxpzlN6JGbtF zO98$g|Madex{g|X=G$*kv87g2ZL=lzT--=B{^;>c9R`u-LGORbo~^4uYc_rxq( zO;r`URps8`T;sL!b6B1>7%?J8yLV{`H`33uSbKZLFrzJU%YznkW8jG2=JBq~G&PX0RQ10LQ<5aMVvKYIT-7uEGs0&TK?3fRd^5lt z0+E}7Ms<@v#~iE92hoP~PiMXv-CnGV_2(*}<$-0Q?w=6^|;`+fN^YH@z)?|zHb7mqD{e+8I` zr}ld(CX^LBFe!MGwf6XpdGDWENVrtE`ANJf`<2hNFDFUU`z6d~Ubt`U2FYGS;m|ec zNpf~!AD|iMbt{OU;SyTeIph6Y{_Un;m;3>D2O#9orLbJB96Qq8xd!weh3tJxA-EFY z6Ggpv-#!~kIh=b2d{=Uinwn=b$(E7{$db1D29m|cDH_$kM&KyZbX&~HXJkredg$f0 z9w9Jd9UjQ5mKC^l_x;aC^Vz!E+Rc&N)S>S~D`nmdmd~`__+S2mF_&UUt^SeakMsPc zCFfk+lORz(C&l9^=h?c`F_!D9EM*iS%wM=rUU=EPL4fD5Q=X7`2=^HeN;3nV-23NV zvR;|~*mXxlTU8!+UQIL-whjSL%4OI6=HWEGf^~_yQ(2jOV0_&m86@h0eQkn&Y{*^| z29*#x0EXrOhQWtEliKh}bZs~tZIDSguU{`O_^Ze+!v4k5XR&895r_n)y&*j&$iuIlfq>1NzAXY^@;GFZ*keAnu=>%&3HukK z%t3_Pb_7UIy}u5QvtsGt!^8tRBy!MNjZg(j(2J69Z2UogcUV75g&V)V;0EP$+ zdCe&ZrFcq)IQFm4HPPOZXL|-^mLh41L0c%=X58?Rg+%F1x4#BKz4{xtq#ICgWcg2z z89)~~4M1=x`Wr z$D2tekPzfTw*D?CGM%3g8HD!v%47oGb}EC#psZlF|Jo}E0w?_DE;tWlw-6k&F_rrH zEN<37RljZM@!2}|>StyWc1eL+72&O#=iTZyUD6%t>EwW)&dtp=#`j{zEWr5Uw!HpE z4s1-bN)s8+X57E|f9$<^IMn<5KRyel#nPe(Lklq#jwdOq*x zz2A@fegbliyzZC2=L_{m@SC+MLSx%pN6l`Npt2O-qwAM`znHToIpd3avK$?1>g7d_Bb$n18R;!xJ4bEY3(kB_{IVr{y=qxb2>d`i#({! zSj~=}#7dthTB&mNKNbZ0Qe3Qmzdi&$Gy{ETj%TspW)OObm@E>n=vTioc&| z)p~yI@qekO&;kY=q(=-FD-?Sr@78PJ&Xkspi0`3DZ?k^R7XO;h3T@{# zRoIec9Q6v*m=Be6TskCs%fx5T2K!t2Hr|H32{F_CbNIY`nh;r2THxC~^1ceyZ|KY=jmhUz;4xJT$ z)N(qzdLEA`)8@a$vvpNczm7bwE#3=X`Nss=?gehO=YEHi7Umw)9P`=XAqhO`JahR= zgn*YcD`TL1Q*aQRfIZm!AN?YU27kB~24wZg5{~l2(BDDTNWsag=TxwhE_UwK(BKUz zSdq;lh?xLiEqY}bjG`L?$SR7_WqHDXLHs+2TfJ+qlVh83aAqK>jbu9y% zS@A27*i{amq@=b$FpewrBBweis{DBZC;~BFv@4$T$8d=L)&r0{7 z!xn;LxOA}c{r#BOTekX;ip>EYXM7E8d-a^yvQ=+wy9z{75ny_npnz?wqXk8OjUaOX z2RZzb-%Y@Qitu_qJ>5-a%$kAUE+Izr8LTL+FOrUh|Lumr&<@ZDOAg?J3J2=}N+Irp z7^EHrwEv@SNDxxhmHGR7rR|QlIPlBS-oo3lo3ooN1x3*#@H)^aHYRYswp=GfkuKk}$j2(r}{pD){8z_e&?o44$Hg>?_n7yU{1x zki$lj=;82&44@cc*s?7_aD;C&J4XVXMLIXm1_i{;pIxJkZUi4Kkd0`SLG$pNRomnGF@s|EtE5VXD1{8CZG}M2Z!SR>rjmNX*M7VT!wRYTPenf}acLwiNnSz7LN82HT@!LSC9Ii4T*b_;PvA zi7~erz8I?vNmOo*13AxAp#GWesnVfa#XEU?PME3(BvE6|q^lD@VWN<}GU@NM09Ht4qxb(PWf?>okEzvz?r9@i84@6L+7*T;5 zko;vw%c8o0YxH5Dk`4UU`V7h9GS6xVQjgHbL1#hy>YWS8hMx96>yT&7cyhxYTm-=Q zTJQeKT&{!U2Ve26Ku(|@TZxrJZN|BKG%O42#X)si4E|O+J?gSK-&+BW5gvjo0*df| zrt7nL1wX%JjFXZ{^cQ(ZE z(2jfu0E|U7B#y}a<*(z^kJa9naST2MaE1Rlk^Iuuwn((D^<)1ZgIxK%$sJrZBpmj3 z6}VvSAK`FVm2fmm;;Cy@GbD@27nWGAxy)0rhb;IV&y_W=FoJ(CfZqy;3WDLzzuW44 zWdg253^Dc_`xPAdqa0fSl4_S30RTB+W^+0R3xmV)oYJ{?BSx2x9{n5X{Rw|TeQ)$ zpm+7PbWGwun)?3M(*Qz@9&?3Z%lF-Ki$(KMmO_@8W%Zujul1$cf2Lpr76Fe+Ryes1 z768Bx@^8PSLJAHj672*PLXLoprP?BOv^^*7*>zWT{Az9oY^3F#)RN=qK~4c+=kMKE zc~GVlDnLnkR~VurH!DxM==j-OYBM~xliLTi33F~g5{a1+xzXZ%3LAFY^(8L}^w@Dq z(}SOOxL$Y;m6-YI8^8~_C)X}+gEXV=KdTr#b%hU4m=&f(!c^OBNJN?7*yTQyVW#Ad zIVU(zO-)lz2rvpT9I%`)KV7db(K+rIM@3xa`F&DzDP($)vB zd0Ge#pq4Mw4vQF%PJPTqvVT>ArqYm&j7rlIks|7GV&{Z~!wCT{zkej)ae;(<17b-3 z9*n)Mz6z>|3CPP2Os#i(hC1;SY9yGULu#|sRb#&mXlxBJRe2~mHn*7);(N^4=n=r} z(9>+m+Ve<)U0;32vbbyDP{vEP?(qt*41C<)Maj+hR0d58=|#iI;&$A;4@k2YK@ zwm~)_smB8m-WrGU_{`@6B97f6Ws19_=_BT9bbZC&yu-VU)n4ukf8<_Qs_OU1JqV6Y z5!-zoLON%7XEfyWa#A4rEk|nUdEhf`n{wdF-gaB7FR?X&4-bT>UI?&cUTeHod+7|H z>*}p-n_QAcmtrn_c5`Z&Ic1^t;pwbqTpK7mJ-YM50Nv`!K^iKkBI;WsvWU()b)@J? z>%G|Bd|ipwGLJv|0N!<2?WBJAs&iT{PdP<|DCt!d39fJQ`s;5Xb)6BwhqaDnzgQVyVv5~`u_6sYa&ZD*)z#Fcy^=#*D;C8@jb`39pJ2&;>8FCf= z&;FOE7@xV}XUZlwBwF75X?kf)V@w(=Qiyl1&>TISMy;xXP=P1B7sw@su# z+<70EVz-^2l{;Tv`M-)c!>S#*g?9|AKc0VEED#a#Y#23(zO6Z7zPfH1b?{PNlOZ})f!hc8gYujm&v$D(MJ~al{+$y+MDp(|F0jo0xw?A z1%tmY0FV@!U(|>t6k_3T6-H7RQ&0tq7NA#Kf0eZO3(x%W#BhwxN90P6WdH??+_x3( z0c}wzhmwPGO!3)Y-V*;&^^^Ytu+4Y8WOmOEh@pzB*#tSp4++THklZuo|FnkiH)t*e z=j5jNt2(7mafPp@O=W{8bnzjbcNo^;0N%fD@6Sl#=kqVVZ9+O>9mfE9$1j7b7e|P) zpeAYec424z=4ExD-{)WA$$xvxI!GIfaM`fULg86sc>5O(*yx(z(12*$ZHB02$S<~3 z{7RPo+9pN2Fpyv4%>}m)+#rv>BPe%(>~9*g;by~5l1!q_PH6qRSzA9?{y)w)zdo}V zlDk+%tT5VD?u?GMj8z`uKTrFw=fmSr(CDCsb;o3Zmzxs}XKsj&0Iin*7d?N2EE7it zzun{Pzek%r?<5VpXbLW7HW2>V4-*$Yc5|;gIvVIxr3R&kxBqUKdZxKUA z0bf@n!6zaG48-9RFZ&TUppugWd;&`R{GJ8=FY>eMU*!NdfD!;rZ7o(r7WDx*&2VFw z@;{;jADNfFd(Xe_@IOBip98kYOX#GKmsHo2*u9h_P?rUe2A@&~b$OIq+$Tyf6LJEk z8lytQtjXHVZ;tx;v$m&HALzU=Kq(T3b`V;??R3VZf}~(hit2qhE$0Elb^o(0LD&mX zyyJr1D>Vo`1*%p9NdBPIgZK0Wun&4WU{OQ=b#&)LWGHQX4m|{Z6+9RZI}M^MV%tng z&9D^pu}44$heOe3_fGAwv9W0u`DZ=y9+yEw$1zL~0m*KI8wp$@pZ6PVLy0SXQvSe; zepTcRpS5=C7?UGs>-b$P{9lo51iBkUAhRDHN?8L88?F9E2Z0Lt7zD#o zt`p!o{zJ5u>v(=PBNa^E8_2}HNG|_i;Ow(kes#+H{Kgvih9egA z1#~56XcqDlH`{cKweNIjngB|Eo!Aduqz z&sIT-CKziO_tPEEa`~~(D5$Ms&7aB30<=|(Pxc))G7bS*H{Xj0JYqyrMb((fJ`V!-b5|YU>j!-RI=9iH~xCw|g zV*hk$e0QIzc$7gcsNlXf8}r=%+W8tFsv@R|-!G5^cD)H?wdJxTlt(3Ir3x)5q~omB zdLeLXflFcWaBvOuHf#a*#8O!Ph6?dnXRZZ*3o`f}*;W6gD|&#H@6*j)A_Cs%RBYzQ zi^Imz@Y(^9Q~~ZJhk*7g?~nl+*&33rBCOMHc4}(VxB*_O?R&JPWsvN z`9Dmn0RB2huu2(xHBR?PKwJzQ&?k^`1EDDd)${*Q^r-$XwRaU%g#@aeUy^VD zv-%XoD?1eu-g0ZhB-?c>*i8{w8Gc3L@4lP;@Ygf}4oU{Cev$gISB9&IWFsDD7OI)H zr~XS(yMkg@&3RfOhIE5cFoZ-?b~pSa534H3S?z^qy%)>E#wkzG`6bg5BobAC(G+Ps z{)HU#CB=r33aWq7Gd;8B-}44~etZwlCL-42FAS4$ytj$YHo^;?;`k>jA(27_LQZSF zh}^7NDeW8JwkslrpbtXUJCVEp-6b%hG?ci)Hgb;xBz*zL^Sa`TjRpazBI^A95Edtm z?0`goB1ZS^51W&si{qO0-$iH5Vt%1E|2fcxbYLdjQQ>7h>2_^x_niiqDnH<|Cw(_X zKn(Wea6X-GKgXS=Ce-$Mze9gv`KSy=eceWU3j+v1)8pj9uj4 z^b2VRa_2XVwZO+BpD}~sdtE$!?(*{F+;b@e+hyMRX zA#}*@m7&JhUs9v!N9GrcC9rZh14B6Zc>}{F`E09yg#}dj1`szZgoA|pQoV{8a`QS| z31l@w1Inv(AaRQ3YyQjeh?h^W_1*N~K0zpQM5J?ddp^ypJJjo_@YG}N{9$|~CPHYj z&bw0HqwHS;_EX2%y=47L7W|{} zb;74A`4UVikQd2jwq!Aq4$*{9G+vOeIPmWi^Dv9yzaEdzT=TE5`Sb5{ew7OU^UVKm z9=I!k-}*~8Wp|QErl@)on(<3woUztV7eBxe>JR@>UGN`vSN`?Pei8ovsUiROfuC&m z{|Q39Lk^?2a-qzWYP}g;r@+xYVTpU+Ej6l?;Y;Y`p&AviY3BlcFD8V z#m*J|n3}HYkfAcZKh|r~UaE3S`Wp5v{&^BVB)tdYHZqUDlWs5HSGcioQ{ngGdS!=+ zZpT9ZLPhir>9NjJ+=m|Zt&lU!4T)R9?W{fu6J^dil00^}XvMT0)SZsiosuoZ zX{H8w{T)&>ZB97D)#1)_zCDAMp)&~ky{u{@vHJ<9cB}cC4+~m>)R?u;k|$*yWJK(p z9^Vg5_Ei=1LHnF~@l6D=Bk~+^vu|zz*qT$gIE^S#BwQR_Dm(Oi4yhgO9?@0$GE1+2y?kd= z!7zM6$R~65WVISE>^%~(4g4p5hw8@zBo9+=zPRsW+P&$ruN@sv&_=q_kD7H$9Fyc& zG5my2?jN^zWbCiXsWJT%$7tXoDqr*MbQTJN8Y-WthE%E&?|9%6X%#hu2?lY zSo}>D`NYZFGGh9qm0#3L?>O_AnJYXV%eT)R z+)*~CZsk4{x!}d<%E{RK1RqJ6;!V5be4oY|E+;Zu6vTNlszS>`EF3GDTJuo@)H>B>{S%$|`@4D5!OTH6JM0Nn0qDe6soxM$qB*oiW5!grQIP?N~20NXm z?RQ*|O79A--}o?mMVY`hMAv2n=S>Wk}?-rS;=8s_O$Qh;HM} z;RNf|hXhT2arS(Fb$N~I7{c+3jFIS>z&?F_ZuOWuHvquYP1MG=I$+++2s48B8a?q~ zNy^qj)v2-rTnx7!iBobK?3C=x&@dx}O9db2dkH&vVUX9cZg0YUlRA!8)kJ95ZCwgivd?>5 zsWN67b71kkl2deL1&c&r7ZdBIqQXP#mz10~igvVJc2pAj5pihz!i5u!r&#oYoDy<*WC!of(J}@%8SR8I) z*5FDLd69xM=cI5E>ShFXxj4;x)U@c&Pq2{F=MDuWyvq}p@QM^!TsOj9hc52Qk+`Ut4a8}G(Mxmk#m;n z1cPl4Qw2oW%$EBM)0tRP>l%d*(RCuo4Aj32$6|xc<6oS=&mAUu&U@Y0esk2M0iA~S-?;Qem%}pYIMh9=F zDAiqfdBg46<%81Nmtb)#b0>VhtS=mrJWVylP0-1=hZmp(5%Kspq#ZC?XJTTR0i~}yP{gw z&OaU96I1zTZ6eh`x9rTL$;-Y%!)eHAbd#fyY8+q$-}^W64w(u+J(8X|8Z!|3%+ zcEGx^T``q6ctSkkYZG)UbZQfkmfdwUlW~Wp=X+) zVAa@E51d^l@%CR39g?6!0`=612@9JBiZqDZ(L`MFt*45DMi zNX1ew+Pv&?||7EM^E)kApJzPI^8u|fv@r~ZzD zVL4}ka=XXmV+!KLUMSnYb|sE&gh742y)0kRk!&af7H<|^e4EZ}Q9;%t7W6zfPxS^v zM$^$MpNhRYCg0=9pRa$_;HCjqTp3vFvp;tNiTutgxXNF%53ISL&Fr;#+%vH!MuL}; znfFkdFg?3;tZ_~bn4npDFHsNX5OI>IHS#*HN64g6Ar7>_9*h(m86|BQRAo;Bn89?Q zZzGZC05c-sTr2>%;7M0L=r-^j<_Y$$ydpZwb?*HDlj|zX2*XC>%YMRTv~$-#)-^zv zLk6`m@@j1MhJQA3k=c>~Emu}ZGVQh3>bmJlxhCR@rjCgpnM6yI9J{cguCP>Vt=LCb zt{vy5f_aMzaV(>SL>M;qI~Xa`ZlG=Y)O}0c>aLlJ)UXvcGZqzlV?^=+u7|a<4qvph zTH%k#)kAV!q7)~7op^OhkS(svIQ}sHWGE@$s`YHZlJg$&0C6P>&~w`(fY=Q1hq}uBP0(M3T8@Xx_oo6$$i{zGATKe zxt#n}70SY-WlqeuqdUv&Ejk-eE(EL@f(d;iJQI0L(N)uYJgjH6v(b zM9H_Bl04+ulOLEZ#}OK(Ju62P6~QFWA0cJuJ35I~Gh0dsY==x{gy2bmD!J(J*F7-j zCtki4aG<*`G08jFlR3;5XTr!p=$bm0E0^0=f${@ex-rBepCo-=UdP4Tzfl5HaD7B6 zyNS&lfoT+Kz5=o``1LMpf_)k<#5z1Hw?8&TS5tD16qkLD%p-UPOfC$`D;bH(18*!a{cmuSvWk12Tl#+7?Tt8g;LaNU)ilJLL1mICCi#V6@QO>#0IR z(p?!f?Q+`s>;PaiUY}&cNm4(Z*|Hk36W=W(hk1J~58TyVVpe*)jdxXTz9X=VD;?gT zu2iyPE|k44hMBRhR50R9+%Vly={mICAe3wo;c$;I3RCV4k|QjP0S zW_uw4=CcUQdXqfj`XsZ@hiF}?k}bRW71LCG@wT|zZY~x{=S<@#fEDb(&COu8gwiLJ z?6rdhmM*y4E#5G~i(x^~$HV*~>loeX%>EK= z4U4P;2GlX&`Uh1aPnz+9ai&>WVX_AJIAaHCr0c^(IxhivN^N{LUj+v%sx1+$Q4>*+ zgF{6gCg?+lP|ih;$o2xvX0#k8XpJSA*m>1;A$G!s)I^<9Hy_L>4R8B|I6mTuhzFLa zjcIa+K$)hy1%$r0pD4fRI{2}&eltYR38{*v>c0`#`eafT##vCN=iO($%dXPbwL-Q# z>^sWd6agkrDA*yubhQGvhe_V2lX*Qvai_Cf*Ls&NV~<%AcFo(O;>qVhLa;Tsvc<5e zXY`FWBxmrVpoWrQH!5_i*4k@sh*Wsu{wankrq+s}zZGC?8k6>K^)}B67P<$-9UNZG zN+i$!PV#u)cc;RFpg|_79wFR+@wzpvv%(H)*7ytSPlYFu?HAFP32}+Cfmx}M1|si0jMGwJ2gq>`bD`nqUSLyJnb|O^ zN*xr!46{Xy;IrpEvqTs&(13h=&QF6RKyG;+ z?722&bD=XO=JJbP#GJtXWx?z9jPeW{T|C_QfhDM3I|7(SHPE`H*QK9Jc!0p_y}i zRrIfo$(6>@kC?kcwd@<`c8fb|c-T2&ogXjU4`ov+I=h&CyGL>7J69x`LlyzK>89bZy^ zrMRPgob?wf4XASldQ{HZkvwd;IB1M?RR1t7*a%{@N5}ZynF7o*Shj<47$;C!L+=sM z=JjZ&WU9eFo%C)pNemSsrnj}?&}q(na)>ij{zR@W(f5Iq2vr63;A&-?Ce+A!Ko@`B zGMmtG&9{u%!XdDIk5;vX9}Ix8j4g!#4SBkgT`U3>|7K{lxa3VJc@CwsD8r6}gPOX|N^jxqaWA*wkI^Lx)8 zB6;9qyp^VJ_*mz?#TxK5C8c%}dEMC2DB-8V{X~GjFth6mX_T0O!7c*Z&4#p`{P6VgO<%A`6X?5hRxoqv$^mHI_R}jo zM0x$7OPJ39)BrO>Hq*({?|^GWz%>$YRq0H8=J9=E+5~pMF+g%Gn-i*KXN6M+KMpO# zFH*9z*M?KFi&NJEFdk*f;DmDJx47=*LV zq<&>2I$c-&!`}?mEN1VaXy%b!Ve4i!C`~_!uJdDil;#INBnldUQ{0cTUIzAM)m%Pv zNzT79$(m)`GK^0mS5SX`XFBs(f3{C-12I(`-)sfLm<=Sm(nzy#wnQk5tIPMI6f>Ek zSAgL{$s~Y0ZtO}s5xlvMXj+gpnaLbk!$Mb~3-*B)xHNw0&PPX5e<1yhYpz#{h|H)o zMI?$fp+ykd{VXR>Nu^!*JMY41{_~A=lYlq$`>PjCe=ZbkfruiLaOhgx$i}Yi(CbArx{!7lxS&FA>g0v}tz#!+ zsyplqjh_D5q;87gfouEjfH>`zNQM?x?0T$Z;UGQhJInWHNl$Zos+;!8ee%j%T+J== zX98dlF8^gOZe*ATe8u;Wk8!Wfem)UJGX_@!H0PMG%QXP2!~;?;W9lZEB<`EjReJ2q zbUd@g8v2Yczb4X@>}}rg4QL9pWeG+S+W0=0&TGbFH3jtyd0~E_NI>UdSgXpuXMU`o zMoP&fX#h&J+-%=C8vrn#DF|YuXV;`8*|IW##L#dLfi?mIb5pgjGl1m&_&n5xWOXg} z-F}hpUExHYA;$MAh|L^d#~w*HI%_oQx_ni_~%P&4D8CE9#C0aBdn} z+?(*)d&&&oEapi+5^U-;L@+uQgkd2W*{*GQ(RhaHL+0fmN#m)T)S3;T15M49hw=_O z5DDxCy6SI#VLfw;goA@dM<)wIRL8FLtrEEpe?KMk8tT*|H<|G~VrIMr0gvJXqE6hX z{PwXDzQON4Kl?RM{7nMM4%lX@dHq0Tn%te2#7K!zW2gIhN+n~tUpwEI#wo#YH9(iKvtN&FtZp6WPibO+aTpx%5MSu9u+vO+6})*P-ROhXvoa)w zI)Z5u^5H8d8k;$NBlegCf$hy`GJzSS@?)K>30u&NlO@OJ*VM(>kPQ{M`+OXBnLq6n zA+Y&#=<;^4Ck2oM8(YC-j3Ss{fGL40bm9k;{#KzP*Yp{s01uJg#ancT@sW51_0+HO z9Xp%14RW7~)0l0Cgi>x%YczE-<2V9Ca`?2*+Uwx7_`BHblcbbDB4w`*)2lGL$5^7b z!F&FB^$GBN1!OmOZEHx^0!LsAKCR|a2MH&KjaUcfHT38rhiOmhzl0nELRA~^!b}2; zK`2KAt9DcvJ)1DBZvB&g&I$wthyL5Izj-4n{0Rcp``GHxOa!!=ht$pB*R3#m?+^8B zz1Vs-b5ZMP!pDxy@XFeBM^YcerdfwfA6t?<-1!6i&oi7QrZ_Xj6!}%bQY{a z-C1Kv$Y^@V41&S&Ds1(GYLZWm2fRVu7rSn1I{C>OF#zu^FA0)Gd`Qc-@f^<9g3M#4 zHl2?Z?3%$3v*HJ_=45@ahJoV7!}UBLqB*E-^RyItL_x+og2&-xJiu766J5e3>x-aC%a;kjnMyZy`|oY@l=S^HE66 z!v)Pg0T!#_tS0F%=^WJX)eoM({eHS}?vK7J5hQ{>I{08f^oMV|;BFON*>9o71QSxf z$Ns)hEsxFBM0W9+pMrK7bkm9C`v6@v@I?E9s(qY2vBt26)g;JS2icPe#%(ew54l{5 zoe>LLKCrNH>>j*)K)EU6Fd*a_mO+yq0>sn0JEm%8svh(ZN3~jAV9`U47(T2M7Eei* zz&~i|An5FuqDy2kIrphGyL62C>$hSvMbRmeUF5wqK?Bh$$~!9-IbHlTe+EsRp^JvpHtnG#=RH1jH|0EEP8eA~ zVXWtS%@HTy;ys~(r2ZX5-fn2Of^0kgN`lWr(03t%{EZC4c|@O!Y7M!R>ft1?c=xMo zxqZk1Qa_H!D-a!6Z9@{_3Jgr$8x$n+4goWRPawPzFwy!*BO~#G3fZY?)x#P*x3;7~Ce$dpFL-qxHL2P@$rI<*&If zh`g(yPJ{Iej|3Q--xmoJnFVIO_-XzDk*6T;ShuuVTXZXAhbc;9>MkpvLh`qbZRYxc z#M$~$8TAuQpzbMp6r!|gO{Iq5wa*zYlywQj9*sm$*Bv=JOvGiYG5>p_HWJ8im zlRW&6cKB0wcz_c4Soml+PI`2_ETXS3B)Ib-^%+SrOtZ;>g#~wKALQGf;!_pqxL&1>+INsgYZfx z2;E6&OG6`3p>Y}e^7BeJ@npLB2LMtDIHiwgGhoQP#vwngBe=3HWt)x(?HSg$v=(;M z$ij=rob4-hU^5NgIUd?CCFCn4ikZGDO1KLsHMbDo+7uwAAVqI#j<#@~dzZ*#H*dp^ zr3Ob226Ur2cc=!2d$;ozO}Uo{Y`7X7n_>}euwu`%gJHK|2tJBau#}C30xzrU751kG ziy18W?bD-e{0_69$@olW(2Lg9ZICEl!K^^>D0rRK9jcQMt5w%v^-XexYTbjSr<7Uu zQ`I}!lW8of7Px8h<}d1Qo1942aaKAmXTexYNPJfBA=-(N$r$lfOfi@$TcjcgRxclFW9Je+Q>%U)@6Orh28i&9?VW>B6?(5S=CQ z+eOO#!c-$oeKLbjEnH$vbf>$48|035hdd}o)_B}~R;KDB#k3?%@}`b~jxn}W=A4ai zSV244w{DH&-Gmq2yJfRN4BcbC{>>%QC~w)48=TdY43>@+o$8Kpi>QrEgS)RTtNzKJ zDizSi&f+OM>jbAFC#iu^EUL72Deduges;g5LhNDi)1|F35;OPiCIO>dHfx+)WVU#M zu`gx5)#JV)u*3ZuwHkVHK(id# zL%5;t&`$3X$MXZ*#I>f(2BN@*#I)_Hs2)^udV{}Bt%3MHFvK~-5@%^esJ`ubjVHm4 z8Tb6*FEhJkvljMt^|#kh4;DA#v`xlJ)2D??I|i?TR*IRqsvL9rM~AEbT(XB)`+gcPk4n<7W3K*n19n zs|A6}+=YZvZh8jFg8Vr9>bly!Sx~xMt%_Ra=!!WL_}Ve*k2dSa2IZDeMJn>IXKbnz z8A1;oDjLY-xhnib2o?v~K*`aP1>NG66_X*f6&ajNTO}%7a!iNYn0C5#Y;R2WZjU?H zv0|A99f$LBd0EVeo7Cmkd%~-}DJ2f%M_Dj(WI|bo;cuEiFOMiZIxs5YFszIOP*!(cn@XeW&WC7 z!=nz3b7D~ujZE^X($~UsOZit}wm$Q1Dnr6{Tw`WUwQ`X29kR|{SN4}?ZswB<#JagH zcEHEV%_7$RjVl;vtXnQwQDuL~4DK$M;HTW4&1^aMK;GQHPRv6&LZDNN7Y2_n*ln3E z&x2YpwNWnZSi{$Zw=OZY4@IpA4L4%ng^cINYk+-P3!S2Jl)QoW;ALw~MrAVpX4E$w zwA!=>opP57i1)qw3t~lA1rRANb*g%cQrcHs6Jm8P_&^Kpw%dprv=Z7^&(P%>h%4pi zUl^1oP;1n9i{-3|8gU6%1FP|8Cp}APq*qjTvRo#!1TBm2vfl89K|?Rt&uPpA5P5Rw z83BV$zkP4v^zYW75`pshbWR9|18T?~M9Ti3{p_Vm-;tO*q& zudSD5^@`ZN0sj$F%loK}szTF3mJg3L+k1W#;7>ch{K3X=>VlGeLMT{me6=#|vPon4 zeQJcl*g+ZfQ>Qm_AzMkV?O|p4H#(Tm%;Ieo!S0pP)>CR(6Al?h^9m9Mf}Lw;{Q|7a z^>uaj4?++A$C2U3XSX)zrpN9;-W^pLjd}&cE@Re`B<@SMAZ{ZJeoqCNT15 z`a9rmmwZ9l1nROqgg;OMMCl2Ysc5tJ=<%Rlk$D`nXK~JM2H$>M7v1s)Yl4kD7tdYl zlwsLuT3Hg<5T2GyjY#SVpEE6GzU#0dEfmx@g<|WFqxeO9uwTGmTOqZHOW6Y1Z6c-p zHz$E6Vx;(;D^L*QL7SPvJ2cF|+#uONtOb@yYR~k4NlZO7uG6cuwf-aXBix|EFTr@@ z^!8NjXy1fHQ5&;NqlFJP&he0CL;)(XV|SP>AHfNNE+tWZ1v|vjchy79kT+ftYqU|b z?g3U;z0i6POYiGlV>q~#wpIpP8=i3BrcCj5?bkVaXCRsY@EwT1D-r{@K#1HPnmRCg8H^f>wfMl0B z5VoF4T0Nxbi;o8+;4_ayowv9}=M)!z7JOsz-AfmU)#7%oCCm`7l>S48>R5S`zl5y;#lN5=_8-$@wqqn_0 z{k=#YHza6~WX3F0>H4=Ch2SEC^*zVoVgOD@3#s%5oGkRS0lfww zdfRo9qnk|t-cseh5~t0zb$v_6s_)1j+h;`NIb%kzK~AgTXEu{F9SL=Lv?a=CX65p` z8_0XlMGgN`_90R`lTdQ8v67jr2?Bq@z0iZJ65{G%`6wrgqV%R{a8z(J!U-(u!}hPU ztiHqz?Tui15L2Yz^arEYmfa~4xU}M*Y z9zTLYK@%h}JfdbVl6M`3I}ofJ=YHox!t?p`5rX$dd|Du17`O4N#I;#pG574Jra9B6 zAbb`@$F3UMl9@qmbdHN%rM?+%OL6aIt!TmJF4>46K!xFM37CI^)HAxMjiZ6@+<$|# zFQy=NM2V4?j?Fv`?!iW=ewos#bSs!p3}4;Ot%X}Qyw(o!6V36N=i#(kK355r?a|r( zZq*p1*0Q@p6hFZgU)Beygp%pcd)UPaW95Ah&~qZthH}Nl>Iz&zHDii1enqrG1BO-@ zi3NoLy@rGR4#*M|(LwrSW3!W46Xu8(d7ac|H(wFem2Z7k4Zac7=b|e1q6!(i6vA>bZ#rj=@b?yefGec@oLP z)1?*!d>-fYatSllPU1o)rA1g-$Vouv4t3cg;BZ6r#gIka@|}*xI5VU4SS{Q8cd02? zVwIkYK{6ozBjl%)xhufKkdxRd@@_YsKS#Td!o3UhEtCniY%Fiw zU5VC9bPa^4j?#GHLVz*wZ?mqK z&M^?jA6@T^ZM+t{in5>d$`h_mH$UN9bB9@+A1jb(Vk+=Sk^Lbi_MHygP9*|Ae#^Ct zou4`yI0F$TfuEJ01?D@CRRn4Kt1Sa7#68U~kgaby0sSPPbTc_OufF~kg)}s;KR^ce z1ANd1dMH4{+Z8^1)94i%6q!-EGP0_WNa_BW+qO&M@7cU@?jX0PFLo=HfrErbR6KQA zWGuf=RHQQ-(aqSA&Ssjx?MgJ^cWuv(pJ%;)aNL<31;?CWN zYdVH5$c?>$?Caa_$mziDkJ-Etqy#8Fd8v>7`68=<+P!ZE=fB zUT-v_sK#Sex83usv6AglLy}@eQ7D%h;YAegab-8N#}}?@@*iwQ7n*m4+sBVu`hVq$ zPj}xnA9)sXKJ|?LskugZRatz|nG}5;7p#}^@4EwMuoVj*n(-&gMj`^^wPHskk;`)0 z0Tz|{KmHtv7=xv@puc;WOzfgT{Bl& zxH5wEt~GbyQS*A_vA%Brf)a}0uCr}DbGwrgP%m^pp=T7vDYv%>S*Rv{7x@c~D%=SR zsh*u^Mn}z#RWEtXev&i1*Hw$IG4{3cuWer6k(pDo^;N40&Fke=b0-)}Y@vMmO%vV} zhk81FPd*i>b{f9$u5<633elxCraR^0BhCD$`>u#kA4{N6p-(c!P9XC#$^9N|w%XW> z?SV7q=CPLkfA?+v*jp^$&FM;&!9O|UqpRZ*mD17W9vj4<4_TJ7Ff zFHC5Knuedd3Sfzsd|*g*)N)_|vUy>&87)o8ZAK$Q%EKm)8FJd{TJXWsO?Z4EHbq0n z6Tbj`wIhEZhJsUH0 zr#~t4Nm5$;%_%9USom>AyikNC$san&@r8f51vL$t*nQuoi9(6qW5~G_q$GdW|9+0^ zzEH0HB(7p%NWNZvZnkgp^w5-f1SQ}v&77QTise&%u_ANlXtfvMggd5^)lKbNr%X(N z?6I(elH#VCLLZBYlpp@4y!<*pb4wX~(SanAe^>K*6zXjpe8tM1=b<{Redmo`tA>dw zV%D{^EH5V0^*kj^=aqit0<(FWL=2_hbwwukm}lr}pX=`E^4)?js`F6; zT6g|YcCKIx{~)D)9%STi=5ZtKTd{_PG*bm)SioOG%BLw|Hp^5PY`$d{jp9ClKSeJw z^1rGod2x$xrS z_t4MRUMPKoFNqPb{^+k~%oBsu--?*xC~3gYH(QI2&NMzr8A{1@ke}=4-(&Y4C^!k1 zm+l+;IJDew%IGbUcA~}o`Kgt=sD=K|8CcD}KSq|{!P)(I4NnGjHKT#1P4OSIF`gg0 z+-+Mu<2(HV-Uuo8AKY~E5oicIeeGAiaXw!#;d-x1X{FB}Un^lUQsZbdI1^K^$`OO| z98PM_i44c-W|5qMuU+BOee3Q#&8avRZ=qQJb|BFBp{H`RB5}{FPv(}oO@klgvxZ+b z_1-r9pM9P(z3`%seyXJ_H7AGWPK^4kZ3`ZSLKgYF-|7cqHC_0{eM0tj53I{OU{&fj zmVAA{OsGk;S7lB`aN<*guAo%xOybZq(xkArOYZ`&Z(X~IU0|zxThrw7%ICW^awqq_ zvWq`?VQMg8ugHSTWazsb9o_erSzAsA$u?X za#%wYio)O%eZUUL=4CFC!vz^9+68tWmiUstw<3DwTU+JACn&&vtuR;70A9@GbdY)z1>bSSdIO6>U zC*R?bebeRR>0X0dTOxiN!-kgrF=NXMdEZCJPgclx{|4_?akeuJno%?b-}*N>zJd+x z?4yfRp0&fnvE2~;6833q-+o%^Q9CiSA8{Fuv(Bod9~Q8-o=0%Z0U3MTlh;cO|81V4 ztAo*G=ZISPzmcbrew?|hnc%aqv%uo)`I_(UOLOmY25N6t?RoX)cGcrb!>ax=K{ftr zK*5*BiMAhD0bTPVf1~4ZG)jBbPc8qqeAbzYb=3mV3gss8fD>VOw{$h-AoM&aK7D>|7oQ`KiU7);V285&Wx39YsWqjRP~-$ z)XEN_f+_w-X;XYo28Ez|&I4=M7Q>JO6>|~p7`+)VbJuR_p?s-q*OLQnv5ZvjGcTHs z8Q$)Bb33fL@1~&c?pL&+nP`*fx7QhRi31@SSQkUv)~VZ_)TxSLF6&6e^MSy_@sDFM zp^XDmqg~O}jwj_#LKT4vw31p!kea_U7^?OmIMPRoHA`uzRA$7!|Jc zD7*m{G^c_V@36D{ufCK^Arx9|FigJKGJ85Zf1eKOBRfYWxyQ*|_GbShm4|K%KCB+a zcox}C^|kuqL}^#}pDzoyJo$o~?%l-=kivur&HJZ z_scsc^%R+N9PCsd<>2gF6NG3DpnTescf$!jn1Me|TYP>J4)c^i@UMX~#dC;os)+&5 z*MPNIiHLWP8##7|wzJ%dR=MSw)@9jaIXiL({J}P><_uWcO<9?9m_4}olV>R{tdn}L z9CprS$jd#>akp!2&Q&&N8IBgY_$E#wpd^+i;MPHDFg7T9lq(l^3@07YzFBk8D{et? z4m8juZGF%vGgmW=&JGR0A#KI_z9rdp)#Rik^(frc{207KI~QyAW6C`C$PY$Nh?iYg z0Oh^rUSN%mFr=1Nj378| z1_1#PsLCV+Ql_A#iXIhoM?qvrZ4`6|8AL%6Afc!j5fCu~LKq}U7z`MM5JKR7Ha)%H zd;fuZe>pr)eXNfrd%y2zSf91lyEo!!1rZMQq&0)+sF5mi`5UI_zwO0 zoh$uL+mh%YYy?H7Hc)%7N_<@u8=rU?E4xYKw=h;*Pd01?+n7i^+8?&C@ksxWoV|=d zxT?F1B3M5vYe)0oS%hgN4rD^8WC+!#kPo@kZWbz_EiYl&kO>;V8^6LZdy|A+Xn)!)%{B3d zh)8H1mg1B|G|AL1j@Fc6XNPx9N!xmRfPz^q0v+EX4BiFlVv;~b^)hAag&&_cM42Rn zA!;|oKautv+SSCPWg0eNL!cja>jr}Q`Cn{2% zAS~0M{t}{h`#CIo6++cxB+v97gaEoNWBh=o>Wj;b3ZO?rbL=x!`GyYjyCMs^2z6{K-GZlUg^-_jV z88L9B$K8xv!y0sauVZyKLjGYNXVSCAE{M{?ESg$!1FF(k`(jr}iy;v7zrpVt-^~}u zTh&nf-GQ)kaS9h^k7>chUXBN;I*iV8A|GyiaP{enu8D?dWU7f;a9MJu%`AFJP3o!I zY6f{Qycb>^^Nv+l$kCt%qO2zpcDcst0BB5`L#G0L_D354bv-TF*Ue za7Oh39)%ioiCbZf*!shdUJ&;n*=vdQmWXH&oqH(NwgrFw4VDu2K4le9*8;=HC!uRqWMIiGuDi1 z=w`+HhcmOL@r4I%|XN(1QMT?xYxK=|!9s1CT0`Uv-c`ax~%@5Sq0g5$Fcqh#;i}ys6=!(zT z&hdE{A_5z9^!6h#FIC|m^j8t7KLrnN34ifoUTi)*szEs645i9D`=-_$ObhwpGdV0x zlY~B)Z>Ac>>Ob06F7H>_K~s0eMpjWXE-1orBnrXmxDndVJQN24L05pM8C*@;D=}+F zjGLdnBc*Q-0+6PhP{&F;X=wdBW}bbdqAz57mB3*2ViKJgLg#&KB9165=AHpoJYn{} z*;2rLu&pNFFEIDIY`)6&_28V}dSQU)6~!@ClOlEpz}gc(%`}i~^odK7B=0tiHRnJi z-ojJS^*q2u;}kLE%$c6=mqN}QyT*L0p8S)Mt}mtacTdr&GvasIKRBETX>qKw!!jy?alu&?&j$+J4bn)WJx2)1@l> zb^~(`LHy%sPcq2CG(z~2Wt6d^Mw99n($8#v49nC$k#pe1edb>q)yw(6Hj4P^fW`+r z*$N8sB=FkK${sCCu|NiDcLfJvR}ToT{2ZffmY1m-8; z*Hbx2$(tdaC1box)TtRFOM>=y+czpTq^Sd3(YtOKui zKQ%FX-*+c{FXY2w_*K7zu(X<}z~kl;#!Z11ExVdy>M4rb?lQJocVGQ!!|6sfvnQPP zN%2`cZua3&U$IXWc0^}Q!4@)sa6$>;UEK6SgOT;|^4em5#NSTXe(1fjp8Eq@wEv1C zdNjB}=fSo|*AUT4D&siZ6l68TpO(4bao9562%Z=AO=~YNBlLe!?ej@-)7S77S9SXE zeOu$L#nI1|yk`4`ji7SVX0F__0lYFI{u|o&9PCXkZq!2O>S3^%0OVBmDO5*y z=-`BDKn9B~<@k+`zyG^4(>+_wL9&x4@qo7l)1lES z5Y)huz8U~fgpVD??;dLvnnCf(lE6cH#>zI35_aN9<#1v648BgR;O&|MC%YsX;n_>H zgyl!aqhSCp4yqi$7MMs0WnYy5{nG$pzHC1da8cs1oxV|!&z$=K4ImONon7^3l($Jm z85x0`njjis(hIS1jbX!s-=@!!qISvaNaOzM*^_RK19`s`1bBGFJWe9N*RqV?2x}8 zm3qi$W%GjdbLtl2yj{ldZ@hOB z-u>SIg}gDMzp>pqI?#Y6S;aP5C7B?lB1qMU3|wnDmyEUNdYsxXgr10-J>9m%cWjKb z$yNT0)H*DyS|OvNG{sr!eA3ix020^WM%f^{xHxq@9<0QhL7L3z4(9G2lhbCV7qcJj z0Lo{xtKQ7<|G95HD9{!j-Qk<`7?nRFDN(5Eg3SB(QPYHQRF$Efb57=T8*{o667dID zEhPk2?H665M%r_Om)F`0p(lsXQ=@6mPL9Z`j1zp(A`%txHxl|YljG2&!#*yYye6nm zV$F)Y(d`)414QXw#A^o>9HO3NiSu7Y^X^h}QT>(eZfau!ClNN=s|UU;fXAmn?8%qfWWnd&jV9YB`V z-NKGS!L5$SipwAJU)&I27X;V2Q;?*S_q6gdG44JT4jR=D{o;*uaiiO^Y$qnKAaTI` z3)~-2QP<3j)zSg6x?+)DnTDwk7$U7X{Y#Q}Y@_%m5%^4UECVpr7W`vpQP`8|79b37 z89OTPZ^cF?QHQO<0U!nRfr_cb06-jWGCSTsgi>W>^3u-Ft5y>&o(FSPE7?Y!pW!@~ ztGyHbNN$;;{XMjSPT$25m;$)Qc`5{I(pWR#;N0Sj4>O~r4t?>TpK=*!{#FAw(b(hWc$7f-UF)t(C@7ba4BEf)J86!kG5BxM`g z3Y{P*YAsQ43Yz*P!%paxkE=df z*^z&{01a*W=c$89RK2Zy7(p@~q@WvMa4NG|WXu=ovqNWfH;2!hyn=CE&;X#Ymn8jw z_|s_*S0#zyqs((8JQrXg=?y?#&n43NQM6C=LLfw%(UO}z_plO}G&Fl4luoDI<54Wh zOQC1Owd?;I*gy*bYQA@oHN3l4U)eN6cQSP`v5Svep~o7100guHFs~L#EDBU%CseW- z*2H!hCHm*Ac(b=07tOH~r7)WXWlSz51bRRZNxI7onnQpc9#by{pTz+LL5oZQ6s^*J zoo$@(1W@VyhguYB%p}Gggc4cc$@;tm9_!pN|9}VFV*O*7(3~so4Yom){EE%ak*e0h z(rPdWgwC%ZMh=SYgkQsEbi#K>UkcCdI^VY|26XkYE@#fjG$5(xqs%-f@}V!B0|f{D z&4e$k8`T3+!f8M#>F?t35GX<3a#g)4z}+pGht30}vLsEj%&QO5^Tf@_)DsH`qHvd| z0eMPF4dl2y!Arpx3RUIDk2W=~TrB^_fntcnY zxRHnx8bFB?8lV?Am#r`jBpM? zvZ+w+v)73Ke63&cy7OUJ&CfK!_W}Em0BiEntNAb;%r(MFz`m!;U}N>BiAda8D4-u} z;p=Tnc&!F_O>zKaIeq9XC(c7E`)a!jtno}2CH1nTkZt76GX*vZ$kR)@hr4V(8u%jV znHhx8w|D=OS*t^Z<2o>xPJbc#nwi`nJ!9viwIf9VN~8YJr=SYpWb#e~HpK?dg`1Fy zm$BLFB$rHCV1Ry(vNtl)nFh04yC55n*A@>YmqLJ+_xxaF5%s9la-uwxXZPUbcE< z4B!-9oD!c^C4`<$u_qrgk>yKF;Oc*gpr;<9eF9PV_6MhE3&utv0_P4V&XORl>hZhS zg^e7d&VM6}bEzs@nQn|1K??csa-bky*w`kApf2ZOiGrxkUjX`UKFvDC!Hl(RWktr3 z9`gkl7px``mE=RS*FkGdfII;4su}L%du-$hNoTEi^AlHY_ooN5-qlmN`9SsvK=2`u49!rY=CixTwHEp60D2lAndx{DyP z7Ycyp$Vw^z=tCF5)+|_3F%Smi`og35%$u_^j10Qis$U*UVLUD;rTQQ?Yl%z4??XTE zi=+{p*USvO0094R12Yd4trLc~Fb=?w?HscTz7se2VvA*ZKZLyz6oH>GEZ0;y05PJT zgzLJDakr!V7TUvhbJc!S73$-1N&nVgp}J!F87T}3{L*X>c^*`gw)KKn2VMv=gEwWv zIsh>hs7q65^CYX)qql0t}HukfiDFL+C_eIc$0hwJw#``2B+IVxxiZ z_PZRgQ~n4-=D^6Qe`O8Zpgo{LY0Np(7rI6=Q@<6a>V$WB(%t0e;IAaThBc01lQ!M@ z&ZAPt6Y1b!0Uq*Rx9-lYIfD!DCxm$VpMuDQz=vVH%sH#(C*|izoDihNz^U;8q0e-h zFJ5zP4@2)D;JNJ#<0w z;?mq)6lV2`I7ND^%9N{0MdxOIFNlcuh5=Oc)+PNsRFz%wKOm&vJ*Lh#?fE1Z$PElj zCjonxx>FwKGp(|$Y(I+^vdG0{X+11lFo=ZsXxykAz%&t2739TjjPZ?d8OZw6@a%4A zNLuEr;jI=JkmR3IODt?d?uErGeAq^||FWLbzad)tzR1)LfTPA-0)0WPR2yt6vx$3_g z3D}Ed?uz*Vg0Zhs-GwYag#O3P-GCx(S=$&Eo@x%veYmGuNY7dNX|osiI33)B~n?er50mj*AzO}wD&Xv8+k>Y zcdR^XDg0ALW_CU-|&y=k*U497z8#MBABv)llQ-Sj?oZvk0Dah;gH+OF;DQNM#)(DU1Ion`K{Az}R zOkFiLSVk|wxQR$g0ODvbOlQs|pryNFW;!i9b)yUb>3Wq~-!K;&L)8H=F9Z!B{G{P+ zEH@*=mV(4}yXlzEdB2NoRSqnl6yA*ZE$Lt(D@XsTtBF$R*6FdB{dZS#9J$qzeT9B zB5KkVq;qRb!pdR7Vvc)Bek-PM#V3tW;3FfN1-^(s8=K!EOf^>i+2*J@ z>|yD6Yg>$ngsYo~sJQ9_couLC!}-m8Lqcse53oH91mZVZHpwNU#>%Eey{GqP?lsOJ zug*-U$8^}(l=#i$_xX8}QWqBi;}&Lrcx|}fuQ1m?y*{VKRSBu{@s4)(Zxg8nAow!5 zsyzKSsUFMYK9B-f!?$}EZesx@n6FOcfn3q0HFOTvSAy9bCZlgIs`Ux2B5P8iD^`OAVXSM+JUS8(sS#fUHVbdDX;z7r0^O;wq)Bwb*=VMgO>+fbBkjCBR=cwkq z;nQ1HY~XRE%dLnXrs&AjPwZfAU|w9&j<7^q$&-4!Zs`qD>gL700|$ut>$l9gpHZ*< zB45tP020S++oSmh7U_xbb0oD?gIvjS4buo7z}Xh*XWms{K|uLCW9~q4kYAJ?Yzo{? zPjwKcyfJB2CL|+7m>2hf1*TJ8VcyY1lXUQnmHUy}dzgU_(zhRB+1v{Uq1YFxx`T~# zFbalwg^(Xhexfq$+ArOza5}o=Rk(-C&$2Thm#V>&mOP2m1qUq_cvBPbj^xTGj%ZE% zR%ux&jKj#3n!|SPyLM#xvyvL!go7ykJUDs!@+33Z>^Y1(EWO%V`I^MY6s2Z^r{ys_ zx3ZJTQ|)6LLH~7hOs@%~Hvm6bqSEmuTG|Jr(av53NjI%NiXJ0XN1|*? zt#4Ac6`EqDM9RMYz?yG_VwliA8gbaa8+M8v$)Wr^8I$Bt80*%|D9`I0w$u4i|I9H{ zWj8ik>ee{rz~*`QH}*ZiBD02*M#apYZ|%8v?(83glA6cceI)+a5b+L?< zxiY8Y`*$P$n}o|OwQ6S>(QWP?a&Vau2#fSAy@M)uFJku4@1G43Gd2rB{e6=%lhoQpv?8{D zoTw-2wTZ*LM5VwFfcVObK{xr?y)fN;%46HopFj0s`=ny&^AT*T`c&eLhXKP6mIv0-mW@!7| zIv2uK-oK$2XrZ}q=}2efaK~y{-mvtFCQZRVpV6g*#ATEC7Tn-=PZyuf2`WJF^kFxb z6!D-#s_Yp?9^d+Vg;%OL1cg!gZf-`8wK!!b;%MF;f57~;F*CV)}=>>Ioe zCH3O)8801g{Thx*_0Y%^-!tv|agP{zSgm{WmMyd#(db=H$5u4C_c4||u!*OFNga~j zALbHtVN5o3J|oa998##yJx0~GQcvDS(&>2^^->JIk?tXH&Ye+*N)o9+mGd;{;Xl&( z53qu3li^d#*0pAyWW@q%)-hIQV2L|W)A9C8Y`r~5y0LPAl=)PCIt4wlVL7Y3)J3!0 z4-y7TJcP2~jVI0}+{Dd~Z+|$kooG6DTPM211WRcQD8q#Y*mVJYb&@;<8wg@AD(CPU~2vKzR%og7RKF!rZ~Pn+N9qt zZtamobpmq|(?=Q#mn-tmLZ{yYiFKXVmpy#?69RMLDJ}dn*64OBD31Ba)G?0YnD1@O z92m-5;CV{$K#NSOAN3cvIROurOH+JHod@QGcbiAKCTRoVNXsejKQW^MaO@iIj^e}d zD1b$GoOFV>I;hs96!Jivkkq}|)D!0*DOmMo=!>f;( zBt)a25p3X$tVH6{S5j;^BX5u?B{GE|Q%1JyO9nyn zYd2sX@m~6FY(O00`!#HXYy$MHWyEeVaPjpA%u*U!zd}K(e0VyKDOn;;c_TQjX{vk* zj;qeH3m|y}`3R1m1^)=*Xd#>&ROx(=Iyk1CJ{~5u=5#cmd432i1z3v@ueC@BKy^aM zPV$$c*1U6JiP57y9YIv{4?d+$hu6;6`4w*K*>ISAU<|_})X!dcAJs2EncCtX*gq6o zyG2;*;!>8&Ro%d5-;y$k;-yI+M@Xq#@KzlHOyowFqx`Lmv zLMki4*jA1M$+s_=`AUI&slk297uB!Cn~E{Y4tX%!mryBiwTy7$%}X!lRJ#?P?6UWJ z969-p9p^D1lP|00@`iY zF~FMF2d#(;8mdEEH-2!=vF81mH^-p!kK#O5QxHfoe^i+$?|=ZRjl`Y8XAoiix4AK% zs0!kW?c5>~d$`GU00%sevm#nmO@bxOO|R17(dQTEj-Vj03f2;VZSJNix^gnMoJwgI zZ)*K$_367eVyXd<{;-5VVQy@;%kdlqclK{7<+ro|y#t|?6++DY;44Eq8<;}2V(=tI;bE&7QF zgpIhv>`1{&G0de)j>#*ibzlS1s5KEbXxikJ%I1GYRga!3Cnq0Z2cGVJy~(ue@yRVG zb|r!D!<(s_q1}0?^{k?9tU@$YY`7Y{B>ipTh{-hsf*y1sZ&c@SBKf@d{#E?Sq3Q$w zo#yU=%K@9ol3!_xNpwyIQxvV5)aWT1U^`EDkMLWQOu&1#>cl0k>Vd5+T|DvYUruMk zw3hJi=@cVjIorPf2mNfxdb~_}gFGwP3%A37B#?e#yB~?WPfBG<$^h_vd@f_CrDv8+ zETDss8l34GZu;eU?2>u_X-zmHxkI6e1N3U-;I$K{Cb+uQ7A-scUe^18N_(N6Bv z7#dx%7)`h{>!18~@dBCrYf9Cm9*ogtZVgTytHIp3a0#l0k@k;tR)Iw7W^-nSZ{UZ} z!LIYUSZzpU9EFY8!3n0Z9J$N1>nU^v73UJcL$;OttGFuhmr(|_#lUwdYf!~gnJwOq zIo!^dHP*s32#;%XM^Q-k@@=NZ)lu};cYj9G`J08UkVZeG8{-Nyych*Gyqr284&M_< zF|-NG+2rz>|8Fv<^}<*n-dBj#zmhUxSB5@FWsIkIb}`C4|7`z#_(r6~&bif?@>bueqP%lWz@IIPiFI2x=e+O$Lkk zs@WEd+bTV?3+NRzIR$(#RfH8EW2sIK*k1 z8|9Vu_Q7Zs4g8Y-)F0k=nq)Jzq$)5A1yNp#P0wf8?iZ_j+!huF7r><$%mLTh`dnb( z++wOo6$?>o12=J1rEh*Ay&JUQcCV|iPuN>{GNtp3=+dRWYe3SNZG2jUCP&cuV4Efo zaBaBkTyKHIzW9~@jr{J3eMsLGi3-fFoqaULJ{Onbw${c=?n6V@&0;4i^ zEi*Ln^*c7(xqmm#TPbl_b^0WU0+e<>SM`wu5HEt(v??(g5koyFEH3!5?pPrUA)LFL zn95*1*xVGax-Oe8h=^jC3e%wrY!s)27iw+{q=3{Nr{G_}zo-@D>Q3V#6Qu?xPB$XV z8e%Pk>0l)Ub5(e!f~J78O2xi({(4~=G|TG^sKZSx15RXoD^_D9I#oX3W=6=ZeEoRh zc^ff$$vl_Gf(lmx-G;=sVAgtx7C@$%O zYYyKEcj2?_S3Fe!7=5y3jBuO`f|o4iO3ngsxQ!ptai^}hwe(!On4fhxJ41HZre=eE zWk}3LQxmYhf0k7Qo)2eAao=rfT{$l$Lz57A3=i^pA8N*jdsVbLcK|AtN&z2{&{Y_x4eFut^n>_6} z&2ZW3*`Q`Pw~;Ah^x~E0fd#}}wY|Mi%~O@ay^!~d-lGnQ@HP4Qk~(pUe6eF1y2tPz zd>+F9nF@HQa;jKePYSps`6+E5!5ZUv?25@GMXePtrPlFz0h`H(fO>XC=Y&%2tRq`L zPPTtYIOjY$Poj^=QuT;S!65fYpkom>L{b*W=9}8qhhOlCnz;>QR6-&O$S^2#L28@V|^#zg;O-7l8!++~ZM) zbs9|(Y#H&)F@?6g;^S*l0Gy8@;$Nx>(xQb|7|D*yTL`CpRt$XVMqz+Sb&E7hE6E>C z2TB^G@EQhQIbM+X5Tfl|)ic>VPCmHnI_!Z^hds*Kn`92%-e;)dE*8qpLi(`r|6(MK-n1X|GxwKq*@=>KQdTe@dM;SH1CK zIvk^Y(j*c`HQ)Q`V<~87_nopNGO_x(tn$hV!ig?`5YL38micToyj{KXKm}e_I^5)5 zKE17yY|{jZ^h_I9rDJ(VYiNP%j+(+*+M7J!nU0}AvFy_vBEUeAx)HaKPqvwj6}O{3 zJYH%X?a5vm)a8t`S{qhC9i%JTs4yOVn20XPHm#1MQ|Ndmj)`7GnlGK4I!~fYW%LZ~ zlX17P1}4}XhAu>Ql`)w;MYxaTt}&R~8gM?+~=`Gfmim+T#*RBW&<> zXcFN#4|Is0nnY(gkWb&SsnVi=726%xBX{Fv_hF@XL~Qi}K#-|Uj0@JZOP-BiB>SKN zJ7eDU>z}wFmGyz#){(Tms_d(IvQU?y?f@4?N5?M}0%gG@8a9kZ6zX{{^K9tv( z2qSo^4#(dDOIe7XB`EKu@PT%4$d_#Dd-S;K%Pc$Rb5nr#r?JxmUAM! z&xyn!t86k=qM#6nmz8}kR=;rbZJCdc{>S80J*n@N7#d!YOXEg=I^Rj?A1qC;_w1G! zTs~6v@eVtw^J+Wl;2?a@Rn63coxIE%N=L-hJyMl4(#G0ubwwxaJ7`TsppVY{h-yNO zI;68xK8vgJu2%k6{cB`Sbv(XRh7NVEs1?oYA27#BGL0!BGdLw_CTV1G^f{>5=_%Mrl=$Zbcp*p zap@8gPE`**byop8{H>d=yo+NOK9k@F90in`|uA~keawu~`&bYdu!I&Y$p8egBwoQo;c zTNThH_867{da1VtG0HP{SyY~633wcA&o+~{3ZV)l-*0OS6XuM_thg!{ORb5N7@AJr zvHM@Yv!>QWJ|S-#Wr65?45-V#0#$F$M-QfEx3rGcTWh6zzLLTQ;h2 zJl;^k!J14Hwxg~WBG>c_#cFt(>pS43uMTUNuts&Tk<+l}2%UeMH=ciFVu(t0Uz7K+ z9(eThkhQ1lj?fqO()qE`UxnY1IaTl$wg`7wM%3TE5L89X9zK(GEMBpUrJ*(sNj3=Q(`9WF9W zIDqov6?eF*O%6NYs4EIW)?}NQH6rVCi^(SeW@5n= z?RHF%{cen;cpvFvY;cW5ee-x>i0hBrH?F41<`2^s4x+gx)xL$MOz|g%qk}o8S2ETK z?{ifdVs*E*QjiGngsUo`mRym9F>8)Qf2ld%Of|@n#EVnZvU#Pz(0 zf;Ylq4gYdQlT_XNq$J1Iw4E$7HBqW$@v)b7bz=CW+UVv%+SCSiJ7d_05}wB$jLn?$ zLS4_xx-2OHNb7rB$pHMji#&}KWH|^>EKuqgJ*`XBV&1@``K(}vPa>lx$D#j z$zw-MIGtrlKE1q(JUi-6jbur*sgd~-)S3PG7kc49Yjy~?a^^szcqu{93kD+mO1D5q zb<_PfpCmLxdn|>!toZAH_c+YGJtY^7-i(Y~BDf1KHmlmA>gJJ-P3|#|uJ2I|LcS%! z;lSo0Y<(vUnmtu4c>(q zygo1~gk113D=pS~Kus;kk(A;iR%00*L2^zr>PYP?nKr()B^DOP@}V$gkQz%5mNwIV z8~ogv1E-}L|5{%kK2Ezc|EPLGtDi|6O7M3KX03BH5_VhWXR9BJN*lwtzl`R7N|;D3 zj=Y<)2h}sFhQpu@q>kozgV@BxLcNf2-`ZZ455JDbqma#YuBM6h1llxzbR54mSpy^8 zB(mGa&aXky`S$d@E`H?tTYfh7n8*iQAvn`*@W{5`L=Tsi5t8e%f@YTYK zx#;UF=E7f%3>S<_tQsg@EwtpvSDZ#OH|0vc%(4D7UqwDNH9A0TdAg;hofF{Edheim zFvt82BYBFfc=J99E{mq{QO)c8gD`dvBRjNT7(a$3SlQ^ae6ANu$BX4YGw->$ z==~^V3!H?4g~a~@*B-%wj>KI_em8R?msI^>Bu=!Antvqe)hG&lpD3rqBjxAaI`Frw zDlQzGhSRYG!mH4@@S#|*QgE|oH{Xj;l5;8v{${MM9lu;5xrDxc3dx);ymx(~EIBlI zr#$EV)1k;7vRQj&eWr=h+o@vLXsz;Do6C5G&vLM_Is>PRf2j8jnbWsIJ+Dph`>Pjl zXa3(m+IjdP=f8fguEYGguBT8`U%|r*RA0HCpZu1ME>*VC)U6)_MhJqtN_UT z{rk{|0{o^Hf1aKB?-%@MDgL`e|5=FtpHUP8QfXyxIrSh=Z_9w6{ri&ozxNvh%&|uN z_a6}S;o#oiq5j{n|8HgZD=_|B+y2gp|HtKDzOZEM=QP2tzN{2{C#XNU|5)-v;IID+ D?%u&q From a36aa730e261ebe7c15f314b694dee27cd7db5da Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 21 Oct 2024 14:39:34 +0200 Subject: [PATCH 241/260] Add graph refinement options and remove unused use_counts --- conf/modules.config | 4 ++-- nextflow.config | 11 ++++++----- nextflow_schema.json | 21 +++++++++++++-------- 3 files changed, 21 insertions(+), 15 deletions(-) diff --git a/conf/modules.config b/conf/modules.config index c8350e70..06d17e9f 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -193,7 +193,6 @@ process { 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": '', ].join(' ').trim() } @@ -222,8 +221,9 @@ process { ext.args = { [ params.multiplet_recovery ? "--multiplet-recovery" : '', - params.leiden_iterations ? "--leiden-iterations ${params.leiden_iterations}" : '', params.graph_min_count ? "--min-count ${params.graph_min_count}" : '', + params.graph_max_refinement_recursion_depth ? "--max-refinement-recursion-depth ${params.graph_max_refinement_recursion_depth}" : '', + params.graph_max_edges_to_split ? "--max-edges-to-split ${params.graph_max_edges_to_split}" : '', ].join(' ').trim() } diff --git a/nextflow.config b/nextflow.config index 20d752e6..fa151d01 100644 --- a/nextflow.config +++ b/nextflow.config @@ -37,12 +37,12 @@ params { max_neighbours = 60 collapse_mismatches = 2 collapse_min_count = 2 - collapse_use_counts = false // graph options - multiplet_recovery = true - leiden_iterations = 10 - graph_min_count = 2 + multiplet_recovery = true + graph_max_refinement_recursion_depth = 5 + graph_max_edges_to_split = 5 + graph_min_count = 2 // annotate options min_size = null @@ -87,7 +87,8 @@ params { skip_layout = false // Main pixelator container override - pixelator_container = null + // TODO: Remove this once the pixelator 0.19 is out + pixelator_container = "ghcr.io/pixelgentechnologies/pixelator:dev" // Boilerplate options outdir = null diff --git a/nextflow_schema.json b/nextflow_schema.json index 24a2cb05..8ca85a06 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -210,10 +210,6 @@ "minimum": 1, "type": "integer" }, - "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" - }, "save_collapsed_reads": { "fa_icon": "fas fa-save", "type": "boolean", @@ -232,13 +228,22 @@ "type": "boolean", "default": 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]", + "graph_max_refinement_recursion_depth": { + "fa_icon": "fas less-than-equal", + "description": "The number of times a component can be broken down into smaller components during the multiplet recovery process.", "type": "integer", + "default": 5, "minimum": 1, "maximum": 100, - "default": 10, + "hidden": true + }, + "graph_max_edges_to_split": { + "fa_icon": "fas less-than-equal", + "description": "Maximum number of edges between the product components as a result of a component split operation during the multiplet recovery process.", + "type": "integer", + "default": 5, + "minimum": 1, + "maximum": 1000, "hidden": true }, "graph_min_count": { From 789abc78dcb6c558caca8f3c559c126913b9eb5d Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 21 Oct 2024 14:45:58 +0200 Subject: [PATCH 242/260] Update CHANGELOG --- CHANGELOG.md | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ed9f0a16..28cbc8da 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,13 +3,29 @@ 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). -## [[1.4.0](https://github.com/nf-core/pixelator/releases/tag/1.4.0] - ????-??-?? +## [[1.4.0dev](https://github.com/nf-core/pixelator/releases/tag/1.4.0] - ????-??-?? ### Enhancements & fixes - [[PR #111](https://github.com/nf-core/pixelator/pull/111)] - Template update for nf-core/tools v3.0.2 +- [[PR #112](https://github.com/nf-core/pixelator/pull/112)] - Add graph refinement options for pixelator 0.19 -## [[1.3.1](https://github.com/nf-core/pixelator/releases/tag/1.3.1)] - 2024-07-31 +### Parameters + +| Old parameter | New parameter | +| ------------------------------------ | ---------------------------------- | +| | `--help_full` | +| | `--show_hidden` | +| `--validationFailUnrecognisedParams` | | +| `--validationLenientMode` | | +| `--validationSchemaIgnoreParams` | | +| `--validationShowHiddenParams` | | +| `--leiden-iterations` | `--max-refinement-recursion-depth` | + +> [!NOTE] +> Parameter has been **updated** if both old and new parameter information is present. +> Parameter has been **added** if just the new parameter information is present. +> Parameter has been **removed** if new parameter information isn't present. ### Software dependencies @@ -17,6 +33,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | ----------- | ----------- | ----------- | | `pixelator` | 0.18.2 | 0.19.0 | +> [!NOTE] +> Dependency has been **updated** if both old and new parameter information is present. +> Dependency has been **added** if just the new parameter information is present. +> Dependency has been **removed** if new parameter information isn't present. + +## [[1.3.1](https://github.com/nf-core/pixelator/releases/tag/1.3.1)] - 2024-07-31 + ### Enhancements & fixes - [[PR #107](https://github.com/nf-core/pixelator/pull/107)] - Fix conda version tag to use pixelator 0.18.2 From fe74a1680a11d18d3e7f113541cccdb1a6c92f2d Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 21 Oct 2024 15:37:09 +0200 Subject: [PATCH 243/260] Ignore linting failure on logo --- .nf-core.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.nf-core.yml b/.nf-core.yml index c885ed26..eb1755a9 100644 --- a/.nf-core.yml +++ b/.nf-core.yml @@ -1,6 +1,8 @@ bump_version: null lint: multiqc_config: false + files_unchanged: + - assets/nf-core-pixelator_logo_light.png files_exist: - assets/multiqc_config.yml - conf/igenomes.config From c644129cc292738f06aa65742437d235f634332e Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 21 Oct 2024 15:39:58 +0200 Subject: [PATCH 244/260] Fix stray space and typo --- nextflow_schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index 8ca85a06..44bf0df5 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -239,7 +239,7 @@ }, "graph_max_edges_to_split": { "fa_icon": "fas less-than-equal", - "description": "Maximum number of edges between the product components as a result of a component split operation during the multiplet recovery process.", + "description": "Maximum number of edges between the produced components as a result of a component split operation during the multiplet recovery process.", "type": "integer", "default": 5, "minimum": 1, From 0e8759f88489c8c7432f433070e98506df7235d4 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 21 Oct 2024 16:15:51 +0200 Subject: [PATCH 245/260] Add `--max-edges-to-split` parameter to CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 28cbc8da..798073dc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | `--validationSchemaIgnoreParams` | | | `--validationShowHiddenParams` | | | `--leiden-iterations` | `--max-refinement-recursion-depth` | +| | `--max-edges-to-split` | > [!NOTE] > Parameter has been **updated** if both old and new parameter information is present. From c3591650a88643fd9eb9640b0f02bc920487d03f Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Mon, 21 Oct 2024 16:18:37 +0200 Subject: [PATCH 246/260] Fix parameter names in CHANGELOG --- CHANGELOG.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 798073dc..0f36d126 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,16 +12,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Parameters -| Old parameter | New parameter | -| ------------------------------------ | ---------------------------------- | -| | `--help_full` | -| | `--show_hidden` | -| `--validationFailUnrecognisedParams` | | -| `--validationLenientMode` | | -| `--validationSchemaIgnoreParams` | | -| `--validationShowHiddenParams` | | -| `--leiden-iterations` | `--max-refinement-recursion-depth` | -| | `--max-edges-to-split` | +| Old parameter | New parameter | +| ------------------------------------ | ---------------------------------------- | +| | `--help_full` | +| | `--show_hidden` | +| `--validationFailUnrecognisedParams` | | +| `--validationLenientMode` | | +| `--validationSchemaIgnoreParams` | | +| `--validationShowHiddenParams` | | +| `--leiden_iterations` | `--graph_max_refinement_recursion_depth` | +| | `--graph_max_edges_to_split` | > [!NOTE] > Parameter has been **updated** if both old and new parameter information is present. From ad31578b5fd0c6c259d060513c36d17e3ed384f2 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 29 Oct 2024 10:30:39 +0100 Subject: [PATCH 247/260] Fix validation check after nf-schema 2 update --- subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf b/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf index 29012bcb..82872b8a 100644 --- a/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf @@ -335,7 +335,7 @@ def resolve_relative_path(relative_path, URI samplesheet_path) { // Validate a given panel key if present against the (dynamic) set of panel options retrieved from pixelator // def validate_panel(LinkedHashMap meta, HashSet options) { - if (meta.panel == null) { + if (meta.panel == null || meta.panel == []) { return meta } @@ -352,7 +352,7 @@ def validate_panel(LinkedHashMap meta, HashSet options) { // Validate a given design key if present against the (dynamic) set of design options retrieved from pixelator // def validate_design(LinkedHashMap meta, HashSet options) { - if (meta.design == null) { + if (meta.design == null || meta.design == []) { return meta } From 3827a3eb9b3393e8f30fc4d46cbaa25b8b815b50 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 29 Oct 2024 13:22:34 +0100 Subject: [PATCH 248/260] Fix non existence check on input_basedir --- nextflow_schema.json | 1 - 1 file changed, 1 deletion(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index 44bf0df5..c7e57cb7 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -26,7 +26,6 @@ "input_basedir": { "type": "string", "format": "directory-path", - "exists": false, "description": "Path to a local or remote directory that is the \"current working directory\" for relative paths defined in the input samplesheet", "fa_icon": "fas fa-folder" }, From 0e7e0c2d188618539b8b8d8b94de67257f41c115 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 29 Oct 2024 13:25:11 +0100 Subject: [PATCH 249/260] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0f36d126..6a525989 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [[PR #111](https://github.com/nf-core/pixelator/pull/111)] - Template update for nf-core/tools v3.0.2 - [[PR #112](https://github.com/nf-core/pixelator/pull/112)] - Add graph refinement options for pixelator 0.19 +- [[PR #113](https://github.com/nf-core/pixelator/pull/113)] - Fix validation issues after nf-core/tools v3.0.2 update ### Parameters From 3067ff374a0ac172f3c41f7896431b2a7086a7ce Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 10 Dec 2024 15:24:35 +0100 Subject: [PATCH 250/260] Remove `--save_recovered_components` options for graph outputs --- CHANGELOG.md | 3 +++ conf/modules.config | 6 ------ docs/output.md | 4 +--- modules/local/pixelator/single-cell/graph/main.nf | 1 - nextflow.config | 1 - nextflow_schema.json | 7 ------- 6 files changed, 4 insertions(+), 18 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6a525989..831f1174 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [[PR #111](https://github.com/nf-core/pixelator/pull/111)] - Template update for nf-core/tools v3.0.2 - [[PR #112](https://github.com/nf-core/pixelator/pull/112)] - Add graph refinement options for pixelator 0.19 - [[PR #113](https://github.com/nf-core/pixelator/pull/113)] - Fix validation issues after nf-core/tools v3.0.2 update +- [[PR #114](https://github.com/nf-core/pixelator/pull/114)] - Remove `--save_recovered_components` options for graph outputs ### Parameters @@ -23,6 +24,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 | `--validationShowHiddenParams` | | | `--leiden_iterations` | `--graph_max_refinement_recursion_depth` | | | `--graph_max_edges_to_split` | +| | `--graph_max_edges_to_split` | +| `--save_recovered_components` | | > [!NOTE] > Parameter has been **updated** if both old and new parameter information is present. diff --git a/conf/modules.config b/conf/modules.config index 06d17e9f..a0e74146 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -228,12 +228,6 @@ process { } publishDir = [ - [ - path: { "${params.outdir}/pixelator" }, - mode: params.publish_dir_mode, - pattern: 'graph/*.components_recovered.csv', - saveAs: { (params.save_recovered_components || params.save_all) ? it : null } - ], [ path: { "${params.outdir}/pixelator" }, mode: params.publish_dir_mode, diff --git a/docs/output.md b/docs/output.md index 7d3f5d09..70b5e72f 100644 --- a/docs/output.md +++ b/docs/output.md @@ -156,9 +156,7 @@ When graphs are computed and identified, their ID names are added back to the ed 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`). These new component IDs are then stored in the "component" column. The information to keep track of the original and -newly recovered components are stored in a file (components_recovered.csv). -This file is not included in the output folder by default, but can be included by passing `--save_recovered_components`. +(see `--multiplet_recovery`). These new component IDs are then stored in the "component" column. The edge list is intermediate and by default not placed in the output folder with the final files delivered to users. Set `--save_edgelist` to enable publishing of these file. diff --git a/modules/local/pixelator/single-cell/graph/main.nf b/modules/local/pixelator/single-cell/graph/main.nf index b26b8e4d..4cd5db80 100644 --- a/modules/local/pixelator/single-cell/graph/main.nf +++ b/modules/local/pixelator/single-cell/graph/main.nf @@ -13,7 +13,6 @@ process PIXELATOR_GRAPH { output: tuple val(meta), path("graph/*.edgelist.parquet") , emit: 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 diff --git a/nextflow.config b/nextflow.config index fa151d01..a7ae9710 100644 --- a/nextflow.config +++ b/nextflow.config @@ -70,7 +70,6 @@ params { save_demux_processed_reads = false save_demux_failed_reads = false save_collapsed_reads = false - save_recovered_components = false save_edgelist = false save_annotate_dataset = false save_raw_component_metrics = false diff --git a/nextflow_schema.json b/nextflow_schema.json index c7e57cb7..40a3d9b9 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -260,13 +260,6 @@ "default": false, "description": "Save an intermediate CSV file containing the unfiltered graph edge list.", "help": "By default, the unfiltered edge list will not be saved to the results directory. Specify this flag (or set to `true` in your config file) to copy these files to the results directory when complete." - }, - "save_recovered_components": { - "fa_icon": "fas fa-save", - "type": "boolean", - "default": false, - "description": "Save an intermediate CSV file containing the recovered components after multiplet recovery.", - "help": "By default, the recovered component will not be saved to the results directory. Specify this flag (or set to `true` in your config file) to copy these files to the results directory when complete." } } }, From 2b52d0013aa2d58104a872f19f6890042455da84 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 10 Dec 2024 15:45:27 +0100 Subject: [PATCH 251/260] Remove reference to `--save_recovered_components` from nextflow_schema.json --- nextflow_schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextflow_schema.json b/nextflow_schema.json index 40a3d9b9..cc5f7b34 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -428,7 +428,7 @@ "type": "boolean", "default": false, "description": "Save all intermediate results.", - "help": "This option is equivalent to passing:\n`--save_amplicon_reads --save_qc_passed_reads --save_qc_failed_reads --save_demux_processed_reads --save_demux_failed_reads --save_collapsed_reads --save_edgelist --save_recovered_components --save_annotate_dataset --save_analysis_dataset`" + "help": "This option is equivalent to passing:\n`--save_amplicon_reads --save_qc_passed_reads --save_qc_failed_reads --save_demux_processed_reads --save_demux_failed_reads --save_collapsed_reads --save_edgelist --save_annotate_dataset --save_analysis_dataset`" } } }, From 37cc14d24e9ee6bc473e4fcde34c97842ef2c0a2 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 10 Dec 2024 17:02:49 +0100 Subject: [PATCH 252/260] Fix test config defaults --- conf/test.config | 1 + conf/test_panel_v2.config | 1 + 2 files changed, 2 insertions(+) diff --git a/conf/test.config b/conf/test.config index 0952ac70..3f5690d4 100644 --- a/conf/test.config +++ b/conf/test.config @@ -32,6 +32,7 @@ params { multiplet_recovery = true min_size = 2 max_size = 100000 + dynamic_filtering = null compute_polarization = true use_full_bipartite = true colocalization_min_region_count = 0 diff --git a/conf/test_panel_v2.config b/conf/test_panel_v2.config index 3e2c3d22..37875578 100644 --- a/conf/test_panel_v2.config +++ b/conf/test_panel_v2.config @@ -33,6 +33,7 @@ params { multiplet_recovery = true min_size = 2 max_size = 100000 + dynamic_filtering = nu compute_polarization = true use_full_bipartite = true colocalization_min_region_count = 0 From ed7e5a6b0c254064ada5aa9e19f373d925321c9f Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Tue, 10 Dec 2024 17:03:05 +0100 Subject: [PATCH 253/260] Update help for `--dynamic_filter` --- conf/test.config | 2 +- conf/test_panel_v2.config | 2 +- nextflow_schema.json | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/conf/test.config b/conf/test.config index 3f5690d4..76444d71 100644 --- a/conf/test.config +++ b/conf/test.config @@ -32,7 +32,7 @@ params { multiplet_recovery = true min_size = 2 max_size = 100000 - dynamic_filtering = null + dynamic_filter = null compute_polarization = true use_full_bipartite = true colocalization_min_region_count = 0 diff --git a/conf/test_panel_v2.config b/conf/test_panel_v2.config index 37875578..7707d127 100644 --- a/conf/test_panel_v2.config +++ b/conf/test_panel_v2.config @@ -33,7 +33,7 @@ params { multiplet_recovery = true min_size = 2 max_size = 100000 - dynamic_filtering = nu + dynamic_filter = null compute_polarization = true use_full_bipartite = true colocalization_min_region_count = 0 diff --git a/nextflow_schema.json b/nextflow_schema.json index cc5f7b34..9d6335ab 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -276,7 +276,8 @@ "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)", + "description": "Enable the estimation of dynamic size filters using a log-rank approach.", + "help_text": "Following options are available:\n- both: estimates both minimum and maximum component size\n- min: estimates the minimum component size (or uses {MINIMUM_N_EDGES_CELL_SIZE} edges, whichever is smallest)\n- max: estimates the maximum component size.\n\nNote that this cannot be set at the same time as `--min-size` or `--max-size`.", "type": "string", "enum": ["both", "min", "max"], "default": "min" From 7d6510e643cac6a5f62303294d97311161f73ec2 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 11 Dec 2024 09:20:35 +0100 Subject: [PATCH 254/260] Remove `--min-size` from test profile --- conf/test.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/test.config b/conf/test.config index 76444d71..d9683271 100644 --- a/conf/test.config +++ b/conf/test.config @@ -30,7 +30,7 @@ params { input_basedir = params.pipelines_testdata_base_path + 'pixelator/testdata' multiplet_recovery = true - min_size = 2 + min_size = null max_size = 100000 dynamic_filter = null compute_polarization = true From 55927f8f4c44d7dfd89099850ff6ad987f12c4ea Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 11 Dec 2024 09:20:54 +0100 Subject: [PATCH 255/260] Fix linter warning --- subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf b/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf index 82872b8a..bcad4b4c 100644 --- a/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_pixelator_pipeline/main.nf @@ -100,13 +100,13 @@ workflow PIPELINE_INITIALISATION { // Create a set of valid pixelator options to pass to --design ch_design_options = PIXELATOR_LIST_OPTIONS.out.designs .splitText() - .map( text -> text.trim()) + .map { it.trim() } .reduce( new HashSet() ) { prev, curr -> prev << curr } // Create a set of valid pixelator panel keys to pass using --panel ch_panel_options = PIXELATOR_LIST_OPTIONS.out.panels .splitText() - .map( text -> text.trim()) + .map { it.trim() } .reduce( new HashSet() ) { prev, curr -> prev << curr } // From e0ecbe91c4360d8a0b38194d172faf375ae8ed2f Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 11 Dec 2024 09:33:07 +0100 Subject: [PATCH 256/260] Update images to pixelator 0.19 --- 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/layout/main.nf | 4 ++-- modules/local/pixelator/single-cell/qc/main.nf | 4 ++-- modules/local/pixelator/single-cell/report/main.nf | 4 ++-- nextflow.config | 3 +-- 12 files changed, 23 insertions(+), 24 deletions(-) diff --git a/modules/local/pixelator/collect_metadata.nf b/modules/local/pixelator/collect_metadata.nf index 0817d806..160810f4 100644 --- a/modules/local/pixelator/collect_metadata.nf +++ b/modules/local/pixelator/collect_metadata.nf @@ -8,8 +8,8 @@ process PIXELATOR_COLLECT_METADATA { conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:pixelator:0.19.0--pyhdfd78af_0' : + 'biocontainers/pixelator:0.19.0--pyhdfd78af_0' }" input: diff --git a/modules/local/pixelator/list_options.nf b/modules/local/pixelator/list_options.nf index 880be7cc..17fa8b22 100644 --- a/modules/local/pixelator/list_options.nf +++ b/modules/local/pixelator/list_options.nf @@ -4,8 +4,8 @@ process PIXELATOR_LIST_OPTIONS { conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:pixelator:0.19.0--pyhdfd78af_0' : + 'biocontainers/pixelator:0.19.0--pyhdfd78af_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 87b5095b..3bdca1e1 100644 --- a/modules/local/pixelator/single-cell/amplicon/main.nf +++ b/modules/local/pixelator/single-cell/amplicon/main.nf @@ -5,8 +5,8 @@ process PIXELATOR_AMPLICON { conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:pixelator:0.19.0--pyhdfd78af_0' : + 'biocontainers/pixelator:0.19.0--pyhdfd78af_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 077fc1f4..9320c854 100644 --- a/modules/local/pixelator/single-cell/analysis/main.nf +++ b/modules/local/pixelator/single-cell/analysis/main.nf @@ -4,8 +4,8 @@ process PIXELATOR_ANALYSIS { conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:pixelator:0.19.0--pyhdfd78af_0' : + 'biocontainers/pixelator:0.19.0--pyhdfd78af_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 48e97194..7164fd38 100644 --- a/modules/local/pixelator/single-cell/annotate/main.nf +++ b/modules/local/pixelator/single-cell/annotate/main.nf @@ -5,8 +5,8 @@ process PIXELATOR_ANNOTATE { conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:pixelator:0.19.0--pyhdfd78af_0' : + 'biocontainers/pixelator:0.19.0--pyhdfd78af_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 0fbe600c..6ac95987 100644 --- a/modules/local/pixelator/single-cell/collapse/main.nf +++ b/modules/local/pixelator/single-cell/collapse/main.nf @@ -4,8 +4,8 @@ process PIXELATOR_COLLAPSE { conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:pixelator:0.19.0--pyhdfd78af_0' : + 'biocontainers/pixelator:0.19.0--pyhdfd78af_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 29e9e769..a4c8f869 100644 --- a/modules/local/pixelator/single-cell/demux/main.nf +++ b/modules/local/pixelator/single-cell/demux/main.nf @@ -5,8 +5,8 @@ process PIXELATOR_DEMUX { conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:pixelator:0.19.0--pyhdfd78af_0' : + 'biocontainers/pixelator:0.19.0--pyhdfd78af_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 4cd5db80..1b405436 100644 --- a/modules/local/pixelator/single-cell/graph/main.nf +++ b/modules/local/pixelator/single-cell/graph/main.nf @@ -5,8 +5,8 @@ process PIXELATOR_GRAPH { conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:pixelator:0.19.0--pyhdfd78af_0' : + 'biocontainers/pixelator:0.19.0--pyhdfd78af_0' }" input: tuple val(meta), path(edge_list) diff --git a/modules/local/pixelator/single-cell/layout/main.nf b/modules/local/pixelator/single-cell/layout/main.nf index ad2524ba..6b919137 100644 --- a/modules/local/pixelator/single-cell/layout/main.nf +++ b/modules/local/pixelator/single-cell/layout/main.nf @@ -5,8 +5,8 @@ process PIXELATOR_LAYOUT { conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:pixelator:0.19.0--pyhdfd78af_0' : + 'biocontainers/pixelator:0.19.0--pyhdfd78af_0' }" input: tuple val(meta), path(data) diff --git a/modules/local/pixelator/single-cell/qc/main.nf b/modules/local/pixelator/single-cell/qc/main.nf index 2184363f..68d3d454 100644 --- a/modules/local/pixelator/single-cell/qc/main.nf +++ b/modules/local/pixelator/single-cell/qc/main.nf @@ -5,8 +5,8 @@ process PIXELATOR_QC { conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:pixelator:0.19.0--pyhdfd78af_0' : + 'biocontainers/pixelator:0.19.0--pyhdfd78af_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 11d73c21..517a0bbb 100644 --- a/modules/local/pixelator/single-cell/report/main.nf +++ b/modules/local/pixelator/single-cell/report/main.nf @@ -5,8 +5,8 @@ process PIXELATOR_REPORT { conda "bioconda::pixelator=0.18.2" container "${ workflow.containerEngine == 'singularity' && !task.ext.singularity_pull_docker_container ? - 'https://depot.galaxyproject.org/singularity/pixelator:0.18.2--pyhdfd78af_0' : - 'biocontainers/pixelator:0.18.2--pyhdfd78af_0' }" + 'https://depot.galaxyproject.org/singularity/pixelator:pixelator:0.19.0--pyhdfd78af_0' : + 'biocontainers/pixelator:0.19.0--pyhdfd78af_0' }" input: tuple val(meta), path(panel_file), val(panel) diff --git a/nextflow.config b/nextflow.config index a7ae9710..6320c244 100644 --- a/nextflow.config +++ b/nextflow.config @@ -86,8 +86,7 @@ params { skip_layout = false // Main pixelator container override - // TODO: Remove this once the pixelator 0.19 is out - pixelator_container = "ghcr.io/pixelgentechnologies/pixelator:dev" + pixelator_container = null // Boilerplate options outdir = null From a580cdfead570ff9dcb043893cf13a7305c866c9 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 11 Dec 2024 09:41:51 +0100 Subject: [PATCH 257/260] Update nf-core subworkflows --- modules.json | 4 ++-- subworkflows/nf-core/utils_nextflow_pipeline/main.nf | 2 ++ .../tests/main.workflow.nf.test | 10 ++++++---- .../nf-core/utils_nfschema_plugin/tests/main.nf.test | 4 ++-- 4 files changed, 12 insertions(+), 8 deletions(-) diff --git a/modules.json b/modules.json index 7c89a4f9..4e150dc1 100644 --- a/modules.json +++ b/modules.json @@ -16,7 +16,7 @@ "nf-core": { "utils_nextflow_pipeline": { "branch": "master", - "git_sha": "3aa0aec1d52d492fe241919f0c6100ebf0074082", + "git_sha": "c2b22d85f30a706a3073387f30380704fcae013b", "installed_by": ["subworkflows"] }, "utils_nfcore_pipeline": { @@ -26,7 +26,7 @@ }, "utils_nfschema_plugin": { "branch": "master", - "git_sha": "bbd5a41f4535a8defafe6080e00ea74c45f4f96c", + "git_sha": "2fd2cd6d0e7b273747f32e465fdc6bcc3ae0814e", "installed_by": ["subworkflows"] } } diff --git a/subworkflows/nf-core/utils_nextflow_pipeline/main.nf b/subworkflows/nf-core/utils_nextflow_pipeline/main.nf index 0fcbf7b3..d6e593e8 100644 --- a/subworkflows/nf-core/utils_nextflow_pipeline/main.nf +++ b/subworkflows/nf-core/utils_nextflow_pipeline/main.nf @@ -92,10 +92,12 @@ def checkCondaChannels() { channels = config.channels } catch (NullPointerException e) { + log.debug(e) log.warn("Could not verify conda channel configuration.") return null } catch (IOException e) { + log.debug(e) log.warn("Could not verify conda channel configuration.") return null } diff --git a/subworkflows/nf-core/utils_nextflow_pipeline/tests/main.workflow.nf.test b/subworkflows/nf-core/utils_nextflow_pipeline/tests/main.workflow.nf.test index ca964ce8..02dbf094 100644 --- a/subworkflows/nf-core/utils_nextflow_pipeline/tests/main.workflow.nf.test +++ b/subworkflows/nf-core/utils_nextflow_pipeline/tests/main.workflow.nf.test @@ -52,10 +52,12 @@ nextflow_workflow { } then { - assertAll( - { assert workflow.success }, - { assert workflow.stdout.contains("nextflow_workflow v9.9.9") } - ) + expect { + with(workflow) { + assert success + assert "nextflow_workflow v9.9.9" in stdout + } + } } } diff --git a/subworkflows/nf-core/utils_nfschema_plugin/tests/main.nf.test b/subworkflows/nf-core/utils_nfschema_plugin/tests/main.nf.test index 842dc432..8fb30164 100644 --- a/subworkflows/nf-core/utils_nfschema_plugin/tests/main.nf.test +++ b/subworkflows/nf-core/utils_nfschema_plugin/tests/main.nf.test @@ -42,7 +42,7 @@ nextflow_workflow { params { test_data = '' - outdir = 1 + outdir = null } workflow { @@ -94,7 +94,7 @@ nextflow_workflow { params { test_data = '' - outdir = 1 + outdir = null } workflow { From 1811faa30b10c527158c6e325302860086d3f53a Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 11 Dec 2024 10:16:34 +0100 Subject: [PATCH 258/260] Reduce `--min-size` filter in test_panel_v2 profile --- conf/test_panel_v2.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/test_panel_v2.config b/conf/test_panel_v2.config index 7707d127..ccca0d59 100644 --- a/conf/test_panel_v2.config +++ b/conf/test_panel_v2.config @@ -31,7 +31,7 @@ params { input_basedir = params.pipelines_testdata_base_path + 'pixelator/testdata/' multiplet_recovery = true - min_size = 2 + min_size = 1 max_size = 100000 dynamic_filter = null compute_polarization = true From f6f937dfeb8824049128af7a766bb3fedfff1b6e Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 11 Dec 2024 10:33:00 +0100 Subject: [PATCH 259/260] Update CHANGELOG --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 831f1174..08fad568 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [[PR #112](https://github.com/nf-core/pixelator/pull/112)] - Add graph refinement options for pixelator 0.19 - [[PR #113](https://github.com/nf-core/pixelator/pull/113)] - Fix validation issues after nf-core/tools v3.0.2 update - [[PR #114](https://github.com/nf-core/pixelator/pull/114)] - Remove `--save_recovered_components` options for graph outputs +- [[PR #115](https://github.com/nf-core/pixelator/pull/115)] - Update containers for pixelator 0.19 ### Parameters From 1559e09d380faf1d2150f2803ed32617f82ecf07 Mon Sep 17 00:00:00 2001 From: Florian De Temmerman Date: Wed, 11 Dec 2024 11:43:36 +0100 Subject: [PATCH 260/260] Bump version from 1.4.0dev to 1.4.0 --- nextflow.config | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/nextflow.config b/nextflow.config index 6320c244..8c0a8246 100644 --- a/nextflow.config +++ b/nextflow.config @@ -300,7 +300,7 @@ manifest { description = """Pipeline for analysis of Molecular Pixelation assays""" mainScript = 'main.nf' nextflowVersion = '!>=24.04.2' - version = '1.4.0dev' + version = '1.4.0' doi = '10.1101/2023.06.05.543770' }

Process Name \\", - " \\ Software Version
CUSTOM_DUMPSOFTWAREVERSIONSpython3.11.7
yaml5.4.1
TOOL1tool10.11.9
TOOL2tool21.9
WorkflowNextflow