BamScaleR 0.99.2
Documentation
- README and the benchmark vignette now report the canonical benchmark run of 2026-10-08, measured on the full 25-file public ENCODE ATAC-seq corpus (single-file target ENCFF191LNL, 151,057,932 alignments). The previous tables came from a much smaller early run and overstated some results: the
GAlignmentsread speedup was shown as 4.03x against a measured 2.50x, and coverage to an in-memoryRleListas 3.29x against 2.49x. Read-pattern speedups are 1.94-2.50x, ATAC fragment-size QC 4.39x end-to-end, and coverage to bigWig 1.52x. - Added the measured in-reader aggregation results to the README (5.47-7.06x over the standard Bioconductor routes; 3.45x over megadepth at matched core counts) and the memory characteristic of
bam_coverage(). - Documented the rename from BamScale and the distinction from the unrelated NCBI command-line tool BAMscale.
- Installation instructions no longer advertise a Bioconductor install that does not yet work; BamScaleR is still under review.
- Recorded two further limitations: sublinear intra-file thread scaling, and that object-free command-line decoders reach higher raw throughput.
BamScaleR 0.99.1
Bug fixes
-
bam_coverage()andbam_coverage_bigwig()no longer allocate memory proportional to the genome declared in the BAM header. The coverage core previously built a dense per-base difference array for every header sequence before reading a single alignment, so a BAM carrying few alignments still cost several GB whenever its header declared a whole genome – 12.6 GB for a 1.4 MB file with a GRCh38 header – and the collapse pass then walked every base of every contig. Coverage is now accumulated as per-thread interval endpoints and swept per contig, so memory and time scale with the alignments actually present rather than with the reference. Contigs without alignments cost nothing and still emit the correct full-length zero run.Measured on a 2.5 GB ATAC-seq BAM (195 contigs): peak RSS 14.2 GB -> 2.5 GB and 71.0 s -> 14.9 s at 24 threads. On a 1.4 MB BAM with a whole-genome header: 12.6 GB -> 0.55 GB (R’s baseline) and 17.6 s -> 7.8 s. Output is unchanged and remains
identical()tocoverage(readGAlignments())at every thread count.Removing the shared difference array also removes the atomic updates from the read loop, which is where most of the speedup comes from.
BamScaleR 0.99.0
Package rename
- The package has been renamed from BamScale to BamScaleR. The former name collided with the existing NCBI command-line tool BAMscale (https://github.com/ncbi/BAMscale; Pongor et al., Epigenetics & Chromatin 2020, doi:10.1186/s13072-020-00343-x), which performs a different task: normalised (“scaled”) coverage-track generation and peak quantification. The rename removes that ambiguity.
- No user-facing functionality changed. All exported functions keep their names, arguments and behaviour.
- The option
BamScale.ga_fastpathis nowBamScaleR.ga_fastpath. - BamScale was accepted into Bioconductor devel on 4 May 2026 and was never part of a numbered Bioconductor release, so no deprecation cycle applies. BamScaleR is submitted as its renamed continuation and version numbering restarts at 0.99.0, as required for a new submission. The entries below document this package’s history under its former name.
Earlier history (as BamScale)
The entries below document this package’s development under its former name, BamScale, before the rename described above. Those version numbers belong to the former name’s series: BamScale was accepted into Bioconductor devel on 4 May 2026 and never shipped in a numbered release, so version numbering restarts at 0.99.0 under the new name. The code described below is carried forward into BamScaleR unchanged.
BamScale 0.99.14
New features
- Four new exported functions compute common BAM summaries entirely inside the multithreaded C++ reader (per-thread accumulators folded in the OpenMP region; only the compact result crosses into R – no per-read R objects are ever materialised), each verified byte-identical to its standard Bioconductor equivalent:
-
fragment_sizes(): paired-end fragment-size (insert-size) distribution; identical totable(abs(scanBam(isize))).drop_mate_unmapped = TRUE(default) replicatesscanBam’sisize = NArule for mate-unmapped reads. -
mapq_dist(): mapping-quality distribution; identical totable(scanBam(mapq))(MAPQ 255 reported as 255, where scanBam usesNA). -
bam_coverage(): per-base coverage as aSimpleRleList;identical()toGenomicAlignments::coverage(readGAlignments(...)), including the CIGAR conventions (M/=/X/Dcover,Ngaps,drop.D.ranges = FALSE) and the readGAlignments record filter (only unmapped reads dropped). -
bam_coverage_bigwig(): single-pass BAM -> coverage bigWig written natively via the bundled libBigWig – no R-side coverage object, nortracklayer::export.bwround-trip. Data and zoom blocks are compressed across the OpenMP threads (parallel = TRUE, default); the parallel output is bit-for-bit identical to the serial writer. Acompress_levelargument exposes the zlib deflate level, andverbose = TRUEprints a per-phase timing breakdown.
-
-
bam_read(as = "GAlignments")now uses a dedicated fast path that assembles theGAlignmentsslots in C++: seqnames/strand accumulated as run-length pairs inside the parallel region, the cigar column materialised through a CHARSXP cache that exploits CIGAR-string redundancy, and qname decoded only when actually requested. Output isidentical()toGenomicAlignments::readGAlignments()(verified at 226M-read scale) and to the previous construction path.options(BamScale.ga_fastpath = FALSE)restores the old path. - Vendored libBigWig 0.4.8 (MIT, Devon Ryan) under
src/libBigWig/(built with-DNOCURL, local files only). Local modifications, each commented inbwWrite.c: a tunable zlib deflate level, deferred parallel block compression, and zoom-level construction from in-memory coverage runs instead of re-reading the just-written data section.
Performance
- Fixed a quadratic reallocation pattern in the reader’s batch merge: per-batch exact-capacity
reserve()calls copied all accumulated data on every batch. Buffers now grow geometrically. This affects every read path – e.g. on a 229M-read BAM at 48 threads,as = "GAlignments"dropped from ~300 s to ~56 s, and plain data-frame reads improved 2.8-4.4x. - CIGAR strings are formatted with an allocation-free digit writer instead of per-op
std::to_string. - Representative large-file results (229M-read ATAC BAM, 48 threads, warm cache):
bam_coverage()7.2x vscoverage(readGAlignments());bam_coverage_bigwig()2.6x vs megadepth at equal compression;fragment_sizes()5.4x vsscanBam+table();as = "GAlignments"4.8x vsreadGAlignments()(and faster even single-threaded).
Bug fixes
-
bam_coverage_bigwig()now works on Windows: the output stream is opened in binary mode ("w+b"; text mode corrupted the bigWig), and zoom levels are always built from the in-memory coverage runs instead of re-reading the just-written stream (Windows’ C runtime is strict about write-to-read transitions on update streams; the in-memory path is byte-identical to the re-read path, verified at 226M-read scale). Vendored libBigWig diagnostics now go throughREprintf(), clearing the compiled-code check NOTE. - Removed a long-standing
^src/Makevars$entry from.Rbuildignore(dating to the pkgdown setup): built tarballs – including the Bioconductor builds – shipped withoutsrc/Makevarsand therefore compiled WITHOUT OpenMP (and without the explicit-lz), silently running single-threaded when installed from a tarball. In-placeR CMD INSTALL .was unaffected, which is why local installs always threaded. Tarball builds now carry the full build configuration (OpenMP, zlib,-DNOCURL, and the bundled libBigWig objects).
Benchmarks
-
run_workflow_benchmark.Rgains first-class arms for the four new functions (--include-fastcov/-bigwig/-fragsize/-mapq), each behind anidentical()correctness gate, with an optional megadepth context arm (--megadepth-bin). -
run_server_benchmark.R: BiocParallel PSOCK clusters are now started and their namespaces pre-loaded outside the timed region in all multi-file arms (previously cluster spin-up was charged to every iteration, diluting comparator ratios at high worker counts); added--include-singleso single and multi scenarios can run in separate stages. - New reproducible manuscript-benchmark tooling:
select_encode_atac.R(ENCODE portal selection -> accession/md5 manifest), manifest-drivendownload_atac_data.Rwith md5 verification,samtools_reference.sh,merge_runs.R, and the stagedrun_manuscript_final.shorchestrator.
Tests
- New testthat gates: byte-identity of
fragment_sizes()/mapq_dist()/bam_coverage()against their Rsamtools/GenomicAlignments equivalents;bam_coverage_bigwig()value-identity after re-import plus bit-for-bit parallel-vs-serial output; sixGAlignmentsfast-path identity gates (threads 1/4, mcols order,use.names, flag/mapq filters, fast == slow,gctorture).
BamScale 0.99.13
New features
- On the compatible read path,
seqandqualare now built directly in C asDNAStringSet/PhredQualityobjects from the packed BAM byte buffer (via the IRanges/XVector C-callable constructors), avoiding a character round-trip. Output remains byte-identical toRsamtools::scanBam. - Added an end-to-end workflow benchmark harness under
inst/benchmarks/(coverage → bigWig and ATAC fragment-size QC) and a self-containedbenchmark-resultsvignette.
Bug fixes
-
GAlignmentsoutput now carries the BAM-header sequence lengths in itsSeqinfo. Previously the seqlengths wereNA, socoverage()extended each seqlevel only to its maximum end andexport.bw()wrote truncated chromosome sizes, diverging fromGenomicAlignments::readGAlignments. - Force-load the C-callable providers (
S4Vectors,IRanges,XVector,Biostrings) andGenomicAlignmentsin.onLoad, so the compatible seq/qual andGAlignmentspaths resolve their constructors and classes on freshSnowParamSOCK workers.
