Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<a href="https://cbib.github.io/SpaceBlocks/"><img src="https://img.shields.io/badge/docs-online-blue" alt="Documentation"></a>
<a href="https://github.com/cbib/SpaceBlocks/actions/workflows/tests.yml"><img src="https://github.com/cbib/SpaceBlocks/actions/workflows/tests.yml/badge.svg" alt="Tests"></a>
<a href="https://github.com/cbib/SpaceBlocks/blob/main/LICENSE.md"><img src="https://img.shields.io/github/license/cbib/SpaceBlocks" alt="License"></a>
<a href="https://snakemake.github.io/snakemake-workflow-catalog/"><img src="https://img.shields.io/badge/Snakemake-workflow-039475" alt="Snakemake workflow"></a>
<a href="https://snakemake.github.io/snakemake-workflow-catalog/docs/workflows/cbib/SpaceBlocks.html"><img src="https://img.shields.io/badge/Snakemake-workflow-039475" alt="Snakemake workflow"></a>
</p>

<p align="center">
Expand Down
2 changes: 1 addition & 1 deletion config/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ The explanations on this page are divided by type (directory, parameter, color,
**Every key in `config/config.yaml` is validated** against `workflow/schemas/config.schema.yaml` before the run starts, so a typo or a missing required field fails immediately with a clear message.

!!! note "Snakemake-catalog page"
The full, always-updated table of every parameter (type, default, required) is generated automatically from the schema and shown on the workflow's Snakemake-catalog page.
The full, always-updated table of every parameter (type, default, required) is generated automatically from the schema and shown on the workflow's [Snakemake-catalog page](https://snakemake.github.io/snakemake-workflow-catalog/docs/workflows/cbib/SpaceBlocks.html#workflow-parameters).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
The full, always-updated table of every parameter (type, default, required) is generated automatically from the schema and shown on the workflow's [Snakemake-catalog page](https://snakemake.github.io/snakemake-workflow-catalog/docs/workflows/cbib/SpaceBlocks.html#workflow-parameters).
The full, always-updated table of every parameter (type, default, required) is generated automatically from the schema and shown on the workflow's [Snakemake-catalog page](https://snakemake.github.io/snakemake-workflow-catalog/docs/workflows/cbib/SpaceBlocks.html).

This page covers the *how* and the *why*; the schema is the exhaustive reference.

## 1. Choose a mode
Expand Down
22 changes: 11 additions & 11 deletions docs/demos.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

This page shows how to run SpaceBlocks end-to-end on **public data** for each supported technology, and documents how to prepare your own inputs for `mode: decoupled`.

!!! warning "Repository synthetic data"
!!! warning "Repository's synthetic data"
The tiny case in `.test/` exists only to exercise the DAG in CI, and to render the workflow's tube map on the Snakemake catalog.

## Technical notes

To keep the tutorials simple and lightweight, we use 1 sample from a public dataset and divide it into **3 artificial samples, keeping only 500 HVGs**.
To keep the tutorials simple and lightweight, we use 1 sample from a public dataset and divide it into **3 artificial samples, keeping only 500 highly-variable genes (HVGs)**.

We provide preconfigured `config` files, pre-annotated regions (GeoJSON files), clusters and cluster-to-cell type equivalences for the example public datasets to ensure reproducibility.
We provide default `config` files, pre-annotated regions (GeoJSON files), clusters and cluster-to-cell type equivalences for the example public datasets to ensure reproducibility.

The exact environment versions used during the generation of these tutorials can be found under `demos/lock.envs`.

Expand All @@ -27,14 +27,14 @@ Our HPC specifications
1 TB RAM
```

## Visium HD example (mouse brain)
## Visium HD uncoupled mode example (mouse brain)

SpaceBlocks is built to analyse single-cell resolution Spatial Transcriptomics data, so Visium HD data needs to be preprocessed via [bin2cell](https://github.com/Teichlab/bin2cell), [ENACT](https://github.com/Sanofi-Public/enact-pipeline) or, as in the Visium HD HeadBlock, Space Ranger >= v4.0.1 (internally implementing StarDist segmentation).

!!! important "Space Ranger version matters"
We use a **Space Ranger ≥ 4.0.1** Visium HD dataset (one that ships `segmented_outputs/`). The older Space Ranger 3.x Mouse Brain release has **no StarDist segmentation**, so `format_visiumhd.py` would > find no cell table.
We use a **Space Ranger ≥ 4.0.1** Visium HD dataset (one that ships `segmented_outputs/`). The older Space Ranger 3.x Mouse Brain release has **no StarDist segmentation**, so `format_visiumhd.py` would find no cell table.

For the example here presented, you may download the Visium HD dataset from the [10x Genomics web](https://www.10xgenomics.com/datasets/visium-hd-cytassist-gene-expression-libraries-of-mouse-brain-he-v4), or via terminal using curl or wget. This dataset has been processed with Space Ranger v4.0.1.
For the example here presented, you may download the Visium HD dataset from the [10x Genomics web](https://www.10xgenomics.com/datasets/visium-hd-cytassist-gene-expression-libraries-of-mouse-brain-he-v4), or via terminal using `curl` or `wget`. This dataset has been processed with Space Ranger v4.0.1.

!!! note "Visium HD demo dataset size and SpaceBlocks mode"
This demo runs in `mode: decoupled` and the size of the dataset is ~15GB.
Expand Down Expand Up @@ -63,7 +63,7 @@ python format_visiumhd.py # -> contracts/<sample>.h5ad + core_samples

We provide `demo_vhd.geojson` for this dataset, as an example QuPath export on the hires image (see [QuPath annotation tutorial](qupath-tutorial.md)).

## Xenium 5K example (human melanoma)
## Xenium 5K uncoupled mode example (human melanoma)

You may download the Xenium dataset from the [10x Genomics web](https://www.10xgenomics.com/datasets/xenium-prime-ffpe-human-skin), or via terminal using curl or wget.

Expand Down Expand Up @@ -92,7 +92,7 @@ Notice that the Xenium GeoJSON is annotated in pixels, while cells are in micron

Importantly, **`format_xenium.py` embeds a greyscale composite of the `morphology_focus` channels in `uns["spatial"]`**, so spatial plots are drawn over the tissue instead of on a bare scatter. Set `HIRES_LEVEL` in the script to trade resolution for file size.

## Atera HeadBlock test (human breast cancer, alpha)
## Atera HeadBlock example (human breast cancer, alpha)

!!! warning "Alpha support"
Atera is expected to ship in the second half of 2026. The dataset below is a public
Expand Down Expand Up @@ -153,10 +153,10 @@ Importantly, the registered H&E is not the raw whole-slide scan, which covers a

The bundle holds a single sample, so `run_postprocessing` is not meaningful/complete here because integration, pseudobulk DE and composition comparisons all need replicates. The shipped config therefore trims those options; see `demos/xenium5k/xenium5k_config.yaml` for a config that exercises them.

## MERSCOPE HeadBlock test (mouse brain)
## MERSCOPE HeadBlock example (mouse brain)

Like the Atera demo, the MERSCOPE demo runs its HeadBlock the contract h5ad is built by `prepare_input_mer` straight from a raw Vizgen region, so it exercises the whole MERSCOPE head chain (`generate_qupath_mer` → `prepare_input_mer`). The
head reads the two Vizgen cell CSVs directly and reuses the `xenium5k.yaml` environment (no intermediate zarr, no new environment).
head reads the two Vizgen cell CSVs directly and reuses the `5k.yaml` environment (no intermediate zarr, no new environment).

!!! note "MERSCOPE demo dataset size and SpaceBlocks mode"
This demo runs in `mode: merscope` and the size of the dataset is ~25GB.
Expand Down Expand Up @@ -227,7 +227,7 @@ configfile: "demos/xenium5k/xenium5k_config.yaml"
```

!!! warning "DO NOT INCLUDE several config files"
**Make sure to only have one uncommented config file in the Snakefile.** Snakemake allows the Snakefile to reference multiple config files, so having multiple config files would lead to unexpected results.
**Make sure to only have one uncommented config file in the Snakefile.** Snakemake allows the Snakefile to reference multiple config files, so having multiple config files could lead to unexpected results.

Then launch the core:

Expand Down
16 changes: 9 additions & 7 deletions docs/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@ SpaceBlocks/
│ └── schemas/*.yaml config + sample-sheet validation
├── config/ config.yaml, README.md (config reference), sample sheets
├── docs/ this documentation site (MkDocs)
├── .test/ tiny synthetic decoupled dataset for CI
├── demos/ full public-data runs (data fetched, not committed)
├── .test/ tiny synthetic decoupled dataset for CI
├── demos/ full public-data runs (data fetched, not committed)
├── tools/ stand-alone helper scripts
└── profiles/default/ SLURM profile (retries, resources)
├── profiles/ execution profiles (retries, resources)
│ ├── default/config.yaml local profile
│ └── slurm/config.yaml SLURM profile
```

## The Blocks
Expand Down Expand Up @@ -99,14 +101,14 @@ For what every file is and why it is useful, see [Outputs](outputs.md); the tree
## Key design decisions

- **HeadBlock/CoreBlock split.** A technology-specific set of rules (HeadBlock) produces a standardized unfiltered AnnData file (contract); the common CoreBlocks consume it for the analyses. Inclusion of new platform/s only requires the development of a new HeadBlock, and the CoreBlock does not need to be changed, unless it has to be expanded.
- **`validate_input` is a DAG gate.** The division in ensured by a validation rule, which can pass or give a hard/soft failure.
- **`validate_input` is a DAG gate.** The division is ensured by a validation rule, which can pass or produce a hard/soft failure.
- A `.json` is written if the contract structure is validated.
- A hard failure prevents the `.json` from being written, and thus the CoreBlock from running.
- Soft issues (missing region annotation, no mito genes, no image) are recorded, but do not necessarily prevent CoreBlocks from running (config file has a paramter for soft-passing).
- Soft issues (missing region annotation, no mito genes, no image) are recorded, but do not necessarily prevent CoreBlocks from running (the config file has a parameter for soft-passing).
- **Coherent naming.** Head rules carry a 3-letter technology code (`_vhd`, `_x5k`) so the organization is easy to follow and heads can coexist.
- **`qc_sweep` rule is diagnostic only** — it never filters, clusters, or writes an h5ad.
- **External annotation takes over.** When enabled, it becomes the primary annotation everywhere. The [Configuration](configuration.md) allows flexibility to retain all cells or remove externally unannotated ones.
- **Config-driven colours** — regions, sample metadata, and cell types, applied consistently across every plot, with a grey fallback for undefined levels. This allows precise and consitent color representations through the analyses.
- **Config-driven colours** — regions, sample metadata, and cell types are applied consistently across every plot, with a grey fallback for undefined levels. This allows precise and consistent colour representations throughout the analyses.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- **Config-driven colours** — regions, sample metadata, and cell types are applied consistently across every plot, with a grey fallback for undefined levels. This allows precise and consistent colour representations throughout the analyses.
- **Config-driven colours** — regions, sample metadata, and cell type colours are applied consistently across every plot, with a grey fallback for undefined levels. This allows precise and consistent colour representations throughout the analyses.

- **Retries scale memory.** `mem_mb` grows with the attempt number, so an OOM-killed job is resubmitted with more RAM.

## Extending SpaceBlocks
Expand All @@ -117,7 +119,7 @@ The aim of SpaceBlocks is to allow for long-term maintainable and extensible Spa

If you wished to run SpaceBlocks but your platform is not listed among the available SpaceBlocks HeadBlocks, there are two options available:
1. Running SpaceBlocks in `mode: decoupled` (**easier, recommended**).
2. Writting a new SpaceBlocks HeadBlock.
2. Writing a new SpaceBlocks HeadBlock.

See [public data end-to-end example runs](demos.md) for demonstrations about how to run SpaceBlocks in decoupled mode.

Expand Down
4 changes: 3 additions & 1 deletion docs/environments.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ The pipeline provisions one conda environment per rule group (run with `--use-co
| `envs/atera.yaml` | atera HeadBlocks (alpha) | pinned `spatialdata-io >=0.7,<0.8` |


`>=` bounds are reproducible enough for day-to-day use but not for archival reproducibility (a future solve may pick newer, potentially breaking versions). For a publication release, we recommend to generate and share the **exact** locks from the environments you actually tested, as below.
`>=` bounds are reproducible enough for day-to-day use but not for archival reproducibility (a future solver may pick newer, potentially breaking versions). For a publication release, we recommend to generate and share the **exact** locks from the environments you actually tested, as below.

!!! tip "Lock your environments"
For a reproducible release, commit one **lock file per environment** next to the
Expand Down Expand Up @@ -68,6 +68,8 @@ conda-lock lock -f workflow/envs/<envname>.yaml -p linux-64 -p osx-64 --kind exp
## Note on the Atera environment

`workflow/envs/atera.yaml` duplicates most of `envs/xenium5k.yaml` on purpose until Atera is officially released. The Atera bundle uses the Xenium Onboard Analysis v4 morphology layout, which only `spatialdata-io >= 0.7` reads, and its `experiment.xenium` reports a sentinel version (`xenium-9.9.9.9`) meaning "newest", so a future reader release that adds an upper-bounded version branch could silently misroute the data.


## Note on the DE environment

`workflow/envs/pseudobulk_de.yaml` is pinned to a coherent Bioconductor 3.18 / R 4.3 release. If your working environment used a different Bioconductor/R release, replace it with an exact export (above) so the published env matches what you validated.
Expand Down
6 changes: 3 additions & 3 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ If you want to see an example run on public data, see the [public data end-to-en
- [Snakemake](https://snakemake.readthedocs.io) ≥ 9.13 and Conda/Mamba.
- For the **Visium HD** head only: an external [Space Ranger](https://www.10xgenomics.com/support/software/space-ranger) ≥ 4.0.1.
- [QuPath](https://qupath.github.io/) (desktop), recommended to include manual region annotations for the downstream analyses (see [QuPath annotation tutorial](qupath-tutorial.md)).
- Configure your Snakemake profile in `--profile profiles/default`. An example for HPCs with slurm scheduler is provided.
- Configure your Snakemake profile in `--profile profiles/default`. An example for HPCs with Slurm scheduler is provided (tested with version 24.11.5).

We recommend to always run snakemake with `--sdm conda`, so each rule gets its pinned environment.
We recommend to always run snakemake with `--sdm conda`, so each rule is executed within its pinned environment.

## 2. Configure

Expand Down Expand Up @@ -50,7 +50,7 @@ While this step is optional (without it, all observations will become annotated

### 3.1. Region annotation in decoupled mode

In `mode: decoupled` there is no Headblock, so `qupath_images` produces nothing and the pipeline never joins a GeoJSON.
In `mode: decoupled` there is no HeadBlock, so `qupath_images` produces nothing and the pipeline never joins a GeoJSON.

Your provided contract h5ads should therefore already carry `obs["region_annotation"]`; otherwise every cell is `Unlabeled`.

Expand Down
6 changes: 3 additions & 3 deletions docs/qupath-tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

SpaceBlocks lets you overlay manual **region annotations** (tumour, healthy, necrosis, …) onto your samples. You draw them once in [QuPath](https://qupath.github.io/), export them as GeoJSON, and the pipeline folds them into `obs["region_annotation"]`.

This is an **optional but recommended step** (without it every cell is `Unlabeled`) because it unlocks the region-aware analyses (neighbourhood, per-region co-occurrence, region-level pseudobulk).
This is an **optional but recommended step** because it unlocks the region-aware analyses (neighbourhood, per-region co-occurrence, region-level pseudobulk). Without it, every cell will be `Unlabeled`.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This is an **optional but recommended step** because it unlocks the region-aware analyses (neighbourhood, per-region co-occurrence, region-level pseudobulk). Without it, every cell will be `Unlabeled`.
This is an **optional but recommended step** because it unlocks the region-aware analyses (neighbourhood, per-region co-occurrence, region-level pseudobulk). **Without it, every cell will be classified as `Unlabeled`**.


Choosing QuPath means anatomopathologists and researchers without bioinformatics skills can annotate the histology directly, while the annotations stay easy to fold back into the AnnData objects.

[Napari](https://napari.org/) is a possible alternative for writting the GeoJSON files, but it is a Python application aimed at programmers.
[Napari](https://napari.org/) is a possible alternative for writing the GeoJSON files, but it is a Python application aimed at programmers.

## 1. Get the image to annotate

| Mode | How to obtain the image |
| --- | --- |
| `visiumhd` / `xenium5k` (a Headblock runs) | `snakemake qupath_images` writes one image per sample under `Samples/{sample}/QuPath_image/` (a hires PNG for Visium HD, a morphology TIFF for Xenium 5K). |
| `visiumhd` / `xenium5k` (a Headblock runs) | `snakemake qupath_images` writes one image per sample under `Samples/{sample}/QuPath_image/` (a Hi-Res PNG for Visium HD, a morphology TIFF for Xenium 5K). |

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
| `visiumhd` / `xenium5k` (a Headblock runs) | `snakemake qupath_images` writes one image per sample under `Samples/{sample}/QuPath_image/` (a Hi-Res PNG for Visium HD, a morphology TIFF for Xenium 5K). |
| `visiumhd` / `xenium5k` (a Headblock runs) | `snakemake qupath_images` writes one image per sample under `Samples/{sample}/QuPath_image/` (a high resolution PNG for Visium HD, a morphology TIFF for Xenium 5K). |

| `decoupled` (no Headblock) | There is no `qupath_images` target — the image is instead **embedded in the contract h5ad** you provided (`uns["spatial"]`). Annotate the regions when you build the contract externally (see [Preparing inputs for decoupled mode](demos.md#preparing-inputs-for-decoupled-mode)). |

## 2. Annotate in QuPath
Expand Down
6 changes: 3 additions & 3 deletions docs/rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Inputs/outputs are summarised; the `.smk` files and `config["resources"]` are th

## HeadBlocks

HeadBlocks are *optional* technology-specific modules, selected in `config["mode"]`. Each
HeadBlocks are *optional*, technology-specific modules selected in `config["mode"]`. Each
one produces the standardized **contract h5ad** the CoreBlocks start from; the rules within
a headBlock carry a 3-letter technology suffix (`_vhd`, `_x5k`, `_ate`, `_mer`) so the organization stays
easy to follow.
Expand All @@ -20,7 +20,7 @@ The pipeline can also run in `mode: decoupled`, without any headBlock — the Co
consume pre-existing contract h5ads directly.

!!! tip "Annotate your regions first"
Whichever headBlock you use, run the `qupath_images` target **first**: it produces the
Whichever HeadBlock you use, run the `qupath_images` target **first**: it produces the
per-sample annotation images, which you annotate in QuPath and export as GeoJSON *before*
launching the rest of the run. The contract builders (`prepare_input_*`) pick those
GeoJSONs up automatically (and fall back to `Unlabeled` regions if none are present).
Expand Down Expand Up @@ -212,7 +212,7 @@ Using the shared expression ranges from the integrated exploration, produces per

## Resources & retries

No rule uses `localrule`, so nothing runs on the scheduler's head node.
No rule uses `localrule`, so nothing runs on the scheduler's head node as long as the selected profile has configured remote execution (e.g. see `profiles/slurm`)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
No rule uses `localrule`, so nothing runs on the scheduler's head node as long as the selected profile has configured remote execution (e.g. see `profiles/slurm`)
No rule uses `localrule`. This is particularly important when the Snakemake profile is configured for remote execution (e.g. see `profiles/slurm`), so nothing runs on the scheduler's head node.


Every compute rule draws `mem_mb` / `runtime` / `threads` from `config["resources"]` (with a `default` fallback).

Expand Down
Loading
Loading