★ Verified · ⇄ Official port of
nf-core/atacseq@2.1.2— same tools, same versions, same commands. Part of the oxo-flow-community catalog.
ATAC-seq (Assay for Transposase-Accessible Chromatin) analysis for a
cohort of samples: raw-read QC (FastQC), adapter trimming (Trim Galore),
BWA-MEM alignment, Picard mark-duplicates, BAMTools filtering, MACS2
broad-peak calling, HOMER peak annotation, FRiP scoring, normalised
bigWig tracks, deepTools QC plots (profile, heatmap and fingerprint) and
a single MultiQC report. Input is a directory of <sample>.fastq.gz
reads plus pre-built reference files; output lands in results/ with
per-step subdirectories and a combined MultiQC HTML report.
The default plan is the upstream single-end aligner=bwa main path
(15 rules, 29 rule instances for the fixture cohort). Upstream branches
that are off the main path — paired-end, alternative aligners
(Bowtie2/Chromap/STAR), reference preparation, mitochondrial filtering,
consensus peaks / DESeq2, preseq, Picard metrics, ataqv, IGV, R QC
plots — are ported as when-gated branches (see
Gated branches); each activates only when its config
key is set, so the default plan is unchanged.
Requires oxo-flow >= 0.12.0. Recommended — prebuilt release binary:
curl -fL -o oxo-flow.tar.gz \
https://github.com/Traitome/oxo-flow/releases/latest/download/oxo-flow-latest-x86_64-unknown-linux-gnu.tar.gz
tar xzf oxo-flow.tar.gz
sudo mv oxo-flow /usr/local/bin/Alternative via conda:
conda install -c bioconda oxo-flow-cliNote the bioconda package may lag the latest release; other platform binaries are on the releases page.
git clone https://github.com/oxo-flow-community/oxo-flow-atacseq.git
cd oxo-flow-atacseqReference data (pre-built files, declared under [config] in main.oxoflow):
| config key | file |
|---|---|
reference |
genome FASTA (with .fai beside it) |
bwa_index |
BWA index prefix (<prefix>.amb/.ann/.bwt/.pac/.sa) |
chrom_sizes |
chrom sizes file (e.g. <genome>.sizes) |
gtf / gene_bed / tss_bed |
annotation for HOMER / deepTools |
blacklist |
optional include-regions BED (upstream ENCODE blacklist + chrM complement) — empty disables -L filtering |
bowtie2_index / chromap_index / star_index |
index files for the alternative aligners (only when aligner is set to them) |
The port does not auto-do what upstream
prepare_genomedoes on the default config: reference/annotation files must be pre-built uncompressed GTF/BED/FASTA and unpacked index files. The convenience branches are available when you need them — setgtfto a.gzsource? Leave it canonical and pointgtf_srcat the archive. The khmer genome-size estimate is on independently: setmacs_gsize = ""to auto-estimate from the FASTA atread_length(default2.7e9= GRCh37/38 @ 50 bp — see Fidelity).
To do upstream prepare_genome:set these config keys happens decompress a .gzGTFgtf_src(canonicalgtfstays fixed)ref::gunzip_gtf(upstream GUNZIP_GTF)GFF3 → GTF gffwithgtfleft emptyref::gffread(upstream GFFREAD); GTF goes toresults/genome/index/ref.gtfdecompress a .gzgene/TSS BEDgene_bed_src/tss_bed_srcref::gunzip_gene_bed/ref::gunzip_tss_bedunpack a BWA index tarball bwa_index_src(a.tar.gz)ref::untar_bwa_index; the extracted index is found at runtime (find -name "*.amb"), exactly like upstream BWA_MEMThe
when-gated convenience rules live inmodules/reference.oxoflow; the consumer rules (HOMER, deepTools, BWA-MEM) pick them up viadepends_on(a closed gate counts as satisfied, so the default pre-built path is unchanged) and resolve the file at shell level, mirroring the khmer genome-size wiring.
Input data: single-end raw/<sample>.fastq.gz reads, named in
[[sample_groups]] (see test/fixtures/ for a tiny working set); the
paired-end branch (config.paired=true) reads
raw/<sample>_1.fastq.gz + raw/<sample>_2.fastq.gz.
Compute: up to 12 CPUs / 72 GB per rule (trimming, alignment and deepTools rules are the heaviest); a few rules need as little as 1 CPU / 6 GB.
Tools: a mixed delivery — 46 of 49 rules run in pinned Docker images
(biocontainers/* tags, e.g. biocontainers/macs2:2.2.7.1--py38h4a8c8d9_3),
executed by oxo-flow via Docker or Singularity; the remaining three rules
(picard_mergesamfiles, picard_markduplicates, merge_replicates) share
one pinned conda env at envs/picard-samtools.yaml (picard 3.0.0,
samtools 1.17), which requires conda/mamba at runtime.
# 1. install oxo-flow (see Installation)
# 2. prepare data: <raw_dir>/<sample>.fastq.gz (single-end), see fixtures/
# 3. preview the plan
oxo-flow dry-run main.oxoflow
# 4. run
oxo-flow run main.oxoflow -j 8
# 5. run a subset
oxo-flow run main.oxoflow -t multiqc --samples first:1Sample names are declared in [[sample_groups]] (S1, S2 in the
fixture set) — add your own names there or point raw_dir at your data.
Pipeline behaviour is tuned through [config]: macs_gsize (default
2.7e9; "" = khmer auto-estimate), narrow_peak, broad_cutoff,
fragment_size,
min_trimmed_reads, out_dir, and the skip_* toggles (skip_fastqc,
skip_qc, skip_trimming, skip_plot_profile, skip_plot_fingerprint,
skip_multiqc, skip_peak_annotation). All can be overridden on the CLI.
Each non-default branch below is [[include]]d from modules/ and gated
by a when condition on a [config] key (defaults in parentheses).
Nothing from a gated branch runs unless its key is set, and a
dry-run/run shows exactly which branch a toggle activates.
| config key(s) | branch (rules) | upstream equivalent |
|---|---|---|
paired = true |
paired-end: pe::fastqc_pe, pe::trimgalore_pe, pe::bwa_mem_pe, pe::bamtools_filter_pe, pe::pe_name_sort_remove_orphans, pe::bedtools_genomecov_pe, pe::plotfingerprint_pe, pe::multiqc_pe (8) |
PE input path + BAM_SORT_STATS_ORPHANS (name sort + orphan removal) |
aligner = "bowtie2" / "chromap" / "star" |
alt::bowtie2_align / alt::chromap_align / alt::star_align (3, SE only) |
BOWTIE2_ALIGN / CHROMAP_CHROMAP / STAR_ALIGN |
prepare_reference = true |
ref::bwa_index, ref::custom_getchromsizes (2) |
BWA_INDEX, CUSTOM_GETCHROMSIZES (prepare_genome) |
macs_gsize = "" |
ref::khmer_uniquekmers (1) |
KHMER_UNIQUEKMERS (prepare_genome — genome-size auto-estimate at read_length) |
mito_name = "chrM" or raw_blacklist = "<bed>" |
mito::genome_blacklist_regions (1) |
GENOME_BLACKLIST_REGIONS (mitochondrial filtering) |
skip_preseq = false |
qce::preseq_lcextrap (1) |
PRESEQ_LCEXTRAP |
skip_picard_metrics = false |
qce::picard_collectmultiplemetrics (1) |
PICARD_COLLECTMULTIPLEMETRICS |
skip_ataqv = false |
qce::get_autosomes, qce::ataqv, qce::mkarv (3) |
GET_AUTOSOMES, ATAQV_ATAQV, ATAQV_MKARV |
skip_peak_qc = false |
qce::plot_macs2_qc, qce::plot_homer_annotatepeaks (2) |
PLOT_MACS2_QC, PLOT_HOMER_ANNOTATEPEAKS |
skip_igv = false |
qce::igv (1) |
IGV |
multiqc_custom_peaks = true |
qce::multiqc_custom_peaks (1) |
MULTIQC_CUSTOM_PEAKS |
skip_consensus_peaks = false |
cons::macs2_consensus, cons::homer_annotatepeaks_consensus, cons::subread_featurecounts, cons::deseq2_qc (4, needs ≥ 2 samples) |
MACS2_CONSENSUS_PEAKS, HOMER_ANNOTATEPEAKS, SUBREAD_FEATURECOUNTS, DESEQ2_QC |
skip_peak_annotation = true |
turns the default HOMER annotation rules off | params.skip_peak_annotation |
Where an upstream branch runs by default (--save_reference, ataqv,
Picard metrics, IGV, consensus/DESeq2, R QC plots), this port ships it
off so the default plan stays the minimal main path; enabling it is
one config flag. See Known divergences.
Upstream: nf-core/atacseq @
2.1.2 (commit 1a1dbe52ffbd82256c941a032b0e22abbd925b8a), MIT license.
Created 2026-08-15; this workflow may lag behind upstream releases.
Upstream attribution in NOTICE.md.
Ported with upstream defaults: aligner=bwa, single-end, narrow_peak=false
(broad peaks), no control. One row per upstream process; steps not ported are
listed with reasons. when-gated rules carry the gate in the Notes column.
| Upstream process | oxo-flow rule | Tool (version) | Notes |
|---|---|---|---|
| FASTQC | fastqc / pe::fastqc_pe |
fastqc 0.11.9 | identical command (--quiet --threads); PE variant in the paired branch |
| TRIMGALORE | trimgalore / pe::trimgalore_pe |
trim-galore 0.6.7 | identical command (--fastqc --cores 8 --gzip); PE variant uses --paired + _val_1/2 outputs |
| FASTQ_FASTQC_UMITOOLS_TRIMGALORE (UMITOOLS_EXTRACT) | — | — | not ported — dead code at 2.1.2: the workflow hardcodes with_umi=false, the branch can never fire |
| BWA_MEM | bwa_mem / pe::bwa_mem_pe |
bwa 0.7.17, samtools 1.17 | identical (-M -R '@RG...', secondary-alignment filter -F 0x0100), mulled container; PE variant merges read groups |
| BOWTIE2_ALIGN | alt::bowtie2_align |
bowtie2 2.5.1, samtools 1.17 | identical (end-to-end, --very-sensitive, -k 4, SAM→BAM filter); when aligner = "bowtie2" |
| CHROMAP_CHROMAP | alt::chromap_align |
chromap 0.2.5, samtools 1.17 | identical (-l 2000 --Tn5-shift --low-mem); when aligner = "chromap" |
| STAR_ALIGN | alt::star_align |
star 2.6.1d | identical (EndToEnd, --alignIntronMax 1, unsorted BAM); when aligner = "star" |
| BAM_SORT_STATS_SAMTOOLS (SAMTOOLS_SORT + SAMTOOLS_INDEX + SAMTOOLS_STATS + SAMTOOLS_FLAGSTAT + SAMTOOLS_IDXSTATS) | samtools_sort_stats / pe::pe_name_sort_remove_orphans |
samtools 1.17 | identical commands, folded into one rule (same env); PE adds name sort → bampe_rm_orphan.py --only_fr_pairs → coordinate sort (upstream BAM_SORT_STATS_ORPHANS) |
| PICARD_MERGESAMFILES_LIBRARY | picard_mergesamfiles / pe::bwa_mem_pe |
picard 3.0.0 | upstream symlink branch for single-library samples replicated; multi-library merge (actual MergeSamFiles) not expressible — no library source in oxo-flow |
| BAM_MARKDUPLICATES_PICARD (PICARD_MARKDUPLICATES + SAMTOOLS_INDEX + SAMTOOLS_STATS + SAMTOOLS_FLAGSTAT + SAMTOOLS_IDXSTATS) | picard_markduplicates |
picard 3.0.0, samtools 1.17 | identical commands (--ASSUME_SORTED --REMOVE_DUPLICATES false, XMX heap sizing); combined conda env |
| BAMTOOLS_FILTER + SAMTOOLS_INDEX + BAM_STATS_SAMTOOLS (SAMTOOLS_STATS + SAMTOOLS_FLAGSTAT + SAMTOOLS_IDXSTATS) | bamtools_filter / pe::bamtools_filter_pe |
bamtools 2.5.2, samtools 1.17 | identical (-F 0x004 -F 0x0400 -q 1, optional -L blacklist, assets/bamtools_filter_{se,pe}.json); PE adds -f 0x001 -F 0x0008 |
| MACS2_CALLPEAK | macs2_callpeak |
macs2 2.2.7.1 | identical (--keep-dup all --nomodel --broad --broad-cutoff 0.1, gsize from config, --format BAM) |
| HOMER_ANNOTATEPEAKS | homer_annotatepeaks / cons::homer_annotatepeaks_consensus |
homer 4.11 | identical (-gid -gtf); consensus variant annotates the consensus BED, when skip_consensus_peaks = false |
| FRIP_SCORE | frip_score |
bedtools 2.30.0, samtools 1.17 | identical (intersectBed -f 0.20, flagstat mapped fraction) |
| BEDTOOLS_GENOMECOV | bedtools_genomecov / pe::bedtools_genomecov_pe |
bedtools 2.30.0 | identical (-bg -scale 1e6/reads -fs fragment_size, sort); PE uses -pc instead of -fs (upstream) |
| UCSC_BEDGRAPHTOBIGWIG | ucsc_bedgraphtobigwig |
ucsc-bedgraphtobigwig 445 | identical |
| BIGWIG_PLOT_DEEPTOOLS (COMPUTEMATRIX scale-regions + reference-point, PLOTPROFILE, PLOTHEATMAP) | deeptools_plots |
deeptools 3.5.1 | identical args (regionBodyLength 1000, ±3000, --missingDataAsZero --skipZeros --smartLabels) |
| MERGED_LIBRARY_DEEPTOOLS_PLOTFINGERPRINT | plotfingerprint / pe::plotfingerprint_pe |
deeptools 3.5.1 | identical (--extendReads fragment_size); PE omits --extendReads (upstream) |
| MULTIQC | multiqc / pe::multiqc_pe |
multiqc 1.13 | upstream mechanism replicated: multiqc_config.yml staged in cwd, multiqc -f .; config path_filters adapted to this port's results/ layout; PE adds the PE fastqc/trimgalore patterns |
| BWA_INDEX | ref::bwa_index |
bwa 0.7.17 | identical (bwa index -p); when prepare_reference = true (default false — port takes pre-built indexes) |
| CUSTOM_GETCHROMSIZES / CUSTOM_GENOME_FASTA_INDEX (prepare_genome) | ref::custom_getchromsizes |
samtools 1.17 (envs/picard-samtools.yaml pin) | samtools faidx + cut -f 1,2 into config.chrom_sizes; when prepare_reference = true |
| GENOME_BLACKLIST_REGIONS (mitochondrial filtering) | mito::genome_blacklist_regions |
bedtools 2.30.0 | sortBed/complementBed over config.raw_blacklist, then optional chrM drop (config.mito_name, keep_mito); when mito_name/raw_blacklist set; consumed via -L by bamtools_filter |
| PRESEQ_LCEXTRAP | qce::preseq_lcextrap |
preseq 3.1.2 | identical (-verbose -bam -seed 1; -pe when paired); when skip_preseq = false |
| PICARD_COLLECTMULTIPLEMETRICS | qce::picard_collectmultiplemetrics |
picard 3.0.0 | identical (5 metrics + PDFs into picard_metrics/{,pdf}/); when skip_picard_metrics = false |
| GET_AUTOSOMES + ATAQV_ATAQV + ATAQV_MKARV | qce::get_autosomes, qce::ataqv, qce::mkarv |
python 3.8.3, ataqv 1.3.1 | identical commands (--ignore-read-groups, mitochondrial-reference-name when mito_name; mkarv HTML index); when skip_ataqv = false |
| PLOT_MACS2_QC / PLOT_HOMER_ANNOTATEPEAKS | qce::plot_macs2_qc / qce::plot_homer_annotatepeaks |
mulled R image | scripts copied verbatim from upstream bin/; when skip_peak_qc = false |
| MULTIQC_CUSTOM_PEAKS | qce::multiqc_custom_peaks |
multiqc headers | identical count/FRiP TSVs (assets/multiqc/ headers copied verbatim); when multiqc_custom_peaks = true (port-only switch; upstream always emits) |
| IGV | qce::igv |
python 3.8.3 | igv_files_to_session.py copied verbatim, --path_prefix '../../'; when skip_igv = false; per-library tracks only (no merged-replicate sets, see below) |
| MACS2_CONSENSUS_PEAKS | cons::macs2_consensus |
mulled (macs2 + bedtools + R) | sort + mergeBed -c 2,3,4,5,6,7,8,9 -o collapse... → macs2_merged_expand.py --min_replicates → BED/SAF/UpSet plot (bin scripts verbatim); when skip_consensus_peaks = false, needs ≥ 2 samples |
| SUBREAD_FEATURECOUNTS | cons::subread_featurecounts |
subread 2.0.1 | identical (-F SAF -O --fracOverlap 0.2 -s 0, -p when paired); when skip_consensus_peaks = false |
| DESEQ2_QC | cons::deseq2_qc |
mulled (R + DESeq2) | deseq2_qc.r verbatim (--id_col 1 --count_col 7, --vst TRUE when deseq2_vst); when skip_consensus_peaks = false and skip_deseq2_qc = false |
PICARD_MERGESAMFILES / BAM_MARKDUPLICATES_PICARD / BAM_BEDGRAPH_BIGWIG_BEDTOOLS_UCSC / BAM_PEAKS_CALL_QC_ANNOTATE_MACS2_HOMER / BED_CONSENSUS_QUANTIFY_QC_BEDTOOLS_FEATURECOUNTS_DESEQ2 (aliased MERGED_REPLICATE_*) |
merge_replicates + the same downstream rules |
picard 3.0.0, samtools 1.17, macs2 2.2.7.1, homer 4.11, bedtools 2.30.0, deepTools 3.5.1 | ported via oxo-flow input_groups: merge_replicates folds per-replicate BAMs by base id (_REP\d+$ suffix) and writes the merged BAM to the canonical {sample}.mLb.clN.sorted.bam path; the regular chain then runs on merged AND per-replicate inputs (same commands as upstream). when skip_merge_replicates = false (default); see "Merged-replicate analysis" below for semantics and deviations |
| INPUT_CHECK (samplesheet_check) | — | — | not ported — pipeline plumbing; oxo-flow provides native [[sample_groups]] declaration + validate |
| DUMP_SOFTWARE_VERSIONS | — | — | not ported — pipeline plumbing; oxo-flow has native version/audit mechanisms |
| PREPARE_GENOME: GFFREAD, GUNZIP, UNTAR | ref::gffread / ref::gunzip_gtf / ref::gunzip_gene_bed / ref::gunzip_tss_bed / ref::untar_bwa_index |
gffread 0.12.1 / gzip (ubuntu:20.04) / tar | ported (when-gated): GFF3→GTF (upstream GFFREAD, --keep-exon-attrs -F -T, GTF written to results/genome/index/ref.gtf), .gz decompression (upstream GUNZIP semantics: gzip -cd — not gunzip, which preserves original group ownership), index tarball unpacking with upstream's single-top-dir --strip-components detection. Activated by gtf_src / gff / gene_bed_src / tss_bed_src / bwa_index_src; consumers resolve the produced files at shell level (depends_on, khmer-style) |
| PREPARE_GENOME: KHMER_UNIQUEKMERS | ref::khmer_uniquekmers |
khmer 3.0.0a3 | ported — genome-size auto-estimate at params.read_length, wired into MACS2_CALLPEAK exactly as upstream (if (!params.macs_gsize) → khmer → --gsize); activate with macs_gsize = "" (upstream semantics: empty value = estimate); when gate config.macs_gsize == '' |
- Branches that upstream runs by default ship OFF here: ataqv,
Picard metrics, IGV, consensus peaks/DESeq2 and the R QC plots all run on
the upstream default path; this port gates them behind
skip_ataqv/skip_picard_metrics/skip_igv/skip_consensus_peaks/skip_peak_qc(defaulttrue= off) so the default plan stays the minimal main path.skip_preseq = truematches the upstream default (preseq is off there too). Enabling a branch is a single config flag. - Reference inputs: upstream builds/derives the reference from
--genome(iGenomes) at runtime; this port consumes pre-built files (reference,bwa_indexprefix,gtf,gene_bed,tss_bed,chrom_sizes, optionalblacklist).prepare_reference = trueinstead generates the BWA index and chrom sizes from the FASTA (the fixture already ships them, so the rules report up-to-date there). - ataqv and Picard metrics are SE-only: both rules consume the
single-end filtered
mLb.clNBAM; the paired-end variants (over themLb.flTorphan-removed BAM) are not ported — withpaired=truethe rules are skipped even when theirskip_*gates are off. - Alternative aligners are single-end only (
whenadds!config.paired): the paired branch is bwa-only, matching upstream's paired alignment options. Misconfigurations (e.g.paired=truewithaligner="star") surface as validation warnings via missing inputs. - PE requires
bwa_indexand producesbwa/library/outputs: the paired branch'sbwa_mem_peemits the full read group (-R '@RG\tID:{sample}\tSM:$SM\tPL:ILLUMINA\tLB:{sample}\tPU:1', whereSMstrips a_T[0-9]*suffix from the sample) rather than upstream's minimal'@RG\tID:{sample}\tSM:{sample}'; the default SE path keeps the original@RGhandling. - Broad peaks are hardcoded: the port's
macs2_callpeakand all consumers use--broad;narrow_peak = trueis honoured bymacs2_callpeakbut the downstream rules in this port read*_peaks.broadPeakonly. - MultiQC config:
path_filters/module_orderwere trimmed to the ported steps; preseq/featureCounts/ataqv/DESeq2 sections appear when their branches are enabled (the PE MultiQC adds the PE fastqc/trimgalore logs). Report comment points at the upstream pipeline. macs_gsize: upstream keys it off the read-length genome block and auto-estimates via khmer when the value is empty; the port exposes it asconfig.macs_gsize(default2.7e9, the upstream GRCh37/38 @ 50 bp value). Settingmacs_gsize = ""ports the upstream empty-value semantics:ref::khmer_uniquekmersestimates the size from the FASTA atconfig.read_length(upstream KHMER_UNIQUEKMERS) and macs2_callpeak reads it into--gsize. Divergence: upstream additionally consults the read-length keyed genome table (e.g. a non-emptymacs_gsizethere overrides khmer); the port uses the explicitconfig.macs_gsizevalue or the khmer estimate, nothing else.- IGV session lists one merged
mLb_*track set: upstream separates per-replicate and merged-replicate bigWigs/peaks into two IGV track sets (mLb_*vsmLb_clN_*); this port'sigvrule scansmerged_library/{bigwig,macs2/broad_peak}once, so merged and per-replicate files land in the same set (the merged files are included whenconfig.merged_samplesnames them). - featureCounts is unstranded:
-s 0is passed explicitly (the upstream default); a[SCI-FEATURECOUNTS-STRAND]preflight hint may appear for the gated rule — informational. size_factors/stays in the workdir: upstream DESeq2_QC publishes thesize_factorsdirectory; the port leaves it in the rule workdir (not declared as an output) — documented, not lost.- Legacy image pin:
qce::multiqc_custom_peaks(modules/qc_extra.oxoflow) still pins the retiredquay.io/nf-core/ubuntu:20.04image (upstream's shell-only rule used a plain ubuntu container). It is kept as-is for byte-identical default behavior; the rule is gated off by default (multiqc_custom_peaks = false). - Dry-run prints benign
input ✗lines on the default plan:{sample}.mLb.clN.sorted.bamis declared as an output of bothbamtools_filter(per-sample chain, runs) andmerge_replicates(merged-replicate chain, off with the default emptymerged_samples), so a dry-run with no files on disk reports that path unresolved for 20 instances (per-sample consumers such asmacs2_callpeak,bedtools_genomecov,frip_score,plotfingerprint, their transitive bigWig/peak consumers, and gated-offpe::/qce::copies).oxo-flow dry-runstill exits 0; a real run resolves the files. - Consensus branch needs ≥ 2 samples: upstream filters the peak
channel to
size() > 1; with one sample the consensus rules would produce degenerate output.
Upstream's MERGED_REPLICATE_* branch (default ON) folds per-replicate
libraries into one merged BAM per sample and drives the whole downstream
chain (markdup-free: the merged BAM is NOT re-marked; bigWig, MACS2/HOMER,
consensus/DESeq2) from it. This port implements the same analysis with
oxo-flow's input_groups primitive — a declared pattern plus group_by
that folds matching files the same way groupTuple() does.
How to use it — declare replicate samples with the upstream _REP\d+
suffix convention (S1_REP1, S1_REP2, …) in your samplesheet, then pass
the base names of the replicate groups through merged_samples so the
MultiQC/IGV aggregation rules wait for the merged files:
# samples_replicates.csv: name,samples rows — one group per sample is fine
# cohort,S1_REP1
# cohort,S1_REP2
# cohort,S2_REP1
# cohort,S2_REP2
oxo-flow run main.oxoflow -j 4 \
--samples @test/fixtures/samples_replicates.csv \
--arg merged_samples=S1,S2The merge is gated by skip_merge_replicates (default false = ON,
matching upstream) AND a non-empty merged_samples — both are needed for
the merged chain to run, so a plain samplesheet with the default config
never instantiates it. With no _REP\d+ samples the merge_replicates
rule instantiates nothing and the default plan is untouched.
Engine requirements: the merged-replicate mode uses the
input_groups primitive (Traitome/oxo-flow#231, unreleased at time of
writing). On released engines the gate is inert — the default path runs
unchanged (the downstream rules fall back to their declared per-sample
inputs), and a replicate run fails fast with a clear message instead of
misbehaving.
What happens: merge_replicates folds the per-replicate filtered BAMs
({sample}_REP{rep}.mLb.clN.sorted.bam) by base id and writes the merged
BAM + index + samtools stats to the canonical
results/bwa/merged_library/{sample}.mLb.clN.sorted.bam path — the same
location the per-sample chain writes — so the regular chain's rules
(macs2_callpeak, homer_annotatepeaks, frip_score,
bedtools_genomecov, ucsc_bedgraphtobigwig, deeptools_plots,
plotfingerprint, multiqc_custom_peaks, and the PE equivalents) run on
the merged BAM with their identical commands, exactly like upstream's
MERGED_REPLICATE_CALL_ANNOTATE_PEAKS / ..._BAM_BEDGRAPH_BIGWIG /
..._PLOT_DEEPTOOLS chain. multiqc/igv aggregate both the
per-replicate and the merged files (upstream does the same; see the
MultiQC/IGV divergences).
Deviations from upstream (all documented, none change the default non-merge path):
- No re-markdup on the merged BAM: upstream's
MERGED_REPLICATE_MARKDUPLICATES_PICARDre-runs MarkDuplicates with--REMOVE_DUPLICATES trueon the merged BAM. Here the merged BAM keeps the per-replicate markdup state (mLb.clN,--REMOVE_DUPLICATES false), matching the port's single-library handling. - Single-replicate groups are merged too: upstream keeps only groups
with ≥ 2 replicates (
groupTuple()size() > 1);input_groupsinstantiates every group it finds, so a loneS1_REP1still produces a mergedS1BAM (MergeSamFiles handles a single input fine). Declare_REP\d+names only when you intend the merge. - Merged naming uses the library suffix (
mLb), notmRp: upstream names merged-replicate files{id}.mRp.clN.sorted.bam; here the merged BAM reuses the canonical{sample}.mLb.clN.sorted.bamname, which is what lets the regular chain consume it unchanged. - One consensus analysis: upstream runs the consensus/DESeq2 chain
twice (per-replicate and merged-replicate peak sets). Here the
glob-based
cons::*rules aggregate per-replicate AND merged peaks in a single run — two sample sets in one consensus matrix instead of two separate analyses. - Do not mix a plain sample with its replicate base: declaring both
S1andS1_REP1makesbamtools_filterandmerge_replicateswrite the sameS1.mLb.clN.sorted.bam— a declared-sample conflict (the duplicate would be caught at validation). Use_REPnames OR the plain name for one sample, never both.
bash test/run.shRuns oxo-flow validate + lint + dry-run (sample selection comes
from the workflow's [[sample_groups]] declaration) against the fixture
data; a debug pass additionally asserts that no literal {wildcards} leak
into expanded commands. See test/run.sh for details.
Apache-2.0. Copyright (c) 2026 oxo-flow-community. Upstream
(nf-core/atacseq) is MIT — see NOTICE.md and LICENSE.upstream.