diff --git a/README.md b/README.md index 8b42ef3..52e773b 100644 --- a/README.md +++ b/README.md @@ -21,47 +21,47 @@ ## Introduction -**nf-core/mspepid** is a bioinformatics pipeline that ... - - - - - +**nf-core/mspepid** is a bioinformatics pipeline for the identification of peptides in mass spectrometry (MS)-based proteomics and peptidomics data. It takes a samplesheet with MS data files (Thermo `.raw`, Bruker TimsTOF `.d`, or open-standard `.mzML`) and a protein sequence database (FASTA) as input, runs one or more configurable database search engines, and performs post-processing with machine learning-based rescoring to produce high-confidence, FDR-filtered peptide-spectrum matches (PSMs). + +The pipeline is designed as a modular, identification-focused building block that can in the future feed into downstream quantification or statistical pipelines such as [nf-core/quantms](https://nf-co.re/quantms) and [nf-core/msproteomics](https://nf-co.re/msproteomics). + +1. Optional: Create entrapment database for FDR benchmarking ([`FDRBench`](https://github.com/percolator/fdrBench)) +2. Generate decoy sequences ([`OpenMS DecoyDatabase`](https://www.openms.de/)) — skipped with `--skip_decoy_generation` if decoys are already present +3. Convert vendor spectrum formats to mzML: + - [`ThermoRawFileParser`](https://github.com/compomics/ThermoRawFileParser) for Thermo `.raw` files + - [`tdf2mzml`](https://github.com/theGreatHerrLebert/tdf2mzml) for Bruker `.d` folders +4. Peptide-spectrum match (PSM) identification with one or more database search engines (more to come): + - [`Comet`](https://uwpr.github.io/Comet/) + - [`Sage`](https://github.com/lazear/sage) +5. Convert search engine results to a common PSM format ([`psm-utils`](https://psm-utils.readthedocs.io/)) +6. PSM rescoring (more to come): + - [`Percolator`](https://github.com/percolator/percolator) - semi-supervised machine learning for PSM re-ranking and FDR estimation + - [`MS2Rescore`](https://ms2rescore.readthedocs.io/) - MS2PIP spectral prediction-based feature generation followed by Percolator ## Usage > [!NOTE] > If you are new to Nextflow and nf-core, please refer to [this page](https://nf-co.re/docs/get_started/environment_setup/overview) on how to set-up Nextflow. Make sure to [test your setup](https://nf-co.re/docs/get_started/run-your-first-pipeline) with `-profile test` before running the workflow on actual data. - +Each row represents one MS run. The `ID` column must be a unique integer. The `spectrum_file` column accepts Thermo `.raw`, Bruker TimsTOF `.d`, and `.mzML` files (with optional `.gz`, `.zip`, or `.tar.gz` compression). The `fasta` column is optional per row — use `--fasta` to apply a single protein database to all runs instead. Now, you can run the pipeline using: - - ```bash nextflow run nf-core/mspepid \ -profile \ --input samplesheet.csv \ + --fasta proteins.fasta \ --outdir ``` @@ -76,9 +76,12 @@ To see the results of an example test run with a full size dataset refer to the For more details about the output files and reports, please refer to the [output documentation](https://nf-co.re/mspepid/output). +The main outputs are Percolator-scored PSM files (`.pout`) organised by search engine and rescoring strategy. +For each enabled search engine and rescoring method, results are written to `//` and respective subfolders `///`. + ## Credits -nf-core/mspepid was originally written by Julian Uszkoreit. +nf-core/mspepid was originally written by the team of the Medical Bioinformatics, Ruhr University Bochum, ([@medbioinf](https://github.com/medbioinf)). We thank the following people for their extensive assistance in the development of this pipeline: diff --git a/assets/samplesheet.csv b/assets/samplesheet.csv index 5f653ab..f3cfa5a 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, +ID,spectrum_file,fasta +1,/path/to/run1.raw,/path/to/proteins.fasta +2,/path/to/run2.mzML,/path/to/proteins.fasta diff --git a/assets/schema_input.json b/assets/schema_input.json index 9e5dbbc..1add03b 100644 --- a/assets/schema_input.json +++ b/assets/schema_input.json @@ -18,6 +18,13 @@ "exists": true, "pattern": "^\\S+\\.(raw|RAW|mzML|mzml|d|)(.tar.(gz|bz2)|.zip|.gz)?$", "errorMessage": "MS file name, cannot contain spaces and must have one of the extensions: raw | RAW | mzML | d" + }, + "fasta": { + "type": "string", + "format": "path", + "exists": true, + "pattern": "^\\S+\\.(fasta|fa|fas|faa)(\\.gz)?$", + "errorMessage": "FASTA file cannot contain spaces and must have one of the extensions: fasta | fa | fas | faa" } }, "required": ["ID", "spectrum_file"] diff --git a/docs/output.md b/docs/output.md index 0e9da2c..0bc6264 100644 --- a/docs/output.md +++ b/docs/output.md @@ -2,17 +2,104 @@ ## Introduction -This document describes the output produced by the pipeline. +This document describes the output produced by the pipeline. All paths are relative to the top-level results directory specified with `--outdir`. -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 - +nf-core/mspepid processes mass spectrometry data through the following steps: -## Pipeline overview +1. **Database preparation** - optional entrapment database creation and decoy sequence generation +2. **Spectra preparation** - decompression and vendor-format conversion to mzML +3. **Spectra identification** - database search with Comet and/or Sage +4. **Rescoring** - FDR-based PSM rescoring with Percolator and/or MS2Rescore + +--- + +## Database preparation + +### Decoy generation + +Decoy protein sequences are automatically appended to the target database unless `--skip_decoy_generation` is set. Decoys are generated by the [OpenMS DecoyDatabase](https://www.openms.de/) tool using sequence reversal as default strategy. + +### Entrapment database + +When `--entrapment_fold` is greater than 0, an entrapment database is created by [FDRBench](https://github.com/percolator/fdrBench) prior to decoy generation. Entrapment sequences (shuffled target proteins) are used for independent FDR estimation during benchmarking. + +--- + +## Spectra identification + +For each enabled search engine, PSM results are written under a subdirectory named after the search engine (e.g. `comet/` or `sage/`). +All results are converted to a common format with [psm-utils](https://psm-utils.readthedocs.io/) for downstream processing. + +### Comet + +
+Output files + +- `comet/` + - `*.mzid` - PSMs in mzIdentML standard format (default output of Comet) + - `*.comet.params` - the final input parameters of the Comet run. + - `*.psmutils.tsv` - PSMs in psm-utils TSV format, used as input to rescoring steps. + - `*.pin` - Percolator input file (PIN format) containing search engine features. + - Additional outputs can be activated, see module description of Comet + +
+ +[Comet](https://uwpr.github.io/Comet/) is a widely used open-source database search engine for tandem mass spectra. + +### Sage + +
+Output files + +- `sage/` + - `*.results.sage.tsv` - PSMs in Sage's default TSV output format. + - `*.results.json` - the final input parameters of the Sage run. + - `*.psmutils.tsv` - PSMs in psm-utils TSV format, used as input to rescoring steps. + - `*.pin` - Percolator input file (PIN format) containing search engine features. + +
+ +[Sage](https://github.com/lazear/sage) is a fast, Rust-based database search engine that scales efficiently to large datasets and large protein databases. + +--- + +## Rescoring + +PSMs from each search engine are rescored independently. Output directories are nested under the search engine directory. + +### Percolator + +
+Output files + +- `/percolator/` + - `*.target.psms` - Target PSMs scored and filtered by Percolator. + - `*.decoy.psms` - Decoy PSMs (used for FDR calibration). + - Additional outputs can be activated, see module description of Percolator + +
+ +[Percolator](https://github.com/percolator/percolator) applies semi-supervised machine learning (using a SVM) to re-rank PSMs using search engine scores and auxiliary features, then estimates FDR using a target-decoy competition approach. + +### MS2Rescore / Tims2Rescore + +
+Output files + +- `/ms2rescore/` + - `*.target.psms` - Target PSMs rescored after MS2PIP feature augmentation. + - `*.decoy.psms` - Decoy PSMs. + - `*.pin`- The original PSMs after identification with additional features created my MS2PIP + +
+ +[MS2Rescore](https://ms2rescore.readthedocs.io/) generates additional rescoring features by comparing observed fragment ion spectra to spectra predicted by [MS2PIP](https://ms2pip.readthedocs.io/). The augmented feature set is then passed to Percolator for final scoring. This typically improves PSM identifications at a given FDR threshold, particularly for challenging samples such as immunopeptidomes or non-tryptic digests. I TIMS data is used as input, automatically Tims2Rescore is applied (which is the default behaviour of newer MS2Rescore implementations). -The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes data using the following steps: +The MS2PIP fragmentation model is controlled by `--ms2rescore_model` (default: `HCD`). -- [Pipeline information](#pipeline-information) - Report metrics generated during the workflow execution +--- ### Pipeline information @@ -21,9 +108,10 @@ The pipeline is built using [Nextflow](https://www.nextflow.io/) and processes d - `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. + - 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` parameters are used when running the pipeline. - Reformatted samplesheet files used as input to the pipeline: `samplesheet.valid.csv`. - Parameters used by the pipeline run: `params.json`. + - Software versions used: `nf_core_mspepid_software_versions.yml`. diff --git a/docs/usage.md b/docs/usage.md index 0b38c6c..50c9580 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -6,50 +6,55 @@ ## Introduction - +nf-core/mspepid identifies peptides from mass spectrometry data by running one or more database search engines against a protein sequence database and applying machine learning-based rescoring to maximise PSM confidence and identification level. + +This page describes how to prepare input data, configure search engines and rescoring tools, and run the pipeline. ## 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 MS runs 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 at least two columns and a header row as shown in the examples below. ```bash --input '[path to samplesheet file]' ``` -### Multiple runs of the same sample +### Samplesheet columns + +| Column | Required | Description | +| --------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `ID` | Yes | A unique integer identifier for each MS run. | +| `spectrum_file` | Yes | Path to the MS data file. Supported formats: Thermo `.raw`, Bruker TimsTOF `.d`, `.mzML`. Files may be compressed with `.gz`, `.zip`, `.tar.gz`, or `.tar.bz2`. | +| `fasta` | No | Path to a per-run protein sequence database (FASTA). Mutually exclusive with the global `--fasta` parameter. | + +### Example samplesheet — global FASTA via `--fasta` -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: +When all runs share the same protein database, provide it with `--fasta` and omit the `fasta` column: ```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 +ID,spectrum_file +1,/path/to/run1.raw +2,/path/to/run2.mzML +3,/path/to/run3.d.tar.gz ``` -### Full samplesheet +```bash +nextflow run nf-core/mspepid \ + --input samplesheet.csv \ + --fasta /path/to/proteins.fasta \ + --outdir ./results \ + -profile docker +``` -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. +### Example samplesheet — per-run FASTA -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. +To use a different protein database for each run, add the `fasta` column. This is useful for e.g. proteogenoomics or other sample-specific databases: ```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, +ID,spectrum_file,fasta +1,/path/to/run1.raw,/path/to/sample1_proteins.fasta +2,/path/to/run2.mzML,/path/to/sample2_proteins.fasta ``` -| 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 @@ -57,10 +62,14 @@ An [example samplesheet](../assets/samplesheet.csv) has been provided with the p The typical command for running the pipeline is as follows: ```bash -nextflow run nf-core/mspepid --input ./samplesheet.csv --outdir ./results -profile docker +nextflow run nf-core/mspepid \ + --input ./samplesheet.csv \ + --fasta ./proteins.fasta \ + --outdir ./results \ + -profile docker ``` -This will launch the pipeline with the `docker` configuration profile. See below for more information about profiles. +This will launch the pipeline with the `docker` configuration profile. All supported search engines and rescoring methods are enabled by default. See parameters for how to customise this. Note that the pipeline will create the following files in your working directory: diff --git a/main.nf b/main.nf index d684a1a..c20e2e8 100644 --- a/main.nf +++ b/main.nf @@ -30,7 +30,7 @@ include { PIPELINE_COMPLETION } from './subworkflows/local/utils_nfcore_mspe workflow NFCORE_MSPEPID { take: - samplesheet // channel: samplesheet read in from --input + samplesheet // channel: [meta, spectrum_file, fasta_file] read in from --input main: @@ -40,7 +40,6 @@ workflow NFCORE_MSPEPID { MSPEPID( samplesheet, params.outdir, - params.fasta, params.entrapment_fold, params.skip_decoy_generation, params.precursor_tol_ppm, diff --git a/nextflow_schema.json b/nextflow_schema.json index 939caa2..0caba21 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -47,9 +47,9 @@ "fasta": { "type": "string", "fa_icon": "fas fa-file", - "pattern": ".(fasta|faa)$", - "description": "Input FASTA protein database", - "help_text": "Path to the protein database file. Whether it already contains decoys is set by the --skip_decoy_generation parameter." + "pattern": "^\\S+\\.(fasta|fa|fas|faa)(.gz)?$", + "description": "Global input FASTA protein database (mutually exclusive with per-run 'fasta' column in the samplesheet)", + "help_text": "Path to a single protein database file applied to all MS runs. Cannot be used together with the 'fasta' column in the samplesheet. Whether the file already contains decoys is controlled by --skip_decoy_generation." }, "entrapment_fold": { "type": "integer", diff --git a/subworkflows/local/spectra_identification/main.nf b/subworkflows/local/spectra_identification/main.nf index 0226947..22ea095 100644 --- a/subworkflows/local/spectra_identification/main.nf +++ b/subworkflows/local/spectra_identification/main.nf @@ -7,8 +7,8 @@ include { PSMUTILSCONVERSIONS } from '../../../modules/local/psmutilsconversions workflow SPECTRA_IDENTIFICATION { take: - ch_fasta - ch_spectra_files // val(meta), path(mzml), path(raw_spectra) + ch_spectra_files // val(meta), path(mzml), path(raw_spectra) + ch_fasta_db // channel: [sample_id, db_fasta] one item per sample precursor_tol_ppm fragment_tol_da run_comet @@ -21,13 +21,14 @@ workflow SPECTRA_IDENTIFICATION { main: ch_versions = channel.empty() - // TODO: this will become the identifications, probably with some meta mapping? + // this will contain the identifications, with some meta data ch_identifications = channel.empty() - // prepare the input channel for identifications - // TODO: this right now only adds the fasta - must be adapted for per sample DB - // TODO: also adapt for per-sample parameters - ch_ident_in = ch_spectra_files.combine(ch_fasta.map { _meta, fasta -> [fasta] }) + // join each spectrum file with its per-run database FASTA + ch_ident_in = ch_spectra_files + .map { meta, mzml, raw -> [meta.id, meta, mzml, raw] } + .join(ch_fasta_db, by: 0) + .map { _id, meta, mzml, raw, fasta -> [meta, mzml, raw, fasta] } // run Comet, if enabled if (run_comet) { @@ -71,15 +72,19 @@ workflow SPECTRA_IDENTIFICATION { ) ch_versions = ch_versions.mix(SAGECONFIG.out.versions_python) - ch_sage_spectra = ch_spectra_files.map { meta, mzml, _raw_spectra -> [meta, mzml] } - // add empty meta information for compatibility and convert to value channel - ch_sage_config = SAGECONFIG.out.config.map { config -> [["ID": "SAGE_CONFIG"], config] }.first() - // convert to value channel - ch_sage_fasta = ch_fasta.first() + ch_sage_config = SAGECONFIG.out.config.map { config -> [['ID': 'SAGE_CONFIG'], config] }.first() + + // Re-use ch_ident_in (already joined with per-run fasta) and split into + // the two separate channels SAGEBETA requires. + ch_sage_joined = ch_ident_in + .multiMap { meta, mzml, _raw, fasta -> + spectra: [meta, mzml] + fasta: [[id: fasta.getBaseName()], fasta] + } SAGEBETA( - ch_sage_spectra, - ch_sage_fasta, + ch_sage_joined.spectra, + ch_sage_joined.fasta, ch_sage_config, ) ch_versions = ch_versions.mix(SAGEBETA.out.versions_sagebeta) diff --git a/subworkflows/local/utils_nfcore_mspepid_pipeline/main.nf b/subworkflows/local/utils_nfcore_mspepid_pipeline/main.nf index e3b0f5b..4ec8771 100644 --- a/subworkflows/local/utils_nfcore_mspepid_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_mspepid_pipeline/main.nf @@ -102,21 +102,36 @@ workflow PIPELINE_INITIALISATION { // Create channel from input file provided through params.input // + // Validate FASTA source mutual exclusivity before building channels + def raw_rows = samplesheetToList(input, "${projectDir}/assets/schema_input.json") + def sheet_has_fasta = raw_rows.any { row -> row.size() > 2 && row[2] } + def sheet_all_fasta = raw_rows.every { row -> row.size() > 2 && row[2] } + + if (params.fasta && sheet_has_fasta) { + error("Conflicting FASTA sources: '--fasta' parameter and 'fasta' column in the samplesheet are mutually exclusive. Please use only one.") + } + if (!params.fasta && !sheet_has_fasta) { + error("No FASTA provided. Please either use '--fasta' or add a 'fasta' column to every row in the samplesheet.") + } + if (!params.fasta && sheet_has_fasta && !sheet_all_fasta) { + error("Incomplete FASTA column: either provide one fasta for each run in the samplesheet, or use the global '--fasta' parameter.") + } + channel - .fromList(samplesheetToList(input, "${projectDir}/assets/schema_input.json")) + .fromList(raw_rows) .map { row -> - def meta = row[0] - def spectrum_file = row.size() > 1 ? row[1] : null - if (!spectrum_file) { - error("Please check input samplesheet -> Missing required column 'spectrum_file' for sample ID: ${meta.id}") - } - def sample_meta = meta + [sampleId: meta.id, id: "${meta.id}-${spectrum_file.simpleName}"] - return [sample_meta.id, sample_meta, spectrum_file] + def meta = row[0] + def spectrum_file = row[1] + // Resolve fasta: either from the global --fasta param or from the samplesheet column. + // Mutual exclusivity and completeness were already validated above. + def fasta_file = params.fasta + ? file(params.fasta, checkIfExists: true) + : row[2] + def sample_meta = meta + [sampleId: meta.id, id: "${meta.id}-${spectrum_file.simpleName}"] + return [sample_meta.id, sample_meta, spectrum_file, fasta_file] } .groupTuple() - .map { samplesheet -> - validateInputSamplesheet(samplesheet) - } + .map { validateInputSamplesheet(it) } .set { ch_samplesheet } emit: @@ -177,13 +192,13 @@ workflow PIPELINE_COMPLETION { // Validate channels from input samplesheet // def validateInputSamplesheet(input) { - def (metas, spectra_files) = input[1..2] + def (metas, spectra_files, fasta_files) = input[1..3] if (spectra_files.size() != 1) { error("Please check input samplesheet -> Expected exactly one 'spectrum_file' per sample row after grouping, got ${spectra_files.size()} for: ${metas[0].id}") } - return [metas[0], spectra_files[0]] + return [metas[0], spectra_files[0], fasta_files[0]] } // // Generate methods description for MultiQC diff --git a/workflows/mspepid.nf b/workflows/mspepid.nf index 5c82165..5e31a10 100644 --- a/workflows/mspepid.nf +++ b/workflows/mspepid.nf @@ -20,9 +20,8 @@ include { SPECTRA_RESCORING } from '../subworkflows/local/spectra_rescoring workflow MSPEPID { take: - ch_samplesheet // channel: samplesheet read in from --input + ch_samplesheet // channel: [meta, spectrum_file, fasta_file] read in from --input outdir - fasta // string: path to fasta file entrapment_fold // integer: fold for entrapment generation, 0 for none skip_decoy_generation // boolean: whether to skip decoy generation precursor_tol_ppm // integer: Precursor mass tolerance in ppm for spectra identification @@ -42,28 +41,40 @@ workflow MSPEPID { def ch_versions = channel.empty() - // create channel for fasta input - ch_fasta = channel.fromPath(fasta, checkIfExists: true) - .map { fa -> [[id: fa.getBaseName()], fa] } + // Extract fasta from the samplesheet channel + ch_fasta = ch_samplesheet.map { meta, _spectrum_file, fasta_file -> [meta, fasta_file] } + + // Deduplicate FASTAs by path: run PREPARE_DATABASES once per unique FASTA file. + // To later join the FASTAs back to the runs, meta gets a 'sample_ids' list of all run IDs + ch_fasta_dedup = ch_fasta + .map { meta, fasta -> [fasta.toString(), meta.id, fasta] } + .groupTuple(by: 0) + .map { _path_key, sample_ids, fastas -> + [[id: fastas[0].getBaseName(), sample_ids: sample_ids], fastas[0]] + } // prepare the databases: decoy generation and entrapment database creation PREPARE_DATABASES( - ch_fasta, + ch_fasta_dedup, entrapment_fold, skip_decoy_generation, ) - ch_fasta_db = PREPARE_DATABASES.out.fasta + // create one [sample_id, db_fasta] for each sample_id in the original samplesheet + ch_fasta_db_per_run = PREPARE_DATABASES.out.fasta + .flatMap { db_meta, db_fasta -> + db_meta.sample_ids.collect { run_id -> [run_id, db_fasta] } + } - // prepare the spectra files + // prepare the spectra files (strip the fasta_file before passing to PREPARE_SPECTRA) PREPARE_SPECTRA( - ch_samplesheet + ch_samplesheet.map { meta, spectrum_file, _fasta_file -> [meta, spectrum_file] } ) ch_prepared_spectra = PREPARE_SPECTRA.out.mzmls.join(PREPARE_SPECTRA.out.uncompressed, by: 0) // spectra identification SPECTRA_IDENTIFICATION( - ch_fasta_db, ch_prepared_spectra, + ch_fasta_db_per_run, precursor_tol_ppm, fragment_tol_da, run_comet,