RestoreBases

Script: restorebases.sh Package: var2 Class: RestoreBases.java

Restores SEQ and QUAL fields onto secondary (0x100) and supplementary (0x800) alignments by copying them from the primary (non-supplementary) alignment of the same read. Aligners like minimap2 emit SEQ=* on secondary/supplementary records, making them unusable for variant calling. RestoreBases recovers the true bases and qualities with no reference bias—unlike MD-tag restoration, which can introduce bias. Essential for accurate variant calling on multi-mapping reads.

Overview

Many short and long-read aligners output secondary and supplementary alignments with SEQ=* and QUAL=*, reducing them to position-only records. This makes them unsuitable for variant calling tools like CallVariants, which require actual base sequences to evaluate evidence.

RestoreBases solves this by matching each read by name, finding its primary (non-supplementary) alignment which carries the true bases and qualities, and copying those fields onto all secondary/supplementary records. The restoration is reference-independent: you get the actual sequenced bases, not bases reconstructed from the MD tag, which would inherit any reference bias in the alignment.

Key advantage: For a true heterozygous variant at allele frequency 0.50, MD-tag reconstruction drops the estimated AF to ~0.17 due to reference bias. RestoreBases preserves the true AF because it uses actual sequenced bases.

Performance Notes

  • For bacterial-sized BAMs (fit in RAM): use ways=1 to skip temporary files
  • For large inputs: increase ways to reduce peak memory of any single subfile
  • Default ways=31 partitions input into 31 temporary subfiles by read-name hash
  • Temporary files are always cleaned up automatically, even on error

Basic Usage

restorebases.sh in=input.bam out=output.bam

The input file must be a SAM or BAM file containing secondary and/or supplementary alignments. RestoreBases will read all records, match them by name, restore bases/quals onto non-primary alignments from their primary counterpart, and write a complete SAM/BAM output with correct SO:unsorted header tag.

How It Works

RestoreBases uses a two-phase approach to bound memory usage:

  1. Partition Phase: Stream the input once and partition records into ways temporary headerless sam.gz subfiles, routed by qname.hashCode()%ways. This ensures all alignments of a read land in the same subfile.
  2. Restore Phase: For each subfile: load into memory, name-sort, group by (qname, pairnum), then for each group copy the primary's seq/qual onto its secondaries/supplementaries. Write results to the final output.

With ways=1, partitioning is skipped (no temp files)—the entire input is loaded, sorted, and restored at once. This is recommended for bacterial BAMs and other inputs that fit comfortably in RAM.

Handling Hard-Clipped Supplementary Alignments

Some aligners hard-clip supplementary records (H in CIGAR), meaning the record's SEQ does not span the full read. RestoreBases offers two strategies in fix mode:

Note: if the primary itself has hard-clips, that group cannot be restored (the full read is not available). Such groups are counted in the statistics.

Parameters

RestoreBases organizes parameters into input/output, processing mode, and memory/performance categories.

Input/Output Parameters

in=<file>
Input SAM or BAM file (with secondary/supplementary alignments). Required.
out=<file>
Output SAM or BAM file. Required. File format is detected by extension (.bam or .sam; .sam.gz for gzipped SAM).
ow=t
(overwrite) Allow overwriting existing output files. Default is true. Set ow=f to prevent accidental overwrites.

Processing Mode Parameters

mode=fix
Processing mode. Valid values: fix or tag. Default is fix.
  • fix: Write the bases/quals directly into SEQ/QUAL fields of non-primary records.
  • tag: Leave SEQ=* unchanged and attach OS:Z: (bases) and OQ:Z: (qualities) optional tags in original sequencing orientation. Useful if you want to preserve the original SEQ=* but have the true bases available as tags.
fix
Bare flag equivalent to mode=fix. Sets mode to write seq/qual directly.
tag
Bare flag equivalent to mode=tag. Sets mode to attach optional tags instead of overwriting SEQ/QUAL.
hardclip=convert
Applies only in fix mode. Controls how supplementary hard-clips are handled. Valid values: convert, truncate, soft, s (aliases for convert). Default is convert.
  • convert (or soft, s): Convert any H operations in supplementary cigars to S operations, then copy the full read. After conversion the CIGAR query-consuming length matches the full SEQ length. Produces larger files but simpler semantics.
  • truncate (or trunc): Keep hard-clip operations in the CIGAR and copy only the range of the read that this record's own segment covers, based on leading and trailing hard-clip counts. Produces smaller files and gives identical variant calls, but requires careful CIGAR interpretation.

Memory and Performance Parameters

ways=31
Number of temporary subfiles to partition input into. Default is 31. Increase for very large inputs to reduce the peak memory needed to hold any single subfile in RAM.
  • ways=1: Skip partitioning, load the entire input into memory at once, and restore single-threaded. Best for bacterial-sized BAMs. Uses no temporary files.
  • ways=N (N > 1): Partition into N subfiles, process each separately. Peak memory per subfile ≈ (total input size) / N. Useful for human whole genomes and other large inputs.
tmpdir=<dir>
Directory for temporary subfiles during multi-way processing. Default is <output_file>_rbtmp (e.g., if out=output.bam, tmpdir is output.bam_rbtmp). The directory and all temporary files are automatically deleted on completion, even if an error occurs. Useful for directing temp files to a high-speed scratch filesystem.
reads=-1
Maximum number of input alignments to process. Default is -1 (process all). Useful for testing on a subset of a large file. Set reads=10000 to process only the first 10,000 records.

Java Parameters

-Xmx<size>
Set maximum heap size. Examples: -Xmx20g (20 GB), -Xmx4000m (4000 MB). Default is auto-detected to approximately 84% of physical memory. The shell script defaults to -Xmx4000m if not specified; adjust upward for large inputs or downward for memory-constrained systems.
-eoom
Exit on out-of-memory exception instead of hanging. Requires Java 8u92 or later. Recommended for production pipelines.
-da
Disable runtime assertions (faster execution). Default is to enable assertions for debugging. Use in production if performance is critical, but keep enabled during development.

Common Use Cases

Preparing minimap2 Output for Variant Calling

minimap2 is a fast long-read aligner that emits supplementary alignments for reads mapping to multiple loci. Its secondary/supplementary records carry SEQ=* to save space:

minimap2 -a -x map-ont ref.fa reads.fastq > reads.sam
samtools view -b reads.sam > reads.bam
samtools sort reads.bam > reads.sorted.bam
restorebases.sh in=reads.sorted.bam out=reads.restored.bam
callvariants.sh in=reads.restored.bam ref=ref.fa out=vars.vcf

After restoration, CallVariants can use all alignments (primary, secondary, supplementary) for evidence, improving variant discovery on repetitive regions and multi-mapping reads.

Restore with Truncate Mode for Space Efficiency

If output file size is a concern and you have supplementary hard-clips, use hardclip=truncate to copy only the relevant segment of each read:

restorebases.sh in=multi.bam out=restored.bam hardclip=truncate

This avoids quadratic seq blowup (where a single read might be repeated N times in different supplementary records) while still providing all bases needed for variant calling.

Testing and Debugging

Use the reads parameter to process a small subset for validation:

restorebases.sh in=large.bam out=test.bam reads=1000

Check the output and statistics before running on the full file.

Large-Scale Human Genome Restoration

For whole-genome BAMs that don't fit in memory, tune ways to control per-subfile memory:

restorebases.sh in=NA12878.bam out=NA12878.restored.bam ways=100 tmpdir=/scratch/restbases_tmp -Xmx8000m

Increase ways to reduce per-subfile memory and prevent OOM. Temporary files are created and cleaned up automatically.

Attaching Tags Instead of Overwriting SEQ/QUAL

In tag mode, the original SEQ=* is preserved and the true bases appear in OS:Z: and OQ:Z: tags:

restorebases.sh in=reads.bam out=reads.tagged.bam mode=tag

Useful if you want to preserve the original BAM structure but have the true bases available for downstream analysis.

Algorithm and Implementation Details

Read Grouping and Matching

RestoreBases groups alignments by (qname, pairnum)—i.e., read name and pair number (0 for single-end or R1, 1 for R2). Within each group, the first record after name-sorting is assumed to be the primary alignment (non-supplementary, non-secondary, with valid bases). All subsequent records in that group are treated as secondary or supplementary.

If no usable primary is found (e.g., all records are supplementary, or the primary has SEQ=*), the entire group is skipped and counted in "Groups with no primary" statistics.

Multi-Way Partitioning Strategy

The partition phase uses qname.hashCode()%ways to route each record to a subfile, ensuring all alignments of a read go to the same subfile. This allows each subfile to be processed independently with bounded memory. Records are buffered in chunks (200 per buffer by default) before writing to reduce I/O overhead.

Hard-Clip Handling in Truncate Mode

In truncate mode, the code extracts the leading hard-clip count from the CIGAR, calculates the query length (soft+hard-inclusive bases), and determines the trailing hard-clip as the remainder. For minus-strand records, the CIGAR is reverse-complemented relative to the read in the forward strand, so the segment start is calculated accordingly. Only the relevant portion of the primary's seq/qual is copied to avoid duplication.

Reference Bias Avoidance

Unlike MD-tag reconstruction (which reconstructs bases by comparing the SEQ to the MD tag and reference), RestoreBases copies the actual sequenced bases from the primary record. This preserves the true allele frequencies: a 50/50 het remains 50/50, not degraded to ~17% as would happen with MD-tag restoration on a misaligned supplementary.

Header Patching

The output header is modified to include or replace the SO: (sort order) field with SO:unsorted, indicating that records are not in any standard sort order. If no @HD line exists, one is created with VN:1.6 and SO:unsorted.

Statistics and Output

RestoreBases prints a summary of work performed:

Performance Considerations

Memory Management

The ways parameter directly controls peak memory usage. Each subfile is held in memory sequentially, so peak memory ≈ (total input size) / ways. For a 100 GB BAM with ways=31, expect ~3.2 GB per subfile.

Speed

RestoreBases performs I/O-bound streaming and sorting, with computational overhead from SAM parsing and name-sorting. For large inputs, temporary file I/O dominates. Using an SSD or high-speed storage for tmpdir significantly improves performance.

Disk Space

During multi-way processing, temporary files consume space roughly equal to the input size. Ensure tmpdir has sufficient free space. Temporary files are cleaned up immediately after each subfile is processed, so total temp space never exceeds input size.

Compressed vs. Uncompressed Output

BAM (binary, compressed) output is smaller and faster to read than SAM (text). Use out=output.bam unless you specifically need text format.

Troubleshooting

Out-of-Memory Errors

If you encounter "java.lang.OutOfMemoryError", either increase the heap size with -Xmx or increase ways to reduce per-subfile memory. For example:

restorebases.sh in=input.bam out=output.bam ways=100 -Xmx8000m

Unexpected Output Size

In fix/convert mode, hard-clips are converted to soft-clips and the full read is copied, which can increase output file size. If this is undesirable, use hardclip=truncate instead.

Partial Restoration

If the statistics show a high number of "Skipped (length mismatch)" records, the input BAM may be corrupted or the supplementary alignments were generated by a non-standard aligner. Check that the CIGAR strings are valid and that the primary and supplementary records truly match.

Temporary Files Not Cleaned Up

If the process crashes, temporary files in tmpdir may remain. You can safely delete them manually; they are headerless SAM.gz files prefixed part_*.sam.gz.

Support

For questions and support: