The AIRR record¶
An AIRR record is one row of GenAIRR's output - one simulated molecule (or one paired-end read pair) with its ground truth, its observed sequence, and the counters that describe what happened on the way through the pipeline. This page is the field-by-field tour, organised by category, with the biology-vs-artefact distinctions made explicit so you can pick the right field for the question you're asking.
Derived, not accumulated¶
GenAIRR builds the record at the end from the final nucleotide pool, allele assignments, live calls, trace, and event history. Coordinates and CIGARs describe the final alignment; productivity checks the observed coding span, reading frame, and conserved anchors. Persistent assignments retain recombination provenance across clonal forks.
validate_records checks the
record against engine state and reference data. Passing confirms
agreement with the implemented checks; it does not prove that every
biological assumption or reference annotation is correct. Projection
and validation share some helpers, so independent checks remain useful.
What an AIRR record is¶
A record is a dict (or one row of result.to_dataframe()).
Field names follow the AIRR-C v1 schema where one exists, with
GenAIRR-specific additions for ground truth, mutation
provenance, corruption counters, clonal structure, and
paired-end layout. The same column order ships in
SimulationResult.to_tsv() / .to_csv() and is reachable
programmatically as result.to_dataframe().columns.
The canonical groups, in the order they appear on the record:
- AIRR metadata (sequence + length + locus)
- V / D / J calls + alignment coordinates + trim
- Junction
- NP regions
- Productivity flags
- SHM and corruption counters
- Per-segment indel + mutation partitions
- V-subregion mutation partition
- End-loss artefacts
- Contamination flag
- D-inversion provenance
- Receptor-revision provenance
- Read layout (paired-end)
- Clonal stamping (when a clonal workflow runs)
- Truth columns (when
expose_provenance=True)
The remaining sections walk through each.
Core sequence fields¶
| Field | Type | Meaning |
|---|---|---|
sequence_id |
str | "{prefix}{i}" - defaults to "seq0", "seq1", … |
sequence |
str | The observed nucleotide sequence; lower-case bases mark corrupted positions (low quality / PCR / sequencing error) |
sequence_aa |
str | Amino-acid translation in the V reading frame |
sequence_alignment |
str | Gapped sequence aligned to the germline (when present) |
germline_alignment |
str | The germline alignment of sequence_alignment |
germline_alignment_d_mask |
str | Germline alignment with the D region masked |
sequence_length |
int | Length of sequence in nucleotides |
rev_comp |
bool | True when the projection reverse-complemented the molecule (set by random_strand_orientation) |
locus |
str | Cartridge identity (e.g. "IGH", "TRB") |
Biological annotations use coding orientation. When rev_comp=True,
sequence contains the observed reverse-strand molecule, while
sequence_aa, alignments, junction/NP strings, and biological query
coordinates refer to its reverse complement. Paired-read windows
instead index the observed sequence.
The lower-case base convention on sequence is load-bearing -
the FASTQ exporter (to_fastq / to_paired_fastq) routes those
positions to the low-quality bucket. See the
Corruption + sequencing artefacts guide
for what each corruption pass writes into sequence.
Allele calls and truth¶
v_call, d_call, and j_call name the final generating alleles. They are
single-valued and define the references used by the germline alignments,
CIGARs, identities, and germline coordinates. Receptor revision updates
v_call to the replacement allele; original_v_call preserves its predecessor.
d_call is the empty string on VJ chains.
Post-mutation inference is exported separately as recalled_v_call,
recalled_d_call, and recalled_j_call. These may contain comma-separated
ties or differ from the generating allele. When the generating allele is
in a tie, it appears first; otherwise calls follow allele-ID order.
Donor-genotype experiments restrict re-calls to alleles carried by that donor.
Re-calls do not change the ground-truth reference or absorb junction bases
into a segment's alignment.
When no inferred evidence is available, for example after complete clipping,
the corresponding recalled_*_call is empty; the standard generating call remains.
rec["v_call"] # generating allele
rec["recalled_v_call"] # evidence-based call; split on commas for ties
Truth columns (opt-in)¶
run_records(..., expose_provenance=True) also emits truth_v_call,
truth_d_call, and truth_j_call. These compatibility aliases equal the
standard calls. They remain available for existing family-validation and
benchmarking workflows.
Junction and productivity¶
| Field | Type | Meaning |
|---|---|---|
junction |
str | Junction sequence (V end + NP1 + D + NP2 + J start, conserved Cys to Trp/Phe) |
junction_aa |
str | Amino-acid translation of junction |
junction_start, junction_end |
int | Junction coordinates within the coding-oriented sequence |
junction_length |
int | Length of junction in nucleotides (always a multiple of 3 when productive: True) |
productive |
bool or null | Acceptance under the named sequence-level policy for the observed coding span |
vj_in_frame |
bool or null | Junction length and original V-anchor position agree with the retained V coding frame |
stop_codon |
bool or null | Whether a complete observed coding codon is definitely a stop; unknown if phase is unavailable or a possible stop remains unresolved |
productivity_policy |
str | canonical_observed_vdj_v1 |
productivity_scope |
str | observed_coding_span |
productivity_reasons |
list[str] | Stable diagnostic codes, or ["accepted"]; JSON in TSV/CSV cells |
The policy requires original V/J landmark provenance, V Cys and J Phe/Trp residue classes, V/J and J-tail frame agreement, and definitely stop-free complete coding codons. It does not establish expression, splicing, folding, or receptor function. J Phe and Trp are both accepted; exact preservation of the reference residue is an optional separate native contract.
A known defect yields False even when another check is unresolved. Otherwise,
missing required evidence yields None, and only all accepted checks yield
True. For example, NNN can hide a stop, GCN is definitely sense, and TAR
is definitely stop. An invalid reference landmark is unresolved annotation,
not proof that the molecule is biologically nonproductive. Reference-admission
exceptions never repair landmarks or invent a translation frame.
Terminal read loss may still permit True when the required landmarks and
coding evidence remain observed. Loss of a required landmark yields unknown;
a structural deletion of an original landmark is a known defect. This scope
makes no claim about sequence outside the observation. junction_aa translates
the physical junction in complete local codons even if vj_in_frame is false.
.productive_only() conditions sampling on accepted support and asserts the
final observed assessment in both execution modes. It raises if that guarantee
cannot be met; no failed or unresolved final record is emitted. The record
validator reconstructs productivity independently of the projection helper.
Recombination structure¶
Per-segment alignment coordinates¶
Python record dicts use 0-based, half-open intervals. TSV defaults
to airr_strict=True, which converts biological coordinates to
1-based closed intervals. CSV and DataFrame exports keep Python
conventions unless airr_strict=True is requested.
For each of V, D, J, the Python fields are:
| Field | Meaning |
|---|---|
{v,d,j}_sequence_start |
Position in the coding-oriented sequence where this segment begins |
{v,d,j}_sequence_end |
Exclusive end position in the coding-oriented sequence |
{v,d,j}_alignment_start |
Position in sequence_alignment |
{v,d,j}_alignment_end |
Exclusive alignment end |
{v,d,j}_germline_start |
Position in the germline allele |
{v,d,j}_germline_end |
Exclusive germline end |
Trim fields (recombination-stage)¶
| Field | Meaning |
|---|---|
v_trim_3 |
Bases removed from the V allele's 3′ end during recombination |
d_trim_5, d_trim_3 |
Bases removed from the D allele's 5′ and 3′ ends |
j_trim_5 |
Bases removed from the J allele's 5′ end |
v_trim_5, j_trim_3 |
Always 0 - these positions aren't trimmed during recombination (the canonical biology) |
These four trim fields are the recombination-stage diet. They
are not the observation-stage length loss - that's
end_loss_5_length and end_loss_3_length (next section).
Mixing them up is the most common confusion on the record
surface; the trim fields describe biology (the recombinase
chewed bases off allele ends before junction joining), while
end-loss describes the sequencer (the read ran short).
NP regions¶
| Field | Meaning |
|---|---|
np1 |
Bases between the final V and D alignment bounds (V–J on VJ chains), including intervening N and P bases |
np2 |
Bases between the final D and J alignment bounds, including intervening N and P bases (VDJ only; empty on VJ) |
np1_aa, np2_aa |
Complete codons within those intervals, translated in the retained V coding frame |
np1_length, np2_length |
Lengths in nucleotides |
The NP strings are slices of the coding-oriented sequence between the final segment alignments. Their lengths equal the lengths of those strings. Bases assigned to an adjacent germline alignment are outside these intervals, regardless of their original N/P provenance.
P-nucleotide lengths¶
Four per-record fields carry the palindromic-insertion lengths sampled during recombination:
| Field | Meaning |
|---|---|
p_v_3_length |
Number of P bases off the V allele's 3′ end (V → NP1 side) |
p_d_5_length |
Number of P bases off the D allele's 5′ end (NP1 → D side) - VDJ only, 0 on VJ |
p_d_3_length |
Number of P bases off the D allele's 3′ end (D → NP2 side) - VDJ only, 0 on VJ |
p_j_5_length |
Number of P bases off the J allele's 5′ end (NP2 → J side on VDJ; NP1 → J side on VJ) |
P bases contribute to the assembled molecule and to np1 / np2
when they lie between the final alignment bounds. The four P
lengths record emitted recombination history and survive clonal
forks or later read loss. They do not necessarily equal the number
of P bases still observed. There is no separate P-sequence field;
alignment bounds and P lengths alone cannot recover P provenance
after arbitrary mutation, indels, or truncation.
The engine samples P-insertion lengths from
cfg.reference_models.p_nucleotide_lengths. See
Junction N/P additions for the
layout diagrams.
CIGAR fields¶
| Field | Meaning |
|---|---|
{v,d,j}_cigar |
Local per-segment CIGAR against the generating allele named by the standard call |
{v,d,j}_score |
Per-segment alignment score (when reported by the projector) |
{v,d,j}_identity |
Fraction from 0 to 1: matching bases divided by alignment columns, including gaps |
{v,d,j}_support |
Per-segment support value (when reported) |
c_call |
Constant-region call (empty when no C-region biology is modeled) |
Python dicts carry local M / I / D operation runs. Strict
exports add leading query-offset S and reference-offset N runs
when nonzero. End loss shortens observed reference coverage; it does not
create alignment deletions. A structural deletion inside the observed span
still produces a D operation. Strict TSV/CSV also write booleans as T / F;
DataFrames retain boolean values.
Mutation and artefact counters¶
Biological SHM¶
n_mutations ← total biological SHM events (canonical)
n_v_mutations ← V-segment SHM
n_d_mutations ← D-segment SHM
n_j_mutations ← J-segment SHM
n_np_mutations ← NP1 + NP2 combined SHM
mutation_rate ← cumulative realized SHM event burden per input-molecule base
These five sum cleanly:
n_v + n_d + n_j + n_np == n_mutations on every record. The
validate_records gate
checks this equality and fires MutationCountSumMismatch if
it ever breaks.
mutation_rate sums each mutation stage's applied events divided by that
stage's input molecule length. Later clipping leaves those contributions
unchanged. It counts repeated hits and reversions, so it is not a mismatch
fraction measured on the observed V segment. Lineage summary records instead
use net differences divided by the node's pre-observation molecule length.
n_mutations is biology only. PCR errors, sequencing
errors, indel-pass errors, and end-loss never increment these
counters - they have their own.
V-subregion mutation partition¶
Six fields that partition n_v_mutations by the assigned V
allele's IMGT subregion intervals:
n_fwr1_mutations
n_cdr1_mutations
n_fwr2_mutations
n_cdr2_mutations
n_fwr3_mutations
n_v_unannotated_mutations
Sum equals n_v_mutations by construction. The unannotated
bucket catches three legitimate non-zero cases (the
V-side CDR3 stretch is the most common); see
SHM and mutation targeting
for the full rules.
Library + sequencing artefacts (non-biological)¶
n_pcr_errors ← bases mutated by PcrAmplifyPass
n_quality_errors ← bases corrupted by SequencingErrorsPass / AmbiguousBaseCallsPass
n_indels ← total indels across all PolymeraseIndelsPass passes
n_v_indels ← indels landing in the V segment
n_d_indels ← indels landing in the D segment
n_j_indels ← indels landing in the J segment
end_loss_5_length ← bases lost from the 5′ end (EndLossPass / primer_trim_5prime)
end_loss_3_length ← bases lost from the 3′ end (EndLossPass / primer_trim_3prime)
is_contaminant ← True when this record is a contaminant (set by `contaminate`)
n_v_indels + n_d_indels + n_j_indels ≤ n_indels - indels that
land in NP1 or NP2 are counted in n_indels but not in any
per-segment bucket (NP indels don't belong to a germline
segment).
primer_trim_*prime is a backwards-compatibility alias for
end_loss_*prime - both write the same end_loss_*_length
field.
Advanced mechanism provenance¶
Three fields surface the engine's recombination-stage editing decisions:
| Field | Type | Meaning |
|---|---|---|
d_inverted |
bool | True when invert_d() committed the D allele in reverse-complement orientation; False otherwise (VJ chains, VDJ without invert_d, inversion that landed on the forward branch) |
receptor_revision_applied |
bool | True when receptor_revision() fired and replaced the committed V; False otherwise |
original_v_call |
str | When receptor_revision_applied: True, the V allele name the recombine pass originally committed (before revision). Empty string "" otherwise - never None |
When receptor_revision_applied: True, v_call reports the
post-revision identity and original_v_call carries the
pre-revision name. See
Recombination editing (D inversion + receptor revision)
for the biology and the rules.
Read layout¶
Eight fields populated only when .paired_end(...) is in the
pipeline:
| Field | Type | Default when absent |
|---|---|---|
read_layout |
str | "" |
r1_sequence |
str | "" |
r2_sequence |
str | "" |
r1_start, r1_end |
int | 0 |
r2_start, r2_end |
int | 0 |
insert_size |
int | 0 |
On single-molecule pipelines (no .paired_end(...) call) all
eight default to their sentinel values, but the columns are
still present in the record. Treat a non-empty r1_sequence as
the canonical "this is a paired-end record" check; read_layout
also carries the layout label ("paired_end" when set).
r1_start/end and r2_start/end are always 0-based, half-open
windows on the observed sequence, even with rev_comp=True
or strict export. r2_sequence is the reverse complement of
its window.
See Paired-end reads and FASTQ for the quality model and the layout coordinates.
Clonal fields¶
All modern clonal workflows stamp clone_id, but the rest of the
surface depends on which clonal model produced the record:
| Field | Type | Produced by | Meaning |
|---|---|---|---|
clone_id |
int | clonal_lineage, clonal_repertoire, expand_clones |
Planted clone / family label (0-based) |
duplicate_count |
int | clonal_lineage, clonal_repertoire |
AIRR-standard abundance after genotype collapse |
parent_id |
int | legacy expand_clones |
Identifier of the ancestor Outcome; equals clone_id for records expanded from that ancestor |
lineage_node_id |
int | clonal_lineage |
Node id of the observed cell in the ground-truth lineage tree |
lineage_parent_id |
int | clonal_lineage |
Parent node id in the lineage tree (-1 for founder) |
lineage_generation |
int | clonal_lineage |
Generation depth of the observed cell |
lineage_abundance |
int | clonal_lineage |
Observation count after final-cell sampling and genotype collapse |
lineage_affinity |
float | clonal_lineage |
Sequence-distance proxy to the target; 0 only when no affinity model is active |
On non-clonal runs, these fields are absent from the record dict. Don't write
code that assumes they're always present; check with "clone_id" in rec.
validate_families() is a safe no-op on non-clonal batches because it handles
the absent case explicitly. See
Clonal simulation overview.
Validation¶
The records page composes cleanly with the validation surface:
validate_records(refdata)checks supported counters, coordinates, calls, and productivity against the underlying engineOutcomeand reference data. It tests consistency with the implemented model, not universal biological correctness or arbitrary custom metadata schemas.validate_families(refdata=None)groups records byclone_idand asserts each family agrees ontruth_v_call,truth_d_call,truth_j_call(when present).validate_families_with_parents(refdata)is for legacyexpand_clonesresults withresult.parents; it compares each descendant against its actual parentOutcome.
The full validation picture lives at the Validation hub.
Common mistakes¶
A handful of issues that show up repeatedly with the record surface.
Treating all n_* counters as biological mutations. Only
the SHM partition (n_mutations, n_v_mutations,
n_d_mutations, n_j_mutations, n_np_mutations, the six
V-subregion fields) describes biology. n_pcr_errors,
n_quality_errors, n_indels, and the end-loss lengths
describe library / sequencer artefacts. They live on the same
record by design - they don't share a counter.
Confusing inferred calls with generating alleles. Standard calls are
single generating alleles. The recalled_*_call fields describe inference
and can contain comma-separated ties.
Confusing trim with end-loss. v_trim_3 is recombination
biology (recombinase chewed bases off the V allele's 3′ end
before junction joining). end_loss_3_length is sequencing
artefact (the read ran short / was 3′ end-loss-clipped). The
field names are similar; the biology is different.
Treating np1 / np2 as N-only strings. They contain the
combined gap between adjacent alignments, including intervening
P bases. Use the four p_*_length fields
(p_v_3_length, p_d_5_length, p_d_3_length, p_j_5_length)
to read emitted palindromic-insertion lengths. These provenance
counters do not identify which P bases remain after read corruption.
Expecting paired-end fields without .paired_end(...). The
eight read-layout fields are present in every record but default
to empty / zero on single-molecule pipelines. r1_sequence ==
"" is the canonical "no paired-end projection ran" check. Don't
try to write FASTQ from a single-molecule pipeline - to_fastq
emits the assembled sequence; to_paired_fastq raises if
read_layout != "paired_end".
Expecting truth_*_call columns to always be present. They
appear only when expose_provenance=True is passed to
run_records(...). Without the flag, the columns are absent
entirely - not None-valued, absent.
Expecting clone_id on non-clonal records. Without a clonal
workflow (clonal_lineage, clonal_repertoire, or legacy
expand_clones), clonal fields are not stamped on the record dict at
all. Check for presence with "clone_id" in rec, not
rec.get("clone_id") is not None.
Where to go next¶
- Your first AIRR record a worked walk-through of one record, field by field.
- Export the results how records become TSV / CSV / FASTA / FASTQ.
- SHM and mutation targeting the SHM counters in depth.
- Corruption and sequencing artefacts the artefact counters in depth.
- Clonal simulation overview -
clone_id,duplicate_count, lineage metadata, and family validation. - Validation hub - re-deriving every
field from the underlying
Outcome.