Skip to content

Germline-aware annotation

By default, Varcode compares each variant with the reference genome. Pass the patient's germline calls to classify somatic variants against the patient's own sequence instead. This matters when an inherited variant sits nearby: if it already changed a codon, a somatic mutation in that codon can produce a different amino acid than the reference codon would suggest.

Phase enumeration limit

When the phase-hypothesis cap is exceeded, the current implementation classifies one all-cis assignment and marks germline_phase_state="too_many_hypotheses". That result does not resolve phase and should not be treated as a definitive consequence. The default cap is eight hypotheses. A correction that preserves uncertainty is tracked in #503.

Basic usage

from varcode import GermlineContext, load_vcf

somatic_variants = load_vcf("tumor.vcf", genome=81)
germline_ctx = GermlineContext.from_germline_vcf("normal.vcf", genome=81)
effects = somatic_variants.effects(germline=germline_ctx)

Use matching genome builds. normal.vcf should be a germline call set, not a sparse normal column from a somatic caller; see constructing a context for that distinction.

Unknown relative phase can produce several candidate effects. See two variants in one codon for a worked example, or phased VCF input to supply evidence. No annotator selection is needed.

Constructing a GermlineContext

Choose the source that matches your input:

from varcode import Completeness, GermlineContext

# Route 1: full germline call set from a real germline caller.
ctx = GermlineContext.from_germline_vcf("normal.vcf", genome=81)

# Route 2: multi-sample VCF, extract one column.
ctx = GermlineContext.from_multi_sample_vcf(
    "tumor_normal.vcf",
    sample="NORMAL", genome=81,
    completeness=Completeness.SPARSE,  # required, no default
)

# Route 3: explicit no-germline fallback (== germline=None).
ctx = GermlineContext.empty()

# Route 4: direct construction from an existing collection of germline variants.
ctx = GermlineContext.from_variants(germline_variants, reference_name="GRCh38")

completeness= distinguishes "no call here = ref/ref" (a real germline caller's output) from "no call here = unknown" (the NORMAL column of a somatic VCF, which only reports rows the somatic caller looked at). Forcing the caller to declare prevents silently mis-treating sparse data as complete.

Completeness Pipeline Absence at a position
COMPLETE DeepVariant, HaplotypeCaller, Strelka2 germline ⇒ ref/ref
SPARSE Mutect2 NORMAL, Strelka2 somatic ⇒ unknown
HOTSPOTS_ONLY Panel-of-normals, ClinVar ⇒ unknown
EMPTY Explicit no-data n/a

When the context is sparse and a somatic variant lands in a window with no germline calls, varcode flags effect.germline_unknown = True rather than silently assuming ref/ref.

Cross-VCF build mismatch

Use a VariantCollection for collection-level build validation. It raises GenomeBuildMismatchError when its somatic variants and germline context have different reference builds:

from varcode import GenomeBuildMismatchError

try:
    effects = somatic_variants.effects(germline=germline_ctx)
except GenomeBuildMismatchError as error:
    print(error.somatic_reference, error.germline_reference)
    raise  # correct the input builds before retrying

The one-variant API does not provide the same collection-level preflight. validate_reference=False is an option on VariantCollection.effects(), not Variant.effects(); it suppresses that check and does not convert coordinates. Prefer correcting mismatched inputs.

How germline changes predictions

Nearby germline variants can change the baseline codon or splice signal used to classify a somatic variant. The current default uses a local window, not a complete patient-genome reconstruction; see limitations.

Phase-set tags describe phase within their source VCF. Do not equate tags from independently produced tumor and normal files. Use a resolver backed by a jointly phased VCF or molecular evidence. Short- and long-read data can both leave phase unknown; coverage and linked alleles determine whether a particular pair is resolved.

Loss of heterozygosity (LOH)

When a reported somatic allele matches the supplied germline, Varcode returns GermlineAlleleOverlap with is_germline_overlap=True, is_loh=None, and loh_status="not_assessed". It does not apply the inherited allele a second time or claim a new somatic protein sequence. The allele may still have a functional effect relative to the genome reference.

LOH requires evidence about loss of an allele previously present in the normal sample (NCI definition). Varcode's sequence annotation does not perform that tumor/normal allelic-state analysis. An allele match, including unchanged heterozygous or inherited homozygous calls, cannot establish LOH. Integrate a dedicated LOH caller's evidence separately.

for effect in effects:
    if getattr(effect, "is_germline_overlap", False):
        print("Germline-overlap flag:", effect.short_description)

Here effects is the collection annotated with germline context above. The check requires germline=; annotation without that context cannot attach the flag. Use detect_germline_overlap for an allele-match query. The deprecated detect_loh now returns None (not assessed) and warns, rather than making a zygosity claim. Mixed inherited/somatic groups in the experimental transcript model remain explicitly unresolved until their allele-aware baseline can be represented.

Limitations

  • Germline-disrupted splice sites get no explicit downgrade. Classification runs against the patient's signal, but there's no "germline already broke this, downgrade severity" path.
  • Subclonal somatic and CNV dosage are not modeled. Every somatic variant is treated as 100% present.
  • Hypothesis cap of 8 by default when phase is unknown across multiple germline variants in a window. Raise via max_hypotheses=. Above the cap, the result is not a definitive consequence; see the phase enumeration limit.
  • Normalization mismatch between germline and somatic VCFs (left-alignment, MNV split) causes apparent position mismatches. Normalize both with the same tool first.
  • Population-frequency germline (gnomAD/ExAC as a substitute for patient germline) is not supported.

Lower-level helpers

The high-level effects(germline=...) path delegates to predict_germline_aware_effect. Both that function and apply_germline_to_transcript (returns a MutantTranscript with germline edits applied) are public — call them directly if you have a custom annotator or want the patient protein without full effect prediction.