diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 7d1f9dfd..4ae42831 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -8,14 +8,14 @@ These are the most common things requested on pull requests (PRs). Remember that PRs should be made against the dev branch, unless you're preparing a pipeline release. -Learn more about contributing: [CONTRIBUTING.md](https://github.com/nf-core/spatialaxe/tree/main/.github/CONTRIBUTING.md) +Learn more about contributing: [CONTRIBUTING.md](https://github.com/nf-core/spatialaxe/tree/master/docs/CONTRIBUTING.md) --> ## PR checklist - [ ] This comment contains a description of changes (with reason). - [ ] If you've fixed a bug or added code that should be tested, add tests! -- [ ] If you've added a new tool - have you followed the pipeline conventions in the [contribution docs](https://github.com/nf-core/spatialaxe/tree/main/.github/CONTRIBUTING.md) +- [ ] If you've added a new tool - have you followed the pipeline conventions in the [contribution docs](https://github.com/nf-core/spatialaxe/tree/master/docs/CONTRIBUTING.md) - [ ] If necessary, also make a PR on the nf-core/spatialaxe _branch_ on the [nf-core/test-datasets](https://github.com/nf-core/test-datasets) repository. - [ ] Make sure your code lints (`nf-core pipelines lint`). - [ ] Ensure the test suite passes (`nextflow run . -profile test,docker --outdir `). diff --git a/.github/actions/nf-test/action.yml b/.github/actions/nf-test/action.yml index 3b9724c7..7964ef2b 100644 --- a/.github/actions/nf-test/action.yml +++ b/.github/actions/nf-test/action.yml @@ -37,7 +37,7 @@ runs: - name: Setup apptainer if: contains(inputs.profile, 'singularity') - uses: eWaterCycle/setup-apptainer@main + uses: eWaterCycle/setup-apptainer@3f706d898c9db585b1d741b4692e66755f3a1b40 # v2.0.0 - name: Set up Singularity if: contains(inputs.profile, 'singularity') diff --git a/.github/workflows/awsfulltest.yml b/.github/workflows/awsfulltest.yml index 874c73e8..02089ec8 100644 --- a/.github/workflows/awsfulltest.yml +++ b/.github/workflows/awsfulltest.yml @@ -32,9 +32,28 @@ jobs: compute_env: ${{ vars.TOWER_COMPUTE_ENV }} revision: ${{ steps.revision.outputs.revision }} workdir: s3://${{ vars.AWS_S3_BUCKET }}/work/spatialaxe/work-${{ steps.revision.outputs.revision }} + nextflow_config: | + plugins { + id 'nf-slack@0.5.0' + } + slack { + enabled = true + bot { + token = '${{ secrets.NFSLACK_BOT_TOKEN }}' + channel = 'spatialaxe' + } + onStart { + enabled = false + } + onComplete { + message = ':white_check_mark: *spatialaxe/test_full* completed successfully! :tada:' + } + onError { + message = ':x: *spatialaxe/test_full* failed :crying_cat_face:' + } + } parameters: | { - "hook_url": "${{ secrets.MEGATESTS_ALERTS_SLACK_HOOK_URL }}", "outdir": "s3://${{ vars.AWS_S3_BUCKET }}/spatialaxe/results-${{ steps.revision.outputs.revision }}" } profiles: test_full diff --git a/.github/workflows/linting.yml b/.github/workflows/linting.yml index 0fd78823..bc32219b 100644 --- a/.github/workflows/linting.yml +++ b/.github/workflows/linting.yml @@ -11,33 +11,31 @@ jobs: pre-commit: runs-on: ubuntu-latest steps: - - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - - name: Set up Python 3.14 - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6 - with: - python-version: "3.14" - - - name: Install pre-commit - run: pip install pre-commit + - name: Install Nextflow + uses: nf-core/setup-nextflow@b4ec1bc7c16a94435159de94a05253542fddf6ef # v3 - - name: Run pre-commit - run: pre-commit run --all-files + - name: Run prek + uses: j178/prek-action@6ad80277337ad479fe43bd70701c3f7f8aa74db3 # v2 nf-core: runs-on: ubuntu-latest steps: - name: Check out pipeline code - uses: actions/checkout@93cb6efe18208431cddfb8368fd83d5badbf9bfd # v5 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6 - name: Install Nextflow - uses: nf-core/setup-nextflow@v2 + uses: nf-core/setup-nextflow@b4ec1bc7c16a94435159de94a05253542fddf6ef # v3 - - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6 + - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6 with: python-version: "3.14" architecture: "x64" + - name: Setup uv + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 + - name: read .nf-core.yml uses: pietrobolcato/action-read-yaml@9f13718d61111b69f30ab4ac683e67a56d254e1d # 1.1.0 id: read_yml @@ -45,12 +43,10 @@ jobs: config: ${{ github.workspace }}/.nf-core.yml - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install nf-core==${{ steps.read_yml.outputs['nf_core_version'] }} + run: uv tool install nf-core==${{ steps.read_yml.outputs['nf_core_version'] }} - name: Run nf-core pipelines lint - if: ${{ github.base_ref != 'master' }} + if: ${{ github.base_ref != 'master' || github.base_ref != 'main' }} env: GITHUB_COMMENTS_URL: ${{ github.event.pull_request.comments_url }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -58,7 +54,7 @@ jobs: run: nf-core -l lint_log.txt pipelines lint --dir ${GITHUB_WORKSPACE} --markdown lint_results.md - name: Run nf-core pipelines lint --release - if: ${{ github.base_ref == 'master' }} + if: ${{ github.base_ref == 'master' || github.base_ref == 'main' }} env: GITHUB_COMMENTS_URL: ${{ github.event.pull_request.comments_url }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -71,7 +67,7 @@ jobs: - name: Upload linting log file artifact if: ${{ always() }} - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 with: name: linting-logs path: | diff --git a/.github/workflows/release-announcements.yml b/.github/workflows/release-announcements.yml index 431d3d44..abc44c92 100644 --- a/.github/workflows/release-announcements.yml +++ b/.github/workflows/release-announcements.yml @@ -18,7 +18,7 @@ jobs: id: get_description run: | echo "description=$(curl -s https://nf-co.re/pipelines.json | jq -r '.remote_workflows[] | select(.full_name == "${{ github.repository }}") | .description')" >> $GITHUB_OUTPUT - - uses: rzr/fediverse-action@master + - uses: rzr/fediverse-action@66c2cbb5b1997666b0e28d597631b6a4f09a2719 # v0.0.6 with: access-token: ${{ secrets.MASTODON_ACCESS_TOKEN }} host: "mstdn.science" # custom host if not "mastodon.social" (default) diff --git a/.nf-core.yml b/.nf-core.yml index 237f6130..43484e56 100644 --- a/.nf-core.yml +++ b/.nf-core.yml @@ -10,9 +10,7 @@ lint: - docs/images/nf-core-spatialaxe_logo_dark.png - docs/images/nf-core-spatialaxe_logo_light.png - .github/PULL_REQUEST_TEMPLATE.md - - .github/workflows/branch.yml - - .github/workflows/linting.yml -nf_core_version: 3.5.2 +nf_core_version: 4.0.3 repository_type: pipeline template: author: Sameesh Kher, Dongze He, Florian Heyl @@ -24,4 +22,4 @@ template: outdir: . skip_features: - igenomes - version: 1.0.0 + version: 1.0.1 diff --git a/CHANGELOG.md b/CHANGELOG.md index d7e6cef7..8e0605fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,24 @@ 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.0 - [date] +## 1.0.1 - [31.07.2026] + +Hotfix to tackle some bugs + +### `Added` + +- Template update for nf-core/tools version 4.0.3 + +### `Fixed` + +- Only pass `--expansion-distance` to `xeniumranger import-segmentation` for nuclei-based imports. It was applied to every import, but xeniumranger rejects it for transcript-assignment imports (proseg/baysor/segger) and cells-only imports with `ERROR: --expansion-distance requires --nuclei`. +- Preserve the URI scheme (e.g. `s3://`) when building Xenium bundle child paths, so bundle validation works when the work directory is on object storage (S3/GCS/Azure). Previously `Path.toString()` dropped the scheme and the resulting path was resolved on the local filesystem, causing `Xenium bundle does not exist` / `NoSuchFileException` failures on AWS Batch. + +### `Dependencies` + +### `Deprecated` + +## 1.0.0 - [18.06.2026] Initial release of nf-core/spatialaxe, created with the [nf-core](https://nf-co.re/) template. diff --git a/README.md b/README.md index ddc05520..ba9c04fd 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ -[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new/nf-core/spatialaxe) +[![Open in GitHub Codespaces](https://img.shields.io/badge/Open_In_GitHub_Codespaces-black?labelColor=grey&logo=github)](https://github.com/codespaces/new/nf-core/spatialaxe) [![GitHub Actions CI Status](https://github.com/nf-core/spatialaxe/actions/workflows/nf-test.yml/badge.svg)](https://github.com/nf-core/spatialaxe/actions/workflows/nf-test.yml) [![GitHub Actions Linting Status](https://github.com/nf-core/spatialaxe/actions/workflows/linting.yml/badge.svg)](https://github.com/nf-core/spatialaxe/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/spatialaxe/results)[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.20733817-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.20733817) [![nf-test](https://img.shields.io/badge/unit_tests-nf--test-337ab7.svg)](https://www.nf-test.com) diff --git a/assets/multiqc_config.yml b/assets/multiqc_config.yml index 8cff5748..2e796b9a 100644 --- a/assets/multiqc_config.yml +++ b/assets/multiqc_config.yml @@ -1,5 +1,5 @@ report_comment: > - This report has been generated by the nf-core/spatialaxe analysis pipeline. For information about how to interpret these results, please see the documentation. + This report has been generated by the nf-core/spatialaxe analysis pipeline. For information about how to interpret these results, please see the documentation. report_section_order: "nf-core-spatialaxe-methods-description": order: -1000 diff --git a/assets/nf-core-spatialaxe_logo_light.png b/assets/nf-core-spatialaxe_logo_light.png index c7a25c98..933ee0ee 100644 Binary files a/assets/nf-core-spatialaxe_logo_light.png and b/assets/nf-core-spatialaxe_logo_light.png differ diff --git a/conf/containers_conda_lock_files_amd64.config b/conf/containers_conda_lock_files_amd64.config new file mode 100644 index 00000000..f6b0997e --- /dev/null +++ b/conf/containers_conda_lock_files_amd64.config @@ -0,0 +1 @@ +process { withName: 'MULTIQC' { container = 'https://wave.seqera.io/v1alpha1/builds/bd-ee7739d47738383b_1/condalock' } } diff --git a/conf/containers_conda_lock_files_arm64.config b/conf/containers_conda_lock_files_arm64.config new file mode 100644 index 00000000..0cd12a28 --- /dev/null +++ b/conf/containers_conda_lock_files_arm64.config @@ -0,0 +1 @@ +process { withName: 'MULTIQC' { container = 'https://wave.seqera.io/v1alpha1/builds/bd-58d7dee710ab3aa8_1/condalock' } } diff --git a/conf/containers_docker_amd64.config b/conf/containers_docker_amd64.config new file mode 100644 index 00000000..dd93726b --- /dev/null +++ b/conf/containers_docker_amd64.config @@ -0,0 +1 @@ +process { withName: 'MULTIQC' { container = 'community.wave.seqera.io/library/multiqc:1.33--ee7739d47738383b' } } diff --git a/conf/containers_docker_arm64.config b/conf/containers_docker_arm64.config new file mode 100644 index 00000000..23418fef --- /dev/null +++ b/conf/containers_docker_arm64.config @@ -0,0 +1 @@ +process { withName: 'MULTIQC' { container = 'community.wave.seqera.io/library/multiqc:1.33--58d7dee710ab3aa8' } } diff --git a/conf/containers_singularity_https_amd64.config b/conf/containers_singularity_https_amd64.config new file mode 100644 index 00000000..d04c5be3 --- /dev/null +++ b/conf/containers_singularity_https_amd64.config @@ -0,0 +1 @@ +process { withName: 'MULTIQC' { container = 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/34/34e733a9ae16a27e80fe00f863ea1479c96416017f24a907996126283e7ecd4d/data' } } diff --git a/conf/containers_singularity_https_arm64.config b/conf/containers_singularity_https_arm64.config new file mode 100644 index 00000000..4a031237 --- /dev/null +++ b/conf/containers_singularity_https_arm64.config @@ -0,0 +1 @@ +process { withName: 'MULTIQC' { container = 'https://community-cr-prod.seqera.io/docker/registry/v2/blobs/sha256/78/78b89e91d89e9cc99ad5ade5be311f347838cb2acbfb4f13bc343b170be09ce4/data' } } diff --git a/conf/containers_singularity_oras_amd64.config b/conf/containers_singularity_oras_amd64.config new file mode 100644 index 00000000..2d8d51fc --- /dev/null +++ b/conf/containers_singularity_oras_amd64.config @@ -0,0 +1 @@ +process { withName: 'MULTIQC' { container = 'oras://community.wave.seqera.io/library/multiqc:1.33--e3576ddf588fa00d' } } diff --git a/conf/containers_singularity_oras_arm64.config b/conf/containers_singularity_oras_arm64.config new file mode 100644 index 00000000..c3210dd0 --- /dev/null +++ b/conf/containers_singularity_oras_arm64.config @@ -0,0 +1 @@ +process { withName: 'MULTIQC' { container = 'oras://community.wave.seqera.io/library/multiqc:1.33--2537ca5f8445e3c2' } } diff --git a/conf/modules.config b/conf/modules.config index 845f0df4..81cf6a25 100644 --- a/conf/modules.config +++ b/conf/modules.config @@ -82,9 +82,6 @@ process { path: "${params.outdir}/${params.mode}/xeniumranger/import_segementation", mode: params.publish_dir_mode, ] - ext.args = {[ - params.expansion_distance != null ? "--expansion-distance=${params.expansion_distance}" : "", - ].join(' ').trim()} } // ---------------------------- proseg --------------------------------------------------- diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md new file mode 100644 index 00000000..55e68d47 --- /dev/null +++ b/docs/CONTRIBUTING.md @@ -0,0 +1,192 @@ +--- +title: Contributing +markdownPlugin: checklist +--- + +# `nf-core/spatialaxe`: Contributing guidelines + +Hi there! +Thanks for taking an interest in improving nf-core/spatialaxe. + +This page describes the recommended nf-core way to contribute to both nf-core/spatialaxe and nf-core pipelines in general, including: + +- [General contribution guidelines](#general-contribution-guidelines): common procedures or guides across all nf-core pipelines. +- [Pipeline-specific contribution guidelines](#pipeline-specific-contribution-guidelines): procedures or guides specific to the development conventions of nf-core/spatialaxe. + +> [!NOTE] +> If you need help using or modifying nf-core/spatialaxe, ask on the nf-core Slack [#spatialaxe](https://nfcore.slack.com/channels/spatialaxe) channel ([join our Slack here](https://nf-co.re/join/slack)). + +## General contribution guidelines + +### Contribution quick start + +To contribute code to any nf-core pipeline: + +- [ ] Ensure you have Nextflow, nf-core tools, and nf-test installed. See the [nf-core/tools repository](https://github.com/nf-core/tools) for instructions. +- [ ] Check whether a GitHub [issue](https://github.com/nf-core/spatialaxe/issues) about your idea already exists. If an issue does not exist, create one so that others are aware you are working on it. +- [ ] [Fork](https://help.github.com/en/github/getting-started-with-github/fork-a-repo) the [nf-core/spatialaxe repository](https://github.com/nf-core/spatialaxe) to your GitHub account. +- [ ] Create a branch on your forked repository and make your changes following [pipeline conventions](#pipeline-contribution-conventions) (if applicable). +- [ ] To fix major bugs, name your branch `patch` and follow the [patch release](#patch-release) process. +- [ ] Update relevant documentation within the `docs/` folder, use nf-core/tools to update `nextflow_schema.json`, and update `CITATIONS.md`. +- [ ] Run and/or update tests. See [Testing](#testing) for more information. +- [ ] [Lint](#lint-tests) your code with nf-core/tools. +- [ ] Submit a pull request (PR) against the `dev` branch and request a review. + +If you are not used to this workflow with Git, see the [GitHub documentation](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests) or [Git resources](https://try.github.io/) for more information. + +## Use of AI and LLMs + +The nf-core stance on the use of AI and LLMs is that humans are still ultimately responsible for their submitted code, regardless of the tools they use. + +If you’re using AI tools, try to stick by these guidelines: + +- Keep PRs as small and focussed as possible +- Avoid any unnecessary changes, such as moving or refactoring code (unless that is the explicit intention of the PR) +- Review all generated code yourself before opening a PR, and ensure that you understand it +- Engage with the community review process and expect to make revisions + +For more detail, see the the [blog post](https://nf-co.re/blog/2026/statement-on-ai) for a statement from the nf-core/core team. + +### Getting help + +For further information and help, see the [nf-core/spatialaxe documentation](https://nf-co.re/spatialaxe/usage) or ask on the nf-core [#spatialaxe](https://nfcore.slack.com/channels/spatialaxe) Slack channel ([join our Slack here](https://nf-co.re/join/slack)). + +### GitHub Codespaces + +You can contribute to nf-core/spatialaxe without installing a local development environment on your machine by using [GitHub Codespaces](https://github.com/codespaces). + +[GitHub Codespaces](https://github.com/codespaces) is an online developer environment that runs in your browser, complete with VS Code and a terminal. +Most nf-core repositories include a devcontainer configuration, which creates a GitHub Codespaces environment specifically for Nextflow development. +The environment includes pre-installed nf-core tools, Nextflow, and a few other helpful utilities via a Docker container. + +To get started, open the repository in [Codespaces](https://github.com/nf-core/spatialaxe/codespaces). + +### Testing + +Once you have made your changes, run the pipeline with nf-test to test them locally. +For additional information, use the `--verbose` flag to view the Nextflow console log output. + +```bash +nf-test test --tag test --profile +docker --verbose +``` + +If you have added new functionality, ensure you update the test assertions in the `.nf.test` files in the `tests/` directory. +Update the snapshots with the following command: + +```bash +nf-test test --tag test --profile +docker --verbose --update-snapshots +``` + +When you create a pull request with changes, GitHub Actions will run automatic tests. +Pull requests are typically reviewed when these tests are passing. + +Two types of tests are typically run: + +#### Lint tests + +nf-core has a [set of guidelines](https://nf-co.re/docs/specifications/overview) which all pipelines must follow. +To enforce these, run linting with nf-core/tools: + +```bash +nf-core pipelines lint +``` + +If you encounter failures or warnings, follow the linked documentation printed to screen. +For more information about linting tests, see [nf-core/tools API documentation](https://nf-co.re/docs/nf-core-tools/api_reference/latest/pipeline_lint_tests/actions_awsfulltest). + +#### Pipeline tests + +Each nf-core pipeline should be set up with a minimal set of test data. +GitHub Actions runs the pipeline on this data to ensure it runs through and exits successfully. +If there are any failures then the automated tests fail. +These tests are run with the latest available version of Nextflow and the minimum required version specified in the pipeline code. + +### Patch release + +> [!WARNING] +> Only in the unlikely event of a release that contains a critical bug. + +- [ ] Create a new branch `patch` on your fork based on `upstream/main` or `upstream/master`. +- [ ] Fix the bug and use nf-core/tools to bump the version to the next semantic version, for example, `1.2.3` → `1.2.4`. +- [ ] Open a Pull Request from `patch` directly to `main`/`master` with the changes. + +### Pipeline contribution conventions + +nf-core semi-standardises how you write code and other contributions to make the nf-core/spatialaxe code and processing logic more understandable for new contributors and to ensure quality. + +#### Add a new pipeline step + +To contribute a new step to the pipeline, follow the general nf-core coding procedure. +Please also refer to the [pipeline-specific contribution guidelines](#pipeline-specific-contribution-guidelines): + +- [ ] Define the corresponding [input channel](#channel-naming-schemes) into your new process from the expected previous process channel. +- [ ] Install a module with nf-core/tools, or write a local module (see [default processes resource requirements](#default-processes-resource-requirements)), and add it to the target `.nf`. +- [ ] Define the output channel if needed. Mix the version output channel into `ch_versions` and relevant files into `ch_multiqc`. +- [ ] Add new or updated parameters to `nextflow.config` with a [default value](#default-parameter-values). +- [ ] Add new or updated parameters and relevant help text to `nextflow_schema.json` with [nf-core/tools](#default-parameter-values). +- [ ] Add validation for relevant parameters to the pipeline utilisation section of `utils_nfcore_\_pipeline/main.nf` subworkflow. +- [ ] Perform local tests to validate that the new code works as expected. + - [ ] If applicable, add a new test in the `tests` directory. +- [ ] Update `usage.md`, `output.md`, and `citation.md` as appropriate. +- [ ] [Lint](lint) the code with nf-core/tools. +- [ ] Update any diagrams or pipeline images as necessary. +- [ ] Update MultiQC config `assets/multiqc_config.yml` so relevant suffixes, file name cleanup, and module plots are in the appropriate order. +- [ ] If applicable, create a [MultiQC](https://seqera.io/multiqc/) module. +- [ ] Add a description of the output files and, if relevant, images from the MultiQC report to `docs/output.md`. + +To update the minimum required Nextflow version, see the [Nextflow version bumping](#nextflow-version-bumping) section below. For more information about pipeline contributions, see [pipeline-specific contribution guidelines](#pipeline-specific-contribution-guidelines). + +#### Channel naming schemes + +Use the following naming schemes for channels to make the channel flow easier to understand: + +- Initial process channel: `ch_output_from_` +- Intermediate and terminal channels: `ch__for_` + +#### Default parameter values + +Parameters should be initialised and defined with default values within the `params` scope in `nextflow.config`. +They should also be documented in the pipeline JSON schema. + +To update `nextflow_schema.json`, run: + +```bash +nf-core pipelines schema build +``` + +The schema builder interface that loads in your browser should automatically update the defaults in the parameter documentation. + +#### Default processes resource requirements + +If you write a local module, specify a default set of resource requirements for the process. + +Sensible defaults for process resource requirements (CPUs, memory, time) should be defined in `conf/base.config`. +Specify these with generic `withLabel:` selectors, so they can be shared across multiple processes and steps of the pipeline. + +nf-core provides a set of standard labels that you should follow where possible, as seen in the [nf-core pipeline template](https://github.com/nf-core/tools/blob/main/nf_core/pipeline-template/conf/base.config). +These labels define resource defaults for single-core processes, modules that require a GPU, and different levels of multi-core configurations with increasing memory requirements. + +Values assigned within these labels can be dynamically passed to a tool using the the `${task.cpus}` and `${task.memory}` Nextflow variables in the `script:` block of a module (see an example in the [modules repository](https://github.com/nf-core/modules/blob/bd1b6a40f55933d94b8c9ca94ec8c1ea0eaf4b82/modules/nf-core/samtools/bam2fq/main.nf#L30)). + +#### Nextflow version bumping + +If you use a new feature from core Nextflow, bump the minimum required Nextflow version in the pipeline with: + +```bash +nf-core pipelines bump-version --nextflow . +``` + +#### Images and figures guidelines + +If you update images or graphics, follow the nf-core [style guidelines](https://nf-co.re/docs/community/brand/workflow-schematics). + +## Pipeline specific contribution guidelines + +- If you update any method / subworkflow / execution step, write a detailed report of why the change was implemented with a small test case +- We would also suggest to discuss with us your recommended changes before raising a PR, this would reduce the effort in case the changes are not approved +- It is always welcome to also update the nf-core `slack` channel `spatialaxe` after a PR is raised +- In the nf-core community we often trade for PR reviews, but we would really appreciate if a member of the `spatialaxe team`(Sameesh Kher, Florian Heyl, Dongze He) is added as a PR reviewer +- If you build a container image, please make sure that this conatiner is available from the nf-core quay.io and not from your personal container repository +- We would move towards implementing conda profile support for the pipeline and would really appreciate if a bioconda package is built around your new tool addition +- Finally, we would appreciate your PR descriptions to be as descriptive as possible +- We look forward to your contributions 🍻 diff --git a/docs/images/nf-core-spatialaxe_logo_dark.png b/docs/images/nf-core-spatialaxe_logo_dark.png index 52ecaa70..9eea0195 100644 Binary files a/docs/images/nf-core-spatialaxe_logo_dark.png and b/docs/images/nf-core-spatialaxe_logo_dark.png differ diff --git a/docs/images/nf-core-spatialaxe_logo_light.png b/docs/images/nf-core-spatialaxe_logo_light.png index 67bdab99..3b655587 100644 Binary files a/docs/images/nf-core-spatialaxe_logo_light.png and b/docs/images/nf-core-spatialaxe_logo_light.png differ diff --git a/main.nf b/main.nf index ed5c9d42..dbbb24d4 100644 --- a/main.nf +++ b/main.nf @@ -135,7 +135,6 @@ workflow { params.plaintext_email, params.outdir, params.monochrome_logs, - params.hook_url, NFCORE_SPATIALAXE.out.multiqc_report ) } diff --git a/modules.json b/modules.json index c215c2ed..494ffa18 100644 --- a/modules.json +++ b/modules.json @@ -53,9 +53,9 @@ "installed_by": ["modules"], "patch": "modules/nf-core/unzip/unzip.diff" }, - "xeniumranger/import-segmentation": { + "xeniumranger/importsegmentation": { "branch": "master", - "git_sha": "39365e944e936511e33b993cdd978e0f12adac9a", + "git_sha": "1fa9ed32cfd532e7d2191b5ffdcc86c783878fb0", "installed_by": ["modules"] }, "xeniumranger/relabel": { diff --git a/modules/nf-core/xeniumranger/import-segmentation/tests/main.nf.test b/modules/nf-core/xeniumranger/import-segmentation/tests/main.nf.test deleted file mode 100644 index 71198099..00000000 --- a/modules/nf-core/xeniumranger/import-segmentation/tests/main.nf.test +++ /dev/null @@ -1,314 +0,0 @@ -nextflow_process { - - name "Test Process XENIUMRANGER_IMPORT_SEGMENTATION" - script "../main.nf" - process "XENIUMRANGER_IMPORT_SEGMENTATION" - config "./nextflow.config" - - tag "modules" - tag "modules_nfcore" - tag "xeniumranger" - tag "xeniumranger/import-segmentation" - tag "unzip" - - setup { - run("UNZIP") { - script "modules/nf-core/unzip/main.nf" - process { - """ - input[0] = [[], file('https://raw.githubusercontent.com/nf-core/test-datasets/spatialaxe/Xenium_Prime_Mouse_Ileum_tiny_outs.zip', checkIfExists: true)] - """ - } - } - } - - test("xeniumranger import-segmentation nuclei npy") { - when { - process { - """ - input[0] = channel.of([ - [id: "test_xeniumranger_import-segmentation"], - ]).combine(UNZIP.out.unzipped_archive.map { it[1] }) - input[1] = 0 - input[2] = [] - input[3] = UNZIP.out.unzipped_archive.map { it[1] } + "/segmentations/nuclei.npy" - input[4] = [] - input[5] = [] - input[6] = [] - """ - } - } - then { - assertAll( - { assert process.success }, - { assert snapshot( - process.out.versions, - process.out.outs.get(0).get(1).findAll { file(it).name !in [ - 'analysis_summary.html', - 'metrics_summary.csv', - 'cell_boundaries.csv.gz', - 'cell_boundaries.parquet', - 'nucleus_boundaries.csv.gz', - 'nucleus_boundaries.parquet', - 'cells.csv.gz', - 'cells.parquet', - 'cells.zarr.zip', - 'transcripts.parquet', - 'transcripts.zarr.zip', - 'clusters.csv', - 'differential_expression.csv', - 'components.csv', - 'projection.csv', - 'variance.csv', - 'analysis.zarr.zip', - 'experiment.xenium', - 'cell_feature_matrix.zarr.zip' - ]} - ).match() - }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'analysis_summary.html' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'metrics_summary.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cell_boundaries.csv.gz' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cell_boundaries.parquet' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'nucleus_boundaries.csv.gz' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'nucleus_boundaries.parquet' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cells.csv.gz' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cells.parquet' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cells.zarr.zip' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'transcripts.parquet' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'clusters.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'differential_expression.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'components.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'projection.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'variance.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'analysis.zarr.zip' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'experiment.xenium' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cell_feature_matrix.zarr.zip' }).exists() }, - ) - } - } - - - test("xeniumranger import-segmentation nuclei tif") { - when { - process { - """ - input[0] = channel.of([ - [id: "test_xeniumranger_import-segmentation"], - ]).combine(UNZIP.out.unzipped_archive.map { it[1] }) - input[1] = 0 - input[2] = [] - input[3] = UNZIP.out.unzipped_archive.map { it[1] } + "/segmentations/nuclei.npy" - input[4] = [] - input[5] = [] - input[6] = [] - """ - } - } - then { - assertAll( - { assert process.success }, - { assert snapshot( - process.out.versions, - process.out.outs.get(0).get(1).findAll { file(it).name !in [ - 'analysis_summary.html', - 'metrics_summary.csv', - 'cell_boundaries.csv.gz', - 'cell_boundaries.parquet', - 'nucleus_boundaries.csv.gz', - 'nucleus_boundaries.parquet', - 'cells.csv.gz', - 'cells.parquet', - 'cells.zarr.zip', - 'transcripts.parquet', - 'transcripts.zarr.zip', - 'clusters.csv', - 'differential_expression.csv', - 'components.csv', - 'projection.csv', - 'variance.csv', - 'analysis.zarr.zip', - 'experiment.xenium', - 'cell_feature_matrix.zarr.zip' - ]} - ).match() - }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'analysis_summary.html' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'metrics_summary.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cell_boundaries.csv.gz' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cell_boundaries.parquet' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'nucleus_boundaries.csv.gz' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'nucleus_boundaries.parquet' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cells.csv.gz' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cells.parquet' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cells.zarr.zip' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'transcripts.parquet' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'clusters.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'differential_expression.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'components.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'projection.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'variance.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'analysis.zarr.zip' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'experiment.xenium' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cell_feature_matrix.zarr.zip' }).exists() }, - ) - } - } - - test("xeniumranger import-segmentation segmentation csv") { - when { - process { - """ - input[0] = channel.of([ - [id: "test_xeniumranger_import-segmentation"], - ]).combine(UNZIP.out.unzipped_archive.map { it[1] }) - input[1] = 0 - input[2] = UNZIP.out.unzipped_archive.map { it[1] } + "/segmentations/imagealignment.csv" - input[3] = [] - input[4] = [] - input[5] = UNZIP.out.unzipped_archive.map { it[1] } + "/segmentations/segmentation.csv" - input[6] = UNZIP.out.unzipped_archive.map { it[1] } + "/segmentations/segmentation_polygons.json" - """ - } - } - then { - assertAll( - { assert process.success }, - { assert snapshot( - process.out.versions, - process.out.outs.get(0).get(1).findAll { file(it).name !in [ - 'analysis_summary.html', - 'metrics_summary.csv', - 'cell_boundaries.csv.gz', - 'cell_boundaries.parquet', - 'nucleus_boundaries.csv.gz', - 'nucleus_boundaries.parquet', - 'cells.csv.gz', - 'cells.parquet', - 'cells.zarr.zip', - 'transcripts.parquet', - 'transcripts.zarr.zip', - 'clusters.csv', - 'differential_expression.csv', - 'components.csv', - 'projection.csv', - 'variance.csv', - 'analysis.zarr.zip', - 'experiment.xenium', - 'cell_feature_matrix.zarr.zip' - ]} - ).match() - }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'analysis_summary.html' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'metrics_summary.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cell_boundaries.csv.gz' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cell_boundaries.parquet' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'nucleus_boundaries.csv.gz' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'nucleus_boundaries.parquet' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cells.csv.gz' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cells.parquet' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cells.zarr.zip' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'transcripts.parquet' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'clusters.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'differential_expression.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'components.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'projection.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'variance.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'analysis.zarr.zip' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'experiment.xenium' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cell_feature_matrix.zarr.zip' }).exists() }, - ) - } - } - - test("xeniumranger import-segmentation") { - when { - process { - """ - input[0] = channel.of([ - [id: "test_xeniumranger_import-segmentation"], - ]).combine(UNZIP.out.unzipped_archive.map { it[1] }) - input[1] = 0 - input[2] = [] - input[3] = UNZIP.out.unzipped_archive.map { it[1] } + "/cells.zarr.zip" - input[4] = [] - input[5] = [] - input[6] = [] - """ - } - } - then { - assertAll( - { assert process.success }, - { assert snapshot( - process.out.versions, - process.out.outs.get(0).get(1).findAll { file(it).name !in [ - 'analysis_summary.html', - 'metrics_summary.csv', - 'cell_boundaries.csv.gz', - 'cell_boundaries.parquet', - 'nucleus_boundaries.csv.gz', - 'nucleus_boundaries.parquet', - 'cells.csv.gz', - 'cells.parquet', - 'cells.zarr.zip', - 'transcripts.parquet', - 'transcripts.zarr.zip', - 'clusters.csv', - 'differential_expression.csv', - 'components.csv', - 'projection.csv', - 'variance.csv', - 'analysis.zarr.zip', - 'experiment.xenium', - 'cell_feature_matrix.zarr.zip' - ]} - ).match() - }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'analysis_summary.html' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'metrics_summary.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cell_boundaries.csv.gz' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cell_boundaries.parquet' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'nucleus_boundaries.csv.gz' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'nucleus_boundaries.parquet' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cells.csv.gz' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cells.parquet' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cells.zarr.zip' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'transcripts.parquet' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'clusters.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'differential_expression.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'components.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'projection.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'variance.csv' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'analysis.zarr.zip' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'experiment.xenium' }).exists() }, - { assert file(process.out.outs.get(0).get(1).find { file(it).name == 'cell_feature_matrix.zarr.zip' }).exists() }, - ) - } - } - - test("xeniumranger import-segmentation stub") { - options "-stub" - when { - process { - """ - input[0] = channel.of([ - [id: "test_xeniumranger_import-segmentation"], - ]).combine(UNZIP.out.unzipped_archive.map { it[1] }) - input[1] = 0 - input[2] = [] - input[3] = UNZIP.out.unzipped_archive.map { it[1] } + "/cells.zarr.zip" - input[4] = [] - input[5] = [] - input[6] = [] - """ - } - } - then { - assertAll( - { assert process.success }, - { assert snapshot(process.out).match() } - ) - } - } -} \ No newline at end of file diff --git a/modules/nf-core/xeniumranger/import-segmentation/tests/main.nf.test.snap b/modules/nf-core/xeniumranger/import-segmentation/tests/main.nf.test.snap deleted file mode 100644 index 1c312ae0..00000000 --- a/modules/nf-core/xeniumranger/import-segmentation/tests/main.nf.test.snap +++ /dev/null @@ -1,127 +0,0 @@ -{ - "xeniumranger import-segmentation": { - "content": [ - [ - "versions.yml:md5,d76e870d71abf94ed9ae972a08b83f63" - ], - [ - "dispersion.csv:md5,e8b1abb880ece8fb730ce34a15f958b4", - "features_selected.csv:md5,c5e32d69f001f938ed316d2108a21e00", - "cell_feature_matrix.h5:md5,96cb400f1b1dd6f8796daea0ad5c74e6", - "barcodes.tsv.gz:md5,04ea06796d6b28517c288904ca043582", - "features.tsv.gz:md5,7862242129681900a9cc4086dc83b62e", - "matrix.mtx.gz:md5,489f86fbd8d65d6b973bb9cc7c5a76f1", - "gene_panel.json:md5,8890dd5fd90706e751554ac3fdfdedde", - "morphology.ome.tif:md5,6b65fff28a38a001b8f25061737fbf9b", - "morphology_focus_0000.ome.tif:md5,90e796ad634d14e62cf2ebcadf2eaf98" - ] - ], - "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.4" - }, - "timestamp": "2024-10-30T00:13:13.575888" - }, - "xeniumranger import-segmentation nuclei npy": { - "content": [ - [ - "versions.yml:md5,d76e870d71abf94ed9ae972a08b83f63" - ], - [ - "dispersion.csv:md5,e8b1abb880ece8fb730ce34a15f958b4", - "features_selected.csv:md5,c5e32d69f001f938ed316d2108a21e00", - "cell_feature_matrix.h5:md5,96cb400f1b1dd6f8796daea0ad5c74e6", - "barcodes.tsv.gz:md5,04ea06796d6b28517c288904ca043582", - "features.tsv.gz:md5,7862242129681900a9cc4086dc83b62e", - "matrix.mtx.gz:md5,489f86fbd8d65d6b973bb9cc7c5a76f1", - "gene_panel.json:md5,8890dd5fd90706e751554ac3fdfdedde", - "morphology.ome.tif:md5,6b65fff28a38a001b8f25061737fbf9b", - "morphology_focus_0000.ome.tif:md5,90e796ad634d14e62cf2ebcadf2eaf98" - ] - ], - "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.4" - }, - "timestamp": "2024-10-29T23:03:26.726334" - }, - "xeniumranger import-segmentation segmentation csv": { - "content": [ - [ - "versions.yml:md5,d76e870d71abf94ed9ae972a08b83f63" - ], - [ - "dispersion.csv:md5,e8b1abb880ece8fb730ce34a15f958b4", - "features_selected.csv:md5,c5e32d69f001f938ed316d2108a21e00", - "cell_feature_matrix.h5:md5,5d74ea595561e0300b6c3e5ec8d06fff", - "barcodes.tsv.gz:md5,97496a9b448d9380cff0575b8e7a6f57", - "features.tsv.gz:md5,7862242129681900a9cc4086dc83b62e", - "matrix.mtx.gz:md5,f93ed82a2a74c154392fc6237642f1d2", - "gene_panel.json:md5,8890dd5fd90706e751554ac3fdfdedde", - "morphology.ome.tif:md5,6b65fff28a38a001b8f25061737fbf9b", - "morphology_focus_0000.ome.tif:md5,90e796ad634d14e62cf2ebcadf2eaf98" - ] - ], - "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.4" - }, - "timestamp": "2024-10-29T23:22:58.158857" - }, - "xeniumranger import-segmentation stub": { - "content": [ - { - "0": [ - [ - { - "id": "test_xeniumranger_import-segmentation" - }, - "fake_file.txt:md5,d41d8cd98f00b204e9800998ecf8427e" - ] - ], - "1": [ - "versions.yml:md5,d76e870d71abf94ed9ae972a08b83f63" - ], - "outs": [ - [ - { - "id": "test_xeniumranger_import-segmentation" - }, - "fake_file.txt:md5,d41d8cd98f00b204e9800998ecf8427e" - ] - ], - "versions": [ - "versions.yml:md5,d76e870d71abf94ed9ae972a08b83f63" - ] - } - ], - "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.4" - }, - "timestamp": "2024-10-30T22:49:39.204133" - }, - "xeniumranger import-segmentation nuclei tif": { - "content": [ - [ - "versions.yml:md5,d76e870d71abf94ed9ae972a08b83f63" - ], - [ - "dispersion.csv:md5,e8b1abb880ece8fb730ce34a15f958b4", - "features_selected.csv:md5,c5e32d69f001f938ed316d2108a21e00", - "cell_feature_matrix.h5:md5,96cb400f1b1dd6f8796daea0ad5c74e6", - "barcodes.tsv.gz:md5,04ea06796d6b28517c288904ca043582", - "features.tsv.gz:md5,7862242129681900a9cc4086dc83b62e", - "matrix.mtx.gz:md5,489f86fbd8d65d6b973bb9cc7c5a76f1", - "gene_panel.json:md5,8890dd5fd90706e751554ac3fdfdedde", - "morphology.ome.tif:md5,6b65fff28a38a001b8f25061737fbf9b", - "morphology_focus_0000.ome.tif:md5,90e796ad634d14e62cf2ebcadf2eaf98" - ] - ], - "meta": { - "nf-test": "0.9.0", - "nextflow": "24.04.4" - }, - "timestamp": "2024-10-29T23:11:37.18721" - } -} \ No newline at end of file diff --git a/modules/nf-core/xeniumranger/import-segmentation/tests/nextflow.config b/modules/nf-core/xeniumranger/import-segmentation/tests/nextflow.config deleted file mode 100644 index e69de29b..00000000 diff --git a/modules/nf-core/xeniumranger/import-segmentation/tests/tags.yml b/modules/nf-core/xeniumranger/import-segmentation/tests/tags.yml deleted file mode 100644 index 90c2b805..00000000 --- a/modules/nf-core/xeniumranger/import-segmentation/tests/tags.yml +++ /dev/null @@ -1,2 +0,0 @@ -xeniumranger/import-segmentation: - - "modules/nf-core/xeniumranger/import-segmentation/**" diff --git a/modules/nf-core/xeniumranger/import-segmentation/main.nf b/modules/nf-core/xeniumranger/importsegmentation/main.nf similarity index 77% rename from modules/nf-core/xeniumranger/import-segmentation/main.nf rename to modules/nf-core/xeniumranger/importsegmentation/main.nf index 264b8a72..57ff2cb6 100644 --- a/modules/nf-core/xeniumranger/import-segmentation/main.nf +++ b/modules/nf-core/xeniumranger/importsegmentation/main.nf @@ -1,11 +1,11 @@ -process XENIUMRANGER_IMPORT_SEGMENTATION { +process XENIUMRANGER_IMPORTSEGMENTATION { tag "$meta.id" label 'process_high' - container "nf-core/xeniumranger:4.0" + container "quay.io/nf-core/xeniumranger:4.0" input: - tuple val(meta), path(xenium_bundle, stageAs: "bundle/"), path(transcript_assignment), path(viz_polygons), path(nuclei), path(cells), path(coordinate_transform), val(units) + tuple val(meta), path(xenium_bundle, stageAs: "bundle/"), path(transcript_assignment), path(viz_polygons), path(nuclei), path(cells), path(coordinate_transform), val(units), val(expansion_distance) output: tuple val(meta), path("${prefix}"), emit: outs @@ -18,7 +18,7 @@ process XENIUMRANGER_IMPORT_SEGMENTATION { // Exit if running this module with -profile conda / -profile mamba if (workflow.profile.tokenize(',').intersect(['conda', 'mamba']).size() >= 1) { - error "XENIUMRANGER_IMPORT_SEGMENTATION module does not support Conda. Please use Docker / Singularity / Podman instead." + error "XENIUMRANGER_IMPORTSEGMENTATION module does not support Conda. Please use Docker / Singularity / Podman instead." } prefix = task.ext.prefix ?: "${meta.id}" @@ -32,6 +32,9 @@ process XENIUMRANGER_IMPORT_SEGMENTATION { def assembled_args = [] if (task.ext.args) { assembled_args << task.ext.args.trim() } + // --expansion-distance is only valid for nuclei-based imports. + // xeniumranger rejects it for transcript-assignment imports and cells-only imports. + if (nuclei && expansion_distance != null) { assembled_args << "--expansion-distance=${expansion_distance}" } if (nuclei) { assembled_args << "--nuclei=\"${nuclei}\"" } if (cells) { assembled_args << "--cells=\"${cells}\"" } if (transcript_assignment) { assembled_args << "--transcript-assignment=\"${transcript_assignment}\"" } @@ -48,14 +51,14 @@ process XENIUMRANGER_IMPORT_SEGMENTATION { """ xeniumranger import-segmentation \\ - --id="XENIUMRANGER_IMPORT_SEGMENTATION" \\ + --id="XENIUMRANGER_IMPORTSEGMENTATION" \\ --xenium-bundle="${xenium_bundle}" \\ --localcores=${task.cpus} \\ --localmem=${task.memory.toGiga()} \\ ${args} rm -rf "${prefix}" - mv XENIUMRANGER_IMPORT_SEGMENTATION/outs "${prefix}" + mv XENIUMRANGER_IMPORTSEGMENTATION/outs "${prefix}" """ stub: diff --git a/modules/nf-core/xeniumranger/import-segmentation/meta.yml b/modules/nf-core/xeniumranger/importsegmentation/meta.yml similarity index 92% rename from modules/nf-core/xeniumranger/import-segmentation/meta.yml rename to modules/nf-core/xeniumranger/importsegmentation/meta.yml index e222df58..801aa47a 100644 --- a/modules/nf-core/xeniumranger/import-segmentation/meta.yml +++ b/modules/nf-core/xeniumranger/importsegmentation/meta.yml @@ -1,6 +1,6 @@ -name: xeniumranger_import_segmentation +name: XENIUMRANGER_IMPORTSEGMENTATION description: | - The xeniumranger import-segmentation module runs `xeniumranger import-segmentation` + The xeniumranger importsegmentation module runs `xeniumranger import-segmentation` to recompute Xenium Onboard Analysis outputs using external segmentation results. It supports two execution modes mirroring the Xenium Ranger CLI: an image-based mode that accepts nuclei and/or cell masks (TIFF/NPY) or GeoJSON polygons together @@ -102,6 +102,13 @@ input: enum: - "microns" - "pixels" + - expansion_distance: + type: integer + optional: true + description: | + Distance (in microns) to expand nuclei outward into cell boundaries. Only valid for + nuclei-based imports (`nuclei` must be set); xeniumranger rejects it for + transcript-assignment imports and cells-only imports. Default: 5. Range: 0-100. output: outs: - - meta: diff --git a/modules/nf-core/xeniumranger/importsegmentation/tests/main.nf.test b/modules/nf-core/xeniumranger/importsegmentation/tests/main.nf.test new file mode 100644 index 00000000..1510df32 --- /dev/null +++ b/modules/nf-core/xeniumranger/importsegmentation/tests/main.nf.test @@ -0,0 +1,91 @@ +nextflow_process { + + name "Test Process XENIUMRANGER_IMPORTSEGMENTATION" + script "../main.nf" + process "XENIUMRANGER_IMPORTSEGMENTATION" + config "./nextflow.config" + + tag "modules" + tag "modules_nfcore" + tag "xeniumranger" + tag "xeniumranger/importsegmentation" + tag "untar" + + + setup { + + run("UNTAR") { + script "modules/nf-core/untar/main.nf" + process { + """ + input[0] = [[], file('https://raw.githubusercontent.com/nf-core/test-datasets/spatialaxe/xenium_bundle.tar.gz', checkIfExists: true)] + """ + } + } + + } + + test("xeniumranger importsegmentation nuclei") { + when { + process { + """ + input[0] = UNTAR.out.untar + .map { meta, bundle -> + [ + [id: "test_xeniumranger_importsegmentation"], + bundle, + [], + [], + bundle + "/cells.zarr.zip", + [], + [], + [], + 5 + ] + } + """ + } + } + + then { + assertAll( + { assert process.success }, + { assert process.out.outs != null }, + { assert file(process.out.outs.get(0).get(1)).isDirectory() }, + { assert file(process.out.outs.get(0).get(1)).list().size() > 0 }, + { assert snapshot(sanitizeOutput(process.out, unstableKeys: ["outs"])).match()}, + ) + } + } + + test("xeniumranger importsegmentation stub") { + options "-stub" + + when { + process { + """ + input[0] = UNTAR.out.untar + .map { meta, bundle -> + [ + [id: "test_xeniumranger_importsegmentation"], + bundle, + [], + [], + bundle + "/cells.zarr.zip", + [], + [], + [], + 5 + ] + } + """ + } + } + then { + assertAll( + { assert process.success }, + { assert snapshot(sanitizeOutput(process.out)).match() } + ) + } + } +} diff --git a/modules/nf-core/xeniumranger/importsegmentation/tests/main.nf.test.snap b/modules/nf-core/xeniumranger/importsegmentation/tests/main.nf.test.snap new file mode 100644 index 00000000..7fb43655 --- /dev/null +++ b/modules/nf-core/xeniumranger/importsegmentation/tests/main.nf.test.snap @@ -0,0 +1,56 @@ +{ + "xeniumranger importsegmentation stub": { + "content": [ + { + "outs": [ + [ + { + "id": "test_xeniumranger_importsegmentation" + }, + [ + "experiment.xenium:md5,d41d8cd98f00b204e9800998ecf8427e" + ] + ] + ], + "versions_xeniumranger": [ + [ + "XENIUMRANGER_IMPORTSEGMENTATION", + "xeniumranger", + "4.0.1.1" + ] + ] + } + ], + "timestamp": "2026-07-27T15:02:26.958198169", + "meta": { + "nf-test": "0.9.5", + "nextflow": "26.04.6" + } + }, + "xeniumranger importsegmentation nuclei": { + "content": [ + { + "outs": [ + [ + { + "id": "test_xeniumranger_importsegmentation" + }, + "test_xeniumranger_importsegmentation" + ] + ], + "versions_xeniumranger": [ + [ + "XENIUMRANGER_IMPORTSEGMENTATION", + "xeniumranger", + "4.0.1.1" + ] + ] + } + ], + "timestamp": "2026-07-27T15:02:12.93562615", + "meta": { + "nf-test": "0.9.5", + "nextflow": "26.04.6" + } + } +} \ No newline at end of file diff --git a/modules/nf-core/xeniumranger/importsegmentation/tests/nextflow.config b/modules/nf-core/xeniumranger/importsegmentation/tests/nextflow.config new file mode 100644 index 00000000..39b095f8 --- /dev/null +++ b/modules/nf-core/xeniumranger/importsegmentation/tests/nextflow.config @@ -0,0 +1,7 @@ +process { + withName: XENIUMRANGER_IMPORTSEGMENTATION { + cpus = 2 + memory = '4.GB' + time = '30.m' + } +} diff --git a/nextflow.config b/nextflow.config index 99b638cc..eef01269 100644 --- a/nextflow.config +++ b/nextflow.config @@ -385,7 +385,7 @@ manifest { mainScript = 'main.nf' defaultBranch = 'master' nextflowVersion = '!>=25.04.0' - version = '1.0.0' + version = '1.0.1' doi = '' } diff --git a/ro-crate-metadata.json b/ro-crate-metadata.json index eb24c81a..2ac72fc8 100644 --- a/ro-crate-metadata.json +++ b/ro-crate-metadata.json @@ -22,8 +22,8 @@ "@id": "./", "@type": "Dataset", "creativeWorkStatus": "Stable", - "datePublished": "2026-05-27T11:36:27+00:00", - "description": "

\n \n \n \"nf-core/spatialaxe\"\n \n

\n\n[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://github.com/codespaces/new/nf-core/spatialaxe)\n[![GitHub Actions CI Status](https://github.com/nf-core/spatialaxe/actions/workflows/nf-test.yml/badge.svg)](https://github.com/nf-core/spatialaxe/actions/workflows/nf-test.yml)\n[![GitHub Actions Linting Status](https://github.com/nf-core/spatialaxe/actions/workflows/linting.yml/badge.svg)](https://github.com/nf-core/spatialaxe/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/spatialaxe/results)[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.XXXXXXX-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.XXXXXXX)\n[![nf-test](https://img.shields.io/badge/unit_tests-nf--test-337ab7.svg)](https://www.nf-test.com)\n\n[![Nextflow](https://img.shields.io/badge/version-%E2%89%A525.04.0-green?style=flat&logo=nextflow&logoColor=white&color=%230DC09D&link=https%3A%2F%2Fnextflow.io)](https://www.nextflow.io/)\n[![nf-core template version](https://img.shields.io/badge/nf--core_template-3.4.1-green?style=flat&logo=nfcore&logoColor=white&color=%2324B064&link=https%3A%2F%2Fnf-co.re)](https://github.com/nf-core/tools/releases/tag/3.4.1)\n[![run with docker](https://img.shields.io/badge/run%20with-docker-0db7ed?labelColor=000000&logo=docker)](https://www.docker.com/)\n[![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/)\n[![Launch on Seqera Platform](https://img.shields.io/badge/Launch%20%F0%9F%9A%80-Seqera%20Platform-%234256e7)](https://cloud.seqera.io/launch?pipeline=https://github.com/nf-core/spatialaxe)\n\n[![Get help on Slack](http://img.shields.io/badge/slack-nf--core%20%23spatialaxe-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/spatialaxe)[![Follow on Bluesky](https://img.shields.io/badge/bluesky-%40nf__core-1185fe?labelColor=000000&logo=bluesky)](https://bsky.app/profile/nf-co.re)[![Follow on Mastodon](https://img.shields.io/badge/mastodon-nf__core-6364ff?labelColor=FFFFFF&logo=mastodon)](https://mstdn.science/@nf_core)[![Watch on YouTube](http://img.shields.io/badge/youtube-nf--core-FF0000?labelColor=000000&logo=youtube)](https://www.youtube.com/c/nf-core)\n\n## Introduction\n\n**nf-core/spatialaxe** is a bioinformatics best-practice processing and quality control pipeline for Xenium (and soon Atera) data. The current plan for the pipeline implementation is shown in the metromap below. **The pipeline is under active developement and changes might occure frequently**.\n\n![nf-core/spatialaxe-metromap](docs/images/spatialaxe-metromap.png)\n\n> [!NOTE]\n> We are currently extending the pipeline for the [10x Atera system](https://www.10xgenomics.com/platforms/atera).\n\n## Tools supported\n\nThe pipeline supports the following tools:\n\n- Segmenation methods:\n - [Baysor](https://doi.org/10.1038/s41587-021-01044-w)\n - [Cellpose](https://doi.org/10.1038/s41592-020-01018-x)\n - [Xenium ranger (XR)](https://www.10xgenomics.com/support/software/xenium-ranger/latest)\n - [StarDist](https://doi.org/10.48550/arXiv.2203.02284)\n- Segmentation free methods:\n - [Ficture](https://doi.org/10.1038/s41592-024-02415-2)\n - [Baysor](https://doi.org/10.1038/s41587-021-01044-w)\n- Transcript assignment methods:\n - [Segger](https://doi.org/10.1101/2025.03.14.643160)\n - [Proseg](https://doi.org/10.1038/s41592-025-02697-0)\n- Utility methods:\n - [SpatialData](https://doi.org/10.1038/s41592-024-02212-x)\n - [Baysor](https://doi.org/10.1038/s41587-021-01044-w)\n- QC methods:\n - [MultiQC Xenium Extra Plugin](https://github.com/MultiQC/xenium-extra)\n - [OPT](https://github.com/JEFworks-Lab/off-target-probe-tracker)\n\n## Usage\n\nOn release, automated continuous integration tests run the pipeline on a full-sized dataset on the AWS cloud infrastructure. This ensures that the pipeline runs on AWS, has sensible resource allocation defaults set to run on real-world datasets, and permits the persistent storage of results to benchmark between pipeline releases and other analysis sources. The results obtained from the full-sized test can be viewed on the [nf-core website](https://nf-co.re/spatialaxe/results).\n\n> [!NOTE]\n> The pipeline does not support conda currently. We are working on it.\n\n## Quick Start\n\n`samplesheet.csv`:\n\n```csv\nsample,bundle,image\ntest_sample,/path/to/xenium-bundle,/path/to/morphology.ome.tif\n```\n\nNow, you can run the pipeline using:\n\n### Run image-based segmentation mode
\n\n`CELLPOSE -> BAYSOR -> XR-IMPORT_SEGMENTATION -> SPATIALDATA -> QC`\n\n```bash\nnextflow run nf-core/spatialaxe \\\n -profile \\\n --input samplesheet.csv \\\n --outdir \\\n --mode \n```\n\n### Run coordinate-based segmentation mode
\n\n`PROSEG -> PROSEG2BAYSOR -> XR-IMPORT_SEGMENTATION -> SPATIALDATA -> QC`\n\n```bash\nnextflow run nf-core/spatialaxe \\\n -profile \\\n --input samplesheet.csv \\\n --outdir \\\n --mode coordinate\n```\n\n### Run segfree mode
\n\n`BAYSOR_SEGFREE`\n\n```bash\nnextflow run nf-core/spatialaxe \\\n -profile \\\n --input samplesheet.csv \\\n --outdir \\\n --mode segfree\n```\n\n### Run preview mode
\n\n`BAYSOR_PREVIEW`\n\n```bash\nnextflow run nf-core/spatialaxe \\\n -profile \\\n --input samplesheet.csv \\\n --outdir \\\n --mode preview\n```\n\n### Run just the quality control
\n\n```bash\nnextflow run nf-core/spatialaxe \\\n -profile \\\n --input samplesheet.csv \\\n --outdir \\\n --mode qc\n```\n\n### Additional information\n\n> [!WARNING]\n> 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/docs/usage/getting_started/configuration#custom-configuration-files).\n\nFor more details and further functionality, please refer to the [usage documentation](https://nf-co.re/spatialaxe/usage) and the [parameter documentation](https://nf-co.re/spatialaxe/parameters).\n\n## Pipeline output\n\nTo see the results of an example test run with a full size dataset refer to the [results](https://nf-co.re/spatialaxe/results) tab on the nf-core website pipeline page.\nFor more details about the output files and reports, please refer to the\n[output documentation](https://nf-co.re/spatialaxe/output).\n\n## Runtime and resource estimations\n\n| Tool | Compute | Runtime (min / med / max) | Peak RSS (min / med / max) |\n| ------------------------- | ------- | ------------------------- | -------------------------- |\n| Cellpose | GPU | 1m / 4m / 1.4h | 10 GB / 26 GB / 554 GB |\n| Cellpose | CPU | 1.3h / 2.3h / 6.5h | 161 GB / 426 GB / 1115 GB |\n| StarDist | GPU | 1m / 4m / 7m | 5 GB / 12 GB / 18 GB |\n| StarDist | CPU | 5m / 6m / 7m | 18 GB / 18 GB / 18 GB |\n| Segger (create_dataset) | GPU | 2m / 9m / 31m | 1.7 GB / 14 GB / 50 GB |\n| Segger (create_dataset) | CPU | 13m / 21m / 46m | 13 GB / 19 GB / 49 GB |\n| Segger (train) | GPU | 10m / 43m / 2.9h | 30 GB / 33 GB / 60 GB |\n| Segger (predict) | GPU | 2m / 16m / 59m | 10 GB / 25 GB / 87 GB |\n| Baysor (whole-image) | CPU | 2m / 30m / 17h | 6 GB / 10 GB / 650 GB |\n| Baysor (tiled) | CPU | 1m / 18m / 13h | 0.2 GB / 34 GB / 530 GB |\n| Proseg | CPU | 1m / 18m / 6.8h | 279 MB / 3.8 GB / 136 GB |\n| XeniumRanger (resegment) | CPU | 18m / 39m / 3.7h | 28 GB / 54 GB / 60 GB |\n| XeniumRanger (import_seg) | CPU | 2m / 7m / 2.7h | 2.6 GB / 11 GB / 51 GB |\n| Ficture (preprocess) | CPU | 3m / 4m / 13m | 331 MB / 357 MB / 21 GB |\n\n- Cellpose GPU vs CPU: 35x faster on GPU (4m median vs 2.3h), 16x less memory (26 GB vs 426 GB)\n- Segger: Only tool that truly requires GPU for all 3 steps (create_dataset, train, predict)\n- StarDist: Very fast on CPU, GPU is not necessary to run its default model\n\n## Credits\n\nnf-core/spatialaxe is mainly developed by [Sameesh Kher](https://github.com/khersameesh24), [Dongze He](https://github.com/dongzehe), and [Florian Heyl](https://github.com/heylf).\n\nWe thank the following people for their extensive assistance in the development of this pipeline:\n\n- Tobias Krause\n- Kre\u0161imir Be\u0161tak (kbestak)\n- Matthias H\u00f6rtenhuber (mashehu)\n- Maxime Garcia (maxulysse)\n- K\u00fcbra Narc\u0131 (kubranarci)\n\n## Contributions and Support\n\nIf you would like to contribute to this pipeline, please see the [contributing guidelines](.github/CONTRIBUTING.md).\n\nFor further information or help, don't hesitate to get in touch on the [Slack `#spatialaxe` channel](https://nfcore.slack.com/channels/spatialaxe) (you can join with [this invite](https://nf-co.re/join/slack)).\n\n## Citations\n\n\n\nAn extensive list of references for the tools used by the pipeline can be found in the [`CITATIONS.md`](CITATIONS.md) file.\n\nYou can cite the `nf-core` publication as follows:\n\n> **The nf-core framework for community-curated bioinformatics pipelines.**\n>\n> Philip Ewels, Alexander Peltzer, Sven Fillinger, Harshil Patel, Johannes Alneberg, Andreas Wilm, Maxime Ulysse Garcia, Paolo Di Tommaso & Sven Nahnsen.\n>\n> _Nat Biotechnol._ 2020 Feb 13. doi: [10.1038/s41587-020-0439-x](https://dx.doi.org/10.1038/s41587-020-0439-x).\n", + "datePublished": "2026-07-23T13:01:17+00:00", + "description": "

\n \n \n \"nf-core/spatialaxe\"\n \n

\n\n[![Open in GitHub Codespaces](https://img.shields.io/badge/Open_In_GitHub_Codespaces-black?labelColor=grey&logo=github)](https://github.com/codespaces/new/nf-core/spatialaxe)\n[![GitHub Actions CI Status](https://github.com/nf-core/spatialaxe/actions/workflows/nf-test.yml/badge.svg)](https://github.com/nf-core/spatialaxe/actions/workflows/nf-test.yml)\n[![GitHub Actions Linting Status](https://github.com/nf-core/spatialaxe/actions/workflows/linting.yml/badge.svg)](https://github.com/nf-core/spatialaxe/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/spatialaxe/results)[![Cite with Zenodo](http://img.shields.io/badge/DOI-10.5281/zenodo.XXXXXXX-1073c8?labelColor=000000)](https://doi.org/10.5281/zenodo.XXXXXXX)\n[![nf-test](https://img.shields.io/badge/unit_tests-nf--test-337ab7.svg)](https://www.nf-test.com)\n\n[![Nextflow](https://img.shields.io/badge/version-%E2%89%A525.04.0-green?style=flat&logo=nextflow&logoColor=white&color=%230DC09D&link=https%3A%2F%2Fnextflow.io)](https://www.nextflow.io/)\n[![nf-core template version](https://img.shields.io/badge/nf--core_template-3.4.1-green?style=flat&logo=nfcore&logoColor=white&color=%2324B064&link=https%3A%2F%2Fnf-co.re)](https://github.com/nf-core/tools/releases/tag/3.4.1)\n[![run with docker](https://img.shields.io/badge/run%20with-docker-0db7ed?labelColor=000000&logo=docker)](https://www.docker.com/)\n[![run with singularity](https://img.shields.io/badge/run%20with-singularity-1d355c.svg?labelColor=000000)](https://sylabs.io/docs/)\n[![Launch on Seqera Platform](https://img.shields.io/badge/Launch%20%F0%9F%9A%80-Seqera%20Platform-%234256e7)](https://cloud.seqera.io/launch?pipeline=https://github.com/nf-core/spatialaxe)\n\n[![Get help on Slack](http://img.shields.io/badge/slack-nf--core%20%23spatialaxe-4A154B?labelColor=000000&logo=slack)](https://nfcore.slack.com/channels/spatialaxe)[![Follow on Bluesky](https://img.shields.io/badge/bluesky-%40nf__core-1185fe?labelColor=000000&logo=bluesky)](https://bsky.app/profile/nf-co.re)[![Follow on Mastodon](https://img.shields.io/badge/mastodon-nf__core-6364ff?labelColor=FFFFFF&logo=mastodon)](https://mstdn.science/@nf_core)[![Watch on YouTube](http://img.shields.io/badge/youtube-nf--core-FF0000?labelColor=000000&logo=youtube)](https://www.youtube.com/c/nf-core)\n\n## Introduction\n\n**nf-core/spatialaxe** is a bioinformatics best-practice processing and quality control pipeline for Xenium (and soon Atera) data. The current plan for the pipeline implementation is shown in the metromap below. **The pipeline is under active developement and changes might occure frequently**.\n\n![nf-core/spatialaxe-metromap](docs/images/spatialaxe-metromap.png)\n\n> [!NOTE]\n> We are currently extending the pipeline for the [10x Atera system](https://www.10xgenomics.com/platforms/atera).\n\n## Tools supported\n\nThe pipeline supports the following tools:\n\n- Segmenation methods:\n - [Baysor](https://doi.org/10.1038/s41587-021-01044-w)\n - [Cellpose](https://doi.org/10.1038/s41592-020-01018-x)\n - [Xenium ranger (XR)](https://www.10xgenomics.com/support/software/xenium-ranger/latest)\n - [StarDist](https://doi.org/10.48550/arXiv.2203.02284)\n- Segmentation free methods:\n - [Ficture](https://doi.org/10.1038/s41592-024-02415-2)\n - [Baysor](https://doi.org/10.1038/s41587-021-01044-w)\n- Transcript assignment methods:\n - [Segger](https://doi.org/10.1101/2025.03.14.643160)\n - [Proseg](https://doi.org/10.1038/s41592-025-02697-0)\n- Utility methods:\n - [SpatialData](https://doi.org/10.1038/s41592-024-02212-x)\n - [Baysor](https://doi.org/10.1038/s41587-021-01044-w)\n- QC methods:\n - [MultiQC Xenium Extra Plugin](https://github.com/MultiQC/xenium-extra)\n - [OPT](https://github.com/JEFworks-Lab/off-target-probe-tracker)\n\n## Usage\n\nOn release, automated continuous integration tests run the pipeline on a full-sized dataset on the AWS cloud infrastructure. This ensures that the pipeline runs on AWS, has sensible resource allocation defaults set to run on real-world datasets, and permits the persistent storage of results to benchmark between pipeline releases and other analysis sources. The results obtained from the full-sized test can be viewed on the [nf-core website](https://nf-co.re/spatialaxe/results).\n\n> [!NOTE]\n> The pipeline does not support conda currently. We are working on it.\n\n## Quick Start\n\n`samplesheet.csv`:\n\n```csv\nsample,bundle,image\ntest_sample,/path/to/xenium-bundle,/path/to/morphology.ome.tif\n```\n\nNow, you can run the pipeline using:\n\n### Run image-based segmentation mode
\n\n`CELLPOSE -> BAYSOR -> XR-IMPORT_SEGMENTATION -> SPATIALDATA -> QC`\n\n```bash\nnextflow run nf-core/spatialaxe \\\n -profile \\\n --input samplesheet.csv \\\n --outdir \\\n --mode \n```\n\n### Run coordinate-based segmentation mode
\n\n`PROSEG -> PROSEG2BAYSOR -> XR-IMPORT_SEGMENTATION -> SPATIALDATA -> QC`\n\n```bash\nnextflow run nf-core/spatialaxe \\\n -profile \\\n --input samplesheet.csv \\\n --outdir \\\n --mode coordinate\n```\n\n### Run segfree mode
\n\n`BAYSOR_SEGFREE`\n\n```bash\nnextflow run nf-core/spatialaxe \\\n -profile \\\n --input samplesheet.csv \\\n --outdir \\\n --mode segfree\n```\n\n### Run preview mode
\n\n`BAYSOR_PREVIEW`\n\n```bash\nnextflow run nf-core/spatialaxe \\\n -profile \\\n --input samplesheet.csv \\\n --outdir \\\n --mode preview\n```\n\n### Run just the quality control
\n\n```bash\nnextflow run nf-core/spatialaxe \\\n -profile \\\n --input samplesheet.csv \\\n --outdir \\\n --mode qc\n```\n\n### Additional information\n\n> [!WARNING]\n> 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/docs/usage/getting_started/configuration#custom-configuration-files).\n\nFor more details and further functionality, please refer to the [usage documentation](https://nf-co.re/spatialaxe/usage) and the [parameter documentation](https://nf-co.re/spatialaxe/parameters).\n\n## Pipeline output\n\nTo see the results of an example test run with a full size dataset refer to the [results](https://nf-co.re/spatialaxe/results) tab on the nf-core website pipeline page.\nFor more details about the output files and reports, please refer to the\n[output documentation](https://nf-co.re/spatialaxe/output).\n\n## Runtime and resource estimations\n\n| Tool | Compute | Runtime (min / med / max) | Peak RSS (min / med / max) |\n| ------------------------- | ------- | ------------------------- | -------------------------- |\n| Cellpose | GPU | 1m / 4m / 1.4h | 10 GB / 26 GB / 554 GB |\n| Cellpose | CPU | 1.3h / 2.3h / 6.5h | 161 GB / 426 GB / 1115 GB |\n| StarDist | GPU | 1m / 4m / 7m | 5 GB / 12 GB / 18 GB |\n| StarDist | CPU | 5m / 6m / 7m | 18 GB / 18 GB / 18 GB |\n| Segger (create_dataset) | GPU | 2m / 9m / 31m | 1.7 GB / 14 GB / 50 GB |\n| Segger (create_dataset) | CPU | 13m / 21m / 46m | 13 GB / 19 GB / 49 GB |\n| Segger (train) | GPU | 10m / 43m / 2.9h | 30 GB / 33 GB / 60 GB |\n| Segger (predict) | GPU | 2m / 16m / 59m | 10 GB / 25 GB / 87 GB |\n| Baysor (whole-image) | CPU | 2m / 30m / 17h | 6 GB / 10 GB / 650 GB |\n| Baysor (tiled) | CPU | 1m / 18m / 13h | 0.2 GB / 34 GB / 530 GB |\n| Proseg | CPU | 1m / 18m / 6.8h | 279 MB / 3.8 GB / 136 GB |\n| XeniumRanger (resegment) | CPU | 18m / 39m / 3.7h | 28 GB / 54 GB / 60 GB |\n| XeniumRanger (import_seg) | CPU | 2m / 7m / 2.7h | 2.6 GB / 11 GB / 51 GB |\n| Ficture (preprocess) | CPU | 3m / 4m / 13m | 331 MB / 357 MB / 21 GB |\n\n- Cellpose GPU vs CPU: 35x faster on GPU (4m median vs 2.3h), 16x less memory (26 GB vs 426 GB)\n- Segger: Only tool that truly requires GPU for all 3 steps (create_dataset, train, predict)\n- StarDist: Very fast on CPU, GPU is not necessary to run its default model\n\n## Credits\n\nnf-core/spatialaxe is mainly developed by [Sameesh Kher](https://github.com/khersameesh24), [Dongze He](https://github.com/dongzehe), and [Florian Heyl](https://github.com/heylf).\n\nWe thank the following people for their extensive assistance in the development of this pipeline:\n\n- Tobias Krause\n- Krešimir Beštak (kbestak)\n- Matthias Hörtenhuber (mashehu)\n- Maxime Garcia (maxulysse)\n- Kübra Narcı (kubranarci)\n\n## Contributions and Support\n\nIf you would like to contribute to this pipeline, please see the [contributing guidelines](.github/CONTRIBUTING.md).\n\nFor further information or help, don't hesitate to get in touch on the [Slack `#spatialaxe` channel](https://nfcore.slack.com/channels/spatialaxe) (you can join with [this invite](https://nf-co.re/join/slack)).\n\n## Citations\n\n\n\nAn extensive list of references for the tools used by the pipeline can be found in the [`CITATIONS.md`](CITATIONS.md) file.\n\nYou can cite the `nf-core` publication as follows:\n\n> **The nf-core framework for community-curated bioinformatics pipelines.**\n>\n> Philip Ewels, Alexander Peltzer, Sven Fillinger, Harshil Patel, Johannes Alneberg, Andreas Wilm, Maxime Ulysse Garcia, Paolo Di Tommaso & Sven Nahnsen.\n>\n> _Nat Biotechnol._ 2020 Feb 13. doi: [10.1038/s41587-020-0439-x](https://dx.doi.org/10.1038/s41587-020-0439-x).\n", "hasPart": [ { "@id": "main.nf" @@ -31,6 +31,9 @@ { "@id": "assets/" }, + { + "@id": "bin/" + }, { "@id": "conf/" }, @@ -43,6 +46,9 @@ { "@id": "modules/" }, + { + "@id": "modules/local/" + }, { "@id": "modules/nf-core/" }, @@ -99,7 +105,7 @@ }, "mentions": [ { - "@id": "#93095c81-90d0-4005-8fe8-257133b523b7" + "@id": "#261e6187-b79f-4b98-ba5b-c00684be47bc" } ], "name": "nf-core/spatialaxe" @@ -126,13 +132,30 @@ "SoftwareSourceCode", "ComputationalWorkflow" ], + "author": [ + { + "@id": "https://orcid.org/0009-0008-2420-6464" + }, + { + "@id": "#5fd4466e-7ce2-4d27-924e-ee46d6d24240" + }, + { + "@id": "https://orcid.org/0000-0002-3651-5685" + } + ], + "contributor": [ + { + "@id": "https://orcid.org/0000-0001-8259-7434" + } + ], "dateCreated": "", - "dateModified": "2026-05-27T13:36:27Z", + "dateModified": "2026-07-23T15:01:17Z", "dct:conformsTo": "https://bioschemas.org/profiles/ComputationalWorkflow/1.0-RELEASE/", "keywords": [ "nf-core", "nextflow", "10x-genomics", + "atera", "image-processing", "spatial", "spatial-data-analysis", @@ -143,6 +166,14 @@ "license": [ "MIT" ], + "maintainer": [ + { + "@id": "https://orcid.org/0009-0008-2420-6464" + }, + { + "@id": "https://orcid.org/0000-0002-3651-5685" + } + ], "name": [ "nf-core/spatialaxe" ], @@ -154,10 +185,10 @@ }, "url": [ "https://github.com/nf-core/spatialaxe", - "https://nf-co.re/spatialaxe/1.0.0/" + "https://nf-co.re/spatialaxe/1.0.1/" ], "version": [ - "1.0.0" + "1.0.1" ] }, { @@ -173,11 +204,11 @@ "version": "!>=25.04.0" }, { - "@id": "#93095c81-90d0-4005-8fe8-257133b523b7", + "@id": "#261e6187-b79f-4b98-ba5b-c00684be47bc", "@type": "TestSuite", "instance": [ { - "@id": "#662205d5-7986-4a2b-99ae-86639b5bf728" + "@id": "#e58b049c-0f60-4176-ae4e-f6fd057d8c60" } ], "mainEntity": { @@ -186,7 +217,7 @@ "name": "Test suite for nf-core/spatialaxe" }, { - "@id": "#662205d5-7986-4a2b-99ae-86639b5bf728", + "@id": "#e58b049c-0f60-4176-ae4e-f6fd057d8c60", "@type": "TestInstance", "name": "GitHub Actions workflow for testing nf-core/spatialaxe", "resource": "repos/nf-core/spatialaxe/actions/workflows/nf-test.yml", @@ -208,6 +239,11 @@ "@type": "Dataset", "description": "Additional files" }, + { + "@id": "bin/", + "@type": "Dataset", + "description": "Scripts that must be callable from a pipeline process" + }, { "@id": "conf/", "@type": "Dataset", @@ -228,6 +264,11 @@ "@type": "Dataset", "description": "Modules used by the pipeline" }, + { + "@id": "modules/local/", + "@type": "Dataset", + "description": "Pipeline-specific modules" + }, { "@id": "modules/nf-core/", "@type": "Dataset", @@ -313,6 +354,35 @@ "@type": "Organization", "name": "nf-core", "url": "https://nf-co.re/" + }, + { + "@id": "https://orcid.org/0009-0008-2420-6464", + "@type": "Person", + "affiliation": "German Cancer Research Center (DKFZ), Heidelberg, DE", + "email": "sameesh.kher@dkfz-heidelberg.de", + "name": "Sameesh Kher", + "url": "https://github.com/khersameesh24" + }, + { + "@id": "#5fd4466e-7ce2-4d27-924e-ee46d6d24240", + "@type": "Person", + "name": "Dongze He" + }, + { + "@id": "https://orcid.org/0000-0002-3651-5685", + "@type": "Person", + "affiliation": "German Cancer Research Center (DKFZ), Heidelberg, DE", + "email": "florian.heyl@dkfz-heidelberg.de", + "name": "Florian Heyl", + "url": "https://github.com/heylf" + }, + { + "@id": "https://orcid.org/0000-0001-8259-7434", + "@type": "Person", + "affiliation": "Altos Labs, San Diego, USA", + "email": "dongzehe.zaza@gmail.com", + "name": "Dongze He", + "url": "https://github.com/dongzehe" } ] } \ No newline at end of file diff --git a/subworkflows/local/baysor_run_prior_segmentation_mask/main.nf b/subworkflows/local/baysor_run_prior_segmentation_mask/main.nf index d5acc0a1..48236e8c 100644 --- a/subworkflows/local/baysor_run_prior_segmentation_mask/main.nf +++ b/subworkflows/local/baysor_run_prior_segmentation_mask/main.nf @@ -4,7 +4,7 @@ include { BAYSOR_PREPROCESS_TRANSCRIPTS } from '../../../modules/local/baysor/preprocess/main' include { BAYSOR_RUN } from '../../../modules/local/baysor/run/main' -include { XENIUMRANGER_IMPORT_SEGMENTATION } from '../../../modules/nf-core/xeniumranger/import-segmentation/main' +include { XENIUMRANGER_IMPORTSEGMENTATION } from '../../../modules/nf-core/xeniumranger/importsegmentation/main' workflow BAYSOR_RUN_PRIOR_SEGMENTATION_MASK { @@ -18,6 +18,7 @@ workflow BAYSOR_RUN_PRIOR_SEGMENTATION_MASK { min_qv // value: minimum transcript QV min_x // value: spatial filter lower x bound min_y // value: spatial filter lower y bound + expansion_distance // value: nuclear expansion distance main: @@ -69,13 +70,14 @@ workflow BAYSOR_RUN_PRIOR_SEGMENTATION_MASK { polygons2d, [], ch_coordinate_space.val, + expansion_distance, ) } - XENIUMRANGER_IMPORT_SEGMENTATION( + XENIUMRANGER_IMPORTSEGMENTATION( ch_imp_seg_inputs ) - ch_redefined_bundle = XENIUMRANGER_IMPORT_SEGMENTATION.out.outs + ch_redefined_bundle = XENIUMRANGER_IMPORTSEGMENTATION.out.outs emit: coordinate_space = ch_coordinate_space // channel: [ "pixels" ] diff --git a/subworkflows/local/baysor_run_transcripts_parquet/main.nf b/subworkflows/local/baysor_run_transcripts_parquet/main.nf index aff27051..c64bf71a 100644 --- a/subworkflows/local/baysor_run_transcripts_parquet/main.nf +++ b/subworkflows/local/baysor_run_transcripts_parquet/main.nf @@ -15,7 +15,7 @@ include { BAYSOR_RUN } from '../../../modules/local/baysor include { BAYSOR_PREPROCESS_TRANSCRIPTS } from '../../../modules/local/baysor/preprocess/main' include { XENIUM_PATCH_STITCH } from '../../../modules/local/xenium_patch/stitch/main' include { RECONSTRUCT_PATCHES } from '../../../modules/local/utility/reconstruct_patches/main' -include { XENIUMRANGER_IMPORT_SEGMENTATION } from '../../../modules/nf-core/xeniumranger/import-segmentation/main' +include { XENIUMRANGER_IMPORTSEGMENTATION } from '../../../modules/nf-core/xeniumranger/importsegmentation/main' workflow BAYSOR_RUN_TRANSCRIPTS_PARQUET { @@ -35,6 +35,7 @@ workflow BAYSOR_RUN_TRANSCRIPTS_PARQUET { min_qv // value: minimum transcript QV min_x // value: spatial filter lower x bound min_y // value: spatial filter lower y bound + expansion_distance // value: nuclear expansion distance main: @@ -112,11 +113,12 @@ workflow BAYSOR_RUN_TRANSCRIPTS_PARQUET { xr_transcript_metadata, xr_cell_polygons, [], [], [], - "microns" + "microns", + expansion_distance, ) } - XENIUMRANGER_IMPORT_SEGMENTATION (ch_xr) + XENIUMRANGER_IMPORTSEGMENTATION (ch_xr) } else { @@ -154,13 +156,14 @@ workflow BAYSOR_RUN_TRANSCRIPTS_PARQUET { segmentation_csv, polygons2d, [], [], [], - ch_coordinate_space.val) + ch_coordinate_space.val, + expansion_distance) } - XENIUMRANGER_IMPORT_SEGMENTATION(ch_xr) + XENIUMRANGER_IMPORTSEGMENTATION(ch_xr) } emit: - redefined_bundle = XENIUMRANGER_IMPORT_SEGMENTATION.out.outs + redefined_bundle = XENIUMRANGER_IMPORTSEGMENTATION.out.outs coordinate_space = ch_coordinate_space } diff --git a/subworkflows/local/baysor_run_transcripts_parquet_tiled/main.nf b/subworkflows/local/baysor_run_transcripts_parquet_tiled/main.nf index 70045331..db290209 100644 --- a/subworkflows/local/baysor_run_transcripts_parquet_tiled/main.nf +++ b/subworkflows/local/baysor_run_transcripts_parquet_tiled/main.nf @@ -6,7 +6,7 @@ include { XENIUM_PATCH_DIVIDE } from '../../../modules/local/xenium include { BAYSOR_PREPROCESS_TRANSCRIPTS } from '../../../modules/local/baysor/preprocess/main' include { BAYSOR_RUN } from '../../../modules/local/baysor/run/main' include { XENIUM_PATCH_STITCH } from '../../../modules/local/xenium_patch/stitch/main' -include { XENIUMRANGER_IMPORT_SEGMENTATION } from '../../../modules/nf-core/xeniumranger/import-segmentation/main' +include { XENIUMRANGER_IMPORTSEGMENTATION } from '../../../modules/nf-core/xeniumranger/importsegmentation/main' workflow BAYSOR_RUN_TRANSCRIPTS_PARQUET_TILED { @@ -19,6 +19,7 @@ workflow BAYSOR_RUN_TRANSCRIPTS_PARQUET_TILED { min_qv // value: minimum transcript QV min_x // value: spatial filter lower x bound min_y // value: spatial filter lower y bound + expansion_distance // value: nuclear expansion distance main: @@ -93,12 +94,12 @@ workflow BAYSOR_RUN_TRANSCRIPTS_PARQUET_TILED { .combine(XENIUM_PATCH_STITCH.out.xr_polygons_transcript, by: 0) .combine(ch_coordinate_space) .map { meta, bundle, geojson, csv, coord_space -> - tuple(meta, bundle, csv, geojson, [], [], [], coord_space) + tuple(meta, bundle, csv, geojson, [], [], [], coord_space, expansion_distance) } - XENIUMRANGER_IMPORT_SEGMENTATION ( ch_xr ) + XENIUMRANGER_IMPORTSEGMENTATION ( ch_xr ) emit: coordinate_space = ch_coordinate_space // channel: [ "microns" ] - redefined_bundle = XENIUMRANGER_IMPORT_SEGMENTATION.out.outs // channel: [ val(meta), ["redefined-xenium-bundle"] ] + redefined_bundle = XENIUMRANGER_IMPORTSEGMENTATION.out.outs // channel: [ val(meta), ["redefined-xenium-bundle"] ] } diff --git a/subworkflows/local/cellpose_baysor_import_segmentation/main.nf b/subworkflows/local/cellpose_baysor_import_segmentation/main.nf index 38bbcc74..06a15a41 100644 --- a/subworkflows/local/cellpose_baysor_import_segmentation/main.nf +++ b/subworkflows/local/cellpose_baysor_import_segmentation/main.nf @@ -11,7 +11,7 @@ include { CONVERT_MASK_UINT32 } from '../../../modules/local/utilit include { BAYSOR_PREPROCESS_TRANSCRIPTS } from '../../../modules/local/baysor/preprocess/main' include { RESIZE_TIF } from '../../../modules/local/utility/resize_tif/main' include { GET_TRANSCRIPTS_COORDINATES } from '../../../modules/local/utility/get_coordinates/main' -include { XENIUMRANGER_IMPORT_SEGMENTATION } from '../../../modules/nf-core/xeniumranger/import-segmentation/main' +include { XENIUMRANGER_IMPORTSEGMENTATION } from '../../../modules/nf-core/xeniumranger/importsegmentation/main' workflow CELLPOSE_BAYSOR_IMPORT_SEGMENTATION { take: @@ -30,6 +30,7 @@ workflow CELLPOSE_BAYSOR_IMPORT_SEGMENTATION { nucleus_segmentation_only // value: bool sharpen_tiff // value: bool stardist_nuclei_model // value: stardist pretrained model name + expansion_distance // value: nuclear expansion distance main: @@ -180,12 +181,13 @@ workflow CELLPOSE_BAYSOR_IMPORT_SEGMENTATION { [], [], ch_coordinate_space.val, + expansion_distance, ) } - XENIUMRANGER_IMPORT_SEGMENTATION(ch_imp_seg_inputs) + XENIUMRANGER_IMPORTSEGMENTATION(ch_imp_seg_inputs) emit: coordinate_space = ch_coordinate_space // channel: [ val("microns") ] - redefined_bundle = XENIUMRANGER_IMPORT_SEGMENTATION.out.outs // channel: [ val(meta), ["redefined-xenium-bundle"] ] + redefined_bundle = XENIUMRANGER_IMPORTSEGMENTATION.out.outs // channel: [ val(meta), ["redefined-xenium-bundle"] ] } diff --git a/subworkflows/local/cellpose_resolift_morphology_ome_tif/main.nf b/subworkflows/local/cellpose_resolift_morphology_ome_tif/main.nf index 6bb38ded..9b95f912 100644 --- a/subworkflows/local/cellpose_resolift_morphology_ome_tif/main.nf +++ b/subworkflows/local/cellpose_resolift_morphology_ome_tif/main.nf @@ -9,7 +9,7 @@ include { CELLPOSE as CELLPOSE_CELLS } from '../../../modules/nf-core/cell include { EXTRACT_DAPI } from '../../../modules/local/utility/extract_dapi/main' include { STARDIST as STARDIST_NUCLEI } from '../../../modules/nf-core/stardist/main' include { CONVERT_MASK_UINT32 } from '../../../modules/local/utility/convert_mask_uint32/main' -include { XENIUMRANGER_IMPORT_SEGMENTATION } from '../../../modules/nf-core/xeniumranger/import-segmentation/main' +include { XENIUMRANGER_IMPORTSEGMENTATION } from '../../../modules/nf-core/xeniumranger/importsegmentation/main' workflow CELLPOSE_RESOLIFT_MORPHOLOGY_OME_TIF { take: @@ -20,6 +20,7 @@ workflow CELLPOSE_RESOLIFT_MORPHOLOGY_OME_TIF { nucleus_segmentation_only // value: bool sharpen_tiff // value: bool stardist_nuclei_model // value: stardist pretrained model name + expansion_distance // value: nuclear expansion distance main: @@ -106,9 +107,10 @@ workflow CELLPOSE_RESOLIFT_MORPHOLOGY_OME_TIF { [], [], coord_space, + expansion_distance, ) } - XENIUMRANGER_IMPORT_SEGMENTATION( + XENIUMRANGER_IMPORTSEGMENTATION( ch_imp_seg_inputs ) } @@ -128,14 +130,15 @@ workflow CELLPOSE_RESOLIFT_MORPHOLOGY_OME_TIF { cells_seg, [], coord_space, + expansion_distance, ) } - XENIUMRANGER_IMPORT_SEGMENTATION( + XENIUMRANGER_IMPORTSEGMENTATION( ch_imp_seg_inputs ) } emit: coordinate_space = ch_coordinate_space // channel: [ ["pixels"] ] - redefined_bundle = XENIUMRANGER_IMPORT_SEGMENTATION.out.outs // channel: [ val(meta), ["redefined-xenium-bundle"] ] + redefined_bundle = XENIUMRANGER_IMPORTSEGMENTATION.out.outs // channel: [ val(meta), ["redefined-xenium-bundle"] ] } diff --git a/subworkflows/local/proseg_preset_proseg2baysor/main.nf b/subworkflows/local/proseg_preset_proseg2baysor/main.nf index 3f9d8c99..000167c5 100644 --- a/subworkflows/local/proseg_preset_proseg2baysor/main.nf +++ b/subworkflows/local/proseg_preset_proseg2baysor/main.nf @@ -4,12 +4,13 @@ include { PROSEG } from '../../../modules/local/proseg/preset/main' include { PROSEG2BAYSOR } from '../../../modules/local/proseg/proseg2baysor/main' -include { XENIUMRANGER_IMPORT_SEGMENTATION } from '../../../modules/nf-core/xeniumranger/import-segmentation/main' +include { XENIUMRANGER_IMPORTSEGMENTATION } from '../../../modules/nf-core/xeniumranger/importsegmentation/main' workflow PROSEG_PRESET_PROSEG2BAYSOR { take: ch_bundle_path // channel: [ val(meta), ["path-to-xenium-bundle"] ] ch_transcripts_file // channel: [ val(meta), [ "transcripts.parquet" ] ] + expansion_distance // value: nuclear expansion distance main: @@ -37,14 +38,15 @@ workflow PROSEG_PRESET_PROSEG2BAYSOR { [], [], ch_coordinate_space.val, + expansion_distance, ) } - XENIUMRANGER_IMPORT_SEGMENTATION( + XENIUMRANGER_IMPORTSEGMENTATION( ch_imp_seg_inputs ) emit: coordinate_space = ch_coordinate_space // channel: [ "microns" ] - redefined_bundle = XENIUMRANGER_IMPORT_SEGMENTATION.out.outs // channel: [ val(meta), ["redefined-xenium-bundle"] ] + redefined_bundle = XENIUMRANGER_IMPORTSEGMENTATION.out.outs // channel: [ val(meta), ["redefined-xenium-bundle"] ] } diff --git a/subworkflows/local/proseg_preset_proseg2baysor_tiled/main.nf b/subworkflows/local/proseg_preset_proseg2baysor_tiled/main.nf index 7ff6b783..5fe72560 100644 --- a/subworkflows/local/proseg_preset_proseg2baysor_tiled/main.nf +++ b/subworkflows/local/proseg_preset_proseg2baysor_tiled/main.nf @@ -6,13 +6,14 @@ include { XENIUM_PATCH_DIVIDE } from '../../../modules/local/xenium include { PROSEG } from '../../../modules/local/proseg/preset/main' include { PROSEG2BAYSOR } from '../../../modules/local/proseg/proseg2baysor/main' include { XENIUM_PATCH_STITCH } from '../../../modules/local/xenium_patch/stitch/main' -include { XENIUMRANGER_IMPORT_SEGMENTATION } from '../../../modules/nf-core/xeniumranger/import-segmentation/main' +include { XENIUMRANGER_IMPORTSEGMENTATION } from '../../../modules/nf-core/xeniumranger/importsegmentation/main' workflow PROSEG_PRESET_PROSEG2BAYSOR_TILED { take: ch_bundle_path // channel: [ val(meta), ["path-to-xenium-bundle"] ] ch_transcripts_file // channel: [ val(meta), [ "transcripts.parquet" ] ] + expansion_distance // value: nuclear expansion distance main: @@ -75,12 +76,12 @@ workflow PROSEG_PRESET_PROSEG2BAYSOR_TILED { .combine(XENIUM_PATCH_STITCH.out.xr_polygons_transcript, by: 0) .combine(ch_coordinate_space) .map { meta, bundle, geojson, csv, coord_space -> - tuple(meta, bundle, csv, geojson, [], [], [], coord_space) + tuple(meta, bundle, csv, geojson, [], [], [], coord_space, expansion_distance) } - XENIUMRANGER_IMPORT_SEGMENTATION ( ch_xr ) + XENIUMRANGER_IMPORTSEGMENTATION ( ch_xr ) emit: coordinate_space = ch_coordinate_space // channel: [ "microns" ] - redefined_bundle = XENIUMRANGER_IMPORT_SEGMENTATION.out.outs // channel: [ val(meta), ["redefined-xenium-bundle"] ] + redefined_bundle = XENIUMRANGER_IMPORTSEGMENTATION.out.outs // channel: [ val(meta), ["redefined-xenium-bundle"] ] } diff --git a/subworkflows/local/segger_create_train_predict/main.nf b/subworkflows/local/segger_create_train_predict/main.nf index 3f832d61..152e0342 100644 --- a/subworkflows/local/segger_create_train_predict/main.nf +++ b/subworkflows/local/segger_create_train_predict/main.nf @@ -6,13 +6,14 @@ include { SEGGER2XR } from '../../../modules/local/utilit include { SEGGER_TRAIN } from '../../../modules/local/segger/train/main' include { SEGGER_PREDICT } from '../../../modules/local/segger/predict/main' include { SEGGER_CREATE_DATASET } from '../../../modules/local/segger/create_dataset/main' -include { XENIUMRANGER_IMPORT_SEGMENTATION } from '../../../modules/nf-core/xeniumranger/import-segmentation/main' +include { XENIUMRANGER_IMPORTSEGMENTATION } from '../../../modules/nf-core/xeniumranger/importsegmentation/main' workflow SEGGER_CREATE_TRAIN_PREDICT { take: ch_bundle // channel: [ val(meta), ["path-to-xenium-bundle"] ] ch_transcripts_file // channel: [ val(meta), [bundle + "/transcripts.parquet"]] segger_model // value: path to a pre-trained segger model checkpoint (or null) + expansion_distance // value: nuclear expansion distance main: @@ -64,14 +65,15 @@ workflow SEGGER_CREATE_TRAIN_PREDICT { [], // cells [], // coordinate_transform ch_coordinate_space.val, + expansion_distance, ) } - XENIUMRANGER_IMPORT_SEGMENTATION( + XENIUMRANGER_IMPORTSEGMENTATION( ch_imp_seg_inputs ) emit: coordinate_space = ch_coordinate_space // channel: [ "microns" ] - redefined_bundle = XENIUMRANGER_IMPORT_SEGMENTATION.out.outs // channel: [ val(meta), ["redefined-xenium-bundle"] ] + redefined_bundle = XENIUMRANGER_IMPORTSEGMENTATION.out.outs // channel: [ val(meta), ["redefined-xenium-bundle"] ] } diff --git a/subworkflows/local/stardist_resolift_morphology_ome_tif/main.nf b/subworkflows/local/stardist_resolift_morphology_ome_tif/main.nf index bc255409..d002d291 100644 --- a/subworkflows/local/stardist_resolift_morphology_ome_tif/main.nf +++ b/subworkflows/local/stardist_resolift_morphology_ome_tif/main.nf @@ -6,7 +6,7 @@ include { RESOLIFT } from '../../../modules/local/resoli include { EXTRACT_DAPI } from '../../../modules/local/utility/extract_dapi/main' include { STARDIST as STARDIST_NUCLEI } from '../../../modules/nf-core/stardist/main' include { CONVERT_MASK_UINT32 } from '../../../modules/local/utility/convert_mask_uint32/main' -include { XENIUMRANGER_IMPORT_SEGMENTATION } from '../../../modules/nf-core/xeniumranger/import-segmentation/main' +include { XENIUMRANGER_IMPORTSEGMENTATION } from '../../../modules/nf-core/xeniumranger/importsegmentation/main' workflow STARDIST_RESOLIFT_MORPHOLOGY_OME_TIF { take: @@ -14,6 +14,7 @@ workflow STARDIST_RESOLIFT_MORPHOLOGY_OME_TIF { ch_bundle_path // channel: [ val(meta), ["path-to-xenium-bundle"] ] sharpen_tiff // value: bool stardist_nuclei_model // value: stardist pretrained model name + expansion_distance // value: nuclear expansion distance main: @@ -58,13 +59,14 @@ workflow STARDIST_RESOLIFT_MORPHOLOGY_OME_TIF { [], [], ch_coordinate_space.val, + expansion_distance, ) } - XENIUMRANGER_IMPORT_SEGMENTATION( + XENIUMRANGER_IMPORTSEGMENTATION( ch_imp_seg_inputs ) emit: coordinate_space = ch_coordinate_space // channel: [ ["pixels"] ] - redefined_bundle = XENIUMRANGER_IMPORT_SEGMENTATION.out.outs // channel: [ val(meta), ["redefined-xenium-bundle"] ] + redefined_bundle = XENIUMRANGER_IMPORTSEGMENTATION.out.outs // channel: [ val(meta), ["redefined-xenium-bundle"] ] } diff --git a/subworkflows/local/utils_nfcore_spatialaxe_pipeline/main.nf b/subworkflows/local/utils_nfcore_spatialaxe_pipeline/main.nf index 801174e2..6f230ef0 100644 --- a/subworkflows/local/utils_nfcore_spatialaxe_pipeline/main.nf +++ b/subworkflows/local/utils_nfcore_spatialaxe_pipeline/main.nf @@ -8,14 +8,14 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -include { UTILS_NFSCHEMA_PLUGIN } from '../../nf-core/utils_nfschema_plugin' -include { paramsSummaryMap } from 'plugin/nf-schema' -include { samplesheetToList } from 'plugin/nf-schema' -include { completionEmail } from '../../nf-core/utils_nfcore_pipeline' -include { completionSummary } from '../../nf-core/utils_nfcore_pipeline' -include { imNotification } from '../../nf-core/utils_nfcore_pipeline' -include { UTILS_NFCORE_PIPELINE } from '../../nf-core/utils_nfcore_pipeline' -include { UTILS_NEXTFLOW_PIPELINE } from '../../nf-core/utils_nextflow_pipeline' +include { UTILS_NFSCHEMA_PLUGIN } from '../../nf-core/utils_nfschema_plugin' +include { paramsSummaryMap } from 'plugin/nf-schema' +include { samplesheetToList } from 'plugin/nf-schema' +include { paramsHelp } from 'plugin/nf-schema' +include { completionEmail } from '../../nf-core/utils_nfcore_pipeline' +include { completionSummary } from '../../nf-core/utils_nfcore_pipeline' +include { UTILS_NFCORE_PIPELINE } from '../../nf-core/utils_nfcore_pipeline' +include { UTILS_NEXTFLOW_PIPELINE } from '../../nf-core/utils_nextflow_pipeline' /* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -63,6 +63,9 @@ workflow PIPELINE_INITIALISATION { // // Validate parameters and generate parameter summary to stdout // + + def before_text = "" + def after_text = "" before_text = """ -\033[2m----------------------------------------------------\033[0m- \033[0;32m,--.\033[0;30m/\033[0;32m,-.\033[0m @@ -73,16 +76,20 @@ workflow PIPELINE_INITIALISATION { \033[0;35m nf-core/spatialaxe ${workflow.manifest.version}\033[0m -\033[2m----------------------------------------------------\033[0m- """ - after_text = """${workflow.manifest.doi ? "\n* The pipeline\n" : ""}${workflow.manifest.doi.tokenize(",").collect { " https://doi.org/${it.trim().replace('https://doi.org/', '')}" }.join("\n")}${workflow.manifest.doi ? "\n" : ""} + after_text = """${workflow.manifest.doi ? "\n* The pipeline\n" : ""}${workflow.manifest.doi.tokenize(",").collect { doi -> " https://doi.org/${doi.trim().replace('https://doi.org/','')}"}.join("\n")}${workflow.manifest.doi ? "\n" : ""} * The nf-core framework https://doi.org/10.1038/s41587-020-0439-x * Software dependencies https://github.com/nf-core/spatialaxe/blob/master/CITATIONS.md """ - command = "nextflow run ${workflow.manifest.name} -profile --input samplesheet.csv --mode --outdir " + if (monochrome_logs) { + before_text = before_text.replaceAll(/\033\[[0-9;]*m/, '') + } - UTILS_NFSCHEMA_PLUGIN( + command = "nextflow run ${workflow.manifest.name} -profile --input samplesheet.csv --outdir " + + UTILS_NFSCHEMA_PLUGIN ( workflow, validate_params, null, @@ -91,13 +98,13 @@ workflow PIPELINE_INITIALISATION { show_hidden, before_text, after_text, - command, + command ) // // Check config provided to the pipeline // - UTILS_NFCORE_PIPELINE( + UTILS_NFCORE_PIPELINE ( nextflow_cli_args ) @@ -162,7 +169,6 @@ workflow PIPELINE_COMPLETION { plaintext_email // boolean: Send plain-text email instead of HTML outdir // path: Path to output directory where results will be published monochrome_logs // boolean: Disable ANSI colour codes in log output - hook_url // string: hook URL for notifications multiqc_report // string: Path to MultiQC report main: @@ -186,13 +192,11 @@ workflow PIPELINE_COMPLETION { } completionSummary(monochrome_logs) - if (hook_url) { - imNotification(summary_params, hook_url) - } + } workflow.onError { - error("❌ Pipeline failed. Please refer to troubleshooting docs: https://nf-co.re/docs/usage/troubleshooting") + log.error "Pipeline failed. Please refer to troubleshooting docs for common issues: https://nf-co.re/docs/running/troubleshooting" } } @@ -290,23 +294,27 @@ def validateInputParameters( // Generate methods description for MultiQC // def toolCitationText() { + // 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 = [ - "Tools used in the workflow included:", - "MultiQC (Ewels et al. 2016)", - ".", - ].join(' ').trim() + "Tools used in the workflow included:", + "FastQC (Andrews 2010),", + "MultiQC (Ewels et al. 2016)", + "." + ].join(' ').trim() return citation_text } def toolBibliographyText() { + // TODO nf-core: Optionally add bibliographic entries to this list. // Can use ternary operators to dynamically construct based conditions, e.g. params["run_xyz"] ? "
  • Author (2023) Pub name, Journal, DOI
  • " : "", // Uncomment function in methodsDescriptionText to render in MultiQC report def reference_text = [ - "
  • Ewels, P., Magnusson, M., Lundin, S., & Käller, M. (2016). MultiQC: summarize analysis results for multiple tools and samples in a single report. Bioinformatics , 32(19), 3047–3048. doi: /10.1093/bioinformatics/btw354
  • " - ].join(' ').trim() + "
  • Andrews S, (2010) FastQC, URL: https://www.bioinformatics.babraham.ac.uk/projects/fastqc/).
  • ", + "
  • Ewels, P., Magnusson, M., Lundin, S., & Käller, M. (2016). MultiQC: summarize analysis results for multiple tools and samples in a single report. Bioinformatics , 32(19), 3047–3048. doi: /10.1093/bioinformatics/btw354
  • " + ].join(' ').trim() return reference_text } @@ -328,10 +336,7 @@ def methodsDescriptionText(mqc_methods_yaml) { temp_doi_ref += "(doi: ${doi_ref.replace("https://doi.org/", "").replace(" ", "")}), " } meta["doi_text"] = temp_doi_ref.substring(0, temp_doi_ref.length() - 2) - } - else { - meta["doi_text"] = "" - } + } else meta["doi_text"] = "" meta["nodoi_text"] = meta.manifest_map.doi ? "" : "
  • If available, make sure to update the text to include the Zenodo DOI of version of the pipeline used.
  • " // Tool references @@ -342,9 +347,10 @@ def methodsDescriptionText(mqc_methods_yaml) { // meta["tool_citations"] = toolCitationText().replaceAll(", \\.", ".").replaceAll("\\. \\.", ".").replaceAll(", \\.", ".") // meta["tool_bibliography"] = toolBibliographyText() + def methods_text = mqc_methods_yaml.text - def engine = new groovy.text.SimpleTemplateEngine() + def engine = new groovy.text.SimpleTemplateEngine() def description_html = engine.createTemplate(methods_text).make(meta) return description_html.toString() diff --git a/subworkflows/local/xeniumranger_import_segmentation_redefine_bundle/main.nf b/subworkflows/local/xeniumranger_import_segmentation_redefine_bundle/main.nf index 4c7b41d5..a2d298f8 100644 --- a/subworkflows/local/xeniumranger_import_segmentation_redefine_bundle/main.nf +++ b/subworkflows/local/xeniumranger_import_segmentation_redefine_bundle/main.nf @@ -2,9 +2,9 @@ // Run xeniumranger import-segmentation // -include { XENIUMRANGER_IMPORT_SEGMENTATION as IMP_SEG_COUNT_MATRIX_EXP_DISTANCE } from '../../../modules/nf-core/xeniumranger/import-segmentation/main' -include { XENIUMRANGER_IMPORT_SEGMENTATION as IMP_SEG_POLYGON_GEOJSON_INPUT } from '../../../modules/nf-core/xeniumranger/import-segmentation/main' -include { XENIUMRANGER_IMPORT_SEGMENTATION as IMP_SEG_TRANS_MATRIX_INPUT } from '../../../modules/nf-core/xeniumranger/import-segmentation/main' +include { XENIUMRANGER_IMPORTSEGMENTATION as IMP_SEG_COUNT_MATRIX_EXP_DISTANCE } from '../../../modules/nf-core/xeniumranger/importsegmentation/main' +include { XENIUMRANGER_IMPORTSEGMENTATION as IMP_SEG_POLYGON_GEOJSON_INPUT } from '../../../modules/nf-core/xeniumranger/importsegmentation/main' +include { XENIUMRANGER_IMPORTSEGMENTATION as IMP_SEG_TRANS_MATRIX_INPUT } from '../../../modules/nf-core/xeniumranger/importsegmentation/main' workflow XENIUMRANGER_IMPORT_SEGMENTATION_REDEFINE_BUNDLE { @@ -40,6 +40,7 @@ workflow XENIUMRANGER_IMPORT_SEGMENTATION_REDEFINE_BUNDLE { [], [], ch_coordinate_space.val, + expansion_distance, ) } @@ -65,6 +66,7 @@ workflow XENIUMRANGER_IMPORT_SEGMENTATION_REDEFINE_BUNDLE { [], [], ch_coordinate_space.val, + expansion_distance, ) } @@ -88,6 +90,7 @@ workflow XENIUMRANGER_IMPORT_SEGMENTATION_REDEFINE_BUNDLE { polygons_geojson, [], ch_coordinate_space.val, + expansion_distance, ) } @@ -117,6 +120,7 @@ workflow XENIUMRANGER_IMPORT_SEGMENTATION_REDEFINE_BUNDLE { polygons_geojson, alignment_csv_file, ch_coordinate_space.val, + expansion_distance, ) } diff --git a/subworkflows/local/xeniumranger_resegment_morphology_ome_tif/main.nf b/subworkflows/local/xeniumranger_resegment_morphology_ome_tif/main.nf index bcd97584..0a5c81ee 100644 --- a/subworkflows/local/xeniumranger_resegment_morphology_ome_tif/main.nf +++ b/subworkflows/local/xeniumranger_resegment_morphology_ome_tif/main.nf @@ -3,12 +3,13 @@ // include { XENIUMRANGER_RESEGMENT } from '../../../modules/nf-core/xeniumranger/resegment/main' -include { XENIUMRANGER_IMPORT_SEGMENTATION } from '../../../modules/nf-core/xeniumranger/import-segmentation/main' +include { XENIUMRANGER_IMPORTSEGMENTATION } from '../../../modules/nf-core/xeniumranger/importsegmentation/main' workflow XENIUMRANGER_RESEGMENT_MORPHOLOGY_OME_TIF { take: ch_bundle_path // channel: [ val(meta), ["path-to-xenium-bundle"] ] nucleus_segmentation_only // value: bool + expansion_distance // value: nuclear expansion distance main: @@ -41,14 +42,15 @@ workflow XENIUMRANGER_RESEGMENT_MORPHOLOGY_OME_TIF { cells_zarr, [], "pixels", + expansion_distance, ) } - XENIUMRANGER_IMPORT_SEGMENTATION( + XENIUMRANGER_IMPORTSEGMENTATION( ch_imp_seg_inputs ) - ch_redefined_bundle = XENIUMRANGER_IMPORT_SEGMENTATION.out.outs + ch_redefined_bundle = XENIUMRANGER_IMPORTSEGMENTATION.out.outs } else { diff --git a/tests/coordinate_mode.nf.test.snap b/tests/coordinate_mode.nf.test.snap index 4967372e..96d5cb15 100644 --- a/tests/coordinate_mode.nf.test.snap +++ b/tests/coordinate_mode.nf.test.snap @@ -24,9 +24,9 @@ "untar": 1.34 }, "Workflow": { - "nf-core/spatialaxe": "v1.0.0" + "nf-core/spatialaxe": "v1.0.1" }, - "XENIUMRANGER_IMPORT_SEGMENTATION": { + "XENIUMRANGER_IMPORTSEGMENTATION": { "xeniumranger": "4.0.1.1" } }, @@ -98,12 +98,11 @@ "coordinate/untar/test_run/nucleus_boundaries.parquet", "coordinate/untar/test_run/transcripts.parquet", "coordinate/untar/test_run/transcripts.zarr.zip", - "coordinate/xeniumranger", - "coordinate/xeniumranger/import_segementation", - "coordinate/xeniumranger/import_segementation/test_run", - "coordinate/xeniumranger/import_segementation/test_run/experiment.xenium", "pipeline_info", - "pipeline_info/nf_core_spatialaxe_software_mqc_versions.yml" + "pipeline_info/nf_core_spatialaxe_software_mqc_versions.yml", + "xeniumranger", + "xeniumranger/test_run", + "xeniumranger/test_run/experiment.xenium" ], [ ".stub:md5,d41d8cd98f00b204e9800998ecf8427e", @@ -143,10 +142,10 @@ "experiment.xenium:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], - "timestamp": "2026-05-18T21:58:08.945192717", + "timestamp": "2026-07-28T12:22:02.690566285", "meta": { "nf-test": "0.9.5", - "nextflow": "25.10.4" + "nextflow": "26.04.6" } } } \ No newline at end of file diff --git a/tests/default.nf.test.snap b/tests/default.nf.test.snap index d5946632..840d3c89 100644 --- a/tests/default.nf.test.snap +++ b/tests/default.nf.test.snap @@ -24,9 +24,9 @@ "untar": 1.34 }, "Workflow": { - "nf-core/spatialaxe": "v1.0.0" + "nf-core/spatialaxe": "v1.0.1" }, - "XENIUMRANGER_IMPORT_SEGMENTATION": { + "XENIUMRANGER_IMPORTSEGMENTATION": { "xeniumranger": "4.0.1.1" } }, @@ -98,12 +98,11 @@ "coordinate/untar/test_run/nucleus_boundaries.parquet", "coordinate/untar/test_run/transcripts.parquet", "coordinate/untar/test_run/transcripts.zarr.zip", - "coordinate/xeniumranger", - "coordinate/xeniumranger/import_segementation", - "coordinate/xeniumranger/import_segementation/test_run", - "coordinate/xeniumranger/import_segementation/test_run/experiment.xenium", "pipeline_info", - "pipeline_info/nf_core_spatialaxe_software_mqc_versions.yml" + "pipeline_info/nf_core_spatialaxe_software_mqc_versions.yml", + "xeniumranger", + "xeniumranger/test_run", + "xeniumranger/test_run/experiment.xenium" ], [ ".stub:md5,d41d8cd98f00b204e9800998ecf8427e", @@ -143,10 +142,10 @@ "experiment.xenium:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], - "timestamp": "2026-05-18T21:55:37.544217665", + "timestamp": "2026-07-28T12:20:41.663589777", "meta": { "nf-test": "0.9.5", - "nextflow": "25.10.4" + "nextflow": "26.04.6" } } } \ No newline at end of file diff --git a/tests/image_mode.nf.test.snap b/tests/image_mode.nf.test.snap index 4b2c5d3a..d70b912f 100644 --- a/tests/image_mode.nf.test.snap +++ b/tests/image_mode.nf.test.snap @@ -30,18 +30,13 @@ "untar": 1.34 }, "Workflow": { - "nf-core/spatialaxe": "v1.0.0" + "nf-core/spatialaxe": "v1.0.1" }, - "XENIUMRANGER_IMPORT_SEGMENTATION": { + "XENIUMRANGER_IMPORTSEGMENTATION": { "xeniumranger": "4.0.1.1" } }, [ - "coordinate", - "coordinate/xeniumranger", - "coordinate/xeniumranger/import_segementation", - "coordinate/xeniumranger/import_segementation/test_run", - "coordinate/xeniumranger/import_segementation/test_run/experiment.xenium", "image", "image/baysor", "image/baysor/preprocess", @@ -115,10 +110,12 @@ "image/utility/resize_tif/test_run", "image/utility/resize_tif/test_run/resized_morphology_focus_0000.ome_cp_masks.tif.tif", "pipeline_info", - "pipeline_info/nf_core_spatialaxe_software_mqc_versions.yml" + "pipeline_info/nf_core_spatialaxe_software_mqc_versions.yml", + "xeniumranger", + "xeniumranger/test_run", + "xeniumranger/test_run/experiment.xenium" ], [ - "experiment.xenium:md5,d41d8cd98f00b204e9800998ecf8427e", "filtered_transcripts.csv:md5,d41d8cd98f00b204e9800998ecf8427e", "segmentation.csv:md5,d41d8cd98f00b204e9800998ecf8427e", "segmentation_polygons_2d.json:md5,d41d8cd98f00b204e9800998ecf8427e", @@ -155,13 +152,14 @@ "nucleus_boundaries.parquet:md5,d41d8cd98f00b204e9800998ecf8427e", "transcripts.parquet:md5,d41d8cd98f00b204e9800998ecf8427e", "transcripts.zarr.zip:md5,d41d8cd98f00b204e9800998ecf8427e", - "resized_morphology_focus_0000.ome_cp_masks.tif.tif:md5,d41d8cd98f00b204e9800998ecf8427e" + "resized_morphology_focus_0000.ome_cp_masks.tif.tif:md5,d41d8cd98f00b204e9800998ecf8427e", + "experiment.xenium:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], - "timestamp": "2026-05-18T22:00:39.030306506", + "timestamp": "2026-07-28T12:22:39.999118613", "meta": { "nf-test": "0.9.5", - "nextflow": "25.10.4" + "nextflow": "26.04.6" } } } \ No newline at end of file diff --git a/tests/nextflow.config b/tests/nextflow.config index 2c1bfd32..5a98fc21 100644 --- a/tests/nextflow.config +++ b/tests/nextflow.config @@ -7,7 +7,7 @@ // Or any resources requirements params { modules_testdata_base_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/modules/data/' - pipelines_testdata_base_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/refs/heads/spatialaxe' + pipelines_testdata_base_path = 'https://raw.githubusercontent.com/nf-core/test-datasets/refs/heads/spatialaxe/' } aws.client.anonymous = true // fixes S3 access issues on self-hosted runners diff --git a/tests/preview_mode.nf.test.snap b/tests/preview_mode.nf.test.snap index e44ff636..cc286151 100644 --- a/tests/preview_mode.nf.test.snap +++ b/tests/preview_mode.nf.test.snap @@ -18,7 +18,7 @@ "untar": 1.34 }, "Workflow": { - "nf-core/spatialaxe": "v1.0.0" + "nf-core/spatialaxe": "v1.0.1" } }, [ @@ -111,10 +111,10 @@ "umap_mqc.tsv:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], - "timestamp": "2026-06-17T11:15:30.764424637", + "timestamp": "2026-07-23T15:57:29.316600844", "meta": { "nf-test": "0.9.5", - "nextflow": "26.04.2" + "nextflow": "26.04.6" } } } \ No newline at end of file diff --git a/tests/segfree_mode.nf.test.snap b/tests/segfree_mode.nf.test.snap index af848bc6..90a83476 100644 --- a/tests/segfree_mode.nf.test.snap +++ b/tests/segfree_mode.nf.test.snap @@ -12,7 +12,7 @@ "untar": 1.34 }, "Workflow": { - "nf-core/spatialaxe": "v1.0.0" + "nf-core/spatialaxe": "v1.0.1" } }, [ @@ -88,10 +88,10 @@ "transcripts.zarr.zip:md5,d41d8cd98f00b204e9800998ecf8427e" ] ], - "timestamp": "2026-05-18T22:02:39.947804998", + "timestamp": "2026-07-23T15:57:58.134363385", "meta": { "nf-test": "0.9.5", - "nextflow": "25.10.4" + "nextflow": "26.04.6" } } } \ No newline at end of file diff --git a/workflows/spatialaxe.nf b/workflows/spatialaxe.nf index 8e11f058..3ff05db9 100644 --- a/workflows/spatialaxe.nf +++ b/workflows/spatialaxe.nf @@ -145,7 +145,9 @@ workflow SPATIALAXE { UNTAR(ch_input_untar) ch_untar_outs = UNTAR.out.untar.map { meta, bundle -> - return [meta, bundle.toString()] + // use toUriString() (not toString()) so the URI scheme (e.g. s3://) + // is preserved when the work dir is on object storage + return [meta, bundle.toUriString()] } ch_samplesheet @@ -205,12 +207,12 @@ workflow SPATIALAXE { error("❌ Xenium bundle does not exist: ${bundle}") } - def missing_required = bundle_required_files.findAll { check -> !file("${bundle_path}/${check}").exists() } + def missing_required = bundle_required_files.findAll { check -> !bundle_path.resolve(check).exists() } if (missing_required) { error("❌ Missing required file(s) in xenium bundle '${bundle}': ${missing_required}") } - def missing_optional = bundle_optional_files.findAll { check -> !file("${bundle_path}/${check}").exists() } + def missing_optional = bundle_optional_files.findAll { check -> !bundle_path.resolve(check).exists() } if (missing_optional) { log.warn("⚠️ Missing optional file(s) in xenium bundle '${bundle}': ${missing_optional}") } @@ -222,7 +224,7 @@ workflow SPATIALAXE { // get transcript.parquet from the xenium bundle ch_transcripts_file = ch_input.map { meta, bundle, _image -> def transcripts_parquet = file( - bundle.toString().replaceFirst(/\/$/, '') + "/transcripts.parquet", + file(bundle).toUriString().replaceFirst(/\/$/, '') + "/transcripts.parquet", checkIfExists: true ) return [meta, transcripts_parquet] @@ -239,7 +241,7 @@ workflow SPATIALAXE { if (image) { morphology_img = file(image) } else { - def bundle_path = bundle.toString().replaceFirst(/\/$/, '') + def bundle_path = file(bundle).toUriString().replaceFirst(/\/$/, '') def focus_v3 = file("${bundle_path}/morphology_focus/morphology_focus_0000.ome.tif") def focus_v4 = file("${bundle_path}/morphology_focus/ch0000_dapi.ome.tif") def focus_v1 = file("${bundle_path}/morphology_focus.ome.tif") @@ -259,7 +261,7 @@ workflow SPATIALAXE { // get experiment metdata - experiment.xenium ch_exp_metadata = ch_input.map { meta, bundle, _image -> def exp_metadata = file( - bundle.toString().replaceFirst(/\/$/, '') + "/experiment.xenium", + file(bundle).toUriString().replaceFirst(/\/$/, '') + "/experiment.xenium", checkIfExists: true ) return [meta, exp_metadata] @@ -345,7 +347,7 @@ workflow SPATIALAXE { // gene panel to use if only --relabel_genes is provided ch_gene_panel = ch_input.map { meta, bundle, _image -> def gene_panel_file = file( - bundle.toString().replaceFirst(/\/$/, '') + "/gene_panel.json", + file(bundle).toUriString().replaceFirst(/\/$/, '') + "/gene_panel.json", checkIfExists: true ) return [meta, gene_panel_file] @@ -432,6 +434,7 @@ workflow SPATIALAXE { nucleus_segmentation_only, sharpen_tiff, stardist_nuclei_model, + expansion_distance, ) ch_redefined_bundle = CELLPOSE_BAYSOR_IMPORT_SEGMENTATION.out.redefined_bundle ch_coordinate_space = CELLPOSE_BAYSOR_IMPORT_SEGMENTATION.out.coordinate_space @@ -443,6 +446,7 @@ workflow SPATIALAXE { XENIUMRANGER_RESEGMENT_MORPHOLOGY_OME_TIF( ch_bundle_path, nucleus_segmentation_only, + expansion_distance, ) ch_redefined_bundle = XENIUMRANGER_RESEGMENT_MORPHOLOGY_OME_TIF.out.redefined_bundle ch_coordinate_space = XENIUMRANGER_RESEGMENT_MORPHOLOGY_OME_TIF.out.coordinate_space @@ -462,6 +466,7 @@ workflow SPATIALAXE { min_qv, min_x, min_y, + expansion_distance, ) } ch_redefined_bundle = BAYSOR_RUN_PRIOR_SEGMENTATION_MASK.out.redefined_bundle @@ -479,6 +484,7 @@ workflow SPATIALAXE { nucleus_segmentation_only, sharpen_tiff, stardist_nuclei_model, + expansion_distance, ) ch_redefined_bundle = CELLPOSE_RESOLIFT_MORPHOLOGY_OME_TIF.out.redefined_bundle ch_coordinate_space = CELLPOSE_RESOLIFT_MORPHOLOGY_OME_TIF.out.coordinate_space @@ -492,6 +498,7 @@ workflow SPATIALAXE { ch_bundle_path, sharpen_tiff, stardist_nuclei_model, + expansion_distance, ) ch_redefined_bundle = STARDIST_RESOLIFT_MORPHOLOGY_OME_TIF.out.redefined_bundle ch_coordinate_space = STARDIST_RESOLIFT_MORPHOLOGY_OME_TIF.out.coordinate_space @@ -512,6 +519,7 @@ workflow SPATIALAXE { PROSEG_PRESET_PROSEG2BAYSOR_TILED( ch_bundle_path, ch_transcripts_file, + expansion_distance, ) ch_redefined_bundle = PROSEG_PRESET_PROSEG2BAYSOR_TILED.out.redefined_bundle ch_coordinate_space = PROSEG_PRESET_PROSEG2BAYSOR_TILED.out.coordinate_space @@ -519,6 +527,7 @@ workflow SPATIALAXE { PROSEG_PRESET_PROSEG2BAYSOR( ch_bundle_path, ch_transcripts_file, + expansion_distance, ) ch_redefined_bundle = PROSEG_PRESET_PROSEG2BAYSOR.out.redefined_bundle ch_coordinate_space = PROSEG_PRESET_PROSEG2BAYSOR.out.coordinate_space @@ -532,6 +541,7 @@ workflow SPATIALAXE { ch_bundle_path, ch_transcripts_file, segger_model, + expansion_distance, ) ch_redefined_bundle = SEGGER_CREATE_TRAIN_PREDICT.out.redefined_bundle ch_coordinate_space = SEGGER_CREATE_TRAIN_PREDICT.out.coordinate_space @@ -563,6 +573,7 @@ workflow SPATIALAXE { min_qv, min_x, min_y, + expansion_distance, ) ch_redefined_bundle = BAYSOR_RUN_TRANSCRIPTS_PARQUET.out.redefined_bundle ch_coordinate_space = BAYSOR_RUN_TRANSCRIPTS_PARQUET.out.coordinate_space