get_MNV

How it works

Indels and Local Haplotypes

How get_MNV reads an indel off the alignments, what it counts, and what each number in the output means. For the caller-compatibility and boundary rules, see Scope and Compatibility; this page is about the reading and the counting.

What an indel is, positionally

A VCF indel is anchored. POS=100 REF=A ALT=AGG does not change position 100: the A is padding, and the GG is inserted between 100 and 101. A deletion POS=100 REF=AGG ALT=A keeps 100 and removes 101 and 102.

That has a consequence get_MNV has to respect everywhere: an insertion does not occupy a reference base. It lives between two of them.

What a read has to see before it may speak

A read is entitled to an opinion about an allele only if it observed enough of the reference to have one. That span is not always the REF allele's own span:

AlleleREF spanSpan a read must observeWhy
Substitutionthe basethe baseNothing lies outside it.
Insertionthe anchoranchor + 1A read that stops on the anchor has covered the whole REF span and seen nothing of the junction. Without the extra base it reads as "the reference is here", which is evidence the insertion is absent that the read never had.
Deletionanchor and deleted basesthat span + 1Inside its own span, a read deleting more than this allele looks identical to one deleting exactly it. Where the deletion ends can only be seen from outside.

One function answers this for every path that observes an allele (observation_ref_len). Before it existed, the exact counting, the haplotype discovery and the indel-versus-substitution phasing each decided it separately, and disagreed. The worst combination cancelled real support: a mate ending on an insertion's anchor was read as "reference here" and stripped the support its partner mate had provided.

Exact event support

Event Reads counts molecules that reproduce the allele exactly over its reference span, CIGAR and all. A read must produce the same local sequence and carry the expected insertion and deletion components. This matters for net-neutral combinations, where an insertion plus a deletion can produce the same sequence as a plain substitution under a different alignment.

Two rules keep that count honest:

Event Depth is the locus depth: by default every molecule observing the anchor base at sufficient quality. --legacy-indel-depth restricts it to molecules that fully span the REF allele, which under-counts depth for a multi-base deletion and biases Event Frequency upward.

Local haplotypes

When several variants fall close together, the question is no longer what each one does but which of them share molecules. get_MNV answers it by reading the molecules, not by enumerating possibilities.

Discovery: what the molecules show

Nearby variants are grouped into a window by proximity, but proximity only proposes. Each candidate variant is judged on its own reference span, so a paired-end fragment can answer for a variant its first mate covers and another its second does. A molecule that did not observe every variant of the window is set aside as partial: it carries what it carries, but placing it in a combination would assert it lacks the variants nobody looked at.

The molecules that did observe the whole window are grouped by the combination they show. That is the output of discovery, and it is a joint distribution: how many molecules are each combination.

Two rules follow from reading rather than enumerating:

Counting: how many molecules are this haplotype

A row's support is the number of molecules whose combination is exactly that one, taken from discovery. The exact-allele count cannot answer this: it matches over the combination's own reference span, so a molecule carrying that combination and something else outside the span matches too. It keeps the job it can do, which is confirming the allele is reproducible at all, and supplying the window depth.

The difference is not academic. With twelve molecules carrying three variants and eight carrying only the first two:

Event ComponentsEvent ReadsEvent DepthEvent Frequency
SNV:28:G>T | INS:29:+GCT | SNV:30:T>A12200.6000
SNV:28:G>T | INS:29:+GCT8200.4000

Counted by allele match, the second row read 20 at 1.0000: the whole species' support, for a haplotype eight molecules carry. The counts now sum to the depth, which is the coherent picture of a window holding two species.

What is emitted

A window that shows more than 64 distinct combinations reports the best supported and says in the log what it set aside, rather than truncating quietly.

Reading an indel row

For the example above:

Positions          28
Variant Type       INDEL
Change Type        In-frame Indel
AA Changes         Ala10delinsSerLeu
Event Class        complex_indel
Event Components   SNV:28:G>T | INS:29:+GCT | SNV:30:T>A
Event Reads        12
Event Depth        20
Event Frequency    0.6000
SO Term            inframe_insertion
HGVS g.            g.28_30delinsTCGCTA

Positions is the compound allele's anchor, not one position per component; Event Components is where the parts are listed. The amino-acid change is the effect of the whole compound allele, so a haplotype of three variants gets one protein consequence rather than three.

A codon-level MNV row overlapping an indel is kept as positional context with Change Type = Indel overlap and Unknown for the residue, because the codon's protein effect is not defined independently of the indel. The complex_indel row beside it is where the real consequence is.

Limits worth knowing

How this is checked

Edit this page on GitHub