diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 4ecfbfe3..b290e090 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -10,15 +10,7 @@ "vscode": { // Set *default* container specific settings.json values on container create. "settings": { - "python.defaultInterpreterPath": "/opt/conda/bin/python", - "python.linting.enabled": true, - "python.linting.pylintEnabled": true, - "python.formatting.autopep8Path": "/opt/conda/bin/autopep8", - "python.formatting.yapfPath": "/opt/conda/bin/yapf", - "python.linting.flake8Path": "/opt/conda/bin/flake8", - "python.linting.pycodestylePath": "/opt/conda/bin/pycodestyle", - "python.linting.pydocstylePath": "/opt/conda/bin/pydocstyle", - "python.linting.pylintPath": "/opt/conda/bin/pylint" + "python.defaultInterpreterPath": "/opt/conda/bin/python" }, // Add the IDs of extensions you want installed when the container is created. diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index cbc724f3..de8d5a87 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -9,9 +9,8 @@ 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 ;) -:::info -If you need help using or modifying nf-core/quantms then the best place to ask is on the nf-core Slack [#quantms](https://nfcore.slack.com/channels/quantms) channel ([join our Slack here](https://nf-co.re/join/slack)). -::: +> [!NOTE] +> If you need help using or modifying nf-core/quantms then the best place to ask is on the nf-core Slack [#quantms](https://nfcore.slack.com/channels/quantms) channel ([join our Slack here](https://nf-co.re/join/slack)). ## Contribution workflow @@ -27,8 +26,11 @@ 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 `. +You have the option to test your changes locally by running the pipeline. For receiving warnings about process selectors and other `debug` information, it is recommended to use the debug profile. Execute all the tests with the following command: + +```bash +nf-test test --profile debug,test,docker --verbose +``` 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. @@ -90,7 +92,7 @@ Once there, use `nf-core schema build` to add to `nextflow_schema.json`. Sensible defaults for process resource requirements (CPUs / memory / time) for a process should be defined in `conf/base.config`. These should generally be specified generic with `withLabel:` selectors so they can be shared across multiple processes/steps of the pipeline. A nf-core standard set of labels that should be followed where possible can be seen in the [nf-core pipeline template](https://github.com/nf-core/tools/blob/master/nf_core/pipeline-template/conf/base.config), which has the default process as a single core-process, and then different levels of multi-core configurations for increasingly large memory requirements defined with standardised labels. -The process resources can be passed on to the tool dynamically within the process with the `${task.cpu}` and `${task.memory}` variables in the `script:` block. +The process resources can be passed on to the tool dynamically within the process with the `${task.cpus}` and `${task.memory}` variables in the `script:` block. ### Naming schemes diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index e3148c43..6e6e8ffe 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/quan - [ ] If you've added a new tool - have you followed the pipeline conventions in the [contribution docs](https://github.com/nf-core/quantms/tree/master/.github/CONTRIBUTING.md) - [ ] If necessary, also make a PR on the nf-core/quantms _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 `). +- [ ] Ensure the test suite passes (`nf-test test main.nf.test -profile test,docker`). - [ ] 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. diff --git a/.github/workflows/awsfulltest.yml b/.github/workflows/awsfulltest.yml index de62f911..8d04e9b7 100644 --- a/.github/workflows/awsfulltest.yml +++ b/.github/workflows/awsfulltest.yml @@ -20,7 +20,9 @@ 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/download_pipeline.yml b/.github/workflows/download_pipeline.yml index f823210d..08622fd5 100644 --- a/.github/workflows/download_pipeline.yml +++ b/.github/workflows/download_pipeline.yml @@ -28,7 +28,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Install Nextflow - uses: nf-core/setup-nextflow@b9f764e8ba5c76b712ace14ecbfcef0e40ae2dd8 # v1 + uses: nf-core/setup-nextflow@v1 - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5 with: diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 748b4311..073e1876 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -35,7 +35,7 @@ jobs: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - name: Install Nextflow - uses: nf-core/setup-nextflow@b9f764e8ba5c76b712ace14ecbfcef0e40ae2dd8 # v1 + uses: nf-core/setup-nextflow@v1 - uses: actions/setup-python@0a5c61591373683505ea898e09a3ea4f39ef2b9c # v5 with: diff --git a/.github/workflows/release-announcements.yml b/.github/workflows/release-announcements.yml index c3674af2..d468aeaa 100644 --- a/.github/workflows/release-announcements.yml +++ b/.github/workflows/release-announcements.yml @@ -12,7 +12,7 @@ jobs: - name: get topics and convert to hashtags id: get_topics run: | - curl -s https://nf-co.re/pipelines.json | jq -r '.remote_workflows[] | select(.name == "${{ github.repository }}") | .topics[]' | awk '{print "#"$0}' | tr '\n' ' ' > $GITHUB_OUTPUT + curl -s https://nf-co.re/pipelines.json | jq -r '.remote_workflows[] | select(.full_name == "${{ github.repository }}") | .topics[]' | awk '{print "#"$0}' | tr '\n' ' ' >> $GITHUB_OUTPUT - uses: rzr/fediverse-action@master with: diff --git a/.gitpod.yml b/.gitpod.yml index 363d5b1d..105a1821 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -10,13 +10,11 @@ tasks: vscode: extensions: # based on nf-core.nf-core-extensionpack - - codezombiech.gitignore # Language support for .gitignore files - # - cssho.vscode-svgviewer # SVG viewer - esbenp.prettier-vscode # Markdown/CommonMark linting and style checking for Visual Studio Code - - eamodio.gitlens # Quickly glimpse into whom, why, and when a line or code block was changed - EditorConfig.EditorConfig # override user/workspace settings with settings found in .editorconfig files - Gruntfuggly.todo-tree # Display TODO and FIXME in a tree view in the activity bar - mechatroner.rainbow-csv # Highlight columns in csv files in different colors - # - nextflow.nextflow # Nextflow syntax highlighting + # - nextflow.nextflow # Nextflow syntax highlighting - oderwat.indent-rainbow # Highlight indentation level - streetsidesoftware.code-spell-checker # Spelling checker for source code + - charliermarsh.ruff # Code linter Ruff diff --git a/README.md b/README.md index b8c0e6e5..3feabe60 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ # ![nf-core/quantms](docs/images/nf-core-quantms_logo_light.png#gh-light-mode-only) ![nf-core/quantms](docs/images/nf-core-quantms_logo_dark.png#gh-dark-mode-only) -[![AWS CI](https://img.shields.io/badge/CI%20tests-full%20size-FF9900?labelColor=000000&logo=Amazon%20AWS)](https://nf-co.re/quantms/results)[![Cite with Zenodo](https://img.shields.io/badge/DOI-10.5281/zenodo.7754148-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.7754148) +[![GitHub Actions CI Status](https://github.com/nf-core/quantms/actions/workflows/ci.yml/badge.svg)](https://github.com/nf-core/quantms/actions/workflows/ci.yml) +[![GitHub Actions Linting Status](https://github.com/nf-core/quantms/actions/workflows/linting.yml/badge.svg)](https://github.com/nf-core/quantms/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/quantms/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) [![Nextflow](https://img.shields.io/badge/nextflow%20DSL2-%E2%89%A523.04.0-23aa62.svg)](https://www.nextflow.io/) [![run with conda](https://img.shields.io/badge/run%20with-conda-3EB049?labelColor=000000&logo=anaconda)](https://docs.conda.io/en/latest/) [![run with docker](https://img.shields.io/badge/run%20with-docker-0db7ed?labelColor=000000&logo=docker)](https://www.docker.com/) [![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/) -[![Launch on Nextflow Tower](https://img.shields.io/badge/Launch%20%F0%9F%9A%80-Nextflow%20Tower-%234256e7)](https://tower.nf/launch?pipeline=https://github.com/nf-core/quantms) +[![Launch on Seqera Platform](https://img.shields.io/badge/Launch%20%F0%9F%9A%80-Seqera%20Platform-%234256e7)](https://tower.nf/launch?pipeline=https://github.com/nf-core/quantms) [![Get help on Slack](https://img.shields.io/badge/slack-nf--core%20%23quantms-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/quantms)[![Follow on Twitter](https://img.shields.io/badge/twitter-%40nf__core-1DA1F2?labelColor=000000&logo=twitter)](https://twitter.com/nf_core)[![Follow on Mastodon](https://img.shields.io/badge/mastodon-nf__core-6364ff?labelColor=FFFFFF&logo=mastodon)](https://mstdn.science/@nf_core)[![Watch on YouTube](https://img.shields.io/badge/youtube-nf--core-FF0000?labelColor=000000&logo=youtube)](https://www.youtube.com/c/nf-core) diff --git a/assets/adaptivecard.json b/assets/adaptivecard.json index 5af06e53..db2612fc 100644 --- a/assets/adaptivecard.json +++ b/assets/adaptivecard.json @@ -54,7 +54,8 @@ "body": [ { "type": "FactSet", - "facts": [<% out << summary.collect{ k,v -> "{\"title\": \"$k\", \"value\" : \"$v\"}"}.join(",\n") %> + "facts": [<% out << summary.collect{ k,v -> "{\"title\": \"$k\", \"value\" : \"$v\"}" + }.join(",\n") %> ] } ] diff --git a/bin/mzml_statistics.py b/bin/mzml_statistics.py index f4f0d298..208ad3ab 100755 --- a/bin/mzml_statistics.py +++ b/bin/mzml_statistics.py @@ -7,12 +7,12 @@ import sys from pathlib import Path import sqlite3 - +import re import pandas as pd from pyopenms import MSExperiment, MzMLFile -def ms_dataframe(ms_path: str) -> None: +def ms_dataframe(ms_path: str, id_only: bool = False) -> None: file_columns = [ "SpectrumID", "MSLevel", @@ -25,8 +25,9 @@ def ms_dataframe(ms_path: str) -> None: "AcquisitionDateTime", ] - def parse_mzml(file_name: str, file_columns: list): + def parse_mzml(file_name: str, file_columns: list, id_only: bool = False): info = [] + psm_part_info = [] exp = MSExperiment() acquisition_datetime = exp.getDateTime().get() MzMLFile().load(file_name, exp) @@ -54,11 +55,23 @@ def parse_mzml(file_name: str, file_columns: list): charge_state = spectrum.getPrecursors()[0].getCharge() emz = spectrum.getPrecursors()[0].getMZ() if spectrum.getPrecursors()[0].getMZ() else None info_list = [id_, MSLevel, charge_state, peak_per_ms, bpc, tic, rt, emz, acquisition_datetime] + mz_array = peaks_tuple[0] + intensity_array = peaks_tuple[1] else: info_list = [id_, MSLevel, None, None, None, None, rt, None, acquisition_datetime] + if id_only and MSLevel == 2: + psm_part_info.append([re.findall(r"[scan|spectrum]=(\d+)", id_)[0], MSLevel, mz_array, intensity_array]) info.append(info_list) + if id_only and len(psm_part_info) > 0: + pd.DataFrame(psm_part_info, columns=["scan", "ms_level", "mz", "intensity"]).to_csv( + f"{Path(ms_path).stem}_spectrum_df.csv", + mode="w", + index=False, + header=True, + ) + return pd.DataFrame(info, columns=file_columns) def parse_bruker_d(file_name: str, file_columns: list): @@ -139,7 +152,7 @@ def parse_bruker_d(file_name: str, file_columns: list): if Path(ms_path).suffix == ".d" and Path(ms_path).is_dir(): ms_df = parse_bruker_d(ms_path, file_columns) elif Path(ms_path).suffix in [".mzML", ".mzml"]: - ms_df = parse_mzml(ms_path, file_columns) + ms_df = parse_mzml(ms_path, file_columns, id_only) else: msg = f"Unrecognized or inexistent mass spec file '{ms_path}'" raise RuntimeError(msg) @@ -155,7 +168,8 @@ def parse_bruker_d(file_name: str, file_columns: list): def main(): ms_path = sys.argv[1] - ms_dataframe(ms_path) + id_only = sys.argv[2] + ms_dataframe(ms_path, id_only) if __name__ == "__main__": diff --git a/bin/psm_conversion.py b/bin/psm_conversion.py new file mode 100644 index 00000000..c122a24f --- /dev/null +++ b/bin/psm_conversion.py @@ -0,0 +1,117 @@ +#!/usr/bin/env python +import numpy as np +import pyopenms as oms +import pandas as pd +import re +import os +from pathlib import Path +import sys + +_parquet_field = [ + "sequence", "protein_accessions", "protein_start_positions", "protein_end_positions", + "modifications", "retention_time", "charge", "calc_mass_to_charge", "reference_file_name", + "scan_number", "peptidoform", "posterior_error_probability", "global_qvalue", "is_decoy", + "consensus_support", "mz_array", "intensity_array", "num_peaks", "search_engines", "id_scores", "hit_rank" +] + + +def mods_position(peptide): + pattern = re.compile(r"\((.*?)\)") + original_mods = pattern.findall(peptide) + peptide = re.sub(r"\(.*?\)", ".", peptide) + position = [i.start() for i in re.finditer(r"\.", peptide)] + for j in range(1, len(position)): + position[j] -= j + + for k in range(0, len(original_mods)): + original_mods[k] = str(position[k]) + "-" + original_mods[k] + + original_mods = [str(i) for i in original_mods] if len(original_mods) > 0 else np.nan + + return original_mods + + +def convert_psm(idxml, spectra_file, export_decoy_psm): + prot_ids = [] + pep_ids = [] + parquet_data = [] + consensus_support = np.nan + mz_array = [] + intensity_array = [] + num_peaks = np.nan + id_scores = [] + search_engines = [] + + oms.IdXMLFile().load(idxml, prot_ids, pep_ids) + if "ConsensusID" in prot_ids[0].getSearchEngine(): + if prot_ids[0].getSearchParameters().metaValueExists("SE:MS-GF+"): + search_engines = ["MS-GF+"] + if prot_ids[0].getSearchParameters().metaValueExists("SE:Comet"): + search_engines.append("Comet") + if prot_ids[0].getSearchParameters().metaValueExists("SE:Sage"): + search_engines.append("Sage") + else: + search_engines = [prot_ids[0].getSearchEngine()] + + reference_file_name = os.path.splitext(prot_ids[0].getMetaValue("spectra_data")[0].decode("UTF-8"))[0] + spectra_df = pd.read_csv(spectra_file) if spectra_file else None + + for peptide_id in pep_ids: + retention_time = peptide_id.getRT() + calc_mass_to_charge = peptide_id.getMZ() + scan_number = int(re.findall(r"(spectrum|scan)=(\d+)", peptide_id.getMetaValue("spectrum_reference"))[0][1]) + + if isinstance(spectra_df, pd.DataFrame): + spectra = spectra_df[spectra_df["scan"] == scan_number] + mz_array = spectra["mz"].values[0] + intensity_array = spectra["intensity"].values[0] + num_peaks = len(mz_array) + + for hit in peptide_id.getHits(): + # if remove decoy when mapped to target+decoy? + is_decoy = 0 if hit.getMetaValue("target_decoy") == "target" else 1 + if export_decoy_psm == "false" and is_decoy: + continue + global_qvalue = np.nan + if len(search_engines) > 1: + if "q-value" in peptide_id.getScoreType(): + global_qvalue = hit.getScore() + consensus_support = hit.getMetaValue("consensus_support") + elif search_engines == "Comet": + id_scores = ["Comet:Expectation value: " + str(hit.getScore())] + elif search_engines == "MS-GF+": + id_scores = ["MS-GF:SpecEValue: " + str(hit.getScore())] + elif search_engines == "Sage": + id_scores = ["Sage:hyperscore: " + str(hit.getScore())] + + charge = hit.getCharge() + peptidoform = hit.getSequence().toString() + modifications = mods_position(peptidoform) + sequence = hit.getSequence().toUnmodifiedString() + protein_accessions = [ev.getProteinAccession() for ev in hit.getPeptideEvidences()] + posterior_error_probability = hit.getMetaValue("Posterior Error Probability_score") + protein_start_positions = [ev.getStart() for ev in hit.getPeptideEvidences()] + protein_end_positions = [ev.getEnd() for ev in hit.getPeptideEvidences()] + hit_rank = hit.getRank() + + parquet_data.append([sequence, protein_accessions, protein_start_positions, protein_end_positions, + modifications, retention_time, charge, calc_mass_to_charge, reference_file_name, + scan_number, peptidoform, posterior_error_probability, global_qvalue, is_decoy, + consensus_support, mz_array, intensity_array, num_peaks, search_engines, id_scores, + hit_rank]) + + pd.DataFrame(parquet_data, columns=_parquet_field).to_csv(f"{Path(idxml).stem}_psm.csv", + mode="w", + index=False, + header=True) + + +def main(): + idxml_path = sys.argv[1] + spectra_file = sys.argv[2] + export_decoy_psm = sys.argv[3] + convert_psm(idxml_path, spectra_file, export_decoy_psm) + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/conf/modules.config b/conf/modules.config index 1f9ea163..c220be03 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -245,6 +245,16 @@ process { ] } + withName: '.*:DDA_ID:PSMFDRCONTROL:IDFILTER' { + ext.args = "-score:pep \"$params.run_fdr_cutoff\"" + ext.suffix = '.idXML' + publishDir = [ + path: { "${params.outdir}/idfilter" }, + mode: params.publish_dir_mode, + saveAs: { filename -> filename.equals('versions.yml') ? null : filename } + ] + } + // PROTEOMICSLFQ withName: '.*:LFQ:PROTEOMICSLFQ' { ext.args = "-debug $params.plfq_debug" diff --git a/modules.json b/modules.json index 4a8c5367..beb0405b 100644 --- a/modules.json +++ b/modules.json @@ -12,10 +12,29 @@ }, "multiqc": { "branch": "master", - "git_sha": "ccacf6f5de6df3bc6d73b665c1fd2933d8bbc290", + "git_sha": "b7ebe95761cd389603f9cc0e0dc384c0f663815a", "installed_by": ["modules"] } } + }, + "subworkflows": { + "nf-core": { + "utils_nextflow_pipeline": { + "branch": "master", + "git_sha": "5caf7640a9ef1d18d765d55339be751bb0969dfa", + "installed_by": ["subworkflows"] + }, + "utils_nfcore_pipeline": { + "branch": "master", + "git_sha": "5caf7640a9ef1d18d765d55339be751bb0969dfa", + "installed_by": ["subworkflows"] + }, + "utils_nfvalidation_plugin": { + "branch": "master", + "git_sha": "5caf7640a9ef1d18d765d55339be751bb0969dfa", + "installed_by": ["subworkflows"] + } + } } } } diff --git a/modules/local/extract_psm/main.nf b/modules/local/extract_psm/main.nf new file mode 100644 index 00000000..a640316a --- /dev/null +++ b/modules/local/extract_psm/main.nf @@ -0,0 +1,36 @@ +process PSMCONVERSION { + tag "$meta.mzml_id" + label 'process_medium' + + conda "bioconda::pyopenms=3.1.0" + if (workflow.containerEngine == 'singularity' && !params.singularity_pull_docker_container) { + container "https://depot.galaxyproject.org/singularity/pyopenms:3.1.0--py39h9b8898c_0" + } else { + container "biocontainers/pyopenms:3.1.0--py39h9b8898c_0" + } + + input: + tuple val(meta), path(idxml_file), path(spectrum_df) + + output: + path "*_psm.csv", emit: psm_info + path "versions.yml", emit: version + path "*.log", emit: log + + script: + def args = task.ext.args ?: '' + def prefix = task.ext.prefix ?: "${meta.mzml_id}" + + + """ + psm_conversion.py "${idxml_file}" \\ + ${spectrum_df} \\ + $params.export_decoy_psm \\ + 2>&1 | tee extract_idxml.log + + cat <<-END_VERSIONS > versions.yml + "${task.process}": + pyopenms: \$(pip show pyopenms | grep "Version" | awk -F ': ' '{print \$2}') + END_VERSIONS + """ +} diff --git a/modules/local/extract_psm/meta.yml b/modules/local/extract_psm/meta.yml new file mode 100644 index 00000000..23586d7e --- /dev/null +++ b/modules/local/extract_psm/meta.yml @@ -0,0 +1,34 @@ +name: PSMCONVERSION +description: A module to extract PSM information from idXML file +keywords: + - PSM + - conversion +tools: + - custom: + description: | + A custom module for PSM extraction. + homepage: https://github.com/bigbio/quantms + documentation: https://github.com/bigbio/quantms/tree/readthedocs +input: + - idxml_file: + type: file + description: idXML identification file + pattern: "*.idXML" + - spectrum_df: + type: file + description: spectrum data file + pattern: "_spectrum_df.csv" + - meta: + type: map + description: Groovy Map containing sample information +output: + - psm_info: + type: file + description: PSM csv file + pattern: "*_psm.csv" + - version: + type: file + description: File containing software version + pattern: "versions.yml" +authors: + - "@daichengxin" diff --git a/modules/local/mzmlstatistics/main.nf b/modules/local/mzmlstatistics/main.nf index f39c8bb6..51770947 100644 --- a/modules/local/mzmlstatistics/main.nf +++ b/modules/local/mzmlstatistics/main.nf @@ -15,6 +15,7 @@ process MZMLSTATISTICS { output: path "*_ms_info.tsv", emit: ms_statistics + tuple val(meta), path("*_spectrum_df.csv"), emit: spectrum_df path "versions.yml", emit: version path "*.log", emit: log @@ -24,6 +25,7 @@ process MZMLSTATISTICS { """ mzml_statistics.py "${ms_file}" \\ + $params.id_only \\ 2>&1 | tee mzml_statistics.log cat <<-END_VERSIONS > versions.yml diff --git a/modules/local/mzmlstatistics/meta.yml b/modules/local/mzmlstatistics/meta.yml index d1fab0da..59a1b451 100644 --- a/modules/local/mzmlstatistics/meta.yml +++ b/modules/local/mzmlstatistics/meta.yml @@ -19,6 +19,10 @@ output: type: file description: mzMLs statistics file pattern: "*_mzml_info.tsv" + - spectrum_df: + type: file + description: spectrum data file + pattern: "_spectrum_df.csv" - version: type: file description: File containing software version diff --git a/modules/local/openms/thirdparty/searchenginemsgf/main.nf b/modules/local/openms/thirdparty/searchenginemsgf/main.nf index cfa0eb2a..b76b1106 100644 --- a/modules/local/openms/thirdparty/searchenginemsgf/main.nf +++ b/modules/local/openms/thirdparty/searchenginemsgf/main.nf @@ -49,9 +49,11 @@ process SEARCHENGINEMSGF { } num_enzyme_termini = "" + max_missed_cleavages = "-max_missed_cleavages ${params.allowed_missed_cleavages}" if (meta.enzyme == "unspecific cleavage") { num_enzyme_termini = "none" + max_missed_cleavages = "" } else if (params.num_enzyme_termini == "fully") { @@ -75,7 +77,7 @@ process SEARCHENGINEMSGF { -max_precursor_charge $params.max_precursor_charge \\ -min_peptide_length $params.min_peptide_length \\ -max_peptide_length $params.max_peptide_length \\ - -max_missed_cleavages $params.allowed_missed_cleavages \\ + ${max_missed_cleavages} \\ -isotope_error_range $params.isotope_error_range \\ -enzyme "${enzyme}" \\ -tryptic ${msgf_num_enzyme_termini} \\ diff --git a/modules/nf-core/multiqc/environment.yml b/modules/nf-core/multiqc/environment.yml index 2212096a..ca39fb67 100644 --- a/modules/nf-core/multiqc/environment.yml +++ b/modules/nf-core/multiqc/environment.yml @@ -4,4 +4,4 @@ channels: - bioconda - defaults dependencies: - - bioconda::multiqc=1.20 + - bioconda::multiqc=1.21 diff --git a/modules/nf-core/multiqc/main.nf b/modules/nf-core/multiqc/main.nf index 354f4430..47ac352f 100644 --- a/modules/nf-core/multiqc/main.nf +++ b/modules/nf-core/multiqc/main.nf @@ -3,8 +3,8 @@ process MULTIQC { 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' }" + 'https://depot.galaxyproject.org/singularity/multiqc:1.21--pyhdfd78af_0' : + 'biocontainers/multiqc:1.21--pyhdfd78af_0' }" input: path multiqc_files, stageAs: "?/*" diff --git a/modules/nf-core/multiqc/tests/main.nf.test.snap b/modules/nf-core/multiqc/tests/main.nf.test.snap index c204b488..bfebd802 100644 --- a/modules/nf-core/multiqc/tests/main.nf.test.snap +++ b/modules/nf-core/multiqc/tests/main.nf.test.snap @@ -2,14 +2,14 @@ "multiqc_versions_single": { "content": [ [ - "versions.yml:md5,d320d4c37e349c5588e07e7a31cd4186" + "versions.yml:md5,21f35ee29416b9b3073c28733efe4b7d" ] ], "meta": { "nf-test": "0.8.4", "nextflow": "23.10.1" }, - "timestamp": "2024-02-14T09:28:51.744211298" + "timestamp": "2024-02-29T08:48:55.657331" }, "multiqc_stub": { "content": [ @@ -17,25 +17,25 @@ "multiqc_report.html", "multiqc_data", "multiqc_plots", - "versions.yml:md5,d320d4c37e349c5588e07e7a31cd4186" + "versions.yml:md5,21f35ee29416b9b3073c28733efe4b7d" ] ], "meta": { "nf-test": "0.8.4", "nextflow": "23.10.1" }, - "timestamp": "2024-02-14T09:29:28.847433492" + "timestamp": "2024-02-29T08:49:49.071937" }, "multiqc_versions_config": { "content": [ [ - "versions.yml:md5,d320d4c37e349c5588e07e7a31cd4186" + "versions.yml:md5,21f35ee29416b9b3073c28733efe4b7d" ] ], "meta": { "nf-test": "0.8.4", "nextflow": "23.10.1" }, - "timestamp": "2024-02-14T09:29:13.223621555" + "timestamp": "2024-02-29T08:49:25.457567" } } \ No newline at end of file diff --git a/nextflow.config b/nextflow.config index 09aeb989..8cd10ce8 100644 --- a/nextflow.config +++ b/nextflow.config @@ -15,6 +15,7 @@ params { local_input_type = 'mzML' database = null acquisition_method = null + id_only = false // Input options input = null @@ -22,6 +23,7 @@ params { // Tools flags posterior_probabilities = 'percolator' add_decoys = false + skip_rescoring = false search_engines = 'comet' sage_processes = 1 run_fdr_cutoff = 0.10 @@ -106,6 +108,9 @@ params { // IDPEP flags outlier_handling = "none" + // DDA_ID flags + export_decoy_psm = true + // Percolator flags train_FDR = 0.05 test_FDR = 0.05 diff --git a/nextflow_schema.json b/nextflow_schema.json index b3dd125b..8375cfa3 100644 --- a/nextflow_schema.json +++ b/nextflow_schema.json @@ -58,6 +58,18 @@ "description": "Proteomics data acquisition method", "enum": ["dda", "dia"], "fa_icon": "far fa-list-ol" + }, + "id_only": { + "type": "boolean", + "description": "Only perform identification subworkflow.", + "fa_icon": "far fa-check-square", + "help_text": "Only perform identification subworkflow for specific cases." + }, + "export_decoy_psm": { + "type": "boolean", + "description": "Whether export PSM from decoy in final identification results", + "fa_icon": "far fa-check-square", + "help_text": "Whether export PSM from decoy in final identification results for dda_id subworkflow for specific cases." } } }, @@ -416,6 +428,12 @@ "description": "Choose between different rescoring/posterior probability calculation methods and set them up.", "default": "", "properties": { + "skip_rescoring": { + "type": "boolean", + "description": "Skip PSM rescoring steps for specific cases, such as studying pure search engine results and search engine ranks", + "default": false, + "fa_icon": "far fa-check-square" + }, "posterior_probabilities": { "type": "string", "description": "How to calculate posterior probabilities for PSMs:\n\n* 'percolator' = Re-score based on PSM-feature-based SVM and transform distance\n to hyperplane for posteriors\n* 'fit_distributions' = Fit positive and negative distributions to scores\n (similar to PeptideProphet)", diff --git a/subworkflows/local/dda_id.nf b/subworkflows/local/dda_id.nf new file mode 100644 index 00000000..c98b1c55 --- /dev/null +++ b/subworkflows/local/dda_id.nf @@ -0,0 +1,104 @@ +// +// MODULE: Local to the pipeline +// +include { DECOYDATABASE } from '../../modules/local/openms/decoydatabase/main' +include { CONSENSUSID } from '../../modules/local/openms/consensusid/main' +include { EXTRACTPSMFEATURES } from '../../modules/local/openms/extractpsmfeatures/main' +include { PERCOLATOR } from '../../modules/local/openms/thirdparty/percolator/main' +include { FALSEDISCOVERYRATE as FDRIDPEP } from '../../modules/local/openms/falsediscoveryrate/main' +include { IDPEP } from '../../modules/local/openms/idpep/main' +include { PSMCONVERSION } from '../../modules/local/extract_psm/main' + +// +// SUBWORKFLOW: Consisting of a mix of local and nf-core/modules +// +include { DATABASESEARCHENGINES } from './databasesearchengines' +include { PSMFDRCONTROL } from './psmfdrcontrol' + +workflow DDA_ID { + take: + ch_file_preparation_results + ch_database_wdecoy + ch_spectrum_data + + main: + + ch_software_versions = Channel.empty() + + // + // SUBWORKFLOW: DatabaseSearchEngines + // + DATABASESEARCHENGINES ( + ch_file_preparation_results, + ch_database_wdecoy + ) + ch_software_versions = ch_software_versions.mix(DATABASESEARCHENGINES.out.versions.ifEmpty(null)) + ch_id_files = DATABASESEARCHENGINES.out.ch_id_files_idx + + ch_id_files.branch{ meta, filename -> + sage: filename.name.contains('sage') + return [meta, filename] + nosage: true + return [meta, filename] + }.set{ch_id_files_branched} + + + // + // SUBWORKFLOW: Rescoring + // + if (params.skip_rescoring == false) { + if (params.posterior_probabilities == 'percolator') { + EXTRACTPSMFEATURES(ch_id_files_branched.nosage) + ch_id_files_feats = ch_id_files_branched.sage.mix(EXTRACTPSMFEATURES.out.id_files_feat) + ch_software_versions = ch_software_versions.mix(EXTRACTPSMFEATURES.out.version) + PERCOLATOR(ch_id_files_feats) + ch_software_versions = ch_software_versions.mix(PERCOLATOR.out.version) + ch_consensus_input = PERCOLATOR.out.id_files_perc + } + + + if (params.posterior_probabilities != 'percolator') { + ch_fdridpep = Channel.empty() + if (params.search_engines.split(",").size() == 1) { + FDRIDPEP(ch_id_files) + ch_software_versions = ch_software_versions.mix(FDRIDPEP.out.version) + ch_id_files = Channel.empty() + ch_fdridpep = FDRIDPEP.out.id_files_idx_ForIDPEP_FDR + } + IDPEP(ch_fdridpep.mix(ch_id_files)) + ch_software_versions = ch_software_versions.mix(IDPEP.out.version) + ch_consensus_input = IDPEP.out.id_files_ForIDPEP + } + + // + // SUBWORKFLOW: PSMFDRCONTROL + // + ch_psmfdrcontrol = Channel.empty() + ch_consensus_results = Channel.empty() + if (params.search_engines.split(",").size() > 1) { + CONSENSUSID(ch_consensus_input.groupTuple(size: params.search_engines.split(",").size())) + ch_software_versions = ch_software_versions.mix(CONSENSUSID.out.version.ifEmpty(null)) + ch_psmfdrcontrol = CONSENSUSID.out.consensusids + ch_consensus_results = CONSENSUSID.out.consensusids + } else { + ch_psmfdrcontrol = ch_consensus_input + } + + PSMFDRCONTROL(ch_psmfdrcontrol) + ch_software_versions = ch_software_versions.mix(PSMFDRCONTROL.out.version.ifEmpty(null)) + + // + // Extract PSMs and export parquet format + // + ch_spectrum_data.view() + PSMFDRCONTROL.out.id_filtered.view() + PSMCONVERSION(PSMFDRCONTROL.out.id_filtered.combine(ch_spectrum_data, by: 0)) + + } else { + PSMCONVERSION(ch_id_files.combine(ch_spectrum_data, by: 0)) + } + + + emit: + version = ch_software_versions +} diff --git a/subworkflows/local/file_preparation.nf b/subworkflows/local/file_preparation.nf index 41d6e637..a2b73fa1 100644 --- a/subworkflows/local/file_preparation.nf +++ b/subworkflows/local/file_preparation.nf @@ -18,6 +18,7 @@ workflow FILE_PREPARATION { ch_results = Channel.empty() ch_statistics = Channel.empty() ch_mqc_data = Channel.empty() + ch_spectrum_df = Channel.empty() // Divide the compressed files ch_rawfiles @@ -80,8 +81,11 @@ workflow FILE_PREPARATION { ch_results = indexed_mzml_bundle.mix(ch_branched_input.dotd) } + MZMLSTATISTICS(ch_results) ch_statistics = ch_statistics.mix(MZMLSTATISTICS.out.ms_statistics.collect()) + ch_spectrum_df = ch_spectrum_df.mix(MZMLSTATISTICS.out.spectrum_df) + ch_versions = ch_versions.mix(MZMLSTATISTICS.out.version) if (params.openms_peakpicking) { @@ -97,6 +101,7 @@ workflow FILE_PREPARATION { emit: results = ch_results // channel: [val(mzml_id), indexedmzml|.d.tar] statistics = ch_statistics // channel: [ *_ms_info.tsv ] + spectrum_data = ch_spectrum_df // channel: [val(mzml_id), *_spectrum_df.csv] version = ch_versions // channel: [ *.version.txt ] } diff --git a/subworkflows/local/utils_nfcore_quantms_pipeline/main.nf b/subworkflows/local/utils_nfcore_quantms_pipeline/main.nf index dc90ad90..2a03b6fa 100644 --- a/subworkflows/local/utils_nfcore_quantms_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_quantms_pipeline/main.nf @@ -1,5 +1,5 @@ // -// Subworkflow with functionality specific to the nf-core/pipeline pipeline +// Subworkflow with functionality specific to the nf-core/quantms pipeline // /* @@ -152,7 +152,9 @@ workflow PIPELINE_COMPLETION { // def validateInputParameters() { genomeExistsError() -}// +} + +// // Validate channels from input samplesheet // def validateInputSamplesheet(input) { @@ -190,7 +192,9 @@ def genomeExistsError() { "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" error(error_string) } -}// +} + +// // Generate methods description for MultiQC // def toolCitationText() { diff --git a/subworkflows/nf-core/utils_nextflow_pipeline/tests/main.function.nf.test b/subworkflows/nf-core/utils_nextflow_pipeline/tests/main.function.nf.test index 8ed4310c..68718e4f 100644 --- a/subworkflows/nf-core/utils_nextflow_pipeline/tests/main.function.nf.test +++ b/subworkflows/nf-core/utils_nextflow_pipeline/tests/main.function.nf.test @@ -51,4 +51,4 @@ nextflow_function { ) } } -} \ No newline at end of file +} diff --git a/subworkflows/nf-core/utils_nextflow_pipeline/tests/main.function.nf.test.snap b/subworkflows/nf-core/utils_nextflow_pipeline/tests/main.function.nf.test.snap index db2030f8..e3f0baf4 100644 --- a/subworkflows/nf-core/utils_nextflow_pipeline/tests/main.function.nf.test.snap +++ b/subworkflows/nf-core/utils_nextflow_pipeline/tests/main.function.nf.test.snap @@ -3,10 +3,18 @@ "content": [ "v9.9.9" ], - "timestamp": "2024-01-19T11:32:36.031083" + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-02-28T12:02:05.308243" }, "Test Function checkCondaChannels": { "content": null, - "timestamp": "2024-01-19T11:32:50.456" + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-02-28T12:02:12.425833" } } \ No newline at end of file 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 f7c54bc6..ca964ce8 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 @@ -11,9 +11,6 @@ nextflow_workflow { test("Should run no inputs") { when { - params { - outdir = "tests/results" - } workflow { """ print_version = false @@ -39,9 +36,6 @@ nextflow_workflow { test("Should print version") { when { - params { - outdir = "tests/results" - } workflow { """ print_version = true @@ -68,19 +62,16 @@ nextflow_workflow { test("Should dump params") { when { - params { - outdir = "$outputDir" - } workflow { """ print_version = false dump_parameters = true - outdir = params.outdir + outdir = 'results' check_conda_channels = false input[0] = false input[1] = true - input[2] = params.outdir + input[2] = outdir input[3] = false """ } @@ -96,19 +87,16 @@ nextflow_workflow { test("Should not create params JSON if no output directory") { when { - params { - outdir = "$outputDir" - } workflow { """ print_version = false dump_parameters = true - outdir = params.outdir + outdir = null check_conda_channels = false input[0] = false input[1] = true - input[2] = null + input[2] = outdir input[3] = false """ } diff --git a/subworkflows/nf-core/utils_nextflow_pipeline/tests/nextflow.config b/subworkflows/nf-core/utils_nextflow_pipeline/tests/nextflow.config index 53574ffe..d0a926bf 100644 --- a/subworkflows/nf-core/utils_nextflow_pipeline/tests/nextflow.config +++ b/subworkflows/nf-core/utils_nextflow_pipeline/tests/nextflow.config @@ -6,4 +6,4 @@ manifest { nextflowVersion = '!>=23.04.0' version = '9.9.9' doi = 'https://doi.org/10.5281/zenodo.5070524' -} \ No newline at end of file +} diff --git a/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.function.nf.test.snap b/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.function.nf.test.snap index 10f948e6..1037232c 100644 --- a/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.function.nf.test.snap +++ b/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.function.nf.test.snap @@ -1,25 +1,41 @@ { "Test Function checkProfileProvided": { "content": null, - "timestamp": "2024-02-09T15:43:55.145717" + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-02-28T12:03:03.360873" }, "Test Function checkConfigProvided": { "content": [ true ], - "timestamp": "2024-01-19T11:34:13.548431224" + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-02-28T12:02:59.729647" }, "Test Function nfCoreLogo": { "content": [ "\n\n-\u001b[2m----------------------------------------------------\u001b[0m-\n \u001b[0;32m,--.\u001b[0;30m/\u001b[0;32m,-.\u001b[0m\n\u001b[0;34m ___ __ __ __ ___ \u001b[0;32m/,-._.--~'\u001b[0m\n\u001b[0;34m |\\ | |__ __ / ` / \\ |__) |__ \u001b[0;33m} {\u001b[0m\n\u001b[0;34m | \\| | \\__, \\__/ | \\ |___ \u001b[0;32m\\`-._,-`-,\u001b[0m\n \u001b[0;32m`._,._,'\u001b[0m\n\u001b[0;35m nextflow_workflow v9.9.9\u001b[0m\n-\u001b[2m----------------------------------------------------\u001b[0m-\n" ], - "timestamp": "2024-01-19T11:34:38.840454873" + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-02-28T12:03:10.562934" }, "Test Function workflowCitation": { "content": [ "If you use nextflow_workflow for your analysis please cite:\n\n* The pipeline\n https://doi.org/10.5281/zenodo.5070524\n\n* The nf-core framework\n https://doi.org/10.1038/s41587-020-0439-x\n\n* Software dependencies\n https://github.com/nextflow_workflow/blob/master/CITATIONS.md" ], - "timestamp": "2024-01-19T11:34:22.24352016" + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-02-28T12:03:07.019761" }, "Test Function without logColours": { "content": [ @@ -73,13 +89,21 @@ "biwhite": "" } ], - "timestamp": "2024-01-19T11:35:04.418416984" + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-02-28T12:03:17.969323" }, "Test Function dashedLine": { "content": [ "-\u001b[2m----------------------------------------------------\u001b[0m-" ], - "timestamp": "2024-01-19T11:34:55.420000755" + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-02-28T12:03:14.366181" }, "Test Function with logColours": { "content": [ @@ -133,6 +157,10 @@ "biwhite": "\u001b[1;97m" } ], - "timestamp": "2024-01-19T11:35:13.436366565" + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-02-28T12:03:21.714424" } } \ No newline at end of file diff --git a/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.workflow.nf.test.snap b/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.workflow.nf.test.snap index d07ce54c..859d1030 100644 --- a/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.workflow.nf.test.snap +++ b/subworkflows/nf-core/utils_nfcore_pipeline/tests/main.workflow.nf.test.snap @@ -10,6 +10,10 @@ ] } ], - "timestamp": "2024-01-19T11:35:22.538940073" + "meta": { + "nf-test": "0.8.4", + "nextflow": "23.10.1" + }, + "timestamp": "2024-02-28T12:03:25.726491" } } \ No newline at end of file diff --git a/subworkflows/nf-core/utils_nfvalidation_plugin/tests/main.nf.test b/subworkflows/nf-core/utils_nfvalidation_plugin/tests/main.nf.test index 517ee54e..5784a33f 100644 --- a/subworkflows/nf-core/utils_nfvalidation_plugin/tests/main.nf.test +++ b/subworkflows/nf-core/utils_nfvalidation_plugin/tests/main.nf.test @@ -197,4 +197,4 @@ nextflow_workflow { ) } } -} \ No newline at end of file +} diff --git a/workflows/quantms.nf b/workflows/quantms.nf index 75097a7b..e0772204 100644 --- a/workflows/quantms.nf +++ b/workflows/quantms.nf @@ -21,6 +21,7 @@ include { DECOYDATABASE } from '../modules/local/openms/decoydatabase/main' include { INPUT_CHECK } from '../subworkflows/local/input_check' include { FILE_PREPARATION } from '../subworkflows/local/file_preparation' include { CREATE_INPUT_CHANNEL } from '../subworkflows/local/create_input_channel' +include { DDA_ID } from '../subworkflows/local/dda_id' /* @@ -103,27 +104,34 @@ workflow QUANTMS { ch_versions = ch_versions.mix(DECOYDATABASE.out.version.ifEmpty(null)) } - - TMT(ch_fileprep_result.iso, CREATE_INPUT_CHANNEL.out.ch_expdesign, ch_searchengine_in_db) - ch_ids_pmultiqc = ch_ids_pmultiqc.mix(TMT.out.ch_pmultiqc_ids) - ch_consensus_pmultiqc = ch_consensus_pmultiqc.mix(TMT.out.ch_pmultiqc_consensus) - ch_pipeline_results = ch_pipeline_results.mix(TMT.out.final_result) - ch_msstats_in = ch_msstats_in.mix(TMT.out.msstats_in) - - LFQ(ch_fileprep_result.lfq, CREATE_INPUT_CHANNEL.out.ch_expdesign, ch_searchengine_in_db) - ch_ids_pmultiqc = ch_ids_pmultiqc.mix(LFQ.out.ch_pmultiqc_ids) - ch_consensus_pmultiqc = ch_consensus_pmultiqc.mix(LFQ.out.ch_pmultiqc_consensus) - ch_pipeline_results = ch_pipeline_results.mix(LFQ.out.final_result) - ch_msstats_in = ch_msstats_in.mix(LFQ.out.msstats_in) - - DIA(ch_fileprep_result.dia, CREATE_INPUT_CHANNEL.out.ch_expdesign, FILE_PREPARATION.out.statistics) - ch_pipeline_results = ch_pipeline_results.mix(DIA.out.diann_report) - ch_msstats_in = ch_msstats_in.mix(DIA.out.msstats_in) + if (params.id_only) { + DDA_ID( FILE_PREPARATION.out.results, ch_searchengine_in_db, FILE_PREPARATION.out.spectrum_data) + ch_versions = ch_versions.mix(DDA_ID.out.version.ifEmpty(null)) + } else { + TMT(ch_fileprep_result.iso, CREATE_INPUT_CHANNEL.out.ch_expdesign, ch_searchengine_in_db) + ch_ids_pmultiqc = ch_ids_pmultiqc.mix(TMT.out.ch_pmultiqc_ids) + ch_consensus_pmultiqc = ch_consensus_pmultiqc.mix(TMT.out.ch_pmultiqc_consensus) + ch_pipeline_results = ch_pipeline_results.mix(TMT.out.final_result) + ch_msstats_in = ch_msstats_in.mix(TMT.out.msstats_in) + ch_versions = ch_versions.mix(TMT.out.versions.ifEmpty(null)) + + LFQ(ch_fileprep_result.lfq, CREATE_INPUT_CHANNEL.out.ch_expdesign, ch_searchengine_in_db) + ch_ids_pmultiqc = ch_ids_pmultiqc.mix(LFQ.out.ch_pmultiqc_ids) + ch_consensus_pmultiqc = ch_consensus_pmultiqc.mix(LFQ.out.ch_pmultiqc_consensus) + ch_pipeline_results = ch_pipeline_results.mix(LFQ.out.final_result) + ch_msstats_in = ch_msstats_in.mix(LFQ.out.msstats_in) + ch_versions = ch_versions.mix(LFQ.out.versions.ifEmpty(null)) + + DIA(ch_fileprep_result.dia, CREATE_INPUT_CHANNEL.out.ch_expdesign, FILE_PREPARATION.out.statistics) + ch_pipeline_results = ch_pipeline_results.mix(DIA.out.diann_report) + ch_msstats_in = ch_msstats_in.mix(DIA.out.msstats_in) + ch_versions = ch_versions.mix(DIA.out.versions.ifEmpty(null)) + } // // Collate and save software versions // - DIA.out.versions.mix(LFQ.out.versions).mix(TMT.out.versions).mix(ch_versions) + ch_versions .branch { yaml : it.asBoolean() other : true