Rpgen is the genomic record source for the Rfmalloc stack. It uses PLINK 2’s own pgenlib and import code to read PGEN, PLINK 1 BED, PED/MAP, TPED/TFAM, VCF/BCF, BGEN, Oxford GEN, HAPS/legend, EIGENSTRAT, and legacy PLINK 1 dosage data into file-backed compute layouts.
The central operation is rpgen_ingest():
library(Rfmalloc)
pgen <- system.file("extdata", "chr21_phase3_start.pgen", package = "Rpgen")
backing <- tempfile(fileext = ".bin")
rt <- open_fmalloc(backing, mode = "scratch", size_gb = 1)
dose <- rpgen_ingest(
pgen,
representation = "dosage",
runtime = rt
)
cat(sprintf(
"%d samples x %d variants, %d payload bytes\n",
nrow(dose), ncol(dose), length(unclass(dose))
))
#> 2504 samples x 485 variants, 1214464 payload bytes
A format reader emits bounded panels of semantic records. Rfmalloc owns the destination allocation and layout:
| Representation | Meaning | Storage |
|---|---|---|
"hardcall" |
0, 1, 2, or missing non-reference count |
2 bits per sample and variant |
"dosage" |
fractional non-reference count | 1-byte fixed point |
"haplotype" |
two phased ref/non-ref bits per sample | locus-major, 1 bit per call |
"f64" |
full-precision dosage | file-backed doubles |
This is the useful split. A source declares whether it produced integers, doubles, or packed phase bits. The destination decides whether those records remain full precision or become a compute codec. Readers do not know codec headers, alignment, or allocation details.
PGEN and BED keep one pgenlib reader open and transfer bounded variant
panels directly into that destination. They never construct a complete
genotype matrix. Haplotype reads use PgrGetP() and preserve phase. A
missing genotype or unphased heterozygote is an error because the
one-bit HMM layout cannot represent either without a separate mask.
| Source | Companion arguments | Current route |
|---|---|---|
| PGEN | optional pvar
|
persistent pgenlib reader |
| PLINK 1 BED | optional bim, fam
|
persistent pgenlib reader |
| PLINK 1 PED/MAP | map |
importer, bounded transpose scratch, record sink |
| PLINK 1 TPED/TFAM | tfam |
importer to record sink |
| VCF/BCF | none | importer to record sink |
| BGEN | optional sample
|
importer to record sink |
| Oxford GEN | sample |
importer to record sink |
| HAPS/legend |
legend, sample, chr
|
bounded count, phased importer to record sink |
| EIGENSTRAT |
ind, snp
|
importer to record sink |
| PLINK 1 dosage |
fam, map
|
importer to record sink |
PLINK 2 already exposes each decoded variant immediately before
STPgenWriter encodes it. During rpgen_ingest(), Rpgen redirects
every writer append shape to Rfmalloc’s record context, including
multiallelic, phase, dosage, and sparse records. No genotype PGEN is
written or read back. The importers may write temporary .pvar/.psam
metadata companions, which are removed on exit. PED/MAP also retains
PLINK 2’s sample-major transpose scratch because changing that physical
order is an actual algorithmic transpose, not a package-boundary copy.
HAPS needs a bounded preliminary line count because its upstream writer
receives an upper bound instead of the exact variant count.
The standalone rpgen_import_*() functions do not activate this hook.
They continue to produce ordinary PGEN files for interoperability.
The dense rpgen_read_hardcalls() and rpgen_read_dosages() functions
remain available for small data, inspection, and reference comparisons.
They are not used by the bounded ingestion path.
rpgen_ingest(..., representation = "haplotype") stores variants as
aligned, contiguous locus rows over donor haplotypes. This is the access
direction used by Li and Stephens HMM kernels such as kalis
Forward/Backward:
hap <- rpgen_ingest(
"cohort.haps",
format = "haps",
representation = "haplotype",
legend = "cohort.legend",
sample = "cohort.sample",
chr = "1",
runtime = rt
)
The vendored pgenlib tree is generated by
tools/vendor-pgenlib/vendorpgen.R. The PLINK 2 importer closure is
generated by tools/vendor-plink2-import/vendorplink2import.R. Both
recipes are pinned and checked for drift in CI. See inst/COPYRIGHTS
and the two PROVENANCE.md files for source pins, local patches, and
licenses.
Rpgen is GPL-3 because it combines with pgenlib’s LGPL-3 code. The vendored zstd, libdeflate, SIMDe, and SFMT components retain their own licenses.
From the r-universe:
install.packages("Rpgen",
repos = c("https://rgenomicsetl.r-universe.dev", getOption("repos")))
or from the monorepo subdirectory:
pak::pak("sounkou-bioinfo/Rfmalloc/packages/Rpgen")
Rpgen depends on Rfmalloc and builds on Linux, macOS, and Windows. CI also checks Apple Silicon and Linux aarch64.