Example #1
0
  // protected basic manipulation routines
  private static List<Allele> makeAlleles(Collection<Allele> alleles) {
    final List<Allele> alleleList = new ArrayList<Allele>(alleles.size());

    boolean sawRef = false;
    for (final Allele a : alleles) {
      for (final Allele b : alleleList) {
        if (a.equals(b, true))
          throw new IllegalArgumentException("Duplicate allele added to VariantContext: " + a);
      }

      // deal with the case where the first allele isn't the reference
      if (a.isReference()) {
        if (sawRef)
          throw new IllegalArgumentException(
              "Alleles for a VariantContext must contain at most one reference allele: " + alleles);
        alleleList.add(0, a);
        sawRef = true;
      } else alleleList.add(a);
    }

    if (alleleList.isEmpty())
      throw new IllegalArgumentException(
          "Cannot create a VariantContext with an empty allele list");

    if (alleleList.get(0).isNonReference())
      throw new IllegalArgumentException(
          "Alleles for a VariantContext must contain at least one reference allele: " + alleles);

    return alleleList;
  }
  /**
   * Returns a context identical to this with the REF and ALT alleles reverse complemented.
   *
   * @param vc variant context
   * @return new vc
   */
  public static VariantContext reverseComplement(VariantContext vc) {
    // create a mapping from original allele to reverse complemented allele
    HashMap<Allele, Allele> alleleMap = new HashMap<Allele, Allele>(vc.getAlleles().size());
    for (Allele originalAllele : vc.getAlleles()) {
      Allele newAllele;
      if (originalAllele.isNoCall() || originalAllele.isNull()) newAllele = originalAllele;
      else
        newAllele =
            Allele.create(
                BaseUtils.simpleReverseComplement(originalAllele.getBases()),
                originalAllele.isReference());
      alleleMap.put(originalAllele, newAllele);
    }

    // create new Genotype objects
    GenotypesContext newGenotypes = GenotypesContext.create(vc.getNSamples());
    for (final Genotype genotype : vc.getGenotypes()) {
      List<Allele> newAlleles = new ArrayList<Allele>();
      for (Allele allele : genotype.getAlleles()) {
        Allele newAllele = alleleMap.get(allele);
        if (newAllele == null) newAllele = Allele.NO_CALL;
        newAlleles.add(newAllele);
      }
      newGenotypes.add(Genotype.modifyAlleles(genotype, newAlleles));
    }

    return new VariantContextBuilder(vc).alleles(alleleMap.values()).genotypes(newGenotypes).make();
  }
  protected void printVerboseData(
      String pos,
      VariantContext vc,
      double PofF,
      double phredScaledConfidence,
      final GenotypeLikelihoodsCalculationModel.Model model) {
    Allele refAllele = null, altAllele = null;
    for (Allele allele : vc.getAlleles()) {
      if (allele.isReference()) refAllele = allele;
      else altAllele = allele;
    }

    for (int i = 0; i <= N; i++) {
      StringBuilder AFline = new StringBuilder("AFINFO\t");
      AFline.append(pos);
      AFline.append("\t");
      AFline.append(refAllele);
      AFline.append("\t");
      if (altAllele != null) AFline.append(altAllele);
      else AFline.append("N/A");
      AFline.append("\t");
      AFline.append(i + "/" + N + "\t");
      AFline.append(String.format("%.2f\t", ((float) i) / N));
      AFline.append(String.format("%.8f\t", getAlleleFrequencyPriors(model)[i]));
      verboseWriter.println(AFline.toString());
    }

    verboseWriter.println("P(f>0) = " + PofF);
    verboseWriter.println("Qscore = " + phredScaledConfidence);
    verboseWriter.println();
  }
Example #4
0
  private void validateAlleles() {
    // check alleles
    boolean alreadySeenRef = false, alreadySeenNull = false;
    for (Allele allele : alleles) {
      // make sure there's only one reference allele
      if (allele.isReference()) {
        if (alreadySeenRef)
          throw new IllegalArgumentException(
              "BUG: Received two reference tagged alleles in VariantContext "
                  + alleles
                  + " this="
                  + this);
        alreadySeenRef = true;
      }

      if (allele.isNoCall()) {
        throw new IllegalArgumentException(
            "BUG: Cannot add a no call allele to a variant context " + alleles + " this=" + this);
      }

      // make sure there's only one null allele
      if (allele.isNull()) {
        if (alreadySeenNull)
          throw new IllegalArgumentException(
              "BUG: Received two null alleles in VariantContext " + alleles + " this=" + this);
        alreadySeenNull = true;
      }
    }

    // make sure there's one reference allele
    if (!alreadySeenRef)
      throw new IllegalArgumentException("No reference allele found in VariantContext");

    //        if ( getType() == Type.INDEL ) {
    //            if ( getReference().length() != (getLocation().size()-1) ) {
    long length = (stop - start) + 1;
    if ((getReference().isNull() && length != 1)
        || (getReference().isNonNull() && (length - getReference().length() > 1))) {
      throw new IllegalStateException(
          "BUG: GenomeLoc "
              + contig
              + ":"
              + start
              + "-"
              + stop
              + " has a size == "
              + length
              + " but the variation reference allele has length "
              + getReference().length()
              + " this = "
              + this);
    }
  }
Example #5
0
  /**
   * helper routine for subcontext
   *
   * @param genotypes genotypes
   * @return allele set
   */
  private final Set<Allele> allelesOfGenotypes(Collection<Genotype> genotypes) {
    final Set<Allele> alleles = new HashSet<Allele>();

    boolean addedref = false;
    for (final Genotype g : genotypes) {
      for (final Allele a : g.getAlleles()) {
        addedref = addedref || a.isReference();
        if (a.isCalled()) alleles.add(a);
      }
    }
    if (!addedref) alleles.add(getReference());

    return alleles;
  }
  private static AlleleMapper resolveIncompatibleAlleles(
      Allele refAllele, VariantContext vc, Set<Allele> allAlleles) {
    if (refAllele.equals(vc.getReference())) return new AlleleMapper(vc);
    else {
      // we really need to do some work.  The refAllele is the longest reference allele seen at this
      // start site.  So imagine it is:
      //
      // refAllele: ACGTGA
      // myRef:     ACGT
      // myAlt:     -
      //
      // We need to remap all of the alleles in vc to include the extra GA so that
      // myRef => refAllele and myAlt => GA
      //

      Allele myRef = vc.getReference();
      if (refAllele.length() <= myRef.length())
        throw new ReviewedStingException(
            "BUG: myRef=" + myRef + " is longer than refAllele=" + refAllele);
      byte[] extraBases =
          Arrays.copyOfRange(refAllele.getBases(), myRef.length(), refAllele.length());

      //            System.out.printf("Remapping allele at %s%n", vc);
      //            System.out.printf("ref   %s%n", refAllele);
      //            System.out.printf("myref %s%n", myRef );
      //            System.out.printf("extrabases %s%n", new String(extraBases));

      Map<Allele, Allele> map = new HashMap<Allele, Allele>();
      for (Allele a : vc.getAlleles()) {
        if (a.isReference()) map.put(a, refAllele);
        else {
          Allele extended = Allele.extend(a, extraBases);
          for (Allele b : allAlleles) if (extended.equals(b)) extended = b;
          //                    System.out.printf("  Extending %s => %s%n", a, extended);
          map.put(a, extended);
        }
      }

      // debugging
      //            System.out.printf("mapping %s%n", map);

      return new AlleleMapper(map);
    }
  }
Example #7
0
  /**
   * the actual constructor. Private access only
   *
   * @param source source
   * @param contig the contig
   * @param start the start base (one based)
   * @param stop the stop reference base (one based)
   * @param alleles alleles
   * @param genotypes genotypes map
   * @param log10PError qual
   * @param filters filters: use null for unfiltered and empty set for passes filters
   * @param attributes attributes
   * @param referenceBaseForIndel padded reference base
   * @param validationToPerform set of validation steps to take
   */
  protected VariantContext(
      String source,
      String ID,
      String contig,
      long start,
      long stop,
      Collection<Allele> alleles,
      GenotypesContext genotypes,
      double log10PError,
      Set<String> filters,
      Map<String, Object> attributes,
      Byte referenceBaseForIndel,
      EnumSet<Validation> validationToPerform) {
    if (contig == null) {
      throw new IllegalArgumentException("Contig cannot be null");
    }
    this.contig = contig;
    this.start = start;
    this.stop = stop;

    // intern for efficiency.  equals calls will generate NPE if ID is inappropriately passed in as
    // null
    if (ID == null || ID.equals(""))
      throw new IllegalArgumentException("ID field cannot be the null or the empty string");
    this.ID = ID.equals(VCFConstants.EMPTY_ID_FIELD) ? VCFConstants.EMPTY_ID_FIELD : ID;

    this.commonInfo = new CommonInfo(source, log10PError, filters, attributes);
    REFERENCE_BASE_FOR_INDEL = referenceBaseForIndel;

    // todo -- remove me when this check is no longer necessary
    if (this.commonInfo.hasAttribute(ID_KEY))
      throw new IllegalArgumentException(
          "Trying to create a VariantContext with a ID key.  Please use provided constructor argument ID");

    if (alleles == null) {
      throw new IllegalArgumentException("Alleles cannot be null");
    }

    // we need to make this a LinkedHashSet in case the user prefers a given ordering of alleles
    this.alleles = makeAlleles(alleles);

    if (genotypes == null || genotypes == NO_GENOTYPES) {
      this.genotypes = NO_GENOTYPES;
    } else {
      this.genotypes = genotypes.immutable();
    }

    // cache the REF and ALT alleles
    int nAlleles = alleles.size();
    for (Allele a : alleles) {
      if (a.isReference()) {
        REF = a;
      } else if (nAlleles == 2) { // only cache ALT when biallelic
        ALT = a;
      }
    }

    if (!validationToPerform.isEmpty()) {
      validate(validationToPerform);
    }
  }
  public static VariantContext createVariantContextWithTrimmedAlleles(VariantContext inputVC) {
    // see if we need to trim common reference base from all alleles
    boolean trimVC;

    // We need to trim common reference base from all alleles in all genotypes if a ref base is
    // common to all alleles
    Allele refAllele = inputVC.getReference();
    if (!inputVC.isVariant()) trimVC = false;
    else if (refAllele.isNull()) trimVC = false;
    else {
      trimVC =
          (AbstractVCFCodec.computeForwardClipping(
                  new ArrayList<Allele>(inputVC.getAlternateAlleles()),
                  inputVC.getReference().getDisplayString())
              > 0);
    }

    // nothing to do if we don't need to trim bases
    if (trimVC) {
      List<Allele> alleles = new ArrayList<Allele>();
      GenotypesContext genotypes = GenotypesContext.create();

      // set the reference base for indels in the attributes
      Map<String, Object> attributes = new TreeMap<String, Object>(inputVC.getAttributes());

      Map<Allele, Allele> originalToTrimmedAlleleMap = new HashMap<Allele, Allele>();

      for (Allele a : inputVC.getAlleles()) {
        if (a.isSymbolic()) {
          alleles.add(a);
          originalToTrimmedAlleleMap.put(a, a);
        } else {
          // get bases for current allele and create a new one with trimmed bases
          byte[] newBases = Arrays.copyOfRange(a.getBases(), 1, a.length());
          Allele trimmedAllele = Allele.create(newBases, a.isReference());
          alleles.add(trimmedAllele);
          originalToTrimmedAlleleMap.put(a, trimmedAllele);
        }
      }

      // detect case where we're trimming bases but resulting vc doesn't have any null allele. In
      // that case, we keep original representation
      // example: mixed records such as {TA*,TGA,TG}
      boolean hasNullAlleles = false;

      for (Allele a : originalToTrimmedAlleleMap.values()) {
        if (a.isNull()) hasNullAlleles = true;
        if (a.isReference()) refAllele = a;
      }

      if (!hasNullAlleles) return inputVC;
      // now we can recreate new genotypes with trimmed alleles
      for (final Genotype genotype : inputVC.getGenotypes()) {

        List<Allele> originalAlleles = genotype.getAlleles();
        List<Allele> trimmedAlleles = new ArrayList<Allele>();
        for (Allele a : originalAlleles) {
          if (a.isCalled()) trimmedAlleles.add(originalToTrimmedAlleleMap.get(a));
          else trimmedAlleles.add(Allele.NO_CALL);
        }
        genotypes.add(Genotype.modifyAlleles(genotype, trimmedAlleles));
      }

      final VariantContextBuilder builder = new VariantContextBuilder(inputVC);
      return builder
          .alleles(alleles)
          .genotypes(genotypes)
          .attributes(attributes)
          .referenceBaseForIndel(new Byte(inputVC.getReference().getBases()[0]))
          .make();
    }

    return inputVC;
  }
  public static VariantContext createVariantContextWithPaddedAlleles(
      VariantContext inputVC, boolean refBaseShouldBeAppliedToEndOfAlleles) {
    // see if we need to pad common reference base from all alleles
    boolean padVC;

    // We need to pad a VC with a common base if the length of the reference allele is less than the
    // length of the VariantContext.
    // This happens because the position of e.g. an indel is always one before the actual event (as
    // per VCF convention).
    long locLength = (inputVC.getEnd() - inputVC.getStart()) + 1;
    if (inputVC.hasSymbolicAlleles()) padVC = true;
    else if (inputVC.getReference().length() == locLength) padVC = false;
    else if (inputVC.getReference().length() == locLength - 1) padVC = true;
    else
      throw new IllegalArgumentException(
          "Badly formed variant context at location "
              + String.valueOf(inputVC.getStart())
              + " in contig "
              + inputVC.getChr()
              + ". Reference length must be at most one base shorter than location size");

    // nothing to do if we don't need to pad bases
    if (padVC) {
      if (!inputVC.hasReferenceBaseForIndel())
        throw new ReviewedStingException(
            "Badly formed variant context at location "
                + inputVC.getChr()
                + ":"
                + inputVC.getStart()
                + "; no padded reference base is available.");

      Byte refByte = inputVC.getReferenceBaseForIndel();

      List<Allele> alleles = new ArrayList<Allele>();

      for (Allele a : inputVC.getAlleles()) {
        // get bases for current allele and create a new one with trimmed bases
        if (a.isSymbolic()) {
          alleles.add(a);
        } else {
          String newBases;
          if (refBaseShouldBeAppliedToEndOfAlleles)
            newBases = a.getBaseString() + new String(new byte[] {refByte});
          else newBases = new String(new byte[] {refByte}) + a.getBaseString();
          alleles.add(Allele.create(newBases, a.isReference()));
        }
      }

      // now we can recreate new genotypes with trimmed alleles
      GenotypesContext genotypes = GenotypesContext.create(inputVC.getNSamples());
      for (final Genotype g : inputVC.getGenotypes()) {
        List<Allele> inAlleles = g.getAlleles();
        List<Allele> newGenotypeAlleles = new ArrayList<Allele>(g.getAlleles().size());
        for (Allele a : inAlleles) {
          if (a.isCalled()) {
            if (a.isSymbolic()) {
              newGenotypeAlleles.add(a);
            } else {
              String newBases;
              if (refBaseShouldBeAppliedToEndOfAlleles)
                newBases = a.getBaseString() + new String(new byte[] {refByte});
              else newBases = new String(new byte[] {refByte}) + a.getBaseString();
              newGenotypeAlleles.add(Allele.create(newBases, a.isReference()));
            }
          } else {
            // add no-call allele
            newGenotypeAlleles.add(Allele.NO_CALL);
          }
        }
        genotypes.add(
            new Genotype(
                g.getSampleName(),
                newGenotypeAlleles,
                g.getLog10PError(),
                g.getFilters(),
                g.getAttributes(),
                g.isPhased()));
      }

      return new VariantContextBuilder(inputVC).alleles(alleles).genotypes(genotypes).make();
    } else return inputVC;
  }
  /**
   * Main entry function to calculate genotypes of a given VC with corresponding GL's
   *
   * @param tracker Tracker
   * @param refContext Reference context
   * @param rawContext Raw context
   * @param stratifiedContexts Stratified alignment contexts
   * @param vc Input VC
   * @param model GL calculation model
   * @param inheritAttributesFromInputVC Output VC will contain attributes inherited from input vc
   * @return VC with assigned genotypes
   */
  public VariantCallContext calculateGenotypes(
      final RefMetaDataTracker tracker,
      final ReferenceContext refContext,
      final AlignmentContext rawContext,
      Map<String, AlignmentContext> stratifiedContexts,
      final VariantContext vc,
      final GenotypeLikelihoodsCalculationModel.Model model,
      final boolean inheritAttributesFromInputVC,
      final Map<String, org.broadinstitute.sting.utils.genotyper.PerReadAlleleLikelihoodMap>
          perReadAlleleLikelihoodMap) {

    boolean limitedContext =
        tracker == null || refContext == null || rawContext == null || stratifiedContexts == null;

    // initialize the data for this thread if that hasn't been done yet
    if (afcm.get() == null) {
      afcm.set(AFCalcFactory.createAFCalc(UAC, N, logger));
    }

    // estimate our confidence in a reference call and return
    if (vc.getNSamples() == 0) {
      if (limitedContext) return null;
      return (UAC.OutputMode != OUTPUT_MODE.EMIT_ALL_SITES
          ? estimateReferenceConfidence(vc, stratifiedContexts, getTheta(model), false, 1.0)
          : generateEmptyContext(tracker, refContext, stratifiedContexts, rawContext));
    }

    AFCalcResult AFresult = afcm.get().getLog10PNonRef(vc, getAlleleFrequencyPriors(model));

    // is the most likely frequency conformation AC=0 for all alternate alleles?
    boolean bestGuessIsRef = true;

    // determine which alternate alleles have AF>0
    final List<Allele> myAlleles = new ArrayList<Allele>(vc.getAlleles().size());
    final List<Integer> alleleCountsofMLE = new ArrayList<Integer>(vc.getAlleles().size());
    myAlleles.add(vc.getReference());
    for (int i = 0; i < AFresult.getAllelesUsedInGenotyping().size(); i++) {
      final Allele alternateAllele = AFresult.getAllelesUsedInGenotyping().get(i);
      if (alternateAllele.isReference()) continue;

      // we are non-ref if the probability of being non-ref > the emit confidence.
      // the emit confidence is phred-scaled, say 30 => 10^-3.
      // the posterior AF > 0 is log10: -5 => 10^-5
      // we are non-ref if 10^-5 < 10^-3 => -5 < -3
      final boolean isNonRef =
          AFresult.isPolymorphic(alternateAllele, UAC.STANDARD_CONFIDENCE_FOR_EMITTING / -10.0);

      // if the most likely AC is not 0, then this is a good alternate allele to use
      if (isNonRef) {
        myAlleles.add(alternateAllele);
        alleleCountsofMLE.add(AFresult.getAlleleCountAtMLE(alternateAllele));
        bestGuessIsRef = false;
      }
      // if in GENOTYPE_GIVEN_ALLELES mode, we still want to allow the use of a poor allele
      else if (UAC.GenotypingMode
          == GenotypeLikelihoodsCalculationModel.GENOTYPING_MODE.GENOTYPE_GIVEN_ALLELES) {
        myAlleles.add(alternateAllele);
        alleleCountsofMLE.add(AFresult.getAlleleCountAtMLE(alternateAllele));
      }
    }

    final double PoFGT0 = Math.pow(10, AFresult.getLog10PosteriorOfAFGT0());

    // note the math.abs is necessary because -10 * 0.0 => -0.0 which isn't nice
    final double phredScaledConfidence =
        Math.abs(
            !bestGuessIsRef
                    || UAC.GenotypingMode
                        == GenotypeLikelihoodsCalculationModel.GENOTYPING_MODE
                            .GENOTYPE_GIVEN_ALLELES
                ? -10 * AFresult.getLog10PosteriorOfAFEq0()
                : -10 * AFresult.getLog10PosteriorOfAFGT0());

    // return a null call if we don't pass the confidence cutoff or the most likely allele frequency
    // is zero
    if (UAC.OutputMode != OUTPUT_MODE.EMIT_ALL_SITES
        && !passesEmitThreshold(phredScaledConfidence, bestGuessIsRef)) {
      // technically, at this point our confidence in a reference call isn't accurately estimated
      //  because it didn't take into account samples with no data, so let's get a better estimate
      return limitedContext
          ? null
          : estimateReferenceConfidence(vc, stratifiedContexts, getTheta(model), true, PoFGT0);
    }

    // start constructing the resulting VC
    final GenomeLoc loc = genomeLocParser.createGenomeLoc(vc);
    final VariantContextBuilder builder =
        new VariantContextBuilder(
            "UG_call", loc.getContig(), loc.getStart(), loc.getStop(), myAlleles);
    builder.log10PError(phredScaledConfidence / -10.0);
    if (!passesCallThreshold(phredScaledConfidence)) builder.filters(filter);

    // create the genotypes
    final GenotypesContext genotypes = afcm.get().subsetAlleles(vc, myAlleles, true, ploidy);
    builder.genotypes(genotypes);

    // print out stats if we have a writer
    if (verboseWriter != null && !limitedContext)
      printVerboseData(refContext.getLocus().toString(), vc, PoFGT0, phredScaledConfidence, model);

    // *** note that calculating strand bias involves overwriting data structures, so we do that
    // last
    final HashMap<String, Object> attributes = new HashMap<String, Object>();

    // inherit attributed from input vc if requested
    if (inheritAttributesFromInputVC) attributes.putAll(vc.getAttributes());
    // if the site was downsampled, record that fact
    if (!limitedContext && rawContext.hasPileupBeenDownsampled())
      attributes.put(VCFConstants.DOWNSAMPLED_KEY, true);

    if (UAC.ANNOTATE_NUMBER_OF_ALLELES_DISCOVERED)
      attributes.put(NUMBER_OF_DISCOVERED_ALLELES_KEY, vc.getAlternateAlleles().size());

    // add the MLE AC and AF annotations
    if (alleleCountsofMLE.size() > 0) {
      attributes.put(VCFConstants.MLE_ALLELE_COUNT_KEY, alleleCountsofMLE);
      final int AN = builder.make().getCalledChrCount();
      final ArrayList<Double> MLEfrequencies = new ArrayList<Double>(alleleCountsofMLE.size());
      // the MLEAC is allowed to be larger than the AN (e.g. in the case of all PLs being 0, the GT
      // is ./. but the exact model may arbitrarily choose an AC>1)
      for (int AC : alleleCountsofMLE) MLEfrequencies.add(Math.min(1.0, (double) AC / (double) AN));
      attributes.put(VCFConstants.MLE_ALLELE_FREQUENCY_KEY, MLEfrequencies);
    }

    if (UAC.COMPUTE_SLOD && !limitedContext && !bestGuessIsRef) {
      // final boolean DEBUG_SLOD = false;

      // the overall lod
      // double overallLog10PofNull = AFresult.log10AlleleFrequencyPosteriors[0];
      double overallLog10PofF = AFresult.getLog10LikelihoodOfAFGT0();
      // if ( DEBUG_SLOD ) System.out.println("overallLog10PofF=" + overallLog10PofF);

      List<Allele> allAllelesToUse = builder.make().getAlleles();

      // the forward lod
      VariantContext vcForward =
          calculateLikelihoods(
              tracker,
              refContext,
              stratifiedContexts,
              AlignmentContextUtils.ReadOrientation.FORWARD,
              allAllelesToUse,
              false,
              model,
              perReadAlleleLikelihoodMap);
      AFresult = afcm.get().getLog10PNonRef(vcForward, getAlleleFrequencyPriors(model));
      // double[] normalizedLog10Posteriors =
      // MathUtils.normalizeFromLog10(AFresult.log10AlleleFrequencyPosteriors, true);
      double forwardLog10PofNull = AFresult.getLog10LikelihoodOfAFEq0();
      double forwardLog10PofF = AFresult.getLog10LikelihoodOfAFGT0();
      // if ( DEBUG_SLOD ) System.out.println("forwardLog10PofNull=" + forwardLog10PofNull + ",
      // forwardLog10PofF=" + forwardLog10PofF);

      // the reverse lod
      VariantContext vcReverse =
          calculateLikelihoods(
              tracker,
              refContext,
              stratifiedContexts,
              AlignmentContextUtils.ReadOrientation.REVERSE,
              allAllelesToUse,
              false,
              model,
              perReadAlleleLikelihoodMap);
      AFresult = afcm.get().getLog10PNonRef(vcReverse, getAlleleFrequencyPriors(model));
      // normalizedLog10Posteriors =
      // MathUtils.normalizeFromLog10(AFresult.log10AlleleFrequencyPosteriors, true);
      double reverseLog10PofNull = AFresult.getLog10LikelihoodOfAFEq0();
      double reverseLog10PofF = AFresult.getLog10LikelihoodOfAFGT0();
      // if ( DEBUG_SLOD ) System.out.println("reverseLog10PofNull=" + reverseLog10PofNull + ",
      // reverseLog10PofF=" + reverseLog10PofF);

      double forwardLod = forwardLog10PofF + reverseLog10PofNull - overallLog10PofF;
      double reverseLod = reverseLog10PofF + forwardLog10PofNull - overallLog10PofF;
      // if ( DEBUG_SLOD ) System.out.println("forward lod=" + forwardLod + ", reverse lod=" +
      // reverseLod);

      // strand score is max bias between forward and reverse strands
      double strandScore = Math.max(forwardLod, reverseLod);
      // rescale by a factor of 10
      strandScore *= 10.0;
      // logger.debug(String.format("SLOD=%f", strandScore));

      if (!Double.isNaN(strandScore)) attributes.put("SB", strandScore);
    }

    // finish constructing the resulting VC
    builder.attributes(attributes);
    VariantContext vcCall = builder.make();

    // if we are subsetting alleles (either because there were too many or because some were not
    // polymorphic)
    // then we may need to trim the alleles (because the original VariantContext may have had to pad
    // at the end).
    if (myAlleles.size() != vc.getAlleles().size()
        && !limitedContext) // limitedContext callers need to handle allele trimming on their own to
                            // keep their perReadAlleleLikelihoodMap alleles in sync
    vcCall = VariantContextUtils.reverseTrimAlleles(vcCall);

    if (annotationEngine != null
        && !limitedContext) { // limitedContext callers need to handle annotations on their own by
                              // calling their own annotationEngine
      // Note: we want to use the *unfiltered* and *unBAQed* context for the annotations
      final ReadBackedPileup pileup = rawContext.getBasePileup();
      stratifiedContexts = AlignmentContextUtils.splitContextBySampleName(pileup);

      vcCall =
          annotationEngine.annotateContext(
              tracker, refContext, stratifiedContexts, vcCall, perReadAlleleLikelihoodMap);
    }

    return new VariantCallContext(vcCall, confidentlyCalled(phredScaledConfidence, PoFGT0));
  }