Optional ORF catalogues¶
The bundled human catalogue is available through load_catalog() without
downloads. CatalogRegistry manages additional versioned ORF catalogues, with
the same explicit discovery, download, build and offline-open workflow as
DatasetRegistry. Once a species' catalogue in the packaged registry is
downloaded or built, load_catalog(species=...) opens it, so extra_orfs(genome) and
Proteome.with_nc_orfs("all") use registered sources. Pass
load_catalog(species=..., registry=...) as nc_orf_sources= to a proteome
constructor for your own registry.
Every catalogue for the species is used, merged: those on the requested
assembly as they are, and those on another assembly the pinned chains reach,
lifted (details). Each catalogue contributes its
release on the requested assembly if it has one, else its default release. It
returns the existing ORFCatalog; query results still bind to the
original PyEnsembl transcript through ReferenceORF. To use several catalogues
for one species together, see
merging catalogues.
The packaged registry supplies catalogues as pinned downloads or builds from their sources' own files:
| Species | Catalogues | ORFs |
|---|---|---|
| Mouse | OpenProt's MS-supported ORFs, Chang et al.'s and Yang et al.'s Ribo-seq ORFs | 60,707 merged |
| Human | the bundled GENCODE set; optionally Chang et al.'s and Yang et al.'s Ribo-seq ORFs | 7,264; 69,286 with both |
| Zebrafish | Yang et al.'s Ribo-seq ORFs | 15,491 |
| C. elegans | Yang et al.'s Ribo-seq ORFs | 4,035 |
| Yeast | Yang et al.'s Ribo-seq ORFs | 1,369 |
| Dog | none exists | 0 |
orfdata setup --species mouse (or zebrafish, worm, yeast) installs a
species' catalogues, with the chain files. orfdata catalogs status
(in Python, catalog_status()) lists every catalogue load_catalog can open,
the bundled one included, with its species, assembly, record count, whether it
is installed and whether it is used:
orfdata catalogs status
The examples below use a caller-supplied registry.
import json
from orfdata import CatalogRegistry
with open("catalog-registry.json") as handle:
registry = CatalogRegistry(json.load(handle))
releases = registry.list(species="yeast")
for manifest in releases:
print(manifest.catalog_id, manifest.version, manifest.coverage)
# Select an entry from your populated registry.
name = releases[0].catalog_id
registry.download(name) # Explicit, checksum-verified acquisition.
catalog = registry.open(name) # Offline; no implicit download or repair.
records = catalog.query(evidence="ms", require_resolved=True)
orfs = catalog.for_transcript(transcript) # An already loaded PyEnsembl transcript.
Use info(name) for immutable provenance and download_metadata(name) for
artifact URLs, checksums and access terms. inspect(name) checks the local
installation. Species, strain, assembly and annotation contexts remain attached
to accepted records; a matching accession alone cannot transfer evidence.
orfdata catalogs --registry catalog-registry.json list --species yeast
orfdata catalogs --registry catalog-registry.json download NAME
orfdata catalogs --registry catalog-registry.json query NAME --evidence ms --require-resolved
Queries share the ordinary ORF CLI's gene, transcript, genomic-position and
evidence filters, and JSON/TSV/FASTA exports. They cannot override the selected
release with a local --catalog argument. Use orfdata --catalog FILE for the
existing standalone local-file workflow.
Mouse¶
pip install "orfdata"
orfdata setup --species mouse --accept-terms # Three catalogues and the chains
orfdata setup downloads the published Chang et al. and RibORF catalogues, builds OpenProt from their pinned sources, and downloads the chain files (--accept-terms for UCSC's non-commercial
GRCm38/GRCm39 chains). It prints what it did with each: built, downloaded,
already installed, repaired or skipped. If one fails, the rest are still
installed and it exits with status 1, naming each failure; in Python,
install_species raises InstallError, whose result holds the same
report. load_catalog(species="mouse") merges OpenProt, Chang et al. and
Yang et al. in that order. Current inputs contain 2,976 MS-supported OpenProt
ORFs, 16,484 Chang et al. ORFs and 51,831 validated Yang et al. target placements.
Overlapping source models merge by coding path and protein while keeping their
original evidence. Merged counts and target-genome placement counts depend on
these versions; inspect the resulting catalogue and proteome layer receipts.
On GRCm38 and NCBIM37 the catalogues are
lifted automatically.
Mouse Ribo-seq ORFs (Chang et al.)¶
ncorf-mammals-mouse holds the mouse ncORFs of Chang et al.
(eLife 2025, doi:10.7554/eLife.109128):
16,484 of the source's 16,485,
called with PRICE, RiboCode and Ribo-TISH on Ensembl 107: an ORF is kept when at
least two callers find it in a library, in at least two libraries, starting at
ATG, CTG, GTG or TTG. Its types are uORF (8,294), lncORF (3,576), uoORF
(1,950), iORF (1,749), dORF (741) and doORF (174).
- Download.
orfdata data fetch ncorf-mammals-mouseinstalls the checksum-pinned 2.22 MB catalogue.orfdata data path ncorf-mammals-mousereturns its local path for offline use. Python callers usefetch_data("ncorf-mammals-mouse")andCatalogRegistry().open("ncorf-mammals-mouse"). The data release includes the manifest, source access terms and acceptance receipt.orfdata catalogs build ncorf-mammals-mousestill explicitly rebuilds from the original inputs;catalogs query ncorf-mammals-mouse --builtselects that result. - Sources. The authors' BED and protein FASTA (MIT, pinned commit), the paper's Table S3 (CC BY 4.0) and Ensembl 107's GTF; about 40 MB.
- Coding paths. The BED garbles multi-exon block sizes, so each ORF's path comes from its representative transcript and transcript coordinates in Ensembl 107, checked against its genomic start, stop, BED span and protein length. 16,484 resolve; one without a stop codon is listed as unresolved. All 16,484 bind to their Ensembl 107 transcript with their exact coding path and protein; 16,470 place on Ensembl 115.
- Starts. The source writes GTG-initiated proteins with valine. A near-cognate start is read by the initiator methionine, so these proteins begin with M, and their records declare the GTG start (CTG and TTG are already starts of the standard code).
- Evidence. Each ORF's library and caller counts become aggregate
Ribo-seq summaries, so
RiboSeq()selects every ORF andMS()none. The libraries aren't named per ORF, soRiboSeq(min_studies=2)selects none.
Mouse RibORF ORFs (Yang et al.)¶
riborf-mouse generation 3 reconstructs the ORFs reported by Yang et al.
(Nat Commun 2024,
doi:10.1038/s41467-024-46240-9)
against their original GENCODE M25/GRCm38 reference before any placement on
Ensembl 107/GRCm39. The original table supplies RNA intervals and BED blocks,
not protein sequences. All 54,128 non-skipped rows reconstruct against the
pinned native RNA, with the full transcript version, length, start/stop and
every BED block checked.
- Download.
orfdata data fetch riborf-mouseinstalls the verified 24.9 MB catalogue. Rebuilding remains available from its pinned source files. - Target placement. Exact full RNA, coding nucleotide sequence and translated protein must agree. All target cDNA and ncRNA sequences are searched, including duplicate loci and renamed transcripts. A surviving stable ID, unchanged RNA length or matching start/stop alone cannot transfer evidence.
- Coverage. 51,831 rows have a unique validated target placement, representing
41,882 distinct proteins. Another 118 have multiple exact target placements;
2,179 lack an unchanged complete target RNA. These 2,297 retain their native
models, sequences, evidence and explicit mapping reasons in
catalog.metadata["unresolved"].extra_orfs(...).unliftedalso reports them under the requested evidence rule. A changed RNA with an unchanged small CDS remains unresolved under this conservative placement method. - Provenance. Placed records retain their native
ORFRecordinrecord.lifted_fromand a typedrecord.reference_mapping. The latter records source/target references, original row ID, RNA interval, native CDS, source pins, coordinate checks and exact sequence comparisons. Evidence summaries continue to name the native reference and protein. No chain file is invented;record.liftoverisNone. JSON and TSV exports retain both objects. Managed manifests keep target annotation contexts inreferencesand native evidence contexts insource_references. - Sources. Supplementary Data 3 (CC BY 4.0), native GENCODE M25 GTF/RNA and Ensembl 107 GTF/cDNA/ncRNA: about 198 MB in total. The 32 MB target GTF is also used by Chang et al., so species setup acquires it once.
- Evidence and length. The 224 samples in 26 GEO series provide aggregate
RibORF presence, not per-ORF named studies.
RiboSeq()selects the target records;MS()andRiboSeq(min_studies=2)select none. The catalogue retains 11,858 target ORFs shorter than five residues. Consumers choose length limits when querying or building proteomes. - IDs and earlier generations. IDs are
riborf_followed by the original source row ID, including its native transcript version and RNA interval. They do not relabel a current coordinate as an original observation. Generations 1/2 reconstructed proteins directly from the target annotation; new builds of those unvalidated generations fail before downloads and direct callers to generation 3. Archived files remain historical data, with their saved provenance; they are not retroactively sequence-validated.
The source's ouORF, odORF and noncoding categories use the common uoORF, doORF and lncORF names. Canonical CDSs, likely sequencing errors and stop-codon readthrough rows remain excluded.
Mouse ORFs from OpenProt¶
openprot-mouse holds the alternative proteins (AltProts) and novel isoforms
that OpenProt 2.2 reports for mouse with at least two
unique MS peptides, on Ensembl 106 GRCm39 transcripts. orfdata does not
redistribute OpenProt's files, and OpenProt states no licence for them. You
build the catalogue from OpenProt's own downloads (3.4 MB, about 5 seconds),
each checked against its pinned size and SHA-256, with orfdata setup --species
mouse along with the Ribo-seq catalogue above, or alone with orfdata catalogs
build openprot-mouse. load_catalog(species="mouse"), extra_orfs(genome)
and Proteome.build(genome, nc_orf_sources="ms") use all three mouse catalogues, for any GRCm39
Ensembl release, so all must be installed. To add only MS-supported ORFs:
from pyensembl import EnsemblRelease
from orfdata import Proteome
proteome = Proteome.build(EnsemblRelease(115, species="mouse"), nc_orf_sources="ms")
print(proteome.summary()["kinds"], len(proteome.excluded))
{'annotated': 66668, 'extra_orf': 2854} 122
- Records. OpenProt's file has 8,645 rows for 2,913 proteins; each row
places one protein on one transcript. The 4,074 rows on Ensembl transcripts
become 2,976 records, one per protein and spliced coding path, named by the
OpenProt accession (
IP_for AltProts,II_for isoforms). The 32 proteins found at several paths, mostly identical copies at different loci such as on chrY, have one record per path (96 in all), namedaccession@transcript; they share one evidence summary. A path on several transcripts is one record, placed on each of them.orf_typeis OpenProt's protein type. - Not imported. The 4,571 rows on RefSeq transcripts are listed in
metadata["unresolved"], because no RefSeq annotation is pinned. Every protein still has a record: the one accession with only RefSeq rows, IP_957586, has the same sequence and path as II_957586, which is imported. - Genes. OpenProt gives Ensembl gene IDs, not names, so query the
catalogue by gene ID (
--gene ENSMUSG00000020149); proteome entries and placements take gene names from the PyEnsembl annotation. A split accession's bare ID, such asIP_824771, names all of its records. - Coordinates. Coding blocks come from OpenProt's BED file. Its README calls the TSV's genomic coordinates 1-based, but they are one lower than the BED's and Ensembl's at both ends (#55). The build checks every row against that offset, its transcript span and its protein length, and fails rather than guessing if any disagrees.
- Checked on Ensembl. On Ensembl 106, OpenProt's own release, 2,864 records bind to their source transcript with their exact coding path and protein. The other 112 lie on transcripts that are in Ensembl's GTF but missing from its cDNA and ncRNA files, so there is no sequence to check. On Ensembl 115, 2,854 place; of the rest, 109 lack a sequence, 10 transcripts are gone and 3 splice paths changed. Each is listed with its reason.
- Evidence. OpenProt's MS score (a sum of unique peptides per study) and
translation-event score (a count of studies) become aggregate summaries for
the protein, not peptides or named studies. So
MS()selects every record andRiboSeq()the 137 placed on Ensembl 115 with translation evidence, butMS(min_peptides=2)andRiboSeq(min_studies=2)select none. A sum of per-study peptide counts is not a count of distinct peptides, and a count of studies names no studies, so neither can show two independent observations. - Other assemblies. GRCm38 and NCBIM37 genomes use the catalogue lifted
automatically once the chains are downloaded (UCSC's GRCm39-to-GRCm38 chain
needs
--accept-terms); see every assembly of a species. - Dog. OpenProt has no dog data, and orfdata has no dog catalogue. Mouse evidence is never transferred to dog through orthology.
Cite Leblanc S, et al. OpenProt 2.0 builds a path to the functional characterization of alternative proteins. Nucleic Acids Res. 2024;52(D1):D522–D528, doi:10.1093/nar/gkad1050.
Human Ribo-seq ORFs (optional)¶
load_catalog(species="human") defaults to the bundled GENCODE catalogue: 7,264 ORFs, offline, with
no setup. Two larger Ribo-seq catalogues are optional, used only when asked
for, because they would change every human selection and make loading human
ORFs about 60 times slower:
orfdata setup --species human --catalogs all # Builds both; about 225 MB of sources
from orfdata import Proteome, extra_orfs, load_catalog
catalog = load_catalog(species="human", catalogs="all") # Bundled, Chang et al. and Yang et al., merged
orfs = extra_orfs(genome, "ribosome", catalogs="all")
proteome = Proteome.build(genome, nc_orf_sources="all")
load_catalog(species="human", catalogs=["ncorf-mammals-human"]) # Just one
orfdata proteome --nc-orf-sources all selects every dataset. Evidence presets
such as nc_orf_sources="ms" also query every compatible dataset; use
nc_orf_sources="bundled" for only the bundled set. They merge after the bundled
catalogue, Chang et al.'s before Yang et al.'s as for mouse, so an ORF in
several keeps the first one's ID. Together they give 69,286 ORFs, of which 69,162 place on Ensembl 115; that
takes about a minute and several GB of memory, against about a second for the
bundled catalogue alone. Neither adds MS evidence, so "ms" selects the same
ORFs either way.
ncorf-mammals-human: the human ncORFs of Chang et al., from 174 Ribo-seq libraries (Table S1), built as for mouse on Ensembl 107 GRCh38: 11,621 of the source's 11,623, two having a coding length that isn't their protein plus a stop. Types: uORF (6,178), lncORF (2,231), uoORF (1,633), iORF (773), dORF (694) and doORF (112); 997 start at GTG.riborf-human: the human ORFs of Yang et al., from 136 samples in 30 GEO series, on GENCODE V39, placed through the same Ensembl 105 transcripts (GENCODE V39 is Ensembl 105): 62,853 of 62,962 non-skipped rows; 109 are on transcripts with no sequence in Ensembl 105's FASTAs. The catalogue includes 11,679 valid records below five residues. Types: uORF (28,981), lncORF (12,938), iORF (5,990), dORF (4,907), uoORF (4,679), N-terminal truncation (3,574), extension (1,046) and doORF (738).
catalogs takes None (the species' default set), "all", or catalogue
names, "bundled" among them. orfdata catalogs status marks optional
catalogues. Everywhere but human, every catalogue is in the default set.
Zebrafish, C. elegans and yeast¶
orfdata setup --species zebrafish # or worm, or yeast
Yang et al.'s RibORF 2.0 ORFs of the mouse catalogue's kind, including valid short source models, from each species' sheet of Supplementary Data 3, imported the same way:
| Catalogue | Samples (GEO series) | Placed through | ORFs | Below 5 aa |
|---|---|---|---|---|
riborf-zebrafish |
20 (2) | Ensembl 115 GRCz11 transcripts | 15,491 | 5,732 |
riborf-worm |
39 (1) | Ensembl 105 WBcel235 transcripts | 4,035 | 1,186 |
riborf-yeast |
5 (1) | the R64-1-1 genome (sacCer3) | 1,369 | 241 |
- Zebrafish: every source transcript has the same length in Ensembl 100 to 115, so the catalogue uses 115. 18 rows have ambiguous bases in the ORF and are left out. Types: uORF (6,168), N-terminal truncation (3,816), iORF (2,696), dORF (1,623), lncORF (501), uoORF (343), extension (224) and doORF (120).
- C. elegans: the source's transcripts match Ensembl 100 and 105, and
WormBase changed many models after, so the catalogue uses 105; all 4,035
place there. Transcript names with dots of their own (
F45H11.11.1) are kept whole. Types: iORF (1,525), uORF (725), truncation (614), extension (556), lncORF (158), uoORF (249), doORF (121) and dORF (87). - Yeast: the source's yeast transcripts carry UTRs, and some are unstable
transcripts (CUTs, SUTs), none of which Ensembl's yeast annotation has; its
transcripts span only the CDS. So each ORF is placed from its genomic span
and translated from the pinned genome. The source gives block sizes but not
starts, so the 4 ORFs on two exons are rebuilt from their first block's
size at the start and last block's at the end, each across a GT-AG intron;
Ty1's programmed frameshift (
YNL284C-B, two blocks 1 nt apart) is left out. A record names the Ensembl transcript (YNL046W_mRNA) only when that transcript carries its coding path; otherwise it names the source's transcript (YNR004W,CUT808), with the Ensembl gene where there is one. Only the 171 ORFs on an Ensembl transcript, such as truncations and iORFs, place on a PyEnsembl genome; the rest (uORFs in 5′ UTRs, ORFs on CUTs) stay in the catalogue, queryable by region and gene, until ORFs on source-defined RNAs are supported (#66). Types: uORF (393), dORF (267), lncORF (260), iORF (139), truncation (138), extension (67), uoORF (58) and doORF (47).
Dog¶
No public resource gives dog ORF-level translation evidence, as of October 2026:
- Ribo-seq. No dog ribosome profiling is in GEO, SRA or ENA, and none of OpenProt, RiboSeq.org (147 organisms), RPFdb, SmProt or GWIPS-viz has dog. The one dog experiment published, Pfeffer et al. 2015 (canine pancreas rough-ER microsomes), deposited no reads.
- Mass spectrometry. Every dog dataset searched canonical proteins only, so none reports a noncanonical peptide as deposited: the Canine PeptideAtlas (PXD052932, 149,546 peptides from 13 tissues, plasma and urine), the dog tumour DLA immunopeptidome (PXD074485, 12,948 distinct database-search ligands), the Pfizer TMT atlas (PXD070290) and the dog brain proteome (PXD033420, PXD034449). The first two are available as dog peptide datasets, as evidence for known proteins.
Dog ncORF evidence therefore needs those spectra searched again against dog ORF sequences. Human and mouse ORFs are never transferred to dog by orthology. Dog proteomes contain their PyEnsembl-annotated proteins, and dog RNA, protein abundance and peptides are available as measurements.
Every assembly of a species¶
Catalogues on another assembly of the species are lifted automatically. Download
the pinned chain files once, and load_catalog, extra_orfs(genome) and
Proteome.build(genome, nc_orf_sources="ms") work on every human and mouse assembly PyEnsembl
knows:
orfdata chains download --accept-terms # Every pinned chain; without --accept-terms, UCSC's mouse ones are skipped
from pyensembl import EnsemblRelease
from orfdata import Proteome
Proteome.build(EnsemblRelease(75), nc_orf_sources="ms") # GRCh37: the GRCh38 catalogue, lifted
With evidence None (every catalogue ORF), each assembly gets:
| Genome | ORF source | Records | Placed | Not placed: main reasons |
|---|---|---|---|---|
| Human Ensembl 115, GRCh38 | bundled | 7,264 | 7,239 | 24 splice paths changed |
| Human Ensembl 75, GRCh37 | lifted, 1 hop | 7,247 | 6,857 | 270 transcripts absent, 108 paths changed, 1 on a transcript with a different protein |
| Human Ensembl 54, NCBI36 | lifted, 1 hop | 7,253 | 4,195 | 2,708 transcripts absent, 2 on a transcript with a different protein |
| Human Ensembl rapid 2022_07, T2T-CHM13v2.0 | lifted, 1 hop | 7,208 | 6,767 | 383 on a transcript with a different protein, 58 on no transcript, 56 not lifted |
| Mouse Ensembl 115, GRCm39 | OpenProt, Chang et al. and Yang et al. | 60,707 | 60,475 | 232 fail transcript/path/sequence binding checks |
Older annotations lack many of the transcripts the sources name, so fewer ORFs place; the rest, and any that didn't lift, are listed with the reason, never silently dropped.
- Which catalogues. Every catalogue for the species is used, merged: on the requested assembly as is, on the others the pinned chains reach, lifted. Each contributes one release: the one on the requested assembly if it has one, else its default. The requested assembly's come first, then by fewest hops; bundled first, then by the registry's priority, which decides whose ID a shared ORF keeps, so a new catalogue never renames existing ORFs. A source already used from a nearer assembly, such as one catalogue registered again under another name on another assembly, counts once. This depends only on the registry and the pinned chains, never on what happens to be installed.
- What's missing. Missing catalogues and chain files are reported together,
each with the command that installs it; nothing is downloaded while
loading.
cache_root(or--cache-root) is where both are looked for. - Your own catalogues.
extra_orfs(genome, catalog=...)andProteome.build(genome, nc_orf_sources=catalog)lift a catalogue, or each of a list, that is on another assembly of the species through the same chains. - Other names. Assemblies go by Ensembl's names.
hg38,GRCh38.p14,mm39and the like get an error that names the assembly to use, and a custom genome needs thatreference_name. - Routes.
chain_routes(species, build)gives the fewest-hop route to each assembly the pinned chains reach, for example GRCm39 to NCBIM37 through GRCm38.lift_to(catalog, build)lifts through it.catalog_status()lists, for each catalogue, the assemblies it reaches and whether their chains are downloaded. - Cost. Lifting takes a few seconds (about 2 s for the human catalogue to GRCh37 or CHM13); a process reuses the result for the same installed catalogues and chains.
- Unlifted ORFs.
ExtraORFs.unliftedlists the ORFs the evidence rule selects that a lift left out; proteomes count them asunliftedand list them among their exclusions.ExtraORFs.excludedholds only records of its catalogue, which is what Varcode looks up.
T2T-CHM13¶
PyEnsembl's EnsemblRelease has no T2T-CHM13 annotation, so orfdata pins
Ensembl's rapid-release one (2022_07) and installs it on request, checking each
file's size and SHA-256. The T2T Consortium's
GRCh38-to-CHM13 chain is public domain (CC0):
orfdata genomes install t2t-chm13 # 143 MB of Ensembl files, plus about 1.1 GB of PyEnsembl index
orfdata chains download GRCh38 T2T-CHM13v2.0
orfdata proteome --genome t2t-chm13 --nc-orf-sources ms --output chm13.fasta
from orfdata import Proteome, load_genome
genome = load_genome("t2t-chm13") # A PyEnsembl Genome named T2T-CHM13v2.0, offline
proteome = Proteome.build(genome, nc_orf_sources="ms") # 104,970 reference proteins (13 mitochondrial) + 2,039 MS-supported ORFs
The bundled GRCh38 catalogue lifts automatically: 7,208 of 7,264 ORFs lift, and
6,767 place on CHM13 transcripts with their exact coding path and protein.
Ensembl gave CHM13 transcripts new IDs, so placement is by path. Of the 441 that
don't place, 383 lie on a CHM13 transcript with their exact coding path whose
sequence, from CHM13's own alleles, encodes a different protein (in 15, the
start codon itself differs); the exclusion reason names that transcript. The
other 58 are on no CHM13 transcript. The genome keeps its species and assembly
accession when pickled, for multiprocessing and Varcode. Your own CHM13 genome
works too if its reference_name is T2T-CHM13v2.0 and it has a species
(or you pass context=); hs1 gets an error saying so.
Names, contigs and mitochondria¶
The same sequences go by many names, and orfdata genomes names lists them,
from orfdata.reference_names(). load_genome, install_genome and the
genomes commands accept any of them, so load_genome("hs1") opens
t2t-chm13. A name is matched exactly, ignoring case; varcode finds these
names inside FASTA paths and VCF ##reference lines.
| Name | Files | Mitochondrion |
|---|---|---|
T2T-CHM13v2.0, GCA_009914755.4 |
NCBI GenBank FASTA; Ensembl's DNA is this assembly | CHM13's own, CP068254.1 |
GCF_009914755.1 |
NCBI RefSeq FASTA | none |
hs1 |
UCSC | CP068254.1 |
chm13v2.0, chm13v2.0_maskedY, chm13v2.0_noY |
T2T Consortium analysis sets; _noY has no chrY |
CP068254.1 |
chm13v2.0_maskedY_rCRS, chm13v2.0_maskedY.rCRS |
The analysis set the T2T README recommends for short-read variant calling | the rCRS, NC_012920.1 |
T2T-CHM13v1.1, GCA_009914755.3, chm13.draft_v1.1 |
v2.0 without chrY | CP068254.1 |
Both mitochondria are 16,569 bp, but CP068254.1 begins at what the rCRS
numbers 577, so the same position is 576 to 577 bp apart in the two; header
lengths can't tell them apart, and only the name can.
Ensembl's rapid-release annotation has no mitochondrial genes. The rCRS is
GRCh38's MT, so t2t-chm13 also carries Ensembl GRCh38 release 115's
annotation of it (37 genes, 13 of them protein coding), on a contig named
NC_012920.1 so that it can never be read as CHM13's own MT. The three
files, about 12 KB, are packaged in ORFdata and rebuilt by
tools/build_rcrs_mitochondrion.py. It checks that Ensembl's MT DNA is
NC_012920.1, that every transcript's exons spell its cDNA, and that every CDS
translates to its protein with the vertebrate mitochondrial code. CHM13's own
mitochondrion has no annotation in its coordinates among the sources ORFdata
pins, so MT stays unannotated.
Names of other versions are listed with no genome and say why:
T2T-CHM13v1.0 (GCA_009914755.2, chm13.draft_v1.0) differs in 23 of its 24
sequence lengths, because v1.1 filled its rDNA gaps and re-polished its
telomeres, and the v0.7 and v0.9 drafts differ throughout. Bare chm13 names
every version, so it selects none.
orfdata genomes contigs hs1 (or orfdata.contig_aliases("hs1")) gives each
sequence's annotation name (1), UCSC name (chr1), GenBank and RefSeq
accessions (CP068277.2, NC_060925.1) and length, from NCBI's assembly
report. It lists only the sequences that name's files share with the pin: no
Y for chm13v2.0_noY, and no MT for GCF_009914755.1, which has none.
For the _rCRS set, chrM is NC_012920.1, the rCRS, rather than MT. chr1 is
248,387,328 bp here and 248,956,422 bp in GRCh38, so ##contig lengths tell
the two assemblies apart.
Lifting a catalogue by hand¶
orfdata lift writes a lifted catalogue to a file, for any route the pinned
chains cover or with your own chain file:
orfdata lift --species human --to GRCh37 --output orfs-grch37.json.gz
"lifted": "orfdata-catalog:2026-10-11.1@GRCh37",
...
"lifted_records": 7247,
"unmapped": 17,
"unmapped_reasons": {
"A coding block crosses an alignment gap or edge": 2,
"A coding block is not aligned": 15
},
orfdata lift writes the lifted catalogue and prints a summary to standard
error. --catalog FILE lifts a local catalogue instead, and --chain FILE uses
your own chain file instead of a downloaded pin. Pass the result back with
--nc-orf-sources for orfdata proteome; JSON and TSV exports include
each record's original and the chain. In Python:
from orfdata import download_chain, lift_catalog, load_catalog, load_chain
download_chain("GRCh38", "GRCh37") # Once; checksum-verified. download_chains() gets them all.
grch37 = lift_catalog(load_catalog(), load_chain("GRCh38", "GRCh37"))
grch37.save("orfs-grch37.json.gz")
print(len(grch37.records), len(grch37.metadata["liftover"]["unmapped"]), grch37.source)
7247 17 orfdata-catalog:2026-10-11.1@GRCh37
- Pinned chain files. The T2T Consortium's GRCh38 to and from
T2T-CHM13v2.0 are public domain. Ensembl's human GRCh38 to and from
GRCh37 and NCBI36,
and mouse GRCm38 to and from
NCBIM37,
have no usage restrictions.
orfdata chains listshows all ten. UCSC's mousemm10ToMm39andmm39ToMm10(GRCm38 and GRCm39) are free only for non-commercial use, so downloading them needs--accept-terms(accept_terms=True).--source-path FILEinstalls a copy you already have after checking it against the pin. Ensembl publishes no GRCm38/GRCm39 chain, and no chain is pinned for dog. - Other chain files.
ChainFile(path, source_build=..., target_build=...)reads any UCSC or Ensembl chain file, gzipped or not. A chain file doesn't name its assemblies, so you declare them. When the file is one of the pinned ones, its direction and species are checked, so a GRCh37-to-GRCh38 file can't lift a GRCh38 catalogue; any other file is taken at your word. UCSC's chains to GCF assemblies, such ascanFam3ToGCF_014441545.1.over.chain.gzfor dog ROS_Cfam_1.0, name contigs by RefSeq accession, so passcontig_names=built from NCBI's assembly report to match the genome's names. Check each file's terms before using or sharing what it produces. - What lifts. A record lifts only when every coding block lies inside one
ungapped alignment block and all blocks land on one contig and strand, in
order. Where a region also aligns to fix patches or alternate haplotypes,
the same-named chromosome is used. The 17 bundled ORFs above that don't
lift are listed in
metadata["liftover"]["unmapped"]with the reason (and, when a block aligns to several places, theircontigs): 15 have no alignment and 2 cross an alignment gap. UCSC's names for unplaced scaffolds, such aschr1_GL456210v1_random, are read as Ensembl's (GL456210.1).chrMis read asMTexcept in GRCh37, where hg19's chrM (NC_001807, 16,571 bp) is not the rCRS that Ensembl names MT and is offset from it. A GRCh37 chain'schrMof the rCRS length (16,569 bp), as in Ensembl's files, is the rCRS; any other GRCh37chrMlifts only through its own chains. - Real data. Lifting the 7,247 back to GRCh38 with
GRCh37_to_GRCh38.chain.gzrecovers the original coordinates of the 7,246 that lift back. On Ensembl 75, 6,857 of the lifted ORFs place on a transcript with their exact coding path and protein. - Provenance. Each lifted record keeps its original in
record.lifted_fromand the chain's file name and SHA-256 inrecord.liftover, the same whether the file was downloaded as a pin or passed by hand. Its ID, protein and evidence must equal the original's. Its initiation flanks move with it when they map consistently and in order. A strain pin is kept, with the chain's SHA-256 in place of an annotation.metadata["liftover"]["input"]describes the input catalogue, including any earlier lift.ORFCatalog.savewrites the same bytes for the same catalogue. - Placement.
extra_orfson the target genome then checks each ORF's exact coding path and protein, so a lift that changes the sequence is excluded rather than trusted. A lifted record names no annotation release. ORFs that didn't lift are reported among the exclusions, as "Did not lift to GRCh37: …", when the evidence rule selects them; through every hop of an earlier lift and every merged input. On Ensembl 75,"ms"selects all 2,186 MS-supported ORFs: 2,097 place, and 3 of the 89 excluded didn't lift. - Size. Each lifted record carries its original, so a lifted catalogue is about twice the size of its input (33 MB for the bundled one as JSON).
- Errors. When no pinned chains reach an assembly, such as HG002,
load_catalog(species=..., genome_build=...)gives theorfdata lift --chaincommand and the Python call that lifts with your own chain file.
Source builds¶
registry.build(name, source_paths={"source.json": path}) explicitly rebuilds
an allowlisted source adapter. registry.open_build(name) opens that local
result, and inspect_build(name) checks its input pins, transform identity and
output receipt without reopening original input files. The CLI equivalents are
catalogs build NAME --source-path source.json=PATH and
catalogs query NAME --built. A local receipt's checksum is distinct from an
independently pinned published artifact; inspect_build().verified remains false.
Builds are atomic. force=True creates a fresh bundle while existing readers'
files survive. Cache roots follow ORFDATA_CACHE, OPENVAX_DATA_CACHE, or the
explicit cache_root; catalogues, quantitative datasets and local source builds
use separate locations.
Defining a release¶
A registry definition maps a name to default_version, versions and an
optional integer priority (default 100): when catalogues are merged, lower
priorities come first, and the first catalogue's ID names an ORF several
share. Each
version contains a CatalogManifest, distribution access terms and an
assets entry for raw catalog.json.gz bytes, pinned by URL, SHA-256 and size.
The manifest declares references, original SourceAsset inputs, citations,
processing and coverage. coverage.records is required; source importers also
report original assignment counts and unresolved categories. Distinct protein
sequences and evidence summaries are separate counts.
Managed files use the existing orfdata.catalog.v1 JSON representation and embed
the full manifest in metadata.catalog_manifest. Every accepted record must
declare a complete genomic reference included in that manifest. Opening validates
the whole manifest, species/assembly, references and declared output counts.
The initial catalog-copy-v1 adapter wraps an independently prepared,
unmanaged, pinned portable catalogue named by processing.catalog_asset; it
preserves native metadata and adds the managed manifest. It rejects an input
that already has a managed manifest instead of replacing its release provenance.
It does not infer missing references or validate new biological models on
behalf of a source importer.
Python developers can use build_catalog(manifest, builder), where
builder(inputs, output_path) writes the managed file. DataCache verifies inputs and validates the completed
output before publishing the local build. Importer paths in metadata are never
executed as Python code.
OpenProt source qualification and its coordinate discrepancy remain separate from this data-management API.
The pinned files contain transcript/protein sequences, not chromosome DNA. For intronic sequence and structural products, explicitly install the matching Ensembl CHM13v2.0 chromosome FASTA:
orfdata genomes install t2t-chm13 --genome-fasta https://ftp.ensembl.org/pub/rapid-release/species/Homo_sapiens/GCA_009914755.4/ensembl/genome/Homo_sapiens-GCA_009914755.4-unmasked.fa.gz
from orfdata import load_genome
dna = "https://ftp.ensembl.org/pub/rapid-release/species/Homo_sapiens/GCA_009914755.4/ensembl/genome/Homo_sapiens-GCA_009914755.4-unmasked.fa.gz"
genome = load_genome("t2t-chm13", genome_fasta_path_or_url=dna)
sequence = genome.sequence("1", 1, 100) # 1-based inclusive; offline after install
A local chromosome FASTA path works too. Supply the same source when loading;
contig names must match the annotation (1 ... 22, X, Y). The consortium
analysis FASTA uses chr1 etc. and requires explicit contig-name remapping before
use with this annotation. Renaming contigs is not coordinate liftover.
DNA is optional and is not silently added to annotation-only loads. This DNA source is caller-selected, separate from the checksum-pinned annotation assets. Allow roughly 1 GB for the compressed download and several GB for installed DNA and indexes. Loading never downloads DNA. Missing DNA raises an installation error when accessed. Species taxonomy and nuclear/mitochondrial genetic codes are retained in PyEnsembl's saved reference metadata.