Skip to content

Germline Variant Calling (Simple Chain)#

The per-sample GATK germline chain: FastQC + fastp QC, BWA-MEM2 alignment with read groups, MarkDuplicates, BQSR, and HaplotypeCaller GVCF — the simpler sibling of gallery 07, which adds joint calling, VQSR, and annotation.

What It Demonstrates#

  • Read-group string -R '@RG\tID:{sample}\tSM:{sample}\tPL:ILLUMINA' renders real TAB separators
  • Each stage declares its own environment (conda for QC tools, singularity for GATK)
  • Mix of environment backends per rule is first-class

Workflow Definition#

# examples/gallery/13_simple_variant_calling.oxoflow
# 13 — Germline Variant Calling (Simple Chain)
# Per-sample germline best practices: FastQC/fastp QC → BWA-MEM2 →
# MarkDuplicates → BQSR → HaplotypeCaller GVCF. The simpler sibling of
# gallery 07 (which adds joint calling, VQSR, and annotation).

[workflow]
name = "simple-variant-calling"
version = "1.0.0"
description = "A simple variant calling pipeline demonstrating oxo-flow features"
author = "Traitome"

[config]
reference = "/data/reference/GRCh38.fa"
known_sites = "/data/reference/dbsnp_146.hg38.vcf.gz"

[defaults]
threads = 4
memory = "8G"

[report]
# template: reserved — supply a real Tera template file path to use one
# template = "variant_report"
format = [
    "html",
    "json",
]
sections = [
    "summary",
    "variants",
    "coverage",
    "qc_metrics",
]

[[rules]]
name = "fastqc"
input = [
    "raw/{sample}_R1.fastq.gz",
    "raw/{sample}_R2.fastq.gz",
]
output = [
    "qc/{sample}_R1_fastqc.html",
    "qc/{sample}_R2_fastqc.html",
]
shell = "fastqc {input} -o qc/"
description = "Quality control check on raw sequencing reads"

[rules.resources]
threads = 2

[rules.environment]
conda = "envs/qc.yaml"

[[rules]]
name = "fastp"
input = [
    "raw/{sample}_R1.fastq.gz",
    "raw/{sample}_R2.fastq.gz",
]
output = [
    "trimmed/{sample}_R1.fastq.gz",
    "trimmed/{sample}_R2.fastq.gz",
    "qc/{sample}_fastp.json",
]
shell = """
fastp -i {input[0]} -I {input[1]} -o {output[0]} -O {output[1]} --json {output[2]} --thread {threads}
"""
description = "Adapter trimming and quality filtering"

[rules.resources]
threads = 8

[rules.environment]
conda = "envs/fastp.yaml"

[[rules]]
name = "bwa_align"
input = [
    "trimmed/{sample}_R1.fastq.gz",
    "trimmed/{sample}_R2.fastq.gz",
]
output = ["aligned/{sample}.sorted.bam"]
shell = '''
rg=$(printf '@RG\tID:%s\tSM:%s\tPL:ILLUMINA' '{sample}' '{sample}')
bwa-mem2 mem -t {threads} -R "$rg" {config.reference} {input[0]} {input[1]} | samtools sort -@ 4 -o {output[0]}
samtools index {output[0]}
'''
description = "Read alignment to reference genome"

[rules.resources]
threads = 16
memory = "32G"

# The pipe needs BOTH bwa-mem2 and samtools; the single-tool docker image
# cannot run it — use a conda environment that ships the pair.
[rules.environment]
conda = "envs/alignment.yaml"

[[rules]]
name = "mark_duplicates"
input = ["aligned/{sample}.sorted.bam"]
output = [
    "dedup/{sample}.dedup.bam",
    "dedup/{sample}.metrics.txt",
]
shell = """
gatk MarkDuplicates --VALIDATION_STRINGENCY SILENT -I {input[0]} -O {output[0]} -M {output[1]} --CREATE_INDEX true
"""
description = "Mark PCR duplicate reads"

[rules.resources]
threads = 4
memory = "16G"

[rules.environment]
singularity = "docker://broadinstitute/gatk:4.5.0.0"

[[rules]]
name = "base_recalibrator"
input = ["dedup/{sample}.dedup.bam"]
output = ["recal/{sample}.recal_data.table"]
shell = """
gatk BaseRecalibrator --read-validation-stringency SILENT -I {input[0]} -R {config.reference} --known-sites {config.known_sites} -O {output[0]}
"""
description = "Base quality score recalibration table"

[rules.resources]
threads = 4
memory = "16G"

[rules.environment]
singularity = "docker://broadinstitute/gatk:4.5.0.0"

[[rules]]
name = "apply_bqsr"
input = [
    "dedup/{sample}.dedup.bam",
    "recal/{sample}.recal_data.table",
]
output = ["recal/{sample}.recal.bam"]
shell = """
gatk ApplyBQSR --read-validation-stringency SILENT -I {input[0]} -R {config.reference} --bqsr-recal-file {input[1]} -O {output[0]}
"""
description = "Apply base quality score recalibration"

[rules.resources]
threads = 4
memory = "16G"

[rules.environment]
singularity = "docker://broadinstitute/gatk:4.5.0.0"

[[rules]]
name = "haplotype_caller"
input = ["recal/{sample}.recal.bam"]
output = ["variants/{sample}.g.vcf.gz"]
shell = """
gatk HaplotypeCaller --read-validation-stringency SILENT -R {config.reference} -I {input[0]} -O {output[0]} -ERC GVCF --native-pair-hmm-threads {threads}
"""
description = "Germline variant calling"

[rules.resources]
threads = 4
memory = "16G"

[rules.environment]
singularity = "docker://broadinstitute/gatk:4.5.0.0"

[[sample_groups]]
name = "samples"
samples = [
    "NA12878",
    "NA12891",
    "NA12892",
]

[sample_groups.metadata]

Try It#

# Inspect the expanded plan first — no data needed:
oxo-flow dry-run examples/gallery/13_simple_variant_calling.oxoflow

# Copy the environment specs next to the workflow, adapt [config]
# paths to your data, then run:
oxo-flow run examples/gallery/13_simple_variant_calling.oxoflow

Input data and environments

Input paths under /data/references/... and raw/ are placeholders — replace them with your own data. The referenced envs/*.yaml specs ship in examples/envs/.