Nextflow pipeline for joint variant calling on Plasmodium Whole Genome Sequencing (WGS) datasets.
- Overview
- Software Environment & Installation
- Quick Start: Test the Pipeline
- Running on Your Own Data
- Pipeline Options
- Input & Output Files
- Debugging
- Workflow Chart
- Citations
The pipeline follows GATK best practices and the MalariaGEN Pf6 data-generating methods.
Pipeline steps:
- Host read removal — Raw reads are mapped to the human GRCh38 reference genome; aligning reads are discarded.
- Parasite alignment — Remaining reads are mapped to the P. falciparum 3D7 reference (PlasmoDB_44).
- BAM processing —
MarkDuplicatesandBaseRecalibrator(GATK) produce analysis-ready BAMs. - Per-sample calling —
HaplotypeCallerin GVCF mode generates per-isolate variant calls. - Joint calling — Per-sample GVCFs are combined via
GenotypeGVCFsinto a multi-sample VCF. - Variant filtration — Hard-filtering or VQSR (
VariantRecalibrator) retains high-quality variants.
The main branch targets Plasmodium falciparum. The pipeline is expected to work with other Plasmodium species when the appropriate configuration and reference files are provided (see nextflow.config).
A vivax branch for Plasmodium vivax is under development: vivax branch.
Supported platforms: macOS (Intel) and Linux.
Estimated install time: 5–20 minutes.
The Nextflow engine and the pipeline may require different Java versions. Two separate Conda environments are used to avoid conflicts:
env/nf.yaml— Nextflow engineenv/snp_call_nf.yaml— Pipeline tools
cd YOUR_WORKING_DIR # replace with your actual path
git clone https://github.com/bguo068/snp_call_nf.git
cd snp_call_nfInstall Miniconda.
# Install Nextflow environment
conda env create -f env/nf.yaml
# Install pipeline environment
conda env create -f env/snp_call_nf.yamlNote: If you already have Nextflow installed, you can skip the
nfenvironment and pin the version directly:nextflow self-update export NXF_VER=26.04.6 nextflow main.nf [args]
Apptainer/Singularity users: See
env/README.mdfor container-based setup instructions.
A tiny test dataset is included in the test_data/ folder. Use it to verify your installation works.
conda activate nf
nextflow main.nfTo test with VQSR enabled:
conda activate nf
nextflow main.nf --vqsr true -profile standard,testconda activate nf
nextflow main.nf -profile sgeconda activate nf
nextflow main.nf -profile slurmTo test with real (but public) ENA data:
cd ena_data
bash download_ena_data.sh # may take hours to download
cd ..
conda activate nf
nextflow main.nf --fq_map ena_data/ena_fastq_map.tsvInternal users (IGS / Rosalind): Symlink existing reference files.
| Server | Command |
|---|---|
| IGS | ln -s /local/projects-t3/toconnor_grp/bing.guo/ref/* ref/ |
| IGS (alt) | ln -s /local/projects-t2/CVD/Takala-Harrison/Cambodia_Bing/ref/* ref/ |
| Rosalind | ln -s /local/data/Malaria/Projects/Takala-Harrison/Cambodia_Bing/ref/* ref/ |
External users: Generate reference files from scratch.
cd ref
conda activate snp_call_nf
python3 prep_ref_files.py
conda deactivate
cd ..Edit fastq_map.tsv (or create a new one) with your sample information.
See fastq_map.tsv for an example.
The file is tab-delimited with five columns:
| Column | Type | Description |
|---|---|---|
| Sample | string | Unique sample identifier |
| HostId | integer | Host genome index (0-based); see params.host in nextflow.config |
| Run | string | Run / replicate identifier (e.g. r1) |
| MateId | integer | 0 for single-end; 1 or 2 for paired-end |
| Fastq | string | Path to the FASTQ file |
conda activate nf
nextflow main.nf --fq_map your_fastq_map.tsv -profile slurmReplace -profile slurm with -profile sge or omit it for local execution.
Before running on a cluster, edit
nextflow.configto match your lab's settings:
- SGE: eg., update
clusterOptions = "-P toconnor-lab -cwd -V"- Slurm: eg., update
clusterOptions = "--account=cvd"
| Flag | Default | Description |
|---|---|---|
--vqsr true |
off | Enable VQSR-based variant filtration |
If VQSR fails due to convergence issues, adjust --vqsr_opts (see nextflow.config).
Control how the genome is divided for parallel joint calling via --split. The available intervals are defined in nextflow.config under params.genome_intervals.
| Value | Behavior |
|---|---|
chromosomes |
(default) One interval per chromosome (14 intervals for P. falciparum) |
intervals |
Sub-chromosomal intervals — splits larger chromosomes for finer-grained parallelism (44 intervals) |
Example:
nextflow main.nf --split intervalsUse these to stop the pipeline at a specific stage:
| Flag | Stops after… |
|---|---|
--parasite_reads_only |
Host read removal — unmapped reads saved to FASTQ |
--coverage_only |
Coverage computation — before GATK_APPLY_BQSR |
--gvcf_only |
Per-sample GVCF generation — before joint calling |
| Flag | Behavior |
|---|---|
| (default) | Reads are aligned to human genome first; aligning reads are removed before parasite mapping. |
--use_concat_genome |
Reads are aligned to a concatenated human+parasite genome. Reads mapping to parasite chromosomes are retained and realigned; human-mapping reads are discarded. |
| File | Purpose |
|---|---|
fastq_map.tsv |
Sample sheet (see Running on Your Own Data) |
nextflow.config |
Pipeline configuration (profiles, params, resources) |
main.nf |
Main Nextflow workflow script |
| Path | Description |
|---|---|
results/readlen_raw/ |
Raw read lengths per sample/run |
results/flagstat_raw/ |
Flagstat for host-genome alignments |
results/flagstat_parasite/ |
Flagstat for parasite-genome alignments |
results/recalibrated/ |
Analysis-ready BAM files (post host-removal & recalibration) |
results/recal_bam_coverage/ |
Read coverage from analysis-ready BAMs |
results/recal_bam_flagstat/ |
Flagstat from analysis-ready BAMs |
results/gvcf/ |
Per-sample GVCF files |
results/jointcall_vcf/ |
Raw joint-called VCF files |
results/hardfilt_vcf/ |
Joint-called VCF with hard-filter annotations |
results/vqsrfilt_vcf/ |
Joint-called VCF with VQSR annotations (only when --vqsr true is used) |
You can use either results/hardfilt_vcf/ or results/vqsrfilt_vcf/ as your final filter annotated call set.
Note: you can use nextflow option -output-dir [res_alt] to change the results folder to a different one
Run a stub workflow to quickly validate workflow logic without executing real jobs:
conda activate nf
NXF_VER=26.04.6 nextflow main.nf -stub --vqsr true -without-condaflowchart TD
A["📁 Raw FASTQ"] --> B{"Host read<br/>removal method"}
subgraph host_rm["2-step host reads removal"]
C["🗄️ BOWTIE2_ALIGN_TO_HOST<br/>Align reads to host genome<br/><br/>📤 flagstat_raw/<br/>📤 readlen_raw/"]
D["️️🗄️ SAMTOOLS_VIEW_RM_HOST_READS<br/>Remove host-mapping reads"]
E["🗄️ SAMTOOLS_FASTQ<br/>Convert unmapped reads <br/> to FASTQ<br/><br/>📤 parasite_reads/"]
C --> D --> E
end
subgraph concat_path["concat_genome path"]
F["🗄️ BOWTIE2_ALIGN_TO_CONCAT_GENOME<br/>Align to human+parasite genome<br/><br/>📤 flagstat_raw/<br/>📤 readlen_raw/"]
G["🗄️ SAMTOOLS_FASTQ<br/>Convert parasite reads <br/> to FASTQ<br/><br/>📤 parasite_reads/"]
F --> G
end
B -->|default| C
B -->|--use_concat_genome| F
E --> H
G --> H
H -.->|parasite_reads_only| H1["🛑 Early stop:<br/>Save parasite FASTQs"]
H["🗄️ BOWTIE2_ALIGN_TO_PARASITE<br/>Align to parasite genome<br/><br/>📤 flagstat_parasite/"] --> I["🗄️ PICARD_MERGE_SORT_BAMS<br/>Merge & sort runs per sample"]
I --> J["️🗄️ PICARD_MARK_DUPLICATES<br/>Mark duplicate reads"]
J --> K["🗄️ GATK_BASE_RECALIBRATOR<br/>Recalibrate base qualities"]
K --> L["🗄️ GATK_APPLY_BQSR<br/>Apply recalibration<br/><br/>📤 recalibrated/"]
L --> L_cov["🗄️ BEDTOOLS_GENOMECOV<br/>Compute coverage<br/><br/>📤 recal_bam_coverage/"]
L --> L_flag["🗄️ SAMTOOLS_FLAGSTAT<br/>Compute flagstat<br/><br/>📤 recal_bam_flagstat/"]
L -.->|coverage_only| L1["🛑 Early stop:<br/>Stop before HaplotypeCaller"]
L --> M["🗄️ GATK_HAPLOTYPE_CALLER<br/>Per-sample GVCF calling<br/><br/>📤 gvcf/"]
M -.->|gvcf_only| M1["🛑 Early stop:<br/>Per-sample GVCFs"]
M --> N["🗄️ GATK_GENOMICS_DB_IMPORT<br/>Import GVCFs to GenomicsDB"]
N --> O["🗄️ GATK_GENOTYPE_GVCFS<br/>Joint genotyping<br/><br/>📤 jointcall_vcf/"]
O --> P["🗄️ GATK_SELECT_VARIANTS<br/>Select SNPs only"]
P --> Q{"Variant<br/>filtration"}
subgraph hard_filt["--hard (default is on)"]
R["🗄️ GATK_VARIANT_FILTRATION<br/>Hard-filter variants<br/><br/>📤 hardfilt_vcf/"]
end
subgraph vqsr_filt["--vqsr (default is off)"]
S["🗄️ GATK_VARIANT_RECALIBRATOR<br/>Build recalibration model"]
T["🗄️ GATK_APPLY_VQSR<br/>Apply VQSR filter<br/><br/>📤 vqsrfilt_vcf/"]
S --> T
end
Q -->|--hard| R
Q -->|--vqsr| S
R --> U["📋 Final VCF with <br/>Filtration Annotations"]
T --> U
This pipeline was originally developed for the posseleff project. If you find it useful, please cite:
Guo, B., Borda, V., Laboulaye, R. et al. Strong positive selection biases identity-by-descent-based inferences of recent demography and population structure in Plasmodium falciparum. Nat Commun 15, 2499 (2024).
https://doi.org/10.1038/s41467-024-46659-0