diff --git a/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/AbstractAnalysisRunner.java b/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/AbstractAnalysisRunner.java index 39533b335..b57deb0e1 100644 --- a/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/AbstractAnalysisRunner.java +++ b/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/AbstractAnalysisRunner.java @@ -23,6 +23,7 @@ import org.monarchinitiative.exomiser.core.analysis.sample.PedigreeSampleValidator; import org.monarchinitiative.exomiser.core.analysis.sample.Sample; import org.monarchinitiative.exomiser.core.analysis.util.*; +import org.monarchinitiative.exomiser.core.analysis.util.acmg.*; import org.monarchinitiative.exomiser.core.filters.*; import org.monarchinitiative.exomiser.core.genome.*; import org.monarchinitiative.exomiser.core.model.*; @@ -40,7 +41,6 @@ import java.util.function.Function; import java.util.function.Predicate; import java.util.function.UnaryOperator; -import java.util.stream.Collectors; import java.util.stream.Stream; import static java.util.stream.Collectors.toConcurrentMap; @@ -129,9 +129,7 @@ public AnalysisResults run(Sample sample, Analysis analysis) { // If no variant steps have been run and there is a VCF present, don't load it here - See issues #129, #478 List genesToScore = variantsLoaded ? getGenesWithVariants(allGenes) : allGenes.values().stream().filter(genesToScore()).toList(); - // Temporarily add a new PValueGeneScorer so as not to break semver will revert to RawScoreGeneScorer in 14.0.0 - CombinedScorePvalueCalculator combinedScorePvalueCalculator = buildCombinedScorePvalueCalculator(sample, analysis, genesToScore.size()); - GeneScorer geneScorer = new PvalueGeneScorer(probandIdentifier, sample.getSex(), inheritanceModeAnnotator, combinedScorePvalueCalculator); + GeneScorer geneScorer = buildGeneScorer(sample, analysis, genesToScore, probandIdentifier, inheritanceModeAnnotator); logger.info("Scoring genes"); List genes = geneScorer.scoreGenes(genesToScore); @@ -154,6 +152,15 @@ public AnalysisResults run(Sample sample, Analysis analysis) { return analysisResults; } + private GeneScorer buildGeneScorer(Sample sample, Analysis analysis, List genesToScore, String probandIdentifier, InheritanceModeAnnotator inheritanceModeAnnotator) { + CombinedScorePvalueCalculator combinedScorePvalueCalculator = buildCombinedScorePvalueCalculator(sample, analysis, genesToScore.size()); + + AcmgEvidenceAssigner acmgEvidenceAssigner = new Acmg2015EvidenceAssigner(probandIdentifier, inheritanceModeAnnotator.getPedigree(), genomeAnalysisService); + AcmgAssignmentCalculator acmgAssignmentCalculator = new AcmgAssignmentCalculator(acmgEvidenceAssigner, new Acmg2020PointsBasedClassifier()); + + return new RawScoreGeneScorer(probandIdentifier, sample.getSex(), inheritanceModeAnnotator, combinedScorePvalueCalculator, acmgAssignmentCalculator); + } + private List collectFilterCounts(List analysisSteps) { // build filter counts List filterStepTypes = analysisSteps.stream() diff --git a/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/util/ContributingAlleleCalculator.java b/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/util/ContributingAlleleCalculator.java index 69b948b62..cdf339de3 100644 --- a/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/util/ContributingAlleleCalculator.java +++ b/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/util/ContributingAlleleCalculator.java @@ -70,21 +70,18 @@ protected List findContributingVariantsForInheritanceMode(Mod //It is critical only the PASS variants are used in the scoring .filter(VariantEvaluation::passedFilters) .filter(variantEvaluation -> variantEvaluation.isCompatibleWith(modeOfInheritance)) - .collect(toUnmodifiableList()); + .toList(); //note these need to be filtered for the relevant ModeOfInheritance before being checked for the contributing variants if (variantsCompatibleWithMode.isEmpty()) { return variantsCompatibleWithMode; } - switch (modeOfInheritance) { - case AUTOSOMAL_RECESSIVE: - return findAutosomalRecessiveContributingVariants(modeOfInheritance, variantsCompatibleWithMode); - case X_RECESSIVE: - return findXRecessiveContributingVariants(modeOfInheritance, variantsCompatibleWithMode); - case ANY: - return findIncompletePenetranceContributingVariants(modeOfInheritance, variantsCompatibleWithMode); - default: - return findNonAutosomalRecessiveContributingVariants(modeOfInheritance, variantsCompatibleWithMode); - } + return switch (modeOfInheritance) { + case AUTOSOMAL_RECESSIVE -> + findAutosomalRecessiveContributingVariants(modeOfInheritance, variantsCompatibleWithMode); + case X_RECESSIVE -> findXRecessiveContributingVariants(modeOfInheritance, variantsCompatibleWithMode); + case ANY -> findIncompletePenetranceContributingVariants(modeOfInheritance, variantsCompatibleWithMode); + default -> findNonAutosomalRecessiveContributingVariants(modeOfInheritance, variantsCompatibleWithMode); + }; } diff --git a/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/util/PvalueGeneScorer.java b/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/util/PvalueGeneScorer.java deleted file mode 100644 index 1db2d0e50..000000000 --- a/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/util/PvalueGeneScorer.java +++ /dev/null @@ -1,138 +0,0 @@ -package org.monarchinitiative.exomiser.core.analysis.util; - -import de.charite.compbio.jannovar.mendel.ModeOfInheritance; -import org.monarchinitiative.exomiser.core.analysis.util.acmg.*; -import org.monarchinitiative.exomiser.core.model.Gene; -import org.monarchinitiative.exomiser.core.model.GeneScore; -import org.monarchinitiative.exomiser.core.model.Pedigree; -import org.monarchinitiative.exomiser.core.model.VariantEvaluation; -import org.monarchinitiative.exomiser.core.phenotype.ModelPhenotypeMatch; -import org.monarchinitiative.exomiser.core.prioritisers.model.Disease; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import java.util.*; -import java.util.function.Function; -import java.util.stream.Collectors; - -/** - * @since 13.1.0 - */ -public class PvalueGeneScorer implements GeneScorer { - - private static final Logger logger = LoggerFactory.getLogger(PvalueGeneScorer.class); - private static final EnumSet JUST_ANY = EnumSet.of(ModeOfInheritance.ANY); - - private final Set inheritanceModes; - - private final ContributingAlleleCalculator contributingAlleleCalculator; - private final GenePriorityScoreCalculator genePriorityScoreCalculator; - private final CombinedScorePvalueCalculator pValueCalculator; - private final AcmgAssignmentCalculator acmgAssignmentCalculator; - - /** - * @param probandId Sample id of the proband in the VCF. - * @param inheritanceModeAnnotator An {@code InheritanceModeAnnotator} for the pedigree related to the proband. - * @throws NullPointerException if any input arguments are null. - * @since 10.0.0 - */ - public PvalueGeneScorer(String probandId, Pedigree.Individual.Sex probandSex, InheritanceModeAnnotator inheritanceModeAnnotator, CombinedScorePvalueCalculator pValueCalculator) { - Objects.requireNonNull(probandId); - Objects.requireNonNull(inheritanceModeAnnotator); - this.inheritanceModes = inheritanceModeAnnotator.getDefinedModes(); - this.contributingAlleleCalculator = new ContributingAlleleCalculator(probandId, probandSex, inheritanceModeAnnotator); - this.genePriorityScoreCalculator = new GenePriorityScoreCalculator(); - this.pValueCalculator = Objects.requireNonNull(pValueCalculator); - AcmgEvidenceAssigner acmgEvidenceAssigner = new Acmg2015EvidenceAssigner(probandId, inheritanceModeAnnotator.getPedigree()); - AcmgEvidenceClassifier acmgEvidenceClassifier = new Acmg2020PointsBasedClassifier(); - this.acmgAssignmentCalculator = new AcmgAssignmentCalculator(acmgEvidenceAssigner, acmgEvidenceClassifier); - } - - @Override - public List scoreGenes(List genes) { - // TODO: v14.0.0 - return stream().toList() can't do this here as it breaks the sorting for the - // MOI-dependent writers. These will be removed in v14.0.0. - return genes.stream() - .parallel() - .map(gene -> { - List geneScores = scoreGene().apply(gene); - gene.addGeneScores(geneScores); - return gene; - }) - .sorted() - .collect(Collectors.toList()); - } - - /** - * Calculates the final ranks of all genes that have survived the filtering - * and prioritising steps. The strategy is that for autosomal dominant - * diseases, we take the single most pathogenic score of any variant - * affecting the gene; for autosomal recessive diseases, we take the mean of - * the two most pathogenic variants. X-linked diseases are filtered such - * that only X-chromosomal genes are left over, and the single worst variant - * is taken. - */ - @Override - public Function> scoreGene() { - return gene -> { - //Handle the scenario where no inheritance mode-dependent step was run - if (inheritanceModes.isEmpty() || inheritanceModes.equals(JUST_ANY)) { - GeneScore geneScore = calculateGeneScore(gene, ModeOfInheritance.ANY); - logger.debug("{}", geneScore); - return Collections.singletonList(geneScore); - } - - List geneScores = new ArrayList<>(inheritanceModes.size()); - for (ModeOfInheritance modeOfInheritance : inheritanceModes) { - GeneScore geneScore = calculateGeneScore(gene, modeOfInheritance); - logger.debug("{}", geneScore); - // IMPORTANT: Do not skip score without variants! - // A gene needs to have a score for each MOI as this will effect the overall ranks depending on the inheritance mode - // the phenotype score and how omim dealt with the inheritance mode compatibility for known diseases affecting that gene. - geneScores.add(geneScore); - } - return geneScores; - }; - } - - private GeneScore calculateGeneScore(Gene gene, ModeOfInheritance modeOfInheritance) { - //It is critical only the PASS variants are used in the scoring - List contributingVariants = contributingAlleleCalculator.findContributingVariantsForInheritanceMode(modeOfInheritance, gene.getPassedVariantEvaluations()); - - GenePriorityScoreCalculator.GenePriorityScore priorityScore = genePriorityScoreCalculator.calculateGenePriorityScore(gene, modeOfInheritance); - - List> compatibleDiseaseMatches = priorityScore.getCompatibleDiseaseMatches(); - - List acmgAssignments = acmgAssignmentCalculator.calculateAcmgAssignments(modeOfInheritance, gene, contributingVariants, compatibleDiseaseMatches); - -// double variantScore = acmgAssignments.stream() -// .mapToDouble(acmgAssignment -> acmgAssignment.acmgEvidence().postProbPath()) -// .average() -// .orElse(0.1); // 0.1 is the equivalent of a 0-point VUS - - double variantScore = contributingVariants.stream() - .mapToDouble(VariantEvaluation::getVariantScore) - .average() - .orElse(0); - - double combinedScore = GeneScorer.calculateCombinedScore(variantScore, priorityScore.getScore(), gene.getPriorityResults().keySet()); - - double pValue = pValueCalculator.calculatePvalueFromCombinedScore(combinedScore); - - return GeneScore.builder() - .geneIdentifier(gene.getGeneIdentifier()) - .modeOfInheritance(modeOfInheritance) - .variantScore(variantScore) - .phenotypeScore(priorityScore.getScore()) - .combinedScore(combinedScore) - .pValue(pValue) - .contributingVariants(contributingVariants) - // TODO this would be a good place to put a contributingModel - // i.e. from HiPhivePrioritiserResult see issue #363 -// .contributingModel() - .compatibleDiseaseMatches(compatibleDiseaseMatches) - .acmgAssignments(acmgAssignments) - .build(); - } - -} diff --git a/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/util/RawScoreGeneScorer.java b/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/util/RawScoreGeneScorer.java index e9ad03353..d4fbc4d77 100644 --- a/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/util/RawScoreGeneScorer.java +++ b/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/util/RawScoreGeneScorer.java @@ -55,6 +55,7 @@ public class RawScoreGeneScorer implements GeneScorer { private final ContributingAlleleCalculator contributingAlleleCalculator; private final GenePriorityScoreCalculator genePriorityScoreCalculator; + private final CombinedScorePvalueCalculator pValueCalculator; private final AcmgAssignmentCalculator acmgAssignmentCalculator; /** @@ -63,25 +64,27 @@ public class RawScoreGeneScorer implements GeneScorer { * @throws NullPointerException if any input arguments are null. * @since 10.0.0 */ - public RawScoreGeneScorer(String probandId, Sex probandSex, InheritanceModeAnnotator inheritanceModeAnnotator) { + public RawScoreGeneScorer(String probandId, Sex probandSex, InheritanceModeAnnotator inheritanceModeAnnotator, CombinedScorePvalueCalculator pValueCalculator, AcmgAssignmentCalculator acmgAssignmentCalculator) { Objects.requireNonNull(probandId); Objects.requireNonNull(inheritanceModeAnnotator); this.inheritanceModes = inheritanceModeAnnotator.getDefinedModes(); this.contributingAlleleCalculator = new ContributingAlleleCalculator(probandId, probandSex, inheritanceModeAnnotator); this.genePriorityScoreCalculator = new GenePriorityScoreCalculator(); - AcmgEvidenceAssigner acmgEvidenceAssigner = new Acmg2015EvidenceAssigner(probandId, inheritanceModeAnnotator.getPedigree()); - AcmgEvidenceClassifier acmgEvidenceClassifier = new Acmg2020PointsBasedClassifier(); - this.acmgAssignmentCalculator = new AcmgAssignmentCalculator(acmgEvidenceAssigner, acmgEvidenceClassifier); + this.pValueCalculator = Objects.requireNonNull(pValueCalculator); + this.acmgAssignmentCalculator = Objects.requireNonNull(acmgAssignmentCalculator); } @Override public List scoreGenes(List genes) { - for (Gene gene : genes) { - List geneScores = scoreGene().apply(gene); - gene.addGeneScores(geneScores); - } - Collections.sort(genes); - return genes; + return genes.stream() + .parallel() + .map(gene -> { + List geneScores = scoreGene().apply(gene); + gene.addGeneScores(geneScores); + return gene; + }) + .sorted() + .toList(); } /** @@ -122,6 +125,15 @@ private GeneScore calculateGeneScore(Gene gene, ModeOfInheritance modeOfInherita GenePriorityScoreCalculator.GenePriorityScore priorityScore = genePriorityScoreCalculator.calculateGenePriorityScore(gene, modeOfInheritance); + List> compatibleDiseaseMatches = priorityScore.getCompatibleDiseaseMatches(); + + List acmgAssignments = acmgAssignmentCalculator.calculateAcmgAssignments(modeOfInheritance, gene, contributingVariants, compatibleDiseaseMatches); + +// double variantScore = acmgAssignments.stream() +// .mapToDouble(acmgAssignment -> acmgAssignment.acmgEvidence().postProbPath()) +// .average() +// .orElse(0.1); // 0.1 is the equivalent of a 0-point VUS + double variantScore = contributingVariants.stream() .mapToDouble(VariantEvaluation::getVariantScore) .average() @@ -129,9 +141,7 @@ private GeneScore calculateGeneScore(Gene gene, ModeOfInheritance modeOfInherita double combinedScore = GeneScorer.calculateCombinedScore(variantScore, priorityScore.getScore(), gene.getPriorityResults().keySet()); - List> compatibleDiseaseMatches = priorityScore.getCompatibleDiseaseMatches(); - - List acmgAssignments = acmgAssignmentCalculator.calculateAcmgAssignments(modeOfInheritance, gene, contributingVariants, compatibleDiseaseMatches); + double pValue = pValueCalculator.calculatePvalueFromCombinedScore(combinedScore); return GeneScore.builder() .geneIdentifier(gene.getGeneIdentifier()) @@ -139,6 +149,7 @@ private GeneScore calculateGeneScore(Gene gene, ModeOfInheritance modeOfInherita .variantScore(variantScore) .phenotypeScore(priorityScore.getScore()) .combinedScore(combinedScore) + .pValue(pValue) .contributingVariants(contributingVariants) // TODO this would be a good place to put a contributingModel // i.e. from HiPhivePrioritiserResult see issue #363 diff --git a/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/util/acmg/Acmg2015EvidenceAssigner.java b/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/util/acmg/Acmg2015EvidenceAssigner.java index 95914a360..78d3466ce 100644 --- a/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/util/acmg/Acmg2015EvidenceAssigner.java +++ b/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/util/acmg/Acmg2015EvidenceAssigner.java @@ -26,6 +26,7 @@ import org.monarchinitiative.exomiser.core.analysis.util.GeneConstraints; import org.monarchinitiative.exomiser.core.analysis.util.InheritanceModeAnalyser; import org.monarchinitiative.exomiser.core.genome.GenomeAssembly; +import org.monarchinitiative.exomiser.core.genome.dao.ClinVarDao; import org.monarchinitiative.exomiser.core.model.*; import org.monarchinitiative.exomiser.core.model.Pedigree.Individual; import org.monarchinitiative.exomiser.core.model.frequency.FrequencyData; @@ -34,12 +35,16 @@ import org.monarchinitiative.exomiser.core.phenotype.ModelPhenotypeMatch; import org.monarchinitiative.exomiser.core.prioritisers.model.Disease; import org.monarchinitiative.exomiser.core.proto.AlleleProto; +import org.monarchinitiative.svart.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; -import java.util.stream.Collectors; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static org.monarchinitiative.exomiser.core.analysis.util.acmg.AcmgCriterion.*; @@ -48,6 +53,8 @@ */ public class Acmg2015EvidenceAssigner implements AcmgEvidenceAssigner { + private static final Logger logger = LoggerFactory.getLogger(Acmg2015EvidenceAssigner.class); + // Variants to be excluded from being assigned BA1 as specified by the ClinGen SVI working group in: // https://www.clinicalgenome.org/site/assets/files/3460/ba1_exception_list_07_30_2018.pdf private static final Set HG19_BA1_EXCLUSION_VARIANTS = Set.of( @@ -91,11 +98,16 @@ public class Acmg2015EvidenceAssigner implements AcmgEvidenceAssigner { AlleleProtoAdaptor.toAlleleKey(3, 15_645_186, "G", "C") ); + // e.g. p.(Lys567Thr) + private static final Pattern MISSENSE_HGVS_P = Pattern.compile("p\\.\\((?[A-Z][a-z]{2}\\d+)(?[A-Z][a-z]{2})\\)"); + private final ClinVarDao clinVarDao; + + private final String probandId; private final Individual.Sex probandSex; private final Pedigree pedigree; - public Acmg2015EvidenceAssigner(String probandId, Pedigree pedigree) { + public Acmg2015EvidenceAssigner(String probandId, Pedigree pedigree, ClinVarDao clinVarDao) { this.probandId = Objects.requireNonNull(probandId); this.pedigree = pedigree == null || pedigree.isEmpty() ? Pedigree.justProband(probandId) : pedigree; Individual proband = this.pedigree.getIndividualById(probandId); @@ -103,6 +115,7 @@ public Acmg2015EvidenceAssigner(String probandId, Pedigree pedigree) { throw new IllegalArgumentException("Proband '" + probandId + "' not found in pedigree " + pedigree); } this.probandSex = proband.getSex(); + this.clinVarDao = clinVarDao; } /** @@ -131,7 +144,6 @@ public AcmgEvidence assignVariantAcmgEvidence(VariantEvaluation variantEvaluatio // Updated recommendation: "Allele frequency is >0.05 in any general continental population dataset of at least // 2,000 observed alleles and found in a gene without a gene- or variant-specific BA1 modification." i.e. ExAC // African, East Asian, European [non-Finnish], Latino, and South Asian -// AlleleProto.AlleleKey alleleKey = AlleleProtoAdaptor.toAlleleKey(variantEvaluation); AlleleProto.AlleleKey alleleKey = variantEvaluation.alleleKey(); boolean isBa1ExcludedVariant = variantEvaluation.getGenomeAssembly() == GenomeAssembly.HG19 ? HG19_BA1_EXCLUSION_VARIANTS.contains(alleleKey) : HG38_BA1_EXCLUSION_VARIANTS.contains(alleleKey); if (!isBa1ExcludedVariant && frequencyData.maxFreqForPopulation(FrequencySource.NON_FOUNDER_POPS) >= 5.0) { @@ -148,9 +160,21 @@ public AcmgEvidence assignVariantAcmgEvidence(VariantEvaluation variantEvaluatio // PVS1 "null variant (nonsense, frameshift, canonical ±1 or 2 splice sites, initiation codon, single or multiexon deletion) in a gene where LOF is a known mechanism of disease" assignPVS1(acmgEvidenceBuilder, variantEvaluation, modeOfInheritance, knownDiseases); - // PS1 "Same amino acid change as a previously established pathogenic variant regardless of nucleotide change" - // Should NOT assign for PS1 for same base change. Unable to assign PS1 due to lack of AA change info in database -// assignPS1(acmgEvidenceBuilder, variantEvaluation.getVariantEffect(), variantEvaluation.getPathogenicityData().getClinVarData()); + // ignore non-missense, truncating, splice or mitochondrial variants + if (isMissenseOrInframeIndel(variantEvaluation.getVariantEffect()) && variantEvaluation.contigId() != 25) { + // ensure region is within contig bounds + Contig contig = variantEvaluation.contig(); + var upStream = Math.max(1, variantEvaluation.start() - 25); + var downStream = Math.min(contig.length(), variantEvaluation.start() + 25); + GenomicInterval genomicInterval = GenomicInterval.of(contig, Strand.POSITIVE, Coordinates.oneBased(upStream, downStream)); + var localClinVarData = clinVarDao.findClinVarRecordsOverlappingInterval(genomicInterval); + // PS1 "Same amino acid change as a previously established pathogenic variant regardless of nucleotide change" + // PM5 "Novel missense change at an amino acid residue where a different missense change determined to be pathogenic has been seen before" + assignPS1PM5(acmgEvidenceBuilder, variantEvaluation, localClinVarData); + // PM1 "Located in a mutational hot spot and/or critical and well-established functional domain (e.g., active site of an enzyme) without benign variation" + assignPM1(acmgEvidenceBuilder, variantEvaluation, localClinVarData); + // TODO: PM1/BP3 "In-frame deletions/insertions in a repetitive region without a known function" - requires domain information. + } if (pedigree.containsId(probandId)) { Individual proband = pedigree.getIndividualById(probandId); @@ -166,7 +190,6 @@ public AcmgEvidence assignVariantAcmgEvidence(VariantEvaluation variantEvaluatio assignPM3orBP2(acmgEvidenceBuilder, variantEvaluation, modeOfInheritance, contributingVariants, hasCompatibleDiseaseMatches); // PM4 Protein length changes as a result of in-frame deletions/insertions in a nonrepeat region or stop-loss variants assignPM4(acmgEvidenceBuilder, variantEvaluation); - // TODO: PM5 "Novel missense change at an amino acid residue where a different missense change determined to be pathogenic has been seen before // PP4 "Patient’s phenotype or family history is highly specific for a disease with a single genetic etiology" assignPP4(acmgEvidenceBuilder, compatibleDiseaseMatches); @@ -214,10 +237,10 @@ private void assignPVS1(AcmgEvidence.Builder acmgEvidenceBuilder, VariantEvaluat // Should this be using the hasCompatibleDiseaseMatches variable? boolean inGeneWithKnownDiseaseAssociations = !knownDiseases.isEmpty(); if (inGeneWithKnownDiseaseAssociations && isLossOfFunctionEffect(variantEvaluation.getVariantEffect()) - && (modeOfInheritance == ModeOfInheritance.ANY + && (modeOfInheritance == ModeOfInheritance.ANY || compatibleWithRecessive(modeOfInheritance) || compatibleWithDominant(modeOfInheritance) && (geneContraint != null && geneContraint.isLossOfFunctionIntolerant()) - ) + ) ) { if (variantEvaluation.hasTranscriptAnnotations()) { TranscriptAnnotation transcriptAnnotation = variantEvaluation.getTranscriptAnnotations().get(0); @@ -235,16 +258,15 @@ private void assignPVS1(AcmgEvidence.Builder acmgEvidenceBuilder, VariantEvaluat } private boolean isLossOfFunctionEffect(VariantEffect variantEffect) { - return variantEffect == VariantEffect.INITIATOR_CODON_VARIANT - || variantEffect == VariantEffect.START_LOST - || variantEffect == VariantEffect.STOP_LOST - || variantEffect == VariantEffect.STOP_GAINED - || variantEffect == VariantEffect.FRAMESHIFT_ELONGATION - || variantEffect == VariantEffect.FRAMESHIFT_TRUNCATION - || variantEffect == VariantEffect.FRAMESHIFT_VARIANT - || variantEffect == VariantEffect.SPLICE_ACCEPTOR_VARIANT - || variantEffect == VariantEffect.SPLICE_DONOR_VARIANT - || variantEffect == VariantEffect.EXON_LOSS_VARIANT; + return variantEffect == VariantEffect.START_LOST + || variantEffect == VariantEffect.STOP_LOST + || variantEffect == VariantEffect.STOP_GAINED + || variantEffect == VariantEffect.FRAMESHIFT_ELONGATION + || variantEffect == VariantEffect.FRAMESHIFT_TRUNCATION + || variantEffect == VariantEffect.FRAMESHIFT_VARIANT + || variantEffect == VariantEffect.SPLICE_ACCEPTOR_VARIANT + || variantEffect == VariantEffect.SPLICE_DONOR_VARIANT + || variantEffect == VariantEffect.EXON_LOSS_VARIANT; } private boolean predictedToLeadToNmd(TranscriptAnnotation transcriptAnnotation) { @@ -309,38 +331,103 @@ private boolean inTrans(SampleGenotype thisVariantGenotype, SampleGenotype other // GT in trans = (1|0 && 0|1) (variant in both copies of gene) // GT in cis = (1|0 && 1|0) or (0|1 && 0|1) (variant in same copy of gene) return thisVariantGenotype.isPhased() && thisVariantGenotype.isHet() - && otherVariantGenotype.isPhased() && otherVariantGenotype.isHet() - && !thisVariantGenotype.equals(otherVariantGenotype); + && otherVariantGenotype.isPhased() && otherVariantGenotype.isHet() + && !thisVariantGenotype.equals(otherVariantGenotype); } private boolean inCis(SampleGenotype thisVariantGenotype, SampleGenotype otherVariantGenotype) { // GT in trans = (1|0 && 0|1) (variant in both copies of gene) // GT in cis = (1|0 && 1|0) or (0|1 && 0|1) (variant in same copy of gene) return thisVariantGenotype.isPhased() && thisVariantGenotype.isHet() - && otherVariantGenotype.isPhased() && otherVariantGenotype.isHet() - && thisVariantGenotype.equals(otherVariantGenotype); + && otherVariantGenotype.isPhased() && otherVariantGenotype.isHet() + && thisVariantGenotype.equals(otherVariantGenotype); } - /** - * PS1 "Same amino acid change as a previously established pathogenic variant regardless of nucleotide change" - */ - private void assignPS1(AcmgEvidence.Builder acmgEvidenceBuilder, VariantEvaluation variantEvaluation, VariantEffect variantEffect, ClinVarData clinVarData) { - ClinVarData.ClinSig primaryInterpretation = clinVarData.getPrimaryInterpretation(); - if (isMissense(variantEffect) && isPathOrLikelyPath(primaryInterpretation) && clinVarData.starRating() >= 2) { - // PS1 "Same amino acid change as a previously established pathogenic variant regardless of nucleotide change" - // TODO: can't quite do this fully as also need to know others with same AA change, if not identical - List annotations = variantEvaluation.getTranscriptAnnotations(); - if (!annotations.isEmpty()) { - TranscriptAnnotation anno = annotations.get(0); - if (anno.getHgvsProtein().equals(clinVarData.getHgvsProtein()) && !anno.getHgvsCdna().equals(clinVarData.getHgvsCdna())) { - acmgEvidenceBuilder.add(PS1); + + // PM1 "Located in a mutational hot spot and/or critical and well-established functional domain (e.g., active site of an enzyme) without benign variation" + private void assignPM1(AcmgEvidence.Builder acmgEvidenceBuilder, VariantEvaluation variantEvaluation, Map localClinVarData) { + // TODO - need UniProt domain / site info and clinvar counts + // can upgrade to STRONG + // https://www.cell.com/ajhg/fulltext/S0002-9297(22)00461-X suggests to limit the combined evidence from PM1 and PP3 to strong + int pathCount = 0; + int vusCount = 0; + int benignCount = 0; + for (var entry : localClinVarData.entrySet()) { + ClinVarData clinVarData = entry.getValue(); + ClinVarData.ClinSig primaryInterpretation = clinVarData.getPrimaryInterpretation(); + if (isMissenseOrInframeIndel(clinVarData.getVariantEffect())) { + if (isPathOrLikelyPath(primaryInterpretation)) { + pathCount++; + } + if (primaryInterpretation == ClinVarData.ClinSig.UNCERTAIN_SIGNIFICANCE) { + vusCount++; + } + if (isBenignOrLikelyBenign(primaryInterpretation)) { + benignCount++; + } + } + } + logger.debug("PM1 evidence for region {} {}:{}-{} Paths: {} VUSs: {} Benigns: {}", variantEvaluation.getGenomeAssembly(), variantEvaluation.contigId(), variantEvaluation.start() - 25, variantEvaluation.end() + 25, pathCount, vusCount, benignCount); + if (pathCount >= 4 && benignCount == 0) { + // could do funkier thing to score other path variants by severity/star rating and distance to variant + if (pathCount > vusCount) { + acmgEvidenceBuilder.add(PM1); + logger.debug("{} -> {}", variantEvaluation, PM1); + } else { + acmgEvidenceBuilder.add(PM1, Evidence.SUPPORTING); + logger.debug("{} -> {}_Supporting", variantEvaluation, PM1); + } + } + } + + private void assignPS1PM5(AcmgEvidence.Builder acmgEvidenceBuilder, VariantEvaluation variantEvaluation, Map localClinVarData) { + if (variantEvaluation.getTranscriptAnnotations().isEmpty()) { + return; + } + String variantProteinChange = variantEvaluation.getTranscriptAnnotations().get(0).getHgvsProtein(); + Matcher queryVariantMatcher = MISSENSE_HGVS_P.matcher(variantProteinChange); + boolean matches = queryVariantMatcher.matches(); + String aaRefPos = matches ? queryVariantMatcher.group("refPos") : ""; + String aaAlt = matches ? queryVariantMatcher.group("alt") : ""; + for (var entry : localClinVarData.entrySet()) { + ClinVarData clinVarData = entry.getValue(); + ClinVarData.ClinSig primaryInterpretation = clinVarData.getPrimaryInterpretation(); + if (isMissenseOrInframeIndel(clinVarData.getVariantEffect()) && isPathOrLikelyPath(primaryInterpretation)) { + GenomicVariant clinVarVariant = entry.getKey(); + if (Math.abs(clinVarVariant.distanceTo(variantEvaluation)) <= 2 && GenomicVariant.compare(clinVarVariant, variantEvaluation) != 0) { + // within codon so check for same AA change or different AA change + Matcher clinvarMatcher = MISSENSE_HGVS_P.matcher(clinVarData.getHgvsProtein()); + if (clinvarMatcher.matches()) { + String clnAaRefPos = clinvarMatcher.group("refPos"); + String clnAaAlt = clinvarMatcher.group("alt"); + if (aaRefPos.equals(clnAaRefPos)) { + if (aaAlt.equals(clnAaAlt)) { + // PS1 "Same amino acid change as a previously established pathogenic variant regardless of nucleotide change" + Evidence evidence; + if (clinVarData.starRating() >= 2) { + evidence = Evidence.STRONG; + } else if (clinVarData.starRating() == 1) { + evidence = Evidence.MODERATE; + } else { + evidence = Evidence.SUPPORTING; + } + logger.debug("{} -> {}_{}", clinVarData.getHgvsProtein(), PS1, evidence); + acmgEvidenceBuilder.add(PS1, evidence); + } else { + Evidence evidence = clinVarData.starRating() >= 2 ? Evidence.MODERATE : Evidence.SUPPORTING; + logger.debug("{} -> {}_{}", clinVarData.getHgvsProtein(), PM5, evidence); + // PM5 "Novel missense change at an amino acid residue where a different missense change determined to be pathogenic has been seen before" + acmgEvidenceBuilder.add(PM5, evidence); + } + } + } } } } } - private boolean isMissense(VariantEffect variantEffect) { - return variantEffect == VariantEffect.MISSENSE_VARIANT; + private boolean isMissenseOrInframeIndel(VariantEffect variantEffect) { + return variantEffect == VariantEffect.MISSENSE_VARIANT || variantEffect == VariantEffect.INFRAME_DELETION || variantEffect == VariantEffect.INFRAME_INSERTION; } /** @@ -351,8 +438,12 @@ private void assignPP5(AcmgEvidence.Builder acmgEvidenceBuilder, ClinVarData cli boolean pathOrLikelyPath = isPathOrLikelyPath(primaryInterpretation); if (pathOrLikelyPath && clinVarData.starRating() == 1) { acmgEvidenceBuilder.add(PP5); - } else if (pathOrLikelyPath && clinVarData.starRating() >= 2) { + } else if (pathOrLikelyPath && clinVarData.starRating() == 2) { + // multiple submitters, no conflicts acmgEvidenceBuilder.add(PP5, Evidence.STRONG); + } else if (pathOrLikelyPath && clinVarData.starRating() >= 3) { + // expert panel or practice guidelines + acmgEvidenceBuilder.add(PP5, Evidence.VERY_STRONG); } } @@ -415,14 +506,6 @@ private boolean possibleDeNovo(SampleGenotype ancestorGenotype, SampleGenotype p return (ancestorGenotype.isNoCall() || ancestorGenotype.isHomRef()) && (probandGenotype.isHet() || probandGenotype.isHomAlt()); } - /** - * PM1 "Located in a mutational hot spot and/or critical and well-established functional domain (e.g., active site of an enzyme) without benign variation" - */ - private void assignPM1(Map acmgEvidenceBuilder) { - // TODO - need UniProt domain / site info and clinvar counts - // can upgrade to STRONG - // https://www.cell.com/ajhg/fulltext/S0002-9297(22)00461-X suggests to limit the combined evidence from PM1 and PP3 to strong - } /** * PM2 "Absent from controls (or at extremely low frequency if recessive) in Exome Sequencing Project, 1000 Genomes @@ -468,8 +551,8 @@ private void assignPM6(AcmgEvidence.Builder acmgEvidenceBuilder, VariantEvaluati // novo variant found by exome sequencing in a child with // nonspecific features such as developmental delay. if (hasCompatibleDiseaseMatches && modeOfInheritance.isDominant() - && contributingVariants.contains(variantEvaluation) - && variantEvaluation.getSampleGenotypes().size() == 1 && variantEvaluation.getSampleGenotype(probandId).isHet()) { + && contributingVariants.contains(variantEvaluation) + && variantEvaluation.getSampleGenotypes().size() == 1 && variantEvaluation.getSampleGenotype(probandId).isHet()) { // making an assumption that this could be a de novo acmgEvidenceBuilder.add(PM6); } @@ -555,20 +638,16 @@ private void assignEnsembleBasedPP3BP4Classification(AcmgEvidence.Builder acmgEv } private boolean isPathogenic(PathogenicityScore pathogenicityScore) { - if (pathogenicityScore instanceof SiftScore) { - SiftScore score = (SiftScore) pathogenicityScore; + if (pathogenicityScore instanceof SiftScore score) { return score.getRawScore() < SiftScore.SIFT_THRESHOLD; } - if (pathogenicityScore instanceof MutationTasterScore) { - MutationTasterScore score = (MutationTasterScore) pathogenicityScore; + if (pathogenicityScore instanceof MutationTasterScore score) { return score.getScore() > MutationTasterScore.MTASTER_THRESHOLD; } - if (pathogenicityScore instanceof PolyPhenScore) { - PolyPhenScore score = (PolyPhenScore) pathogenicityScore; + if (pathogenicityScore instanceof PolyPhenScore score) { return score.getScore() > PolyPhenScore.POLYPHEN_PROB_DAMAGING_THRESHOLD; } - if (pathogenicityScore instanceof CaddScore) { - CaddScore score = (CaddScore) pathogenicityScore; + if (pathogenicityScore instanceof CaddScore score) { // 95-99% most deleterious. return score.getRawScore() >= 13.0f; } @@ -598,7 +677,7 @@ private void assignBS4(AcmgEvidence.Builder acmgEvidenceBuilder, VariantEvaluati .filter(Individual::isAffected) .filter(individual -> !individual.getId().equals(probandId)) .filter(individual -> individual.getFamilyId().equals(proband.getFamilyId())) - .collect(Collectors.toList()); + .toList(); boolean segregatesWithAffectedInFamily = true; SampleGenotype probandGenotype = variantEvaluation.getSampleGenotype(probandId); if (!affectedFamilyMembers.isEmpty()) { diff --git a/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/util/acmg/AcmgAssignmentCalculator.java b/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/util/acmg/AcmgAssignmentCalculator.java index 33a1a0199..01b414804 100644 --- a/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/util/acmg/AcmgAssignmentCalculator.java +++ b/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/analysis/util/acmg/AcmgAssignmentCalculator.java @@ -61,7 +61,7 @@ public List calculateAcmgAssignments(ModeOfInheritance modeOfInh return contributingVariants.stream() .sorted(VariantEvaluation::compareByRank) .map(assignVariantAcmg(modeOfInheritance, gene, contributingVariants, knownDiseases, compatibleDiseaseMatches, disease)) - .collect(Collectors.toUnmodifiableList()); + .toList(); } private Disease findTopDiseaseMatch(List> compatibleDiseaseMatches) { @@ -75,7 +75,7 @@ private Disease findTopDiseaseMatch(List> compatibl private List findKnownDiseasesCompatibleWithMoi(ModeOfInheritance modeOfInheritance, Gene gene) { return gene.getAssociatedDiseases().stream() .filter(disease -> disease.getInheritanceMode().isCompatibleWith(modeOfInheritance)) - .collect(Collectors.toUnmodifiableList()); + .toList(); } private Function assignVariantAcmg(ModeOfInheritance modeOfInheritance, Gene gene, List contributingVariants, List knownDiseases, List> compatibleDiseaseMatches, Disease disease) { diff --git a/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/genome/GenomeAnalysisServiceImpl.java b/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/genome/GenomeAnalysisServiceImpl.java index 5f920e903..84743ff79 100644 --- a/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/genome/GenomeAnalysisServiceImpl.java +++ b/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/genome/GenomeAnalysisServiceImpl.java @@ -23,10 +23,14 @@ import org.monarchinitiative.exomiser.core.model.*; import org.monarchinitiative.exomiser.core.model.frequency.FrequencyData; import org.monarchinitiative.exomiser.core.model.frequency.FrequencySource; +import org.monarchinitiative.exomiser.core.model.pathogenicity.ClinVarData; import org.monarchinitiative.exomiser.core.model.pathogenicity.PathogenicityData; import org.monarchinitiative.exomiser.core.model.pathogenicity.PathogenicitySource; +import org.monarchinitiative.svart.GenomicInterval; +import org.monarchinitiative.svart.GenomicVariant; import java.util.List; +import java.util.Map; import java.util.Set; /** @@ -91,4 +95,19 @@ public FrequencyData getVariantFrequencyData(Variant variant, Set pathogenicitySources) { return variantDataService.getVariantPathogenicityData(variant, pathogenicitySources); } + + @Override + public ClinVarData getClinVarData(Variant variant) { + return variantDataService.getClinVarData(variant); + } + + @Override + public ClinVarData getClinVarData(GenomicVariant genomicVariant) { + return variantDataService.getClinVarData(genomicVariant); + } + + @Override + public Map findClinVarRecordsOverlappingInterval(GenomicInterval genomicInterval) { + return variantDataService.findClinVarRecordsOverlappingInterval(genomicInterval); + } } diff --git a/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/genome/VariantDataService.java b/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/genome/VariantDataService.java index d59011f75..4cf6ed64a 100644 --- a/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/genome/VariantDataService.java +++ b/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/genome/VariantDataService.java @@ -25,6 +25,7 @@ */ package org.monarchinitiative.exomiser.core.genome; +import org.monarchinitiative.exomiser.core.genome.dao.ClinVarDao; import org.monarchinitiative.exomiser.core.model.Variant; import org.monarchinitiative.exomiser.core.model.frequency.FrequencyData; import org.monarchinitiative.exomiser.core.model.frequency.FrequencySource; @@ -37,7 +38,7 @@ * * @author Jules Jacobsen */ -public interface VariantDataService { +public interface VariantDataService extends ClinVarDao { public boolean variantIsWhiteListed(Variant variant); diff --git a/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/genome/VariantDataServiceImpl.java b/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/genome/VariantDataServiceImpl.java index d935e1fc0..32e925266 100644 --- a/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/genome/VariantDataServiceImpl.java +++ b/exomiser-core/src/main/java/org/monarchinitiative/exomiser/core/genome/VariantDataServiceImpl.java @@ -30,6 +30,8 @@ import org.monarchinitiative.exomiser.core.model.pathogenicity.PathogenicityData; import org.monarchinitiative.exomiser.core.model.pathogenicity.PathogenicityScore; import org.monarchinitiative.exomiser.core.model.pathogenicity.PathogenicitySource; +import org.monarchinitiative.svart.GenomicInterval; +import org.monarchinitiative.svart.GenomicVariant; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -180,6 +182,21 @@ private void addAllWantedScores(Set pathogenicitySources, P } } + @Override + public ClinVarData getClinVarData(Variant variant) { + return clinVarDao.getClinVarData(variant); + } + + @Override + public ClinVarData getClinVarData(GenomicVariant genomicVariant) { + return clinVarDao.getClinVarData(genomicVariant); + } + + @Override + public Map findClinVarRecordsOverlappingInterval(GenomicInterval genomicInterval) { + return clinVarDao.findClinVarRecordsOverlappingInterval(genomicInterval); + } + public static Builder builder() { return new Builder(); } diff --git a/exomiser-core/src/test/java/org/monarchinitiative/exomiser/core/analysis/util/RawScoreGeneScorerTest.java b/exomiser-core/src/test/java/org/monarchinitiative/exomiser/core/analysis/util/RawScoreGeneScorerTest.java index ecfe8b20f..29d6ca5dd 100644 --- a/exomiser-core/src/test/java/org/monarchinitiative/exomiser/core/analysis/util/RawScoreGeneScorerTest.java +++ b/exomiser-core/src/test/java/org/monarchinitiative/exomiser/core/analysis/util/RawScoreGeneScorerTest.java @@ -32,9 +32,14 @@ import htsjdk.variant.variantcontext.GenotypeType; import htsjdk.variant.variantcontext.VariantContext; import org.junit.jupiter.api.Test; +import org.monarchinitiative.exomiser.core.analysis.util.acmg.Acmg2015EvidenceAssigner; +import org.monarchinitiative.exomiser.core.analysis.util.acmg.Acmg2020PointsBasedClassifier; +import org.monarchinitiative.exomiser.core.analysis.util.acmg.AcmgAssignmentCalculator; +import org.monarchinitiative.exomiser.core.analysis.util.acmg.AcmgEvidenceAssigner; import org.monarchinitiative.exomiser.core.filters.FilterResult; import org.monarchinitiative.exomiser.core.filters.FilterType; import org.monarchinitiative.exomiser.core.genome.TestFactory; +import org.monarchinitiative.exomiser.core.genome.TestVariantDataService; import org.monarchinitiative.exomiser.core.model.*; import org.monarchinitiative.exomiser.core.model.Pedigree.Individual; import org.monarchinitiative.exomiser.core.model.Pedigree.Individual.Sex; @@ -60,6 +65,7 @@ public class RawScoreGeneScorerTest { private static final FilterResult FAIL_FREQUENCY = FilterResult.fail(FilterType.FREQUENCY_FILTER); private static final FilterResult PASS_PATHOGENICITY = FilterResult.pass(FilterType.PATHOGENICITY_FILTER); private static final FilterResult FAIL_PATHOGENICITY = FilterResult.fail(FilterType.PATHOGENICITY_FILTER); + private final CombinedScorePvalueCalculator noOpCombinedScorePvalueCalculator = CombinedScorePvalueCalculator.noOpCombinedScorePvalueCalculator(); private Gene newGene(VariantEvaluation... variantEvaluations) { Gene gene = new Gene("TEST1", 1234); @@ -119,7 +125,8 @@ private List scoreGene(Gene gene, ModeOfInheritance modeOfInheritance private List scoreGene(Gene gene, ModeOfInheritance modeOfInheritance, String probandSample, Pedigree pedigree, Sex sex) { InheritanceModeAnnotator inheritanceModeAnnotator = new InheritanceModeAnnotator(pedigree, InheritanceModeOptions .defaultForModes(modeOfInheritance)); - RawScoreGeneScorer instance = new RawScoreGeneScorer(probandSample, sex, inheritanceModeAnnotator); + AcmgAssignmentCalculator acmgAssignmentCalculator = acmgAssignmentCalculator(probandSample, pedigree); + RawScoreGeneScorer instance = new RawScoreGeneScorer(probandSample, sex, inheritanceModeAnnotator, noOpCombinedScorePvalueCalculator, acmgAssignmentCalculator); return instance.scoreGene().apply(gene); } @@ -137,12 +144,18 @@ private List scoreGene(Gene gene, InheritanceModeOptions inheritanceM private RawScoreGeneScorer getInstance(InheritanceModeOptions inheritanceModeOptions, String probandSample, Pedigree pedigree) { InheritanceModeAnnotator inheritanceModeAnnotator = new InheritanceModeAnnotator(pedigree, inheritanceModeOptions); - return new RawScoreGeneScorer(probandSample, Sex.UNKNOWN, inheritanceModeAnnotator); + AcmgAssignmentCalculator acmgAssignmentCalculator = acmgAssignmentCalculator(probandSample, pedigree); + return new RawScoreGeneScorer(probandSample, Sex.UNKNOWN, inheritanceModeAnnotator, noOpCombinedScorePvalueCalculator, acmgAssignmentCalculator); } private RawScoreGeneScorer getInstance(InheritanceModeOptions inheritanceModeOptions, Sex sex, String probandSample, Pedigree pedigree) { InheritanceModeAnnotator inheritanceModeAnnotator = new InheritanceModeAnnotator(pedigree, inheritanceModeOptions); - return new RawScoreGeneScorer(probandSample, sex, inheritanceModeAnnotator); + AcmgAssignmentCalculator acmgAssignmentCalculator = acmgAssignmentCalculator(probandSample, pedigree); + return new RawScoreGeneScorer(probandSample, sex, inheritanceModeAnnotator, noOpCombinedScorePvalueCalculator, acmgAssignmentCalculator); + } + + private static AcmgAssignmentCalculator acmgAssignmentCalculator(String probandSample, Pedigree pedigree) { + return new AcmgAssignmentCalculator(new Acmg2015EvidenceAssigner(probandSample, pedigree, TestVariantDataService.stub()), new Acmg2020PointsBasedClassifier()); } @Test @@ -714,12 +727,13 @@ public void testGenesAreRankedAccordingToScore() { InheritanceModeAnnotator inheritanceModeAnnotator = new InheritanceModeAnnotator(Pedigree.justProband("Nemo"), InheritanceModeOptions .empty()); - RawScoreGeneScorer instance = new RawScoreGeneScorer("Nemo", Sex.UNKNOWN, inheritanceModeAnnotator); - instance.scoreGenes(genes); + AcmgAssignmentCalculator acmgAssignmentCalculator = acmgAssignmentCalculator("Nemo", inheritanceModeAnnotator.getPedigree()); + GeneScorer instance = new RawScoreGeneScorer("Nemo", Sex.UNKNOWN, inheritanceModeAnnotator, noOpCombinedScorePvalueCalculator, acmgAssignmentCalculator); - assertThat(genes.indexOf(first), equalTo(0)); - assertThat(genes.indexOf(middle), equalTo(1)); - assertThat(genes.indexOf(last), equalTo(2)); + List scoredGenes = instance.scoreGenes(genes); + assertThat(scoredGenes.indexOf(first), equalTo(0)); + assertThat(scoredGenes.indexOf(middle), equalTo(1)); + assertThat(scoredGenes.indexOf(last), equalTo(2)); } ///Priority and Combined score tests diff --git a/exomiser-core/src/test/java/org/monarchinitiative/exomiser/core/analysis/util/acmg/Acmg2015EvidenceAssignerTest.java b/exomiser-core/src/test/java/org/monarchinitiative/exomiser/core/analysis/util/acmg/Acmg2015EvidenceAssignerTest.java index 4f27ff4bd..79f5d7853 100644 --- a/exomiser-core/src/test/java/org/monarchinitiative/exomiser/core/analysis/util/acmg/Acmg2015EvidenceAssignerTest.java +++ b/exomiser-core/src/test/java/org/monarchinitiative/exomiser/core/analysis/util/acmg/Acmg2015EvidenceAssignerTest.java @@ -27,6 +27,8 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.monarchinitiative.exomiser.core.genome.TestFactory; +import org.monarchinitiative.exomiser.core.genome.TestGenomeDataService; +import org.monarchinitiative.exomiser.core.genome.TestVariantDataService; import org.monarchinitiative.exomiser.core.model.*; import org.monarchinitiative.exomiser.core.model.Pedigree.Individual.Status; import org.monarchinitiative.exomiser.core.model.frequency.Frequency; @@ -53,14 +55,19 @@ class Acmg2015EvidenceAssignerTest { + + private Acmg2015EvidenceAssigner acmgEvidenceAssigner(String probandId, Pedigree pedigree) { + return new Acmg2015EvidenceAssigner(probandId, pedigree, TestVariantDataService.stub()); + } + @Test void throwsExceptionWithMismatchedIds() { - assertThrows(IllegalArgumentException.class, () -> new Acmg2015EvidenceAssigner("Zaphod", justProband("Ford", MALE))); + assertThrows(IllegalArgumentException.class, () -> acmgEvidenceAssigner("Zaphod", justProband("Ford", MALE))); } - + @Test void testAssignsPVS1() { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", justProband("proband", MALE)); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", justProband("proband", MALE)); // https://www.ncbi.nlm.nih.gov/clinvar/variation/484600/ 3* PATHOGENIC variant - reviewed by expert panel // requires variant to be on a transcript predicted to undergo NMD in a LoF-intolerant gene for full PVS1 TranscriptAnnotation transcriptAnnotation = TranscriptAnnotation.builder() @@ -103,7 +110,7 @@ void testAssignsPVS1() { "UNKNOWN, X_DOMINANT, X_DOMINANT, true", }) void testAssignsPVS1(Pedigree.Individual.Sex probandSex, InheritanceMode diseaseInheritanceMode, ModeOfInheritance modeOfInheritance, boolean expectPvs1) { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", justProband("proband", probandSex)); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", justProband("proband", probandSex)); // https://www.ncbi.nlm.nih.gov/clinvar/variation/484600/ 3* PATHOGENIC variant - reviewed by expert panel TranscriptAnnotation transcriptAnnotation = TranscriptAnnotation.builder() .variantEffect(VariantEffect.START_LOST) @@ -130,7 +137,7 @@ void testAssignsPS2() { Individual mother = Individual.builder().id("mother").sex(FEMALE).status(Status.UNAFFECTED).build(); Individual father = Individual.builder().id("father").sex(MALE).status(Status.UNAFFECTED).build(); Pedigree pedigree = Pedigree.of(proband, mother, father); - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", pedigree); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", pedigree); // https://www.ncbi.nlm.nih.gov/clinvar/variation/484600/ 3* PATHOGENIC variant - reviewed by expert panel VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 89624227, "A", "G") // n.b. PTEN is a haploinsufficient gene @@ -159,7 +166,7 @@ void testAssignsPS2_hasFamilyHistory() { Individual mother = Individual.builder().id("mother").sex(FEMALE).status(Status.UNAFFECTED).build(); Individual father = Individual.builder().id("father").sex(MALE).status(Status.UNAFFECTED).build(); Pedigree pedigree = Pedigree.of(proband, mother, father); - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", pedigree); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", pedigree); // https://www.ncbi.nlm.nih.gov/clinvar/variation/484600/ 3* PATHOGENIC variant - reviewed by expert panel VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 89624227, "A", "G") // n.b. PTEN is a haploinsufficient gene @@ -184,7 +191,7 @@ void testAssignsPS2_hasFamilyHistory() { @Test void testAssignsPM2AutosomalDominant() { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", Pedigree.empty()); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", Pedigree.empty()); VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 12345, "A", "G") // n.b. missing frequency data - will trigger PM2 .frequencyData(FrequencyData.empty()) @@ -199,7 +206,7 @@ void testAssignsPM2AutosomalDominant() { @Test void testAssignsPM2AutosomalDominantAllowsPresenceOfLocalFrequency() { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", Pedigree.empty()); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", Pedigree.empty()); VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 12345, "A", "G") // n.b. missing frequency data APART FROM LOCAL - will trigger PM2 .frequencyData(FrequencyData.of(Frequency.of(FrequencySource.LOCAL, 0.019f))) @@ -214,7 +221,7 @@ void testAssignsPM2AutosomalDominantAllowsPresenceOfLocalFrequency() { @Test void testAssignsPM2AutosomalRecessive() { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", Pedigree.empty()); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", Pedigree.empty()); VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 12345, "A", "G") // n.b. low frequency for AR - will trigger PM2 .frequencyData(FrequencyData.of(Frequency.of(FrequencySource.GNOMAD_E_EAS, 0.009f))) @@ -227,7 +234,7 @@ void testAssignsPM2AutosomalRecessive() { @Test void testVariantNeedNotBeInGeneWithKnownDiseaseAssociationForAcmgCriteriaToBeAssigned() { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", Pedigree.empty()); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", Pedigree.empty()); VariantEvaluation variantEvaluation = TestFactory.variantBuilder(1, 12345, "A", "G") // n.b. missing frequency data - should trigger PM2 .frequencyData(FrequencyData.of()) @@ -241,7 +248,7 @@ void testVariantNeedNotBeInGeneWithKnownDiseaseAssociationForAcmgCriteriaToBeAss @Test void testAssignsPM3() { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", null); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", null); // https://www.ncbi.nlm.nih.gov/clinvar/variation/484600/ 3* PATHOGENIC variant - reviewed by expert panel VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 89000000, "A", "G") // n.b. PTEN is a haploinsufficient gene @@ -276,7 +283,7 @@ void testAssignsPM3() { @Test void testAssignsBP2_InCisWithPathAR() { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", Pedigree.empty()); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", Pedigree.empty()); // https://www.ncbi.nlm.nih.gov/clinvar/variation/484600/ 3* PATHOGENIC variant - reviewed by expert panel VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 89000000, "A", "G") // n.b. has frequency data - will not trigger PM2 @@ -303,7 +310,7 @@ void testAssignsBP2_InCisWithPathAR() { @Test void testAssignsBP2_InTransWithPathAD() { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", Pedigree.empty()); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", Pedigree.empty()); // https://www.ncbi.nlm.nih.gov/clinvar/variation/484600/ 3* PATHOGENIC variant - reviewed by expert panel VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 89000000, "A", "G") // n.b. PTEN is a haploinsufficient gene @@ -338,7 +345,7 @@ void testAssignsBP2_InTransWithPathAD() { @Test void testAssignsPM4() { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", justProband("proband", MALE)); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", justProband("proband", MALE)); VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 89624227, "A", "G") .geneSymbol("MUC6") .frequencyData(FrequencyData.of(Frequency.of(FrequencySource.EXAC_AMERICAN, 0.1f))) // prevent PM2 assignment @@ -351,7 +358,7 @@ void testAssignsPM4() { @Test void testAssignsPM4_NotAssignedPM4WhenPVS1Present() { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", justProband("proband", MALE)); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", justProband("proband", MALE)); TranscriptAnnotation startLostAnnotation = TranscriptAnnotation.builder() .geneSymbol("PTEN") @@ -379,7 +386,7 @@ class ComputationalEvidence { @Test void testAssignsPP3() { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", justProband("proband", MALE)); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", justProband("proband", MALE)); VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 89624227, "A", "G") .geneSymbol("PTEN") .frequencyData(FrequencyData.of(Frequency.of(FrequencySource.EXAC_AMERICAN, 0.1f))) // prevent PM2 assignment @@ -400,7 +407,7 @@ void testAssignsPP3() { "REVEL, 1.0f, PP3, STRONG" }) void testAssignsPP3_singleScoreIsInsufficientUnlessItsRevel(PathogenicitySource pathogenicitySource, float pathogenicityScore, AcmgCriterion acmgCriterion, Evidence evidence) { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", justProband("proband", MALE)); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", justProband("proband", MALE)); VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 89624227, "A", "G") .geneSymbol("PTEN") .frequencyData(FrequencyData.of(Frequency.of(FrequencySource.EXAC_AMERICAN, 0.1f))) // prevent PM2 assignment @@ -416,7 +423,7 @@ void testAssignsPP3_singleScoreIsInsufficientUnlessItsRevel(PathogenicitySource @Test void testAssignsPP3_majorityMustBePath() { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", justProband("proband", MALE)); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", justProband("proband", MALE)); VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 89624227, "A", "G") .geneSymbol("PTEN") .frequencyData(FrequencyData.of(Frequency.of(FrequencySource.EXAC_AMERICAN, 0.1f))) // prevent PM2 assignment @@ -434,7 +441,7 @@ void testAssignsPP3_majorityMustBePath() { @Test void testPP3andPM4_majorityMustBePathOrBenign() { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", justProband("proband", MALE)); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", justProband("proband", MALE)); VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 89624227, "A", "G") .geneSymbol("PTEN") .frequencyData(FrequencyData.of(Frequency.of(FrequencySource.EXAC_AMERICAN, 0.1f))) // prevent PM2 assignment @@ -453,7 +460,7 @@ void testPP3andPM4_majorityMustBePathOrBenign() { @Test void testAssignsBP4() { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", justProband("proband", MALE)); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", justProband("proband", MALE)); VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 89624227, "A", "G") .geneSymbol("PTEN") .frequencyData(FrequencyData.of(Frequency.of(FrequencySource.EXAC_AMERICAN, 0.1f))) // prevent PM2 assignment @@ -474,7 +481,7 @@ void testAssignsBP4() { "REVEL, 0.0f, BP4, VERY_STRONG" }) void testAssignsBP4_singleScoreIsInsufficientIfNotRevel(PathogenicitySource pathogenicitySource, float pathogenicityScore, AcmgCriterion acmgCriterion, Evidence evidence) { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", justProband("proband", MALE)); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", justProband("proband", MALE)); VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 89624227, "A", "G") .geneSymbol("PTEN") .frequencyData(FrequencyData.of(Frequency.of(FrequencySource.EXAC_AMERICAN, 0.1f))) // prevent PM2 assignment @@ -489,7 +496,7 @@ void testAssignsBP4_singleScoreIsInsufficientIfNotRevel(PathogenicitySource path } void testAssignsBP4_majorityMustBeBenign() { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", justProband("proband", MALE)); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", justProband("proband", MALE)); VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 89624227, "A", "G") .geneSymbol("PTEN") .frequencyData(FrequencyData.of(Frequency.of(FrequencySource.EXAC_AMERICAN, 0.1f))) // prevent PM2 assignment @@ -517,7 +524,7 @@ void testAssignsBP4_majorityMustBeBenign() { "0.003f, BP4, VERY_STRONG", }) public void testRevelOverridesAllOtherScores(float revelScore, AcmgCriterion acmgCriterion, Evidence evidence) { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", justProband("proband", MALE)); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", justProband("proband", MALE)); VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 89624227, "A", "G") .geneSymbol("PTEN") .frequencyData(FrequencyData.of(Frequency.of(FrequencySource.EXAC_AMERICAN, 0.1f))) // prevent PM2 assignment @@ -540,7 +547,7 @@ public void testRevelOverridesAllOtherScores(float revelScore, AcmgCriterion acm // PP4 @Test void testAssignsPP4() { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", justProband("proband", MALE)); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", justProband("proband", MALE)); VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 89624227, "A", "G") .geneSymbol("PTEN") .frequencyData(FrequencyData.of(Frequency.of(FrequencySource.EXAC_AMERICAN, 0.1f))) // prevent PM2 assignment @@ -563,11 +570,11 @@ class ClinicalEvidence { value = { "criteria provided, single submitter; SUPPORTING", "criteria provided, multiple submitters, no conflicts; STRONG", - "reviewed by expert panel; STRONG", - "practice guideline; STRONG", + "reviewed by expert panel; VERY_STRONG", + "practice guideline; VERY_STRONG", }) void testAssignsPP5(String reviewStatus, AcmgCriterion.Evidence evidence) { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", Pedigree.empty()); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", Pedigree.empty()); VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 89000000, "A", "G") // n.b. PTEN is a haploinsufficient gene .geneSymbol("PTEN") @@ -596,7 +603,7 @@ void testAssignsPP5(String reviewStatus, AcmgCriterion.Evidence evidence) { "practice guideline; STRONG", }) void testAssignsBP6(String reviewStatus, AcmgCriterion.Evidence evidence) { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", Pedigree.empty()); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", Pedigree.empty()); // https://www.ncbi.nlm.nih.gov/clinvar/variation/127667/ VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 89622915, "A", "G") // n.b. PTEN is a haploinsufficient gene @@ -619,7 +626,7 @@ void testAssignsBP6(String reviewStatus, AcmgCriterion.Evidence evidence) { @Test void testAssignsBA1() { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", justProband("proband", MALE)); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", justProband("proband", MALE)); VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 89624227, "A", "G") .geneSymbol("PTEN") // high allele freq - triggers BA1 assignment @@ -633,7 +640,7 @@ void testAssignsBA1() { @Test void testDoesntAssignBA1ForException() { - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", justProband("proband", MALE)); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", justProband("proband", MALE)); // NM_004004.6:c.109G>A https://www.ncbi.nlm.nih.gov/clinvar/variation/17023/ VariantEvaluation variantEvaluation = TestFactory.variantBuilder(3, 128598490, "C", "CTAAG") .geneSymbol("GJB2") @@ -655,7 +662,7 @@ void testAssignsBS4() { Individual mother = Individual.builder().id("mother").sex(FEMALE).status(Status.AFFECTED).build(); Individual father = Individual.builder().id("father").sex(MALE).status(Status.UNAFFECTED).build(); Pedigree pedigree = Pedigree.of(proband, mother, father); - Acmg2015EvidenceAssigner instance = new Acmg2015EvidenceAssigner("proband", pedigree); + Acmg2015EvidenceAssigner instance = acmgEvidenceAssigner("proband", pedigree); // https://www.ncbi.nlm.nih.gov/clinvar/variation/484600/ 3* PATHOGENIC variant - reviewed by expert panel VariantEvaluation variantEvaluation = TestFactory.variantBuilder(10, 89624227, "A", "G") // n.b. PTEN is a haploinsufficient gene diff --git a/exomiser-core/src/test/java/org/monarchinitiative/exomiser/core/analysis/util/acmg/AcmgAssignmentCalculatorTest.java b/exomiser-core/src/test/java/org/monarchinitiative/exomiser/core/analysis/util/acmg/AcmgAssignmentCalculatorTest.java index e48a0f1a9..05a47eb8a 100644 --- a/exomiser-core/src/test/java/org/monarchinitiative/exomiser/core/analysis/util/acmg/AcmgAssignmentCalculatorTest.java +++ b/exomiser-core/src/test/java/org/monarchinitiative/exomiser/core/analysis/util/acmg/AcmgAssignmentCalculatorTest.java @@ -26,6 +26,7 @@ import org.monarchinitiative.exomiser.core.filters.FilterResult; import org.monarchinitiative.exomiser.core.filters.FilterType; import org.monarchinitiative.exomiser.core.genome.TestFactory; +import org.monarchinitiative.exomiser.core.genome.TestVariantDataService; import org.monarchinitiative.exomiser.core.model.Gene; import org.monarchinitiative.exomiser.core.model.TranscriptAnnotation; import org.monarchinitiative.exomiser.core.model.VariantEvaluation; @@ -98,11 +99,11 @@ void calculatePathAcmgAssignments() { .add(AcmgCriterion.PM2, AcmgCriterion.Evidence.SUPPORTING) .add(AcmgCriterion.PP3, AcmgCriterion.Evidence.STRONG) .add(AcmgCriterion.PP4) - .add(AcmgCriterion.PP5, AcmgCriterion.Evidence.STRONG) + .add(AcmgCriterion.PP5, AcmgCriterion.Evidence.VERY_STRONG) .build(); AcmgAssignment acmgAssignment = AcmgAssignment.of(variantEvaluation, gene.getGeneIdentifier(), ModeOfInheritance.AUTOSOMAL_DOMINANT, cowdenSyndrome, acmgEvidence, AcmgClassification.PATHOGENIC); - AcmgEvidenceAssigner acmgEvidenceAssigner = new Acmg2015EvidenceAssigner("proband", justProband("proband", MALE)); + AcmgEvidenceAssigner acmgEvidenceAssigner = new Acmg2015EvidenceAssigner("proband", justProband("proband", MALE), TestVariantDataService.stub()); AcmgAssignmentCalculator instance = new AcmgAssignmentCalculator(acmgEvidenceAssigner, new Acgs2020Classifier()); List acmgAssignments = instance.calculateAcmgAssignments(ModeOfInheritance.AUTOSOMAL_DOMINANT, gene, List.of(variantEvaluation), compatibleDiseaseMatches); assertThat(acmgAssignments, equalTo(List.of(acmgAssignment))); @@ -136,7 +137,7 @@ void calculateVusAcmgAssignments() { AcmgEvidence acmgEvidence = AcmgEvidence.empty(); AcmgAssignment acmgAssignment = AcmgAssignment.of(variantEvaluation, gene.getGeneIdentifier(), ModeOfInheritance.AUTOSOMAL_DOMINANT, cowdenSyndrome, acmgEvidence, AcmgClassification.UNCERTAIN_SIGNIFICANCE); - AcmgEvidenceAssigner acmgEvidenceAssigner = new Acmg2015EvidenceAssigner("proband", justProband("proband", MALE)); + AcmgEvidenceAssigner acmgEvidenceAssigner = new Acmg2015EvidenceAssigner("proband", justProband("proband", MALE), TestVariantDataService.stub()); AcmgAssignmentCalculator instance = new AcmgAssignmentCalculator(acmgEvidenceAssigner, new Acgs2020Classifier()); List acmgAssignments = instance.calculateAcmgAssignments(ModeOfInheritance.AUTOSOMAL_DOMINANT, gene, List.of(variantEvaluation), compatibleDiseaseMatches); assertThat(acmgAssignments, equalTo(List.of(acmgAssignment))); @@ -172,7 +173,7 @@ void calculateBenignAcmgAssignments() { .build(); AcmgAssignment acmgAssignment = AcmgAssignment.of(variantEvaluation, gene.getGeneIdentifier(), ModeOfInheritance.AUTOSOMAL_DOMINANT, cowdenSyndrome, acmgEvidence, AcmgClassification.BENIGN); - AcmgEvidenceAssigner acmgEvidenceAssigner = new Acmg2015EvidenceAssigner("proband", justProband("proband", MALE)); + AcmgEvidenceAssigner acmgEvidenceAssigner = new Acmg2015EvidenceAssigner("proband", justProband("proband", MALE), TestVariantDataService.stub()); AcmgAssignmentCalculator instance = new AcmgAssignmentCalculator(acmgEvidenceAssigner, new Acmg2020PointsBasedClassifier()); List acmgAssignments = instance.calculateAcmgAssignments(ModeOfInheritance.AUTOSOMAL_DOMINANT, gene, List.of(variantEvaluation), compatibleDiseaseMatches); assertThat(acmgAssignments, equalTo(List.of(acmgAssignment))); diff --git a/exomiser-core/src/test/java/org/monarchinitiative/exomiser/core/genome/TestVariantDataService.java b/exomiser-core/src/test/java/org/monarchinitiative/exomiser/core/genome/TestVariantDataService.java index f0d42d1fb..32ce1561b 100644 --- a/exomiser-core/src/test/java/org/monarchinitiative/exomiser/core/genome/TestVariantDataService.java +++ b/exomiser-core/src/test/java/org/monarchinitiative/exomiser/core/genome/TestVariantDataService.java @@ -31,9 +31,12 @@ import org.monarchinitiative.exomiser.core.model.frequency.Frequency; import org.monarchinitiative.exomiser.core.model.frequency.FrequencyData; import org.monarchinitiative.exomiser.core.model.frequency.FrequencySource; +import org.monarchinitiative.exomiser.core.model.pathogenicity.ClinVarData; import org.monarchinitiative.exomiser.core.model.pathogenicity.PathogenicityData; import org.monarchinitiative.exomiser.core.model.pathogenicity.PathogenicityScore; import org.monarchinitiative.exomiser.core.model.pathogenicity.PathogenicitySource; +import org.monarchinitiative.svart.GenomicInterval; +import org.monarchinitiative.svart.GenomicVariant; import java.util.*; import java.util.stream.Collectors; @@ -119,6 +122,21 @@ public PathogenicityData getVariantPathogenicityData(Variant variant, Set findClinVarRecordsOverlappingInterval(GenomicInterval genomicInterval) { + return null; + } + public static Builder builder() { return new Builder(); } @@ -181,6 +199,20 @@ public PathogenicityData getVariantPathogenicityData(Variant variant, Set findClinVarRecordsOverlappingInterval(GenomicInterval genomicInterval) { + return Map.of(); + } } } diff --git a/exomiser-core/src/test/java/org/monarchinitiative/exomiser/core/genome/dao/ClinVarDaoMvStoreTest.java b/exomiser-core/src/test/java/org/monarchinitiative/exomiser/core/genome/dao/ClinVarDaoMvStoreTest.java index db8397bbf..7e55da727 100644 --- a/exomiser-core/src/test/java/org/monarchinitiative/exomiser/core/genome/dao/ClinVarDaoMvStoreTest.java +++ b/exomiser-core/src/test/java/org/monarchinitiative/exomiser/core/genome/dao/ClinVarDaoMvStoreTest.java @@ -6,8 +6,8 @@ import org.h2.mvstore.MVStore; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import org.monarchinitiative.exomiser.core.analysis.util.acmg.Acmg2015EvidenceAssigner; -import org.monarchinitiative.exomiser.core.analysis.util.acmg.Acmg2020PointsBasedClassifier; +import org.monarchinitiative.exomiser.core.analysis.util.GeneConstraints; +import org.monarchinitiative.exomiser.core.analysis.util.acmg.*; import org.monarchinitiative.exomiser.core.genome.GenomeAssembly; import org.monarchinitiative.exomiser.core.genome.JannovarVariantAnnotator; import org.monarchinitiative.exomiser.core.genome.VariantAnnotator; @@ -17,6 +17,8 @@ import org.monarchinitiative.exomiser.core.model.frequency.FrequencyData; import org.monarchinitiative.exomiser.core.model.pathogenicity.ClinVarData; import org.monarchinitiative.exomiser.core.model.pathogenicity.PathogenicityData; +import org.monarchinitiative.exomiser.core.model.pathogenicity.PathogenicityScore; +import org.monarchinitiative.exomiser.core.model.pathogenicity.PathogenicitySource; import org.monarchinitiative.exomiser.core.proto.AlleleProto; import org.monarchinitiative.svart.*; @@ -235,8 +237,9 @@ void manualDataExplorer() { // https://mart.ensembl.org/info/genome/genebuild/canonical.html (see also vitt). Ideally the Jannovar Annotations // should be sorted before being converted to TranscriptAnnotations. This isn't an issue if MANE only // transcripts are being used as these are the only ones available to report on. - GenomicVariant genomicVariant = variant(assembly, "17-10551919-G-C"); // 10-123256215-T-G hg38:10-121496701-T-G + GenomicVariant genomicVariant = variant(assembly, "10-89624227-A-G"); // 10-123256215-T-G hg38:10-121496701-T-G + // 10-89624227-A-G (PTEN) // 10-123247514-C-G : PS1, PM1, PM2_Supporting, PM5_Supporting, PP3, PP5_Strong // 10-123247517-T-G : PS1_Supporting, PM1, PM2_Supporting, PM5_Supporting, PP2, PP3 System.out.println("Searching for: " + toBroad(genomicVariant)); @@ -253,40 +256,48 @@ void manualDataExplorer() { List variantAnnotations = variantAnnotator.annotate(genomicVariant); AlleleProto.AlleleProperties alleleProperties = allelePropertiesDao.getAlleleProperties(alleleKey, assembly); FrequencyData frequencyData = AlleleProtoAdaptor.toFrequencyData(alleleProperties); + PathogenicityData pathogenicityData = AlleleProtoAdaptor.toPathogenicityData(alleleProperties); + pathogenicityData = PathogenicityData.of(pathogenicityData.pathogenicityScores().stream().filter(score -> score.getSource() == PathogenicitySource.ALPHA_MISSENSE).toList()); ClinVarData clinVarData = clinVarDao.getClinVarData(genomicVariant); VariantAnnotation variantAnnotation = variantAnnotations.isEmpty() ? null : variantAnnotations.get(0); + VariantEvaluation variantEvaluation = VariantEvaluation.builder() + .variant(genomicVariant) + .geneId(variantAnnotation.getGeneId()) + .geneSymbol(variantAnnotation.getGeneSymbol()) + .variantEffect(variantAnnotation.getVariantEffect()) + .annotations(variantAnnotation.getTranscriptAnnotations()) + .frequencyData(frequencyData) + .pathogenicityData(PathogenicityData.of(clinVarData, pathogenicityData.pathogenicityScores())) + .compatibleInheritanceModes(Set.of(ModeOfInheritance.AUTOSOMAL_RECESSIVE)) + .build(); + TranscriptAnnotation transcriptAnnotation = variantAnnotation != null && variantAnnotation.hasTranscriptAnnotations() ? variantAnnotation.getTranscriptAnnotations().get(0) : null; System.out.println(toBroad(genomicVariant) + (variantAnnotation == null ? "" : " " + variantAnnotation.getGeneSymbol() + (transcriptAnnotation == null ? "" : ":" + transcriptAnnotation.getHgvsCdna() + ":" + transcriptAnnotation.getHgvsProtein() ) + " " + variantAnnotation.getVariantEffect())); - System.out.print(frequencyData.getRsId()); - System.out.println(clinVarData.isEmpty() ? "" : " " + clinVarData.getPrimaryInterpretation() + " (" + clinVarData.starRating() + "*) " + clinVarData.getVariationId() + " " + clinVarData.getGeneSymbol() + ":" + clinVarData.getHgvsCdna() + ":" + clinVarData.getHgvsProtein() + " " + clinVarData.getVariantEffect()); + System.out.println(frequencyData.getRsId() + " " + (transcriptAnnotation == null ? "" : transcriptAnnotation.getAccession())); + String geneSymbol = variantAnnotation.getGeneSymbol(); + System.out.println(geneSymbol + " " + GeneConstraints.geneConstraint(geneSymbol)); + System.out.println(clinVarData.isEmpty() ? "" : clinVarData.getPrimaryInterpretation() + " (" + clinVarData.starRating() + "*) " + clinVarData.getVariationId() + " " + clinVarData.getGeneSymbol() + ":" + clinVarData.getHgvsCdna() + ":" + clinVarData.getHgvsProtein() + " " + clinVarData.getVariantEffect()); + System.out.println("Variant score: " + variantEvaluation.getVariantScore() + " Frequency score: " + variantEvaluation.getFrequencyScore() + " Pathogenicity score: " + variantEvaluation.getPathogenicityScore()); System.out.println("Frequency data:"); if (frequencyData.isEmpty()) { - System.out.println("\t-"); + System.out.println("\n\t-"); } else { - frequencyData.frequencies().forEach(freq -> System.out.println("\t" + freq.source() + "=" + freq.frequency())); + System.out.println("\tfrequency score: " + frequencyData.frequencyScore()); + frequencyData.frequencies().forEach(freq -> System.out.println("\t" + freq.source() + "=" + freq.frequency() + "(" + freq.ac() + "|" + freq.an() + "|" + freq.homs() + ")")); } System.out.println("Pathogenicity scores:"); - PathogenicityData pathogenicityData = AlleleProtoAdaptor.toPathogenicityData(alleleProperties); if (pathogenicityData.isEmpty()) { System.out.println("\t-"); } else { + System.out.println("\tpathogenicity score: " + pathogenicityData.pathogenicityScore()); pathogenicityData.pathogenicityScores().forEach(path -> System.out.println("\t" + path)); } - VariantEvaluation variantEvaluation = VariantEvaluation.builder() - .variant(genomicVariant) - .geneId(variantAnnotation.getGeneId()) - .geneSymbol(variantAnnotation.getGeneSymbol()) - .variantEffect(variantAnnotation.getVariantEffect()) - .annotations(variantAnnotation.getTranscriptAnnotations()) - .frequencyData(frequencyData) - .pathogenicityData(PathogenicityData.of(clinVarData, pathogenicityData.pathogenicityScores())) - .compatibleInheritanceModes(Set.of(ModeOfInheritance.AUTOSOMAL_DOMINANT)) - .build(); - Acmg2015EvidenceAssigner acmg2015EvidenceAssigner = new Acmg2015EvidenceAssigner("sample", null, clinVarDao); - var acmgEvidence = acmg2015EvidenceAssigner.assignVariantAcmgEvidence(variantEvaluation, ModeOfInheritance.AUTOSOMAL_DOMINANT, List.of(variantEvaluation), List.of(), List.of()); - Acmg2020PointsBasedClassifier classifier = new Acmg2020PointsBasedClassifier(); + AcmgEvidenceAssigner acmgEvidenceAssigner = new Acmg2015EvidenceAssigner("sample", Pedigree.justProband("sample"), clinVarDao); + var acmgEvidence = acmgEvidenceAssigner.assignVariantAcmgEvidence(variantEvaluation, ModeOfInheritance.AUTOSOMAL_RECESSIVE, List.of(variantEvaluation), List.of(), List.of()); + AcmgEvidenceClassifier classifier = new Acmg2020PointsBasedClassifier(); + // should use an AcmgAssignmentCalculator to figure out the correct disease-gene association to use System.out.println(classifier.classify(acmgEvidence) + " " + acmgEvidence + " points=" + acmgEvidence.points()); }