From c3ea96d85616a3ce0b4b90d40e2d141b1efd16cf Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 2 Sep 2011 08:42:01 -0400 Subject: [PATCH 001/196] Removing many unused functions of unquestionable purpose --- .../sting/utils/QualityUtils.java | 101 ++---------------- 1 file changed, 10 insertions(+), 91 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/QualityUtils.java b/public/java/src/org/broadinstitute/sting/utils/QualityUtils.java index fad2320fc..093da7dd6 100755 --- a/public/java/src/org/broadinstitute/sting/utils/QualityUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/QualityUtils.java @@ -9,14 +9,17 @@ import net.sf.samtools.SAMUtils; * @author Kiran Garimella */ public class QualityUtils { - public final static byte MAX_QUAL_SCORE = SAMUtils.MAX_PHRED_SCORE; public final static double MIN_REASONABLE_ERROR = 0.0001; public final static byte MAX_REASONABLE_Q_SCORE = 40; public final static byte MIN_USABLE_Q_SCORE = 6; - public final static int MAPPING_QUALITY_UNAVAILABLE = 255; + private static double qualToErrorProbCache[] = new double[256]; + static { + for (byte i = 0; i < 256; i++) qualToErrorProbCache[i] = qualToErrorProbRaw(i); + } + /** * Private constructor. No instantiating this class! */ @@ -33,10 +36,6 @@ public class QualityUtils { return 1.0 - qualToErrorProb(qual); } - static public double qualToProb(int qual) { - return qualToProb( (double)qual ); - } - static public double qualToProb(double qual) { return 1.0 - Math.pow(10.0, qual/(-10.0)); } @@ -48,10 +47,14 @@ public class QualityUtils { * @param qual a quality score (0-40) * @return a probability (0.0-1.0) */ - static public double qualToErrorProb(byte qual) { + static public double qualToErrorProbRaw(byte qual) { return Math.pow(10.0, ((double) qual)/-10.0); } + static public double qualToErrorProb(byte qual) { + return qualToErrorProbCache[qual]; + } + /** * Convert a probability to a quality score. Note, this is capped at Q40. * @@ -110,88 +113,4 @@ public class QualityUtils { //return (byte) Math.min(qual, maxQual); return (byte) Math.max(Math.min(qual, maxQual), 1); } - - /** - * Compress a base and a probability into a single byte so that it can be output in a SAMRecord's SQ field. - * Note: the highest probability this function can encode is 64%, so this function should only never be used on the best base hypothesis. - * Another note: the probability encoded here gets rounded to the nearest 1%. - * - * @param baseIndex the base index - * @param prob the base probability - * @return a byte containing the index and the probability - */ - static public byte baseAndProbToCompressedQuality(int baseIndex, double prob) { - byte compressedQual = 0; - - compressedQual = (byte) baseIndex; - - byte cprob = (byte) (100.0*prob); - byte qualmask = (byte) 252; - compressedQual += ((cprob << 2) & qualmask); - - return compressedQual; - } - - /** - * From a compressed base, extract the base index (0:A, 1:C, 2:G, 3:T) - * - * @param compressedQual the compressed quality score, as returned by baseAndProbToCompressedQuality - * @return base index - */ - static public int compressedQualityToBaseIndex(byte compressedQual) { - return (int) (compressedQual & 0x3); - } - - /** - * From a compressed base, extract the base probability - * - * @param compressedQual the compressed quality score, as returned by baseAndProbToCompressedQuality - * @return the probability - */ - static public double compressedQualityToProb(byte compressedQual) { - // Because java natives are signed, extra care must be taken to avoid - // shifting a 1 into the sign bit in the implicit promotion of 2 to an int. - int x2 = ((int) compressedQual) & 0xff; - x2 = (x2 >>> 2); - - return ((double) x2)/100.0; - } - - /** - * Return the complement of a compressed quality - * - * @param compressedQual the compressed quality score (as returned by baseAndProbToCompressedQuality) - * @return the complementary compressed quality - */ - static public byte complementCompressedQuality(byte compressedQual) { - int baseIndex = compressedQualityToBaseIndex(compressedQual); - double prob = compressedQualityToProb(compressedQual); - - return baseAndProbToCompressedQuality(BaseUtils.complementIndex(baseIndex), prob); - } - - /** - * Return the reverse complement of a byte array of compressed qualities - * - * @param compressedQuals a byte array of compressed quality scores - * @return the reverse complement of the byte array - */ - static public byte[] reverseComplementCompressedQualityArray(byte[] compressedQuals) { - byte[] rcCompressedQuals = new byte[compressedQuals.length]; - - for (int pos = 0; pos < compressedQuals.length; pos++) { - rcCompressedQuals[compressedQuals.length - pos - 1] = complementCompressedQuality(compressedQuals[pos]); - } - - return rcCompressedQuals; - } - - /** - * Return the reverse of a byte array of qualities (compressed or otherwise) - * @param quals the array of bytes to be reversed - * @return the reverse of the quality array - */ - static public byte[] reverseQualityArray( byte[] quals ) { - return Utils.reverse(quals); // no sense in duplicating functionality - } } From c57198a1b998ba25b7facac526cafa04f9b8f77a Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 2 Sep 2011 08:46:17 -0400 Subject: [PATCH 002/196] Optimizations in VCFCodec -- Don't create an empty LinkedHashSet() for PASS fields. Just return Collections.emptySet() instead. -- For filter fields with actual values, returns an unmodifiableSet instead of one that can be changed --- .../broadinstitute/sting/utils/codecs/vcf/VCFCodec.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFCodec.java index fa030ef5f..cd320b332 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFCodec.java @@ -110,11 +110,8 @@ public class VCFCodec extends AbstractVCFCodec { if ( filterString.equals(VCFConstants.UNFILTERED) ) return null; - // empty set for passes filters - LinkedHashSet fFields = new LinkedHashSet(); - if ( filterString.equals(VCFConstants.PASSES_FILTERS_v4) ) - return fFields; + return Collections.emptySet(); if ( filterString.equals(VCFConstants.PASSES_FILTERS_v3) ) generateException(VCFConstants.PASSES_FILTERS_v3 + " is an invalid filter name in vcf4"); if ( filterString.length() == 0 ) @@ -124,6 +121,8 @@ public class VCFCodec extends AbstractVCFCodec { if ( filterHash.containsKey(filterString) ) return filterHash.get(filterString); + // empty set for passes filters + LinkedHashSet fFields = new LinkedHashSet(); // otherwise we have to parse and cache the value if ( filterString.indexOf(VCFConstants.FILTER_CODE_SEPARATOR) == -1 ) fFields.add(filterString); @@ -132,7 +131,7 @@ public class VCFCodec extends AbstractVCFCodec { filterHash.put(filterString, fFields); - return fFields; + return Collections.unmodifiableSet(fFields); } From 82f213177730123def312b6a5b64878f7f665769 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 2 Sep 2011 12:27:11 -0400 Subject: [PATCH 003/196] Simplied getAttributeAsX interfaces -- Removed versions getAttribriteAsX(key) that except on not having the value. -- Removed version that getAttributeAsXNoException(key) -- The only available assessors are now getAttributeAsX(key, default). -- This single accessors properly handle their argument types, so if the value is a double it is returned directly for getAttributeAsDouble(), or if it's a string it's converted to a double. If the key isn't found, default is returned. --- .../gatk/walkers/annotator/SBByDepth.java | 2 +- .../indels/HaplotypeIndelErrorModel.java | 2 +- .../gatk/walkers/phasing/PhasingRead.java | 2 +- .../walkers/phasing/RefSeqDataParser.java | 10 ++-- .../varianteval/evaluators/CountVariants.java | 6 +-- .../evaluators/GenotypePhasingEvaluator.java | 3 +- .../evaluators/SimpleMetricsByAC.java | 2 +- .../evaluators/TiTvVariantEvaluator.java | 2 +- .../evaluators/ValidationReport.java | 2 +- .../stratifications/AlleleCount.java | 2 +- .../stratifications/AlleleFrequency.java | 2 +- .../stratifications/Degeneracy.java | 10 ++-- .../stratifications/FunctionalClass.java | 4 +- .../VQSRCalibrationCurve.java | 4 +- .../walkers/variantutils/SelectVariants.java | 2 +- .../walkers/variantutils/VariantsToTable.java | 2 +- .../sting/utils/codecs/vcf/VCFCodec.java | 14 +++-- .../sting/utils/variantcontext/Genotype.java | 9 ---- .../InferredGeneticContext.java | 53 ++++++++++++------- .../utils/variantcontext/VariantContext.java | 10 ---- .../variantcontext/VariantContextUtils.java | 16 +++--- 21 files changed, 79 insertions(+), 80 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SBByDepth.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SBByDepth.java index 180bed24d..d2c4d24ab 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SBByDepth.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SBByDepth.java @@ -26,7 +26,7 @@ public class SBByDepth extends AnnotationByDepth { if (!vc.hasAttribute(VCFConstants.STRAND_BIAS_KEY)) return null; - double sBias = Double.valueOf(vc.getAttributeAsString(VCFConstants.STRAND_BIAS_KEY)); + double sBias = vc.getAttributeAsDouble(VCFConstants.STRAND_BIAS_KEY, -1); final Map genotypes = vc.getGenotypes(); if ( genotypes == null || genotypes.size() == 0 ) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/HaplotypeIndelErrorModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/HaplotypeIndelErrorModel.java index e68aa31e0..232e468f9 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/HaplotypeIndelErrorModel.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/HaplotypeIndelErrorModel.java @@ -73,7 +73,7 @@ public class HaplotypeIndelErrorModel { baseMatchArray = new double[MAX_CACHED_QUAL+1]; baseMismatchArray = new double[MAX_CACHED_QUAL+1]; for (int k=1; k <= MAX_CACHED_QUAL; k++) { - double baseProb = QualityUtils.qualToProb(k); + double baseProb = QualityUtils.qualToProb((byte)k); baseMatchArray[k] = probToQual(baseProb); diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/phasing/PhasingRead.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/phasing/PhasingRead.java index a56c9e21e..63fb33295 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/phasing/PhasingRead.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/phasing/PhasingRead.java @@ -37,7 +37,7 @@ public class PhasingRead extends BaseArray { public PhasingRead(int length, int mappingQual) { super(length); - this.mappingProb = new PreciseNonNegativeDouble(QualityUtils.qualToProb(mappingQual)); + this.mappingProb = new PreciseNonNegativeDouble(QualityUtils.qualToProb((byte)mappingQual)); this.baseProbs = new PreciseNonNegativeDouble[length]; Arrays.fill(this.baseProbs, null); diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/phasing/RefSeqDataParser.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/phasing/RefSeqDataParser.java index 55da1c152..f94140814 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/phasing/RefSeqDataParser.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/phasing/RefSeqDataParser.java @@ -44,12 +44,12 @@ public class RefSeqDataParser { String nameKeyToUseMultiplePrefix = nameKeyToUse + "_"; Map entriesToNames = new HashMap(); - Integer numRecords = vc.getAttributeAsIntegerNoException(NUM_RECORDS_KEY); - if (numRecords != null) { + int numRecords = vc.getAttributeAsInt(NUM_RECORDS_KEY, -1); + if (numRecords != -1) { boolean done = false; if (numRecords == 1) { // Check if perhaps the single record doesn't end with "_1": - String name = vc.getAttributeAsStringNoException(nameKeyToUse); + String name = vc.getAttributeAsString(nameKeyToUse, null); if (name != null) { entriesToNames.put(nameKeyToUse, name); done = true; @@ -59,14 +59,14 @@ public class RefSeqDataParser { if (!done) { for (int i = 1; i <= numRecords; i++) { String key = nameKeyToUseMultiplePrefix + i; - String name = vc.getAttributeAsStringNoException(key); + String name = vc.getAttributeAsString(key, null); if (name != null) entriesToNames.put(key, name); } } } else { // no entry with the # of records: - String name = vc.getAttributeAsStringNoException(nameKeyToUse); + String name = vc.getAttributeAsString(nameKeyToUse, null); if (name != null) { entriesToNames.put(nameKeyToUse, name); } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/CountVariants.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/CountVariants.java index 59ef3d992..fd379dfda 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/CountVariants.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/CountVariants.java @@ -109,12 +109,12 @@ public class CountVariants extends VariantEvaluator implements StandardEval { case SNP: nVariantLoci++; nSNPs++; - if (vc1.getAttributeAsBoolean("ISSINGLETON")) nSingletons++; + if (vc1.getAttributeAsBoolean("ISSINGLETON", false)) nSingletons++; break; case MNP: nVariantLoci++; nMNPs++; - if (vc1.getAttributeAsBoolean("ISSINGLETON")) nSingletons++; + if (vc1.getAttributeAsBoolean("ISSINGLETON", false)) nSingletons++; break; case INDEL: nVariantLoci++; @@ -136,7 +136,7 @@ public class CountVariants extends VariantEvaluator implements StandardEval { String refStr = vc1.getReference().getBaseString().toUpperCase(); - String aaStr = vc1.hasAttribute("ANCESTRALALLELE") ? vc1.getAttributeAsString("ANCESTRALALLELE").toUpperCase() : null; + String aaStr = vc1.hasAttribute("ANCESTRALALLELE") ? vc1.getAttributeAsString("ANCESTRALALLELE", null).toUpperCase() : null; // if (aaStr.equals(".")) { // aaStr = refStr; // } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/GenotypePhasingEvaluator.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/GenotypePhasingEvaluator.java index a476a2680..e69dbfb28 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/GenotypePhasingEvaluator.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/GenotypePhasingEvaluator.java @@ -219,7 +219,8 @@ public class GenotypePhasingEvaluator extends VariantEvaluator { } public static Double getPQ(Genotype gt) { - return gt.getAttributeAsDoubleNoException(ReadBackedPhasingWalker.PQ_KEY); + Double d = gt.getAttributeAsDouble(ReadBackedPhasingWalker.PQ_KEY, -1); + return d == -1 ? null : d; } public static boolean topMatchesTop(AllelePair b1, AllelePair b2) { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/SimpleMetricsByAC.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/SimpleMetricsByAC.java index d466645ea..38cbf1c45 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/SimpleMetricsByAC.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/SimpleMetricsByAC.java @@ -120,7 +120,7 @@ public class SimpleMetricsByAC extends VariantEvaluator implements StandardEval if ( eval.hasGenotypes() ) ac = eval.getChromosomeCount(eval.getAlternateAllele(0)); else if ( eval.hasAttribute("AC") ) { - ac = Integer.valueOf(eval.getAttributeAsString("AC")); + ac = eval.getAttributeAsInt("AC", -1); } if ( ac != -1 ) { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/TiTvVariantEvaluator.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/TiTvVariantEvaluator.java index be957abd7..ee58012a0 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/TiTvVariantEvaluator.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/TiTvVariantEvaluator.java @@ -50,7 +50,7 @@ public class TiTvVariantEvaluator extends VariantEvaluator implements StandardEv } String refStr = vc.getReference().getBaseString().toUpperCase(); - String aaStr = vc.getAttributeAsString("ANCESTRALALLELE").toUpperCase(); + String aaStr = vc.getAttributeAsString("ANCESTRALALLELE", null).toUpperCase(); if (aaStr != null && !aaStr.equalsIgnoreCase("null") && !aaStr.equals(".")) { BaseUtils.BaseSubstitutionType aaSubType = BaseUtils.SNPSubstitutionType(aaStr.getBytes()[0], vc.getAlternateAllele(0).getBases()[0]); diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/ValidationReport.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/ValidationReport.java index 9c331b577..7fa56785b 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/ValidationReport.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/ValidationReport.java @@ -130,7 +130,7 @@ public class ValidationReport extends VariantEvaluator implements StandardEval { //// System.out.printf(" ac = %d%n", ac); } else - ac = vc.getAttributeAsInt(VCFConstants.ALLELE_COUNT_KEY); + ac = vc.getAttributeAsInt(VCFConstants.ALLELE_COUNT_KEY, 0); return ac > 0 ? SiteStatus.POLY : SiteStatus.MONO; } else if ( vc.hasGenotypes() ) { return vc.isPolymorphic() ? SiteStatus.POLY : SiteStatus.MONO; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/AlleleCount.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/AlleleCount.java index 5cdea4e00..56b06d032 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/AlleleCount.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/AlleleCount.java @@ -45,7 +45,7 @@ public class AlleleCount extends VariantStratifier { if (eval != null) { int AC = -1; if ( eval.hasAttribute("AC") && eval.getAttribute("AC") instanceof Integer ) { - AC = eval.getAttributeAsInt("AC"); + AC = eval.getAttributeAsInt("AC", 0); } else if ( eval.isVariant() ) { for (Allele allele : eval.getAlternateAlleles()) AC = Math.max(AC, eval.getChromosomeCount(allele)); diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/AlleleFrequency.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/AlleleFrequency.java index 96d9f30ec..ac1ee9e0e 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/AlleleFrequency.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/AlleleFrequency.java @@ -28,7 +28,7 @@ public class AlleleFrequency extends VariantStratifier { if (eval != null) { try { - relevantStates.add(String.format("%.3f", (5.0 * MathUtils.round(eval.getAttributeAsDouble("AF") / 5.0, 3)))); + relevantStates.add(String.format("%.3f", (5.0 * MathUtils.round(eval.getAttributeAsDouble("AF", 0.0) / 5.0, 3)))); } catch (Exception e) { return relevantStates; } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Degeneracy.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Degeneracy.java index cc878e975..06ac05ec8 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Degeneracy.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Degeneracy.java @@ -92,8 +92,8 @@ public class Degeneracy extends VariantStratifier { Integer frame = null; if (eval.hasAttribute("refseq.functionalClass")) { - aa = eval.getAttributeAsString("refseq.variantAA"); - frame = eval.getAttributeAsInt("refseq.frame"); + aa = eval.getAttributeAsString("refseq.variantAA", null); + frame = eval.getAttributeAsInt("refseq.frame", 0); } else if (eval.hasAttribute("refseq.functionalClass_1")) { int annotationId = 1; String key; @@ -101,7 +101,7 @@ public class Degeneracy extends VariantStratifier { do { key = String.format("refseq.functionalClass_%d", annotationId); - String newtype = eval.getAttributeAsString(key); + String newtype = eval.getAttributeAsString(key, null); if ( newtype != null && ( type == null || @@ -111,13 +111,13 @@ public class Degeneracy extends VariantStratifier { type = newtype; String aakey = String.format("refseq.variantAA_%d", annotationId); - aa = eval.getAttributeAsString(aakey); + aa = eval.getAttributeAsString(aakey, null); if (aa != null) { String framekey = String.format("refseq.frame_%d", annotationId); if (eval.hasAttribute(framekey)) { - frame = eval.getAttributeAsInt(framekey); + frame = eval.getAttributeAsInt(framekey, 0); } } } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java index 0de871fe6..4af12fbd1 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java @@ -32,7 +32,7 @@ public class FunctionalClass extends VariantStratifier { String type = null; if (eval.hasAttribute("refseq.functionalClass")) { - type = eval.getAttributeAsString("refseq.functionalClass"); + type = eval.getAttributeAsString("refseq.functionalClass", null); } else if (eval.hasAttribute("refseq.functionalClass_1")) { int annotationId = 1; String key; @@ -40,7 +40,7 @@ public class FunctionalClass extends VariantStratifier { do { key = String.format("refseq.functionalClass_%d", annotationId); - String newtype = eval.getAttributeAsString(key); + String newtype = eval.getAttributeAsString(key, null); if ( newtype != null && !newtype.equalsIgnoreCase("null") && ( type == null || diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VQSRCalibrationCurve.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VQSRCalibrationCurve.java index bc7252ec2..04ba3ff14 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VQSRCalibrationCurve.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VQSRCalibrationCurve.java @@ -115,7 +115,7 @@ public class VQSRCalibrationCurve { if ( vc.isFiltered() ) return 0.0; else if ( vc.hasAttribute(VQSRQualKey) ) { - double qual = vc.getAttributeAsDouble(VQSRQualKey); + double qual = vc.getAttributeAsDouble(VQSRQualKey, 0.0); return probTrueVariant(qual); } else { throw new UserException.VariantContextMissingRequiredField(VQSRQualKey, vc); @@ -143,7 +143,7 @@ public class VQSRCalibrationCurve { for ( int i = 0; i < log10Likelihoods.length; i++) { double p = Math.pow(10, log10Likelihoods[i]); double q = alpha * p + (1-alpha) * noInfoPr; - if ( DEBUG ) System.out.printf(" vqslod = %.2f, p = %.2e, alpha = %.2e, q = %.2e%n", vc.getAttributeAsDouble(VQSRQualKey), p, alpha, q); + if ( DEBUG ) System.out.printf(" vqslod = %.2f, p = %.2e, alpha = %.2e, q = %.2e%n", vc.getAttributeAsDouble(VQSRQualKey, 0.0), p, alpha, q); updated[i] = Math.log10(q); } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/SelectVariants.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/SelectVariants.java index bb3cd82a1..ceafb0cf5 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/SelectVariants.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/SelectVariants.java @@ -575,7 +575,7 @@ public class SelectVariants extends RodWalker { // ok we have a comp VC and we need to match the AF spectrum of inputAFRodName. // We then pick a variant with probablity AF*desiredFraction if ( sub.hasAttribute(VCFConstants.ALLELE_FREQUENCY_KEY) ) { - String afo = sub.getAttributeAsString(VCFConstants.ALLELE_FREQUENCY_KEY); + String afo = sub.getAttributeAsString(VCFConstants.ALLELE_FREQUENCY_KEY, null); double af; double afBoost = 1.0; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantsToTable.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantsToTable.java index 2a877fb09..aafbe4db4 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantsToTable.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantsToTable.java @@ -192,7 +192,7 @@ public class VariantsToTable extends RodWalker { if ( getters.containsKey(field) ) { val = getters.get(field).get(vc); } else if ( vc.hasAttribute(field) ) { - val = vc.getAttributeAsString(field); + val = vc.getAttributeAsString(field, null); } else if ( isWildCard(field) ) { Set wildVals = new HashSet(); for ( Map.Entry elt : vc.getAttributes().entrySet()) { diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFCodec.java index cd320b332..94e40fc98 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFCodec.java @@ -105,7 +105,10 @@ public class VCFCodec extends AbstractVCFCodec { * @return a set of the filters applied or null if filters were not applied to the record (e.g. as per the missing value in a VCF) */ protected Set parseFilters(String filterString) { + return parseFilters(filterHash, lineNo, filterString); + } + public static Set parseFilters(final Map> cache, final int lineNo, final String filterString) { // null for unfiltered if ( filterString.equals(VCFConstants.UNFILTERED) ) return null; @@ -113,13 +116,13 @@ public class VCFCodec extends AbstractVCFCodec { if ( filterString.equals(VCFConstants.PASSES_FILTERS_v4) ) return Collections.emptySet(); if ( filterString.equals(VCFConstants.PASSES_FILTERS_v3) ) - generateException(VCFConstants.PASSES_FILTERS_v3 + " is an invalid filter name in vcf4"); + generateException(VCFConstants.PASSES_FILTERS_v3 + " is an invalid filter name in vcf4", lineNo); if ( filterString.length() == 0 ) - generateException("The VCF specification requires a valid filter status"); + generateException("The VCF specification requires a valid filter status", lineNo); // do we have the filter string cached? - if ( filterHash.containsKey(filterString) ) - return filterHash.get(filterString); + if ( cache != null && cache.containsKey(filterString) ) + return Collections.unmodifiableSet(cache.get(filterString)); // empty set for passes filters LinkedHashSet fFields = new LinkedHashSet(); @@ -129,7 +132,8 @@ public class VCFCodec extends AbstractVCFCodec { else fFields.addAll(Arrays.asList(filterString.split(VCFConstants.FILTER_CODE_SEPARATOR))); - filterHash.put(filterString, fFields); + fFields = fFields; + if ( cache != null ) cache.put(filterString, fFields); return Collections.unmodifiableSet(fFields); } diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/Genotype.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/Genotype.java index fdf3d97db..85d752003 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/Genotype.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/Genotype.java @@ -293,17 +293,8 @@ public class Genotype { return commonInfo.getAttribute(key, defaultValue); } - public String getAttributeAsString(String key) { return commonInfo.getAttributeAsString(key); } public String getAttributeAsString(String key, String defaultValue) { return commonInfo.getAttributeAsString(key, defaultValue); } - public int getAttributeAsInt(String key) { return commonInfo.getAttributeAsInt(key); } public int getAttributeAsInt(String key, int defaultValue) { return commonInfo.getAttributeAsInt(key, defaultValue); } - public double getAttributeAsDouble(String key) { return commonInfo.getAttributeAsDouble(key); } public double getAttributeAsDouble(String key, double defaultValue) { return commonInfo.getAttributeAsDouble(key, defaultValue); } - public boolean getAttributeAsBoolean(String key) { return commonInfo.getAttributeAsBoolean(key); } public boolean getAttributeAsBoolean(String key, boolean defaultValue) { return commonInfo.getAttributeAsBoolean(key, defaultValue); } - - public Integer getAttributeAsIntegerNoException(String key) { return commonInfo.getAttributeAsIntegerNoException(key); } - public Double getAttributeAsDoubleNoException(String key) { return commonInfo.getAttributeAsDoubleNoException(key); } - public String getAttributeAsStringNoException(String key) { return commonInfo.getAttributeAsStringNoException(key); } - public Boolean getAttributeAsBooleanNoException(String key) { return commonInfo.getAttributeAsBooleanNoException(key); } } \ No newline at end of file diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/InferredGeneticContext.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/InferredGeneticContext.java index 3d162adb0..4266fb4b5 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/InferredGeneticContext.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/InferredGeneticContext.java @@ -204,27 +204,40 @@ public final class InferredGeneticContext { return defaultValue; } -// public AttributedObject getAttributes(Collection keys) { -// AttributedObject selected = new AttributedObject(); -// -// for ( Object key : keys ) -// selected.putAttribute(key, this.getAttribute(key)); -// -// return selected; -// } + public String getAttributeAsString(String key, String defaultValue) { + Object x = getAttribute(key); + if ( x == null ) return defaultValue; + if ( x instanceof String ) return (String)x; + return String.valueOf(x); // throws an exception if this isn't a string + } - public String getAttributeAsString(String key) { return (String.valueOf(getAttribute(key))); } // **NOTE**: will turn a null Object into the String "null" - public int getAttributeAsInt(String key) { Object x = getAttribute(key); return x instanceof Integer ? (Integer)x : Integer.valueOf((String)x); } - public double getAttributeAsDouble(String key) { Object x = getAttribute(key); return x instanceof Double ? (Double)x : Double.valueOf((String)x); } - public boolean getAttributeAsBoolean(String key) { Object x = getAttribute(key); return x instanceof Boolean ? (Boolean)x : Boolean.valueOf((String)x); } + public int getAttributeAsInt(String key, int defaultValue) { + Object x = getAttribute(key); + if ( x == null ) return defaultValue; + if ( x instanceof Integer ) return (Integer)x; + return Integer.valueOf((String)x); // throws an exception if this isn't a string + } - public String getAttributeAsString(String key, String defaultValue) { return (String)getAttribute(key, defaultValue); } - public int getAttributeAsInt(String key, int defaultValue) { return (Integer)getAttribute(key, defaultValue); } - public double getAttributeAsDouble(String key, double defaultValue) { return (Double)getAttribute(key, defaultValue); } - public boolean getAttributeAsBoolean(String key, boolean defaultValue){ return (Boolean)getAttribute(key, defaultValue); } + public double getAttributeAsDouble(String key, double defaultValue) { + Object x = getAttribute(key); + if ( x == null ) return defaultValue; + if ( x instanceof Double ) return (Double)x; + return Double.valueOf((String)x); // throws an exception if this isn't a string + } - public Integer getAttributeAsIntegerNoException(String key) { try {return getAttributeAsInt(key);} catch (Exception e) {return null;} } - public Double getAttributeAsDoubleNoException(String key) { try {return getAttributeAsDouble(key);} catch (Exception e) {return null;} } - public String getAttributeAsStringNoException(String key) { if (getAttribute(key) == null) return null; return getAttributeAsString(key); } - public Boolean getAttributeAsBooleanNoException(String key) { try {return getAttributeAsBoolean(key);} catch (Exception e) {return null;} } + public boolean getAttributeAsBoolean(String key, boolean defaultValue) { + Object x = getAttribute(key); + if ( x == null ) return defaultValue; + if ( x instanceof Boolean ) return (Boolean)x; + return Boolean.valueOf((String)x); // throws an exception if this isn't a string + } + +// public String getAttributeAsString(String key) { return (String.valueOf(getAttribute(key))); } // **NOTE**: will turn a null Object into the String "null" +// public int getAttributeAsInt(String key) { Object x = getAttribute(key); return x instanceof Integer ? (Integer)x : Integer.valueOf((String)x); } +// public double getAttributeAsDouble(String key) { Object x = getAttribute(key); return x instanceof Double ? (Double)x : Double.valueOf((String)x); } +// public boolean getAttributeAsBoolean(String key) { Object x = getAttribute(key); return x instanceof Boolean ? (Boolean)x : Boolean.valueOf((String)x); } +// public Integer getAttributeAsIntegerNoException(String key) { try {return getAttributeAsInt(key);} catch (Exception e) {return null;} } +// public Double getAttributeAsDoubleNoException(String key) { try {return getAttributeAsDouble(key);} catch (Exception e) {return null;} } +// public String getAttributeAsStringNoException(String key) { if (getAttribute(key) == null) return null; return getAttributeAsString(key); } +// public Boolean getAttributeAsBooleanNoException(String key) { try {return getAttributeAsBoolean(key);} catch (Exception e) {return null;} } } \ No newline at end of file diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java index 673fe4529..e6637a5d9 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java @@ -666,21 +666,11 @@ public class VariantContext implements Feature { // to enable tribble intergrati return commonInfo.getAttribute(key, defaultValue); } - public String getAttributeAsString(String key) { return commonInfo.getAttributeAsString(key); } public String getAttributeAsString(String key, String defaultValue) { return commonInfo.getAttributeAsString(key, defaultValue); } - public int getAttributeAsInt(String key) { return commonInfo.getAttributeAsInt(key); } public int getAttributeAsInt(String key, int defaultValue) { return commonInfo.getAttributeAsInt(key, defaultValue); } - public double getAttributeAsDouble(String key) { return commonInfo.getAttributeAsDouble(key); } public double getAttributeAsDouble(String key, double defaultValue) { return commonInfo.getAttributeAsDouble(key, defaultValue); } - public boolean getAttributeAsBoolean(String key) { return commonInfo.getAttributeAsBoolean(key); } public boolean getAttributeAsBoolean(String key, boolean defaultValue) { return commonInfo.getAttributeAsBoolean(key, defaultValue); } - public Integer getAttributeAsIntegerNoException(String key) { return commonInfo.getAttributeAsIntegerNoException(key); } - public Double getAttributeAsDoubleNoException(String key) { return commonInfo.getAttributeAsDoubleNoException(key); } - public String getAttributeAsStringNoException(String key) { return commonInfo.getAttributeAsStringNoException(key); } - public Boolean getAttributeAsBooleanNoException(String key) { return commonInfo.getAttributeAsBooleanNoException(key); } - - // --------------------------------------------------------------------------------------------------------- // // Working with alleles diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java index 986d6305c..d5c541b19 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java @@ -565,11 +565,11 @@ public class VariantContextUtils { // special case DP (add it up) and ID (just preserve it) // if (vc.hasAttribute(VCFConstants.DEPTH_KEY)) - depth += Integer.valueOf(vc.getAttributeAsString(VCFConstants.DEPTH_KEY)); + depth += vc.getAttributeAsInt(VCFConstants.DEPTH_KEY, 0); if (rsID == null && vc.hasID()) rsID = vc.getID(); if (mergeInfoWithMaxAC && vc.hasAttribute(VCFConstants.ALLELE_COUNT_KEY)) { - String rawAlleleCounts = vc.getAttributeAsString(VCFConstants.ALLELE_COUNT_KEY); + String rawAlleleCounts = vc.getAttributeAsString(VCFConstants.ALLELE_COUNT_KEY, null); // lets see if the string contains a , separator if (rawAlleleCounts.contains(VCFConstants.INFO_FIELD_ARRAY_SEPARATOR)) { List alleleCountArray = Arrays.asList(rawAlleleCounts.substring(1, rawAlleleCounts.length() - 1).split(VCFConstants.INFO_FIELD_ARRAY_SEPARATOR)); @@ -1147,9 +1147,7 @@ public class VariantContextUtils { for (String orAttrib : MERGE_OR_ATTRIBS) { boolean attribVal = false; for (VariantContext vc : vcList) { - Boolean val = vc.getAttributeAsBooleanNoException(orAttrib); - if (val != null) - attribVal = (attribVal || val); + attribVal = vc.getAttributeAsBoolean(orAttrib, false); if (attribVal) // already true, so no reason to continue: break; } @@ -1159,7 +1157,7 @@ public class VariantContextUtils { // Merge ID fields: String iDVal = null; for (VariantContext vc : vcList) { - String val = vc.getAttributeAsStringNoException(VariantContext.ID_KEY); + String val = vc.getAttributeAsString(VariantContext.ID_KEY, null); if (val != null && !val.equals(VCFConstants.EMPTY_ID_FIELD)) { if (iDVal == null) iDVal = val; @@ -1239,8 +1237,10 @@ public class VariantContextUtils { public PhaseAndQuality(Genotype gt) { this.isPhased = gt.isPhased(); - if (this.isPhased) - this.PQ = gt.getAttributeAsDoubleNoException(ReadBackedPhasingWalker.PQ_KEY); + if (this.isPhased) { + this.PQ = gt.getAttributeAsDouble(ReadBackedPhasingWalker.PQ_KEY, -1); + if ( this.PQ == -1 ) this.PQ = null; + } } } From 124ef6c4834d8a948629291762b7f9d7d9696d50 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 2 Sep 2011 21:12:28 -0400 Subject: [PATCH 004/196] MISSING_VALUE now gets defaultValue in getAttribute functions --- .../sting/utils/variantcontext/InferredGeneticContext.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/InferredGeneticContext.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/InferredGeneticContext.java index 4266fb4b5..bf16cd1cf 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/InferredGeneticContext.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/InferredGeneticContext.java @@ -1,6 +1,8 @@ package org.broadinstitute.sting.utils.variantcontext; +import org.broadinstitute.sting.utils.codecs.vcf.VCFConstants; + import java.util.*; @@ -213,7 +215,7 @@ public final class InferredGeneticContext { public int getAttributeAsInt(String key, int defaultValue) { Object x = getAttribute(key); - if ( x == null ) return defaultValue; + if ( x == null || x == VCFConstants.MISSING_VALUE_v4 ) return defaultValue; if ( x instanceof Integer ) return (Integer)x; return Integer.valueOf((String)x); // throws an exception if this isn't a string } From 03aa04e37c8a2cf31d650cba6a8c703fbb033978 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 2 Sep 2011 21:13:08 -0400 Subject: [PATCH 005/196] Simple refactoring to make formating functions public --- .../sting/utils/codecs/vcf/AbstractVCFCodec.java | 2 +- .../sting/utils/codecs/vcf/StandardVCFWriter.java | 11 +++++++++-- .../sting/utils/codecs/vcf/VCFCodec.java | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java index bb212e128..624d06a71 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java @@ -227,7 +227,7 @@ public abstract class AbstractVCFCodec implements FeatureCodec, NameAwareCodec, throw new UserException.MalformedVCF(message, lineNo); } - private static void generateException(String message, int lineNo) { + protected static void generateException(String message, int lineNo) { throw new UserException.MalformedVCF(message, lineNo); } diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/StandardVCFWriter.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/StandardVCFWriter.java index d3705813c..e28cd7598 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/StandardVCFWriter.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/StandardVCFWriter.java @@ -275,7 +275,7 @@ public class StandardVCFWriter implements VCFWriter { mWriter.write(VCFConstants.FIELD_SEPARATOR); // FILTER - String filters = vc.isFiltered() ? ParsingUtils.join(";", ParsingUtils.sortList(vc.getFilters())) : (filtersWereAppliedToContext || vc.filtersWereApplied() ? VCFConstants.PASSES_FILTERS_v4 : VCFConstants.UNFILTERED); + String filters = getFilterString(vc, filtersWereAppliedToContext); mWriter.write(filters); mWriter.write(VCFConstants.FIELD_SEPARATOR); @@ -319,7 +319,14 @@ public class StandardVCFWriter implements VCFWriter { } catch (IOException e) { throw new RuntimeException("Unable to write the VCF object to " + locationString()); } + } + public static final String getFilterString(final VariantContext vc) { + return getFilterString(vc, false); + } + + public static final String getFilterString(final VariantContext vc, boolean forcePASS) { + return vc.isFiltered() ? ParsingUtils.join(";", ParsingUtils.sortList(vc.getFilters())) : (forcePASS || vc.filtersWereApplied() ? VCFConstants.PASSES_FILTERS_v4 : VCFConstants.UNFILTERED); } private String getQualValue(double qual) { @@ -462,7 +469,7 @@ public class StandardVCFWriter implements VCFWriter { mWriter.write(encoding); } - private static String formatVCFField(Object val) { + public static String formatVCFField(Object val) { String result; if ( val == null ) result = VCFConstants.MISSING_VALUE_v4; diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFCodec.java index 94e40fc98..42ea05355 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFCodec.java @@ -118,7 +118,7 @@ public class VCFCodec extends AbstractVCFCodec { if ( filterString.equals(VCFConstants.PASSES_FILTERS_v3) ) generateException(VCFConstants.PASSES_FILTERS_v3 + " is an invalid filter name in vcf4", lineNo); if ( filterString.length() == 0 ) - generateException("The VCF specification requires a valid filter status", lineNo); + generateException("The VCF specification requires a valid filter status: filter was " + filterString, lineNo); // do we have the filter string cached? if ( cache != null && cache.containsKey(filterString) ) From 048202d18e42444e47e6237246b31d24c57dec9a Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 2 Sep 2011 21:13:28 -0400 Subject: [PATCH 006/196] Bugfix for cached quals --- .../java/src/org/broadinstitute/sting/utils/QualityUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/QualityUtils.java b/public/java/src/org/broadinstitute/sting/utils/QualityUtils.java index 093da7dd6..19e03a19d 100755 --- a/public/java/src/org/broadinstitute/sting/utils/QualityUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/QualityUtils.java @@ -17,7 +17,7 @@ public class QualityUtils { private static double qualToErrorProbCache[] = new double[256]; static { - for (byte i = 0; i < 256; i++) qualToErrorProbCache[i] = qualToErrorProbRaw(i); + for (int i = 0; i < 256; i++) qualToErrorProbCache[i] = qualToErrorProbRaw((byte)i); } /** From d471617c65f6d1f3885ea6628b7676ed6bbc6f8d Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 2 Sep 2011 21:15:19 -0400 Subject: [PATCH 007/196] GATK binary VCF (gvcf) prototype format for efficiency testing -- Very minimal working version that can read / write binary VCFs with genotypes -- Already 10x faster for sites, 5x for fully parsed genotypes, and 1000x for skipping genotypes when reading --- .../broadinstitute/sting/utils/gvcf/GVCF.java | 252 ++++++++++++++++++ .../sting/utils/gvcf/GVCFGenotype.java | 147 ++++++++++ .../sting/utils/gvcf/GVCFHeader.java | 180 +++++++++++++ .../sting/utils/gvcf/GVCFHeaderBuilder.java | 80 ++++++ 4 files changed, 659 insertions(+) create mode 100644 public/java/src/org/broadinstitute/sting/utils/gvcf/GVCF.java create mode 100644 public/java/src/org/broadinstitute/sting/utils/gvcf/GVCFGenotype.java create mode 100644 public/java/src/org/broadinstitute/sting/utils/gvcf/GVCFHeader.java create mode 100644 public/java/src/org/broadinstitute/sting/utils/gvcf/GVCFHeaderBuilder.java diff --git a/public/java/src/org/broadinstitute/sting/utils/gvcf/GVCF.java b/public/java/src/org/broadinstitute/sting/utils/gvcf/GVCF.java new file mode 100644 index 000000000..8568c1aab --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/utils/gvcf/GVCF.java @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2011, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.utils.gvcf; + +import org.broadinstitute.sting.utils.QualityUtils; +import org.broadinstitute.sting.utils.Utils; +import org.broadinstitute.sting.utils.codecs.vcf.StandardVCFWriter; +import org.broadinstitute.sting.utils.codecs.vcf.VCFConstants; +import org.broadinstitute.sting.utils.exceptions.UserException; +import org.broadinstitute.sting.utils.variantcontext.Allele; +import org.broadinstitute.sting.utils.variantcontext.Genotype; +import org.broadinstitute.sting.utils.variantcontext.VariantContext; + +import java.io.*; +import java.util.*; + +/** + * GATK binary VCF record + * + * @author Your Name + * @since Date created + */ +public class GVCF { + private final static int RECORD_TERMINATOR = 123456789; + private int chromOffset; + private int start, stop; + private String id; + private List alleleMap; + private int alleleOffsets[]; + private float qual; + private byte refPad; + private String info; + private int filterOffset; + + private List genotypes = Collections.emptyList(); + + public GVCF(final GVCFHeaderBuilder gvcfHeaderBuilder, final VariantContext vc, boolean skipGenotypes) { + chromOffset = gvcfHeaderBuilder.encodeString(vc.getChr()); + start = vc.getStart(); + stop = vc.getEnd(); + refPad = vc.hasReferenceBaseForIndel() ? vc.getReferenceBaseForIndel() : 0; + id = vc.getID(); + + // encode alleles + alleleMap = new ArrayList(vc.getNAlleles()); + alleleOffsets = new int[vc.getNAlleles()]; + alleleMap.add(vc.getReference()); + alleleOffsets[0] = gvcfHeaderBuilder.encodeAllele(vc.getReference()); + for ( int i = 0; i < vc.getAlternateAlleles().size(); i++ ) { + alleleMap.add(vc.getAlternateAllele(i)); + alleleOffsets[i+1] = gvcfHeaderBuilder.encodeAllele(vc.getAlternateAllele(i)); + } + + qual = (float)vc.getNegLog10PError(); //qualToByte(vc.getPhredScaledQual()); + info = infoFieldString(vc, gvcfHeaderBuilder); + filterOffset = gvcfHeaderBuilder.encodeString(StandardVCFWriter.getFilterString(vc)); + + if ( ! skipGenotypes ) { + genotypes = encodeGenotypes(gvcfHeaderBuilder, vc); + } + } + + public GVCF(DataInputStream inputStream, boolean skipGenotypes) throws IOException { + chromOffset = inputStream.readInt(); + start = inputStream.readInt(); + stop = inputStream.readInt(); + id = inputStream.readUTF(); + refPad = inputStream.readByte(); + alleleOffsets = readIntArray(inputStream); + qual = inputStream.readFloat(); + info = inputStream.readUTF(); + filterOffset = inputStream.readInt(); + + int nGenotypes = inputStream.readInt(); + int sizeOfGenotypes = inputStream.readInt(); + if ( skipGenotypes ) { + genotypes = Collections.emptyList(); + inputStream.skipBytes(sizeOfGenotypes); + } else { + genotypes = new ArrayList(nGenotypes); + for ( int i = 0; i < nGenotypes; i++ ) + genotypes.add(new GVCFGenotype(this, inputStream)); + } + + int recordDone = inputStream.readInt(); + if ( recordDone != RECORD_TERMINATOR ) + throw new UserException.MalformedFile("Record not terminated by RECORD_TERMINATOR key"); + } + + public VariantContext decode(final String source, final GVCFHeader header) { + final String contig = header.getString(chromOffset); + alleleMap = header.getAlleles(alleleOffsets); + double negLog10PError = qual; // QualityUtils.qualToErrorProb(qual); + Set filters = header.getFilters(filterOffset); + Map attributes = new HashMap(); + attributes.put("INFO", info); + Byte refPadByte = refPad == 0 ? null : refPad; + Map genotypes = decodeGenotypes(header); + + return new VariantContext(source, contig, start, stop, alleleMap, genotypes, negLog10PError, filters, attributes, refPadByte); + } + + private Map decodeGenotypes(final GVCFHeader header) { + if ( genotypes.isEmpty() ) + return VariantContext.NO_GENOTYPES; + else { + Map map = new TreeMap(); + + for ( int i = 0; i < genotypes.size(); i++ ) { + final String sampleName = header.getSample(i); + final Genotype g = genotypes.get(i).decode(sampleName, header, this, alleleMap); + map.put(sampleName, g); + } + + return map; + } + } + + private List encodeGenotypes(final GVCFHeaderBuilder gvcfHeaderBuilder, final VariantContext vc) { + int nGenotypes = vc.getNSamples(); + if ( nGenotypes > 0 ) { + List genotypes = new ArrayList(nGenotypes); + for ( int i = 0; i < nGenotypes; i++ ) genotypes.add(null); + + for ( Genotype g : vc.getGenotypes().values() ) { + int i = gvcfHeaderBuilder.encodeSample(g.getSampleName()); + genotypes.set(i, new GVCFGenotype(gvcfHeaderBuilder, alleleMap, g)); + } + + return genotypes; + } else { + return Collections.emptyList(); + } + } + + public int getNAlleles() { return alleleOffsets.length; } + + public int write(DataOutputStream outputStream) throws IOException { + int startSize = outputStream.size(); + outputStream.writeInt(chromOffset); + outputStream.writeInt(start); + outputStream.writeInt(stop); + outputStream.writeUTF(id); + outputStream.writeByte(refPad); + writeIntArray(alleleOffsets, outputStream, true); + outputStream.writeFloat(qual); + outputStream.writeUTF(info); + outputStream.writeInt(filterOffset); + + int nGenotypes = genotypes.size(); + int expectedSizeOfGenotypes = nGenotypes == 0 ? 0 : genotypes.get(0).sizeInBytes() * nGenotypes; + outputStream.writeInt(nGenotypes); + outputStream.writeInt(expectedSizeOfGenotypes); + int obsSizeOfGenotypes = 0; + for ( GVCFGenotype g : genotypes ) + obsSizeOfGenotypes += g.write(outputStream); + if ( obsSizeOfGenotypes != expectedSizeOfGenotypes ) + throw new RuntimeException("Expect and observed genotype sizes disagree! expect = " + expectedSizeOfGenotypes + " obs =" + obsSizeOfGenotypes); + + outputStream.writeInt(RECORD_TERMINATOR); + return outputStream.size() - startSize; + } + + private final String infoFieldString(VariantContext vc, final GVCFHeaderBuilder gvcfHeaderBuilder) { + StringBuilder s = new StringBuilder(); + + boolean first = true; + for ( Map.Entry field : vc.getAttributes().entrySet() ) { + String key = field.getKey(); + if ( key.equals(VariantContext.ID_KEY) || key.equals(VariantContext.UNPARSED_GENOTYPE_MAP_KEY) || key.equals(VariantContext.UNPARSED_GENOTYPE_PARSER_KEY) ) + continue; + int stringIndex = gvcfHeaderBuilder.encodeString(key); + String outputValue = StandardVCFWriter.formatVCFField(field.getValue()); + if ( outputValue != null ) { + if ( ! first ) s.append(";"); + s.append(stringIndex).append("=").append(outputValue); + first = false; + } + } + + return s.toString(); + } + + private final static int BUFFER_SIZE = 1048576; // 2**20 + public static DataOutputStream createOutputStream(final File file) throws FileNotFoundException { + return new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file), BUFFER_SIZE)); + } + + public static DataInputStream createInputStream(final File file) throws FileNotFoundException { + return new DataInputStream(new BufferedInputStream(new FileInputStream(file), BUFFER_SIZE)); + } + + protected final static int[] readIntArray(final DataInputStream inputStream) throws IOException { + return readIntArray(inputStream, inputStream.readInt()); + } + + protected final static int[] readIntArray(final DataInputStream inputStream, int size) throws IOException { + int[] array = new int[size]; + for ( int i = 0; i < array.length; i++ ) + array[i] = inputStream.readInt(); + return array; + } + + protected final static void writeIntArray(int[] array, final DataOutputStream outputStream, boolean writeSize) throws IOException { + if ( writeSize ) outputStream.writeInt(array.length); + for ( int i : array ) + outputStream.writeInt(i); + } + + protected final static byte[] readByteArray(final DataInputStream inputStream) throws IOException { + return readByteArray(inputStream, inputStream.readInt()); + } + + protected final static byte[] readByteArray(final DataInputStream inputStream, int size) throws IOException { + byte[] array = new byte[size]; + for ( int i = 0; i < array.length; i++ ) + array[i] = inputStream.readByte(); + return array; + } + + protected final static void writeByteArray(byte[] array, final DataOutputStream outputStream, boolean writeSize) throws IOException { + if ( writeSize ) outputStream.writeInt(array.length); + for ( byte i : array ) + outputStream.writeByte(i); + } + + protected final static byte qualToByte(double phredScaledQual) { + return (byte)Math.round(Math.min(phredScaledQual, 255)); + } +} diff --git a/public/java/src/org/broadinstitute/sting/utils/gvcf/GVCFGenotype.java b/public/java/src/org/broadinstitute/sting/utils/gvcf/GVCFGenotype.java new file mode 100644 index 000000000..2ef6d9b3a --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/utils/gvcf/GVCFGenotype.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2011, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.utils.gvcf; + +import org.broadinstitute.sting.utils.variantcontext.Allele; +import org.broadinstitute.sting.utils.variantcontext.Genotype; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.*; + +/** + * GATK binary VCF record + * + * @author Your Name + * @since Date created + */ +public class GVCFGenotype { + private byte gq; + private int gt; + private int dp; + private int ad[]; + private byte[] pl; + + // todo -- what to do about phasing? Perhaps we shouldn't support it + // todo -- is the FL field generic or just a flag? Should we even support per sample filtering? + + public GVCFGenotype(final GVCFHeaderBuilder gvcfHeaderBuilder, final List allAlleles, Genotype genotype) { + gq = GVCF.qualToByte(genotype.getPhredScaledQual()); + gt = encodeAlleles(genotype.getAlleles(), allAlleles); + + dp = genotype.getAttributeAsInt("DP", 0); + + int nAlleles = allAlleles.size(); + ad = new int[nAlleles]; + + int npls = nAllelesToNPls(nAlleles); + pl = new byte[npls]; + } + + private int nAllelesToNPls( int nAlleles ) { + return nAlleles*(nAlleles+1) / 2; + } + + public GVCFGenotype(GVCF gvcf, DataInputStream inputStream) throws IOException { + int gqInt = inputStream.readUnsignedByte(); + gq = (byte)gqInt; + gt = inputStream.readInt(); + dp = inputStream.readInt(); + ad = GVCF.readIntArray(inputStream, gvcf.getNAlleles()); + pl = GVCF.readByteArray(inputStream, nAllelesToNPls(gvcf.getNAlleles())); + } + + // 2 alleles => 1 + 8 + 8 + 3 => 20 + protected int sizeInBytes() { + return 1 // gq + + 4 * 2 // gt + dp + + 4 * ad.length // ad + + 1 * pl.length; // pl + } + + public Genotype decode(final String sampleName, final GVCFHeader header, GVCF gvcf, List alleleIndex) { + final List alleles = decodeAlleles(gt, alleleIndex); + final double negLog10PError = gq / 10.0; + final Set filters = Collections.emptySet(); + final Map attributes = new HashMap(); + attributes.put("DP", dp); + attributes.put("AD", ad); + attributes.put("PL", pl); + + return new Genotype(sampleName, alleles, negLog10PError, filters, attributes, false); + } + + private static int encodeAlleles(List gtList, List allAlleles) { + final int nAlleles = gtList.size(); + if ( nAlleles > 4 ) + throw new IllegalArgumentException("encodeAlleles doesn't support more than 4 alt alleles, but I saw " + gtList); + + int gtInt = 0; + for ( int i = 0; i < nAlleles ; i++ ) { + final int bitOffset = i * 8; + final int allelei = getAlleleIndex(gtList.get(i), allAlleles); + final int gti = (allelei + 1) << bitOffset; + gtInt = gtInt | gti; + } + + return gtInt; + } + + private static int getAlleleIndex(Allele q, List allAlleles) { + if ( q.isNoCall() ) + return 254; + for ( int i = 0; i < allAlleles.size(); i++ ) + if ( q.equals(allAlleles.get(i)) ) + return i; + throw new IllegalStateException("getAlleleIndex passed allele not in map! allele " + q + " allAlleles " + allAlleles); + } + + private static List decodeAlleles(int gtInt, List alleleIndex) { + List alleles = new ArrayList(4); + + for ( int i = 0; i < 32; i += 8 ) { + final int gi = (gtInt & (0x000000FF << i)) >> i; + if ( gi != 0 ) { + final int allelei = gi - 1; + alleles.add( allelei == 254 ? Allele.NO_CALL : alleleIndex.get(allelei) ); + } else { + break; + } + } + + return alleles; + } + + public int write(DataOutputStream outputStream) throws IOException { + int startSize = outputStream.size(); + outputStream.writeByte(gq); + outputStream.writeInt(gt); + outputStream.writeInt(dp); + GVCF.writeIntArray(ad, outputStream, false); + GVCF.writeByteArray(pl, outputStream, false); + return outputStream.size() - startSize; + } +} diff --git a/public/java/src/org/broadinstitute/sting/utils/gvcf/GVCFHeader.java b/public/java/src/org/broadinstitute/sting/utils/gvcf/GVCFHeader.java new file mode 100644 index 000000000..c52c975bd --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/utils/gvcf/GVCFHeader.java @@ -0,0 +1,180 @@ +/* + * Copyright (c) 2011, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.utils.gvcf; + +import org.apache.log4j.Logger; +import org.broadinstitute.sting.utils.QualityUtils; +import org.broadinstitute.sting.utils.codecs.vcf.AbstractVCFCodec; +import org.broadinstitute.sting.utils.codecs.vcf.VCFCodec; +import org.broadinstitute.sting.utils.codecs.vcf.VCFConstants; +import org.broadinstitute.sting.utils.exceptions.UserException; +import org.broadinstitute.sting.utils.variantcontext.Allele; + +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.util.*; + +/** + * [Short one sentence description of this walker] + *

+ *

+ * [Functionality of this walker] + *

+ *

+ *

Input

+ *

+ * [Input description] + *

+ *

+ *

Output

+ *

+ * [Output description] + *

+ *

+ *

Examples

+ *
+ *    java
+ *      -jar GenomeAnalysisTK.jar
+ *      -T $WalkerName
+ *  
+ * + * @author Your Name + * @since Date created + */ +public class GVCFHeader { + final protected static Logger logger = Logger.getLogger(GVCFHeader.class); + + private static byte[] MAGIC_HEADER = "GVCF0.1\1".getBytes(); + final List alleles; + final List strings; + final List samples; + final List> filters; + + public GVCFHeader(final Map allelesIn, final Map stringIn, final Map samplesIn) { + this.alleles = linearize(allelesIn); + this.strings = linearize(stringIn); + this.samples = linearize(samplesIn); + this.filters = null; // not used with this constructor + } + + public GVCFHeader(DataInputStream inputStream) throws IOException { + byte[] headerTest = new byte[MAGIC_HEADER.length]; + inputStream.read(headerTest); + if ( ! Arrays.equals(headerTest, MAGIC_HEADER) ) { + throw new UserException("Could not read GVCF file. MAGIC_HEADER missing. Saw " + headerTest); + } else { + alleles = stringsToAlleles(readStrings(inputStream)); + strings = readStrings(inputStream); + samples = readStrings(inputStream); + logger.info(String.format("Allele map of %d elements", alleles.size())); + logger.info(String.format("String map of %d elements", strings.size())); + logger.info(String.format("Sample map of %d elements", samples.size())); + filters = initializeFilterCache(); + } + } + + public int write(final DataOutputStream outputStream) throws IOException { + int startBytes = outputStream.size(); + outputStream.write(MAGIC_HEADER); + write(outputStream, allelesToStrings(alleles)); + write(outputStream, strings); + write(outputStream, samples); + return outputStream.size() - startBytes; + } + + public void write(DataOutputStream outputStream, List l) throws IOException { + outputStream.writeInt(l.size()); + for ( String elt : l ) outputStream.writeUTF(elt); + } + + private List allelesToStrings(List alleles) { + List strings = new ArrayList(alleles.size()); + for ( Allele allele : alleles ) strings.add(allele.toString()); + return strings; + } + + private List> initializeFilterCache() { + // required to allow offset -> set lookup + List> l = new ArrayList>(strings.size()); + for ( int i = 0; i < strings.size(); i++ ) l.add(null); + return l; + } + + private static List stringsToAlleles(final List strings) { + final List alleles = new ArrayList(strings.size()); + for ( String string : strings ) { + boolean isRef = string.endsWith("*"); + if ( isRef ) string = string.substring(0, string.length() - 1); + alleles.add(Allele.create(string, isRef)); + } + return alleles; + } + + private static List readStrings(final DataInputStream inputStream) throws IOException { + final int nStrings = inputStream.readInt(); + + final List strings = new ArrayList(nStrings); + for ( int i = 0; i < nStrings; i++ ) { + strings.add(inputStream.readUTF()); + } + + return strings; + } + + private static List linearize(final Map map) { + final ArrayList l = new ArrayList(map.size()); + for ( int i = 0; i < map.size(); i++ ) l.add(null); + for ( final Map.Entry elt : map.entrySet() ) + l.set(elt.getValue(), elt.getKey()); + return l; + } + + public String getSample(final int offset) { return samples.get(offset); } + public String getString(final int offset) { return strings.get(offset); } + public Allele getAllele(final int offset) { return alleles.get(offset); } + public List getAlleles(final int[] offsets) { + final List alleles = new ArrayList(offsets.length); + for ( int i : offsets ) alleles.add(getAllele(i)); + return alleles; + } + + public Set getFilters(final int offset) { + Set cached = filters.get(offset); + + if ( cached != null ) + return cached; + else { + final String filterString = getString(offset); + if ( filterString.equals(VCFConstants.UNFILTERED) ) + return null; // UNFILTERED records are represented by null + else { + Set set = VCFCodec.parseFilters(null, -1, filterString); + filters.set(offset, set); // remember the result + return set; + } + } + } +} diff --git a/public/java/src/org/broadinstitute/sting/utils/gvcf/GVCFHeaderBuilder.java b/public/java/src/org/broadinstitute/sting/utils/gvcf/GVCFHeaderBuilder.java new file mode 100644 index 000000000..2d045b8ea --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/utils/gvcf/GVCFHeaderBuilder.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2011, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.utils.gvcf; + +import org.broadinstitute.sting.utils.variantcontext.Allele; + +import java.util.HashMap; +import java.util.Map; + +/** + * [Short one sentence description of this walker] + *

+ *

+ * [Functionality of this walker] + *

+ *

+ *

Input

+ *

+ * [Input description] + *

+ *

+ *

Output

+ *

+ * [Output description] + *

+ *

+ *

Examples

+ *
+ *    java
+ *      -jar GenomeAnalysisTK.jar
+ *      -T $WalkerName
+ *  
+ * + * @author Your Name + * @since Date created + */ +public class GVCFHeaderBuilder { + Map alleles = new HashMap(); + Map strings = new HashMap(); + Map samples = new HashMap(); + + public GVCFHeader createHeader() { + return new GVCFHeader(alleles, strings, samples); + } + + public int encodeString(final String chr) { return encode(strings, chr); } + public int encodeAllele(final Allele allele) { return encode(alleles, allele); } + public int encodeSample(final String sampleName) { return encode(samples, sampleName); } + + private int encode(Map map, T key) { + Integer v = map.get(key); + if ( v == null ) { + v = map.size(); + map.put(key, v); + } + return v; + } +} From 9127849f5d2871621945a4f005af91dc7cfa8dd9 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Wed, 7 Sep 2011 14:54:10 -0400 Subject: [PATCH 009/196] BugFix for unit test --- .../sting/gatk/traversals/TraverseReadsUnitTest.java | 1 + 1 file changed, 1 insertion(+) diff --git a/public/java/test/org/broadinstitute/sting/gatk/traversals/TraverseReadsUnitTest.java b/public/java/test/org/broadinstitute/sting/gatk/traversals/TraverseReadsUnitTest.java index c0d32a05b..7f4d96add 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/traversals/TraverseReadsUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/traversals/TraverseReadsUnitTest.java @@ -127,6 +127,7 @@ public class TraverseReadsUnitTest extends BaseTest { Object accumulator = countReadWalker.reduceInit(); while (shardStrategy.hasNext()) { + traversalEngine.startTimersIfNecessary(); Shard shard = shardStrategy.next(); if (shard == null) { From aa9e32f2f115a81b643b52317d40fc46e79195ef Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Wed, 7 Sep 2011 15:48:06 -0400 Subject: [PATCH 010/196] Reverting Mark's previous commit as per the open discussion. Now the eval modules check isPolymorphic() before accruing stats when appropriate. Fixed the IndelLengthHistogram module not to error out if the indel isn't simple (that would have been bad). Only integration test that needed to be updated was the tranches one based on a separate commit from Mark. --- .../varianteval/evaluators/CompOverlap.java | 2 +- .../varianteval/evaluators/CountVariants.java | 3 +- .../evaluators/IndelLengthHistogram.java | 15 +-- .../evaluators/IndelStatistics.java | 2 +- .../evaluators/SimpleMetricsByAC.java | 2 +- .../evaluators/ThetaVariantEvaluator.java | 103 +++++++++--------- .../evaluators/TiTvVariantEvaluator.java | 2 +- .../evaluators/ValidationReport.java | 5 +- .../evaluators/VariantQualityScore.java | 2 +- .../varianteval/util/VariantEvalUtils.java | 2 +- .../VariantEvalIntegrationTest.java | 2 +- 11 files changed, 69 insertions(+), 71 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/CompOverlap.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/CompOverlap.java index 2ea64c49c..5ccacac37 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/CompOverlap.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/CompOverlap.java @@ -75,7 +75,7 @@ public class CompOverlap extends VariantEvaluator implements StandardEval { } public String update2(VariantContext eval, VariantContext comp, RefMetaDataTracker tracker, ReferenceContext ref, AlignmentContext context) { - boolean evalIsGood = eval != null && eval.isVariant(); + boolean evalIsGood = eval != null && eval.isPolymorphic(); boolean compIsGood = comp != null && comp.isNotFiltered() && (eval == null || comp.getType() == eval.getType()); if (compIsGood) nCompVariants++; // count the number of comp events diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/CountVariants.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/CountVariants.java index 59ef3d992..2913c97a6 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/CountVariants.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/CountVariants.java @@ -100,11 +100,12 @@ public class CountVariants extends VariantEvaluator implements StandardEval { // So in order to maintain consistency with the previous implementation (and the intention of the original author), I've // added in a proxy check for monomorphic status here. // Protect against case when vc only as no-calls too - can happen if we strafity by sample and sample as a single no-call. - if ( !vc1.isVariant() || (vc1.hasGenotypes() && vc1.getHomRefCount() + vc1.getNoCallCount() == vc1.getNSamples()) ) { + if ( vc1.isMonomorphic() ) { nRefLoci++; } else { switch (vc1.getType()) { case NO_VARIATION: + // shouldn't get here break; case SNP: nVariantLoci++; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/IndelLengthHistogram.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/IndelLengthHistogram.java index 35fffd815..ffe7c185f 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/IndelLengthHistogram.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/IndelLengthHistogram.java @@ -90,18 +90,19 @@ public class IndelLengthHistogram extends VariantEvaluator { public int getComparisonOrder() { return 1; } // need only the evals public String update1(VariantContext vc1, RefMetaDataTracker tracker, ReferenceContext ref, AlignmentContext context) { - if ( ! vc1.isBiallelic() && vc1.isIndel() ) { - //veWalker.getLogger().warn("[IndelLengthHistogram] Non-biallelic indel at "+ref.getLocus()+" ignored."); - return vc1.toString(); // biallelic sites are output - } - if ( vc1.isIndel() ) { + if ( vc1.isIndel() && vc1.isPolymorphic() ) { + + if ( ! vc1.isBiallelic() ) { + //veWalker.getLogger().warn("[IndelLengthHistogram] Non-biallelic indel at "+ref.getLocus()+" ignored."); + return vc1.toString(); // biallelic sites are output + } + + // only count simple insertions/deletions, not complex indels if ( vc1.isSimpleInsertion() ) { indelHistogram.update(vc1.getAlternateAllele(0).length()); } else if ( vc1.isSimpleDeletion() ) { indelHistogram.update(-vc1.getReference().length()); - } else { - throw new ReviewedStingException("Indel type that is not insertion or deletion."); } } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/IndelStatistics.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/IndelStatistics.java index fc347339d..f70e6c2de 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/IndelStatistics.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/IndelStatistics.java @@ -270,7 +270,7 @@ public class IndelStatistics extends VariantEvaluator { public String update1(VariantContext eval, RefMetaDataTracker tracker, ReferenceContext ref, AlignmentContext context) { - if (eval != null ) { + if (eval != null && eval.isPolymorphic()) { if ( indelStats == null ) { indelStats = new IndelStats(eval); } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/SimpleMetricsByAC.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/SimpleMetricsByAC.java index d466645ea..203c15a85 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/SimpleMetricsByAC.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/SimpleMetricsByAC.java @@ -166,7 +166,7 @@ public class SimpleMetricsByAC extends VariantEvaluator implements StandardEval } } - if ( eval.isSNP() && eval.isBiallelic() && metrics != null ) { + if ( eval.isSNP() && eval.isBiallelic() && eval.isPolymorphic() && metrics != null ) { metrics.incrValue(eval); } } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/ThetaVariantEvaluator.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/ThetaVariantEvaluator.java index ec43cbd55..e51623c3c 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/ThetaVariantEvaluator.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/ThetaVariantEvaluator.java @@ -37,77 +37,74 @@ public class ThetaVariantEvaluator extends VariantEvaluator { } public String update1(VariantContext vc, RefMetaDataTracker tracker, ReferenceContext ref, AlignmentContext context) { - if (vc == null || !vc.isSNP() || !vc.hasGenotypes()) { + if (vc == null || !vc.isSNP() || !vc.hasGenotypes() || vc.isMonomorphic()) { return null; //no interesting sites } - if (vc.hasGenotypes()) { + //this maps allele to a count + ConcurrentMap alleleCounts = new ConcurrentHashMap(); - //this maps allele to a count - ConcurrentMap alleleCounts = new ConcurrentHashMap(); + int numHetsHere = 0; + float numGenosHere = 0; + int numIndsHere = 0; - int numHetsHere = 0; - float numGenosHere = 0; - int numIndsHere = 0; + for (Genotype genotype : vc.getGenotypes().values()) { + numIndsHere++; + if (!genotype.isNoCall()) { + //increment stats for heterozygosity + if (genotype.isHet()) { + numHetsHere++; + } - for (Genotype genotype : vc.getGenotypes().values()) { - numIndsHere++; - if (!genotype.isNoCall()) { - //increment stats for heterozygosity - if (genotype.isHet()) { - numHetsHere++; - } + numGenosHere++; + //increment stats for pairwise mismatches - numGenosHere++; - //increment stats for pairwise mismatches - - for (Allele allele : genotype.getAlleles()) { - if (allele.isNonNull() && allele.isCalled()) { - String alleleString = allele.toString(); - alleleCounts.putIfAbsent(alleleString, 0); - alleleCounts.put(alleleString, alleleCounts.get(alleleString) + 1); - } + for (Allele allele : genotype.getAlleles()) { + if (allele.isNonNull() && allele.isCalled()) { + String alleleString = allele.toString(); + alleleCounts.putIfAbsent(alleleString, 0); + alleleCounts.put(alleleString, alleleCounts.get(alleleString) + 1); } } } - if (numGenosHere > 0) { - //only if have one called genotype at least - this.numSites++; + } + if (numGenosHere > 0) { + //only if have one called genotype at least + this.numSites++; - this.totalHet += numHetsHere / numGenosHere; + this.totalHet += numHetsHere / numGenosHere; - //compute based on num sites - float harmonicFactor = 0; - for (int i = 1; i <= numIndsHere; i++) { - harmonicFactor += 1.0 / i; - } - this.thetaRegionNumSites += 1.0 / harmonicFactor; + //compute based on num sites + float harmonicFactor = 0; + for (int i = 1; i <= numIndsHere; i++) { + harmonicFactor += 1.0 / i; + } + this.thetaRegionNumSites += 1.0 / harmonicFactor; - //now compute pairwise mismatches - float numPairwise = 0; - float numDiffs = 0; - for (String allele1 : alleleCounts.keySet()) { - int allele1Count = alleleCounts.get(allele1); + //now compute pairwise mismatches + float numPairwise = 0; + float numDiffs = 0; + for (String allele1 : alleleCounts.keySet()) { + int allele1Count = alleleCounts.get(allele1); - for (String allele2 : alleleCounts.keySet()) { - if (allele1.compareTo(allele2) < 0) { - continue; - } - if (allele1 .compareTo(allele2) == 0) { - numPairwise += allele1Count * (allele1Count - 1) * .5; + for (String allele2 : alleleCounts.keySet()) { + if (allele1.compareTo(allele2) < 0) { + continue; + } + if (allele1 .compareTo(allele2) == 0) { + numPairwise += allele1Count * (allele1Count - 1) * .5; - } - else { - int allele2Count = alleleCounts.get(allele2); - numPairwise += allele1Count * allele2Count; - numDiffs += allele1Count * allele2Count; - } + } + else { + int allele2Count = alleleCounts.get(allele2); + numPairwise += allele1Count * allele2Count; + numDiffs += allele1Count * allele2Count; } } + } - if (numPairwise > 0) { - this.totalAvgDiffs += numDiffs / numPairwise; - } + if (numPairwise > 0) { + this.totalAvgDiffs += numDiffs / numPairwise; } } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/TiTvVariantEvaluator.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/TiTvVariantEvaluator.java index be957abd7..1feb37e01 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/TiTvVariantEvaluator.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/TiTvVariantEvaluator.java @@ -40,7 +40,7 @@ public class TiTvVariantEvaluator extends VariantEvaluator implements StandardEv } public void updateTiTv(VariantContext vc, boolean updateStandard) { - if (vc != null && vc.isSNP() && vc.isBiallelic()) { + if (vc != null && vc.isSNP() && vc.isBiallelic() && vc.isPolymorphic()) { if (VariantContextUtils.isTransition(vc)) { if (updateStandard) nTiInComp++; else nTi++; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/ValidationReport.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/ValidationReport.java index 9c331b577..307b4f684 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/ValidationReport.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/ValidationReport.java @@ -117,7 +117,8 @@ public class ValidationReport extends VariantEvaluator implements StandardEval { public SiteStatus calcSiteStatus(VariantContext vc) { if ( vc == null ) return SiteStatus.NO_CALL; if ( vc.isFiltered() ) return SiteStatus.FILTERED; - if ( ! vc.isVariant() ) return SiteStatus.MONO; + if ( vc.isMonomorphic() ) return SiteStatus.MONO; + if ( vc.hasGenotypes() ) return SiteStatus.POLY; // must be polymorphic if isMonomorphic was false and there are genotypes if ( vc.hasAttribute(VCFConstants.ALLELE_COUNT_KEY) ) { int ac = 0; @@ -132,8 +133,6 @@ public class ValidationReport extends VariantEvaluator implements StandardEval { else ac = vc.getAttributeAsInt(VCFConstants.ALLELE_COUNT_KEY); return ac > 0 ? SiteStatus.POLY : SiteStatus.MONO; - } else if ( vc.hasGenotypes() ) { - return vc.isPolymorphic() ? SiteStatus.POLY : SiteStatus.MONO; } else { return TREAT_ALL_SITES_IN_EVAL_VCF_AS_CALLED ? SiteStatus.POLY : SiteStatus.NO_CALL; // we can't figure out what to do //return SiteStatus.NO_CALL; // we can't figure out what to do diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/VariantQualityScore.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/VariantQualityScore.java index b6ad55b18..263227938 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/VariantQualityScore.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/VariantQualityScore.java @@ -232,7 +232,7 @@ public class VariantQualityScore extends VariantEvaluator { public String update1(VariantContext eval, RefMetaDataTracker tracker, ReferenceContext ref, AlignmentContext context) { final String interesting = null; - if( eval != null && eval.isSNP() && eval.isBiallelic() ) { //BUGBUG: only counting biallelic sites (revisit what to do with triallelic sites) + if( eval != null && eval.isSNP() && eval.isBiallelic() && eval.isPolymorphic() ) { //BUGBUG: only counting biallelic sites (revisit what to do with triallelic sites) if( titvStats == null ) { titvStats = new TiTvStats(); } titvStats.incrValue(eval.getPhredScaledQual(), VariantContextUtils.isTransition(eval)); diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/util/VariantEvalUtils.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/util/VariantEvalUtils.java index 3cc039141..92e7c6554 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/util/VariantEvalUtils.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/util/VariantEvalUtils.java @@ -277,7 +277,7 @@ public class VariantEvalUtils { * @return a new VariantContext with just the requested samples */ public VariantContext getSubsetOfVariantContext(VariantContext vc, Collection sampleNames) { - VariantContext vcsub = vc.subContextFromGenotypes(vc.getGenotypes(sampleNames).values()); + VariantContext vcsub = vc.subContextFromGenotypes(vc.getGenotypes(sampleNames).values(), vc.getAlleles()); HashMap newAts = new HashMap(vcsub.getAttributes()); diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java index 3503a2353..7b6d13223 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java @@ -264,7 +264,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { @Test public void testTranches() { String extraArgs = "-T VariantEval -R "+ hg18Reference +" --eval " + validationDataLocation + "GA2.WEx.cleaned.ug.snpfiltered.indelfiltered.optimized.vcf -o %s -EV TiTvVariantEvaluator -L chr1 -noEV -ST CpG -tf " + testDir + "tranches.6.txt"; - WalkerTestSpec spec = new WalkerTestSpec(extraArgs,1,Arrays.asList("984df6e94a546294fc7e0846cbac2dfe")); + WalkerTestSpec spec = new WalkerTestSpec(extraArgs,1,Arrays.asList("6af2b9959aa1778a5b712536de453952")); executeTestParallel("testTranches",spec); } From 2ded0277628e97e0363b8af051580a87786f0459 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Wed, 7 Sep 2011 16:09:24 -0400 Subject: [PATCH 011/196] Removed dysfunctional tranches support from VariantEval --- .../walkers/varianteval/VariantEvalWalker.java | 18 ------------------ .../VariantEvalIntegrationTest.java | 2 +- 2 files changed, 1 insertion(+), 19 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalWalker.java index 65e3d3e5a..0d09b7033 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalWalker.java @@ -149,9 +149,6 @@ public class VariantEvalWalker extends RodWalker implements Tr @Argument(shortName="mvq", fullName="mendelianViolationQualThreshold", doc="Minimum genotype QUAL score for each trio member required to accept a site as a violation", required=false) protected double MENDELIAN_VIOLATION_QUAL_THRESHOLD = 50; - @Argument(fullName="tranchesFile", shortName="tf", doc="The input tranches file describing where to cut the data", required=false) - private String TRANCHE_FILENAME = null; - @Argument(fullName="ancestralAlignments", shortName="aa", doc="Fasta file with ancestral alleles", required=false) private File ancestralAlignmentsFile = null; @@ -226,16 +223,6 @@ public class VariantEvalWalker extends RodWalker implements Tr } sampleNamesForStratification.add(ALL_SAMPLE_NAME); - // Add select expressions for anything in the tranches file - if ( TRANCHE_FILENAME != null ) { - // we are going to build a few select names automatically from the tranches file - for ( Tranche t : Tranche.readTranches(new File(TRANCHE_FILENAME)) ) { - logger.info("Adding select for all variant above the pCut of : " + t); - SELECT_EXPS.add(String.format(VariantRecalibrator.VQS_LOD_KEY + " >= %.2f", t.minVQSLod)); - SELECT_NAMES.add(String.format("TS-%.2f", t.ts)); - } - } - // Initialize select expressions for (VariantContextUtils.JexlVCMatchExp jexl : VariantContextUtils.initializeMatchExps(SELECT_NAMES, SELECT_EXPS)) { SortableJexlVCMatchExp sjexl = new SortableJexlVCMatchExp(jexl.name, jexl.exp); @@ -245,18 +232,13 @@ public class VariantEvalWalker extends RodWalker implements Tr // Initialize the set of stratifications and evaluations to use stratificationObjects = variantEvalUtils.initializeStratificationObjects(this, NO_STANDARD_STRATIFICATIONS, STRATIFICATIONS_TO_USE); Set> evaluationObjects = variantEvalUtils.initializeEvaluationObjects(NO_STANDARD_MODULES, MODULES_TO_USE); - boolean usingJEXL = false; for ( VariantStratifier vs : getStratificationObjects() ) { if ( vs.getClass().getSimpleName().equals("Filter") ) byFilterIsEnabled = true; else if ( vs.getClass().getSimpleName().equals("Sample") ) perSampleIsEnabled = true; - usingJEXL = usingJEXL || vs.getClass().equals(JexlExpression.class); } - if ( TRANCHE_FILENAME != null && ! usingJEXL ) - throw new UserException.BadArgumentValue("tf", "Requires the JexlExpression ST to enabled"); - // Initialize the evaluation contexts evaluationContexts = variantEvalUtils.initializeEvaluationContexts(stratificationObjects, evaluationObjects, null, null); diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java index 7b6d13223..6c4393d6a 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java @@ -261,7 +261,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { return String.format("%s -select '%s' -selectName %s", cmd, select, name); } - @Test + @Test(enabled = false) // no longer supported in the GATK public void testTranches() { String extraArgs = "-T VariantEval -R "+ hg18Reference +" --eval " + validationDataLocation + "GA2.WEx.cleaned.ug.snpfiltered.indelfiltered.optimized.vcf -o %s -EV TiTvVariantEvaluator -L chr1 -noEV -ST CpG -tf " + testDir + "tranches.6.txt"; WalkerTestSpec spec = new WalkerTestSpec(extraArgs,1,Arrays.asList("6af2b9959aa1778a5b712536de453952")); From 9604fb2ba34d619d96459938e32d757758216c91 Mon Sep 17 00:00:00 2001 From: Guillermo del Angel Date: Wed, 7 Sep 2011 16:49:16 -0400 Subject: [PATCH 012/196] Necessary but not sufficient step to fix GenotypeGivenAlleles mode in UG which is now busted --- .../gatk/walkers/genotyper/UnifiedGenotyperEngine.java | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java index 06455df6d..b1332bdf9 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java @@ -39,10 +39,7 @@ import org.broadinstitute.sting.utils.codecs.vcf.VCFConstants; import org.broadinstitute.sting.utils.pileup.PileupElement; import org.broadinstitute.sting.utils.pileup.ReadBackedExtendedEventPileup; import org.broadinstitute.sting.utils.pileup.ReadBackedPileup; -import org.broadinstitute.sting.utils.variantcontext.Allele; -import org.broadinstitute.sting.utils.variantcontext.Genotype; -import org.broadinstitute.sting.utils.variantcontext.GenotypeLikelihoods; -import org.broadinstitute.sting.utils.variantcontext.VariantContext; +import org.broadinstitute.sting.utils.variantcontext.*; import java.io.PrintStream; import java.util.*; @@ -239,7 +236,8 @@ public class UnifiedGenotyperEngine { VariantContext vcInput = SNPGenotypeLikelihoodsCalculationModel.getSNPVCFromAllelesRod(tracker, ref, false, logger, UAC.alleles); if ( vcInput == null ) return null; - vc = new VariantContext("UG_call", vcInput.getChr(), vcInput.getStart(), vcInput.getEnd(), vcInput.getAlleles()); + vc = new VariantContext("UG_call", vcInput.getChr(), vcInput.getStart(), vcInput.getEnd(), vcInput.getAlleles(), InferredGeneticContext.NO_NEG_LOG_10PERROR, null, null, ref.getBase()); + } else { // deal with bad/non-standard reference bases if ( !Allele.acceptableAlleleBases(new byte[]{ref.getBase()}) ) From 01b6177ce15c2d4270ac863da1a2e4e43e020411 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Wed, 7 Sep 2011 17:10:56 -0400 Subject: [PATCH 013/196] Renaming GVCF -> GCF --- .../utils/{gvcf/GVCF.java => gcf/GCF.java} | 47 +++++++++---------- .../GCFGenotype.java} | 20 ++++---- .../GVCFHeader.java => gcf/GCFHeader.java} | 12 ++--- .../GCFHeaderBuilder.java} | 8 ++-- 4 files changed, 41 insertions(+), 46 deletions(-) rename public/java/src/org/broadinstitute/sting/utils/{gvcf/GVCF.java => gcf/GCF.java} (83%) rename public/java/src/org/broadinstitute/sting/utils/{gvcf/GVCFGenotype.java => gcf/GCFGenotype.java} (86%) rename public/java/src/org/broadinstitute/sting/utils/{gvcf/GVCFHeader.java => gcf/GCFHeader.java} (92%) rename public/java/src/org/broadinstitute/sting/utils/{gvcf/GVCFHeaderBuilder.java => gcf/GCFHeaderBuilder.java} (93%) diff --git a/public/java/src/org/broadinstitute/sting/utils/gvcf/GVCF.java b/public/java/src/org/broadinstitute/sting/utils/gcf/GCF.java similarity index 83% rename from public/java/src/org/broadinstitute/sting/utils/gvcf/GVCF.java rename to public/java/src/org/broadinstitute/sting/utils/gcf/GCF.java index 8568c1aab..5ab241ebf 100644 --- a/public/java/src/org/broadinstitute/sting/utils/gvcf/GVCF.java +++ b/public/java/src/org/broadinstitute/sting/utils/gcf/GCF.java @@ -22,12 +22,9 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -package org.broadinstitute.sting.utils.gvcf; +package org.broadinstitute.sting.utils.gcf; -import org.broadinstitute.sting.utils.QualityUtils; -import org.broadinstitute.sting.utils.Utils; import org.broadinstitute.sting.utils.codecs.vcf.StandardVCFWriter; -import org.broadinstitute.sting.utils.codecs.vcf.VCFConstants; import org.broadinstitute.sting.utils.exceptions.UserException; import org.broadinstitute.sting.utils.variantcontext.Allele; import org.broadinstitute.sting.utils.variantcontext.Genotype; @@ -42,7 +39,7 @@ import java.util.*; * @author Your Name * @since Date created */ -public class GVCF { +public class GCF { private final static int RECORD_TERMINATOR = 123456789; private int chromOffset; private int start, stop; @@ -54,10 +51,10 @@ public class GVCF { private String info; private int filterOffset; - private List genotypes = Collections.emptyList(); + private List genotypes = Collections.emptyList(); - public GVCF(final GVCFHeaderBuilder gvcfHeaderBuilder, final VariantContext vc, boolean skipGenotypes) { - chromOffset = gvcfHeaderBuilder.encodeString(vc.getChr()); + public GCF(final GCFHeaderBuilder GCFHeaderBuilder, final VariantContext vc, boolean skipGenotypes) { + chromOffset = GCFHeaderBuilder.encodeString(vc.getChr()); start = vc.getStart(); stop = vc.getEnd(); refPad = vc.hasReferenceBaseForIndel() ? vc.getReferenceBaseForIndel() : 0; @@ -67,22 +64,22 @@ public class GVCF { alleleMap = new ArrayList(vc.getNAlleles()); alleleOffsets = new int[vc.getNAlleles()]; alleleMap.add(vc.getReference()); - alleleOffsets[0] = gvcfHeaderBuilder.encodeAllele(vc.getReference()); + alleleOffsets[0] = GCFHeaderBuilder.encodeAllele(vc.getReference()); for ( int i = 0; i < vc.getAlternateAlleles().size(); i++ ) { alleleMap.add(vc.getAlternateAllele(i)); - alleleOffsets[i+1] = gvcfHeaderBuilder.encodeAllele(vc.getAlternateAllele(i)); + alleleOffsets[i+1] = GCFHeaderBuilder.encodeAllele(vc.getAlternateAllele(i)); } qual = (float)vc.getNegLog10PError(); //qualToByte(vc.getPhredScaledQual()); - info = infoFieldString(vc, gvcfHeaderBuilder); - filterOffset = gvcfHeaderBuilder.encodeString(StandardVCFWriter.getFilterString(vc)); + info = infoFieldString(vc, GCFHeaderBuilder); + filterOffset = GCFHeaderBuilder.encodeString(StandardVCFWriter.getFilterString(vc)); if ( ! skipGenotypes ) { - genotypes = encodeGenotypes(gvcfHeaderBuilder, vc); + genotypes = encodeGenotypes(GCFHeaderBuilder, vc); } } - public GVCF(DataInputStream inputStream, boolean skipGenotypes) throws IOException { + public GCF(DataInputStream inputStream, boolean skipGenotypes) throws IOException { chromOffset = inputStream.readInt(); start = inputStream.readInt(); stop = inputStream.readInt(); @@ -99,9 +96,9 @@ public class GVCF { genotypes = Collections.emptyList(); inputStream.skipBytes(sizeOfGenotypes); } else { - genotypes = new ArrayList(nGenotypes); + genotypes = new ArrayList(nGenotypes); for ( int i = 0; i < nGenotypes; i++ ) - genotypes.add(new GVCFGenotype(this, inputStream)); + genotypes.add(new GCFGenotype(this, inputStream)); } int recordDone = inputStream.readInt(); @@ -109,7 +106,7 @@ public class GVCF { throw new UserException.MalformedFile("Record not terminated by RECORD_TERMINATOR key"); } - public VariantContext decode(final String source, final GVCFHeader header) { + public VariantContext decode(final String source, final GCFHeader header) { final String contig = header.getString(chromOffset); alleleMap = header.getAlleles(alleleOffsets); double negLog10PError = qual; // QualityUtils.qualToErrorProb(qual); @@ -122,7 +119,7 @@ public class GVCF { return new VariantContext(source, contig, start, stop, alleleMap, genotypes, negLog10PError, filters, attributes, refPadByte); } - private Map decodeGenotypes(final GVCFHeader header) { + private Map decodeGenotypes(final GCFHeader header) { if ( genotypes.isEmpty() ) return VariantContext.NO_GENOTYPES; else { @@ -138,15 +135,15 @@ public class GVCF { } } - private List encodeGenotypes(final GVCFHeaderBuilder gvcfHeaderBuilder, final VariantContext vc) { + private List encodeGenotypes(final GCFHeaderBuilder GCFHeaderBuilder, final VariantContext vc) { int nGenotypes = vc.getNSamples(); if ( nGenotypes > 0 ) { - List genotypes = new ArrayList(nGenotypes); + List genotypes = new ArrayList(nGenotypes); for ( int i = 0; i < nGenotypes; i++ ) genotypes.add(null); for ( Genotype g : vc.getGenotypes().values() ) { - int i = gvcfHeaderBuilder.encodeSample(g.getSampleName()); - genotypes.set(i, new GVCFGenotype(gvcfHeaderBuilder, alleleMap, g)); + int i = GCFHeaderBuilder.encodeSample(g.getSampleName()); + genotypes.set(i, new GCFGenotype(GCFHeaderBuilder, alleleMap, g)); } return genotypes; @@ -174,7 +171,7 @@ public class GVCF { outputStream.writeInt(nGenotypes); outputStream.writeInt(expectedSizeOfGenotypes); int obsSizeOfGenotypes = 0; - for ( GVCFGenotype g : genotypes ) + for ( GCFGenotype g : genotypes ) obsSizeOfGenotypes += g.write(outputStream); if ( obsSizeOfGenotypes != expectedSizeOfGenotypes ) throw new RuntimeException("Expect and observed genotype sizes disagree! expect = " + expectedSizeOfGenotypes + " obs =" + obsSizeOfGenotypes); @@ -183,7 +180,7 @@ public class GVCF { return outputStream.size() - startSize; } - private final String infoFieldString(VariantContext vc, final GVCFHeaderBuilder gvcfHeaderBuilder) { + private final String infoFieldString(VariantContext vc, final GCFHeaderBuilder GCFHeaderBuilder) { StringBuilder s = new StringBuilder(); boolean first = true; @@ -191,7 +188,7 @@ public class GVCF { String key = field.getKey(); if ( key.equals(VariantContext.ID_KEY) || key.equals(VariantContext.UNPARSED_GENOTYPE_MAP_KEY) || key.equals(VariantContext.UNPARSED_GENOTYPE_PARSER_KEY) ) continue; - int stringIndex = gvcfHeaderBuilder.encodeString(key); + int stringIndex = GCFHeaderBuilder.encodeString(key); String outputValue = StandardVCFWriter.formatVCFField(field.getValue()); if ( outputValue != null ) { if ( ! first ) s.append(";"); diff --git a/public/java/src/org/broadinstitute/sting/utils/gvcf/GVCFGenotype.java b/public/java/src/org/broadinstitute/sting/utils/gcf/GCFGenotype.java similarity index 86% rename from public/java/src/org/broadinstitute/sting/utils/gvcf/GVCFGenotype.java rename to public/java/src/org/broadinstitute/sting/utils/gcf/GCFGenotype.java index 2ef6d9b3a..dd1fb091c 100644 --- a/public/java/src/org/broadinstitute/sting/utils/gvcf/GVCFGenotype.java +++ b/public/java/src/org/broadinstitute/sting/utils/gcf/GCFGenotype.java @@ -22,7 +22,7 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -package org.broadinstitute.sting.utils.gvcf; +package org.broadinstitute.sting.utils.gcf; import org.broadinstitute.sting.utils.variantcontext.Allele; import org.broadinstitute.sting.utils.variantcontext.Genotype; @@ -38,7 +38,7 @@ import java.util.*; * @author Your Name * @since Date created */ -public class GVCFGenotype { +public class GCFGenotype { private byte gq; private int gt; private int dp; @@ -48,8 +48,8 @@ public class GVCFGenotype { // todo -- what to do about phasing? Perhaps we shouldn't support it // todo -- is the FL field generic or just a flag? Should we even support per sample filtering? - public GVCFGenotype(final GVCFHeaderBuilder gvcfHeaderBuilder, final List allAlleles, Genotype genotype) { - gq = GVCF.qualToByte(genotype.getPhredScaledQual()); + public GCFGenotype(final GCFHeaderBuilder GCFHeaderBuilder, final List allAlleles, Genotype genotype) { + gq = GCF.qualToByte(genotype.getPhredScaledQual()); gt = encodeAlleles(genotype.getAlleles(), allAlleles); dp = genotype.getAttributeAsInt("DP", 0); @@ -65,13 +65,13 @@ public class GVCFGenotype { return nAlleles*(nAlleles+1) / 2; } - public GVCFGenotype(GVCF gvcf, DataInputStream inputStream) throws IOException { + public GCFGenotype(GCF GCF, DataInputStream inputStream) throws IOException { int gqInt = inputStream.readUnsignedByte(); gq = (byte)gqInt; gt = inputStream.readInt(); dp = inputStream.readInt(); - ad = GVCF.readIntArray(inputStream, gvcf.getNAlleles()); - pl = GVCF.readByteArray(inputStream, nAllelesToNPls(gvcf.getNAlleles())); + ad = GCF.readIntArray(inputStream, GCF.getNAlleles()); + pl = GCF.readByteArray(inputStream, nAllelesToNPls(GCF.getNAlleles())); } // 2 alleles => 1 + 8 + 8 + 3 => 20 @@ -82,7 +82,7 @@ public class GVCFGenotype { + 1 * pl.length; // pl } - public Genotype decode(final String sampleName, final GVCFHeader header, GVCF gvcf, List alleleIndex) { + public Genotype decode(final String sampleName, final GCFHeader header, GCF GCF, List alleleIndex) { final List alleles = decodeAlleles(gt, alleleIndex); final double negLog10PError = gq / 10.0; final Set filters = Collections.emptySet(); @@ -140,8 +140,8 @@ public class GVCFGenotype { outputStream.writeByte(gq); outputStream.writeInt(gt); outputStream.writeInt(dp); - GVCF.writeIntArray(ad, outputStream, false); - GVCF.writeByteArray(pl, outputStream, false); + GCF.writeIntArray(ad, outputStream, false); + GCF.writeByteArray(pl, outputStream, false); return outputStream.size() - startSize; } } diff --git a/public/java/src/org/broadinstitute/sting/utils/gvcf/GVCFHeader.java b/public/java/src/org/broadinstitute/sting/utils/gcf/GCFHeader.java similarity index 92% rename from public/java/src/org/broadinstitute/sting/utils/gvcf/GVCFHeader.java rename to public/java/src/org/broadinstitute/sting/utils/gcf/GCFHeader.java index c52c975bd..d0c765cc4 100644 --- a/public/java/src/org/broadinstitute/sting/utils/gvcf/GVCFHeader.java +++ b/public/java/src/org/broadinstitute/sting/utils/gcf/GCFHeader.java @@ -22,11 +22,9 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -package org.broadinstitute.sting.utils.gvcf; +package org.broadinstitute.sting.utils.gcf; import org.apache.log4j.Logger; -import org.broadinstitute.sting.utils.QualityUtils; -import org.broadinstitute.sting.utils.codecs.vcf.AbstractVCFCodec; import org.broadinstitute.sting.utils.codecs.vcf.VCFCodec; import org.broadinstitute.sting.utils.codecs.vcf.VCFConstants; import org.broadinstitute.sting.utils.exceptions.UserException; @@ -64,8 +62,8 @@ import java.util.*; * @author Your Name * @since Date created */ -public class GVCFHeader { - final protected static Logger logger = Logger.getLogger(GVCFHeader.class); +public class GCFHeader { + final protected static Logger logger = Logger.getLogger(GCFHeader.class); private static byte[] MAGIC_HEADER = "GVCF0.1\1".getBytes(); final List alleles; @@ -73,14 +71,14 @@ public class GVCFHeader { final List samples; final List> filters; - public GVCFHeader(final Map allelesIn, final Map stringIn, final Map samplesIn) { + public GCFHeader(final Map allelesIn, final Map stringIn, final Map samplesIn) { this.alleles = linearize(allelesIn); this.strings = linearize(stringIn); this.samples = linearize(samplesIn); this.filters = null; // not used with this constructor } - public GVCFHeader(DataInputStream inputStream) throws IOException { + public GCFHeader(DataInputStream inputStream) throws IOException { byte[] headerTest = new byte[MAGIC_HEADER.length]; inputStream.read(headerTest); if ( ! Arrays.equals(headerTest, MAGIC_HEADER) ) { diff --git a/public/java/src/org/broadinstitute/sting/utils/gvcf/GVCFHeaderBuilder.java b/public/java/src/org/broadinstitute/sting/utils/gcf/GCFHeaderBuilder.java similarity index 93% rename from public/java/src/org/broadinstitute/sting/utils/gvcf/GVCFHeaderBuilder.java rename to public/java/src/org/broadinstitute/sting/utils/gcf/GCFHeaderBuilder.java index 2d045b8ea..40e01ec72 100644 --- a/public/java/src/org/broadinstitute/sting/utils/gvcf/GVCFHeaderBuilder.java +++ b/public/java/src/org/broadinstitute/sting/utils/gcf/GCFHeaderBuilder.java @@ -22,7 +22,7 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -package org.broadinstitute.sting.utils.gvcf; +package org.broadinstitute.sting.utils.gcf; import org.broadinstitute.sting.utils.variantcontext.Allele; @@ -56,13 +56,13 @@ import java.util.Map; * @author Your Name * @since Date created */ -public class GVCFHeaderBuilder { +public class GCFHeaderBuilder { Map alleles = new HashMap(); Map strings = new HashMap(); Map samples = new HashMap(); - public GVCFHeader createHeader() { - return new GVCFHeader(alleles, strings, samples); + public GCFHeader createHeader() { + return new GCFHeader(alleles, strings, samples); } public int encodeString(final String chr) { return encode(strings, chr); } From fe5724b6ea7c77f3f38f35b2d29d118860b6fc2a Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Wed, 7 Sep 2011 23:27:08 -0400 Subject: [PATCH 016/196] Refactored indexing part of StandardVCFWriter into superclass -- Now other implementations of the VCFWriter can easily share common functions, such as writing an index on the fly --- .../utils/codecs/vcf/IndexingVCFWriter.java | 116 ++++++++++++++++++ .../utils/codecs/vcf/StandardVCFWriter.java | 107 +++++----------- 2 files changed, 148 insertions(+), 75 deletions(-) create mode 100644 public/java/src/org/broadinstitute/sting/utils/codecs/vcf/IndexingVCFWriter.java diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/IndexingVCFWriter.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/IndexingVCFWriter.java new file mode 100644 index 000000000..632bf8ed3 --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/IndexingVCFWriter.java @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2011, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.utils.codecs.vcf; + +import org.broad.tribble.Tribble; +import org.broad.tribble.TribbleException; +import org.broad.tribble.index.DynamicIndexCreator; +import org.broad.tribble.index.Index; +import org.broad.tribble.index.IndexFactory; +import org.broad.tribble.util.LittleEndianOutputStream; +import org.broad.tribble.util.PositionalStream; +import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; +import org.broadinstitute.sting.utils.variantcontext.VariantContext; + +import java.io.*; + +/** + * this class writes VCF files + */ +public abstract class IndexingVCFWriter implements VCFWriter { + final private File indexFile; + final private String name; + + private PositionalStream positionalStream; + private DynamicIndexCreator indexer; + private LittleEndianOutputStream idxStream; + + protected IndexingVCFWriter(String name, File location, OutputStream output, boolean enableOnTheFlyIndexing) { + this.name = name; + + if ( enableOnTheFlyIndexing ) { + indexFile = Tribble.indexFile(location); + try { + idxStream = new LittleEndianOutputStream(new FileOutputStream(indexFile)); + //System.out.println("Creating index on the fly for " + location); + indexer = new DynamicIndexCreator(IndexFactory.IndexBalanceApproach.FOR_SEEK_TIME); + indexer.initialize(location, indexer.defaultBinSize()); + positionalStream = new PositionalStream(output); + } catch ( IOException ex ) { + // No matter what we keep going, since we don't care if we can't create the index file + } + } else { + idxStream = null; + indexer = null; + positionalStream = null; + indexFile = null; + } + } + + public String getStreamName() { + return name; + } + + public abstract void writeHeader(VCFHeader header); + + /** + * attempt to close the VCF file + */ + public void close() { + // try to close the index stream (keep it separate to help debugging efforts) + if ( indexer != null ) { + try { + Index index = indexer.finalizeIndex(positionalStream.getPosition()); + index.write(idxStream); + idxStream.close(); + } catch (IOException e) { + throw new ReviewedStingException("Unable to close index for " + getStreamName(), e); + } + } + } + + /** + * add a record to the file + * + * @param vc the Variant Context object + */ + public void add(VariantContext vc) { + // if we are doing on the fly indexing, add the record ***before*** we write any bytes + if ( indexer != null ) + indexer.addFeature(vc, positionalStream.getPosition()); + } + + protected static final String writerName(File location, OutputStream stream) { + return location == null ? stream.toString() : location.getAbsolutePath(); + } + + protected static OutputStream openOutputStream(File location) { + try { + return new FileOutputStream(location); + } catch (FileNotFoundException e) { + throw new ReviewedStingException("Unable to create VCF file at location: " + location, e); + } + } +} diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/StandardVCFWriter.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/StandardVCFWriter.java index e28cd7598..ebcba9635 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/StandardVCFWriter.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/StandardVCFWriter.java @@ -44,26 +44,19 @@ import java.util.*; /** * this class writes VCF files */ -public class StandardVCFWriter implements VCFWriter { +public class StandardVCFWriter extends IndexingVCFWriter { + // the print stream we're writing to + final protected BufferedWriter mWriter; + + // should we write genotypes or just sites? + final protected boolean doNotWriteGenotypes; // the VCF header we're storing protected VCFHeader mHeader = null; - // the print stream we're writing to - protected BufferedWriter mWriter; - protected PositionalStream positionalStream = null; - // were filters applied? protected boolean filtersWereAppliedToContext = false; - // should we write genotypes or just sites? - protected boolean doNotWriteGenotypes = false; - - protected DynamicIndexCreator indexer = null; - protected File indexFile = null; - LittleEndianOutputStream idxStream = null; - File location = null; - /** * create a VCF writer, given a file to write to * @@ -93,32 +86,22 @@ public class StandardVCFWriter implements VCFWriter { * @param doNotWriteGenotypes do not write genotypes */ public StandardVCFWriter(OutputStream output, boolean doNotWriteGenotypes) { - mWriter = new BufferedWriter(new OutputStreamWriter(output)); - this.doNotWriteGenotypes = doNotWriteGenotypes; + this(null, output, false, doNotWriteGenotypes); } public StandardVCFWriter(File location, OutputStream output, boolean enableOnTheFlyIndexing, boolean doNotWriteGenotypes) { - this.location = location; - - if ( enableOnTheFlyIndexing ) { - indexFile = Tribble.indexFile(location); - try { - idxStream = new LittleEndianOutputStream(new FileOutputStream(indexFile)); - //System.out.println("Creating index on the fly for " + location); - indexer = new DynamicIndexCreator(IndexFactory.IndexBalanceApproach.FOR_SEEK_TIME); - indexer.initialize(location, indexer.defaultBinSize()); - positionalStream = new PositionalStream(output); - output = positionalStream; - } catch ( IOException ex ) { - // No matter what we keep going, since we don't care if we can't create the index file - } - } - - //mWriter = new BufferedWriter(new OutputStreamWriter(new PositionalStream(output))); - mWriter = new BufferedWriter(new OutputStreamWriter(output)); + super(writerName(location, output), location, output, enableOnTheFlyIndexing); + mWriter = new BufferedWriter(new OutputStreamWriter(output)); // todo -- fix buffer size this.doNotWriteGenotypes = doNotWriteGenotypes; } + // -------------------------------------------------------------------------------- + // + // VCFWriter interface functions + // + // -------------------------------------------------------------------------------- + + @Override public void writeHeader(VCFHeader header) { mHeader = doNotWriteGenotypes ? new VCFHeader(header.getMetaData()) : header; @@ -158,44 +141,24 @@ public class StandardVCFWriter implements VCFWriter { mWriter.flush(); // necessary so that writing to an output stream will work } catch (IOException e) { - throw new TribbleException("IOException writing the VCF header to " + locationString(), e); + throw new ReviewedStingException("IOException writing the VCF header to " + getStreamName(), e); } } - private String locationString() { - return location == null ? mWriter.toString() : location.getAbsolutePath(); - } - /** * attempt to close the VCF file */ + @Override public void close() { // try to close the vcf stream try { mWriter.flush(); mWriter.close(); } catch (IOException e) { - throw new TribbleException("Unable to close " + locationString() + " because of " + e.getMessage()); + throw new ReviewedStingException("Unable to close " + getStreamName(), e); } - // try to close the index stream (keep it separate to help debugging efforts) - if ( indexer != null ) { - try { - Index index = indexer.finalizeIndex(positionalStream.getPosition()); - index.write(idxStream); - idxStream.close(); - } catch (IOException e) { - throw new TribbleException("Unable to close index for " + locationString() + " because of " + e.getMessage()); - } - } - } - - protected static OutputStream openOutputStream(File location) { - try { - return new FileOutputStream(location); - } catch (FileNotFoundException e) { - throw new TribbleException("Unable to create VCF file at location: " + location); - } + super.close(); } /** @@ -203,28 +166,17 @@ public class StandardVCFWriter implements VCFWriter { * * @param vc the Variant Context object */ + @Override public void add(VariantContext vc) { - add(vc, false); - } - - /** - * add a record to the file - * - * @param vc the Variant Context object - * @param refBaseShouldBeAppliedToEndOfAlleles *** THIS SHOULD BE FALSE EXCEPT FOR AN INDEL AT THE EXTREME BEGINNING OF A CONTIG (WHERE THERE IS NO PREVIOUS BASE, SO WE USE THE BASE AFTER THE EVENT INSTEAD) - */ - public void add(VariantContext vc, boolean refBaseShouldBeAppliedToEndOfAlleles) { if ( mHeader == null ) - throw new IllegalStateException("The VCF Header must be written before records can be added: " + locationString()); + throw new IllegalStateException("The VCF Header must be written before records can be added: " + getStreamName()); if ( doNotWriteGenotypes ) vc = VariantContext.modifyGenotypes(vc, null); try { - vc = VariantContext.createVariantContextWithPaddedAlleles(vc, refBaseShouldBeAppliedToEndOfAlleles); - - // if we are doing on the fly indexing, add the record ***before*** we write any bytes - if ( indexer != null ) indexer.addFeature(vc, positionalStream.getPosition()); + vc = VariantContext.createVariantContextWithPaddedAlleles(vc, false); + super.add(vc); Map alleleMap = new HashMap(vc.getAlleles().size()); alleleMap.put(Allele.NO_CALL, VCFConstants.EMPTY_ALLELE); // convenience for lookup @@ -317,10 +269,16 @@ public class StandardVCFWriter implements VCFWriter { mWriter.write("\n"); mWriter.flush(); // necessary so that writing to an output stream will work } catch (IOException e) { - throw new RuntimeException("Unable to write the VCF object to " + locationString()); + throw new RuntimeException("Unable to write the VCF object to " + getStreamName()); } } + // -------------------------------------------------------------------------------- + // + // implementation functions + // + // -------------------------------------------------------------------------------- + public static final String getFilterString(final VariantContext vc) { return getFilterString(vc, false); } @@ -531,12 +489,11 @@ public class StandardVCFWriter implements VCFWriter { } - public static int countOccurrences(char c, String s) { + private static int countOccurrences(char c, String s) { int count = 0; for (int i = 0; i < s.length(); i++) { count += s.charAt(i) == c ? 1 : 0; } return count; } - } From cd2c511c4ae8a7d13ca6fe3604308ca5fdea5c00 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Wed, 7 Sep 2011 23:28:46 -0400 Subject: [PATCH 017/196] GCF improvements -- Support for streaming VCF writing via the VCFWriter interface -- GCF now has a header and a footer. The header is minimal, and contains a forward pointer to the position of the footer in the file. -- Readers now read the header, and then jump to the footer to get the rest of the "header" information -- Version now a field in GCF --- .../broadinstitute/sting/utils/gcf/GCF.java | 69 +++++----- .../sting/utils/gcf/GCFHeader.java | 49 +++++-- .../sting/utils/gcf/GCFWriter.java | 122 ++++++++++++++++++ 3 files changed, 198 insertions(+), 42 deletions(-) create mode 100644 public/java/src/org/broadinstitute/sting/utils/gcf/GCFWriter.java diff --git a/public/java/src/org/broadinstitute/sting/utils/gcf/GCF.java b/public/java/src/org/broadinstitute/sting/utils/gcf/GCF.java index 5ab241ebf..ef0d9ca42 100644 --- a/public/java/src/org/broadinstitute/sting/utils/gcf/GCF.java +++ b/public/java/src/org/broadinstitute/sting/utils/gcf/GCF.java @@ -79,8 +79,13 @@ public class GCF { } } - public GCF(DataInputStream inputStream, boolean skipGenotypes) throws IOException { + public GCF(DataInputStream inputStream, boolean skipGenotypes) throws IOException, EOFException { chromOffset = inputStream.readInt(); + + // have we reached the footer? + if ( chromOffset == GCFHeader.FOOTER_START_MARKER ) + throw new EOFException(); + start = inputStream.readInt(); stop = inputStream.readInt(); id = inputStream.readUTF(); @@ -106,6 +111,32 @@ public class GCF { throw new UserException.MalformedFile("Record not terminated by RECORD_TERMINATOR key"); } + public int write(DataOutputStream outputStream) throws IOException { + int startSize = outputStream.size(); + outputStream.writeInt(chromOffset); + outputStream.writeInt(start); + outputStream.writeInt(stop); + outputStream.writeUTF(id); + outputStream.writeByte(refPad); + writeIntArray(alleleOffsets, outputStream, true); + outputStream.writeFloat(qual); + outputStream.writeUTF(info); + outputStream.writeInt(filterOffset); + + int nGenotypes = genotypes.size(); + int expectedSizeOfGenotypes = nGenotypes == 0 ? 0 : genotypes.get(0).sizeInBytes() * nGenotypes; + outputStream.writeInt(nGenotypes); + outputStream.writeInt(expectedSizeOfGenotypes); + int obsSizeOfGenotypes = 0; + for ( GCFGenotype g : genotypes ) + obsSizeOfGenotypes += g.write(outputStream); + if ( obsSizeOfGenotypes != expectedSizeOfGenotypes ) + throw new RuntimeException("Expect and observed genotype sizes disagree! expect = " + expectedSizeOfGenotypes + " obs =" + obsSizeOfGenotypes); + + outputStream.writeInt(RECORD_TERMINATOR); + return outputStream.size() - startSize; + } + public VariantContext decode(final String source, final GCFHeader header) { final String contig = header.getString(chromOffset); alleleMap = header.getAlleles(alleleOffsets); @@ -154,31 +185,6 @@ public class GCF { public int getNAlleles() { return alleleOffsets.length; } - public int write(DataOutputStream outputStream) throws IOException { - int startSize = outputStream.size(); - outputStream.writeInt(chromOffset); - outputStream.writeInt(start); - outputStream.writeInt(stop); - outputStream.writeUTF(id); - outputStream.writeByte(refPad); - writeIntArray(alleleOffsets, outputStream, true); - outputStream.writeFloat(qual); - outputStream.writeUTF(info); - outputStream.writeInt(filterOffset); - - int nGenotypes = genotypes.size(); - int expectedSizeOfGenotypes = nGenotypes == 0 ? 0 : genotypes.get(0).sizeInBytes() * nGenotypes; - outputStream.writeInt(nGenotypes); - outputStream.writeInt(expectedSizeOfGenotypes); - int obsSizeOfGenotypes = 0; - for ( GCFGenotype g : genotypes ) - obsSizeOfGenotypes += g.write(outputStream); - if ( obsSizeOfGenotypes != expectedSizeOfGenotypes ) - throw new RuntimeException("Expect and observed genotype sizes disagree! expect = " + expectedSizeOfGenotypes + " obs =" + obsSizeOfGenotypes); - - outputStream.writeInt(RECORD_TERMINATOR); - return outputStream.size() - startSize; - } private final String infoFieldString(VariantContext vc, final GCFHeaderBuilder GCFHeaderBuilder) { StringBuilder s = new StringBuilder(); @@ -200,13 +206,14 @@ public class GCF { return s.toString(); } - private final static int BUFFER_SIZE = 1048576; // 2**20 - public static DataOutputStream createOutputStream(final File file) throws FileNotFoundException { - return new DataOutputStream(new BufferedOutputStream(new FileOutputStream(file), BUFFER_SIZE)); + protected final static int BUFFER_SIZE = 1048576; // 2**20 + + public static DataInputStream createDataInputStream(final InputStream stream) { + return new DataInputStream(new BufferedInputStream(stream, BUFFER_SIZE)); } - public static DataInputStream createInputStream(final File file) throws FileNotFoundException { - return new DataInputStream(new BufferedInputStream(new FileInputStream(file), BUFFER_SIZE)); + public static FileInputStream createFileInputStream(final File file) throws FileNotFoundException { + return new FileInputStream(file); } protected final static int[] readIntArray(final DataInputStream inputStream) throws IOException { diff --git a/public/java/src/org/broadinstitute/sting/utils/gcf/GCFHeader.java b/public/java/src/org/broadinstitute/sting/utils/gcf/GCFHeader.java index d0c765cc4..6d96eda56 100644 --- a/public/java/src/org/broadinstitute/sting/utils/gcf/GCFHeader.java +++ b/public/java/src/org/broadinstitute/sting/utils/gcf/GCFHeader.java @@ -30,9 +30,7 @@ import org.broadinstitute.sting.utils.codecs.vcf.VCFConstants; import org.broadinstitute.sting.utils.exceptions.UserException; import org.broadinstitute.sting.utils.variantcontext.Allele; -import java.io.DataInputStream; -import java.io.DataOutputStream; -import java.io.IOException; +import java.io.*; import java.util.*; /** @@ -65,25 +63,45 @@ import java.util.*; public class GCFHeader { final protected static Logger logger = Logger.getLogger(GCFHeader.class); - private static byte[] MAGIC_HEADER = "GVCF0.1\1".getBytes(); + public final static int GCF_VERSION = 1; + public final static byte[] GCF_FILE_START_MARKER = "GCF\1".getBytes(); + public final static int FOOTER_START_MARKER = -1; + public final static long HEADER_FORWARD_REFERENCE_OFFSET = GCF_FILE_START_MARKER.length + 4; // for the version + + final int version; + long footerPosition; final List alleles; final List strings; final List samples; final List> filters; public GCFHeader(final Map allelesIn, final Map stringIn, final Map samplesIn) { + version = GCF_VERSION; + footerPosition = 0; this.alleles = linearize(allelesIn); this.strings = linearize(stringIn); this.samples = linearize(samplesIn); this.filters = null; // not used with this constructor } - public GCFHeader(DataInputStream inputStream) throws IOException { - byte[] headerTest = new byte[MAGIC_HEADER.length]; + public GCFHeader(FileInputStream fileInputStream) throws IOException { + DataInputStream inputStream = new DataInputStream(fileInputStream); + byte[] headerTest = new byte[GCF_FILE_START_MARKER.length]; inputStream.read(headerTest); - if ( ! Arrays.equals(headerTest, MAGIC_HEADER) ) { - throw new UserException("Could not read GVCF file. MAGIC_HEADER missing. Saw " + headerTest); + if ( ! Arrays.equals(headerTest, GCF_FILE_START_MARKER) ) { + throw new UserException("Could not read GVCF file. GCF_FILE_START_MARKER missing. Saw " + new String(headerTest)); } else { + version = inputStream.readInt(); + logger.info("Read GCF version " + version); + footerPosition = inputStream.readLong(); + logger.info("Read footer position of " + footerPosition); + long lastPos = fileInputStream.getChannel().position(); + logger.info(" Last position is " + lastPos); + + // seek to the footer + fileInputStream.getChannel().position(footerPosition); + if ( inputStream.readInt() != FOOTER_START_MARKER ) + throw new UserException.MalformedFile("Malformed GCF file: couldn't find the footer marker"); alleles = stringsToAlleles(readStrings(inputStream)); strings = readStrings(inputStream); samples = readStrings(inputStream); @@ -91,19 +109,28 @@ public class GCFHeader { logger.info(String.format("String map of %d elements", strings.size())); logger.info(String.format("Sample map of %d elements", samples.size())); filters = initializeFilterCache(); + fileInputStream.getChannel().position(lastPos); } } - public int write(final DataOutputStream outputStream) throws IOException { + public static int writeHeader(final DataOutputStream outputStream) throws IOException { int startBytes = outputStream.size(); - outputStream.write(MAGIC_HEADER); + outputStream.write(GCF_FILE_START_MARKER); + outputStream.writeInt(GCF_VERSION); + outputStream.writeLong(0); + return outputStream.size() - startBytes; + } + + public int writeFooter(final DataOutputStream outputStream) throws IOException { + int startBytes = outputStream.size(); + outputStream.writeInt(FOOTER_START_MARKER); // has to be the same as chrom encoding write(outputStream, allelesToStrings(alleles)); write(outputStream, strings); write(outputStream, samples); return outputStream.size() - startBytes; } - public void write(DataOutputStream outputStream, List l) throws IOException { + private void write(DataOutputStream outputStream, List l) throws IOException { outputStream.writeInt(l.size()); for ( String elt : l ) outputStream.writeUTF(elt); } diff --git a/public/java/src/org/broadinstitute/sting/utils/gcf/GCFWriter.java b/public/java/src/org/broadinstitute/sting/utils/gcf/GCFWriter.java new file mode 100644 index 000000000..7ff6e27a2 --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/utils/gcf/GCFWriter.java @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2011, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.utils.gcf; + +import org.broadinstitute.sting.utils.codecs.vcf.IndexingVCFWriter; +import org.broadinstitute.sting.utils.codecs.vcf.VCFHeader; +import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; +import org.broadinstitute.sting.utils.exceptions.UserException; +import org.broadinstitute.sting.utils.variantcontext.VariantContext; + +import java.io.*; + +/** + * GCFWriter implementing the VCFWriter interface + * @author Your Name + * @since Date created + */ +public class GCFWriter extends IndexingVCFWriter { + final boolean skipGenotypes; + final FileOutputStream fileOutputStream; + final DataOutputStream dataOutputStream; + final GCFHeaderBuilder gcfHeaderBuilder; + int nbytes = 0; + VCFHeader header = null; + File location; + + // -------------------------------------------------------------------------------- + // + // Constructors + // + // -------------------------------------------------------------------------------- + + public GCFWriter(File location, boolean enableOnTheFlyIndexing, boolean doNotWriteGenotypes) { + super(writerName(location, null), location, null, enableOnTheFlyIndexing); + this.location = location; + this.skipGenotypes = doNotWriteGenotypes; + + // write the output + try { + fileOutputStream = new FileOutputStream(location); + dataOutputStream = createDataOutputStream(fileOutputStream); + gcfHeaderBuilder = new GCFHeaderBuilder(); + } catch ( FileNotFoundException e ) { + throw new UserException.CouldNotCreateOutputFile(location, e); + } + } + + // -------------------------------------------------------------------------------- + // + // VCFWriter interface functions + // + // -------------------------------------------------------------------------------- + + @Override + public void writeHeader(VCFHeader header) { + this.header = header; + try { + nbytes += GCFHeader.writeHeader(dataOutputStream); + } catch ( IOException e ) { + throw new UserException.CouldNotCreateOutputFile(getStreamName(), "Couldn't write header", e); + } + } + + @Override + public void add(VariantContext vc) { + super.add(vc); + GCF gcf = new GCF(gcfHeaderBuilder, vc, skipGenotypes); + try { + nbytes += gcf.write(dataOutputStream); + } catch ( IOException e ) { + throw new UserException.CouldNotCreateOutputFile(getStreamName(), "Failed to add gcf record " + gcf + " to stream " + getStreamName(), e); + } + } + + @Override + public void close() { + // todo -- write out VCF header lines + GCFHeader gcfHeader = gcfHeaderBuilder.createHeader(); + try { + long headerPosition = nbytes; + nbytes += gcfHeader.writeFooter(dataOutputStream); + dataOutputStream.close(); + //System.out.println("Writing forward reference to " + headerPosition); + + RandomAccessFile raFile = new RandomAccessFile(location, "rw"); + raFile.seek(GCFHeader.HEADER_FORWARD_REFERENCE_OFFSET); + raFile.writeLong(headerPosition); + raFile.close(); + } catch ( IOException e ) { + throw new ReviewedStingException("Failed to close GCFWriter " + getStreamName(), e); + } + + super.close(); + } + + private static final DataOutputStream createDataOutputStream(final OutputStream stream) { + return new DataOutputStream(new BufferedOutputStream(stream, GCF.BUFFER_SIZE)); + } + +} From 59841f82324d543d2943a3bfeb88cbd83f93532e Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Thu, 8 Sep 2011 08:41:16 -0400 Subject: [PATCH 018/196] Fixing genotype given alleles for indels. Only take the records that start at this locus. --- ...NPGenotypeLikelihoodsCalculationModel.java | 23 +--------------- .../genotyper/UnifiedGenotyperEngine.java | 26 +++++++++++++++++-- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/SNPGenotypeLikelihoodsCalculationModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/SNPGenotypeLikelihoodsCalculationModel.java index 477155241..6905ce4a4 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/SNPGenotypeLikelihoodsCalculationModel.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/SNPGenotypeLikelihoodsCalculationModel.java @@ -26,14 +26,12 @@ package org.broadinstitute.sting.gatk.walkers.genotyper; import org.apache.log4j.Logger; -import org.broadinstitute.sting.commandline.RodBinding; import org.broadinstitute.sting.gatk.contexts.AlignmentContext; import org.broadinstitute.sting.gatk.contexts.AlignmentContextUtils; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; import org.broadinstitute.sting.utils.BaseUtils; import org.broadinstitute.sting.utils.baq.BAQ; -import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; import org.broadinstitute.sting.utils.exceptions.StingException; import org.broadinstitute.sting.utils.genotype.DiploidGenotype; import org.broadinstitute.sting.utils.pileup.PileupElement; @@ -58,25 +56,6 @@ public class SNPGenotypeLikelihoodsCalculationModel extends GenotypeLikelihoodsC useAlleleFromVCF = UAC.GenotypingMode == GENOTYPING_MODE.GENOTYPE_GIVEN_ALLELES; } - public static VariantContext getSNPVCFromAllelesRod(RefMetaDataTracker tracker, ReferenceContext ref, boolean requireSNP, Logger logger, final RodBinding allelesBinding) { - if ( tracker == null || ref == null || logger == null ) - throw new ReviewedStingException("Bad arguments: tracker=" + tracker + " ref=" + ref + " logger=" + logger); - VariantContext vc = null; - - // search for usable record - for( final VariantContext vc_input : tracker.getValues(allelesBinding) ) { - if ( vc_input != null && ! vc_input.isFiltered() && (! requireSNP || vc_input.isSNP() )) { - if ( vc == null ) { - vc = vc_input; - } else { - logger.warn("Multiple valid VCF records detected at site " + ref.getLocus() + ", only considering alleles from first record"); - } - } - } - - return vc; - } - public Allele getLikelihoods(RefMetaDataTracker tracker, ReferenceContext ref, Map contexts, @@ -96,7 +75,7 @@ public class SNPGenotypeLikelihoodsCalculationModel extends GenotypeLikelihoodsC if ( alternateAlleleToUse != null ) { bestAlternateAllele = alternateAlleleToUse.getBases()[0]; } else if ( useAlleleFromVCF ) { - VariantContext vc = getSNPVCFromAllelesRod(tracker, ref, true, logger, UAC.alleles); + VariantContext vc = UnifiedGenotyperEngine.getVCFromAllelesRod(tracker, ref, ref.getLocus(), true, logger, UAC.alleles); // ignore places where we don't have a variant if ( vc == null ) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java index b1332bdf9..c558ecfbe 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java @@ -27,6 +27,7 @@ package org.broadinstitute.sting.gatk.walkers.genotyper; import com.google.java.contract.Requires; import org.apache.log4j.Logger; +import org.broadinstitute.sting.commandline.RodBinding; import org.broadinstitute.sting.gatk.GenomeAnalysisEngine; import org.broadinstitute.sting.gatk.contexts.AlignmentContext; import org.broadinstitute.sting.gatk.contexts.AlignmentContextUtils; @@ -36,6 +37,7 @@ import org.broadinstitute.sting.gatk.walkers.annotator.VariantAnnotatorEngine; import org.broadinstitute.sting.utils.*; import org.broadinstitute.sting.utils.baq.BAQ; import org.broadinstitute.sting.utils.codecs.vcf.VCFConstants; +import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; import org.broadinstitute.sting.utils.pileup.PileupElement; import org.broadinstitute.sting.utils.pileup.ReadBackedExtendedEventPileup; import org.broadinstitute.sting.utils.pileup.ReadBackedPileup; @@ -233,7 +235,7 @@ public class UnifiedGenotyperEngine { private VariantCallContext generateEmptyContext(RefMetaDataTracker tracker, ReferenceContext ref, Map stratifiedContexts, AlignmentContext rawContext) { VariantContext vc; if ( UAC.GenotypingMode == GenotypeLikelihoodsCalculationModel.GENOTYPING_MODE.GENOTYPE_GIVEN_ALLELES ) { - VariantContext vcInput = SNPGenotypeLikelihoodsCalculationModel.getSNPVCFromAllelesRod(tracker, ref, false, logger, UAC.alleles); + VariantContext vcInput = UnifiedGenotyperEngine.getVCFromAllelesRod(tracker, ref, rawContext.getLocation(), false, logger, UAC.alleles); if ( vcInput == null ) return null; vc = new VariantContext("UG_call", vcInput.getChr(), vcInput.getStart(), vcInput.getEnd(), vcInput.getAlleles(), InferredGeneticContext.NO_NEG_LOG_10PERROR, null, null, ref.getBase()); @@ -633,7 +635,7 @@ public class UnifiedGenotyperEngine { // no extended event pileup // if we're genotyping given alleles and we have a requested SNP at this position, do SNP if (UAC.GenotypingMode == GenotypeLikelihoodsCalculationModel.GENOTYPING_MODE.GENOTYPE_GIVEN_ALLELES) { - VariantContext vcInput = SNPGenotypeLikelihoodsCalculationModel.getSNPVCFromAllelesRod(tracker, refContext, false, logger, UAC.alleles); + VariantContext vcInput = UnifiedGenotyperEngine.getVCFromAllelesRod(tracker, refContext, rawContext.getLocation(), false, logger, UAC.alleles); if (vcInput == null) return null; @@ -739,4 +741,24 @@ public class UnifiedGenotyperEngine { return afcm; } + + public static VariantContext getVCFromAllelesRod(RefMetaDataTracker tracker, ReferenceContext ref, GenomeLoc loc, boolean requireSNP, Logger logger, final RodBinding allelesBinding) { + if ( tracker == null || ref == null || logger == null ) + throw new ReviewedStingException("Bad arguments: tracker=" + tracker + " ref=" + ref + " logger=" + logger); + VariantContext vc = null; + + // search for usable record + for( final VariantContext vc_input : tracker.getValues(allelesBinding, loc) ) { + //System.out.println(vc_input); + if ( vc_input != null && ! vc_input.isFiltered() && (! requireSNP || vc_input.isSNP() )) { + if ( vc == null ) { + vc = vc_input; + } else { + logger.warn("Multiple valid VCF records detected in the alleles input file at site " + ref.getLocus() + ", only considering the first record"); + } + } + } + + return vc; + } } From 29c968ab604bb982600ebb9b5a3c6be035a482b1 Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Thu, 8 Sep 2011 08:42:43 -0400 Subject: [PATCH 019/196] clean up --- .../sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java | 1 - 1 file changed, 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java index c558ecfbe..87dd37bf6 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java @@ -749,7 +749,6 @@ public class UnifiedGenotyperEngine { // search for usable record for( final VariantContext vc_input : tracker.getValues(allelesBinding, loc) ) { - //System.out.println(vc_input); if ( vc_input != null && ! vc_input.isFiltered() && (! requireSNP || vc_input.isSNP() )) { if ( vc == null ) { vc = vc_input; From 6e6bf796d5f3a39c0dcab76893825a1c6e80d549 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 8 Sep 2011 08:46:38 -0400 Subject: [PATCH 020/196] first version of somatic detector --- .../walkers/cancer/AssignSomaticStatus.java | 184 ++++++++++++++++++ 1 file changed, 184 insertions(+) create mode 100644 public/java/src/org/broadinstitute/sting/gatk/walkers/cancer/AssignSomaticStatus.java diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/cancer/AssignSomaticStatus.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/cancer/AssignSomaticStatus.java new file mode 100644 index 000000000..d77621b6b --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/cancer/AssignSomaticStatus.java @@ -0,0 +1,184 @@ +/* + * Copyright (c) 2011, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.gatk.walkers.cancer; + +import net.sf.picard.util.MathUtil; +import org.broadinstitute.sting.commandline.Argument; +import org.broadinstitute.sting.commandline.ArgumentCollection; +import org.broadinstitute.sting.commandline.Output; +import org.broadinstitute.sting.gatk.arguments.StandardVariantContextInputArgumentCollection; +import org.broadinstitute.sting.gatk.contexts.AlignmentContext; +import org.broadinstitute.sting.gatk.contexts.ReferenceContext; +import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; +import org.broadinstitute.sting.gatk.walkers.RodWalker; +import org.broadinstitute.sting.utils.MathUtils; +import org.broadinstitute.sting.utils.SampleUtils; +import org.broadinstitute.sting.utils.Utils; +import org.broadinstitute.sting.utils.codecs.vcf.*; +import org.broadinstitute.sting.utils.text.XReadLines; +import org.broadinstitute.sting.utils.variantcontext.Allele; +import org.broadinstitute.sting.utils.variantcontext.Genotype; +import org.broadinstitute.sting.utils.variantcontext.VariantContext; +import org.broadinstitute.sting.utils.variantcontext.VariantContextUtils; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.*; + +/** + * Assigns somatic status to a set of calls + */ +public class AssignSomaticStatus extends RodWalker { + @ArgumentCollection + protected StandardVariantContextInputArgumentCollection variantCollection = new StandardVariantContextInputArgumentCollection(); + + @Argument(shortName="t", fullName="tumorsample", required=true, doc="List of tumor samples") + public Set tumorSamplesArg; + + @Argument(shortName="somaticPriorQ", fullName="somaticPriorQ", required=false, doc="Phred-scaled probability that a site is a somatic mutation") + public byte somaticPriorQ = 60; + + @Output + protected VCFWriter vcfWriter = null; + + private final String SOMATIC_TAG_NAME = "SOMATIC"; + private final String SOURCE_NAME = "AssignSomaticStatus"; + + private Set tumorSamples = new HashSet(); + private Set normalSamples = new HashSet(); + + /** + * Parse the familial relationship specification, and initialize VCF writer + */ + public void initialize() { + List rodNames = new ArrayList(); + rodNames.add(variantCollection.variants.getName()); + + Map vcfRods = VCFUtils.getVCFHeadersFromRods(getToolkit(), rodNames); + Set vcfSamples = SampleUtils.getSampleList(vcfRods, VariantContextUtils.GenotypeMergeType.REQUIRE_UNIQUE); + + // set up tumor and normal samples + for ( final String sample : vcfSamples ) { + if ( tumorSamplesArg.contains(sample) ) + tumorSamples.add(sample); + else + normalSamples.add(sample); + } + logger.info("N tumor samples: " + tumorSamples.size()); + logger.info("N normal samples: " + normalSamples.size()); + if ( tumorSamples.size() != normalSamples.size() ) + logger.warn("Number of tumor samples isn't equal the number of normal samples"); + + Set headerLines = new HashSet(); + headerLines.addAll(VCFUtils.getHeaderFields(this.getToolkit())); + headerLines.add(new VCFFormatHeaderLine(SOMATIC_TAG_NAME, 1, VCFHeaderLineType.Float, "Probability that the site is a somatic mutation")); + headerLines.add(new VCFHeaderLine("source", SOURCE_NAME)); + vcfWriter.writeHeader(new VCFHeader(headerLines, vcfSamples)); + } + + private double log10pNonRefInSamples(final VariantContext vc, final Set samples) { + return log10pSumInSamples(vc, samples, false); + } + + private double log10pRefInSamples(final VariantContext vc, final Set samples) { + return log10pSumInSamples(vc, samples, true); + } + + private double log10pSumInSamples(final VariantContext vc, final Set samples, boolean calcRefP) { + double log10p = 0; + + for ( final String sample : samples ) { + Genotype g = vc.getGenotype(sample); + if ( g.isNoCall() ) { + log10p += 0; + } else { + double[] gLikelihoods = MathUtils.normalizeFromLog10(g.getLikelihoods().getAsVector()); + double log10pNonRefSample = Math.log10(calcRefP ? gLikelihoods[0] : 1 - gLikelihoods[0]); + log10p += log10pNonRefSample; + } + } + + return log10p; + } + + private double calcLog10pSomatic(final VariantContext vc) { + // walk over tumors, and calculate pNonRef + double log10pNonRefInTumors = log10pNonRefInSamples(vc, tumorSamples); + double log10pRefInNormals = log10pRefInSamples(vc, normalSamples); + double log10SomaticPrior = MathUtils.phredScaleToLog10Probability(somaticPriorQ); + double log10Somatic = log10SomaticPrior + log10pNonRefInTumors - log10pRefInNormals; + return log10Somatic; + } + + /** + * For each variant in the file, determine the phasing for the child and replace the child's genotype with the trio's genotype + * + * @param tracker the reference meta-data tracker + * @param ref the reference context + * @param context the alignment context + * @return null + */ + @Override + public Integer map(RefMetaDataTracker tracker, ReferenceContext ref, AlignmentContext context) { + if (tracker != null) { + for ( final VariantContext vc : tracker.getValues(variantCollection.variants, context.getLocation()) ) { + double log10pSomatic = calcLog10pSomatic(vc); + + // write in the somatic status probability + Map attrs = new HashMap(); // vc.getAttributes()); + attrs.put(SOMATIC_TAG_NAME, MathUtils.log10ProbabilityToPhredScale(log10pSomatic)); + VariantContext newvc = VariantContext.modifyAttributes(vc, attrs); + + vcfWriter.add(newvc); + } + + return null; + } + + return null; + } + + /** + * Provide an initial value for reduce computations. + * + * @return Initial value of reduce. + */ + @Override + public Integer reduceInit() { + return null; + } + + /** + * Reduces a single map with the accumulator provided as the ReduceType. + * + * @param value result of the map. + * @param sum accumulator for the reduce. + * @return accumulator with result of the map taken into account. + */ + @Override + public Integer reduce(Integer value, Integer sum) { + return null; + } +} From e0020b2b295b0da8b3b58dff9781d6f807653d43 Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Thu, 8 Sep 2011 08:58:37 -0400 Subject: [PATCH 021/196] Fixing PrintRODs. Now has input and only prints out one copy of each record --- .../sting/gatk/walkers/PrintRODsWalker.java | 10 +++--- .../UnifiedGenotyperIntegrationTest.java | 34 ++++--------------- 2 files changed, 13 insertions(+), 31 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/PrintRODsWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/PrintRODsWalker.java index 84549b13a..7960f5c35 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/PrintRODsWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/PrintRODsWalker.java @@ -26,21 +26,23 @@ package org.broadinstitute.sting.gatk.walkers; import org.broad.tribble.Feature; +import org.broadinstitute.sting.commandline.Input; import org.broadinstitute.sting.commandline.Output; +import org.broadinstitute.sting.commandline.RodBinding; import org.broadinstitute.sting.gatk.contexts.AlignmentContext; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; -import org.broadinstitute.sting.gatk.refdata.VariantContextAdaptors; -import org.broadinstitute.sting.gatk.refdata.utils.GATKFeature; import java.io.PrintStream; -import java.util.Iterator; /** * Prints out all of the RODs in the input data set. Data is rendered using the toString() method * of the given ROD. */ public class PrintRODsWalker extends RodWalker { + @Input(fullName="input", shortName = "input", doc="The input ROD which should be printed out.", required=true) + public RodBinding input; + @Output PrintStream out; @@ -62,7 +64,7 @@ public class PrintRODsWalker extends RodWalker { if ( tracker == null ) return 0; - for ( Feature feature : tracker.getValues(Feature.class) ) { + for ( Feature feature : tracker.getValues(Feature.class, context.getLocation()) ) { out.println(feature.toString()); } diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperIntegrationTest.java index da0c8f81f..f0164b7c4 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperIntegrationTest.java @@ -32,24 +32,6 @@ public class UnifiedGenotyperIntegrationTest extends WalkerTest { executeTest("test MultiSample Pilot1", spec); } - // @Test - // todo - currently not working because when calling indels, using GENOTYPE_GIVEN_ALLELES yields a different result than in normal mode. To be fixed when extended events are removed. - public void testMultiSamplePilot2AndRecallingWithAlleles() { - String md5 = "b45636b29891f9df573ad2af6f507ee0"; - - WalkerTest.WalkerTestSpec spec1 = new WalkerTest.WalkerTestSpec( - baseCommand + " -I " + validationDataLocation + "pilot2_daughters.chr20.10k-11k.bam -o %s -L 20:10,000,000-10,050,000", 1, - Arrays.asList(md5)); - List result = executeTest("test MultiSample Pilot2", spec1).getFirst(); - - GenomeAnalysisEngine.resetRandomGenerator(); - - WalkerTest.WalkerTestSpec spec2 = new WalkerTest.WalkerTestSpec( - baseCommand + " --genotyping_mode GENOTYPE_GIVEN_ALLELES -alleles " + result.get(0).getAbsolutePath() + " -I " + validationDataLocation + "pilot2_daughters.chr20.10k-11k.bam -o %s -L 20:10,000,000-10,050,000", 1, - Arrays.asList(md5)); - executeTest("test MultiSample Pilot2 with alleles passed in", spec2); - } - @Test public void testWithAllelesPassedIn() { WalkerTest.WalkerTestSpec spec1 = new WalkerTest.WalkerTestSpec( @@ -87,15 +69,6 @@ public class UnifiedGenotyperIntegrationTest extends WalkerTest { executeTest("test compressed output", spec); } - // todo -- fixme -// @Test -// public void testCompressedOutputParallel() { -// WalkerTest.WalkerTestSpec spec = new WalkerTest.WalkerTestSpec( -// baseCommand + " -I " + validationDataLocation + "NA12878.1kg.p2.chr1_10mb_11_mb.SLX.bam -o %s -L 1:10,000,000-10,100,000 -nt 4", 1, -// Arrays.asList("gz"), Arrays.asList(COMPRESSED_OUTPUT_MD5)); -// executeTest("testCompressedOutput-nt4", spec); -// } - // -------------------------------------------------------------------------------------------------------------- // // testing parallelization @@ -296,6 +269,13 @@ public class UnifiedGenotyperIntegrationTest extends WalkerTest { "pilot2_daughters.chr20.10k-11k.bam -o %s -L 20:10,000,000-10,100,000", 1, Arrays.asList("94977d6e42e764280e9deaf4e3ac8c80")); executeTest("test MultiSample Pilot2 indels with alleles passed in and emitting all sites", spec2); + + WalkerTest.WalkerTestSpec spec3 = new WalkerTest.WalkerTestSpec( + baseCommandIndels + " --genotyping_mode GENOTYPE_GIVEN_ALLELES -alleles " + validationDataLocation + "indelAllelesForUG.vcf -I " + validationDataLocation + + "pilot2_daughters.chr20.10k-11k.bam -o %s -L 20:10,000,000-10,100,000", 1, + Arrays.asList("408d3aba4d094c067fc00a43992c2292")); + executeTest("test MultiSample Pilot2 indels with complicated records", spec3); + } From 9cba1019c83167c1d56f7e0d2a4d45b7eee1bbec Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Thu, 8 Sep 2011 09:25:13 -0400 Subject: [PATCH 022/196] Another fix for genotype given alleles for indels. Expanding the indel integration tests to include multiallelics and indel records that overlap --- .../genotyper/IndelGenotypeLikelihoodsCalculationModel.java | 2 +- .../walkers/genotyper/UnifiedGenotyperIntegrationTest.java | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/IndelGenotypeLikelihoodsCalculationModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/IndelGenotypeLikelihoodsCalculationModel.java index 41b340058..07f02de57 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/IndelGenotypeLikelihoodsCalculationModel.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/IndelGenotypeLikelihoodsCalculationModel.java @@ -321,7 +321,7 @@ public class IndelGenotypeLikelihoodsCalculationModel extends GenotypeLikelihood haplotypeMap.clear(); if (getAlleleListFromVCF) { - for( final VariantContext vc_input : tracker.getValues(UAC.alleles) ) { + for( final VariantContext vc_input : tracker.getValues(UAC.alleles, loc) ) { if( vc_input != null && allowableTypes.contains(vc_input.getType()) && ref.getLocus().getStart() == vc_input.getStart()) { diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperIntegrationTest.java index f0164b7c4..185880401 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperIntegrationTest.java @@ -271,9 +271,9 @@ public class UnifiedGenotyperIntegrationTest extends WalkerTest { executeTest("test MultiSample Pilot2 indels with alleles passed in and emitting all sites", spec2); WalkerTest.WalkerTestSpec spec3 = new WalkerTest.WalkerTestSpec( - baseCommandIndels + " --genotyping_mode GENOTYPE_GIVEN_ALLELES -alleles " + validationDataLocation + "indelAllelesForUG.vcf -I " + validationDataLocation + - "pilot2_daughters.chr20.10k-11k.bam -o %s -L 20:10,000,000-10,100,000", 1, - Arrays.asList("408d3aba4d094c067fc00a43992c2292")); + baseCommandIndels + " --genotyping_mode GENOTYPE_GIVEN_ALLELES -alleles " + validationDataLocation + "ALL.wgs.union_v2.20101123.indels.sites.vcf -I " + validationDataLocation + + "pilot2_daughters.chr20.10k-11k.bam -o %s -L 20:10,000,000-10,080,000", 1, + Arrays.asList("e66b7321e2ac91742ad3ef91040daafd")); executeTest("test MultiSample Pilot2 indels with complicated records", spec3); } From 2636d216dee775999c8a3ff8a66be83cd51bafed Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Thu, 8 Sep 2011 10:38:13 -0400 Subject: [PATCH 023/196] Adding indel vqsr integration test --- ...ntRecalibrationWalkersIntegrationTest.java | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibrationWalkersIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibrationWalkersIntegrationTest.java index a5b0412e8..f3fd08cdd 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibrationWalkersIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibrationWalkersIntegrationTest.java @@ -73,5 +73,53 @@ public class VariantRecalibrationWalkersIntegrationTest extends WalkerTest { Arrays.asList(params.cutVCFMD5)); executeTest("testApplyRecalibration-"+params.inVCF, spec); } + + VRTest indel = new VRTest("combined.phase1.chr20.raw.indels.sites.vcf", + "6d7ee4cb651c8b666e4a4523363caaff", // tranches + "4759b111a5aa53975d46e0f22c7983bf", // recal file + "5d7e07d8813db96ba3f3dfe4737f83d1"); // cut VCF + + @DataProvider(name = "VRIndelTest") + public Object[][] createData2() { + return new Object[][]{ {indel} }; + } + + @Test(dataProvider = "VRIndelTest") + public void testVariantRecalibratorIndel(VRTest params) { + //System.out.printf("PARAMS FOR %s is %s%n", vcf, clusterFile); + WalkerTest.WalkerTestSpec spec = new WalkerTest.WalkerTestSpec( + "-R " + b37KGReference + + " -known:prior=10.0 " + GATKDataLocation + "dbsnp_132_b37.leftAligned.vcf" + + " -training:prior=15.0 " + comparisonDataLocation + "Validated/Mills_Devine_Indels_2011/ALL.wgs.indels_mills_devine_hg19_leftAligned_collapsed_double_hit.sites.vcf" + + " -truth:prior=15.0 " + comparisonDataLocation + "Validated/Mills_Devine_Indels_2011/ALL.wgs.indels_mills_devine_hg19_leftAligned_collapsed_double_hit.sites.vcf" + + " -T VariantRecalibrator" + + " -input " + params.inVCF + + " -L 20:1,000,000-40,000,000" + + " -an QD -an ReadPosRankSum -an HaplotypeScore" + + " -percentBad 0.08" + + " -mode INDEL -mG 3" + + " --minNumBadVariants 0" + + " --trustAllPolymorphic" + // for speed + " -recalFile %s" + + " -tranchesFile %s", + Arrays.asList(params.recalMD5, params.tranchesMD5)); + executeTest("testVariantRecalibratorIndel-"+params.inVCF, spec).getFirst(); + } + + @Test(dataProvider = "VRIndelTest",dependsOnMethods="testVariantRecalibratorIndel") + public void testApplyRecalibrationIndel(VRTest params) { + WalkerTest.WalkerTestSpec spec = new WalkerTest.WalkerTestSpec( + "-R " + b37KGReference + + " -T ApplyRecalibration" + + " -L 20:12,000,000-30,000,000" + + " -mode INDEL" + + " -NO_HEADER" + + " -input " + params.inVCF + + " -o %s" + + " -tranchesFile " + MD5DB.getMD5FilePath(params.tranchesMD5, null) + + " -recalFile " + MD5DB.getMD5FilePath(params.recalMD5, null), + Arrays.asList(params.cutVCFMD5)); + executeTest("testApplyRecalibrationIndel-"+params.inVCF, spec); + } } From 7557f4a03a0d4e62d5de121cd44e051f4c74e929 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 8 Sep 2011 11:54:14 -0400 Subject: [PATCH 024/196] AssignSomaticStatus, now with the correct mathematical model --- .../walkers/cancer/AssignSomaticStatus.java | 71 ++++++++++++++----- 1 file changed, 54 insertions(+), 17 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/cancer/AssignSomaticStatus.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/cancer/AssignSomaticStatus.java index d77621b6b..389e3d49a 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/cancer/AssignSomaticStatus.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/cancer/AssignSomaticStatus.java @@ -60,10 +60,13 @@ public class AssignSomaticStatus extends RodWalker { @Argument(shortName="somaticPriorQ", fullName="somaticPriorQ", required=false, doc="Phred-scaled probability that a site is a somatic mutation") public byte somaticPriorQ = 60; + @Argument(shortName="somaticMinLOD", fullName="somaticMinLOD", required=false, doc="Phred-scaled min probability that a site should be called somatic mutation") + public byte somaticMinLOD = 1; + @Output protected VCFWriter vcfWriter = null; - private final String SOMATIC_TAG_NAME = "SOMATIC"; + private final String SOMATIC_LOD_TAG_NAME = "SOMATIC_LOD"; private final String SOURCE_NAME = "AssignSomaticStatus"; private Set tumorSamples = new HashSet(); @@ -93,43 +96,75 @@ public class AssignSomaticStatus extends RodWalker { Set headerLines = new HashSet(); headerLines.addAll(VCFUtils.getHeaderFields(this.getToolkit())); - headerLines.add(new VCFFormatHeaderLine(SOMATIC_TAG_NAME, 1, VCFHeaderLineType.Float, "Probability that the site is a somatic mutation")); + headerLines.add(new VCFInfoHeaderLine(VCFConstants.SOMATIC_KEY, 0, VCFHeaderLineType.Flag, "Is this a confidently called somatic mutation")); + headerLines.add(new VCFFormatHeaderLine(SOMATIC_LOD_TAG_NAME, 1, VCFHeaderLineType.Float, "log10 probability that the site is a somatic mutation")); headerLines.add(new VCFHeaderLine("source", SOURCE_NAME)); vcfWriter.writeHeader(new VCFHeader(headerLines, vcfSamples)); } private double log10pNonRefInSamples(final VariantContext vc, final Set samples) { - return log10pSumInSamples(vc, samples, false); - } + double[] log10ps = log10PLFromSamples(vc, samples, false); + return MathUtils.log10sumLog10(log10ps); // product of probs => prod in real space + } private double log10pRefInSamples(final VariantContext vc, final Set samples) { - return log10pSumInSamples(vc, samples, true); + double[] log10ps = log10PLFromSamples(vc, samples, true); + return MathUtils.sum(log10ps); // product is sum } - private double log10pSumInSamples(final VariantContext vc, final Set samples, boolean calcRefP) { - double log10p = 0; + private double[] log10PLFromSamples(final VariantContext vc, final Set samples, boolean calcRefP) { + double[] log10p = new double[samples.size()]; + int i = 0; for ( final String sample : samples ) { Genotype g = vc.getGenotype(sample); - if ( g.isNoCall() ) { - log10p += 0; - } else { + double log10pSample = -1000; + if ( ! g.isNoCall() ) { double[] gLikelihoods = MathUtils.normalizeFromLog10(g.getLikelihoods().getAsVector()); - double log10pNonRefSample = Math.log10(calcRefP ? gLikelihoods[0] : 1 - gLikelihoods[0]); - log10p += log10pNonRefSample; + log10pSample = Math.log10(calcRefP ? gLikelihoods[0] : 1 - gLikelihoods[0]); + log10pSample = Double.isInfinite(log10pSample) ? -10000 : log10pSample; } + log10p[i++] = log10pSample; } return log10p; } + /** + * P(somatic | D) + * = P(somatic) * P(D | somatic) + * = P(somatic) * P(D | normals are ref) * P(D | tumors are non-ref) + * + * P(! somatic | D) + * = P(! somatic) * P(D | ! somatic) + * = P(! somatic) * + * * ( P(D | normals are non-ref) * P(D | tumors are non-ref) [germline] + * + P(D | normals are ref) * P(D | tumors are ref)) [no-variant at all] + * + * @param vc + * @return + */ private double calcLog10pSomatic(final VariantContext vc) { - // walk over tumors, and calculate pNonRef + // walk over tumors double log10pNonRefInTumors = log10pNonRefInSamples(vc, tumorSamples); + double log10pRefInTumors = log10pRefInSamples(vc, tumorSamples); + + // walk over normals + double log10pNonRefInNormals = log10pNonRefInSamples(vc, normalSamples); double log10pRefInNormals = log10pRefInSamples(vc, normalSamples); - double log10SomaticPrior = MathUtils.phredScaleToLog10Probability(somaticPriorQ); - double log10Somatic = log10SomaticPrior + log10pNonRefInTumors - log10pRefInNormals; - return log10Somatic; + + // priors + double log10pSomaticPrior = MathUtils.phredScaleToLog10Probability(somaticPriorQ); + double log10pNotSomaticPrior = Math.log10(1 - MathUtils.phredScaleToProbability(somaticPriorQ)); + + double log10pNotSomaticGermline = log10pNonRefInNormals + log10pNonRefInTumors; + double log10pNotSomaticNoVariant = log10pRefInNormals + log10pRefInTumors; + + double log10pNotSomatic = log10pNotSomaticPrior + MathUtils.log10sumLog10(new double[]{log10pNotSomaticGermline, log10pNotSomaticNoVariant}); + double log10pSomatic = log10pSomaticPrior + log10pNonRefInTumors + log10pRefInNormals; + double lod = log10pSomatic - log10pNotSomatic; + + return Double.isInfinite(lod) ? -10000 : lod; } /** @@ -148,7 +183,9 @@ public class AssignSomaticStatus extends RodWalker { // write in the somatic status probability Map attrs = new HashMap(); // vc.getAttributes()); - attrs.put(SOMATIC_TAG_NAME, MathUtils.log10ProbabilityToPhredScale(log10pSomatic)); + attrs.put(SOMATIC_LOD_TAG_NAME, log10pSomatic); + if ( log10pSomatic > somaticMinLOD ) + attrs.put(VCFConstants.SOMATIC_KEY, true); VariantContext newvc = VariantContext.modifyAttributes(vc, attrs); vcfWriter.add(newvc); From 5edc8f8578240ae83f710d87bf631d335df436ee Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 8 Sep 2011 11:54:55 -0400 Subject: [PATCH 025/196] Moved to private package (intended home) --- .../walkers/cancer/AssignSomaticStatus.java | 221 ------------------ 1 file changed, 221 deletions(-) delete mode 100644 public/java/src/org/broadinstitute/sting/gatk/walkers/cancer/AssignSomaticStatus.java diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/cancer/AssignSomaticStatus.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/cancer/AssignSomaticStatus.java deleted file mode 100644 index 389e3d49a..000000000 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/cancer/AssignSomaticStatus.java +++ /dev/null @@ -1,221 +0,0 @@ -/* - * Copyright (c) 2011, The Broad Institute - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -package org.broadinstitute.sting.gatk.walkers.cancer; - -import net.sf.picard.util.MathUtil; -import org.broadinstitute.sting.commandline.Argument; -import org.broadinstitute.sting.commandline.ArgumentCollection; -import org.broadinstitute.sting.commandline.Output; -import org.broadinstitute.sting.gatk.arguments.StandardVariantContextInputArgumentCollection; -import org.broadinstitute.sting.gatk.contexts.AlignmentContext; -import org.broadinstitute.sting.gatk.contexts.ReferenceContext; -import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; -import org.broadinstitute.sting.gatk.walkers.RodWalker; -import org.broadinstitute.sting.utils.MathUtils; -import org.broadinstitute.sting.utils.SampleUtils; -import org.broadinstitute.sting.utils.Utils; -import org.broadinstitute.sting.utils.codecs.vcf.*; -import org.broadinstitute.sting.utils.text.XReadLines; -import org.broadinstitute.sting.utils.variantcontext.Allele; -import org.broadinstitute.sting.utils.variantcontext.Genotype; -import org.broadinstitute.sting.utils.variantcontext.VariantContext; -import org.broadinstitute.sting.utils.variantcontext.VariantContextUtils; - -import java.io.File; -import java.io.FileNotFoundException; -import java.util.*; - -/** - * Assigns somatic status to a set of calls - */ -public class AssignSomaticStatus extends RodWalker { - @ArgumentCollection - protected StandardVariantContextInputArgumentCollection variantCollection = new StandardVariantContextInputArgumentCollection(); - - @Argument(shortName="t", fullName="tumorsample", required=true, doc="List of tumor samples") - public Set tumorSamplesArg; - - @Argument(shortName="somaticPriorQ", fullName="somaticPriorQ", required=false, doc="Phred-scaled probability that a site is a somatic mutation") - public byte somaticPriorQ = 60; - - @Argument(shortName="somaticMinLOD", fullName="somaticMinLOD", required=false, doc="Phred-scaled min probability that a site should be called somatic mutation") - public byte somaticMinLOD = 1; - - @Output - protected VCFWriter vcfWriter = null; - - private final String SOMATIC_LOD_TAG_NAME = "SOMATIC_LOD"; - private final String SOURCE_NAME = "AssignSomaticStatus"; - - private Set tumorSamples = new HashSet(); - private Set normalSamples = new HashSet(); - - /** - * Parse the familial relationship specification, and initialize VCF writer - */ - public void initialize() { - List rodNames = new ArrayList(); - rodNames.add(variantCollection.variants.getName()); - - Map vcfRods = VCFUtils.getVCFHeadersFromRods(getToolkit(), rodNames); - Set vcfSamples = SampleUtils.getSampleList(vcfRods, VariantContextUtils.GenotypeMergeType.REQUIRE_UNIQUE); - - // set up tumor and normal samples - for ( final String sample : vcfSamples ) { - if ( tumorSamplesArg.contains(sample) ) - tumorSamples.add(sample); - else - normalSamples.add(sample); - } - logger.info("N tumor samples: " + tumorSamples.size()); - logger.info("N normal samples: " + normalSamples.size()); - if ( tumorSamples.size() != normalSamples.size() ) - logger.warn("Number of tumor samples isn't equal the number of normal samples"); - - Set headerLines = new HashSet(); - headerLines.addAll(VCFUtils.getHeaderFields(this.getToolkit())); - headerLines.add(new VCFInfoHeaderLine(VCFConstants.SOMATIC_KEY, 0, VCFHeaderLineType.Flag, "Is this a confidently called somatic mutation")); - headerLines.add(new VCFFormatHeaderLine(SOMATIC_LOD_TAG_NAME, 1, VCFHeaderLineType.Float, "log10 probability that the site is a somatic mutation")); - headerLines.add(new VCFHeaderLine("source", SOURCE_NAME)); - vcfWriter.writeHeader(new VCFHeader(headerLines, vcfSamples)); - } - - private double log10pNonRefInSamples(final VariantContext vc, final Set samples) { - double[] log10ps = log10PLFromSamples(vc, samples, false); - return MathUtils.log10sumLog10(log10ps); // product of probs => prod in real space - } - - private double log10pRefInSamples(final VariantContext vc, final Set samples) { - double[] log10ps = log10PLFromSamples(vc, samples, true); - return MathUtils.sum(log10ps); // product is sum - } - - private double[] log10PLFromSamples(final VariantContext vc, final Set samples, boolean calcRefP) { - double[] log10p = new double[samples.size()]; - - int i = 0; - for ( final String sample : samples ) { - Genotype g = vc.getGenotype(sample); - double log10pSample = -1000; - if ( ! g.isNoCall() ) { - double[] gLikelihoods = MathUtils.normalizeFromLog10(g.getLikelihoods().getAsVector()); - log10pSample = Math.log10(calcRefP ? gLikelihoods[0] : 1 - gLikelihoods[0]); - log10pSample = Double.isInfinite(log10pSample) ? -10000 : log10pSample; - } - log10p[i++] = log10pSample; - } - - return log10p; - } - - /** - * P(somatic | D) - * = P(somatic) * P(D | somatic) - * = P(somatic) * P(D | normals are ref) * P(D | tumors are non-ref) - * - * P(! somatic | D) - * = P(! somatic) * P(D | ! somatic) - * = P(! somatic) * - * * ( P(D | normals are non-ref) * P(D | tumors are non-ref) [germline] - * + P(D | normals are ref) * P(D | tumors are ref)) [no-variant at all] - * - * @param vc - * @return - */ - private double calcLog10pSomatic(final VariantContext vc) { - // walk over tumors - double log10pNonRefInTumors = log10pNonRefInSamples(vc, tumorSamples); - double log10pRefInTumors = log10pRefInSamples(vc, tumorSamples); - - // walk over normals - double log10pNonRefInNormals = log10pNonRefInSamples(vc, normalSamples); - double log10pRefInNormals = log10pRefInSamples(vc, normalSamples); - - // priors - double log10pSomaticPrior = MathUtils.phredScaleToLog10Probability(somaticPriorQ); - double log10pNotSomaticPrior = Math.log10(1 - MathUtils.phredScaleToProbability(somaticPriorQ)); - - double log10pNotSomaticGermline = log10pNonRefInNormals + log10pNonRefInTumors; - double log10pNotSomaticNoVariant = log10pRefInNormals + log10pRefInTumors; - - double log10pNotSomatic = log10pNotSomaticPrior + MathUtils.log10sumLog10(new double[]{log10pNotSomaticGermline, log10pNotSomaticNoVariant}); - double log10pSomatic = log10pSomaticPrior + log10pNonRefInTumors + log10pRefInNormals; - double lod = log10pSomatic - log10pNotSomatic; - - return Double.isInfinite(lod) ? -10000 : lod; - } - - /** - * For each variant in the file, determine the phasing for the child and replace the child's genotype with the trio's genotype - * - * @param tracker the reference meta-data tracker - * @param ref the reference context - * @param context the alignment context - * @return null - */ - @Override - public Integer map(RefMetaDataTracker tracker, ReferenceContext ref, AlignmentContext context) { - if (tracker != null) { - for ( final VariantContext vc : tracker.getValues(variantCollection.variants, context.getLocation()) ) { - double log10pSomatic = calcLog10pSomatic(vc); - - // write in the somatic status probability - Map attrs = new HashMap(); // vc.getAttributes()); - attrs.put(SOMATIC_LOD_TAG_NAME, log10pSomatic); - if ( log10pSomatic > somaticMinLOD ) - attrs.put(VCFConstants.SOMATIC_KEY, true); - VariantContext newvc = VariantContext.modifyAttributes(vc, attrs); - - vcfWriter.add(newvc); - } - - return null; - } - - return null; - } - - /** - * Provide an initial value for reduce computations. - * - * @return Initial value of reduce. - */ - @Override - public Integer reduceInit() { - return null; - } - - /** - * Reduces a single map with the accumulator provided as the ReduceType. - * - * @param value result of the map. - * @param sum accumulator for the reduce. - * @return accumulator with result of the map taken into account. - */ - @Override - public Integer reduce(Integer value, Integer sum) { - return null; - } -} From eaaba6eb5136a3fe5b4f91d9a72077cf1d9fc514 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Thu, 8 Sep 2011 13:17:34 -0400 Subject: [PATCH 027/196] Confirming that when stratifying by sample in VE the monomorphic sites for a given sample are not counted for the relevant metrics. Adding integration test to cover it. --- .../VariantEvalIntegrationTest.java | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java index 6c4393d6a..699c8fac7 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java @@ -14,6 +14,26 @@ public class VariantEvalIntegrationTest extends WalkerTest { private static String cmdRoot = "-T VariantEval" + " -R " + b36KGReference; + @Test + public void testStratifySamplesAndExcludeMonomorphicSites() { + WalkerTestSpec spec = new WalkerTestSpec( + buildCommandLine( + "-T VariantEval", + "-R " + b37KGReference, + "--dbsnp " + b37dbSNP132, + "--eval " + variantEvalTestDataRoot + "/CEU.trio.callsForVE.vcf", + "-noEV", + "-EV TiTvVariantEvaluator", + "-ST Sample", + "-BTI eval", + "-o %s" + ), + 1, + Arrays.asList("6a71b17c19f5914c277a99f45f5d9c39") + ); + executeTest("testStratifySamplesAndExcludeMonomorphicSites", spec); + } + @Test public void testFundamentalsCountVariantsSNPsAndIndels() { WalkerTestSpec spec = new WalkerTestSpec( From 48461b34afc6af2a545f961ac3563b7b0a602725 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 8 Sep 2011 15:01:13 -0400 Subject: [PATCH 028/196] Added TYPE argument to print out VariantType --- .../sting/gatk/walkers/variantutils/VariantsToTable.java | 1 + 1 file changed, 1 insertion(+) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantsToTable.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantsToTable.java index 2a877fb09..bf9ff35de 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantsToTable.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantsToTable.java @@ -309,6 +309,7 @@ public class VariantsToTable extends RodWalker { getters.put("HOM-REF", new Getter() { public String get(VariantContext vc) { return Integer.toString(vc.getHomRefCount()); } }); getters.put("HOM-VAR", new Getter() { public String get(VariantContext vc) { return Integer.toString(vc.getHomVarCount()); } }); getters.put("NO-CALL", new Getter() { public String get(VariantContext vc) { return Integer.toString(vc.getNoCallCount()); } }); + getters.put("TYPE", new Getter() { public String get(VariantContext vc) { return vc.getType().toString(); } }); getters.put("VAR", new Getter() { public String get(VariantContext vc) { return Integer.toString(vc.getHetCount() + vc.getHomVarCount()); } }); getters.put("NSAMPLES", new Getter() { public String get(VariantContext vc) { return Integer.toString(vc.getNSamples()); } }); getters.put("NCALLED", new Getter() { public String get(VariantContext vc) { return Integer.toString(vc.getNSamples() - vc.getNoCallCount()); } }); From 388c9a9c55119dc9b882b4e6f0704ccdb1330037 Mon Sep 17 00:00:00 2001 From: David Roazen Date: Fri, 2 Sep 2011 14:34:31 -0400 Subject: [PATCH 029/196] Enable public-only tests. Public-only tests will allow us to check for runtime public -> private dependencies when bamboo updates the github repository (currently, we only check for *compile-time* public -> private dependencies). To compile/run only public tests, append ".public" to the name of an existing test target: ant test.public ant integrationtest.public ant performancetest.public ant pipelinetest.public ant pipelinetestrun.public --- build.xml | 282 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 155 insertions(+), 127 deletions(-) diff --git a/build.xml b/build.xml index 275cb5555..beca6bce0 100644 --- a/build.xml +++ b/build.xml @@ -709,53 +709,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -769,20 +722,116 @@ - - - + + + + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -792,10 +841,10 @@ - + - - + - - + + + + - - + + + + + + + + + + + + + + - - + + - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - + - + + + - + - + + + - + - + + + - + - + + + - + + + + + + - - + + - - + + - - - - - - + + From 367bbee25a6bd73248a9aa2834c5c1fb5e625e83 Mon Sep 17 00:00:00 2001 From: Khalid Shakir Date: Fri, 9 Sep 2011 01:33:25 -0400 Subject: [PATCH 031/196] Fixed typo when printing the contents or last N lines of a file. Thanks to larryns. --- .../org/broadinstitute/sting/queue/engine/FunctionEdge.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scala/src/org/broadinstitute/sting/queue/engine/FunctionEdge.scala b/public/scala/src/org/broadinstitute/sting/queue/engine/FunctionEdge.scala index 68bc7ae61..162ed1b3c 100644 --- a/public/scala/src/org/broadinstitute/sting/queue/engine/FunctionEdge.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/engine/FunctionEdge.scala @@ -154,7 +154,7 @@ class FunctionEdge(val function: QFunction, val inputs: QNode, val outputs: QNod val maxLines = 100 val tailLines = IOUtils.tail(errorFile, maxLines) val nl = "%n".format() - val summary = if (tailLines.size <= maxLines) "Last %d lines".format(maxLines) else "Contents" + val summary = if (tailLines.size > maxLines) "Last %d lines".format(maxLines) else "Contents" logger.error("%s of %s:%n%s".format(summary, errorFile, tailLines.mkString(nl))) } else { logger.error("Unable to access log file: %s".format(errorFile)) From 6ad8943ca07bc9b89edc8cfb70123c4966f003df Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Fri, 9 Sep 2011 09:45:24 -0400 Subject: [PATCH 032/196] CompOverlap no longer keeps track of the number of comp sites since it wasn't (and cannot) keeping track of them correctly. --- .../gatk/walkers/varianteval/evaluators/CompOverlap.java | 6 +----- .../walkers/varianteval/VariantEvalIntegrationTest.java | 6 +++--- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/CompOverlap.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/CompOverlap.java index 5ccacac37..9facb11b5 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/CompOverlap.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/CompOverlap.java @@ -22,9 +22,6 @@ public class CompOverlap extends VariantEvaluator implements StandardEval { @DataPoint(description = "number of eval SNP sites") long nEvalVariants = 0; - @DataPoint(description = "number of comp SNP sites") - long nCompVariants = 0; - @DataPoint(description = "number of eval sites outside of comp sites") long novelSites = 0; @@ -76,9 +73,8 @@ public class CompOverlap extends VariantEvaluator implements StandardEval { public String update2(VariantContext eval, VariantContext comp, RefMetaDataTracker tracker, ReferenceContext ref, AlignmentContext context) { boolean evalIsGood = eval != null && eval.isPolymorphic(); - boolean compIsGood = comp != null && comp.isNotFiltered() && (eval == null || comp.getType() == eval.getType()); + boolean compIsGood = comp != null && comp.isNotFiltered(); - if (compIsGood) nCompVariants++; // count the number of comp events if (evalIsGood) nEvalVariants++; // count the number of eval events if (compIsGood && evalIsGood) { diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java index 699c8fac7..f94c20ff6 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java @@ -291,7 +291,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { @Test public void testCompOverlap() { String extraArgs = "-T VariantEval -R " + b37KGReference + " -L " + validationDataLocation + "VariantEval/pacbio.hg19.intervals --comp:comphapmap " + comparisonDataLocation + "Validated/HapMap/3.3/genotypes_r27_nr.b37_fwd.vcf --eval " + validationDataLocation + "VariantEval/pacbio.ts.recalibrated.vcf -noEV -EV CompOverlap -sn NA12878 -noST -ST Novelty -o %s"; - WalkerTestSpec spec = new WalkerTestSpec(extraArgs,1,Arrays.asList("462d4784dd55294ef9d5118217b157a5")); + WalkerTestSpec spec = new WalkerTestSpec(extraArgs,1,Arrays.asList("009ecc8376a20dce81ff5299ef6bfecb")); executeTestParallel("testCompOverlap",spec); } @@ -332,13 +332,13 @@ public class VariantEvalIntegrationTest extends WalkerTest { " -noST -noEV -ST Novelty -EV CompOverlap" + " -o %s"; - WalkerTestSpec spec = new WalkerTestSpec(extraArgs,1,Arrays.asList("a3c2177849cb00fdff99574cff7f0e4f")); + WalkerTestSpec spec = new WalkerTestSpec(extraArgs,1,Arrays.asList("0b81d97f843ec4a1a4222d1f9949bfca")); executeTestParallel("testMultipleCompTracks",spec); } @Test public void testPerSampleAndSubsettedSampleHaveSameResults() { - String md5 = "dab415cc76846e18fcf8c78f2b2ee033"; + String md5 = "b0565ac61b2860248e4abd478a177b5e"; WalkerTestSpec spec = new WalkerTestSpec( buildCommandLine( From 51eb95d6388d5f62e70d088fe433efda4f87bbe8 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Fri, 9 Sep 2011 11:46:37 -0400 Subject: [PATCH 034/196] Missed these tests before --- .../walkers/varianteval/VariantEvalIntegrationTest.java | 8 ++++---- .../walkers/variantutils/VCFStreamingIntegrationTest.java | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java index f94c20ff6..e992684bc 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java @@ -256,7 +256,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { " --eval " + validationDataLocation + "yri.trio.gatk_glftrio.intersection.annotated.filtered.chr1.vcf" + " --comp:comp_genotypes,VCF3 " + validationDataLocation + "yri.trio.gatk.ug.head.vcf"; WalkerTestSpec spec = new WalkerTestSpec(withSelect(tests, "DP < 50", "DP50") + " " + extraArgs + " -ST CpG -o %s", - 1, Arrays.asList("2df4f8911ffc3c8d042298723ed465f8")); + 1, Arrays.asList("f70997b6a3e7fdc89d11e1d61a2463d4")); executeTestParallel("testSelect1", spec); } @@ -273,7 +273,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { @Test public void testCompVsEvalAC() { String extraArgs = "-T VariantEval -R "+b36KGReference+" -o %s -ST CpG -EV GenotypeConcordance --eval:evalYRI,VCF3 " + validationDataLocation + "yri.trio.gatk.ug.very.few.lines.vcf --comp:compYRI,VCF3 " + validationDataLocation + "yri.trio.gatk.fake.genotypes.ac.test.vcf"; - WalkerTestSpec spec = new WalkerTestSpec(extraArgs,1,Arrays.asList("ed54aa127b173d8ad8b6482f2a929a42")); + WalkerTestSpec spec = new WalkerTestSpec(extraArgs,1,Arrays.asList("407682de41dcf139ea635e9cda21b912")); executeTestParallel("testCompVsEvalAC",spec); } @@ -303,7 +303,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { " --dbsnp " + b37dbSNP132 + " --eval:evalBI " + validationDataLocation + "VariantEval/ALL.20100201.chr20.bi.sites.vcf" + " -noST -ST Novelty -o %s"; - WalkerTestSpec spec = new WalkerTestSpec(extraArgs,1,Arrays.asList("18c44636e36d6657110bf984f8eac181")); + WalkerTestSpec spec = new WalkerTestSpec(extraArgs,1,Arrays.asList("424c9d438b1faa59b2c29413ba32f37b")); executeTestParallel("testEvalTrackWithoutGenotypes",spec); } @@ -315,7 +315,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { " --eval:evalBI " + validationDataLocation + "VariantEval/ALL.20100201.chr20.bi.sites.vcf" + " --eval:evalBC " + validationDataLocation + "VariantEval/ALL.20100201.chr20.bc.sites.vcf" + " -noST -ST Novelty -o %s"; - WalkerTestSpec spec = new WalkerTestSpec(extraArgs,1,Arrays.asList("1b8ae4fd10de0888bd843f833859d990")); + WalkerTestSpec spec = new WalkerTestSpec(extraArgs,1,Arrays.asList("18fa0b89ebfff51141975d7e4ce7a159")); executeTestParallel("testMultipleEvalTracksWithoutGenotypes",spec); } diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/VCFStreamingIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/VCFStreamingIntegrationTest.java index 3801e132d..00044f859 100644 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/VCFStreamingIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/VCFStreamingIntegrationTest.java @@ -98,7 +98,7 @@ public class VCFStreamingIntegrationTest extends WalkerTest { " -EV CompOverlap -noEV -noST" + " -o %s", 1, - Arrays.asList("ea09bf764adba9765b99921c5ba2c709") + Arrays.asList("d46a735ffa898f4aa6b3758c5b03f06d") ); executeTest("testVCFStreamingChain", selectTestSpec); From 60a36188453cea7e36785e80f02b7ecbbae1e779 Mon Sep 17 00:00:00 2001 From: David Roazen Date: Fri, 9 Sep 2011 11:45:57 -0400 Subject: [PATCH 035/196] Added "alltests" build targets. To run the same set of tests as the bamboo "All Tests" plan (unit tests, integration tests, and pipeline tests): ant alltests To do the same as above on only the public portion of the codebase: ant alltests.public --- build.xml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/build.xml b/build.xml index beca6bce0..a192ab6f3 100644 --- a/build.xml +++ b/build.xml @@ -896,6 +896,18 @@ + + + + + + + + + + + + From 6bd8a53efd218857a831133e2621b9c4d5dd6375 Mon Sep 17 00:00:00 2001 From: David Roazen Date: Fri, 9 Sep 2011 12:04:41 -0400 Subject: [PATCH 036/196] Fix nasty bug involving the build report generation when multiple test targets are specified on the same command line. Ant immutable properties: sometimes your friend, often your enemy. --- build.xml | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/build.xml b/build.xml index a192ab6f3..e5ad9daf0 100644 --- a/build.xml +++ b/build.xml @@ -821,6 +821,7 @@ + @@ -828,10 +829,6 @@ - - - - @@ -841,10 +838,10 @@ - + - - - + + - + @@ -913,7 +910,7 @@ - + @@ -921,7 +918,7 @@ - + @@ -929,7 +926,7 @@ - + @@ -937,7 +934,7 @@ - + @@ -946,24 +943,24 @@ - + - + - + - + - + From 06cb20f2a5fd2681a95613ae0b8b8a53c6002f4b Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 9 Sep 2011 12:56:45 -0400 Subject: [PATCH 037/196] Intermediate commit cleaning up scatter intervals -- Adding unit tests to ensure uniformity of intervals --- .../sting/utils/interval/IntervalUtils.java | 57 +- .../utils/interval/IntervalUtilsUnitTest.java | 1032 +++++++++-------- .../queue/extensions/gatk/GATKIntervals.scala | 130 +-- .../gatk/IntervalScatterFunction.scala | 4 +- .../gatk/GATKIntervalsUnitTest.scala | 10 +- 5 files changed, 658 insertions(+), 575 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/interval/IntervalUtils.java b/public/java/src/org/broadinstitute/sting/utils/interval/IntervalUtils.java index f551e1368..41cbbe59f 100644 --- a/public/java/src/org/broadinstitute/sting/utils/interval/IntervalUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/interval/IntervalUtils.java @@ -334,24 +334,44 @@ public class IntervalUtils { } /** - * Splits an interval list into multiple files. - * @param fileHeader The sam file header. + * Splits an interval list into multiple sublists. * @param locs The genome locs to split. * @param splits The stop points for the genome locs returned by splitFixedIntervals. - * @param scatterParts The output interval lists to write to. + * @return A list of lists of genome locs, split according to splits */ - public static void scatterFixedIntervals(SAMFileHeader fileHeader, List locs, List splits, List scatterParts) { - if (splits.size() != scatterParts.size()) - throw new UserException.BadArgumentValue("splits", String.format("Split points %d does not equal the number of scatter parts %d.", splits.size(), scatterParts.size())); - int fileIndex = 0; + public static List> splitIntervalsToSubLists(List locs, List splits) { int locIndex = 1; int start = 0; + List> sublists = new ArrayList>(splits.size()); for (Integer stop: splits) { - IntervalList intervalList = new IntervalList(fileHeader); + List curList = new ArrayList(); for (int i = start; i < stop; i++) - intervalList.add(toInterval(locs.get(i), locIndex++)); - intervalList.write(scatterParts.get(fileIndex++)); + curList.add(locs.get(i)); start = stop; + sublists.add(curList); + } + + return sublists; + } + + + /** + * Splits an interval list into multiple files. + * @param fileHeader The sam file header. + * @param splits Pre-divided genome locs returned by splitFixedIntervals. + * @param scatterParts The output interval lists to write to. + */ + public static void scatterFixedIntervals(SAMFileHeader fileHeader, List> splits, List scatterParts) { + if (splits.size() != scatterParts.size()) + throw new UserException.BadArgumentValue("splits", String.format("Split points %d does not equal the number of scatter parts %d.", splits.size(), scatterParts.size())); + + int fileIndex = 0; + int locIndex = 1; + for (final List split : splits) { + IntervalList intervalList = new IntervalList(fileHeader); + for (final GenomeLoc loc : split) + intervalList.add(toInterval(loc, locIndex++)); + intervalList.write(scatterParts.get(fileIndex++)); } } @@ -361,17 +381,15 @@ public class IntervalUtils { * @param numParts Number of parts to split the locs into. * @return The stop points to split the genome locs. */ - public static List splitFixedIntervals(List locs, int numParts) { + public static List> splitFixedIntervals(List locs, int numParts) { if (locs.size() < numParts) throw new UserException.BadArgumentValue("scatterParts", String.format("Cannot scatter %d locs into %d parts.", locs.size(), numParts)); - long locsSize = 0; - for (GenomeLoc loc: locs) - locsSize += loc.size(); - List splitPoints = new ArrayList(); + final long locsSize = intervalSize(locs); + final List splitPoints = new ArrayList(); addFixedSplit(splitPoints, locs, locsSize, 0, locs.size(), numParts); Collections.sort(splitPoints); splitPoints.add(locs.size()); - return splitPoints; + return splitIntervalsToSubLists(locs, splitPoints); } private static void addFixedSplit(List splitPoints, List locs, long locsSize, int startIndex, int stopIndex, int numParts) { @@ -441,4 +459,11 @@ public class IntervalUtils { return merged; } } + + public static final long intervalSize(final List locs) { + long size = 0; + for ( final GenomeLoc loc : locs ) + size += loc.size(); + return size; + } } diff --git a/public/java/test/org/broadinstitute/sting/utils/interval/IntervalUtilsUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/interval/IntervalUtilsUnitTest.java index bb892eec8..bd6bf9591 100644 --- a/public/java/test/org/broadinstitute/sting/utils/interval/IntervalUtilsUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/utils/interval/IntervalUtilsUnitTest.java @@ -30,6 +30,20 @@ public class IntervalUtilsUnitTest extends BaseTest { private SAMFileHeader hg19Header; private GenomeLocParser hg19GenomeLocParser; private List hg19ReferenceLocs; + private List hg19exomeIntervals; + + private List getLocs(String... intervals) { + return getLocs(Arrays.asList(intervals)); + } + + private List getLocs(List intervals) { + if (intervals.size() == 0) + return hg18ReferenceLocs; + List locs = new ArrayList(); + for (String interval: intervals) + locs.add(hg18GenomeLocParser.parseGenomeLoc(interval)); + return locs; + } @BeforeClass public void init() { @@ -54,511 +68,555 @@ public class IntervalUtilsUnitTest extends BaseTest { ReferenceSequenceFile seq = new CachingIndexedFastaSequenceFile(hg19Ref); hg19GenomeLocParser = new GenomeLocParser(seq); hg19ReferenceLocs = Collections.unmodifiableList(GenomeLocSortedSet.createSetFromSequenceDictionary(referenceDataSource.getReference().getSequenceDictionary()).toList()) ; + + hg19exomeIntervals = Collections.unmodifiableList(IntervalUtils.parseIntervalArguments(hg19GenomeLocParser, Arrays.asList(hg19Intervals), false)); } catch(FileNotFoundException ex) { throw new UserException.CouldNotReadInputFile(hg19Ref,ex); } } - @Test(expectedExceptions=UserException.class) - public void testMergeListsBySetOperatorNoOverlap() { - // a couple of lists we'll use for the testing - List listEveryTwoFromOne = new ArrayList(); - List listEveryTwoFromTwo = new ArrayList(); + // ------------------------------------------------------------------------------------- + // + // tests to ensure the quality of the interval cuts of the interval cutting functions + // + // ------------------------------------------------------------------------------------- - // create the two lists we'll use - for (int x = 1; x < 101; x++) { - if (x % 2 == 0) - listEveryTwoFromTwo.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x)); - else - listEveryTwoFromOne.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x)); + private class IntervalSlicingTest extends TestDataProvider { + public int parts; + public double maxAllowableVariance; + + private IntervalSlicingTest(final int parts, final double maxAllowableVariance) { + super(IntervalSlicingTest.class); + this.parts = parts; + this.maxAllowableVariance = maxAllowableVariance; } - List ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, listEveryTwoFromOne, IntervalSetRule.UNION); - Assert.assertEquals(ret.size(), 100); - ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, listEveryTwoFromOne, IntervalSetRule.INTERSECTION); - Assert.assertEquals(ret.size(), 0); - } - - @Test - public void testMergeListsBySetOperatorAllOverlap() { - // a couple of lists we'll use for the testing - List allSites = new ArrayList(); - List listEveryTwoFromTwo = new ArrayList(); - - // create the two lists we'll use - for (int x = 1; x < 101; x++) { - if (x % 2 == 0) - listEveryTwoFromTwo.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x)); - allSites.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x)); - } - - List ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, allSites, IntervalSetRule.UNION); - Assert.assertEquals(ret.size(), 150); - ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, allSites, IntervalSetRule.INTERSECTION); - Assert.assertEquals(ret.size(), 50); - } - - @Test - public void testMergeListsBySetOperator() { - // a couple of lists we'll use for the testing - List allSites = new ArrayList(); - List listEveryTwoFromTwo = new ArrayList(); - - // create the two lists we'll use - for (int x = 1; x < 101; x++) { - if (x % 5 == 0) { - listEveryTwoFromTwo.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x)); - allSites.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x)); - } - } - - List ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, allSites, IntervalSetRule.UNION); - Assert.assertEquals(ret.size(), 40); - ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, allSites, IntervalSetRule.INTERSECTION); - Assert.assertEquals(ret.size(), 20); - } - - @Test - public void testGetContigLengths() { - Map lengths = IntervalUtils.getContigSizes(new File(BaseTest.hg18Reference)); - Assert.assertEquals((long)lengths.get("chr1"), 247249719); - Assert.assertEquals((long)lengths.get("chr2"), 242951149); - Assert.assertEquals((long)lengths.get("chr3"), 199501827); - Assert.assertEquals((long)lengths.get("chr20"), 62435964); - Assert.assertEquals((long)lengths.get("chrX"), 154913754); - } - - private List getLocs(String... intervals) { - return getLocs(Arrays.asList(intervals)); - } - - private List getLocs(List intervals) { - if (intervals.size() == 0) - return hg18ReferenceLocs; - List locs = new ArrayList(); - for (String interval: intervals) - locs.add(hg18GenomeLocParser.parseGenomeLoc(interval)); - return locs; - } - - @Test - public void testParseIntervalArguments() { - Assert.assertEquals(getLocs().size(), 45); - Assert.assertEquals(getLocs("chr1", "chr2", "chr3").size(), 3); - Assert.assertEquals(getLocs("chr1:1-2", "chr1:4-5", "chr2:1-1", "chr3:2-2").size(), 4); - } - - @Test - public void testIsIntervalFile() { - Assert.assertTrue(IntervalUtils.isIntervalFile(BaseTest.validationDataLocation + "empty_intervals.list")); - Assert.assertTrue(IntervalUtils.isIntervalFile(BaseTest.validationDataLocation + "empty_intervals.list", true)); - - List extensions = Arrays.asList("bed", "interval_list", "intervals", "list", "picard"); - for (String extension: extensions) { - Assert.assertTrue(IntervalUtils.isIntervalFile("test_intervals." + extension, false), "Tested interval file extension: " + extension); + public String toString() { + return String.format("IntervalSlicingTest parts=%d maxVar=%.2f", parts, maxAllowableVariance); } } - @Test(expectedExceptions = UserException.CouldNotReadInputFile.class) - public void testMissingIntervalFile() { - IntervalUtils.isIntervalFile(BaseTest.validationDataLocation + "no_such_intervals.list"); + @DataProvider(name = "intervalslicingdata") + public Object[][] createTrees() { +// new IntervalSlicingTest(1, 0); +// new IntervalSlicingTest(2, 0.1); + new IntervalSlicingTest(5, 0.1); +// new IntervalSlicingTest(10, 0.1); +// new IntervalSlicingTest(67, 0.1); +// new IntervalSlicingTest(100, 0.1); +// new IntervalSlicingTest(500, 0.1); +// new IntervalSlicingTest(1000, 0.1); + return IntervalSlicingTest.getTests(IntervalSlicingTest.class); } - @Test - public void testFixedScatterIntervalsBasic() { - GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1"); - GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2"); - GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3"); + @Test(dataProvider = "intervalslicingdata") + public void testFixedScatterIntervalsAlgorithm(IntervalSlicingTest test) { + List> splits = IntervalUtils.splitFixedIntervals(hg19exomeIntervals, test.parts); - List files = testFiles("basic.", 3, ".intervals"); + long totalSize = IntervalUtils.intervalSize(hg19exomeIntervals); + long idealSplitSize = totalSize / test.parts; - List locs = getLocs("chr1", "chr2", "chr3"); - List splits = IntervalUtils.splitFixedIntervals(locs, files.size()); - IntervalUtils.scatterFixedIntervals(hg18Header, locs, splits, files); - - List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); - List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); - List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); - - Assert.assertEquals(locs1.size(), 1); - Assert.assertEquals(locs2.size(), 1); - Assert.assertEquals(locs3.size(), 1); - - Assert.assertEquals(locs1.get(0), chr1); - Assert.assertEquals(locs2.get(0), chr2); - Assert.assertEquals(locs3.get(0), chr3); - } - - @Test - public void testScatterFixedIntervalsLessFiles() { - GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1"); - GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2"); - GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3"); - GenomeLoc chr4 = hg18GenomeLocParser.parseGenomeLoc("chr4"); - - List files = testFiles("less.", 3, ".intervals"); - - List locs = getLocs("chr1", "chr2", "chr3", "chr4"); - List splits = IntervalUtils.splitFixedIntervals(locs, files.size()); - IntervalUtils.scatterFixedIntervals(hg18Header, locs, splits, files); - - List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); - List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); - List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); - - Assert.assertEquals(locs1.size(), 1); - Assert.assertEquals(locs2.size(), 1); - Assert.assertEquals(locs3.size(), 2); - - Assert.assertEquals(locs1.get(0), chr1); - Assert.assertEquals(locs2.get(0), chr2); - Assert.assertEquals(locs3.get(0), chr3); - Assert.assertEquals(locs3.get(1), chr4); - } - - @Test(expectedExceptions=UserException.BadArgumentValue.class) - public void testSplitFixedIntervalsMoreFiles() { - List files = testFiles("more.", 3, ".intervals"); - List locs = getLocs("chr1", "chr2"); - IntervalUtils.splitFixedIntervals(locs, files.size()); - } - - @Test(expectedExceptions=UserException.BadArgumentValue.class) - public void testScatterFixedIntervalsMoreFiles() { - List files = testFiles("more.", 3, ".intervals"); - List locs = getLocs("chr1", "chr2"); - List splits = IntervalUtils.splitFixedIntervals(locs, locs.size()); // locs.size() instead of files.size() - IntervalUtils.scatterFixedIntervals(hg18Header, locs, splits, files); - } - @Test - public void testScatterFixedIntervalsStart() { - List intervals = Arrays.asList("chr1:1-2", "chr1:4-5", "chr2:1-1", "chr3:2-2"); - GenomeLoc chr1a = hg18GenomeLocParser.parseGenomeLoc("chr1:1-2"); - GenomeLoc chr1b = hg18GenomeLocParser.parseGenomeLoc("chr1:4-5"); - GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2:1-1"); - GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3:2-2"); - - List files = testFiles("split.", 3, ".intervals"); - - List locs = getLocs(intervals); - List splits = IntervalUtils.splitFixedIntervals(locs, files.size()); - IntervalUtils.scatterFixedIntervals(hg18Header, locs, splits, files); - - List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); - List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); - List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); - - Assert.assertEquals(locs1.size(), 1); - Assert.assertEquals(locs2.size(), 1); - Assert.assertEquals(locs3.size(), 2); - - Assert.assertEquals(locs1.get(0), chr1a); - Assert.assertEquals(locs2.get(0), chr1b); - Assert.assertEquals(locs3.get(0), chr2); - Assert.assertEquals(locs3.get(1), chr3); - } - - @Test - public void testScatterFixedIntervalsMiddle() { - List intervals = Arrays.asList("chr1:1-1", "chr2:1-2", "chr2:4-5", "chr3:2-2"); - GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1:1-1"); - GenomeLoc chr2a = hg18GenomeLocParser.parseGenomeLoc("chr2:1-2"); - GenomeLoc chr2b = hg18GenomeLocParser.parseGenomeLoc("chr2:4-5"); - GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3:2-2"); - - List files = testFiles("split.", 3, ".intervals"); - - List locs = getLocs(intervals); - List splits = IntervalUtils.splitFixedIntervals(locs, files.size()); - IntervalUtils.scatterFixedIntervals(hg18Header, locs, splits, files); - - List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); - List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); - List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); - - Assert.assertEquals(locs1.size(), 1); - Assert.assertEquals(locs2.size(), 1); - Assert.assertEquals(locs3.size(), 2); - - Assert.assertEquals(locs1.get(0), chr1); - Assert.assertEquals(locs2.get(0), chr2a); - Assert.assertEquals(locs3.get(0), chr2b); - Assert.assertEquals(locs3.get(1), chr3); - } - - @Test - public void testScatterFixedIntervalsEnd() { - List intervals = Arrays.asList("chr1:1-1", "chr2:2-2", "chr3:1-2", "chr3:4-5"); - GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1:1-1"); - GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2:2-2"); - GenomeLoc chr3a = hg18GenomeLocParser.parseGenomeLoc("chr3:1-2"); - GenomeLoc chr3b = hg18GenomeLocParser.parseGenomeLoc("chr3:4-5"); - - List files = testFiles("split.", 3, ".intervals"); - - List locs = getLocs(intervals); - List splits = IntervalUtils.splitFixedIntervals(locs, files.size()); - IntervalUtils.scatterFixedIntervals(hg18Header, locs, splits, files); - - List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); - List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); - List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); - - Assert.assertEquals(locs1.size(), 2); - Assert.assertEquals(locs2.size(), 1); - Assert.assertEquals(locs3.size(), 1); - - Assert.assertEquals(locs1.get(0), chr1); - Assert.assertEquals(locs1.get(1), chr2); - Assert.assertEquals(locs2.get(0), chr3a); - Assert.assertEquals(locs3.get(0), chr3b); - } - - @Test - public void testScatterFixedIntervalsFile() { - List files = testFiles("sg.", 20, ".intervals"); - List locs = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(BaseTest.GATKDataLocation + "whole_exome_agilent_designed_120.targets.hg18.chr20.interval_list"), false); - List splits = IntervalUtils.splitFixedIntervals(locs, files.size()); - - int[] counts = { - 125, 138, 287, 291, 312, 105, 155, 324, - 295, 298, 141, 121, 285, 302, 282, 88, - 116, 274, 282, 248 -// 5169, 5573, 10017, 10567, 10551, -// 5087, 4908, 10120, 10435, 10399, -// 5391, 4735, 10621, 10352, 10654, -// 5227, 5256, 10151, 9649, 9825 - }; - - //String splitCounts = ""; - for (int lastIndex = 0, i = 0; i < splits.size(); i++) { - int splitIndex = splits.get(i); - int splitCount = (splitIndex - lastIndex); - //splitCounts += ", " + splitCount; - lastIndex = splitIndex; - Assert.assertEquals(splitCount, counts[i], "Num intervals in split " + i); + long sumOfSplitSizes = 0; + int counter = 0; + for ( final List split : splits ) { + long splitSize = IntervalUtils.intervalSize(split); + double sigma = (splitSize - idealSplitSize) / (1.0 * idealSplitSize); + logger.warn(String.format("Split %d size %d ideal %d sigma %.2f", counter, splitSize, idealSplitSize, sigma)); + counter++; + sumOfSplitSizes += splitSize; + Assert.assertTrue(Math.abs(sigma) <= test.maxAllowableVariance, String.format("Interval %d (size %d ideal %d) has a variance %.2f outside of the tolerated range %.2f", counter, splitSize, idealSplitSize, sigma, test.maxAllowableVariance)); } - //System.out.println(splitCounts.substring(2)); - IntervalUtils.scatterFixedIntervals(hg18Header, locs, splits, files); - - int locIndex = 0; - for (int i = 0; i < files.size(); i++) { - String file = files.get(i).toString(); - List parsedLocs = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(file), false); - Assert.assertEquals(parsedLocs.size(), counts[i], "Intervals in " + file); - for (GenomeLoc parsedLoc: parsedLocs) - Assert.assertEquals(parsedLoc, locs.get(locIndex), String.format("Genome loc %d from file %d", locIndex++, i)); - } - Assert.assertEquals(locIndex, locs.size(), "Total number of GenomeLocs"); + Assert.assertEquals(totalSize, sumOfSplitSizes, "Split intervals don't contain the exact number of bases in the origianl intervals"); } - @Test - public void testScatterFixedIntervalsMax() { - List files = testFiles("sg.", 85, ".intervals"); - List splits = IntervalUtils.splitFixedIntervals(hg19ReferenceLocs, files.size()); - IntervalUtils.scatterFixedIntervals(hg19Header, hg19ReferenceLocs, splits, files); - - for (int i = 0; i < files.size(); i++) { - String file = files.get(i).toString(); - List parsedLocs = IntervalUtils.parseIntervalArguments(hg19GenomeLocParser, Arrays.asList(file), false); - Assert.assertEquals(parsedLocs.size(), 1, "parsedLocs[" + i + "].size()"); - Assert.assertEquals(parsedLocs.get(0), hg19ReferenceLocs.get(i), "parsedLocs[" + i + "].get()"); - } - } - - @Test - public void testScatterContigIntervalsOrder() { - List intervals = Arrays.asList("chr2:1-1", "chr1:1-1", "chr3:2-2"); - GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1:1-1"); - GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2:1-1"); - GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3:2-2"); - - List files = testFiles("split.", 3, ".intervals"); - - IntervalUtils.scatterContigIntervals(hg18Header, getLocs(intervals), files); - - List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); - List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); - List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); - - Assert.assertEquals(locs1.size(), 1); - Assert.assertEquals(locs2.size(), 1); - Assert.assertEquals(locs3.size(), 1); - - Assert.assertEquals(locs1.get(0), chr2); - Assert.assertEquals(locs2.get(0), chr1); - Assert.assertEquals(locs3.get(0), chr3); - } - - @Test - public void testScatterContigIntervalsBasic() { - GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1"); - GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2"); - GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3"); - - List files = testFiles("contig_basic.", 3, ".intervals"); - - IntervalUtils.scatterContigIntervals(hg18Header, getLocs("chr1", "chr2", "chr3"), files); - - List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); - List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); - List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); - - Assert.assertEquals(locs1.size(), 1); - Assert.assertEquals(locs2.size(), 1); - Assert.assertEquals(locs3.size(), 1); - - Assert.assertEquals(locs1.get(0), chr1); - Assert.assertEquals(locs2.get(0), chr2); - Assert.assertEquals(locs3.get(0), chr3); - } - - @Test - public void testScatterContigIntervalsLessFiles() { - GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1"); - GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2"); - GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3"); - GenomeLoc chr4 = hg18GenomeLocParser.parseGenomeLoc("chr4"); - - List files = testFiles("contig_less.", 3, ".intervals"); - - IntervalUtils.scatterContigIntervals(hg18Header, getLocs("chr1", "chr2", "chr3", "chr4"), files); - - List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); - List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); - List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); - - Assert.assertEquals(locs1.size(), 1); - Assert.assertEquals(locs2.size(), 1); - Assert.assertEquals(locs3.size(), 2); - - Assert.assertEquals(locs1.get(0), chr1); - Assert.assertEquals(locs2.get(0), chr2); - Assert.assertEquals(locs3.get(0), chr3); - Assert.assertEquals(locs3.get(1), chr4); - } - - @Test(expectedExceptions=UserException.BadArgumentValue.class) - public void testScatterContigIntervalsMoreFiles() { - List files = testFiles("contig_more.", 3, ".intervals"); - IntervalUtils.scatterContigIntervals(hg18Header, getLocs("chr1", "chr2"), files); - } - - @Test - public void testScatterContigIntervalsStart() { - List intervals = Arrays.asList("chr1:1-2", "chr1:4-5", "chr2:1-1", "chr3:2-2"); - GenomeLoc chr1a = hg18GenomeLocParser.parseGenomeLoc("chr1:1-2"); - GenomeLoc chr1b = hg18GenomeLocParser.parseGenomeLoc("chr1:4-5"); - GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2:1-1"); - GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3:2-2"); - - List files = testFiles("contig_split_start.", 3, ".intervals"); - - IntervalUtils.scatterContigIntervals(hg18Header, getLocs(intervals), files); - - List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); - List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); - List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); - - Assert.assertEquals(locs1.size(), 2); - Assert.assertEquals(locs2.size(), 1); - Assert.assertEquals(locs3.size(), 1); - - Assert.assertEquals(locs1.get(0), chr1a); - Assert.assertEquals(locs1.get(1), chr1b); - Assert.assertEquals(locs2.get(0), chr2); - Assert.assertEquals(locs3.get(0), chr3); - } - - @Test - public void testScatterContigIntervalsMiddle() { - List intervals = Arrays.asList("chr1:1-1", "chr2:1-2", "chr2:4-5", "chr3:2-2"); - GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1:1-1"); - GenomeLoc chr2a = hg18GenomeLocParser.parseGenomeLoc("chr2:1-2"); - GenomeLoc chr2b = hg18GenomeLocParser.parseGenomeLoc("chr2:4-5"); - GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3:2-2"); - - List files = testFiles("contig_split_middle.", 3, ".intervals"); - - IntervalUtils.scatterContigIntervals(hg18Header, getLocs(intervals), files); - - List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); - List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); - List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); - - Assert.assertEquals(locs1.size(), 1); - Assert.assertEquals(locs2.size(), 2); - Assert.assertEquals(locs3.size(), 1); - - Assert.assertEquals(locs1.get(0), chr1); - Assert.assertEquals(locs2.get(0), chr2a); - Assert.assertEquals(locs2.get(1), chr2b); - Assert.assertEquals(locs3.get(0), chr3); - } - - @Test - public void testScatterContigIntervalsEnd() { - List intervals = Arrays.asList("chr1:1-1", "chr2:2-2", "chr3:1-2", "chr3:4-5"); - GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1:1-1"); - GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2:2-2"); - GenomeLoc chr3a = hg18GenomeLocParser.parseGenomeLoc("chr3:1-2"); - GenomeLoc chr3b = hg18GenomeLocParser.parseGenomeLoc("chr3:4-5"); - - List files = testFiles("contig_split_end.", 3 ,".intervals"); - - IntervalUtils.scatterContigIntervals(hg18Header, getLocs(intervals), files); - - List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); - List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); - List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); - - Assert.assertEquals(locs1.size(), 1); - Assert.assertEquals(locs2.size(), 1); - Assert.assertEquals(locs3.size(), 2); - - Assert.assertEquals(locs1.get(0), chr1); - Assert.assertEquals(locs2.get(0), chr2); - Assert.assertEquals(locs3.get(0), chr3a); - Assert.assertEquals(locs3.get(1), chr3b); - } - - @Test - public void testScatterContigIntervalsMax() { - List files = testFiles("sg.", 85, ".intervals"); - IntervalUtils.scatterContigIntervals(hg19Header, hg19ReferenceLocs, files); - - for (int i = 0; i < files.size(); i++) { - String file = files.get(i).toString(); - List parsedLocs = IntervalUtils.parseIntervalArguments(hg19GenomeLocParser, Arrays.asList(file), false); - Assert.assertEquals(parsedLocs.size(), 1, "parsedLocs[" + i + "].size()"); - Assert.assertEquals(parsedLocs.get(0), hg19ReferenceLocs.get(i), "parsedLocs[" + i + "].get()"); - } - } - - private List testFiles(String prefix, int count, String suffix) { - ArrayList files = new ArrayList(); - for (int i = 1; i <= count; i++) { - files.add(createTempFile(prefix + i, suffix)); - } - return files; - } - - @DataProvider(name="unmergedIntervals") - public Object[][] getUnmergedIntervals() { - return new Object[][] { - new Object[] {"small_unmerged_picard_intervals.list"}, - new Object[] {"small_unmerged_gatk_intervals.list"} - }; - } - - @Test(dataProvider="unmergedIntervals") - public void testUnmergedIntervals(String unmergedIntervals) { - List locs = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Collections.singletonList(validationDataLocation + unmergedIntervals), false); - Assert.assertEquals(locs.size(), 2); - - List merged = IntervalUtils.mergeIntervalLocations(locs, IntervalMergingRule.ALL); - Assert.assertEquals(merged.size(), 1); - } +// @Test(expectedExceptions=UserException.class) +// public void testMergeListsBySetOperatorNoOverlap() { +// // a couple of lists we'll use for the testing +// List listEveryTwoFromOne = new ArrayList(); +// List listEveryTwoFromTwo = new ArrayList(); +// +// // create the two lists we'll use +// for (int x = 1; x < 101; x++) { +// if (x % 2 == 0) +// listEveryTwoFromTwo.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x)); +// else +// listEveryTwoFromOne.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x)); +// } +// +// List ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, listEveryTwoFromOne, IntervalSetRule.UNION); +// Assert.assertEquals(ret.size(), 100); +// ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, listEveryTwoFromOne, IntervalSetRule.INTERSECTION); +// Assert.assertEquals(ret.size(), 0); +// } +// +// @Test +// public void testMergeListsBySetOperatorAllOverlap() { +// // a couple of lists we'll use for the testing +// List allSites = new ArrayList(); +// List listEveryTwoFromTwo = new ArrayList(); +// +// // create the two lists we'll use +// for (int x = 1; x < 101; x++) { +// if (x % 2 == 0) +// listEveryTwoFromTwo.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x)); +// allSites.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x)); +// } +// +// List ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, allSites, IntervalSetRule.UNION); +// Assert.assertEquals(ret.size(), 150); +// ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, allSites, IntervalSetRule.INTERSECTION); +// Assert.assertEquals(ret.size(), 50); +// } +// +// @Test +// public void testMergeListsBySetOperator() { +// // a couple of lists we'll use for the testing +// List allSites = new ArrayList(); +// List listEveryTwoFromTwo = new ArrayList(); +// +// // create the two lists we'll use +// for (int x = 1; x < 101; x++) { +// if (x % 5 == 0) { +// listEveryTwoFromTwo.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x)); +// allSites.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x)); +// } +// } +// +// List ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, allSites, IntervalSetRule.UNION); +// Assert.assertEquals(ret.size(), 40); +// ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, allSites, IntervalSetRule.INTERSECTION); +// Assert.assertEquals(ret.size(), 20); +// } +// +// @Test +// public void testGetContigLengths() { +// Map lengths = IntervalUtils.getContigSizes(new File(BaseTest.hg18Reference)); +// Assert.assertEquals((long)lengths.get("chr1"), 247249719); +// Assert.assertEquals((long)lengths.get("chr2"), 242951149); +// Assert.assertEquals((long)lengths.get("chr3"), 199501827); +// Assert.assertEquals((long)lengths.get("chr20"), 62435964); +// Assert.assertEquals((long)lengths.get("chrX"), 154913754); +// } +// +// @Test +// public void testParseIntervalArguments() { +// Assert.assertEquals(getLocs().size(), 45); +// Assert.assertEquals(getLocs("chr1", "chr2", "chr3").size(), 3); +// Assert.assertEquals(getLocs("chr1:1-2", "chr1:4-5", "chr2:1-1", "chr3:2-2").size(), 4); +// } +// +// @Test +// public void testIsIntervalFile() { +// Assert.assertTrue(IntervalUtils.isIntervalFile(BaseTest.validationDataLocation + "empty_intervals.list")); +// Assert.assertTrue(IntervalUtils.isIntervalFile(BaseTest.validationDataLocation + "empty_intervals.list", true)); +// +// List extensions = Arrays.asList("bed", "interval_list", "intervals", "list", "picard"); +// for (String extension: extensions) { +// Assert.assertTrue(IntervalUtils.isIntervalFile("test_intervals." + extension, false), "Tested interval file extension: " + extension); +// } +// } +// +// @Test(expectedExceptions = UserException.CouldNotReadInputFile.class) +// public void testMissingIntervalFile() { +// IntervalUtils.isIntervalFile(BaseTest.validationDataLocation + "no_such_intervals.list"); +// } +// +// @Test +// public void testFixedScatterIntervalsBasic() { +// GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1"); +// GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2"); +// GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3"); +// +// List files = testFiles("basic.", 3, ".intervals"); +// +// List locs = getLocs("chr1", "chr2", "chr3"); +// List splits = IntervalUtils.splitFixedIntervals(locs, files.size()); +// IntervalUtils.scatterFixedIntervals(hg18Header, locs, splits, files); +// +// List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); +// List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); +// List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); +// +// Assert.assertEquals(locs1.size(), 1); +// Assert.assertEquals(locs2.size(), 1); +// Assert.assertEquals(locs3.size(), 1); +// +// Assert.assertEquals(locs1.get(0), chr1); +// Assert.assertEquals(locs2.get(0), chr2); +// Assert.assertEquals(locs3.get(0), chr3); +// } +// +// @Test +// public void testScatterFixedIntervalsLessFiles() { +// GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1"); +// GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2"); +// GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3"); +// GenomeLoc chr4 = hg18GenomeLocParser.parseGenomeLoc("chr4"); +// +// List files = testFiles("less.", 3, ".intervals"); +// +// List locs = getLocs("chr1", "chr2", "chr3", "chr4"); +// List splits = IntervalUtils.splitFixedIntervals(locs, files.size()); +// IntervalUtils.scatterFixedIntervals(hg18Header, locs, splits, files); +// +// List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); +// List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); +// List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); +// +// Assert.assertEquals(locs1.size(), 1); +// Assert.assertEquals(locs2.size(), 1); +// Assert.assertEquals(locs3.size(), 2); +// +// Assert.assertEquals(locs1.get(0), chr1); +// Assert.assertEquals(locs2.get(0), chr2); +// Assert.assertEquals(locs3.get(0), chr3); +// Assert.assertEquals(locs3.get(1), chr4); +// } +// +// @Test(expectedExceptions=UserException.BadArgumentValue.class) +// public void testSplitFixedIntervalsMoreFiles() { +// List files = testFiles("more.", 3, ".intervals"); +// List locs = getLocs("chr1", "chr2"); +// IntervalUtils.splitFixedIntervals(locs, files.size()); +// } +// +// @Test(expectedExceptions=UserException.BadArgumentValue.class) +// public void testScatterFixedIntervalsMoreFiles() { +// List files = testFiles("more.", 3, ".intervals"); +// List locs = getLocs("chr1", "chr2"); +// List splits = IntervalUtils.splitFixedIntervals(locs, locs.size()); // locs.size() instead of files.size() +// IntervalUtils.scatterFixedIntervals(hg18Header, locs, splits, files); +// } +// @Test +// public void testScatterFixedIntervalsStart() { +// List intervals = Arrays.asList("chr1:1-2", "chr1:4-5", "chr2:1-1", "chr3:2-2"); +// GenomeLoc chr1a = hg18GenomeLocParser.parseGenomeLoc("chr1:1-2"); +// GenomeLoc chr1b = hg18GenomeLocParser.parseGenomeLoc("chr1:4-5"); +// GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2:1-1"); +// GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3:2-2"); +// +// List files = testFiles("split.", 3, ".intervals"); +// +// List locs = getLocs(intervals); +// List splits = IntervalUtils.splitFixedIntervals(locs, files.size()); +// IntervalUtils.scatterFixedIntervals(hg18Header, locs, splits, files); +// +// List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); +// List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); +// List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); +// +// Assert.assertEquals(locs1.size(), 1); +// Assert.assertEquals(locs2.size(), 1); +// Assert.assertEquals(locs3.size(), 2); +// +// Assert.assertEquals(locs1.get(0), chr1a); +// Assert.assertEquals(locs2.get(0), chr1b); +// Assert.assertEquals(locs3.get(0), chr2); +// Assert.assertEquals(locs3.get(1), chr3); +// } +// +// @Test +// public void testScatterFixedIntervalsMiddle() { +// List intervals = Arrays.asList("chr1:1-1", "chr2:1-2", "chr2:4-5", "chr3:2-2"); +// GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1:1-1"); +// GenomeLoc chr2a = hg18GenomeLocParser.parseGenomeLoc("chr2:1-2"); +// GenomeLoc chr2b = hg18GenomeLocParser.parseGenomeLoc("chr2:4-5"); +// GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3:2-2"); +// +// List files = testFiles("split.", 3, ".intervals"); +// +// List locs = getLocs(intervals); +// List splits = IntervalUtils.splitFixedIntervals(locs, files.size()); +// IntervalUtils.scatterFixedIntervals(hg18Header, locs, splits, files); +// +// List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); +// List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); +// List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); +// +// Assert.assertEquals(locs1.size(), 1); +// Assert.assertEquals(locs2.size(), 1); +// Assert.assertEquals(locs3.size(), 2); +// +// Assert.assertEquals(locs1.get(0), chr1); +// Assert.assertEquals(locs2.get(0), chr2a); +// Assert.assertEquals(locs3.get(0), chr2b); +// Assert.assertEquals(locs3.get(1), chr3); +// } +// +// @Test +// public void testScatterFixedIntervalsEnd() { +// List intervals = Arrays.asList("chr1:1-1", "chr2:2-2", "chr3:1-2", "chr3:4-5"); +// GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1:1-1"); +// GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2:2-2"); +// GenomeLoc chr3a = hg18GenomeLocParser.parseGenomeLoc("chr3:1-2"); +// GenomeLoc chr3b = hg18GenomeLocParser.parseGenomeLoc("chr3:4-5"); +// +// List files = testFiles("split.", 3, ".intervals"); +// +// List locs = getLocs(intervals); +// List splits = IntervalUtils.splitFixedIntervals(locs, files.size()); +// IntervalUtils.scatterFixedIntervals(hg18Header, locs, splits, files); +// +// List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); +// List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); +// List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); +// +// Assert.assertEquals(locs1.size(), 2); +// Assert.assertEquals(locs2.size(), 1); +// Assert.assertEquals(locs3.size(), 1); +// +// Assert.assertEquals(locs1.get(0), chr1); +// Assert.assertEquals(locs1.get(1), chr2); +// Assert.assertEquals(locs2.get(0), chr3a); +// Assert.assertEquals(locs3.get(0), chr3b); +// } +// +// @Test +// public void testScatterFixedIntervalsFile() { +// List files = testFiles("sg.", 20, ".intervals"); +// List locs = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(BaseTest.GATKDataLocation + "whole_exome_agilent_designed_120.targets.hg18.chr20.interval_list"), false); +// List splits = IntervalUtils.splitFixedIntervals(locs, files.size()); +// +// int[] counts = { +// 125, 138, 287, 291, 312, 105, 155, 324, +// 295, 298, 141, 121, 285, 302, 282, 88, +// 116, 274, 282, 248 +//// 5169, 5573, 10017, 10567, 10551, +//// 5087, 4908, 10120, 10435, 10399, +//// 5391, 4735, 10621, 10352, 10654, +//// 5227, 5256, 10151, 9649, 9825 +// }; +// +// //String splitCounts = ""; +// for (int lastIndex = 0, i = 0; i < splits.size(); i++) { +// int splitIndex = splits.get(i); +// int splitCount = (splitIndex - lastIndex); +// //splitCounts += ", " + splitCount; +// lastIndex = splitIndex; +// Assert.assertEquals(splitCount, counts[i], "Num intervals in split " + i); +// } +// //System.out.println(splitCounts.substring(2)); +// +// IntervalUtils.scatterFixedIntervals(hg18Header, locs, splits, files); +// +// int locIndex = 0; +// for (int i = 0; i < files.size(); i++) { +// String file = files.get(i).toString(); +// List parsedLocs = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(file), false); +// Assert.assertEquals(parsedLocs.size(), counts[i], "Intervals in " + file); +// for (GenomeLoc parsedLoc: parsedLocs) +// Assert.assertEquals(parsedLoc, locs.get(locIndex), String.format("Genome loc %d from file %d", locIndex++, i)); +// } +// Assert.assertEquals(locIndex, locs.size(), "Total number of GenomeLocs"); +// } +// +// @Test +// public void testScatterFixedIntervalsMax() { +// List files = testFiles("sg.", 85, ".intervals"); +// List splits = IntervalUtils.splitFixedIntervals(hg19ReferenceLocs, files.size()); +// IntervalUtils.scatterFixedIntervals(hg19Header, hg19ReferenceLocs, splits, files); +// +// for (int i = 0; i < files.size(); i++) { +// String file = files.get(i).toString(); +// List parsedLocs = IntervalUtils.parseIntervalArguments(hg19GenomeLocParser, Arrays.asList(file), false); +// Assert.assertEquals(parsedLocs.size(), 1, "parsedLocs[" + i + "].size()"); +// Assert.assertEquals(parsedLocs.get(0), hg19ReferenceLocs.get(i), "parsedLocs[" + i + "].get()"); +// } +// } +// +// @Test +// public void testScatterContigIntervalsOrder() { +// List intervals = Arrays.asList("chr2:1-1", "chr1:1-1", "chr3:2-2"); +// GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1:1-1"); +// GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2:1-1"); +// GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3:2-2"); +// +// List files = testFiles("split.", 3, ".intervals"); +// +// IntervalUtils.scatterContigIntervals(hg18Header, getLocs(intervals), files); +// +// List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); +// List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); +// List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); +// +// Assert.assertEquals(locs1.size(), 1); +// Assert.assertEquals(locs2.size(), 1); +// Assert.assertEquals(locs3.size(), 1); +// +// Assert.assertEquals(locs1.get(0), chr2); +// Assert.assertEquals(locs2.get(0), chr1); +// Assert.assertEquals(locs3.get(0), chr3); +// } +// +// @Test +// public void testScatterContigIntervalsBasic() { +// GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1"); +// GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2"); +// GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3"); +// +// List files = testFiles("contig_basic.", 3, ".intervals"); +// +// IntervalUtils.scatterContigIntervals(hg18Header, getLocs("chr1", "chr2", "chr3"), files); +// +// List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); +// List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); +// List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); +// +// Assert.assertEquals(locs1.size(), 1); +// Assert.assertEquals(locs2.size(), 1); +// Assert.assertEquals(locs3.size(), 1); +// +// Assert.assertEquals(locs1.get(0), chr1); +// Assert.assertEquals(locs2.get(0), chr2); +// Assert.assertEquals(locs3.get(0), chr3); +// } +// +// @Test +// public void testScatterContigIntervalsLessFiles() { +// GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1"); +// GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2"); +// GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3"); +// GenomeLoc chr4 = hg18GenomeLocParser.parseGenomeLoc("chr4"); +// +// List files = testFiles("contig_less.", 3, ".intervals"); +// +// IntervalUtils.scatterContigIntervals(hg18Header, getLocs("chr1", "chr2", "chr3", "chr4"), files); +// +// List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); +// List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); +// List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); +// +// Assert.assertEquals(locs1.size(), 1); +// Assert.assertEquals(locs2.size(), 1); +// Assert.assertEquals(locs3.size(), 2); +// +// Assert.assertEquals(locs1.get(0), chr1); +// Assert.assertEquals(locs2.get(0), chr2); +// Assert.assertEquals(locs3.get(0), chr3); +// Assert.assertEquals(locs3.get(1), chr4); +// } +// +// @Test(expectedExceptions=UserException.BadArgumentValue.class) +// public void testScatterContigIntervalsMoreFiles() { +// List files = testFiles("contig_more.", 3, ".intervals"); +// IntervalUtils.scatterContigIntervals(hg18Header, getLocs("chr1", "chr2"), files); +// } +// +// @Test +// public void testScatterContigIntervalsStart() { +// List intervals = Arrays.asList("chr1:1-2", "chr1:4-5", "chr2:1-1", "chr3:2-2"); +// GenomeLoc chr1a = hg18GenomeLocParser.parseGenomeLoc("chr1:1-2"); +// GenomeLoc chr1b = hg18GenomeLocParser.parseGenomeLoc("chr1:4-5"); +// GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2:1-1"); +// GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3:2-2"); +// +// List files = testFiles("contig_split_start.", 3, ".intervals"); +// +// IntervalUtils.scatterContigIntervals(hg18Header, getLocs(intervals), files); +// +// List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); +// List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); +// List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); +// +// Assert.assertEquals(locs1.size(), 2); +// Assert.assertEquals(locs2.size(), 1); +// Assert.assertEquals(locs3.size(), 1); +// +// Assert.assertEquals(locs1.get(0), chr1a); +// Assert.assertEquals(locs1.get(1), chr1b); +// Assert.assertEquals(locs2.get(0), chr2); +// Assert.assertEquals(locs3.get(0), chr3); +// } +// +// @Test +// public void testScatterContigIntervalsMiddle() { +// List intervals = Arrays.asList("chr1:1-1", "chr2:1-2", "chr2:4-5", "chr3:2-2"); +// GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1:1-1"); +// GenomeLoc chr2a = hg18GenomeLocParser.parseGenomeLoc("chr2:1-2"); +// GenomeLoc chr2b = hg18GenomeLocParser.parseGenomeLoc("chr2:4-5"); +// GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3:2-2"); +// +// List files = testFiles("contig_split_middle.", 3, ".intervals"); +// +// IntervalUtils.scatterContigIntervals(hg18Header, getLocs(intervals), files); +// +// List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); +// List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); +// List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); +// +// Assert.assertEquals(locs1.size(), 1); +// Assert.assertEquals(locs2.size(), 2); +// Assert.assertEquals(locs3.size(), 1); +// +// Assert.assertEquals(locs1.get(0), chr1); +// Assert.assertEquals(locs2.get(0), chr2a); +// Assert.assertEquals(locs2.get(1), chr2b); +// Assert.assertEquals(locs3.get(0), chr3); +// } +// +// @Test +// public void testScatterContigIntervalsEnd() { +// List intervals = Arrays.asList("chr1:1-1", "chr2:2-2", "chr3:1-2", "chr3:4-5"); +// GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1:1-1"); +// GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2:2-2"); +// GenomeLoc chr3a = hg18GenomeLocParser.parseGenomeLoc("chr3:1-2"); +// GenomeLoc chr3b = hg18GenomeLocParser.parseGenomeLoc("chr3:4-5"); +// +// List files = testFiles("contig_split_end.", 3 ,".intervals"); +// +// IntervalUtils.scatterContigIntervals(hg18Header, getLocs(intervals), files); +// +// List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false); +// List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false); +// List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false); +// +// Assert.assertEquals(locs1.size(), 1); +// Assert.assertEquals(locs2.size(), 1); +// Assert.assertEquals(locs3.size(), 2); +// +// Assert.assertEquals(locs1.get(0), chr1); +// Assert.assertEquals(locs2.get(0), chr2); +// Assert.assertEquals(locs3.get(0), chr3a); +// Assert.assertEquals(locs3.get(1), chr3b); +// } +// +// @Test +// public void testScatterContigIntervalsMax() { +// List files = testFiles("sg.", 85, ".intervals"); +// IntervalUtils.scatterContigIntervals(hg19Header, hg19ReferenceLocs, files); +// +// for (int i = 0; i < files.size(); i++) { +// String file = files.get(i).toString(); +// List parsedLocs = IntervalUtils.parseIntervalArguments(hg19GenomeLocParser, Arrays.asList(file), false); +// Assert.assertEquals(parsedLocs.size(), 1, "parsedLocs[" + i + "].size()"); +// Assert.assertEquals(parsedLocs.get(0), hg19ReferenceLocs.get(i), "parsedLocs[" + i + "].get()"); +// } +// } +// +// private List testFiles(String prefix, int count, String suffix) { +// ArrayList files = new ArrayList(); +// for (int i = 1; i <= count; i++) { +// files.add(createTempFile(prefix + i, suffix)); +// } +// return files; +// } +// +// @DataProvider(name="unmergedIntervals") +// public Object[][] getUnmergedIntervals() { +// return new Object[][] { +// new Object[] {"small_unmerged_picard_intervals.list"}, +// new Object[] {"small_unmerged_gatk_intervals.list"} +// }; +// } +// +// @Test(dataProvider="unmergedIntervals") +// public void testUnmergedIntervals(String unmergedIntervals) { +// List locs = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Collections.singletonList(validationDataLocation + unmergedIntervals), false); +// Assert.assertEquals(locs.size(), 2); +// +// List merged = IntervalUtils.mergeIntervalLocations(locs, IntervalMergingRule.ALL); +// Assert.assertEquals(merged.size(), 1); +// } } diff --git a/public/scala/src/org/broadinstitute/sting/queue/extensions/gatk/GATKIntervals.scala b/public/scala/src/org/broadinstitute/sting/queue/extensions/gatk/GATKIntervals.scala index aae5e438c..0fb997f43 100755 --- a/public/scala/src/org/broadinstitute/sting/queue/extensions/gatk/GATKIntervals.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/extensions/gatk/GATKIntervals.scala @@ -1,65 +1,65 @@ -/* - * Copyright (c) 2011, The Broad Institute - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -package org.broadinstitute.sting.queue.extensions.gatk - -import java.io.File -import collection.JavaConversions._ -import org.broadinstitute.sting.utils.interval.IntervalUtils -import org.broadinstitute.sting.gatk.datasources.reference.ReferenceDataSource -import net.sf.samtools.SAMFileHeader -import java.util.Collections -import org.broadinstitute.sting.utils.{GenomeLoc, GenomeLocSortedSet, GenomeLocParser} - -case class GATKIntervals(reference: File, intervals: List[String]) { - private lazy val referenceDataSource = new ReferenceDataSource(reference) - private var splitsBySize = Map.empty[Int, java.util.List[java.lang.Integer]] - - lazy val samFileHeader = { - val header = new SAMFileHeader - header.setSequenceDictionary(referenceDataSource.getReference.getSequenceDictionary) - header - } - - lazy val locs: java.util.List[GenomeLoc] = { - val parser = new GenomeLocParser(referenceDataSource.getReference) - val parsedLocs = - if (intervals.isEmpty) - GenomeLocSortedSet.createSetFromSequenceDictionary(samFileHeader.getSequenceDictionary).toList - else - IntervalUtils.parseIntervalArguments(parser, intervals, false) - Collections.sort(parsedLocs) - Collections.unmodifiableList(parsedLocs) - } - - lazy val contigs = locs.map(_.getContig).distinct.toList - - def getSplits(size: Int) = { - splitsBySize.getOrElse(size, { - val splits: java.util.List[java.lang.Integer] = IntervalUtils.splitFixedIntervals(locs, size) - splitsBySize += size -> splits - splits - }) - } -} +/* + * Copyright (c) 2011, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.queue.extensions.gatk + +import java.io.File +import collection.JavaConversions._ +import org.broadinstitute.sting.utils.interval.IntervalUtils +import org.broadinstitute.sting.gatk.datasources.reference.ReferenceDataSource +import net.sf.samtools.SAMFileHeader +import java.util.Collections +import org.broadinstitute.sting.utils.{GenomeLoc, GenomeLocSortedSet, GenomeLocParser} + +case class GATKIntervals(reference: File, intervals: List[String]) { + private lazy val referenceDataSource = new ReferenceDataSource(reference) +// private var splitsBySize = Map.empty[Int, java.util.List[java.lang.Integer]] + + lazy val samFileHeader = { + val header = new SAMFileHeader + header.setSequenceDictionary(referenceDataSource.getReference.getSequenceDictionary) + header + } + + lazy val locs: java.util.List[GenomeLoc] = { + val parser = new GenomeLocParser(referenceDataSource.getReference) + val parsedLocs = + if (intervals.isEmpty) + GenomeLocSortedSet.createSetFromSequenceDictionary(samFileHeader.getSequenceDictionary).toList + else + IntervalUtils.parseIntervalArguments(parser, intervals, false) + Collections.sort(parsedLocs) + Collections.unmodifiableList(parsedLocs) + } + + lazy val contigs = locs.map(_.getContig).distinct.toList + +// def getSplits(size: Int) = { +// splitsBySize.getOrElse(size, { +// val splits: java.util.List[java.lang.Integer] = IntervalUtils.splitFixedIntervals(locs, size) +// splitsBySize += size -> splits +// splits +// }) +// } +} diff --git a/public/scala/src/org/broadinstitute/sting/queue/extensions/gatk/IntervalScatterFunction.scala b/public/scala/src/org/broadinstitute/sting/queue/extensions/gatk/IntervalScatterFunction.scala index d88d272b9..f65d5ab29 100644 --- a/public/scala/src/org/broadinstitute/sting/queue/extensions/gatk/IntervalScatterFunction.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/extensions/gatk/IntervalScatterFunction.scala @@ -37,7 +37,7 @@ class IntervalScatterFunction extends GATKScatterFunction with InProcessFunction def run() { val gi = GATKScatterFunction.getGATKIntervals(this.referenceSequence, this.intervals) - IntervalUtils.scatterFixedIntervals(gi.samFileHeader, gi.locs, - gi.getSplits(this.scatterOutputFiles.size), this.scatterOutputFiles) + val splits = IntervalUtils.splitFixedIntervals(gi.locs, this.scatterOutputFiles.size) + IntervalUtils.scatterFixedIntervals(gi.samFileHeader, splits, this.scatterOutputFiles) } } diff --git a/public/scala/test/org/broadinstitute/sting/queue/extensions/gatk/GATKIntervalsUnitTest.scala b/public/scala/test/org/broadinstitute/sting/queue/extensions/gatk/GATKIntervalsUnitTest.scala index b3a2d23ae..38abe24ef 100644 --- a/public/scala/test/org/broadinstitute/sting/queue/extensions/gatk/GATKIntervalsUnitTest.scala +++ b/public/scala/test/org/broadinstitute/sting/queue/extensions/gatk/GATKIntervalsUnitTest.scala @@ -53,8 +53,8 @@ class GATKIntervalsUnitTest { val gi = new GATKIntervals(hg18Reference, List("chr1:1-1", "chr2:2-3", "chr3:3-5")) Assert.assertEquals(gi.locs.toList, List(chr1, chr2, chr3)) Assert.assertEquals(gi.contigs, List("chr1", "chr2", "chr3")) - Assert.assertEquals(gi.getSplits(2).toList, List(2, 3)) - Assert.assertEquals(gi.getSplits(3).toList, List(1, 2, 3)) +// Assert.assertEquals(gi.getSplits(2).toList, List(2, 3)) +// Assert.assertEquals(gi.getSplits(3).toList, List(1, 2, 3)) } @Test(timeOut = 30000) @@ -65,7 +65,7 @@ class GATKIntervalsUnitTest { // for(Item item: javaConvertedScalaList) // This for loop is actually an O(N^2) operation as the iterator calls the // O(N) javaConvertedScalaList.size() for each iteration of the loop. - Assert.assertEquals(gi.getSplits(gi.locs.size).size, 189894) + //Assert.assertEquals(gi.getSplits(gi.locs.size).size, 189894) Assert.assertEquals(gi.contigs.size, 24) } @@ -74,8 +74,8 @@ class GATKIntervalsUnitTest { val gi = new GATKIntervals(hg18Reference, Nil) Assert.assertEquals(gi.locs, hg18ReferenceLocs) Assert.assertEquals(gi.contigs.size, hg18ReferenceLocs.size) - Assert.assertEquals(gi.getSplits(2).toList, List(10, 45)) - Assert.assertEquals(gi.getSplits(4).toList, List(5, 10, 16, 45)) +// Assert.assertEquals(gi.getSplits(2).toList, List(10, 45)) +// Assert.assertEquals(gi.getSplits(4).toList, List(5, 10, 16, 45)) } @Test From 91c949db74c3bc67e02f7bc7ef99d062ed3a0c53 Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Fri, 9 Sep 2011 12:57:14 -0400 Subject: [PATCH 038/196] Fixing ValidateVariants so that it validates deletion records. Fixing GATKdocs. --- .../gatk/walkers/variantutils/ValidateVariants.java | 5 ----- .../sting/utils/variantcontext/VariantContext.java | 11 ++++++----- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/ValidateVariants.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/ValidateVariants.java index 2c7902914..fdfca982c 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/ValidateVariants.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/ValidateVariants.java @@ -56,11 +56,6 @@ import java.util.Set; * A variant set to filter. *

* - *

Output

- *

- * A filtered VCF. - *

- * *

Examples

*
  * java -Xmx2g -jar GenomeAnalysisTK.jar \
diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java
index 699133e38..1c65102ae 100755
--- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java
+++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java
@@ -1085,14 +1085,15 @@ public class VariantContext implements Feature { // to enable tribble intergrati
     }
 
     public void validateReferenceBases(Allele reference, Byte paddedRefBase) {
-        // don't validate if we're an insertion or complex event
-        if ( !reference.isNull() && getReference().length() == 1 && !reference.basesMatch(getReference()) ) {
-            throw new TribbleException.InternalCodecException(String.format("the REF allele is incorrect for the record at position %s:%d, %s vs. %s", getChr(), getStart(), reference.getBaseString(), getReference().getBaseString()));
+        // don't validate if we're a complex event
+        if ( !isComplexIndel() && !reference.isNull() && !reference.basesMatch(getReference()) ) {
+            throw new TribbleException.InternalCodecException(String.format("the REF allele is incorrect for the record at position %s:%d, fasta says %s vs. VCF says %s", getChr(), getStart(), reference.getBaseString(), getReference().getBaseString()));
         }
 
         // we also need to validate the padding base for simple indels
-        if ( hasReferenceBaseForIndel() && !getReferenceBaseForIndel().equals(paddedRefBase) )
-            throw new TribbleException.InternalCodecException(String.format("the padded REF base is incorrect for the record at position %s:%d, %s vs. %s", getChr(), getStart(), (char)getReferenceBaseForIndel().byteValue(), (char)paddedRefBase.byteValue()));
+        if ( hasReferenceBaseForIndel() && !getReferenceBaseForIndel().equals(paddedRefBase) ) {
+            throw new TribbleException.InternalCodecException(String.format("the padded REF base is incorrect for the record at position %s:%d, fasta says %s vs. VCF says %s", getChr(), getStart(), (char)paddedRefBase.byteValue(), (char)getReferenceBaseForIndel().byteValue()));
+        }
     }
 
     public void validateRSIDs(Set rsIDs) {

From 354529bff37626c5f46de89a60987d3d3fd40aec Mon Sep 17 00:00:00 2001
From: Ryan Poplin 
Date: Fri, 9 Sep 2011 13:15:24 -0400
Subject: [PATCH 039/196] adding Validate Variants integration test with a
 deletion

---
 .../ValidateVariantsIntegrationTest.java             | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/ValidateVariantsIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/ValidateVariantsIntegrationTest.java
index adf3b21a8..3d41be1ae 100755
--- a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/ValidateVariantsIntegrationTest.java
+++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/ValidateVariantsIntegrationTest.java
@@ -113,4 +113,16 @@ public class ValidateVariantsIntegrationTest extends WalkerTest {
 
         executeTest("test bad alt allele", spec);
     }
+
+    @Test
+    public void testBadAllele2() {
+        WalkerTestSpec spec = new WalkerTestSpec(
+            baseTestString("validationExampleBad3.vcf", "ALLELES"),
+            0,
+            UserException.MalformedFile.class
+        );
+
+        executeTest("test bad alt allele", spec);
+    }
+
 }

From 1953edcd2d1a2292117cb07ff4bb0e48e2605f0e Mon Sep 17 00:00:00 2001
From: Ryan Poplin 
Date: Fri, 9 Sep 2011 13:39:08 -0400
Subject: [PATCH 040/196] updating Validate Variants deletion integration test

---
 .../walkers/variantutils/ValidateVariantsIntegrationTest.java | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/ValidateVariantsIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/ValidateVariantsIntegrationTest.java
index 3d41be1ae..5f71f82fd 100755
--- a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/ValidateVariantsIntegrationTest.java
+++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/ValidateVariantsIntegrationTest.java
@@ -117,12 +117,12 @@ public class ValidateVariantsIntegrationTest extends WalkerTest {
     @Test
     public void testBadAllele2() {
         WalkerTestSpec spec = new WalkerTestSpec(
-            baseTestString("validationExampleBad3.vcf", "ALLELES"),
+            baseTestString("validationExampleBad3.vcf", "REF"),
             0,
             UserException.MalformedFile.class
         );
 
-        executeTest("test bad alt allele", spec);
+        executeTest("test bad ref allele in deletion", spec);
     }
 
 }

From 7f9000382e4d9ef30e25739401b72e7d1a7c2fcc Mon Sep 17 00:00:00 2001
From: Mauricio Carneiro 
Date: Fri, 9 Sep 2011 14:09:11 -0400
Subject: [PATCH 041/196] Making indel calls default in the MDCP

You can turn off indel calling by using -noIndels.
---
 .../queue/qscripts/MethodsDevelopmentCallingPipeline.scala  | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala
index 80bfe03d1..17d614290 100755
--- a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala
+++ b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala
@@ -22,8 +22,8 @@ class MethodsDevelopmentCallingPipeline extends QScript {
   @Argument(shortName="noBAQ", doc="turns off BAQ calculation", required=false)
   var noBAQ: Boolean = false
 
-  @Argument(shortName="indels", doc="calls indels with the Unified Genotyper", required=false)
-  var callIndels: Boolean = false
+  @Argument(shortName="noIndels", doc="do not call indels with the Unified Genotyper", required=false)
+  var noIndels: Boolean = false
 
   @Argument(shortName="LOCAL_ET", doc="Doesn't use the AWS S3 storage for ET option", required=false)
   var LOCAL_ET: Boolean = false
@@ -165,7 +165,7 @@ class MethodsDevelopmentCallingPipeline extends QScript {
     val goldStandard = true
     for (target <- targets) {
       if( !skipCalling ) {
-        if (callIndels) add(new indelCall(target), new indelFilter(target), new indelEvaluation(target))
+        if (!noIndels) add(new indelCall(target), new indelFilter(target), new indelEvaluation(target))
         add(new snpCall(target))
         add(new VQSR(target, !goldStandard))
         add(new applyVQSR(target, !goldStandard))

From 87dc5cfb24a8065a07f0fdec3d09a0943210e4ae Mon Sep 17 00:00:00 2001
From: Mark DePristo 
Date: Fri, 9 Sep 2011 14:23:13 -0400
Subject: [PATCH 042/196] Whitespace cleanup

---
 public/java/src/org/broadinstitute/sting/utils/GenomeLoc.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/public/java/src/org/broadinstitute/sting/utils/GenomeLoc.java b/public/java/src/org/broadinstitute/sting/utils/GenomeLoc.java
index b96923589..b66198713 100644
--- a/public/java/src/org/broadinstitute/sting/utils/GenomeLoc.java
+++ b/public/java/src/org/broadinstitute/sting/utils/GenomeLoc.java
@@ -306,7 +306,7 @@ public class GenomeLoc implements Comparable, Serializable, HasGenome
     
     @Override
     public int hashCode() {
-        return (int)( start << 16 + stop << 4 + contigIndex );
+        return start << 16 | stop << 4 | contigIndex;
     }
 
 

From c6436ee5f0f3359912e8210f99828a33680c745c Mon Sep 17 00:00:00 2001
From: Mark DePristo 
Date: Fri, 9 Sep 2011 14:24:29 -0400
Subject: [PATCH 043/196] Whitespace cleanup

---
 public/java/src/org/broadinstitute/sting/utils/GenomeLoc.java | 1 +
 1 file changed, 1 insertion(+)

diff --git a/public/java/src/org/broadinstitute/sting/utils/GenomeLoc.java b/public/java/src/org/broadinstitute/sting/utils/GenomeLoc.java
index b66198713..ba4919175 100644
--- a/public/java/src/org/broadinstitute/sting/utils/GenomeLoc.java
+++ b/public/java/src/org/broadinstitute/sting/utils/GenomeLoc.java
@@ -307,6 +307,7 @@ public class GenomeLoc implements Comparable, Serializable, HasGenome
     @Override
     public int hashCode() {
         return start << 16 | stop << 4 | contigIndex;
+
     }
 
 

From 3c8445b934c127581919d6be960ebc372be21342 Mon Sep 17 00:00:00 2001
From: Mark DePristo 
Date: Fri, 9 Sep 2011 14:25:37 -0400
Subject: [PATCH 044/196] Performance bugfix for GenomeLoc.hashcode

-- old version overflowed so most GenomeLocs had 0 hashcode.  Now uses or not plus to combine
---
 public/java/src/org/broadinstitute/sting/utils/GenomeLoc.java | 1 -
 1 file changed, 1 deletion(-)

diff --git a/public/java/src/org/broadinstitute/sting/utils/GenomeLoc.java b/public/java/src/org/broadinstitute/sting/utils/GenomeLoc.java
index ba4919175..b66198713 100644
--- a/public/java/src/org/broadinstitute/sting/utils/GenomeLoc.java
+++ b/public/java/src/org/broadinstitute/sting/utils/GenomeLoc.java
@@ -307,7 +307,6 @@ public class GenomeLoc implements Comparable, Serializable, HasGenome
     @Override
     public int hashCode() {
         return start << 16 | stop << 4 | contigIndex;
-
     }
 
 

From 72536e5d6db56f495d560f1bcf2536c6896a49c0 Mon Sep 17 00:00:00 2001
From: Mark DePristo 
Date: Fri, 9 Sep 2011 15:44:47 -0400
Subject: [PATCH 045/196] Done

---
 build.xml                                     |    4 +-
 .../sting/utils/interval/IntervalUtils.java   |   70 +-
 .../utils/interval/IntervalUtilsUnitTest.java | 1000 +++++++++--------
 3 files changed, 522 insertions(+), 552 deletions(-)

diff --git a/build.xml b/build.xml
index beca6bce0..efefdd438 100644
--- a/build.xml
+++ b/build.xml
@@ -855,8 +855,8 @@
                 
                 
                 
-
-
+                
+                
                 
                     
                     
diff --git a/public/java/src/org/broadinstitute/sting/utils/interval/IntervalUtils.java b/public/java/src/org/broadinstitute/sting/utils/interval/IntervalUtils.java
index 41cbbe59f..2cfcc19a9 100644
--- a/public/java/src/org/broadinstitute/sting/utils/interval/IntervalUtils.java
+++ b/public/java/src/org/broadinstitute/sting/utils/interval/IntervalUtils.java
@@ -333,28 +333,6 @@ public class IntervalUtils {
             throw new UserException.BadArgumentValue("scatterParts", String.format("Only able to write contigs into %d of %d files.", fileIndex + 1, scatterParts.size()));
     }
 
-    /**
-     * Splits an interval list into multiple sublists.
-     * @param locs The genome locs to split.
-     * @param splits The stop points for the genome locs returned by splitFixedIntervals.
-     * @return A list of lists of genome locs, split according to splits
-     */
-    public static List> splitIntervalsToSubLists(List locs, List splits) {
-        int locIndex = 1;
-        int start = 0;
-        List> sublists = new ArrayList>(splits.size());
-        for (Integer stop: splits) {
-            List curList = new ArrayList();
-            for (int i = start; i < stop; i++)
-                curList.add(locs.get(i));
-            start = stop;
-            sublists.add(curList);
-        }
-
-        return sublists;
-    }
-
-
     /**
      * Splits an interval list into multiple files.
      * @param fileHeader The sam file header.
@@ -384,39 +362,27 @@ public class IntervalUtils {
     public static List> splitFixedIntervals(List locs, int numParts) {
         if (locs.size() < numParts)
             throw new UserException.BadArgumentValue("scatterParts", String.format("Cannot scatter %d locs into %d parts.", locs.size(), numParts));
+
         final long locsSize = intervalSize(locs);
-        final List splitPoints = new ArrayList();
-        addFixedSplit(splitPoints, locs, locsSize, 0, locs.size(), numParts);
-        Collections.sort(splitPoints);
-        splitPoints.add(locs.size());
-        return splitIntervalsToSubLists(locs, splitPoints);
-    }
+        final double idealSplitSize = locsSize / numParts;
+        final List> splits = new ArrayList>(numParts);
+        final LinkedList remainingLocs = new LinkedList(locs);
 
-    private static void addFixedSplit(List splitPoints, List locs, long locsSize, int startIndex, int stopIndex, int numParts) {
-        if (numParts < 2)
-            return;
-        int halfParts = (numParts + 1) / 2;
-        Pair splitPoint = getFixedSplit(locs, locsSize, startIndex, stopIndex, halfParts, numParts - halfParts);
-        int splitIndex = splitPoint.first;
-        long splitSize = splitPoint.second;
-        splitPoints.add(splitIndex);
-        addFixedSplit(splitPoints, locs, splitSize, startIndex, splitIndex, halfParts);
-        addFixedSplit(splitPoints, locs, locsSize - splitSize, splitIndex, stopIndex, numParts - halfParts);
-    }
+        for ( int i = 0; i < numParts; i++ ) {
+            long splitSize = 0;
+            List split = new ArrayList();
+            while ( ! remainingLocs.isEmpty() ) {
+                final GenomeLoc toAdd = remainingLocs.pop();
+                splitSize += toAdd.size();
+                split.add(toAdd);
+                final long nextEltSize = remainingLocs.isEmpty() ? 0 : remainingLocs.peek().size();
+                if ( splitSize + (i % 2 == 0 ? 0 : nextEltSize) > idealSplitSize )
+                    break;
+            }
+            splits.add(split);
+        }
 
-    private static Pair getFixedSplit(List locs, long locsSize, int startIndex, int stopIndex, int minLocs, int maxLocs) {
-        int splitIndex = startIndex;
-        long splitSize = 0;
-        for (int i = 0; i < minLocs; i++) {
-            splitSize += locs.get(splitIndex).size();
-            splitIndex++;
-        }
-        long halfSize = locsSize / 2;
-        while (splitIndex < (stopIndex - maxLocs) && splitSize < halfSize) {
-            splitSize += locs.get(splitIndex).size();
-            splitIndex++;
-        }
-        return new Pair(splitIndex, splitSize);
+        return splits;
     }
 
     /**
diff --git a/public/java/test/org/broadinstitute/sting/utils/interval/IntervalUtilsUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/interval/IntervalUtilsUnitTest.java
index bd6bf9591..4809f1b5c 100644
--- a/public/java/test/org/broadinstitute/sting/utils/interval/IntervalUtilsUnitTest.java
+++ b/public/java/test/org/broadinstitute/sting/utils/interval/IntervalUtilsUnitTest.java
@@ -1,6 +1,7 @@
 package org.broadinstitute.sting.utils.interval;
 
 import net.sf.picard.reference.ReferenceSequenceFile;
+import net.sf.picard.util.IntervalUtil;
 import net.sf.samtools.SAMFileHeader;
 import org.broadinstitute.sting.BaseTest;
 import org.broadinstitute.sting.gatk.datasources.reference.ReferenceDataSource;
@@ -99,19 +100,26 @@ public class IntervalUtilsUnitTest extends BaseTest {
 
     @DataProvider(name = "intervalslicingdata")
     public Object[][] createTrees() {
-//        new IntervalSlicingTest(1, 0);
-//        new IntervalSlicingTest(2, 0.1);
-        new IntervalSlicingTest(5, 0.1);
-//        new IntervalSlicingTest(10, 0.1);
-//        new IntervalSlicingTest(67, 0.1);
-//        new IntervalSlicingTest(100, 0.1);
-//        new IntervalSlicingTest(500, 0.1);
-//        new IntervalSlicingTest(1000, 0.1);
+        new IntervalSlicingTest(1, 0);
+        new IntervalSlicingTest(2, 0.1);
+        new IntervalSlicingTest(3, 0.1);
+        new IntervalSlicingTest(7, 0.1);
+        new IntervalSlicingTest(10, 0.1);
+        new IntervalSlicingTest(31, 0.1);
+        new IntervalSlicingTest(67, 0.1);
+        new IntervalSlicingTest(100, 0.1);
+        new IntervalSlicingTest(127, 0.1);
+        // starts to become a bit less efficiency with larger cuts
+        new IntervalSlicingTest(500, 0.5);
+        new IntervalSlicingTest(1000, 1);
+        new IntervalSlicingTest(10000, 10);
         return IntervalSlicingTest.getTests(IntervalSlicingTest.class);
     }
 
     @Test(dataProvider = "intervalslicingdata")
     public void testFixedScatterIntervalsAlgorithm(IntervalSlicingTest test) {
+        Set locsSet = new HashSet(hg19exomeIntervals);
+        Set notFoundSet = new HashSet(hg19exomeIntervals);
         List> splits = IntervalUtils.splitFixedIntervals(hg19exomeIntervals, test.parts);
 
         long totalSize = IntervalUtils.intervalSize(hg19exomeIntervals);
@@ -122,501 +130,497 @@ public class IntervalUtilsUnitTest extends BaseTest {
         for ( final List split : splits ) {
             long splitSize = IntervalUtils.intervalSize(split);
             double sigma = (splitSize - idealSplitSize) / (1.0 * idealSplitSize);
-            logger.warn(String.format("Split %d size %d ideal %d sigma %.2f", counter, splitSize, idealSplitSize, sigma));
+            //logger.warn(String.format("Split %d size %d ideal %d sigma %.2f", counter, splitSize, idealSplitSize, sigma));
             counter++;
             sumOfSplitSizes += splitSize;
             Assert.assertTrue(Math.abs(sigma) <= test.maxAllowableVariance, String.format("Interval %d (size %d ideal %d) has a variance %.2f outside of the tolerated range %.2f", counter, splitSize, idealSplitSize, sigma, test.maxAllowableVariance));
+
+            for ( final GenomeLoc loc : split ) {
+                Assert.assertTrue(locsSet.contains(loc), "Split location " + loc + " not found in set of input locs");
+                notFoundSet.remove(loc);
+            }
         }
 
-        Assert.assertEquals(totalSize, sumOfSplitSizes, "Split intervals don't contain the exact number of bases in the origianl intervals");
+        Assert.assertEquals(sumOfSplitSizes, totalSize, "Split intervals don't contain the exact number of bases in the original intervals");
+        Assert.assertTrue(notFoundSet.isEmpty(), "Not all intervals were present in the split set");
     }
 
-//    @Test(expectedExceptions=UserException.class)
-//    public void testMergeListsBySetOperatorNoOverlap() {
-//        // a couple of lists we'll use for the testing
-//        List listEveryTwoFromOne = new ArrayList();
-//        List listEveryTwoFromTwo = new ArrayList();
-//
-//        // create the two lists we'll use
-//        for (int x = 1; x < 101; x++) {
-//            if (x % 2 == 0)
-//                listEveryTwoFromTwo.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x));
-//            else
-//                listEveryTwoFromOne.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x));
-//        }
-//
-//        List ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, listEveryTwoFromOne, IntervalSetRule.UNION);
-//        Assert.assertEquals(ret.size(), 100);
-//        ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, listEveryTwoFromOne, IntervalSetRule.INTERSECTION);
-//        Assert.assertEquals(ret.size(), 0);
-//    }
-//
-//    @Test
-//    public void testMergeListsBySetOperatorAllOverlap() {
-//        // a couple of lists we'll use for the testing
-//        List allSites = new ArrayList();
-//        List listEveryTwoFromTwo = new ArrayList();
-//
-//        // create the two lists we'll use
-//        for (int x = 1; x < 101; x++) {
-//            if (x % 2 == 0)
-//                listEveryTwoFromTwo.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x));
-//            allSites.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x));
-//        }
-//
-//        List ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, allSites, IntervalSetRule.UNION);
-//        Assert.assertEquals(ret.size(), 150);
-//        ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, allSites, IntervalSetRule.INTERSECTION);
-//        Assert.assertEquals(ret.size(), 50);
-//    }
-//
-//    @Test
-//    public void testMergeListsBySetOperator() {
-//        // a couple of lists we'll use for the testing
-//        List allSites = new ArrayList();
-//        List listEveryTwoFromTwo = new ArrayList();
-//
-//        // create the two lists we'll use
-//        for (int x = 1; x < 101; x++) {
-//            if (x % 5 == 0) {
-//                listEveryTwoFromTwo.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x));
-//                allSites.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x));
-//            }
-//        }
-//
-//        List ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, allSites, IntervalSetRule.UNION);
-//        Assert.assertEquals(ret.size(), 40);
-//        ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, allSites, IntervalSetRule.INTERSECTION);
-//        Assert.assertEquals(ret.size(), 20);
-//    }
-//
-//    @Test
-//    public void testGetContigLengths() {
-//        Map lengths = IntervalUtils.getContigSizes(new File(BaseTest.hg18Reference));
-//        Assert.assertEquals((long)lengths.get("chr1"), 247249719);
-//        Assert.assertEquals((long)lengths.get("chr2"), 242951149);
-//        Assert.assertEquals((long)lengths.get("chr3"), 199501827);
-//        Assert.assertEquals((long)lengths.get("chr20"), 62435964);
-//        Assert.assertEquals((long)lengths.get("chrX"), 154913754);
-//    }
-//
-//    @Test
-//    public void testParseIntervalArguments() {
-//        Assert.assertEquals(getLocs().size(), 45);
-//        Assert.assertEquals(getLocs("chr1", "chr2", "chr3").size(), 3);
-//        Assert.assertEquals(getLocs("chr1:1-2", "chr1:4-5", "chr2:1-1", "chr3:2-2").size(), 4);
-//    }
-//
-//    @Test
-//    public void testIsIntervalFile() {
-//        Assert.assertTrue(IntervalUtils.isIntervalFile(BaseTest.validationDataLocation + "empty_intervals.list"));
-//        Assert.assertTrue(IntervalUtils.isIntervalFile(BaseTest.validationDataLocation + "empty_intervals.list", true));
-//
-//        List extensions = Arrays.asList("bed", "interval_list", "intervals", "list", "picard");
-//        for (String extension: extensions) {
-//            Assert.assertTrue(IntervalUtils.isIntervalFile("test_intervals." + extension, false), "Tested interval file extension: " + extension);
-//        }
-//    }
-//
-//    @Test(expectedExceptions = UserException.CouldNotReadInputFile.class)
-//    public void testMissingIntervalFile() {
-//        IntervalUtils.isIntervalFile(BaseTest.validationDataLocation + "no_such_intervals.list");
-//    }
-//
-//    @Test
-//    public void testFixedScatterIntervalsBasic() {
-//        GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1");
-//        GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2");
-//        GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3");
-//
-//        List files = testFiles("basic.", 3, ".intervals");
-//
-//        List locs = getLocs("chr1", "chr2", "chr3");
-//        List splits = IntervalUtils.splitFixedIntervals(locs, files.size());
-//        IntervalUtils.scatterFixedIntervals(hg18Header, locs, splits, files);
-//
-//        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
-//        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
-//        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
-//
-//        Assert.assertEquals(locs1.size(), 1);
-//        Assert.assertEquals(locs2.size(), 1);
-//        Assert.assertEquals(locs3.size(), 1);
-//
-//        Assert.assertEquals(locs1.get(0), chr1);
-//        Assert.assertEquals(locs2.get(0), chr2);
-//        Assert.assertEquals(locs3.get(0), chr3);
-//    }
-//
-//    @Test
-//    public void testScatterFixedIntervalsLessFiles() {
-//        GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1");
-//        GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2");
-//        GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3");
-//        GenomeLoc chr4 = hg18GenomeLocParser.parseGenomeLoc("chr4");
-//
-//        List files = testFiles("less.", 3, ".intervals");
-//
-//        List locs = getLocs("chr1", "chr2", "chr3", "chr4");
-//        List splits = IntervalUtils.splitFixedIntervals(locs, files.size());
-//        IntervalUtils.scatterFixedIntervals(hg18Header, locs, splits, files);
-//
-//        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
-//        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
-//        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
-//
-//        Assert.assertEquals(locs1.size(), 1);
-//        Assert.assertEquals(locs2.size(), 1);
-//        Assert.assertEquals(locs3.size(), 2);
-//
-//        Assert.assertEquals(locs1.get(0), chr1);
-//        Assert.assertEquals(locs2.get(0), chr2);
-//        Assert.assertEquals(locs3.get(0), chr3);
-//        Assert.assertEquals(locs3.get(1), chr4);
-//    }
-//
-//    @Test(expectedExceptions=UserException.BadArgumentValue.class)
-//    public void testSplitFixedIntervalsMoreFiles() {
-//        List files = testFiles("more.", 3, ".intervals");
-//        List locs = getLocs("chr1", "chr2");
-//        IntervalUtils.splitFixedIntervals(locs, files.size());
-//    }
-//
-//    @Test(expectedExceptions=UserException.BadArgumentValue.class)
-//    public void testScatterFixedIntervalsMoreFiles() {
-//        List files = testFiles("more.", 3, ".intervals");
-//        List locs = getLocs("chr1", "chr2");
-//        List splits = IntervalUtils.splitFixedIntervals(locs, locs.size()); // locs.size() instead of files.size()
-//        IntervalUtils.scatterFixedIntervals(hg18Header, locs, splits, files);
-//    }
-//    @Test
-//    public void testScatterFixedIntervalsStart() {
-//        List intervals = Arrays.asList("chr1:1-2", "chr1:4-5", "chr2:1-1", "chr3:2-2");
-//        GenomeLoc chr1a = hg18GenomeLocParser.parseGenomeLoc("chr1:1-2");
-//        GenomeLoc chr1b = hg18GenomeLocParser.parseGenomeLoc("chr1:4-5");
-//        GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2:1-1");
-//        GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3:2-2");
-//
-//        List files = testFiles("split.", 3, ".intervals");
-//
-//        List locs = getLocs(intervals);
-//        List splits = IntervalUtils.splitFixedIntervals(locs, files.size());
-//        IntervalUtils.scatterFixedIntervals(hg18Header, locs, splits, files);
-//
-//        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
-//        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
-//        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
-//
-//        Assert.assertEquals(locs1.size(), 1);
-//        Assert.assertEquals(locs2.size(), 1);
-//        Assert.assertEquals(locs3.size(), 2);
-//
-//        Assert.assertEquals(locs1.get(0), chr1a);
-//        Assert.assertEquals(locs2.get(0), chr1b);
-//        Assert.assertEquals(locs3.get(0), chr2);
-//        Assert.assertEquals(locs3.get(1), chr3);
-//    }
-//
-//    @Test
-//    public void testScatterFixedIntervalsMiddle() {
-//        List intervals = Arrays.asList("chr1:1-1", "chr2:1-2", "chr2:4-5", "chr3:2-2");
-//        GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1:1-1");
-//        GenomeLoc chr2a = hg18GenomeLocParser.parseGenomeLoc("chr2:1-2");
-//        GenomeLoc chr2b = hg18GenomeLocParser.parseGenomeLoc("chr2:4-5");
-//        GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3:2-2");
-//
-//        List files = testFiles("split.", 3, ".intervals");
-//
-//        List locs = getLocs(intervals);
-//        List splits = IntervalUtils.splitFixedIntervals(locs, files.size());
-//        IntervalUtils.scatterFixedIntervals(hg18Header, locs, splits, files);
-//
-//        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
-//        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
-//        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
-//
-//        Assert.assertEquals(locs1.size(), 1);
-//        Assert.assertEquals(locs2.size(), 1);
-//        Assert.assertEquals(locs3.size(), 2);
-//
-//        Assert.assertEquals(locs1.get(0), chr1);
-//        Assert.assertEquals(locs2.get(0), chr2a);
-//        Assert.assertEquals(locs3.get(0), chr2b);
-//        Assert.assertEquals(locs3.get(1), chr3);
-//    }
-//
-//    @Test
-//    public void testScatterFixedIntervalsEnd() {
-//        List intervals = Arrays.asList("chr1:1-1", "chr2:2-2", "chr3:1-2", "chr3:4-5");
-//        GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1:1-1");
-//        GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2:2-2");
-//        GenomeLoc chr3a = hg18GenomeLocParser.parseGenomeLoc("chr3:1-2");
-//        GenomeLoc chr3b = hg18GenomeLocParser.parseGenomeLoc("chr3:4-5");
-//
-//        List files = testFiles("split.", 3, ".intervals");
-//
-//        List locs = getLocs(intervals);
-//        List splits = IntervalUtils.splitFixedIntervals(locs, files.size());
-//        IntervalUtils.scatterFixedIntervals(hg18Header, locs, splits, files);
-//
-//        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
-//        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
-//        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
-//
-//        Assert.assertEquals(locs1.size(), 2);
-//        Assert.assertEquals(locs2.size(), 1);
-//        Assert.assertEquals(locs3.size(), 1);
-//
-//        Assert.assertEquals(locs1.get(0), chr1);
-//        Assert.assertEquals(locs1.get(1), chr2);
-//        Assert.assertEquals(locs2.get(0), chr3a);
-//        Assert.assertEquals(locs3.get(0), chr3b);
-//    }
-//
-//    @Test
-//    public void testScatterFixedIntervalsFile() {
-//        List files = testFiles("sg.", 20, ".intervals");
-//        List locs = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(BaseTest.GATKDataLocation + "whole_exome_agilent_designed_120.targets.hg18.chr20.interval_list"), false);
-//        List splits = IntervalUtils.splitFixedIntervals(locs, files.size());
-//
-//        int[] counts = {
-//                125, 138, 287, 291, 312, 105, 155, 324,
-//                295, 298, 141, 121, 285, 302, 282, 88,
-//                116, 274, 282, 248
-////                5169, 5573, 10017, 10567, 10551,
-////                5087, 4908, 10120, 10435, 10399,
-////                5391, 4735, 10621, 10352, 10654,
-////                5227, 5256, 10151, 9649, 9825
-//        };
-//
-//        //String splitCounts = "";
-//        for (int lastIndex = 0, i = 0; i < splits.size(); i++) {
-//            int splitIndex = splits.get(i);
-//            int splitCount = (splitIndex - lastIndex);
-//            //splitCounts += ", " + splitCount;
-//            lastIndex = splitIndex;
-//            Assert.assertEquals(splitCount, counts[i], "Num intervals in split " + i);
-//        }
-//        //System.out.println(splitCounts.substring(2));
-//
-//        IntervalUtils.scatterFixedIntervals(hg18Header, locs, splits, files);
-//
-//        int locIndex = 0;
-//        for (int i = 0; i < files.size(); i++) {
-//            String file = files.get(i).toString();
-//            List parsedLocs = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(file), false);
-//            Assert.assertEquals(parsedLocs.size(), counts[i], "Intervals in " + file);
-//            for (GenomeLoc parsedLoc: parsedLocs)
-//                Assert.assertEquals(parsedLoc, locs.get(locIndex), String.format("Genome loc %d from file %d", locIndex++, i));
-//        }
-//        Assert.assertEquals(locIndex, locs.size(), "Total number of GenomeLocs");
-//    }
-//
-//    @Test
-//    public void testScatterFixedIntervalsMax() {
-//        List files = testFiles("sg.", 85, ".intervals");
-//        List splits = IntervalUtils.splitFixedIntervals(hg19ReferenceLocs, files.size());
-//        IntervalUtils.scatterFixedIntervals(hg19Header, hg19ReferenceLocs, splits, files);
-//
-//        for (int i = 0; i < files.size(); i++) {
-//            String file = files.get(i).toString();
-//            List parsedLocs = IntervalUtils.parseIntervalArguments(hg19GenomeLocParser, Arrays.asList(file), false);
-//            Assert.assertEquals(parsedLocs.size(), 1, "parsedLocs[" + i + "].size()");
-//            Assert.assertEquals(parsedLocs.get(0), hg19ReferenceLocs.get(i), "parsedLocs[" + i + "].get()");
-//        }
-//    }
-//
-//    @Test
-//    public void testScatterContigIntervalsOrder() {
-//        List intervals = Arrays.asList("chr2:1-1", "chr1:1-1", "chr3:2-2");
-//        GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1:1-1");
-//        GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2:1-1");
-//        GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3:2-2");
-//
-//        List files = testFiles("split.", 3, ".intervals");
-//
-//        IntervalUtils.scatterContigIntervals(hg18Header, getLocs(intervals), files);
-//
-//        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
-//        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
-//        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
-//
-//        Assert.assertEquals(locs1.size(), 1);
-//        Assert.assertEquals(locs2.size(), 1);
-//        Assert.assertEquals(locs3.size(), 1);
-//
-//        Assert.assertEquals(locs1.get(0), chr2);
-//        Assert.assertEquals(locs2.get(0), chr1);
-//        Assert.assertEquals(locs3.get(0), chr3);
-//    }
-//
-//    @Test
-//    public void testScatterContigIntervalsBasic() {
-//        GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1");
-//        GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2");
-//        GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3");
-//
-//        List files = testFiles("contig_basic.", 3, ".intervals");
-//
-//        IntervalUtils.scatterContigIntervals(hg18Header, getLocs("chr1", "chr2", "chr3"), files);
-//
-//        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
-//        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
-//        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
-//
-//        Assert.assertEquals(locs1.size(), 1);
-//        Assert.assertEquals(locs2.size(), 1);
-//        Assert.assertEquals(locs3.size(), 1);
-//
-//        Assert.assertEquals(locs1.get(0), chr1);
-//        Assert.assertEquals(locs2.get(0), chr2);
-//        Assert.assertEquals(locs3.get(0), chr3);
-//    }
-//
-//    @Test
-//    public void testScatterContigIntervalsLessFiles() {
-//        GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1");
-//        GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2");
-//        GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3");
-//        GenomeLoc chr4 = hg18GenomeLocParser.parseGenomeLoc("chr4");
-//
-//        List files = testFiles("contig_less.", 3, ".intervals");
-//
-//        IntervalUtils.scatterContigIntervals(hg18Header, getLocs("chr1", "chr2", "chr3", "chr4"), files);
-//
-//        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
-//        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
-//        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
-//
-//        Assert.assertEquals(locs1.size(), 1);
-//        Assert.assertEquals(locs2.size(), 1);
-//        Assert.assertEquals(locs3.size(), 2);
-//
-//        Assert.assertEquals(locs1.get(0), chr1);
-//        Assert.assertEquals(locs2.get(0), chr2);
-//        Assert.assertEquals(locs3.get(0), chr3);
-//        Assert.assertEquals(locs3.get(1), chr4);
-//    }
-//
-//    @Test(expectedExceptions=UserException.BadArgumentValue.class)
-//    public void testScatterContigIntervalsMoreFiles() {
-//        List files = testFiles("contig_more.", 3, ".intervals");
-//        IntervalUtils.scatterContigIntervals(hg18Header, getLocs("chr1", "chr2"), files);
-//    }
-//
-//    @Test
-//    public void testScatterContigIntervalsStart() {
-//        List intervals = Arrays.asList("chr1:1-2", "chr1:4-5", "chr2:1-1", "chr3:2-2");
-//        GenomeLoc chr1a = hg18GenomeLocParser.parseGenomeLoc("chr1:1-2");
-//        GenomeLoc chr1b = hg18GenomeLocParser.parseGenomeLoc("chr1:4-5");
-//        GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2:1-1");
-//        GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3:2-2");
-//
-//        List files = testFiles("contig_split_start.", 3, ".intervals");
-//
-//        IntervalUtils.scatterContigIntervals(hg18Header, getLocs(intervals), files);
-//
-//        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
-//        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
-//        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
-//
-//        Assert.assertEquals(locs1.size(), 2);
-//        Assert.assertEquals(locs2.size(), 1);
-//        Assert.assertEquals(locs3.size(), 1);
-//
-//        Assert.assertEquals(locs1.get(0), chr1a);
-//        Assert.assertEquals(locs1.get(1), chr1b);
-//        Assert.assertEquals(locs2.get(0), chr2);
-//        Assert.assertEquals(locs3.get(0), chr3);
-//    }
-//
-//    @Test
-//    public void testScatterContigIntervalsMiddle() {
-//        List intervals = Arrays.asList("chr1:1-1", "chr2:1-2", "chr2:4-5", "chr3:2-2");
-//        GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1:1-1");
-//        GenomeLoc chr2a = hg18GenomeLocParser.parseGenomeLoc("chr2:1-2");
-//        GenomeLoc chr2b = hg18GenomeLocParser.parseGenomeLoc("chr2:4-5");
-//        GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3:2-2");
-//
-//        List files = testFiles("contig_split_middle.", 3, ".intervals");
-//
-//        IntervalUtils.scatterContigIntervals(hg18Header, getLocs(intervals), files);
-//
-//        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
-//        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
-//        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
-//
-//        Assert.assertEquals(locs1.size(), 1);
-//        Assert.assertEquals(locs2.size(), 2);
-//        Assert.assertEquals(locs3.size(), 1);
-//
-//        Assert.assertEquals(locs1.get(0), chr1);
-//        Assert.assertEquals(locs2.get(0), chr2a);
-//        Assert.assertEquals(locs2.get(1), chr2b);
-//        Assert.assertEquals(locs3.get(0), chr3);
-//    }
-//
-//    @Test
-//    public void testScatterContigIntervalsEnd() {
-//        List intervals = Arrays.asList("chr1:1-1", "chr2:2-2", "chr3:1-2", "chr3:4-5");
-//        GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1:1-1");
-//        GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2:2-2");
-//        GenomeLoc chr3a = hg18GenomeLocParser.parseGenomeLoc("chr3:1-2");
-//        GenomeLoc chr3b = hg18GenomeLocParser.parseGenomeLoc("chr3:4-5");
-//
-//        List files = testFiles("contig_split_end.", 3 ,".intervals");
-//
-//        IntervalUtils.scatterContigIntervals(hg18Header, getLocs(intervals), files);
-//
-//        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
-//        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
-//        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
-//
-//        Assert.assertEquals(locs1.size(), 1);
-//        Assert.assertEquals(locs2.size(), 1);
-//        Assert.assertEquals(locs3.size(), 2);
-//
-//        Assert.assertEquals(locs1.get(0), chr1);
-//        Assert.assertEquals(locs2.get(0), chr2);
-//        Assert.assertEquals(locs3.get(0), chr3a);
-//        Assert.assertEquals(locs3.get(1), chr3b);
-//    }
-//
-//    @Test
-//    public void testScatterContigIntervalsMax() {
-//        List files = testFiles("sg.", 85, ".intervals");
-//        IntervalUtils.scatterContigIntervals(hg19Header, hg19ReferenceLocs, files);
-//
-//        for (int i = 0; i < files.size(); i++) {
-//            String file = files.get(i).toString();
-//            List parsedLocs = IntervalUtils.parseIntervalArguments(hg19GenomeLocParser, Arrays.asList(file), false);
-//            Assert.assertEquals(parsedLocs.size(), 1, "parsedLocs[" + i + "].size()");
-//            Assert.assertEquals(parsedLocs.get(0), hg19ReferenceLocs.get(i), "parsedLocs[" + i + "].get()");
-//        }
-//    }
-//
-//    private List testFiles(String prefix, int count, String suffix) {
-//        ArrayList files = new ArrayList();
-//        for (int i = 1; i <= count; i++) {
-//            files.add(createTempFile(prefix + i, suffix));
-//        }
-//        return files;
-//    }
-//
-//    @DataProvider(name="unmergedIntervals")
-//    public Object[][] getUnmergedIntervals() {
-//        return new Object[][] {
-//                new Object[] {"small_unmerged_picard_intervals.list"},
-//                new Object[] {"small_unmerged_gatk_intervals.list"}
-//        };
-//    }
-//
-//    @Test(dataProvider="unmergedIntervals")
-//    public void testUnmergedIntervals(String unmergedIntervals) {
-//        List locs = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Collections.singletonList(validationDataLocation + unmergedIntervals), false);
-//        Assert.assertEquals(locs.size(), 2);
-//
-//        List merged = IntervalUtils.mergeIntervalLocations(locs, IntervalMergingRule.ALL);
-//        Assert.assertEquals(merged.size(), 1);
-//    }
+    @Test(expectedExceptions=UserException.class)
+    public void testMergeListsBySetOperatorNoOverlap() {
+        // a couple of lists we'll use for the testing
+        List listEveryTwoFromOne = new ArrayList();
+        List listEveryTwoFromTwo = new ArrayList();
+
+        // create the two lists we'll use
+        for (int x = 1; x < 101; x++) {
+            if (x % 2 == 0)
+                listEveryTwoFromTwo.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x));
+            else
+                listEveryTwoFromOne.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x));
+        }
+
+        List ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, listEveryTwoFromOne, IntervalSetRule.UNION);
+        Assert.assertEquals(ret.size(), 100);
+        ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, listEveryTwoFromOne, IntervalSetRule.INTERSECTION);
+        Assert.assertEquals(ret.size(), 0);
+    }
+
+    @Test
+    public void testMergeListsBySetOperatorAllOverlap() {
+        // a couple of lists we'll use for the testing
+        List allSites = new ArrayList();
+        List listEveryTwoFromTwo = new ArrayList();
+
+        // create the two lists we'll use
+        for (int x = 1; x < 101; x++) {
+            if (x % 2 == 0)
+                listEveryTwoFromTwo.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x));
+            allSites.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x));
+        }
+
+        List ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, allSites, IntervalSetRule.UNION);
+        Assert.assertEquals(ret.size(), 150);
+        ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, allSites, IntervalSetRule.INTERSECTION);
+        Assert.assertEquals(ret.size(), 50);
+    }
+
+    @Test
+    public void testMergeListsBySetOperator() {
+        // a couple of lists we'll use for the testing
+        List allSites = new ArrayList();
+        List listEveryTwoFromTwo = new ArrayList();
+
+        // create the two lists we'll use
+        for (int x = 1; x < 101; x++) {
+            if (x % 5 == 0) {
+                listEveryTwoFromTwo.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x));
+                allSites.add(hg18GenomeLocParser.createGenomeLoc("chr1",x,x));
+            }
+        }
+
+        List ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, allSites, IntervalSetRule.UNION);
+        Assert.assertEquals(ret.size(), 40);
+        ret = IntervalUtils.mergeListsBySetOperator(listEveryTwoFromTwo, allSites, IntervalSetRule.INTERSECTION);
+        Assert.assertEquals(ret.size(), 20);
+    }
+
+    @Test
+    public void testGetContigLengths() {
+        Map lengths = IntervalUtils.getContigSizes(new File(BaseTest.hg18Reference));
+        Assert.assertEquals((long)lengths.get("chr1"), 247249719);
+        Assert.assertEquals((long)lengths.get("chr2"), 242951149);
+        Assert.assertEquals((long)lengths.get("chr3"), 199501827);
+        Assert.assertEquals((long)lengths.get("chr20"), 62435964);
+        Assert.assertEquals((long)lengths.get("chrX"), 154913754);
+    }
+
+    @Test
+    public void testParseIntervalArguments() {
+        Assert.assertEquals(getLocs().size(), 45);
+        Assert.assertEquals(getLocs("chr1", "chr2", "chr3").size(), 3);
+        Assert.assertEquals(getLocs("chr1:1-2", "chr1:4-5", "chr2:1-1", "chr3:2-2").size(), 4);
+    }
+
+    @Test
+    public void testIsIntervalFile() {
+        Assert.assertTrue(IntervalUtils.isIntervalFile(BaseTest.validationDataLocation + "empty_intervals.list"));
+        Assert.assertTrue(IntervalUtils.isIntervalFile(BaseTest.validationDataLocation + "empty_intervals.list", true));
+
+        List extensions = Arrays.asList("bed", "interval_list", "intervals", "list", "picard");
+        for (String extension: extensions) {
+            Assert.assertTrue(IntervalUtils.isIntervalFile("test_intervals." + extension, false), "Tested interval file extension: " + extension);
+        }
+    }
+
+    @Test(expectedExceptions = UserException.CouldNotReadInputFile.class)
+    public void testMissingIntervalFile() {
+        IntervalUtils.isIntervalFile(BaseTest.validationDataLocation + "no_such_intervals.list");
+    }
+
+    @Test
+    public void testFixedScatterIntervalsBasic() {
+        GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1");
+        GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2");
+        GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3");
+
+        List files = testFiles("basic.", 3, ".intervals");
+
+        List locs = getLocs("chr1", "chr2", "chr3");
+        IntervalUtils.scatterFixedIntervals(hg18Header, IntervalUtils.splitFixedIntervals(locs, files.size()), files);
+
+        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
+        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
+        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
+
+        Assert.assertEquals(locs1.size(), 1);
+        Assert.assertEquals(locs2.size(), 1);
+        Assert.assertEquals(locs3.size(), 1);
+
+        Assert.assertEquals(locs1.get(0), chr1);
+        Assert.assertEquals(locs2.get(0), chr2);
+        Assert.assertEquals(locs3.get(0), chr3);
+    }
+
+    @Test
+    public void testScatterFixedIntervalsLessFiles() {
+        GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1");
+        GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2");
+        GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3");
+        GenomeLoc chr4 = hg18GenomeLocParser.parseGenomeLoc("chr4");
+
+        List files = testFiles("less.", 3, ".intervals");
+
+        List locs = getLocs("chr1", "chr2", "chr3", "chr4");
+        IntervalUtils.scatterFixedIntervals(hg18Header, IntervalUtils.splitFixedIntervals(locs, files.size()), files);
+
+        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
+        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
+        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
+
+        Assert.assertEquals(locs1.size(), 2);
+        Assert.assertEquals(locs2.size(), 1);
+        Assert.assertEquals(locs3.size(), 1);
+
+        Assert.assertEquals(locs1.get(0), chr1);
+        Assert.assertEquals(locs1.get(1), chr2);
+        Assert.assertEquals(locs2.get(0), chr3);
+        Assert.assertEquals(locs3.get(0), chr4);
+    }
+
+    @Test(expectedExceptions=UserException.BadArgumentValue.class)
+    public void testSplitFixedIntervalsMoreFiles() {
+        List files = testFiles("more.", 3, ".intervals");
+        List locs = getLocs("chr1", "chr2");
+        IntervalUtils.splitFixedIntervals(locs, files.size());
+    }
+
+    @Test(expectedExceptions=UserException.BadArgumentValue.class)
+    public void testScatterFixedIntervalsMoreFiles() {
+        List files = testFiles("more.", 3, ".intervals");
+        List locs = getLocs("chr1", "chr2");
+        IntervalUtils.scatterFixedIntervals(hg18Header, IntervalUtils.splitFixedIntervals(locs, locs.size()), files);
+    }
+    @Test
+    public void testScatterFixedIntervalsStart() {
+        List intervals = Arrays.asList("chr1:1-2", "chr1:4-5", "chr2:1-1", "chr3:2-2");
+        GenomeLoc chr1a = hg18GenomeLocParser.parseGenomeLoc("chr1:1-2");
+        GenomeLoc chr1b = hg18GenomeLocParser.parseGenomeLoc("chr1:4-5");
+        GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2:1-1");
+        GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3:2-2");
+
+        List files = testFiles("split.", 3, ".intervals");
+
+        List locs = getLocs(intervals);
+        IntervalUtils.scatterFixedIntervals(hg18Header, IntervalUtils.splitFixedIntervals(locs, files.size()), files);
+
+        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
+        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
+        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
+
+        Assert.assertEquals(locs1.size(), 1);
+        Assert.assertEquals(locs2.size(), 1);
+        Assert.assertEquals(locs3.size(), 2);
+
+        Assert.assertEquals(locs1.get(0), chr1a);
+        Assert.assertEquals(locs2.get(0), chr1b);
+        Assert.assertEquals(locs3.get(0), chr2);
+        Assert.assertEquals(locs3.get(1), chr3);
+    }
+
+    @Test
+    public void testScatterFixedIntervalsMiddle() {
+        List intervals = Arrays.asList("chr1:1-1", "chr2:1-2", "chr2:4-5", "chr3:2-2");
+        GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1:1-1");
+        GenomeLoc chr2a = hg18GenomeLocParser.parseGenomeLoc("chr2:1-2");
+        GenomeLoc chr2b = hg18GenomeLocParser.parseGenomeLoc("chr2:4-5");
+        GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3:2-2");
+
+        List files = testFiles("split.", 3, ".intervals");
+
+        List locs = getLocs(intervals);
+        IntervalUtils.scatterFixedIntervals(hg18Header, IntervalUtils.splitFixedIntervals(locs, files.size()), files);
+
+        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
+        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
+        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
+
+        Assert.assertEquals(locs1.size(), 1);
+        Assert.assertEquals(locs2.size(), 1);
+        Assert.assertEquals(locs3.size(), 2);
+
+        Assert.assertEquals(locs1.get(0), chr1);
+        Assert.assertEquals(locs2.get(0), chr2a);
+        Assert.assertEquals(locs3.get(0), chr2b);
+        Assert.assertEquals(locs3.get(1), chr3);
+    }
+
+    @Test
+    public void testScatterFixedIntervalsEnd() {
+        List intervals = Arrays.asList("chr1:1-1", "chr2:2-2", "chr3:1-2", "chr3:4-5");
+        GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1:1-1");
+        GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2:2-2");
+        GenomeLoc chr3a = hg18GenomeLocParser.parseGenomeLoc("chr3:1-2");
+        GenomeLoc chr3b = hg18GenomeLocParser.parseGenomeLoc("chr3:4-5");
+
+        List files = testFiles("split.", 3, ".intervals");
+
+        List locs = getLocs(intervals);
+        IntervalUtils.scatterFixedIntervals(hg18Header, IntervalUtils.splitFixedIntervals(locs, files.size()), files);
+
+        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
+        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
+        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
+
+        Assert.assertEquals(locs1.size(), 2);
+        Assert.assertEquals(locs2.size(), 1);
+        Assert.assertEquals(locs3.size(), 1);
+
+        Assert.assertEquals(locs1.get(0), chr1);
+        Assert.assertEquals(locs1.get(1), chr2);
+        Assert.assertEquals(locs2.get(0), chr3a);
+        Assert.assertEquals(locs3.get(0), chr3b);
+    }
+
+    @Test
+    public void testScatterFixedIntervalsFile() {
+        List files = testFiles("sg.", 20, ".intervals");
+        List locs = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(BaseTest.GATKDataLocation + "whole_exome_agilent_designed_120.targets.hg18.chr20.interval_list"), false);
+        List> splits = IntervalUtils.splitFixedIntervals(locs, files.size());
+
+        int[] counts = {
+                125, 138, 287, 291, 312, 105, 155, 324,
+                295, 298, 141, 121, 285, 302, 282, 88,
+                116, 274, 282, 248
+//                5169, 5573, 10017, 10567, 10551,
+//                5087, 4908, 10120, 10435, 10399,
+//                5391, 4735, 10621, 10352, 10654,
+//                5227, 5256, 10151, 9649, 9825
+        };
+
+        //String splitCounts = "";
+        for (int i = 0; i < splits.size(); i++) {
+            long splitCount = splits.get(i).size();
+            Assert.assertEquals(splitCount, counts[i], "Num intervals in split " + i);
+        }
+        //System.out.println(splitCounts.substring(2));
+
+        IntervalUtils.scatterFixedIntervals(hg18Header, splits, files);
+
+        int locIndex = 0;
+        for (int i = 0; i < files.size(); i++) {
+            String file = files.get(i).toString();
+            List parsedLocs = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(file), false);
+            Assert.assertEquals(parsedLocs.size(), counts[i], "Intervals in " + file);
+            for (GenomeLoc parsedLoc: parsedLocs)
+                Assert.assertEquals(parsedLoc, locs.get(locIndex), String.format("Genome loc %d from file %d", locIndex++, i));
+        }
+        Assert.assertEquals(locIndex, locs.size(), "Total number of GenomeLocs");
+    }
+
+    @Test
+    public void testScatterFixedIntervalsMax() {
+        List files = testFiles("sg.", 85, ".intervals");
+        IntervalUtils.scatterFixedIntervals(hg19Header, IntervalUtils.splitFixedIntervals(hg19ReferenceLocs, files.size()), files);
+
+        for (int i = 0; i < files.size(); i++) {
+            String file = files.get(i).toString();
+            List parsedLocs = IntervalUtils.parseIntervalArguments(hg19GenomeLocParser, Arrays.asList(file), false);
+            Assert.assertEquals(parsedLocs.size(), 1, "parsedLocs[" + i + "].size()");
+            Assert.assertEquals(parsedLocs.get(0), hg19ReferenceLocs.get(i), "parsedLocs[" + i + "].get()");
+        }
+    }
+
+    @Test
+    public void testScatterContigIntervalsOrder() {
+        List intervals = Arrays.asList("chr2:1-1", "chr1:1-1", "chr3:2-2");
+        GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1:1-1");
+        GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2:1-1");
+        GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3:2-2");
+
+        List files = testFiles("split.", 3, ".intervals");
+
+        IntervalUtils.scatterContigIntervals(hg18Header, getLocs(intervals), files);
+
+        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
+        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
+        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
+
+        Assert.assertEquals(locs1.size(), 1);
+        Assert.assertEquals(locs2.size(), 1);
+        Assert.assertEquals(locs3.size(), 1);
+
+        Assert.assertEquals(locs1.get(0), chr2);
+        Assert.assertEquals(locs2.get(0), chr1);
+        Assert.assertEquals(locs3.get(0), chr3);
+    }
+
+    @Test
+    public void testScatterContigIntervalsBasic() {
+        GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1");
+        GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2");
+        GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3");
+
+        List files = testFiles("contig_basic.", 3, ".intervals");
+
+        IntervalUtils.scatterContigIntervals(hg18Header, getLocs("chr1", "chr2", "chr3"), files);
+
+        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
+        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
+        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
+
+        Assert.assertEquals(locs1.size(), 1);
+        Assert.assertEquals(locs2.size(), 1);
+        Assert.assertEquals(locs3.size(), 1);
+
+        Assert.assertEquals(locs1.get(0), chr1);
+        Assert.assertEquals(locs2.get(0), chr2);
+        Assert.assertEquals(locs3.get(0), chr3);
+    }
+
+    @Test
+    public void testScatterContigIntervalsLessFiles() {
+        GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1");
+        GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2");
+        GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3");
+        GenomeLoc chr4 = hg18GenomeLocParser.parseGenomeLoc("chr4");
+
+        List files = testFiles("contig_less.", 3, ".intervals");
+
+        IntervalUtils.scatterContigIntervals(hg18Header, getLocs("chr1", "chr2", "chr3", "chr4"), files);
+
+        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
+        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
+        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
+
+        Assert.assertEquals(locs1.size(), 1);
+        Assert.assertEquals(locs2.size(), 1);
+        Assert.assertEquals(locs3.size(), 2);
+
+        Assert.assertEquals(locs1.get(0), chr1);
+        Assert.assertEquals(locs2.get(0), chr2);
+        Assert.assertEquals(locs3.get(0), chr3);
+        Assert.assertEquals(locs3.get(1), chr4);
+    }
+
+    @Test(expectedExceptions=UserException.BadArgumentValue.class)
+    public void testScatterContigIntervalsMoreFiles() {
+        List files = testFiles("contig_more.", 3, ".intervals");
+        IntervalUtils.scatterContigIntervals(hg18Header, getLocs("chr1", "chr2"), files);
+    }
+
+    @Test
+    public void testScatterContigIntervalsStart() {
+        List intervals = Arrays.asList("chr1:1-2", "chr1:4-5", "chr2:1-1", "chr3:2-2");
+        GenomeLoc chr1a = hg18GenomeLocParser.parseGenomeLoc("chr1:1-2");
+        GenomeLoc chr1b = hg18GenomeLocParser.parseGenomeLoc("chr1:4-5");
+        GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2:1-1");
+        GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3:2-2");
+
+        List files = testFiles("contig_split_start.", 3, ".intervals");
+
+        IntervalUtils.scatterContigIntervals(hg18Header, getLocs(intervals), files);
+
+        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
+        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
+        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
+
+        Assert.assertEquals(locs1.size(), 2);
+        Assert.assertEquals(locs2.size(), 1);
+        Assert.assertEquals(locs3.size(), 1);
+
+        Assert.assertEquals(locs1.get(0), chr1a);
+        Assert.assertEquals(locs1.get(1), chr1b);
+        Assert.assertEquals(locs2.get(0), chr2);
+        Assert.assertEquals(locs3.get(0), chr3);
+    }
+
+    @Test
+    public void testScatterContigIntervalsMiddle() {
+        List intervals = Arrays.asList("chr1:1-1", "chr2:1-2", "chr2:4-5", "chr3:2-2");
+        GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1:1-1");
+        GenomeLoc chr2a = hg18GenomeLocParser.parseGenomeLoc("chr2:1-2");
+        GenomeLoc chr2b = hg18GenomeLocParser.parseGenomeLoc("chr2:4-5");
+        GenomeLoc chr3 = hg18GenomeLocParser.parseGenomeLoc("chr3:2-2");
+
+        List files = testFiles("contig_split_middle.", 3, ".intervals");
+
+        IntervalUtils.scatterContigIntervals(hg18Header, getLocs(intervals), files);
+
+        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
+        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
+        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
+
+        Assert.assertEquals(locs1.size(), 1);
+        Assert.assertEquals(locs2.size(), 2);
+        Assert.assertEquals(locs3.size(), 1);
+
+        Assert.assertEquals(locs1.get(0), chr1);
+        Assert.assertEquals(locs2.get(0), chr2a);
+        Assert.assertEquals(locs2.get(1), chr2b);
+        Assert.assertEquals(locs3.get(0), chr3);
+    }
+
+    @Test
+    public void testScatterContigIntervalsEnd() {
+        List intervals = Arrays.asList("chr1:1-1", "chr2:2-2", "chr3:1-2", "chr3:4-5");
+        GenomeLoc chr1 = hg18GenomeLocParser.parseGenomeLoc("chr1:1-1");
+        GenomeLoc chr2 = hg18GenomeLocParser.parseGenomeLoc("chr2:2-2");
+        GenomeLoc chr3a = hg18GenomeLocParser.parseGenomeLoc("chr3:1-2");
+        GenomeLoc chr3b = hg18GenomeLocParser.parseGenomeLoc("chr3:4-5");
+
+        List files = testFiles("contig_split_end.", 3 ,".intervals");
+
+        IntervalUtils.scatterContigIntervals(hg18Header, getLocs(intervals), files);
+
+        List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
+        List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
+        List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
+
+        Assert.assertEquals(locs1.size(), 1);
+        Assert.assertEquals(locs2.size(), 1);
+        Assert.assertEquals(locs3.size(), 2);
+
+        Assert.assertEquals(locs1.get(0), chr1);
+        Assert.assertEquals(locs2.get(0), chr2);
+        Assert.assertEquals(locs3.get(0), chr3a);
+        Assert.assertEquals(locs3.get(1), chr3b);
+    }
+
+    @Test
+    public void testScatterContigIntervalsMax() {
+        List files = testFiles("sg.", 85, ".intervals");
+        IntervalUtils.scatterContigIntervals(hg19Header, hg19ReferenceLocs, files);
+
+        for (int i = 0; i < files.size(); i++) {
+            String file = files.get(i).toString();
+            List parsedLocs = IntervalUtils.parseIntervalArguments(hg19GenomeLocParser, Arrays.asList(file), false);
+            Assert.assertEquals(parsedLocs.size(), 1, "parsedLocs[" + i + "].size()");
+            Assert.assertEquals(parsedLocs.get(0), hg19ReferenceLocs.get(i), "parsedLocs[" + i + "].get()");
+        }
+    }
+
+    private List testFiles(String prefix, int count, String suffix) {
+        ArrayList files = new ArrayList();
+        for (int i = 1; i <= count; i++) {
+            files.add(createTempFile(prefix + i, suffix));
+        }
+        return files;
+    }
+
+    @DataProvider(name="unmergedIntervals")
+    public Object[][] getUnmergedIntervals() {
+        return new Object[][] {
+                new Object[] {"small_unmerged_picard_intervals.list"},
+                new Object[] {"small_unmerged_gatk_intervals.list"}
+        };
+    }
+
+    @Test(dataProvider="unmergedIntervals")
+    public void testUnmergedIntervals(String unmergedIntervals) {
+        List locs = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Collections.singletonList(validationDataLocation + unmergedIntervals), false);
+        Assert.assertEquals(locs.size(), 2);
+
+        List merged = IntervalUtils.mergeIntervalLocations(locs, IntervalMergingRule.ALL);
+        Assert.assertEquals(merged.size(), 1);
+    }
 }

From 9e650dfc17621a76e421184ae33b03c2515f193c Mon Sep 17 00:00:00 2001
From: Mauricio Carneiro 
Date: Fri, 9 Sep 2011 16:25:31 -0400
Subject: [PATCH 046/196] Fixing SelectVariants documentation

getting rid of messages telling users to go for the YAML file. The idea is to not support these anymore.
---
 .../gatk/walkers/variantutils/SelectVariants.java   | 13 ++++++-------
 1 file changed, 6 insertions(+), 7 deletions(-)

diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/SelectVariants.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/SelectVariants.java
index 35ff66243..018c4dcc2 100755
--- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/SelectVariants.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/SelectVariants.java
@@ -145,10 +145,9 @@ import java.util.*;
  *   -R ref.fasta \
  *   -T SelectVariants \
  *   --variant input.vcf \
- *   -o output.vcf \
- *   -SM family.yaml \
  *   -family NA12891+NA12892=NA12878 \
- *   -mvq 50
+ *   -mvq 50 \
+ *   -o violations.vcf
  *
  * Creating a sample of exactly 1000 variants randomly chosen with equal probability from the variant VCF:
  * java -Xmx2g -jar GenomeAnalysisTK.jar \
@@ -265,17 +264,17 @@ public class SelectVariants extends RodWalker {
     private File AF_FILE = new File("");
 
     @Hidden
-    @Argument(fullName="family_structure_file", shortName="familyFile", doc="USE YAML FILE INSTEAD (-SM) !!! string formatted as dad+mom=child where these parameters determine which sample names are examined", required=false)
+    @Argument(fullName="family_structure_file", shortName="familyFile", doc="use -family unless you know what you're doing", required=false)
     private File FAMILY_STRUCTURE_FILE = null;
 
     /**
      * String formatted as dad+mom=child where these parameters determine which sample names are examined.
      */
-    @Argument(fullName="family_structure", shortName="family", doc="Deprecated; use the -SM argument instead", required=false)
+    @Argument(fullName="family_structure", shortName="family", doc="string formatted as dad+mom=child where these parameters determine which sample names are examined", required=false)
     private String FAMILY_STRUCTURE = "";
 
     /**
-     * Sample metadata information will be taken from a YAML file (see the -SM argument).
+     * This activates the mendelian violation module that will select all variants that correspond to a mendelian violation following the rules given by the family structure.
      */
     @Argument(fullName="mendelianViolation", shortName="mv", doc="output mendelian violation sites only", required=false)
     private Boolean MENDELIAN_VIOLATIONS = false;
@@ -306,7 +305,7 @@ public class SelectVariants extends RodWalker {
 
 
     @Hidden
-    @Argument(fullName="outMVFile", shortName="outMVFile", doc="USE YAML FILE INSTEAD (-SM) !!! string formatted as dad+mom=child where these parameters determine which sample names are examined", required=false)
+    @Argument(fullName="outMVFile", shortName="outMVFile", doc="", required=false)
     private String outMVFile = null;
 
     /* Private class used to store the intermediate variants in the integer random selection process */

From a807205fc3966855a4ab122ac4aa0548e425178b Mon Sep 17 00:00:00 2001
From: Guillermo del Angel 
Date: Fri, 9 Sep 2011 18:00:23 -0400
Subject: [PATCH 047/196] a) Minor optimization to softMax() computation to
 avoid redundant operations, results in about 5-10% increase in speed in indel
 calling. b) Added (but left commented out since it may affect integration
 tests and to isolate commits) fix to per-sample DP reporting, so that
 deletions are included in count. c) Bug fix to avoid having non-reference
 genotypes assigned to samples with PL=0,0,0. Correct behavior should be to
 no-call these samples, and to ignore these samples when computing AC
 distribution since their likelihoods are not informative.

---
 .../genotyper/ExactAFCalculationModel.java    | 84 +++++++++++++------
 ...elGenotypeLikelihoodsCalculationModel.java | 20 ++++-
 .../broadinstitute/sting/utils/MathUtils.java | 54 +++++-------
 3 files changed, 95 insertions(+), 63 deletions(-)

diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/ExactAFCalculationModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/ExactAFCalculationModel.java
index cd006a3cf..6ae437b27 100755
--- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/ExactAFCalculationModel.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/ExactAFCalculationModel.java
@@ -63,7 +63,7 @@ public class ExactAFCalculationModel extends AlleleFrequencyCalculationModel {
 
     private boolean SIMPLE_GREEDY_GENOTYPER = false;
 
-
+    private final static double SUM_GL_THRESH_NOCALL = -0.001; // if sum(gl) is bigger than this threshold, we treat GL's as non-informative and will force a no-call.
 
     final private ExactCalculation calcToUse;
     protected ExactAFCalculationModel(UnifiedArgumentCollection UAC, int N, Logger logger, PrintStream verboseWriter) {
@@ -178,22 +178,25 @@ public class ExactAFCalculationModel extends AlleleFrequencyCalculationModel {
 
     }
 
-    private static final double[][] getGLs(Map GLs) {
-        double[][] genotypeLikelihoods = new double[GLs.size()+1][];
+    private static final ArrayList getGLs(Map GLs) {
+        ArrayList genotypeLikelihoods = new ArrayList();
 
-        int j = 0;
+        //int j = 0;
+        genotypeLikelihoods.add(new double[]{0.0,0.0,0.0}); // dummy
         for ( Genotype sample : GLs.values() ) {
-            j++;
-
             if ( sample.hasLikelihoods() ) {
                 //double[] genotypeLikelihoods = MathUtils.normalizeFromLog10(GLs.get(sample).getLikelihoods());
-                genotypeLikelihoods[j] = sample.getLikelihoods().getAsVector();
+                double[] gls = sample.getLikelihoods().getAsVector();
+
+                if (MathUtils.sum(gls) < SUM_GL_THRESH_NOCALL)
+                    genotypeLikelihoods.add(gls);
             }
         }
 
         return genotypeLikelihoods;
     }
 
+
     // -------------------------------------------------------------------------------------
     //
     // Linearized, ~O(N), implementation.
@@ -318,9 +321,9 @@ public class ExactAFCalculationModel extends AlleleFrequencyCalculationModel {
     public int linearExact(Map GLs,
                            double[] log10AlleleFrequencyPriors,
                            double[] log10AlleleFrequencyPosteriors, int idxAA, int idxAB, int idxBB) {
-        final int numSamples = GLs.size();
+        final ArrayList genotypeLikelihoods = getGLs(GLs);
+        final int numSamples = genotypeLikelihoods.size()-1;
         final int numChr = 2*numSamples;
-        final double[][] genotypeLikelihoods = getGLs(GLs);
 
         final ExactACCache logY = new ExactACCache(numSamples+1);
         logY.getkMinus0()[0] = 0.0; // the zero case
@@ -334,14 +337,14 @@ public class ExactAFCalculationModel extends AlleleFrequencyCalculationModel {
 
             if ( k == 0 ) { // special case for k = 0
                 for ( int j=1; j <= numSamples; j++ ) {
-                    kMinus0[j] = kMinus0[j-1] + genotypeLikelihoods[j][idxAA];
+                    kMinus0[j] = kMinus0[j-1] + genotypeLikelihoods.get(j)[idxAA];
                 }
             } else { // k > 0
                 final double[] kMinus1 = logY.getkMinus1();
                 final double[] kMinus2 = logY.getkMinus2();
 
                 for ( int j=1; j <= numSamples; j++ ) {
-                    final double[] gl = genotypeLikelihoods[j];
+                    final double[] gl = genotypeLikelihoods.get(j);
                     final double logDenominator = MathUtils.log10Cache[2*j] + MathUtils.log10Cache[2*j-1];
 
                     double aa = Double.NEGATIVE_INFINITY;
@@ -434,10 +437,6 @@ public class ExactAFCalculationModel extends AlleleFrequencyCalculationModel {
         if ( !vc.isVariant() )
             throw new UserException("The VCF record passed in does not contain an ALT allele at " + vc.getChr() + ":" + vc.getStart());
 
-        boolean multiAllelicRecord = false;
-
-        if (vc.getAlternateAlleles().size() > 1)
-            multiAllelicRecord = true;
 
         Map GLs = vc.getGenotypes();
         double[][] pathMetricArray = new double[GLs.size()+1][AFofMaxLikelihood+1];
@@ -454,7 +453,7 @@ public class ExactAFCalculationModel extends AlleleFrequencyCalculationModel {
         pathMetricArray[0][0] = 0.0;
 
         // todo = can't deal with optimal dynamic programming solution with multiallelic records
-        if (SIMPLE_GREEDY_GENOTYPER || multiAllelicRecord) {
+        if (SIMPLE_GREEDY_GENOTYPER || !vc.isBiallelic()) {
             sampleIndices.addAll(GLs.keySet());
             sampleIdx = GLs.size();
         }
@@ -465,6 +464,17 @@ public class ExactAFCalculationModel extends AlleleFrequencyCalculationModel {
                     continue;
 
                 double[] likelihoods = sample.getValue().getLikelihoods().getAsVector();
+
+                if (MathUtils.sum(likelihoods) > SUM_GL_THRESH_NOCALL)     {
+                    //System.out.print(sample.getKey()+":");
+                    //for (int k=0; k < likelihoods.length; k++)
+                    //   System.out.format("%4.2f ",likelihoods[k]);
+                    //System.out.println();
+                    // all likelihoods are essentially the same: skip this sample and will later on force no call.
+                    //sampleIdx++;
+                    continue;
+                }
+
                 sampleIndices.add(sample.getKey());
 
                 for (int k=0; k <= AFofMaxLikelihood; k++) {
@@ -504,22 +514,25 @@ public class ExactAFCalculationModel extends AlleleFrequencyCalculationModel {
             Genotype g = GLs.get(sample);
             if ( !g.hasLikelihoods() )
                 continue;
-
-            if (SIMPLE_GREEDY_GENOTYPER || multiAllelicRecord)
-                bestGTguess = Utils.findIndexOfMaxEntry(g.getLikelihoods().getAsVector());
-            else {
-                int newIdx = tracebackArray[k][startIdx];
-                bestGTguess = startIdx - newIdx;
-                startIdx = newIdx;
-            }
-
+            // if all likelihoods are essentially the same: we want to force no-call. In this case, we skip this sample for now,
+            // and will add no-call genotype to GL's in a second pass
             ArrayList myAlleles = new ArrayList();
 
             double qual = Double.NEGATIVE_INFINITY;
             double[] likelihoods = g.getLikelihoods().getAsVector();
+
+            if (SIMPLE_GREEDY_GENOTYPER || !vc.isBiallelic()) {
+                bestGTguess = Utils.findIndexOfMaxEntry(g.getLikelihoods().getAsVector());
+            }
+            else {
+                int newIdx = tracebackArray[k][startIdx];;
+                bestGTguess = startIdx - newIdx;
+                startIdx = newIdx;
+            }
+
             /*           System.out.format("Sample: %s GL:",sample);
                     for (int i=0; i < likelihoods.length; i++)
-                        System.out.format("%1.4f ",likelihoods[i]);
+                        System.out.format("%1.4f, ",likelihoods[i]);
             */
 
             for (int i=0; i < likelihoods.length; i++) {
@@ -570,6 +583,25 @@ public class ExactAFCalculationModel extends AlleleFrequencyCalculationModel {
 
         }
 
+        for ( Map.Entry sample : GLs.entrySet() ) {
+
+            if ( !sample.getValue().hasLikelihoods() )
+                continue;
+            Genotype g = GLs.get(sample.getKey());
+
+            double[] likelihoods = sample.getValue().getLikelihoods().getAsVector();
+
+            if (MathUtils.sum(likelihoods) <= SUM_GL_THRESH_NOCALL)
+                continue; // regular likelihoods
+
+            ArrayList myAlleles = new ArrayList();
+
+            double qual = Genotype.NO_NEG_LOG_10PERROR;
+            myAlleles.add(Allele.NO_CALL);
+            myAlleles.add(Allele.NO_CALL);
+            //System.out.println(myAlleles.toString());
+            calls.put(sample.getKey(), new Genotype(sample.getKey(), myAlleles, qual, null, g.getAttributes(), false));
+        }
         return calls;
     }
 
diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/IndelGenotypeLikelihoodsCalculationModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/IndelGenotypeLikelihoodsCalculationModel.java
index 07f02de57..2a99f1aad 100755
--- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/IndelGenotypeLikelihoodsCalculationModel.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/IndelGenotypeLikelihoodsCalculationModel.java
@@ -32,7 +32,9 @@ import org.broadinstitute.sting.gatk.contexts.ReferenceContext;
 import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker;
 import org.broadinstitute.sting.gatk.walkers.indels.HaplotypeIndelErrorModel;
 import org.broadinstitute.sting.gatk.walkers.indels.PairHMMIndelErrorModel;
+import org.broadinstitute.sting.utils.BaseUtils;
 import org.broadinstitute.sting.utils.GenomeLoc;
+import org.broadinstitute.sting.utils.MathUtils;
 import org.broadinstitute.sting.utils.collections.Pair;
 import org.broadinstitute.sting.utils.exceptions.StingException;
 import org.broadinstitute.sting.utils.genotype.Haplotype;
@@ -413,16 +415,14 @@ public class IndelGenotypeLikelihoodsCalculationModel extends GenotypeLikelihood
 
             if (pileup != null ) {
                 double[] genotypeLikelihoods;
+
                 if (useOldWrongHorribleHackedUpLikelihoodModel)
                    genotypeLikelihoods = model.computeReadHaplotypeLikelihoods( pileup, haplotypeMap);
                 else
                     genotypeLikelihoods = pairModel.computeReadHaplotypeLikelihoods( pileup, haplotypeMap, ref, eventLength, getIndelLikelihoodMap());
 
 
-
-                // which genotype likelihoods correspond to two most likely alleles? By convention, likelihood vector is ordered as for example
-                // for 3 alleles it's 00 01 11 02 12 22
-                 GLs.put(sample.getKey(), new MultiallelicGenotypeLikelihoods(sample.getKey(),
+                GLs.put(sample.getKey(), new MultiallelicGenotypeLikelihoods(sample.getKey(),
                         alleleList,
                         genotypeLikelihoods,
                         getFilteredDepth(pileup)));
@@ -444,4 +444,16 @@ public class IndelGenotypeLikelihoodsCalculationModel extends GenotypeLikelihood
         return indelLikelihoodMap.get();
     }
 
+    // Overload function in GenotypeLikelihoodsCalculationModel so that, for an indel case, we consider a deletion as part of the pileup,
+    // so that per-sample DP will include deletions covering the event.
+    protected int getFilteredDepth(ReadBackedPileup pileup) {
+        int count = 0;
+        for ( PileupElement p : pileup ) {
+            if (/*p.isDeletion() ||*/ BaseUtils.isRegularBase(p.getBase()) )
+                count++;
+        }
+
+        return count;
+    }
+
 }
\ No newline at end of file
diff --git a/public/java/src/org/broadinstitute/sting/utils/MathUtils.java b/public/java/src/org/broadinstitute/sting/utils/MathUtils.java
index f4c057c15..0d85f9606 100644
--- a/public/java/src/org/broadinstitute/sting/utils/MathUtils.java
+++ b/public/java/src/org/broadinstitute/sting/utils/MathUtils.java
@@ -1056,42 +1056,30 @@ public class MathUtils {
     }
 
     static public double softMax(final double x, final double y) {
-         if (Double.isInfinite(x))
-             return y;
+        // we need to compute log10(10^x + 10^y)
+        // By Jacobian logarithm identity, this is equal to
+        // max(x,y) + log10(1+10^-abs(x-y))
+        // we compute the second term as a table lookup
+        // with integer quantization
 
-         if (Double.isInfinite(y))
-             return x;
+        // slow exact version:
+        // return Math.log10(Math.pow(10.0,x) + Math.pow(10.0,y));
 
-         if (y >= x + MAX_JACOBIAN_TOLERANCE)
-             return y;
-         if (x >= y + MAX_JACOBIAN_TOLERANCE)
-             return x;
+        double diff = x-y;
 
-         // OK, so |y-x| < tol: we use the following identity then:
-         // we need to compute log10(10^x + 10^y)
-         // By Jacobian logarithm identity, this is equal to
-         // max(x,y) + log10(1+10^-abs(x-y))
-         // we compute the second term as a table lookup
-         // with integer quantization
-
-         //double diff = Math.abs(x-y);
-         double diff = x-y;
-         double t1 =x;
-         if (diff<0) { //
-             t1 = y;
-             diff= -diff;
-         }
-         // t has max(x,y), diff has abs(x-y)
-         // we have pre-stored correction for 0,0.1,0.2,... 10.0
-         //int ind = (int)Math.round(diff*INV_JACOBIAN_LOG_TABLE_STEP);
-         int ind = (int)(diff*INV_JACOBIAN_LOG_TABLE_STEP+0.5);
-         // gdebug+
-         //double z =Math.log10(1+Math.pow(10.0,-diff));
-         //System.out.format("x: %f, y:%f, app: %f, true: %f ind:%d\n",x,y,t2,z,ind);
-         //gdebug-
-         return t1+jacobianLogTable[ind];
-         // return Math.log10(Math.pow(10.0,x) + Math.pow(10.0,y));
-     }
+        if (diff > MAX_JACOBIAN_TOLERANCE)
+            return x;
+        else if (diff < -MAX_JACOBIAN_TOLERANCE)
+            return y;
+        else if (diff >= 0) {
+            int ind = (int)(diff*INV_JACOBIAN_LOG_TABLE_STEP+0.5);
+            return x + jacobianLogTable[ind];
+        }
+        else {
+            int ind = (int)(-diff*INV_JACOBIAN_LOG_TABLE_STEP+0.5);
+            return y + jacobianLogTable[ind];
+        }
+    }
 
     public static double phredScaleToProbability (byte q) {
         return Math.pow(10,(-q)/10.0);

From b399424a9cd4c842e8dac0e2e0f9c17ba4002ff4 Mon Sep 17 00:00:00 2001
From: Guillermo del Angel 
Date: Fri, 9 Sep 2011 20:44:47 -0400
Subject: [PATCH 048/196] Fix integration test affected by non-calling all-zero
 PL samples, and add a more complicated multi-sample integration test from a
 phase 1 case, GBR with mixed technologies and complex input alleles

---
 .../genotyper/UnifiedGenotyperIntegrationTest.java     | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperIntegrationTest.java
index 185880401..e212e07ea 100755
--- a/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperIntegrationTest.java
+++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperIntegrationTest.java
@@ -18,6 +18,7 @@ public class UnifiedGenotyperIntegrationTest extends WalkerTest {
 
     private final static String baseCommand = "-T UnifiedGenotyper -R " + b36KGReference + " -NO_HEADER -glm BOTH --dbsnp " + b36dbSNP129;
     private final static String baseCommandIndels = "-T UnifiedGenotyper -R " + b36KGReference + " -NO_HEADER -glm INDEL --dbsnp " + b36dbSNP129;
+    private final static String baseCommandIndelsb37 = "-T UnifiedGenotyper -R " + b37KGReference + " -NO_HEADER -glm INDEL --dbsnp " + b37dbSNP132;
 
     // --------------------------------------------------------------------------------------------------------------
     //
@@ -28,7 +29,7 @@ public class UnifiedGenotyperIntegrationTest extends WalkerTest {
     public void testMultiSamplePilot1() {
         WalkerTest.WalkerTestSpec spec = new WalkerTest.WalkerTestSpec(
                 baseCommand + " -I " + validationDataLocation + "low_coverage_CEU.chr1.10k-11k.bam -o %s -L 1:10,022,000-10,025,000", 1,
-                Arrays.asList("149e6ad9b3fd23551254a691286a96b3"));
+                Arrays.asList("4bd3e874d071c4df250dce32cf441aab"));
         executeTest("test MultiSample Pilot1", spec);
     }
 
@@ -276,7 +277,14 @@ public class UnifiedGenotyperIntegrationTest extends WalkerTest {
                 Arrays.asList("e66b7321e2ac91742ad3ef91040daafd"));
         executeTest("test MultiSample Pilot2 indels with complicated records", spec3);
 
+        WalkerTest.WalkerTestSpec spec4 = new WalkerTest.WalkerTestSpec(
+                baseCommandIndelsb37 + " --genotyping_mode GENOTYPE_GIVEN_ALLELES -alleles " + validationDataLocation + "ALL.wgs.union_v2_chr20_100_110K.20101123.indels.sites.vcf -I " + validationDataLocation +
+                        "phase1_GBR_realigned.chr20.100K-110K.bam -o %s -L 20:100,000-110,000", 1,
+                Arrays.asList("b6c3f771e8844a64681187ebb2b620f1"));
+        executeTest("test MultiSample 1000G Phase1 indels with complicated records emitting all sites", spec4);
+
     }
 
 
+
 }

From 9344938360d6d7d1312dc5993023fedd4f540701 Mon Sep 17 00:00:00 2001
From: Guillermo del Angel 
Date: Sat, 10 Sep 2011 19:41:01 -0400
Subject: [PATCH 049/196] Uncomment code to add deleted bases covering an indel
 to per-sample genotype reporting, update integration tests accordingly

---
 ...elGenotypeLikelihoodsCalculationModel.java |  2 +-
 .../UnifiedGenotyperIntegrationTest.java      | 24 +++++++++----------
 2 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/IndelGenotypeLikelihoodsCalculationModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/IndelGenotypeLikelihoodsCalculationModel.java
index 2a99f1aad..ec5eefd60 100755
--- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/IndelGenotypeLikelihoodsCalculationModel.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/IndelGenotypeLikelihoodsCalculationModel.java
@@ -449,7 +449,7 @@ public class IndelGenotypeLikelihoodsCalculationModel extends GenotypeLikelihood
     protected int getFilteredDepth(ReadBackedPileup pileup) {
         int count = 0;
         for ( PileupElement p : pileup ) {
-            if (/*p.isDeletion() ||*/ BaseUtils.isRegularBase(p.getBase()) )
+            if (p.isDeletion() || BaseUtils.isRegularBase(p.getBase()) )
                 count++;
         }
 
diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperIntegrationTest.java
index e212e07ea..41496bdf1 100755
--- a/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperIntegrationTest.java
+++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperIntegrationTest.java
@@ -29,7 +29,7 @@ public class UnifiedGenotyperIntegrationTest extends WalkerTest {
     public void testMultiSamplePilot1() {
         WalkerTest.WalkerTestSpec spec = new WalkerTest.WalkerTestSpec(
                 baseCommand + " -I " + validationDataLocation + "low_coverage_CEU.chr1.10k-11k.bam -o %s -L 1:10,022,000-10,025,000", 1,
-                Arrays.asList("4bd3e874d071c4df250dce32cf441aab"));
+                Arrays.asList("e6639ea2dc81635c706e6c35921406d7"));
         executeTest("test MultiSample Pilot1", spec);
     }
 
@@ -50,7 +50,7 @@ public class UnifiedGenotyperIntegrationTest extends WalkerTest {
     public void testSingleSamplePilot2() {
         WalkerTest.WalkerTestSpec spec = new WalkerTest.WalkerTestSpec(
                 baseCommand + " -I " + validationDataLocation + "NA12878.1kg.p2.chr1_10mb_11_mb.SLX.bam -o %s -L 1:10,000,000-10,100,000", 1,
-                Arrays.asList("82d469145c174486ccc494884852cc58"));
+                Arrays.asList("d1cbd1fb9f3f7323941a95bc2def7e5a"));
         executeTest("test SingleSample Pilot2", spec);
     }
 
@@ -60,7 +60,7 @@ public class UnifiedGenotyperIntegrationTest extends WalkerTest {
     //
     // --------------------------------------------------------------------------------------------------------------
 
-    private final static String COMPRESSED_OUTPUT_MD5 = "a5a9f38c645d6004d4640765a8b77ce4";
+    private final static String COMPRESSED_OUTPUT_MD5 = "2732b169cdccb21eb3ea00429619de79";
 
     @Test
     public void testCompressedOutput() {
@@ -81,7 +81,7 @@ public class UnifiedGenotyperIntegrationTest extends WalkerTest {
 
         // Note that we need to turn off any randomization for this to work, so no downsampling and no annotations
 
-        String md5 = "0a45761c0e557d9c2080eb9e7f4f6c41";
+        String md5 = "cbac3960bbcb9d6192c57549208c182c";
 
         WalkerTest.WalkerTestSpec spec1 = new WalkerTest.WalkerTestSpec(
                 baseCommand + " -dt NONE -G none -I " + validationDataLocation + "NA12878.1kg.p2.chr1_10mb_11_mb.SLX.bam -o %s -L 1:10,000,000-10,075,000", 1,
@@ -160,8 +160,8 @@ public class UnifiedGenotyperIntegrationTest extends WalkerTest {
     @Test
     public void testHeterozyosity() {
         HashMap e = new HashMap();
-        e.put( 0.01, "af5199fbc0853cf5888acdcc88f012bc" );
-        e.put( 1.0 / 1850, "4e6938645ccde1fdf204ffbf4e88170f" );
+        e.put( 0.01, "aed69402ddffe7f2ed5ca98563bfba02" );
+        e.put( 1.0 / 1850, "fa94a059f08c1821b721335d93ed2ea5" );
 
         for ( Map.Entry entry : e.entrySet() ) {
             WalkerTest.WalkerTestSpec spec = new WalkerTest.WalkerTestSpec(
@@ -185,7 +185,7 @@ public class UnifiedGenotyperIntegrationTest extends WalkerTest {
                         " -o %s" +
                         " -L 1:10,000,000-10,100,000",
                 1,
-                Arrays.asList("213ebaaaacf850312d885e918eb33500"));
+                Arrays.asList("1c080e6596d4c830bb5d147b04e2a82c"));
 
         executeTest(String.format("test multiple technologies"), spec);
     }
@@ -204,7 +204,7 @@ public class UnifiedGenotyperIntegrationTest extends WalkerTest {
                         " -L 1:10,000,000-10,100,000" +
                         " -baq CALCULATE_AS_NECESSARY",
                 1,
-                Arrays.asList("3aecba34a89f3525afa57a38dc20e6cd"));
+                Arrays.asList("9129ad748ca3be2d3b321d2d7e83ae5b"));
 
         executeTest(String.format("test calling with BAQ"), spec);
     }
@@ -223,7 +223,7 @@ public class UnifiedGenotyperIntegrationTest extends WalkerTest {
                         " -o %s" +
                         " -L 1:10,000,000-10,500,000",
                 1,
-                Arrays.asList("043973c719a85de29a35a33a674616fb"));
+                Arrays.asList("0bece77ce6bc447438ef9b2921b2dc41"));
 
         executeTest(String.format("test indel caller in SLX"), spec);
     }
@@ -238,7 +238,7 @@ public class UnifiedGenotyperIntegrationTest extends WalkerTest {
                         " -minIndelCnt 1" +
                         " -L 1:10,000,000-10,100,000",
                 1,
-                Arrays.asList("68d4e6c1849e892467aed61c33e7bf24"));
+                Arrays.asList("5fe98ee853586dc9db58f0bc97daea63"));
 
         executeTest(String.format("test indel caller in SLX witn low min allele count"), spec);
     }
@@ -251,7 +251,7 @@ public class UnifiedGenotyperIntegrationTest extends WalkerTest {
                          " -o %s" +
                          " -L 1:10,000,000-10,500,000",
                  1,
-                 Arrays.asList("f86d453c5d2d2f33fb28ae2050658a5e"));
+                 Arrays.asList("790b1a1d6ab79eee8c24812bb8ca6fae"));
 
          executeTest(String.format("test indel calling, multiple technologies"), spec);
      }
@@ -280,7 +280,7 @@ public class UnifiedGenotyperIntegrationTest extends WalkerTest {
         WalkerTest.WalkerTestSpec spec4 = new WalkerTest.WalkerTestSpec(
                 baseCommandIndelsb37 + " --genotyping_mode GENOTYPE_GIVEN_ALLELES -alleles " + validationDataLocation + "ALL.wgs.union_v2_chr20_100_110K.20101123.indels.sites.vcf -I " + validationDataLocation +
                         "phase1_GBR_realigned.chr20.100K-110K.bam -o %s -L 20:100,000-110,000", 1,
-                Arrays.asList("b6c3f771e8844a64681187ebb2b620f1"));
+                Arrays.asList("4be308fd9e8167ebee677f62a7a753b7"));
         executeTest("test MultiSample 1000G Phase1 indels with complicated records emitting all sites", spec4);
 
     }

From 07d365ce392bc5c38ad1f3dfc7348819b7017438 Mon Sep 17 00:00:00 2001
From: Ryan Poplin 
Date: Mon, 12 Sep 2011 09:01:34 -0400
Subject: [PATCH 053/196] Fixing units in queue job report Gantt plots

---
 public/R/queueJobReport.R | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/public/R/queueJobReport.R b/public/R/queueJobReport.R
index a24d269c9..31916361e 100644
--- a/public/R/queueJobReport.R
+++ b/public/R/queueJobReport.R
@@ -140,6 +140,8 @@ print(paste("Project          :", inputFileName))
 convertUnits <- function(gatkReportData) {
   convertGroup <- function(g) {
     g$runtime = g$runtime * ORIGINAL_UNITS_TO_SECONDS
+    g$startTime = g$startTime * ORIGINAL_UNITS_TO_SECONDS
+    g$doneTime = g$doneTime * ORIGINAL_UNITS_TO_SECONDS
     g
   }
   lapply(gatkReportData, convertGroup)

From 60ebe68aff12290f18527faed04f3f7bb356d962 Mon Sep 17 00:00:00 2001
From: Ryan Poplin 
Date: Mon, 12 Sep 2011 09:43:23 -0400
Subject: [PATCH 054/196] Fixing issue in VariantEval in which insertion and
 deletion events weren't treated symmetrically. Added new option to require
 strict allele matching.

---
 .../gatk/walkers/varianteval/VariantEvalWalker.java | 13 ++++++++-----
 1 file changed, 8 insertions(+), 5 deletions(-)

diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalWalker.java
index 0d09b7033..266b97af0 100755
--- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalWalker.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalWalker.java
@@ -55,7 +55,7 @@ import java.util.*;
  *
  * 

Output

*

- * Evaluation tables. + * Evaluation tables detailing the results of the eval modules which were applied. *

* *

Examples

@@ -152,6 +152,9 @@ public class VariantEvalWalker extends RodWalker implements Tr @Argument(fullName="ancestralAlignments", shortName="aa", doc="Fasta file with ancestral alleles", required=false) private File ancestralAlignmentsFile = null; + @Argument(fullName="requireStrictAlleleMatch", shortName="strict", doc="If provided only comp and eval tracks with exactly matching reference and alternate alleles will be counted as overlapping", required=false) + private boolean requireStrictAlleleMatch = false; + // Variables private Set jexlExpressions = new TreeSet(); @@ -360,16 +363,16 @@ public class VariantEvalWalker extends RodWalker implements Tr if ( matchingComps.size() == 0 ) return null; - // find the comp which matches the alternate allele from eval + // find the comp which matches both the reference allele and alternate allele from eval Allele altEval = eval.getAlternateAlleles().size() == 0 ? null : eval.getAlternateAllele(0); for ( VariantContext comp : matchingComps ) { Allele altComp = comp.getAlternateAlleles().size() == 0 ? null : comp.getAlternateAllele(0); - if ( (altEval == null && altComp == null) || (altEval != null && altEval.equals(altComp)) ) + if ( (altEval == null && altComp == null) || (altEval != null && altEval.equals(altComp) && eval.getReference().equals(comp.getReference())) ) return comp; } - // if none match, just return the first one - return matchingComps.get(0); + // if none match, just return the first one unless we require a strict match + return (requireStrictAlleleMatch ? null : matchingComps.get(0)); } public Integer treeReduce(Integer lhs, Integer rhs) { return null; } From 981b78ea50708139cc3157ccff990fca6cb3e7e8 Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Mon, 12 Sep 2011 12:17:43 -0400 Subject: [PATCH 055/196] Changing the VQSR command line syntax back to the parsed tags approach. This cleans up the code and makes sure we won't be parsing the same rod file multiple times. I've tried to update the appropriate qscripts. --- .../variantrecalibration/TrainingSet.java | 76 ++++++++++++++++ .../VariantDataManager.java | 88 +++++++++---------- .../VariantRecalibrator.java | 66 ++++---------- ...ntRecalibrationWalkersIntegrationTest.java | 13 ++- .../MethodsDevelopmentCallingPipeline.scala | 10 +-- 5 files changed, 147 insertions(+), 106 deletions(-) create mode 100755 public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/TrainingSet.java diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/TrainingSet.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/TrainingSet.java new file mode 100755 index 000000000..5f688d001 --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/TrainingSet.java @@ -0,0 +1,76 @@ +/* + * Copyright (c) 2011 The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR + * THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.gatk.walkers.variantrecalibration; + +import org.apache.log4j.Logger; +import org.broadinstitute.sting.commandline.RodBinding; +import org.broadinstitute.sting.commandline.Tags; +import org.broadinstitute.sting.utils.variantcontext.VariantContext; + +/** + * Created by IntelliJ IDEA. + * User: rpoplin + * Date: 3/12/11 + */ + +public class TrainingSet { + + public RodBinding rodBinding; + public boolean isKnown = false; + public boolean isTraining = false; + public boolean isAntiTraining = false; + public boolean isTruth = false; + public boolean isConsensus = false; + public double prior = 0.0; + + protected final static Logger logger = Logger.getLogger(TrainingSet.class); + + public TrainingSet( final RodBinding rodBinding) { + this.rodBinding = rodBinding; + + final Tags tags = rodBinding.getTags(); + final String name = rodBinding.getName(); + + // Parse the tags to decide which tracks have which properties + if( tags != null ) { + isKnown = tags.containsKey("known") && tags.getValue("known").equals("true"); + isTraining = tags.containsKey("training") && tags.getValue("training").equals("true"); + isAntiTraining = tags.containsKey("bad") && tags.getValue("bad").equals("true"); + isTruth = tags.containsKey("truth") && tags.getValue("truth").equals("true"); + isConsensus = tags.containsKey("consensus") && tags.getValue("consensus").equals("true"); + prior = ( tags.containsKey("prior") ? Double.parseDouble(tags.getValue("prior")) : prior ); + } + + // Report back to the user which tracks were found and the properties that were detected + if( !isConsensus && !isAntiTraining ) { + logger.info( String.format( "Found %s track: \tKnown = %s \tTraining = %s \tTruth = %s \tPrior = Q%.1f", name, isKnown, isTraining, isTruth, prior) ); + } else if( isConsensus ) { + logger.info( String.format( "Found consensus track: %s", name) ); + } else { + logger.info( String.format( "Found bad sites training track: %s", name) ); + } + } +} diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantDataManager.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantDataManager.java index 429becfc7..e04bfab76 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantDataManager.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantDataManager.java @@ -51,10 +51,10 @@ public class VariantDataManager { private ExpandingArrayList data; private final double[] meanVector; private final double[] varianceVector; // this is really the standard deviation - public final ArrayList annotationKeys; + public final List annotationKeys; private final VariantRecalibratorArgumentCollection VRAC; protected final static Logger logger = Logger.getLogger(VariantDataManager.class); - + protected final List trainingSets; public VariantDataManager( final List annotationKeys, final VariantRecalibratorArgumentCollection VRAC ) { this.data = null; @@ -62,6 +62,7 @@ public class VariantDataManager { this.VRAC = VRAC; meanVector = new double[this.annotationKeys.size()]; varianceVector = new double[this.annotationKeys.size()]; + trainingSets = new ArrayList(); } public void setData( final ExpandingArrayList data ) { @@ -104,6 +105,31 @@ public class VariantDataManager { } } + public void addTrainingSet( final TrainingSet trainingSet ) { + trainingSets.add( trainingSet ); + } + + public boolean checkHasTrainingSet() { + for( final TrainingSet trainingSet : trainingSets ) { + if( trainingSet.isTraining ) { return true; } + } + return false; + } + + public boolean checkHasTruthSet() { + for( final TrainingSet trainingSet : trainingSets ) { + if( trainingSet.isTruth ) { return true; } + } + return false; + } + + public boolean checkHasKnownSet() { + for( final TrainingSet trainingSet : trainingSets ) { + if( trainingSet.isKnown ) { return true; } + } + return false; + } + public ExpandingArrayList getTrainingData() { final ExpandingArrayList trainingData = new ExpandingArrayList(); for( final VariantDatum datum : data ) { @@ -232,57 +258,35 @@ public class VariantDataManager { return value; } - public void parseTrainingSets( final RefMetaDataTracker tracker, final GenomeLoc genomeLoc, final VariantContext evalVC, final VariantDatum datum, final boolean TRUST_ALL_POLYMORPHIC, final HashMap rodToPriorMap, - final List> training, final List> truth, final List> known, final List> badSites, final List> resource) { + public void parseTrainingSets( final RefMetaDataTracker tracker, final GenomeLoc genomeLoc, final VariantContext evalVC, final VariantDatum datum, final boolean TRUST_ALL_POLYMORPHIC ) { datum.isKnown = false; datum.atTruthSite = false; datum.atTrainingSite = false; datum.atAntiTrainingSite = false; datum.prior = 2.0; - //BUGBUG: need to clean this up - - for( final RodBinding rod : training ) { - for( final VariantContext trainVC : tracker.getValues(rod, genomeLoc) ) { + for( final TrainingSet trainingSet : trainingSets ) { + for( final VariantContext trainVC : tracker.getValues(trainingSet.rodBinding, genomeLoc) ) { if( isValidVariant( evalVC, trainVC, TRUST_ALL_POLYMORPHIC ) ) { - datum.atTrainingSite = true; - datum.prior = Math.max( datum.prior, (rodToPriorMap.containsKey(rod.getName()) ? rodToPriorMap.get(rod.getName()) : 0.0) ); + datum.isKnown = datum.isKnown || trainingSet.isKnown; + datum.atTruthSite = datum.atTruthSite || trainingSet.isTruth; + datum.atTrainingSite = datum.atTrainingSite || trainingSet.isTraining; + datum.prior = Math.max( datum.prior, trainingSet.prior ); + datum.consensusCount += ( trainingSet.isConsensus ? 1 : 0 ); } - } - } - for( final RodBinding rod : truth ) { - for( final VariantContext trainVC : tracker.getValues(rod, genomeLoc) ) { - if( isValidVariant( evalVC, trainVC, TRUST_ALL_POLYMORPHIC ) ) { - datum.atTruthSite = true; - datum.prior = Math.max( datum.prior, (rodToPriorMap.containsKey(rod.getName()) ? rodToPriorMap.get(rod.getName()) : 0.0) ); - } - } - } - for( final RodBinding rod : known ) { - for( final VariantContext trainVC : tracker.getValues(rod, genomeLoc) ) { - if( isValidVariant( evalVC, trainVC, TRUST_ALL_POLYMORPHIC ) ) { - datum.isKnown = true; - datum.prior = Math.max( datum.prior, (rodToPriorMap.containsKey(rod.getName()) ? rodToPriorMap.get(rod.getName()) : 0.0) ); - } - } - } - for( final RodBinding rod : resource ) { - for( final VariantContext trainVC : tracker.getValues(rod, genomeLoc) ) { - if( isValidVariant( evalVC, trainVC, TRUST_ALL_POLYMORPHIC ) ) { - datum.prior = Math.max( datum.prior, (rodToPriorMap.containsKey(rod.getName()) ? rodToPriorMap.get(rod.getName()) : 0.0) ); - } - } - } - for( final RodBinding rod : badSites ) { - for( final VariantContext trainVC : tracker.getValues(rod, genomeLoc) ) { if( trainVC != null ) { - datum.atAntiTrainingSite = true; - datum.prior = Math.max( datum.prior, (rodToPriorMap.containsKey(rod.getName()) ? rodToPriorMap.get(rod.getName()) : 0.0) ); + datum.atAntiTrainingSite = datum.atAntiTrainingSite || trainingSet.isAntiTraining; } } } } + private boolean isValidVariant( final VariantContext evalVC, final VariantContext trainVC, final boolean TRUST_ALL_POLYMORPHIC) { + return trainVC != null && trainVC.isNotFiltered() && trainVC.isVariant() && + ((evalVC.isSNP() && trainVC.isSNP()) || ((evalVC.isIndel()||evalVC.isMixed()) && (trainVC.isIndel()||trainVC.isMixed()))) && + (TRUST_ALL_POLYMORPHIC || !trainVC.hasGenotypes() || trainVC.isPolymorphic()); + } + public void writeOutRecalibrationTable( final PrintStream RECAL_FILE ) { for( final VariantDatum datum : data ) { RECAL_FILE.println(String.format("%s,%d,%d,%.4f,%s", @@ -290,10 +294,4 @@ public class VariantDataManager { (datum.worstAnnotation != -1 ? annotationKeys.get(datum.worstAnnotation) : "NULL"))); } } - - private boolean isValidVariant( final VariantContext evalVC, final VariantContext trainVC, final boolean TRUST_ALL_POLYMORPHIC) { - return trainVC != null && trainVC.isNotFiltered() && trainVC.isVariant() && - ((evalVC.isSNP() && trainVC.isSNP()) || ((evalVC.isIndel()||evalVC.isMixed()) && (trainVC.isIndel()||trainVC.isMixed()))) && - (TRUST_ALL_POLYMORPHIC || !trainVC.hasGenotypes() || trainVC.isPolymorphic()); - } } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibrator.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibrator.java index df4faebd1..529d17285 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibrator.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibrator.java @@ -77,16 +77,15 @@ import java.util.*; *

* A tranches file which shows various metrics of the recalibration callset as a function of making several slices through the data. * - *

Examples

+ *

Example

*
  * java -Xmx4g -jar GenomeAnalysisTK.jar \
  *   -T VariantRecalibrator \
  *   -R reference/human_g1k_v37.fasta \
  *   -input NA12878.HiSeq.WGS.bwa.cleaned.raw.hg19.subset.vcf \
- *   -truth:prior=15.0 hapmap_3.3.b37.sites.vcf \
- *   -training:prior=15.0 hapmap_3.3.b37.sites.vcf \
- *   -training:prior=12.0 1000G_omni2.5.b37.sites.vcf \
- *   -known:prior=8.0 dbsnp_132.b37.vcf \
+ *   -resource:hapmap,known=false,training=true,truth=true,prior=15.0 hapmap_3.3.b37.sites.vcf \
+ *   -resource:omni,known=false,training=true,truth=false,prior=12.0 1000G_omni2.5.b37.sites.vcf \
+ *   -resource:dbsnp,known=true,training=false,truth=false,prior=8.0 dbsnp_132.b37.vcf \
  *   -an QD -an HaplotypeScore -an MQRankSum -an ReadPosRankSum -an FS -an MQ \
  *   -recalFile path/to/output.recal \
  *   -tranchesFile path/to/output.tranches \
@@ -112,34 +111,11 @@ public class VariantRecalibrator extends RodWalker> input;
 
     /**
-     * Input variants which are found to overlap with these training sites are used to build the Gaussian mixture model.
-     */
-    @Input(fullName="training", shortName = "training", doc="A list of training variants used to train the Gaussian mixture model", required=true)
-    public List> training;
-
-    /**
-     * When deciding where to set the cutoff in VQSLOD sensitivity to these truth sites is used.
-     * Typically one might want to say I dropped my threshold until I got back 99% of HapMap sites, for example.
-     */
-    @Input(fullName="truth", shortName = "truth", doc="A list of true variants to be used when deciding the truth sensitivity cut of the final callset", required=true)
-    public List> truth;
-
-    /**
-     * The known / novel status of a variant isn't used by the algorithm itself and is only used for reporting / display purposes.
-     * The output metrics are stratified by known status in order to aid in comparisons with other call sets.
-     */
-    @Input(fullName="known", shortName = "known", doc="A list of known variants to be used for metric comparison purposes", required=false)
-    public List> known = Collections.emptyList();
-
-    /**
-     * In addition to using the worst 3% of variants as compared to the Gaussian mixture model, we can also supplement the list
-     * with a database of known bad variants. Maybe these are loci which are frequently filtered out in many projects (centromere, for example).
-     */
-    @Input(fullName="badSites", shortName = "badSites", doc="A list of known bad variants used to supplement training the negative model", required=false)
-    public List> badSites = Collections.emptyList();
-
-    /**
-     * Any set of sites for which you would like to apply a prior probability but for which you don't want to use as training, truth, or known sites.
+     * Any set of VCF files to use as lists of training, truth, or known sites.
+     * Training - Input variants which are found to overlap with these training sites are used to build the Gaussian mixture model.
+     * Truth - When deciding where to set the cutoff in VQSLOD sensitivity to these truth sites is used.
+     * Known - The known / novel status of a variant isn't used by the algorithm itself and is only used for reporting / display purposes.
+     * Bad - In addition to using the worst 3% of variants as compared to the Gaussian mixture model, we can also supplement the list with a database of known bad variants.
      */
     @Input(fullName="resource", shortName = "resource", doc="A list of sites for which to apply a prior probability of being correct but which aren't used by the algorithm", required=false)
     public List> resource = Collections.emptyList();
@@ -205,7 +181,6 @@ public class VariantRecalibrator extends RodWalker ignoreInputFilterSet = new TreeSet();
     private final VariantRecalibratorEngine engine = new VariantRecalibratorEngine( VRAC );
-    private final HashMap rodToPriorMap = new HashMap();
 
     //---------------------------------------------------------------------------------------------------------------
     //
@@ -227,18 +202,15 @@ public class VariantRecalibrator extends RodWalker> allInputBindings = new ArrayList>();
-        allInputBindings.addAll(truth);
-        allInputBindings.addAll(training);
-        allInputBindings.addAll(known);
-        allInputBindings.addAll(badSites);
-        allInputBindings.addAll(resource);
-        for( final RodBinding rod : allInputBindings ) {
-            try {
-                rodToPriorMap.put(rod.getName(), (rod.getTags().containsKey("prior") ? Double.parseDouble(rod.getTags().getValue("prior")) : 0.0) );
-            } catch( NumberFormatException e ) {
-                throw new UserException.BadInput("Bad rod binding syntax. Prior key-value tag detected but isn't parsable. Expecting something like -training:prior=12.0 my.set.vcf");
-            }
+        for( RodBinding rod : resource ) {
+            dataManager.addTrainingSet( new TrainingSet( rod ) );
+        }
+
+        if( !dataManager.checkHasTrainingSet() ) {
+            throw new UserException.CommandLineException( "No training set found! Please provide sets of known polymorphic loci marked with the training=true ROD binding tag. For example, -B:hapmap,VCF,known=false,training=true,truth=true,prior=12.0 hapmapFile.vcf" );
+        }
+        if( !dataManager.checkHasTruthSet() ) {
+            throw new UserException.CommandLineException( "No truth set found! Please provide sets of known polymorphic loci marked with the truth=true ROD binding tag. For example, -B:hapmap,VCF,known=false,training=true,truth=true,prior=12.0 hapmapFile.vcf" );
         }
     }
 
@@ -270,7 +242,7 @@ public class VariantRecalibrator extends RodWalker= 10) {

From 9d9d438bc4b1804631126565ad15f4a69f649b0b Mon Sep 17 00:00:00 2001
From: David Roazen 
Date: Mon, 12 Sep 2011 12:28:23 -0400
Subject: [PATCH 056/196] New VariantAnnotatorEngine capability: an
 initialize() method for all annotation classes.

All VariantAnnotator annotation classes may now have an (optional) initialize() method
that gets called by the VariantAnnotatorEngine ONCE before annotation starts.

As an example of how this can be used, the SnpEff annotation class will use the initialize()
method to check whether the SnpEff version number stored in the vcf header is a supported
version, and also to verify that its required RodBinding is present.
---
 .../gatk/walkers/annotator/VariantAnnotator.java  |  3 +++
 .../walkers/annotator/VariantAnnotatorEngine.java | 15 +++++++++++----
 .../interfaces/AnnotatorCompatibleWalker.java     |  1 +
 .../interfaces/VariantAnnotatorAnnotation.java    |  9 +++------
 .../gatk/walkers/genotyper/UnifiedGenotyper.java  |  1 +
 5 files changed, 19 insertions(+), 10 deletions(-)

diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotator.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotator.java
index 96a400c68..971727727 100755
--- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotator.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotator.java
@@ -86,6 +86,7 @@ public class VariantAnnotator extends RodWalker implements Ann
 
     @ArgumentCollection
     protected StandardVariantContextInputArgumentCollection variantCollection = new StandardVariantContextInputArgumentCollection();
+    public RodBinding getVariantRodBinding() { return variantCollection.variants; }
 
     /**
      * The INFO field will be annotated with information on the most biologically-significant effect
@@ -208,6 +209,8 @@ public class VariantAnnotator extends RodWalker implements Ann
             engine = new VariantAnnotatorEngine(annotationGroupsToUse, annotationsToUse, this);
         engine.initializeExpressions(expressionsToUse);
 
+        engine.invokeAnnotationInitializationMethods();
+
         // setup the header fields
         // note that if any of the definitions conflict with our new ones, then we want to overwrite the old ones
         Set hInfo = new HashSet();
diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorEngine.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorEngine.java
index 01926a7f3..17830f129 100755
--- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorEngine.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorEngine.java
@@ -29,10 +29,7 @@ import org.broadinstitute.sting.commandline.RodBinding;
 import org.broadinstitute.sting.gatk.contexts.AlignmentContext;
 import org.broadinstitute.sting.gatk.contexts.ReferenceContext;
 import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker;
-import org.broadinstitute.sting.gatk.walkers.annotator.interfaces.AnnotationInterfaceManager;
-import org.broadinstitute.sting.gatk.walkers.annotator.interfaces.AnnotatorCompatibleWalker;
-import org.broadinstitute.sting.gatk.walkers.annotator.interfaces.GenotypeAnnotation;
-import org.broadinstitute.sting.gatk.walkers.annotator.interfaces.InfoFieldAnnotation;
+import org.broadinstitute.sting.gatk.walkers.annotator.interfaces.*;
 import org.broadinstitute.sting.utils.codecs.vcf.*;
 import org.broadinstitute.sting.utils.exceptions.UserException;
 import org.broadinstitute.sting.utils.variantcontext.Genotype;
@@ -113,6 +110,16 @@ public class VariantAnnotatorEngine {
             dbAnnotations.put(rod, rod.getName());
     }
 
+    public void invokeAnnotationInitializationMethods() {
+        for ( VariantAnnotatorAnnotation annotation : requestedInfoAnnotations ) {
+            annotation.initialize(walker);
+        }
+
+        for ( VariantAnnotatorAnnotation annotation : requestedGenotypeAnnotations ) {
+            annotation.initialize(walker);
+        }
+    }
+
     public Set getVCFAnnotationDescriptions() {
 
         Set descriptions = new HashSet();
diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/interfaces/AnnotatorCompatibleWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/interfaces/AnnotatorCompatibleWalker.java
index 20a2aea0e..9dda57ae3 100644
--- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/interfaces/AnnotatorCompatibleWalker.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/interfaces/AnnotatorCompatibleWalker.java
@@ -9,6 +9,7 @@ import java.util.List;
 public interface AnnotatorCompatibleWalker {
 
     // getter methods for various used bindings
+    public abstract RodBinding getVariantRodBinding();
     public abstract RodBinding getSnpEffRodBinding();
     public abstract RodBinding getDbsnpRodBinding();
     public abstract List> getCompRodBindings();
diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/interfaces/VariantAnnotatorAnnotation.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/interfaces/VariantAnnotatorAnnotation.java
index f33d61df9..9e48de9c3 100644
--- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/interfaces/VariantAnnotatorAnnotation.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/interfaces/VariantAnnotatorAnnotation.java
@@ -24,18 +24,15 @@
 
 package org.broadinstitute.sting.gatk.walkers.annotator.interfaces;
 
-import org.broadinstitute.sting.gatk.contexts.AlignmentContext;
-import org.broadinstitute.sting.gatk.contexts.ReferenceContext;
-import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker;
-import org.broadinstitute.sting.utils.codecs.vcf.VCFInfoHeaderLine;
 import org.broadinstitute.sting.utils.help.DocumentedGATKFeature;
-import org.broadinstitute.sting.utils.variantcontext.VariantContext;
 
 import java.util.List;
-import java.util.Map;
 
 @DocumentedGATKFeature(enable = true, groupName = "VariantAnnotator annotations", summary = "VariantAnnotator annotations")
 public abstract class VariantAnnotatorAnnotation {
     // return the INFO keys
     public abstract List getKeyNames();
+
+    // initialization method (optional for subclasses, and therefore non-abstract)
+    public void initialize ( AnnotatorCompatibleWalker walker ) { }
 }
\ No newline at end of file
diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyper.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyper.java
index d5dbdedd6..4ee2d5f44 100755
--- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyper.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyper.java
@@ -127,6 +127,7 @@ public class UnifiedGenotyper extends LocusWalker getDbsnpRodBinding() { return dbsnp.dbsnp; }
+    public RodBinding getVariantRodBinding() { return null; }
     public RodBinding getSnpEffRodBinding() { return null; }
     public List> getCompRodBindings() { return Collections.emptyList(); }
     public List> getResourceRodBindings() { return Collections.emptyList(); }

From ec4b30de6d8e9634ca0014c65215460bda066b64 Mon Sep 17 00:00:00 2001
From: Eric Banks 
Date: Mon, 12 Sep 2011 14:45:53 -0400
Subject: [PATCH 057/196] Patch from Laurent: typo leads to bad error messages.

---
 .../sting/commandline/ArgumentTypeDescriptor.java               | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java b/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java
index 16358d05f..5fff8f609 100644
--- a/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java
+++ b/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java
@@ -379,7 +379,7 @@ class RodBindingArgumentTypeDescriptor extends ArgumentTypeDescriptor {
                     }
 
                     if ( tribbleType == null )
-                        if ( ! file.canRead() | !! file.isFile() ) {
+                        if ( ! file.canRead() | ! file.isFile() ) {
                             throw new UserException.BadArgumentValue(name, "Couldn't read file to determine type: " + file);
                         } else {
                             throw new UserException.CommandLineException(

From 4e116760f4f8577bfaf677ed7711cfc599a6c90d Mon Sep 17 00:00:00 2001
From: Eric Banks 
Date: Mon, 12 Sep 2011 15:09:25 -0400
Subject: [PATCH 058/196] Removing some old cruft from the packages dir. 
 Updating AnalyzeCovariates to include all Covariates.

---
 public/packages/AnalyzeCovariates.xml         |  5 +---
 .../packages/FindContaminatingReadGroups.xml  | 10 -------
 public/packages/GATKResources.xml             | 20 --------------
 public/packages/IndelGenotyper.xml            | 11 --------
 .../packages/LocalRealignmentAroundIndels.xml | 12 ---------
 .../packages/QualityScoresRecalibration.xml   | 18 -------------
 public/packages/RMDIndexer.xml                | 13 ----------
 public/packages/UnifiedGenotyper.xml          | 11 --------
 public/packages/VariantAnnotator.xml          | 26 -------------------
 public/packages/VariantEval.xml               | 18 -------------
 public/packages/VariantFiltration.xml         | 13 ----------
 public/packages/VariantRecalibration.xml      | 12 ---------
 12 files changed, 1 insertion(+), 168 deletions(-)
 delete mode 100644 public/packages/FindContaminatingReadGroups.xml
 delete mode 100755 public/packages/GATKResources.xml
 delete mode 100644 public/packages/IndelGenotyper.xml
 delete mode 100644 public/packages/LocalRealignmentAroundIndels.xml
 delete mode 100644 public/packages/QualityScoresRecalibration.xml
 delete mode 100644 public/packages/RMDIndexer.xml
 delete mode 100644 public/packages/UnifiedGenotyper.xml
 delete mode 100644 public/packages/VariantAnnotator.xml
 delete mode 100644 public/packages/VariantEval.xml
 delete mode 100644 public/packages/VariantFiltration.xml
 delete mode 100644 public/packages/VariantRecalibration.xml

diff --git a/public/packages/AnalyzeCovariates.xml b/public/packages/AnalyzeCovariates.xml
index 7e31934df..a6675a63d 100644
--- a/public/packages/AnalyzeCovariates.xml
+++ b/public/packages/AnalyzeCovariates.xml
@@ -6,10 +6,7 @@
     
       
       
-      
-      
-      
-      
+      
     
   
   
diff --git a/public/packages/FindContaminatingReadGroups.xml b/public/packages/FindContaminatingReadGroups.xml
deleted file mode 100644
index 880f64a81..000000000
--- a/public/packages/FindContaminatingReadGroups.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-  
-    
-    
-    
-      
-    
-  
-
diff --git a/public/packages/GATKResources.xml b/public/packages/GATKResources.xml
deleted file mode 100755
index 87e6e0e50..000000000
--- a/public/packages/GATKResources.xml
+++ /dev/null
@@ -1,20 +0,0 @@
-
-
-  
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-    
-   
-
diff --git a/public/packages/IndelGenotyper.xml b/public/packages/IndelGenotyper.xml
deleted file mode 100644
index c9e3ae0f6..000000000
--- a/public/packages/IndelGenotyper.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-  
-    
-    
-    
-      
-      
-    
-  
-
diff --git a/public/packages/LocalRealignmentAroundIndels.xml b/public/packages/LocalRealignmentAroundIndels.xml
deleted file mode 100644
index 46960e69f..000000000
--- a/public/packages/LocalRealignmentAroundIndels.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-  
-    
-    
-    
-      
-      
-      
-    
-  
-
diff --git a/public/packages/QualityScoresRecalibration.xml b/public/packages/QualityScoresRecalibration.xml
deleted file mode 100644
index 95e8b7c63..000000000
--- a/public/packages/QualityScoresRecalibration.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-  
-    
-    
-    
-      
-      
-      
-      
-      
-      
-      
-      
-      
-    
-  
-
diff --git a/public/packages/RMDIndexer.xml b/public/packages/RMDIndexer.xml
deleted file mode 100644
index 5d40876de..000000000
--- a/public/packages/RMDIndexer.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-  
-    
-    
-    
-      
-      
-      
-      
-    
-  
-
diff --git a/public/packages/UnifiedGenotyper.xml b/public/packages/UnifiedGenotyper.xml
deleted file mode 100644
index 67a17640c..000000000
--- a/public/packages/UnifiedGenotyper.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
-  
-    
-    
-    
-      
-      
-    
-  
-
diff --git a/public/packages/VariantAnnotator.xml b/public/packages/VariantAnnotator.xml
deleted file mode 100644
index 88c0701f0..000000000
--- a/public/packages/VariantAnnotator.xml
+++ /dev/null
@@ -1,26 +0,0 @@
-
-
-  
-    
-    
-    
-      
-      
-      
-      
-      
-      
-      
-      
-      
-      
-      
-      
-      
-      
-      
-      
-      
-    
-  
-
diff --git a/public/packages/VariantEval.xml b/public/packages/VariantEval.xml
deleted file mode 100644
index 791066fb7..000000000
--- a/public/packages/VariantEval.xml
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-  
-    
-    
-    
-      
-      
-      
-      
-      
-      
-      
-      
-      
-    
-  
-
diff --git a/public/packages/VariantFiltration.xml b/public/packages/VariantFiltration.xml
deleted file mode 100644
index 48fa0ff37..000000000
--- a/public/packages/VariantFiltration.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-  
-    
-    
-    
-      
-      
-      
-      
-    
-  
-
diff --git a/public/packages/VariantRecalibration.xml b/public/packages/VariantRecalibration.xml
deleted file mode 100644
index 6fe6b1eff..000000000
--- a/public/packages/VariantRecalibration.xml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-  
-    
-    
-    
-      
-      
-      
-    
-  
-

From e63d9d8f8edeaab24ad3bff06a97cc15b14b0043 Mon Sep 17 00:00:00 2001
From: Matt Hanna 
Date: Mon, 12 Sep 2011 21:50:59 -0400
Subject: [PATCH 059/196] Mauricio pointed out to me that dynamic merging the
 unmapped regions of multiple BAMs ('-L unmapped' with a BAM list) was
 completely broken.  Sorry about this!  Fixed.

---
 .../gatk/datasources/reads/BAMScheduler.java  |  3 +-
 .../gatk/datasources/reads/GATKBAMIndex.java  | 39 +++++++++++++++++++
 .../datasources/reads/ReadShardStrategy.java  | 21 ++--------
 3 files changed, 45 insertions(+), 18 deletions(-)

diff --git a/public/java/src/org/broadinstitute/sting/gatk/datasources/reads/BAMScheduler.java b/public/java/src/org/broadinstitute/sting/gatk/datasources/reads/BAMScheduler.java
index 467aebac5..47eb55b28 100644
--- a/public/java/src/org/broadinstitute/sting/gatk/datasources/reads/BAMScheduler.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/datasources/reads/BAMScheduler.java
@@ -26,6 +26,7 @@ package org.broadinstitute.sting.gatk.datasources.reads;
 
 import net.sf.picard.util.PeekableIterator;
 import net.sf.samtools.GATKBAMFileSpan;
+import net.sf.samtools.GATKChunk;
 import org.broadinstitute.sting.utils.GenomeLoc;
 import org.broadinstitute.sting.utils.GenomeLocSortedSet;
 
@@ -84,7 +85,7 @@ public class BAMScheduler implements Iterator {
             if(currentLocus == GenomeLoc.UNMAPPED) {
                 nextFilePointer = new FilePointer(GenomeLoc.UNMAPPED);
                 for(SAMReaderID id: dataSource.getReaderIDs())
-                    nextFilePointer.addFileSpans(id,new GATKBAMFileSpan());
+                    nextFilePointer.addFileSpans(id,new GATKBAMFileSpan(new GATKChunk(indexFiles.get(id).getStartOfLastLinearBin(),Long.MAX_VALUE)));
                 currentLocus = null;
                 continue;
             }
diff --git a/public/java/src/org/broadinstitute/sting/gatk/datasources/reads/GATKBAMIndex.java b/public/java/src/org/broadinstitute/sting/gatk/datasources/reads/GATKBAMIndex.java
index 5d0c38b78..dc703ff23 100644
--- a/public/java/src/org/broadinstitute/sting/gatk/datasources/reads/GATKBAMIndex.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/datasources/reads/GATKBAMIndex.java
@@ -215,6 +215,45 @@ public class GATKBAMIndex {
         return (new GATKBin(bin).getBinNumber()-levelStart+1)*(BIN_GENOMIC_SPAN /levelSize);
     }
 
+    /**
+     * Use to get close to the unmapped reads at the end of a BAM file.
+     * @return The file offset of the first record in the last linear bin, or -1
+     * if there are no elements in linear bins (i.e. no mapped reads).
+     */
+    public long getStartOfLastLinearBin() {
+        openIndexFile();
+
+        seek(4);
+
+        final int sequenceCount = readInteger();
+        // Because no reads may align to the last sequence in the sequence dictionary,
+        // grab the last element of the linear index for each sequence, and return
+        // the last one from the last sequence that has one.
+        long lastLinearIndexPointer = -1;
+        for (int i = 0; i < sequenceCount; i++) {
+            // System.out.println("# Sequence TID: " + i);
+            final int nBins = readInteger();
+            // System.out.println("# nBins: " + nBins);
+            for (int j1 = 0; j1 < nBins; j1++) {
+                // Skip bin #
+                skipBytes(4);
+                final int nChunks = readInteger();
+                // Skip chunks
+                skipBytes(16 * nChunks);
+            }
+            final int nLinearBins = readInteger();
+            if (nLinearBins > 0) {
+                // Skip to last element of list of linear bins
+                skipBytes(8 * (nLinearBins - 1));
+                lastLinearIndexPointer = readLongs(1)[0];
+            }
+        }
+
+        closeIndexFile();
+
+        return lastLinearIndexPointer;
+    }
+
     /**
      * Gets the possible number of bins for a given reference sequence.
      * @return How many bins could possibly be used according to this indexing scheme to index a single contig.
diff --git a/public/java/src/org/broadinstitute/sting/gatk/datasources/reads/ReadShardStrategy.java b/public/java/src/org/broadinstitute/sting/gatk/datasources/reads/ReadShardStrategy.java
index c2235ec73..5ea75dbb0 100755
--- a/public/java/src/org/broadinstitute/sting/gatk/datasources/reads/ReadShardStrategy.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/datasources/reads/ReadShardStrategy.java
@@ -134,24 +134,11 @@ public class ReadShardStrategy implements ShardStrategy {
             Map selectedReaders = new HashMap();
             while(selectedReaders.size() == 0 && currentFilePointer != null) {
                 shardPosition = currentFilePointer.fileSpans;
+
                 for(SAMReaderID id: shardPosition.keySet()) {
-                    // If the region contains location information (in other words, it is not at
-                    // the start of the unmapped region), add the region.
-                    if(currentFilePointer.isRegionUnmapped) {
-                        // If the region is unmapped and no location data exists, add a null as an indicator to
-                        // start at the next unmapped region.
-                        if(!isIntoUnmappedRegion) {
-                            selectedReaders.put(id,null);
-                            isIntoUnmappedRegion = true;
-                        }
-                        else
-                            selectedReaders.put(id,position.get(id));
-                    }
-                    else {
-                        SAMFileSpan fileSpan = shardPosition.get(id).removeContentsBefore(position.get(id));
-                        if(!fileSpan.isEmpty())
-                            selectedReaders.put(id,fileSpan);
-                    }
+                    SAMFileSpan fileSpan = shardPosition.get(id).removeContentsBefore(position.get(id));
+                    if(!fileSpan.isEmpty())
+                        selectedReaders.put(id,fileSpan);
                 }
 
                 if(selectedReaders.size() > 0) {

From 2316b6aad3e81cc0cd88980acd73d716fd4cdb2d Mon Sep 17 00:00:00 2001
From: Mark DePristo 
Date: Mon, 12 Sep 2011 22:02:42 -0400
Subject: [PATCH 060/196] Trying to fix problems with S3 uploading behind
 firewalls

-- Cannot reproduce the very long waits reported by some users.
-- Fixed problem that exception might result in an undeleted file, which is now fixed with deleteOnExit()
---
 .../sting/gatk/phonehome/GATKRunReport.java               | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/public/java/src/org/broadinstitute/sting/gatk/phonehome/GATKRunReport.java b/public/java/src/org/broadinstitute/sting/gatk/phonehome/GATKRunReport.java
index 4d94130a8..70307380b 100644
--- a/public/java/src/org/broadinstitute/sting/gatk/phonehome/GATKRunReport.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/phonehome/GATKRunReport.java
@@ -293,15 +293,16 @@ public class GATKRunReport {
      * That is, postReport() is guarenteed not to fail for any reason.
      */
     private File postReportToLocalDisk(File rootDir) {
+        String filename = getID() + ".report.xml.gz";
+        File file = new File(rootDir, filename);
         try {
-            String filename = getID() + ".report.xml.gz";
-            File file = new File(rootDir, filename);
             postReportToFile(file);
             logger.debug("Wrote report to " + file);
             return file;
         } catch ( Exception e ) {
             // we catch everything, and no matter what eat the error
             exceptDuringRunReport("Couldn't read report file", e);
+            file.delete();
             return null;
         }
     }
@@ -312,6 +313,7 @@ public class GATKRunReport {
         File localFile = postReportToLocalDisk(new File("./"));
         logger.debug("Generating GATK report to AWS S3 based on local file " + localFile);
         if ( localFile != null ) { // we succeeded in creating the local file
+            localFile.deleteOnExit();
             try {
                 // stop us from printing the annoying, and meaningless, mime types warning
                 Logger mimeTypeLogger = Logger.getLogger(org.jets3t.service.utils.Mimetypes.class);
@@ -342,8 +344,6 @@ public class GATKRunReport {
                 exceptDuringRunReport("Couldn't calculate MD5", e);
             } catch ( IOException e ) {
                 exceptDuringRunReport("Couldn't read report file", e);
-            } finally {
-                localFile.delete();
             }
         }
     }

From edf29d0616c576ece9a99af23cd42a54feb83e87 Mon Sep 17 00:00:00 2001
From: Mark DePristo 
Date: Mon, 12 Sep 2011 22:16:52 -0400
Subject: [PATCH 062/196] Explicit info message about uploading S3 log

---
 .../org/broadinstitute/sting/gatk/phonehome/GATKRunReport.java   | 1 +
 1 file changed, 1 insertion(+)

diff --git a/public/java/src/org/broadinstitute/sting/gatk/phonehome/GATKRunReport.java b/public/java/src/org/broadinstitute/sting/gatk/phonehome/GATKRunReport.java
index 70307380b..5a7658031 100644
--- a/public/java/src/org/broadinstitute/sting/gatk/phonehome/GATKRunReport.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/phonehome/GATKRunReport.java
@@ -338,6 +338,7 @@ public class GATKRunReport {
                 //logger.info("Uploading " + localFile + " to AWS bucket");
                 S3Object s3Object = s3Service.putObject(REPORT_BUCKET_NAME, fileObject);
                 logger.debug("Uploaded to AWS: " + s3Object);
+                logger.info("Uploaded run statistics report to AWS S3");
             } catch ( S3ServiceException e ) {
                 exceptDuringRunReport("S3 exception occurred", e);
             } catch ( NoSuchAlgorithmException e ) {

From c6672f2397fd55de79caa05420fc0ff8201d0e4d Mon Sep 17 00:00:00 2001
From: Guillermo del Angel 
Date: Tue, 13 Sep 2011 16:57:37 -0400
Subject: [PATCH 063/196] Intermediate (but necessary) fix for Beagle walkers:
 if a marker is absent in the Beagle output files, but present in the input
 vcf, there's no reason why it should be omitted in the output vcf. Rather,
 the vc is written as is from the input vcf

---
 .../beagle/BeagleOutputToVCFWalker.java       | 19 +++++++------------
 .../walkers/beagle/BeagleIntegrationTest.java |  4 ++--
 2 files changed, 9 insertions(+), 14 deletions(-)

diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/beagle/BeagleOutputToVCFWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/beagle/BeagleOutputToVCFWalker.java
index 880dba5d0..7f6dabeec 100755
--- a/public/java/src/org/broadinstitute/sting/gatk/walkers/beagle/BeagleOutputToVCFWalker.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/beagle/BeagleOutputToVCFWalker.java
@@ -175,21 +175,16 @@ public class BeagleOutputToVCFWalker  extends RodWalker {
         }
 
         BeagleFeature beagleR2Feature = tracker.getFirstValue(beagleR2);
-        // ignore places where we don't have a variant
-        if ( beagleR2Feature == null )
-            return 0;
-
-
         BeagleFeature beagleProbsFeature = tracker.getFirstValue(beagleProbs);
-
-        // ignore places where we don't have a variant
-        if ( beagleProbsFeature == null )
-            return 0;
-
         BeagleFeature beaglePhasedFeature = tracker.getFirstValue(beaglePhased);
+
         // ignore places where we don't have a variant
-        if ( beaglePhasedFeature == null )
-            return 0;
+        if ( beagleR2Feature == null || beagleProbsFeature == null ||  beaglePhasedFeature == null)
+        {
+           vcfWriter.add(vc_input);
+           return 1;
+        }
+
 
         // get reference base for current position
         byte refByte = ref.getBase();
diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/beagle/BeagleIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/beagle/BeagleIntegrationTest.java
index 5f759fdbf..1a01ef8e8 100755
--- a/public/java/test/org/broadinstitute/sting/gatk/walkers/beagle/BeagleIntegrationTest.java
+++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/beagle/BeagleIntegrationTest.java
@@ -41,7 +41,7 @@ public class BeagleIntegrationTest extends WalkerTest {
                         "--beagleR2:BEAGLE " + beagleValidationDataLocation + "inttestbgl.r2 " +
                         "--beagleProbs:BEAGLE " + beagleValidationDataLocation + "inttestbgl.gprobs " +
                         "--beaglePhased:BEAGLE " + beagleValidationDataLocation + "inttestbgl.phased " +
-                        "-o %s -NO_HEADER", 1, Arrays.asList("3531451e84208264104040993889aaf4"));
+                        "-o %s -NO_HEADER", 1, Arrays.asList("b445d280fd8fee1eeb4aacb3f5a54847"));
         executeTest("test BeagleOutputToVCF", spec);
     }
    
@@ -72,7 +72,7 @@ public class BeagleIntegrationTest extends WalkerTest {
                 "--beagleR2:beagle /humgen/gsa-hpprojects/GATK/data/Validation_Data/EUR_beagle_in_test.r2 "+
                 "--beagleProbs:beagle /humgen/gsa-hpprojects/GATK/data/Validation_Data/EUR_beagle_in_test.gprobs.bgl "+
                 "--beaglePhased:beagle /humgen/gsa-hpprojects/GATK/data/Validation_Data/EUR_beagle_in_test.phased.bgl "+
-                "-L 20:1-70000 -o %s -NO_HEADER ",1,Arrays.asList("8dd6ec53994fb46c5c22af8535d22965"));
+                "-L 20:1-70000 -o %s -NO_HEADER ",1,Arrays.asList("51a57ea565176edd96d907906914b0ee"));
 
         executeTest("testBeagleChangesSitesToRef",spec);
     }

From 1213b2f8c67bb871c418f13b16728a702c877786 Mon Sep 17 00:00:00 2001
From: David Roazen 
Date: Fri, 9 Sep 2011 16:10:30 -0400
Subject: [PATCH 064/196] SnpEff 2.0.2 support

-Rewrote SnpEff support in VariantAnnotator to support the latest SnpEff release (version 2.0.2)
-Removed support for SnpEff 1.9.6 (and associated tribble codec)
-Will refuse to parse SnpEff output files produced by unsupported versions (or without a version tag)
-Correctly matches ref/alt alleles before annotating a record, unlike the previous version
-Correctly handles indels (again, unlike the previous version
---
 .../sting/gatk/walkers/annotator/SnpEff.java  | 482 ++++++++++++++----
 .../walkers/annotator/VariantAnnotator.java   |   9 +-
 .../annotator/VariantAnnotatorEngine.java     |  12 +-
 .../interfaces/AnnotatorCompatibleWalker.java |   5 +-
 .../VariantAnnotatorAnnotation.java           |   3 +-
 .../walkers/genotyper/UnifiedGenotyper.java   |   5 +-
 .../utils/codecs/snpEff/SnpEffCodec.java      | 282 ----------
 .../utils/codecs/snpEff/SnpEffConstants.java  | 115 -----
 .../utils/codecs/snpEff/SnpEffFeature.java    | 423 ---------------
 .../sting/utils/codecs/vcf/VCFHeader.java     |  12 +
 .../utils/variantcontext/VariantContext.java  |  22 +
 .../walkers/annotator/SnpEffUnitTest.java     |  86 ++++
 .../VariantAnnotatorIntegrationTest.java      |  21 +-
 .../codecs/snpEff/SnpEffCodecUnitTest.java    | 259 ----------
 14 files changed, 539 insertions(+), 1197 deletions(-)
 delete mode 100644 public/java/src/org/broadinstitute/sting/utils/codecs/snpEff/SnpEffCodec.java
 delete mode 100644 public/java/src/org/broadinstitute/sting/utils/codecs/snpEff/SnpEffConstants.java
 delete mode 100644 public/java/src/org/broadinstitute/sting/utils/codecs/snpEff/SnpEffFeature.java
 create mode 100644 public/java/test/org/broadinstitute/sting/gatk/walkers/annotator/SnpEffUnitTest.java
 delete mode 100644 public/java/test/org/broadinstitute/sting/utils/codecs/snpEff/SnpEffCodecUnitTest.java

diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SnpEff.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SnpEff.java
index 350c683c2..14abbca5b 100644
--- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SnpEff.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SnpEff.java
@@ -24,7 +24,9 @@
 
 package org.broadinstitute.sting.gatk.walkers.annotator;
 
+import org.apache.log4j.Logger;
 import org.broadinstitute.sting.commandline.RodBinding;
+import org.broadinstitute.sting.gatk.GenomeAnalysisEngine;
 import org.broadinstitute.sting.gatk.contexts.AlignmentContext;
 import org.broadinstitute.sting.gatk.contexts.ReferenceContext;
 import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker;
@@ -32,10 +34,7 @@ import org.broadinstitute.sting.gatk.walkers.annotator.interfaces.AnnotatorCompa
 import org.broadinstitute.sting.gatk.walkers.annotator.interfaces.ExperimentalAnnotation;
 import org.broadinstitute.sting.gatk.walkers.annotator.interfaces.InfoFieldAnnotation;
 import org.broadinstitute.sting.utils.Utils;
-import org.broadinstitute.sting.utils.codecs.snpEff.SnpEffConstants;
-import org.broadinstitute.sting.utils.codecs.snpEff.SnpEffFeature;
-import org.broadinstitute.sting.utils.codecs.vcf.VCFHeaderLineType;
-import org.broadinstitute.sting.utils.codecs.vcf.VCFInfoHeaderLine;
+import org.broadinstitute.sting.utils.codecs.vcf.*;
 import org.broadinstitute.sting.utils.exceptions.UserException;
 import org.broadinstitute.sting.utils.variantcontext.VariantContext;
 
@@ -46,134 +45,421 @@ import java.util.*;
  * (http://snpeff.sourceforge.net/).
  *
  * For each variant, chooses one of the effects of highest biological impact from the SnpEff
- * output file (which must be provided on the command line via --snpEffFile:SnpEff ),
+ * output file (which must be provided on the command line via --snpEffFile .vcf),
  * and adds annotations on that effect.
  *
- * The possible biological effects and their associated impacts are defined in the class:
- * org.broadinstitute.sting.utils.codecs.snpEff.SnpEffConstants
- *
  * @author David Roazen
  */
 public class SnpEff extends InfoFieldAnnotation implements ExperimentalAnnotation {
 
-    // SnpEff annotation key names:
-    public static final String GENE_ID_KEY = "GENE_ID";
-    public static final String GENE_NAME_KEY = "GENE_NAME";
-    public static final String TRANSCRIPT_ID_KEY = "TRANSCRIPT_ID";
-    public static final String EXON_ID_KEY = "EXON_ID";
-    public static final String EXON_RANK_KEY = "EXON_RANK";
-    public static final String WITHIN_NON_CODING_GENE_KEY = "WITHIN_NON_CODING_GENE";
-    public static final String EFFECT_KEY = "EFFECT";
-    public static final String EFFECT_IMPACT_KEY = "EFFECT_IMPACT";
-    public static final String EFFECT_EXTRA_INFORMATION_KEY = "EFFECT_EXTRA_INFORMATION";
-    public static final String OLD_NEW_AA_KEY = "OLD_NEW_AA";
-    public static final String OLD_NEW_CODON_KEY = "OLD_NEW_CODON";
-    public static final String CODON_NUM_KEY = "CODON_NUM";
-    public static final String CDS_SIZE_KEY = "CDS_SIZE";
+    private static Logger logger = Logger.getLogger(SnpEff.class);
+
+    // We refuse to parse SnpEff output files generated by unsupported versions, or
+    // lacking a SnpEff version number in the VCF header:
+    public static final String[] SUPPORTED_SNPEFF_VERSIONS = { "2.0.2" };
+    public static final String SNPEFF_VCF_HEADER_VERSION_LINE_KEY = "SnpEffVersion";
+
+    // SnpEff aggregates all effects (and effect metadata) together into a single INFO
+    // field annotation with the key EFF:
+    public static final String SNPEFF_INFO_FIELD_KEY = "EFF";
+    public static final String SNPEFF_EFFECT_METADATA_DELIMITER = "[()]";
+    public static final String SNPEFF_EFFECT_METADATA_SUBFIELD_DELIMITER = "\\|";
+
+    // Key names for the INFO field annotations we will add to each record, along
+    // with parsing-related information:
+    public enum InfoFieldKey {
+        EFF                   (-1),
+        EFF_IMPACT            (0),
+        EFF_CODON_CHANGE      (1),
+        EFF_AMINO_ACID_CHANGE (2),
+        EFF_GENE_NAME         (3),
+        EFF_GENE_BIOTYPE      (4),
+        EFF_TRANSCRIPT_ID     (6),
+        EFF_EXON_ID           (7);
+
+        // Index within the effect metadata subfields from the SnpEff EFF annotation
+        // where each key's associated value can be found during parsing.
+        private final int fieldIndex;
+
+        InfoFieldKey ( int fieldIndex ) {
+            this.fieldIndex = fieldIndex;
+        }
+
+        public int getFieldIndex() {
+            return fieldIndex;
+        }
+    }
+
+    // Possible SnpEff biological effects. All effect names found in the SnpEff input file
+    // are validated against this list.
+    public enum EffectType {
+        NONE,
+        CHROMOSOME,
+        INTERGENIC,
+        UPSTREAM,
+        UTR_5_PRIME,
+        UTR_5_DELETED,
+        START_GAINED,
+        SPLICE_SITE_ACCEPTOR,
+        SPLICE_SITE_DONOR,
+        START_LOST,
+        SYNONYMOUS_START,
+        NON_SYNONYMOUS_START,
+        CDS,
+        GENE,
+        TRANSCRIPT,
+        EXON,
+        EXON_DELETED,
+        NON_SYNONYMOUS_CODING,
+        SYNONYMOUS_CODING,
+        FRAME_SHIFT,
+        CODON_CHANGE,
+        CODON_INSERTION,
+        CODON_CHANGE_PLUS_CODON_INSERTION,
+        CODON_DELETION,
+        CODON_CHANGE_PLUS_CODON_DELETION,
+        STOP_GAINED,
+        SYNONYMOUS_STOP,
+        NON_SYNONYMOUS_STOP,
+        STOP_LOST,
+        INTRON,
+        UTR_3_PRIME,
+        UTR_3_DELETED,
+        DOWNSTREAM,
+        INTRON_CONSERVED,
+        INTERGENIC_CONSERVED,
+        REGULATION,
+        CUSTOM,
+        WITHIN_NON_CODING_GENE
+    }
+
+    // SnpEff labels each effect as either LOW, MODERATE, or HIGH impact.
+    public enum EffectImpact {
+        LOW       (1),
+        MODERATE  (2),
+        HIGH      (3);
+
+        private final int severityRating;
+
+        EffectImpact ( int severityRating ) {
+            this.severityRating = severityRating;
+        }
+
+        public boolean isHigherImpactThan ( EffectImpact other ) {
+            return this.severityRating > other.severityRating;
+        }
+    }
+
+    // SnpEff labels most effects as either CODING or NON_CODING, but sometimes omits this information.
+    public enum EffectCoding {
+        CODING,
+        NON_CODING,
+        UNKNOWN
+    }
+
+
+    public void initialize ( AnnotatorCompatibleWalker walker, GenomeAnalysisEngine toolkit ) {
+        validateRodBinding(walker.getSnpEffRodBinding());
+        checkSnpEffVersion(walker, toolkit);
+    }
 
     public Map annotate ( RefMetaDataTracker tracker, AnnotatorCompatibleWalker walker, ReferenceContext ref, Map stratifiedContexts, VariantContext vc ) {
-        RodBinding snpEffRodBinding = walker.getSnpEffRodBinding();
-        validateRodBinding(snpEffRodBinding);
+        RodBinding snpEffRodBinding = walker.getSnpEffRodBinding();
 
-        List features = tracker.getValues(snpEffRodBinding, ref.getLocus());
+        // Get only SnpEff records that start at this locus, not merely span it:
+        List snpEffRecords = tracker.getValues(snpEffRodBinding, ref.getLocus());
 
-        // Add only annotations for one of the most biologically-significant effects as defined in
-        // the SnpEffConstants class:
-        SnpEffFeature mostSignificantEffect = getMostSignificantEffect(features);
-
-        if ( mostSignificantEffect == null ) {
+        // Within this set, look for a SnpEff record whose ref/alt alleles match the record to annotate.
+        // If there is more than one such record, we only need to pick the first one, since the biological
+        // effects will be the same across all such records:
+        VariantContext matchingRecord = getMatchingSnpEffRecord(snpEffRecords, vc);
+        if ( matchingRecord == null ) {
             return null;
         }
 
-        return generateAnnotations(mostSignificantEffect);
+        // Parse the SnpEff INFO field annotation from the matching record into individual effect objects:
+        List effects = parseSnpEffRecord(matchingRecord);
+        if ( effects.size() == 0 ) {
+            return null;
+        }
+
+        // Add only annotations for one of the most biologically-significant effects from this set:
+        SnpEffEffect mostSignificantEffect = getMostSignificantEffect(effects);
+        return mostSignificantEffect.getAnnotations();
     }
 
-    private void validateRodBinding ( RodBinding snpEffRodBinding ) {
+    private void validateRodBinding ( RodBinding snpEffRodBinding ) {
         if ( snpEffRodBinding == null || ! snpEffRodBinding.isBound() ) {
-            throw new UserException("The SnpEff annotator requires that a SnpEff output file be provided " +
-                                    "as a rodbinding on the command line, but no SnpEff rodbinding was found.");
+            throw new UserException("The SnpEff annotator requires that a SnpEff VCF output file be provided " +
+                                    "as a rodbinding on the command line via the --snpEffFile option, but " +
+                                    "no SnpEff rodbinding was found.");
         }
     }
 
-    private SnpEffFeature getMostSignificantEffect ( List snpEffFeatures ) {
-        SnpEffFeature mostSignificantEffect = null;
+    private void checkSnpEffVersion ( AnnotatorCompatibleWalker walker, GenomeAnalysisEngine toolkit ) {
+        RodBinding snpEffRodBinding = walker.getSnpEffRodBinding();
 
-        for ( SnpEffFeature snpEffFeature : snpEffFeatures ) {
+        VCFHeader snpEffVCFHeader = VCFUtils.getVCFHeadersFromRods(toolkit, Arrays.asList(snpEffRodBinding.getName())).get(snpEffRodBinding.getName());
+        VCFHeaderLine snpEffVersionLine = snpEffVCFHeader.getOtherHeaderLine(SNPEFF_VCF_HEADER_VERSION_LINE_KEY);
+
+        if ( snpEffVersionLine == null || snpEffVersionLine.getValue() == null || snpEffVersionLine.getValue().trim().length() == 0 ) {
+            throw new UserException("Could not find a " + SNPEFF_VCF_HEADER_VERSION_LINE_KEY + " entry in the VCF header for the SnpEff " +
+                                    "input file, and so could not verify that the file was generated by a supported version of SnpEff (" +
+                                    Arrays.toString(SUPPORTED_SNPEFF_VERSIONS) + ")");
+        }
+
+        String snpEffVersionString = snpEffVersionLine.getValue().replaceAll("\"", "").split(" ")[0];
+
+        if ( ! isSupportedSnpEffVersion(snpEffVersionString) ) {
+            throw new UserException("The version of SnpEff used to generate the SnpEff input file (" + snpEffVersionString + ") " +
+                                    "is not currently supported by the GATK. Supported versions are: " + Arrays.toString(SUPPORTED_SNPEFF_VERSIONS));
+        }
+    }
+
+    private boolean isSupportedSnpEffVersion ( String versionString ) {
+        for ( String supportedVersion : SUPPORTED_SNPEFF_VERSIONS ) {
+            if ( supportedVersion.equals(versionString) ) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    private VariantContext getMatchingSnpEffRecord ( List snpEffRecords, VariantContext vc ) {
+        for ( VariantContext snpEffRecord : snpEffRecords ) {
+            if ( snpEffRecord.hasSameAlternateAllelesAs(vc) && snpEffRecord.getReference().equals(vc.getReference()) ) {
+                return snpEffRecord;
+            }
+        }
+
+        return null;
+    }
+
+    private List parseSnpEffRecord ( VariantContext snpEffRecord ) {
+        List parsedEffects = new ArrayList();
+
+        Object effectFieldValue = snpEffRecord.getAttribute(SNPEFF_INFO_FIELD_KEY);
+        List individualEffects;
+
+        // The VCF codec stores multi-valued fields as a List, and single-valued fields as a String.
+        // We can have either in the case of SnpEff, since there may be one or more than one effect in this record.
+        if ( effectFieldValue instanceof List ) {
+            individualEffects = (List)effectFieldValue;
+        }
+        else {
+            individualEffects = Arrays.asList((String)effectFieldValue);
+        }
+
+        for ( String effectString : individualEffects ) {
+            String[] effectNameAndMetadata = effectString.split(SNPEFF_EFFECT_METADATA_DELIMITER);
+
+            if ( effectNameAndMetadata.length != 2 ) {
+                logger.warn(String.format("Malformed SnpEff effect field at %s:%d, skipping: %s",
+                                          snpEffRecord.getChr(), snpEffRecord.getStart(), effectString));
+                continue;
+            }
+
+            String effectName = effectNameAndMetadata[0];
+            String[] effectMetadata = effectNameAndMetadata[1].split(SNPEFF_EFFECT_METADATA_SUBFIELD_DELIMITER, -1);
+
+            SnpEffEffect parsedEffect = new SnpEffEffect(effectName, effectMetadata);
+
+            if ( parsedEffect.isWellFormed() ) {
+                parsedEffects.add(parsedEffect);
+            }
+            else {
+                logger.warn(String.format("Skipping malformed SnpEff effect field at %s:%d. Error was: \"%s\". Field was: \"%s\"",
+                                          snpEffRecord.getChr(), snpEffRecord.getStart(), parsedEffect.getParseError(), effectString));
+            }
+        }
+
+        return parsedEffects;
+    }
+
+    private SnpEffEffect getMostSignificantEffect ( List effects ) {
+        SnpEffEffect mostSignificantEffect = null;
+
+        for ( SnpEffEffect effect : effects ) {
             if ( mostSignificantEffect == null ||
-                 snpEffFeature.isHigherImpactThan(mostSignificantEffect) ) {
+                 effect.isHigherImpactThan(mostSignificantEffect) ) {
 
-                mostSignificantEffect = snpEffFeature;
+                mostSignificantEffect = effect;
             }
         }
 
         return mostSignificantEffect;
     }
 
-    private Map generateAnnotations ( SnpEffFeature mostSignificantEffect ) {
-        Map annotations = new LinkedHashMap(Utils.optimumHashSize(getKeyNames().size()));
-
-        if ( mostSignificantEffect.hasGeneID() )
-            annotations.put(GENE_ID_KEY, mostSignificantEffect.getGeneID());
-        if ( mostSignificantEffect.hasGeneName() )
-            annotations.put(GENE_NAME_KEY, mostSignificantEffect.getGeneName());
-        if ( mostSignificantEffect.hasTranscriptID() )
-            annotations.put(TRANSCRIPT_ID_KEY, mostSignificantEffect.getTranscriptID());
-        if ( mostSignificantEffect.hasExonID() )
-            annotations.put(EXON_ID_KEY, mostSignificantEffect.getExonID());
-        if ( mostSignificantEffect.hasExonRank() )
-            annotations.put(EXON_RANK_KEY, Integer.toString(mostSignificantEffect.getExonRank()));
-        if ( mostSignificantEffect.isNonCodingGene() )
-            annotations.put(WITHIN_NON_CODING_GENE_KEY, null);
-
-        annotations.put(EFFECT_KEY, mostSignificantEffect.getEffect().toString());
-        annotations.put(EFFECT_IMPACT_KEY, mostSignificantEffect.getEffectImpact().toString());
-        if ( mostSignificantEffect.hasEffectExtraInformation() )
-            annotations.put(EFFECT_EXTRA_INFORMATION_KEY, mostSignificantEffect.getEffectExtraInformation());
-
-        if ( mostSignificantEffect.hasOldAndNewAA() )
-            annotations.put(OLD_NEW_AA_KEY, mostSignificantEffect.getOldAndNewAA());
-        if ( mostSignificantEffect.hasOldAndNewCodon() )
-            annotations.put(OLD_NEW_CODON_KEY, mostSignificantEffect.getOldAndNewCodon());
-        if ( mostSignificantEffect.hasCodonNum() )
-            annotations.put(CODON_NUM_KEY, Integer.toString(mostSignificantEffect.getCodonNum()));
-        if ( mostSignificantEffect.hasCdsSize() )
-            annotations.put(CDS_SIZE_KEY, Integer.toString(mostSignificantEffect.getCdsSize()));
-
-        return annotations;
-    }
-
     public List getKeyNames() {
-        return Arrays.asList( GENE_ID_KEY,
-                              GENE_NAME_KEY,
-                              TRANSCRIPT_ID_KEY,
-                              EXON_ID_KEY,
-                              EXON_RANK_KEY,
-                              WITHIN_NON_CODING_GENE_KEY,
-                              EFFECT_KEY,
-                              EFFECT_IMPACT_KEY,
-                              EFFECT_EXTRA_INFORMATION_KEY,
-                              OLD_NEW_AA_KEY,
-                              OLD_NEW_CODON_KEY,
-                              CODON_NUM_KEY,
-                              CDS_SIZE_KEY
+        return Arrays.asList( InfoFieldKey.EFF.toString(),
+                              InfoFieldKey.EFF_IMPACT.toString(),
+                              InfoFieldKey.EFF_CODON_CHANGE.toString(),
+                              InfoFieldKey.EFF_AMINO_ACID_CHANGE.toString(),
+                              InfoFieldKey.EFF_GENE_NAME.toString(),
+                              InfoFieldKey.EFF_GENE_BIOTYPE.toString(),
+                              InfoFieldKey.EFF_TRANSCRIPT_ID.toString(),
+                              InfoFieldKey.EFF_EXON_ID.toString()
                             );
     }
 
     public List getDescriptions() {
         return Arrays.asList(
-            new VCFInfoHeaderLine(GENE_ID_KEY,                  1, VCFHeaderLineType.String,  "Gene ID for the highest-impact effect resulting from the current variant"),
-            new VCFInfoHeaderLine(GENE_NAME_KEY,                1, VCFHeaderLineType.String,  "Gene name for the highest-impact effect resulting from the current variant"),
-            new VCFInfoHeaderLine(TRANSCRIPT_ID_KEY,            1, VCFHeaderLineType.String,  "Transcript ID for the highest-impact effect resulting from the current variant"),
-            new VCFInfoHeaderLine(EXON_ID_KEY,                  1, VCFHeaderLineType.String,  "Exon ID for the highest-impact effect resulting from the current variant"),
-            new VCFInfoHeaderLine(EXON_RANK_KEY,                1, VCFHeaderLineType.Integer, "Exon rank for the highest-impact effect resulting from the current variant"),
-            new VCFInfoHeaderLine(WITHIN_NON_CODING_GENE_KEY,   0, VCFHeaderLineType.Flag,    "If this flag is present, the highest-impact effect resulting from the current variant is within a non-coding gene"),
-            new VCFInfoHeaderLine(EFFECT_KEY,                   1, VCFHeaderLineType.String,  "The highest-impact effect resulting from the current variant (or one of the highest-impact effects, if there is a tie)"),
-            new VCFInfoHeaderLine(EFFECT_IMPACT_KEY,            1, VCFHeaderLineType.String,  "Impact of the highest-impact effect resulting from the current variant " + Arrays.toString(SnpEffConstants.EffectImpact.values())),
-            new VCFInfoHeaderLine(EFFECT_EXTRA_INFORMATION_KEY, 1, VCFHeaderLineType.String,  "Additional information about the highest-impact effect resulting from the current variant"),
-            new VCFInfoHeaderLine(OLD_NEW_AA_KEY,               1, VCFHeaderLineType.String,  "Old/New amino acid for the highest-impact effect resulting from the current variant"),
-            new VCFInfoHeaderLine(OLD_NEW_CODON_KEY,            1, VCFHeaderLineType.String,  "Old/New codon for the highest-impact effect resulting from the current variant"),
-            new VCFInfoHeaderLine(CODON_NUM_KEY,                1, VCFHeaderLineType.Integer, "Codon number for the highest-impact effect resulting from the current variant"),
-            new VCFInfoHeaderLine(CDS_SIZE_KEY,                 1, VCFHeaderLineType.Integer, "CDS size for the highest-impact effect resulting from the current variant")
+            new VCFInfoHeaderLine(InfoFieldKey.EFF.toString(),                   1, VCFHeaderLineType.String,  "The highest-impact effect resulting from the current variant (or one of the highest-impact effects, if there is a tie)"),
+            new VCFInfoHeaderLine(InfoFieldKey.EFF_IMPACT.toString(),            1, VCFHeaderLineType.String,  "Impact of the highest-impact effect resulting from the current variant " + Arrays.toString(EffectImpact.values())),
+            new VCFInfoHeaderLine(InfoFieldKey.EFF_CODON_CHANGE.toString(),      1, VCFHeaderLineType.String,  "Old/New codon for the highest-impact effect resulting from the current variant"),
+            new VCFInfoHeaderLine(InfoFieldKey.EFF_AMINO_ACID_CHANGE.toString(), 1, VCFHeaderLineType.String,  "Old/New amino acid for the highest-impact effect resulting from the current variant"),
+            new VCFInfoHeaderLine(InfoFieldKey.EFF_GENE_NAME.toString(),         1, VCFHeaderLineType.String,  "Gene name for the highest-impact effect resulting from the current variant"),
+            new VCFInfoHeaderLine(InfoFieldKey.EFF_GENE_BIOTYPE.toString(),      1, VCFHeaderLineType.String,  "Gene biotype for the highest-impact effect resulting from the current variant"),
+            new VCFInfoHeaderLine(InfoFieldKey.EFF_TRANSCRIPT_ID.toString(),     1, VCFHeaderLineType.String,  "Transcript ID for the highest-impact effect resulting from the current variant"),
+            new VCFInfoHeaderLine(InfoFieldKey.EFF_EXON_ID.toString(),           1, VCFHeaderLineType.String,  "Exon ID for the highest-impact effect resulting from the current variant")
         );
     }
+
+    /**
+     * Helper class to parse, validate, and store a single SnpEff effect and its metadata.
+     */
+    protected static class SnpEffEffect {
+        private EffectType effect;
+        private EffectImpact impact;
+        private String codonChange;
+        private String aminoAcidChange;
+        private String geneName;
+        private String geneBiotype;
+        private EffectCoding coding;
+        private String transcriptID;
+        private String exonID;
+
+        private String parseError = null;
+        private boolean isWellFormed = true;
+
+        private static final int EXPECTED_NUMBER_OF_METADATA_FIELDS = 8;
+        private static final int NUMBER_OF_METADATA_FIELDS_UPON_WARNING = 9;
+        private static final int NUMBER_OF_METADATA_FIELDS_UPON_ERROR = 10;
+
+        // Note that contrary to the description for the EFF field layout that SnpEff adds to the VCF header,
+        // errors come after warnings, not vice versa:
+        private static final int SNPEFF_WARNING_FIELD_INDEX = NUMBER_OF_METADATA_FIELDS_UPON_WARNING - 1;
+        private static final int SNPEFF_ERROR_FIELD_INDEX = NUMBER_OF_METADATA_FIELDS_UPON_ERROR - 1;
+
+        private static final int SNPEFF_CODING_FIELD_INDEX = 5;
+
+        public SnpEffEffect ( String effectName, String[] effectMetadata ) {
+            parseEffectName(effectName);
+            parseEffectMetadata(effectMetadata);
+        }
+
+        private void parseEffectName ( String effectName ) {
+            try {
+                effect = EffectType.valueOf(effectName);
+            }
+            catch ( IllegalArgumentException e ) {
+                parseError(String.format("%s is not a recognized effect type", effectName));
+            }
+        }
+
+        private void parseEffectMetadata ( String[] effectMetadata ) {
+            if ( effectMetadata.length != EXPECTED_NUMBER_OF_METADATA_FIELDS ) {
+                if ( effectMetadata.length == NUMBER_OF_METADATA_FIELDS_UPON_WARNING ) {
+                    parseError(String.format("SnpEff issued the following warning: %s", effectMetadata[SNPEFF_WARNING_FIELD_INDEX]));
+                }
+                else if ( effectMetadata.length == NUMBER_OF_METADATA_FIELDS_UPON_ERROR ) {
+                    parseError(String.format("SnpEff issued the following error: %s", effectMetadata[SNPEFF_ERROR_FIELD_INDEX]));
+                }
+                else {
+                    parseError(String.format("Wrong number of effect metadata fields. Expected %d but found %d",
+                                             EXPECTED_NUMBER_OF_METADATA_FIELDS, effectMetadata.length));
+                }
+
+                return;
+            }
+
+            try {
+                impact = EffectImpact.valueOf(effectMetadata[InfoFieldKey.EFF_IMPACT.getFieldIndex()]);
+            }
+            catch ( IllegalArgumentException e ) {
+                parseError(String.format("Unrecognized value for effect impact: %s", effectMetadata[InfoFieldKey.EFF_IMPACT.getFieldIndex()]));
+            }
+
+            codonChange = effectMetadata[InfoFieldKey.EFF_CODON_CHANGE.getFieldIndex()];
+            aminoAcidChange = effectMetadata[InfoFieldKey.EFF_AMINO_ACID_CHANGE.getFieldIndex()];
+            geneName = effectMetadata[InfoFieldKey.EFF_GENE_NAME.getFieldIndex()];
+            geneBiotype = effectMetadata[InfoFieldKey.EFF_GENE_BIOTYPE.getFieldIndex()];
+
+            if ( effectMetadata[SNPEFF_CODING_FIELD_INDEX].trim().length() > 0 ) {
+                try {
+                    coding = EffectCoding.valueOf(effectMetadata[SNPEFF_CODING_FIELD_INDEX]);
+                }
+                catch ( IllegalArgumentException e ) {
+                    parseError(String.format("Unrecognized value for effect coding: %s", effectMetadata[SNPEFF_CODING_FIELD_INDEX]));
+                }
+            }
+            else {
+                coding = EffectCoding.UNKNOWN;
+            }
+
+            transcriptID = effectMetadata[InfoFieldKey.EFF_TRANSCRIPT_ID.getFieldIndex()];
+            exonID = effectMetadata[InfoFieldKey.EFF_EXON_ID.getFieldIndex()];
+        }
+
+        private void parseError ( String message ) {
+            isWellFormed = false;
+
+            // Cache only the first error encountered:
+            if ( parseError == null ) {
+                parseError = message;
+            }
+        }
+
+        public boolean isWellFormed() {
+            return isWellFormed;
+        }
+
+        public String getParseError() {
+            return parseError == null ? "" : parseError;
+        }
+
+        public boolean isCoding() {
+            return coding == EffectCoding.CODING;
+        }
+
+        public boolean isHigherImpactThan ( SnpEffEffect other ) {
+            // If one effect is within a coding gene and the other is not, the effect that is
+            // within the coding gene has higher impact:
+
+            if ( isCoding() && ! other.isCoding() ) {
+                return true;
+            }
+            else if ( ! isCoding() && other.isCoding() ) {
+                return false;
+            }
+
+            // Otherwise, both effects are either in or not in a coding gene, so we compare the impacts
+            // of the effects themselves:
+
+            return impact.isHigherImpactThan(other.impact);
+        }
+
+        public Map getAnnotations() {
+            Map annotations = new LinkedHashMap(Utils.optimumHashSize(InfoFieldKey.values().length));
+
+            addAnnotation(annotations, InfoFieldKey.EFF.toString(), effect.toString());
+            addAnnotation(annotations, InfoFieldKey.EFF_IMPACT.toString(), impact.toString());
+            addAnnotation(annotations, InfoFieldKey.EFF_CODON_CHANGE.toString(), codonChange);
+            addAnnotation(annotations, InfoFieldKey.EFF_AMINO_ACID_CHANGE.toString(), aminoAcidChange);
+            addAnnotation(annotations, InfoFieldKey.EFF_GENE_NAME.toString(), geneName);
+            addAnnotation(annotations, InfoFieldKey.EFF_GENE_BIOTYPE.toString(), geneBiotype);
+            addAnnotation(annotations, InfoFieldKey.EFF_TRANSCRIPT_ID.toString(), transcriptID);
+            addAnnotation(annotations, InfoFieldKey.EFF_EXON_ID.toString(), exonID);
+
+            return annotations;
+        }
+
+        private void addAnnotation ( Map annotations, String keyName, String keyValue ) {
+            // Only add annotations for keys associated with non-empty values:
+            if ( keyValue != null && keyValue.trim().length() > 0 ) {
+                annotations.put(keyName, keyValue);
+            }
+        }
+    }
 }
diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotator.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotator.java
index 971727727..fb3dbc3cf 100755
--- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotator.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotator.java
@@ -40,7 +40,6 @@ import org.broadinstitute.sting.gatk.walkers.annotator.interfaces.InfoFieldAnnot
 import org.broadinstitute.sting.utils.BaseUtils;
 import org.broadinstitute.sting.utils.SampleUtils;
 import org.broadinstitute.sting.utils.classloader.PluginManager;
-import org.broadinstitute.sting.utils.codecs.snpEff.SnpEffFeature;
 import org.broadinstitute.sting.utils.codecs.vcf.*;
 import org.broadinstitute.sting.utils.variantcontext.VariantContext;
 import org.broadinstitute.sting.utils.variantcontext.VariantContextUtils;
@@ -93,8 +92,8 @@ public class VariantAnnotator extends RodWalker implements Ann
      * listed in the SnpEff output file for each variant.
      */
     @Input(fullName="snpEffFile", shortName = "snpEffFile", doc="A SnpEff output file from which to add annotations", required=false)
-    public RodBinding snpEffFile;
-    public RodBinding getSnpEffRodBinding() { return snpEffFile; }
+    public RodBinding snpEffFile;
+    public RodBinding getSnpEffRodBinding() { return snpEffFile; }
 
     /**
       * rsIDs from this file are used to populate the ID column of the output.  Also, the DB INFO flag will be set when appropriate.
@@ -204,9 +203,9 @@ public class VariantAnnotator extends RodWalker implements Ann
         }
 
         if ( USE_ALL_ANNOTATIONS )
-            engine = new VariantAnnotatorEngine(this);
+            engine = new VariantAnnotatorEngine(this, getToolkit());
         else
-            engine = new VariantAnnotatorEngine(annotationGroupsToUse, annotationsToUse, this);
+            engine = new VariantAnnotatorEngine(annotationGroupsToUse, annotationsToUse, this, getToolkit());
         engine.initializeExpressions(expressionsToUse);
 
         engine.invokeAnnotationInitializationMethods();
diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorEngine.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorEngine.java
index 17830f129..68cd07803 100755
--- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorEngine.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorEngine.java
@@ -26,6 +26,7 @@
 package org.broadinstitute.sting.gatk.walkers.annotator;
 
 import org.broadinstitute.sting.commandline.RodBinding;
+import org.broadinstitute.sting.gatk.GenomeAnalysisEngine;
 import org.broadinstitute.sting.gatk.contexts.AlignmentContext;
 import org.broadinstitute.sting.gatk.contexts.ReferenceContext;
 import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker;
@@ -46,6 +47,7 @@ public class VariantAnnotatorEngine {
 
     private HashMap, String> dbAnnotations = new HashMap, String>();
     private AnnotatorCompatibleWalker walker;
+    private GenomeAnalysisEngine toolkit;
 
     private static class VAExpression {
 
@@ -71,16 +73,18 @@ public class VariantAnnotatorEngine {
     }
 
     // use this constructor if you want all possible annotations
-    public VariantAnnotatorEngine(AnnotatorCompatibleWalker walker) {
+    public VariantAnnotatorEngine(AnnotatorCompatibleWalker walker, GenomeAnalysisEngine toolkit) {
         this.walker = walker;
+        this.toolkit = toolkit;
         requestedInfoAnnotations = AnnotationInterfaceManager.createAllInfoFieldAnnotations();
         requestedGenotypeAnnotations = AnnotationInterfaceManager.createAllGenotypeAnnotations();
         initializeDBs();
     }
 
     // use this constructor if you want to select specific annotations (and/or interfaces)
-    public VariantAnnotatorEngine(List annotationGroupsToUse, List annotationsToUse, AnnotatorCompatibleWalker walker) {
+    public VariantAnnotatorEngine(List annotationGroupsToUse, List annotationsToUse, AnnotatorCompatibleWalker walker, GenomeAnalysisEngine toolkit) {
         this.walker = walker;
+        this.toolkit = toolkit;
         initializeAnnotations(annotationGroupsToUse, annotationsToUse);
         initializeDBs();
     }
@@ -112,11 +116,11 @@ public class VariantAnnotatorEngine {
 
     public void invokeAnnotationInitializationMethods() {
         for ( VariantAnnotatorAnnotation annotation : requestedInfoAnnotations ) {
-            annotation.initialize(walker);
+            annotation.initialize(walker, toolkit);
         }
 
         for ( VariantAnnotatorAnnotation annotation : requestedGenotypeAnnotations ) {
-            annotation.initialize(walker);
+            annotation.initialize(walker, toolkit);
         }
     }
 
diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/interfaces/AnnotatorCompatibleWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/interfaces/AnnotatorCompatibleWalker.java
index 9dda57ae3..7200f841b 100644
--- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/interfaces/AnnotatorCompatibleWalker.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/interfaces/AnnotatorCompatibleWalker.java
@@ -1,7 +1,6 @@
 package org.broadinstitute.sting.gatk.walkers.annotator.interfaces;
 
 import org.broadinstitute.sting.commandline.RodBinding;
-import org.broadinstitute.sting.utils.codecs.snpEff.SnpEffFeature;
 import org.broadinstitute.sting.utils.variantcontext.VariantContext;
 
 import java.util.List;
@@ -10,8 +9,8 @@ public interface AnnotatorCompatibleWalker {
 
     // getter methods for various used bindings
     public abstract RodBinding getVariantRodBinding();
-    public abstract RodBinding getSnpEffRodBinding();
+    public abstract RodBinding getSnpEffRodBinding();
     public abstract RodBinding getDbsnpRodBinding();
     public abstract List> getCompRodBindings();
     public abstract List> getResourceRodBindings();
-}
\ No newline at end of file
+}
diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/interfaces/VariantAnnotatorAnnotation.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/interfaces/VariantAnnotatorAnnotation.java
index 9e48de9c3..160a3d258 100644
--- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/interfaces/VariantAnnotatorAnnotation.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/interfaces/VariantAnnotatorAnnotation.java
@@ -24,6 +24,7 @@
 
 package org.broadinstitute.sting.gatk.walkers.annotator.interfaces;
 
+import org.broadinstitute.sting.gatk.GenomeAnalysisEngine;
 import org.broadinstitute.sting.utils.help.DocumentedGATKFeature;
 
 import java.util.List;
@@ -34,5 +35,5 @@ public abstract class VariantAnnotatorAnnotation {
     public abstract List getKeyNames();
 
     // initialization method (optional for subclasses, and therefore non-abstract)
-    public void initialize ( AnnotatorCompatibleWalker walker ) { }
+    public void initialize ( AnnotatorCompatibleWalker walker, GenomeAnalysisEngine toolkit ) { }
 }
\ No newline at end of file
diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyper.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyper.java
index 4ee2d5f44..428f97e2a 100755
--- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyper.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyper.java
@@ -38,7 +38,6 @@ import org.broadinstitute.sting.gatk.walkers.annotator.VariantAnnotatorEngine;
 import org.broadinstitute.sting.gatk.walkers.annotator.interfaces.AnnotatorCompatibleWalker;
 import org.broadinstitute.sting.utils.SampleUtils;
 import org.broadinstitute.sting.utils.baq.BAQ;
-import org.broadinstitute.sting.utils.codecs.snpEff.SnpEffFeature;
 import org.broadinstitute.sting.utils.codecs.vcf.*;
 import org.broadinstitute.sting.utils.variantcontext.VariantContext;
 
@@ -128,7 +127,7 @@ public class UnifiedGenotyper extends LocusWalker getDbsnpRodBinding() { return dbsnp.dbsnp; }
     public RodBinding getVariantRodBinding() { return null; }
-    public RodBinding getSnpEffRodBinding() { return null; }
+    public RodBinding getSnpEffRodBinding() { return null; }
     public List> getCompRodBindings() { return Collections.emptyList(); }
     public List> getResourceRodBindings() { return Collections.emptyList(); }
 
@@ -211,7 +210,7 @@ public class UnifiedGenotyper extends LocusWalker
- * This format has 23 tab-delimited fields:
- *
- * 
- * Chromosome
- * Position
- * Reference
- * Change
- * Change Type: {SNP, MNP, INS, DEL}
- * Zygosity: {Hom, Het}
- * Quality
- * Coverage
- * Warnings
- * Gene ID
- * Gene Name
- * Bio Type
- * Transcript ID
- * Exon ID
- * Exon Rank
- * Effect
- * Old/New Amino Acid
- * Old/New Codon
- * Codon Num
- * CDS Size
- * Codons Around
- * Amino Acids Around
- * Custom Interval ID
- * 
- * Note that we treat all except the Chromosome, Position, and Effect fields as optional. - *

- * - *

- * See also: @see SNPEff project page - *

- * - * @author David Roazen - * @since 2011 - */ -public class SnpEffCodec implements FeatureCodec, SelfScopingFeatureCodec { - - public static final int EXPECTED_NUMBER_OF_FIELDS = 23; - public static final String FIELD_DELIMITER_PATTERN = "\\t"; - public static final String EFFECT_FIELD_DELIMITER_PATTERN = "[,:]"; - public static final String HEADER_LINE_START = "# "; - public static final String[] HEADER_FIELD_NAMES = { "Chromo", - "Position", - "Reference", - "Change", - "Change type", - "Homozygous", - "Quality", - "Coverage", - "Warnings", - "Gene_ID", - "Gene_name", - "Bio_type", - "Trancript_ID", // yes, this is how it's spelled in the SnpEff output - "Exon_ID", - "Exon_Rank", - "Effect", - "old_AA/new_AA", - "Old_codon/New_codon", - "Codon_Num(CDS)", - "CDS_size", - "Codons around", - "AAs around", - "Custom_interval_ID" - }; - - // The "Chromo", "Position", and "Effect" fields are required to be non-empty in every SnpEff output line: - public static final int[] REQUIRED_FIELDS = { 0, 1, 15 }; - - public static final String NON_CODING_GENE_FLAG = "WITHIN_NON_CODING_GENE"; - - - public Feature decodeLoc ( String line ) { - return decode(line); - } - - public Feature decode ( String line ) { - String[] tokens = line.split(FIELD_DELIMITER_PATTERN, -1); - - if ( tokens.length != EXPECTED_NUMBER_OF_FIELDS ) { - throw new TribbleException.InvalidDecodeLine("Line does not have the expected (" + EXPECTED_NUMBER_OF_FIELDS + - ") number of fields: found " + tokens.length + " fields.", line); - } - - try { - trimAllFields(tokens); - checkForRequiredFields(tokens, line); - - String contig = tokens[0]; - long position = Long.parseLong(tokens[1]); - - String reference = tokens[2].isEmpty() ? null : tokens[2]; - String change = tokens[3].isEmpty() ? null : tokens[3]; - ChangeType changeType = tokens[4].isEmpty() ? null : ChangeType.valueOf(tokens[4]); - Zygosity zygosity = tokens[5].isEmpty() ? null : Zygosity.valueOf(tokens[5]); - Double quality = tokens[6].isEmpty() ? null : Double.parseDouble(tokens[6]); - Long coverage = tokens[7].isEmpty() ? null : Long.parseLong(tokens[7]); - String warnings = tokens[8].isEmpty() ? null : tokens[8]; - String geneID = tokens[9].isEmpty() ? null : tokens[9]; - String geneName = tokens[10].isEmpty() ? null : tokens[10]; - String bioType = tokens[11].isEmpty() ? null : tokens[11]; - String transcriptID = tokens[12].isEmpty() ? null : tokens[12]; - String exonID = tokens[13].isEmpty() ? null : tokens[13]; - Integer exonRank = tokens[14].isEmpty() ? null : Integer.parseInt(tokens[14]); - - boolean isNonCodingGene = isNonCodingGene(tokens[15]); - - // Split the effect field into three subfields if the WITHIN_NON_CODING_GENE flag is present, - // otherwise split it into two subfields. We need this limit to prevent the extra effect-related information - // in the final field (when present) from being inappropriately tokenized: - - int effectFieldTokenLimit = isNonCodingGene ? 3 : 2; - String[] effectFieldTokens = tokens[15].split(EFFECT_FIELD_DELIMITER_PATTERN, effectFieldTokenLimit); - EffectType effect = parseEffect(effectFieldTokens, isNonCodingGene); - String effectExtraInformation = parseEffectExtraInformation(effectFieldTokens, isNonCodingGene); - - String oldAndNewAA = tokens[16].isEmpty() ? null : tokens[16]; - String oldAndNewCodon = tokens[17].isEmpty() ? null : tokens[17]; - Integer codonNum = tokens[18].isEmpty() ? null : Integer.parseInt(tokens[18]); - Integer cdsSize = tokens[19].isEmpty() ? null : Integer.parseInt(tokens[19]); - String codonsAround = tokens[20].isEmpty() ? null : tokens[20]; - String aasAround = tokens[21].isEmpty() ? null : tokens[21]; - String customIntervalID = tokens[22].isEmpty() ? null : tokens[22]; - - return new SnpEffFeature(contig, position, reference, change, changeType, zygosity, quality, coverage, - warnings, geneID, geneName, bioType, transcriptID, exonID, exonRank, isNonCodingGene, - effect, effectExtraInformation, oldAndNewAA, oldAndNewCodon, codonNum, cdsSize, - codonsAround, aasAround, customIntervalID); - } - catch ( NumberFormatException e ) { - throw new TribbleException.InvalidDecodeLine("Error parsing a numeric field : " + e.getMessage(), line); - } - catch ( IllegalArgumentException e ) { - throw new TribbleException.InvalidDecodeLine("Illegal value in field: " + e.getMessage(), line); - } - } - - private void trimAllFields ( String[] tokens ) { - for ( int i = 0; i < tokens.length; i++ ) { - tokens[i] = tokens[i].trim(); - } - } - - private void checkForRequiredFields ( String[] tokens, String line ) { - for ( int requiredFieldIndex : REQUIRED_FIELDS ) { - if ( tokens[requiredFieldIndex].isEmpty() ) { - throw new TribbleException.InvalidDecodeLine("Line is missing required field \"" + - HEADER_FIELD_NAMES[requiredFieldIndex] + "\"", - line); - } - } - } - - private boolean isNonCodingGene ( String effectField ) { - return effectField.startsWith(NON_CODING_GENE_FLAG); - } - - private EffectType parseEffect ( String[] effectFieldTokens, boolean isNonCodingGene ) { - String effectName = ""; - - // If there's a WITHIN_NON_CODING_GENE flag, the effect name will be in the second subfield, - // otherwise it will be in the first subfield: - - if ( effectFieldTokens.length > 1 && isNonCodingGene ) { - effectName = effectFieldTokens[1].trim(); - } - else { - effectName = effectFieldTokens[0].trim(); - } - - return EffectType.valueOf(effectName); - } - - private String parseEffectExtraInformation ( String[] effectFieldTokens, boolean isNonCodingGene ) { - - // The extra effect-related information, if present, will always be the last subfield: - - if ( (effectFieldTokens.length == 2 && ! isNonCodingGene) || effectFieldTokens.length == 3 ) { - return effectFieldTokens[effectFieldTokens.length - 1].trim(); - } - - return null; - } - - public Class getFeatureType() { - return SnpEffFeature.class; - } - - public Object readHeader ( LineReader reader ) { - String headerLine = ""; - - try { - headerLine = reader.readLine(); - } - catch ( IOException e ) { - throw new TribbleException("Unable to read header line from input file."); - } - - validateHeaderLine(headerLine); - return headerLine; - } - - private void validateHeaderLine ( String headerLine ) { - if ( headerLine == null || ! headerLine.startsWith(HEADER_LINE_START) ) { - throw new TribbleException.InvalidHeader("Header line does not start with " + HEADER_LINE_START); - } - - String[] headerTokens = headerLine.substring(HEADER_LINE_START.length()).split(FIELD_DELIMITER_PATTERN); - - if ( headerTokens.length != EXPECTED_NUMBER_OF_FIELDS ) { - throw new TribbleException.InvalidHeader("Header line does not contain headings for the expected number (" + - EXPECTED_NUMBER_OF_FIELDS + ") of columns."); - } - - for ( int columnIndex = 0; columnIndex < headerTokens.length; columnIndex++ ) { - if ( ! HEADER_FIELD_NAMES[columnIndex].equals(headerTokens[columnIndex]) ) { - throw new TribbleException.InvalidHeader("Header field #" + columnIndex + ": Expected \"" + - HEADER_FIELD_NAMES[columnIndex] + "\" but found \"" + - headerTokens[columnIndex] + "\""); - } - } - } - - public boolean canDecode ( final File potentialInput ) { - try { - LineReader reader = new AsciiLineReader(new FileInputStream(potentialInput)); - readHeader(reader); - } - catch ( Exception e ) { - return false; - } - - return true; - } -} diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/snpEff/SnpEffConstants.java b/public/java/src/org/broadinstitute/sting/utils/codecs/snpEff/SnpEffConstants.java deleted file mode 100644 index 270db470f..000000000 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/snpEff/SnpEffConstants.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (c) 2011, The Broad Institute - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -package org.broadinstitute.sting.utils.codecs.snpEff; - -/** - * A set of constants associated with the SnpEff codec. - * - * @author David Roazen - */ -public class SnpEffConstants { - - // Possible SnpEff biological effects and their associated impacts: - public enum EffectType { - START_GAINED (EffectImpact.HIGH), - START_LOST (EffectImpact.HIGH), - EXON_DELETED (EffectImpact.HIGH), - FRAME_SHIFT (EffectImpact.HIGH), - STOP_GAINED (EffectImpact.HIGH), - STOP_LOST (EffectImpact.HIGH), - SPLICE_SITE_ACCEPTOR (EffectImpact.HIGH), - SPLICE_SITE_DONOR (EffectImpact.HIGH), - - NON_SYNONYMOUS_CODING (EffectImpact.MODERATE), - UTR_5_DELETED (EffectImpact.MODERATE), - UTR_3_DELETED (EffectImpact.MODERATE), - CODON_INSERTION (EffectImpact.MODERATE), - CODON_CHANGE_PLUS_CODON_INSERTION (EffectImpact.MODERATE), - CODON_DELETION (EffectImpact.MODERATE), - CODON_CHANGE_PLUS_CODON_DELETION (EffectImpact.MODERATE), - - NONE (EffectImpact.LOW), - CHROMOSOME (EffectImpact.LOW), - INTERGENIC (EffectImpact.LOW), - UPSTREAM (EffectImpact.LOW), - UTR_5_PRIME (EffectImpact.LOW), - SYNONYMOUS_START (EffectImpact.LOW), - NON_SYNONYMOUS_START (EffectImpact.LOW), - CDS (EffectImpact.LOW), - GENE (EffectImpact.LOW), - TRANSCRIPT (EffectImpact.LOW), - EXON (EffectImpact.LOW), - SYNONYMOUS_CODING (EffectImpact.LOW), - CODON_CHANGE (EffectImpact.LOW), - SYNONYMOUS_STOP (EffectImpact.LOW), - NON_SYNONYMOUS_STOP (EffectImpact.LOW), - INTRON (EffectImpact.LOW), - UTR_3_PRIME (EffectImpact.LOW), - DOWNSTREAM (EffectImpact.LOW), - INTRON_CONSERVED (EffectImpact.LOW), - INTERGENIC_CONSERVED (EffectImpact.LOW), - CUSTOM (EffectImpact.LOW); - - private final EffectImpact impact; - - EffectType ( EffectImpact impact ) { - this.impact = impact; - } - - public EffectImpact getImpact() { - return impact; - } - } - - public enum EffectImpact { - LOW (1), - MODERATE (2), - HIGH (3); - - private final int severityRating; - - EffectImpact ( int severityRating ) { - this.severityRating = severityRating; - } - - public boolean isHigherImpactThan ( EffectImpact other ) { - return this.severityRating > other.severityRating; - } - } - - // The kinds of variants supported by the SnpEff output format: - public enum ChangeType { - SNP, - MNP, - INS, - DEL - } - - // Possible zygosities of SnpEff variants: - public enum Zygosity { - Hom, - Het - } -} diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/snpEff/SnpEffFeature.java b/public/java/src/org/broadinstitute/sting/utils/codecs/snpEff/SnpEffFeature.java deleted file mode 100644 index 2f120b7d2..000000000 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/snpEff/SnpEffFeature.java +++ /dev/null @@ -1,423 +0,0 @@ -/* - * Copyright (c) 2011, The Broad Institute - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -package org.broadinstitute.sting.utils.codecs.snpEff; - -import org.broad.tribble.Feature; - -import java.util.NoSuchElementException; - -import static org.broadinstitute.sting.utils.codecs.snpEff.SnpEffConstants.EffectType; -import static org.broadinstitute.sting.utils.codecs.snpEff.SnpEffConstants.EffectImpact; -import static org.broadinstitute.sting.utils.codecs.snpEff.SnpEffConstants.ChangeType; -import static org.broadinstitute.sting.utils.codecs.snpEff.SnpEffConstants.Zygosity; - -/** - * Feature returned by the SnpEff codec -- stores the parsed field values from a line of SnpEff output. - * - * Many fields are optional, and missing values are represented by nulls. You should always call the - * hasX() method before calling the corresponding getX() method. Required fields can never be null - * and do not have a hasX() method. - * - * @author David Roazen - */ -public class SnpEffFeature implements Feature { - - private String contig; // REQUIRED FIELD - private long position; // REQUIRED FIELD - private String reference; - private String change; - private ChangeType changeType; - private Zygosity zygosity; - private Double quality; - private Long coverage; - private String warnings; - private String geneID; - private String geneName; - private String bioType; - private String transcriptID; - private String exonID; - private Integer exonRank; - private boolean isNonCodingGene; // REQUIRED FIELD - private EffectType effect; // REQUIRED FIELD - private String effectExtraInformation; - private String oldAndNewAA; - private String oldAndNewCodon; - private Integer codonNum; - private Integer cdsSize; - private String codonsAround; - private String aasAround; - private String customIntervalID; - - public SnpEffFeature ( String contig, - long position, - String reference, - String change, - ChangeType changeType, - Zygosity zygosity, - Double quality, - Long coverage, - String warnings, - String geneID, - String geneName, - String bioType, - String transcriptID, - String exonID, - Integer exonRank, - boolean isNonCodingGene, - EffectType effect, - String effectExtraInformation, - String oldAndNewAA, - String oldAndNewCodon, - Integer codonNum, - Integer cdsSize, - String codonsAround, - String aasAround, - String customIntervalID ) { - - if ( contig == null || effect == null ) { - throw new IllegalArgumentException("contig and effect cannot be null, as they are required fields"); - } - - this.contig = contig; - this.position = position; - this.reference = reference; - this.change = change; - this.changeType = changeType; - this.zygosity = zygosity; - this.quality = quality; - this.coverage = coverage; - this.warnings = warnings; - this.geneID = geneID; - this.geneName = geneName; - this.bioType = bioType; - this.transcriptID = transcriptID; - this.exonID = exonID; - this.exonRank = exonRank; - this.isNonCodingGene = isNonCodingGene; - this.effect = effect; - this.effectExtraInformation = effectExtraInformation; - this.oldAndNewAA = oldAndNewAA; - this.oldAndNewCodon = oldAndNewCodon; - this.codonNum = codonNum; - this.cdsSize = cdsSize; - this.codonsAround = codonsAround; - this.aasAround = aasAround; - this.customIntervalID = customIntervalID; - } - - public boolean isHigherImpactThan ( SnpEffFeature other ) { - - // If one effect is in a non-coding gene and the other is not, the effect NOT in the - // non-coding gene has higher impact: - - if ( ! isNonCodingGene() && other.isNonCodingGene() ) { - return true; - } - else if ( isNonCodingGene() && ! other.isNonCodingGene() ) { - return false; - } - - // Otherwise, both effects are either in or not in a non-coding gene, so we compare the impacts - // of the effects themselves as defined in the SnpEffConstants class: - - return getEffectImpact().isHigherImpactThan(other.getEffectImpact()); - } - - public String getChr() { - return contig; - } - - public int getStart() { - return (int)position; - } - - public int getEnd() { - return (int)position; - } - - public boolean hasReference() { - return reference != null; - } - - public String getReference() { - if ( reference == null ) throw new NoSuchElementException("This feature has no reference field"); - return reference; - } - - public boolean hasChange() { - return change != null; - } - - public String getChange() { - if ( change == null ) throw new NoSuchElementException("This feature has no change field"); - return change; - } - - public boolean hasChangeType() { - return changeType != null; - } - - public ChangeType getChangeType() { - if ( changeType == null ) throw new NoSuchElementException("This feature has no changeType field"); - return changeType; - } - - public boolean hasZygosity() { - return zygosity != null; - } - - public Zygosity getZygosity() { - if ( zygosity == null ) throw new NoSuchElementException("This feature has no zygosity field"); - return zygosity; - } - - public boolean hasQuality() { - return quality != null; - } - - public Double getQuality() { - if ( quality == null ) throw new NoSuchElementException("This feature has no quality field"); - return quality; - } - - public boolean hasCoverage() { - return coverage != null; - } - - public Long getCoverage() { - if ( coverage == null ) throw new NoSuchElementException("This feature has no coverage field"); - return coverage; - } - - public boolean hasWarnings() { - return warnings != null; - } - - public String getWarnings() { - if ( warnings == null ) throw new NoSuchElementException("This feature has no warnings field"); - return warnings; - } - - public boolean hasGeneID() { - return geneID != null; - } - - public String getGeneID() { - if ( geneID == null ) throw new NoSuchElementException("This feature has no geneID field"); - return geneID; - } - - public boolean hasGeneName() { - return geneName != null; - } - - public String getGeneName() { - if ( geneName == null ) throw new NoSuchElementException("This feature has no geneName field"); - return geneName; - } - - public boolean hasBioType() { - return bioType != null; - } - - public String getBioType() { - if ( bioType == null ) throw new NoSuchElementException("This feature has no bioType field"); - return bioType; - } - - public boolean hasTranscriptID() { - return transcriptID != null; - } - - public String getTranscriptID() { - if ( transcriptID == null ) throw new NoSuchElementException("This feature has no transcriptID field"); - return transcriptID; - } - - public boolean hasExonID() { - return exonID != null; - } - - public String getExonID() { - if ( exonID == null ) throw new NoSuchElementException("This feature has no exonID field"); - return exonID; - } - - public boolean hasExonRank() { - return exonRank != null; - } - - public Integer getExonRank() { - if ( exonRank == null ) throw new NoSuchElementException("This feature has no exonRank field"); - return exonRank; - } - - public boolean isNonCodingGene() { - return isNonCodingGene; - } - - public EffectType getEffect() { - return effect; - } - - public EffectImpact getEffectImpact() { - return effect.getImpact(); - } - - public boolean hasEffectExtraInformation() { - return effectExtraInformation != null; - } - - public String getEffectExtraInformation() { - if ( effectExtraInformation == null ) throw new NoSuchElementException("This feature has no effectExtraInformation field"); - return effectExtraInformation; - } - - public boolean hasOldAndNewAA() { - return oldAndNewAA != null; - } - - public String getOldAndNewAA() { - if ( oldAndNewAA == null ) throw new NoSuchElementException("This feature has no oldAndNewAA field"); - return oldAndNewAA; - } - - public boolean hasOldAndNewCodon() { - return oldAndNewCodon != null; - } - - public String getOldAndNewCodon() { - if ( oldAndNewCodon == null ) throw new NoSuchElementException("This feature has no oldAndNewCodon field"); - return oldAndNewCodon; - } - - public boolean hasCodonNum() { - return codonNum != null; - } - - public Integer getCodonNum() { - if ( codonNum == null ) throw new NoSuchElementException("This feature has no codonNum field"); - return codonNum; - } - - public boolean hasCdsSize() { - return cdsSize != null; - } - - public Integer getCdsSize() { - if ( cdsSize == null ) throw new NoSuchElementException("This feature has no cdsSize field"); - return cdsSize; - } - - public boolean hasCodonsAround() { - return codonsAround != null; - } - - public String getCodonsAround() { - if ( codonsAround == null ) throw new NoSuchElementException("This feature has no codonsAround field"); - return codonsAround; - } - - public boolean hadAasAround() { - return aasAround != null; - } - - public String getAasAround() { - if ( aasAround == null ) throw new NoSuchElementException("This feature has no aasAround field"); - return aasAround; - } - - public boolean hasCustomIntervalID() { - return customIntervalID != null; - } - - public String getCustomIntervalID() { - if ( customIntervalID == null ) throw new NoSuchElementException("This feature has no customIntervalID field"); - return customIntervalID; - } - - public boolean equals ( Object o ) { - if ( o == null || ! (o instanceof SnpEffFeature) ) { - return false; - } - - SnpEffFeature other = (SnpEffFeature)o; - - return contig.equals(other.contig) && - position == other.position && - (reference == null ? other.reference == null : reference.equals(other.reference)) && - (change == null ? other.change == null : change.equals(other.change)) && - changeType == other.changeType && - zygosity == other.zygosity && - (quality == null ? other.quality == null : quality.equals(other.quality)) && - (coverage == null ? other.coverage == null : coverage.equals(other.coverage)) && - (warnings == null ? other.warnings == null : warnings.equals(other.warnings)) && - (geneID == null ? other.geneID == null : geneID.equals(other.geneID)) && - (geneName == null ? other.geneName == null : geneName.equals(other.geneName)) && - (bioType == null ? other.bioType == null : bioType.equals(other.bioType)) && - (transcriptID == null ? other.transcriptID == null : transcriptID.equals(other.transcriptID)) && - (exonID == null ? other.exonID == null : exonID.equals(other.exonID)) && - (exonRank == null ? other.exonRank == null : exonRank.equals(other.exonRank)) && - isNonCodingGene == other.isNonCodingGene && - effect == other.effect && - (effectExtraInformation == null ? other.effectExtraInformation == null : effectExtraInformation.equals(other.effectExtraInformation)) && - (oldAndNewAA == null ? other.oldAndNewAA == null : oldAndNewAA.equals(other.oldAndNewAA)) && - (oldAndNewCodon == null ? other.oldAndNewCodon == null : oldAndNewCodon.equals(other.oldAndNewCodon)) && - (codonNum == null ? other.codonNum == null : codonNum.equals(other.codonNum)) && - (cdsSize == null ? other.cdsSize == null : cdsSize.equals(other.cdsSize)) && - (codonsAround == null ? other.codonsAround == null : codonsAround.equals(other.codonsAround)) && - (aasAround == null ? other.aasAround == null : aasAround.equals(other.aasAround)) && - (customIntervalID == null ? other.customIntervalID == null : customIntervalID.equals(other.customIntervalID)); - } - - public String toString() { - return "[Contig: " + contig + - " Position: " + position + - " Reference: " + reference + - " Change: " + change + - " Change Type: " + changeType + - " Zygosity: " + zygosity + - " Quality: " + quality + - " Coverage: " + coverage + - " Warnings: " + warnings + - " Gene ID: " + geneID + - " Gene Name: " + geneName + - " Bio Type: " + bioType + - " Transcript ID: " + transcriptID + - " Exon ID: " + exonID + - " Exon Rank: " + exonRank + - " Non-Coding Gene: " + isNonCodingGene + - " Effect: " + effect + - " Effect Extra Information: " + effectExtraInformation + - " Old/New AA: " + oldAndNewAA + - " Old/New Codon: " + oldAndNewCodon + - " Codon Num: " + codonNum + - " CDS Size: " + cdsSize + - " Codons Around: " + codonsAround + - " AAs Around: " + aasAround + - " Custom Interval ID: " + customIntervalID + - "]"; - } -} diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFHeader.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFHeader.java index eb01e5dca..fd1c74993 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFHeader.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFHeader.java @@ -24,6 +24,7 @@ public class VCFHeader { private final Set mMetaData; private final Map mInfoMetaData = new HashMap(); private final Map mFormatMetaData = new HashMap(); + private final Map mOtherMetaData = new HashMap(); // the list of auxillary tags private final Set mGenotypeSampleNames = new LinkedHashSet(); @@ -110,6 +111,9 @@ public class VCFHeader { VCFFormatHeaderLine formatLine = (VCFFormatHeaderLine)line; mFormatMetaData.put(formatLine.getName(), formatLine); } + else { + mOtherMetaData.put(line.getKey(), line); + } } } @@ -185,6 +189,14 @@ public class VCFHeader { public VCFFormatHeaderLine getFormatHeaderLine(String key) { return mFormatMetaData.get(key); } + + /** + * @param key the header key name + * @return the meta data line, or null if there is none + */ + public VCFHeaderLine getOtherHeaderLine(String key) { + return mOtherMetaData.get(key); + } } diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java index 1c65102ae..cfd59b504 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java @@ -817,6 +817,28 @@ public class VariantContext implements Feature { // to enable tribble intergrati throw new IllegalArgumentException("Requested " + i + " alternative allele but there are only " + n + " alternative alleles " + this); } + /** + * @param other VariantContext whose alternate alleles to compare against + * @return true if this VariantContext has the same alternate alleles as other, + * regardless of ordering. Otherwise returns false. + */ + public boolean hasSameAlternateAllelesAs ( VariantContext other ) { + Set thisAlternateAlleles = getAlternateAlleles(); + Set otherAlternateAlleles = other.getAlternateAlleles(); + + if ( thisAlternateAlleles.size() != otherAlternateAlleles.size() ) { + return false; + } + + for ( Allele allele : thisAlternateAlleles ) { + if ( ! otherAlternateAlleles.contains(allele) ) { + return false; + } + } + + return true; + } + // --------------------------------------------------------------------------------------------------------- // // Working with genotypes diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/annotator/SnpEffUnitTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/annotator/SnpEffUnitTest.java new file mode 100644 index 000000000..462abeba1 --- /dev/null +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/annotator/SnpEffUnitTest.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2011, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.gatk.walkers.annotator; + +import org.testng.Assert; +import org.testng.annotations.Test; +import org.broadinstitute.sting.gatk.walkers.annotator.SnpEff.SnpEffEffect; + +public class SnpEffUnitTest { + + @Test + public void testParseWellFormedEffect() { + String effectName = "NON_SYNONYMOUS_CODING"; + String[] effectMetadata = { "MODERATE", "Aca/Gca", "T/A", "OR4F5", "protein_coding", "CODING", "ENST00000534990", "exon_1_69037_69829" }; + + SnpEffEffect effect = new SnpEffEffect(effectName, effectMetadata); + Assert.assertTrue( effect.isWellFormed() && effect.isCoding() ); + } + + @Test + public void testParseInvalidEffectNameEffect() { + String effectName = "MADE_UP_EFFECT"; + String[] effectMetadata = { "MODERATE", "Aca/Gca", "T/A", "OR4F5", "protein_coding", "CODING", "ENST00000534990", "exon_1_69037_69829" }; + + SnpEffEffect effect = new SnpEffEffect(effectName, effectMetadata); + Assert.assertFalse(effect.isWellFormed()); + } + + @Test + public void testParseInvalidEffectImpactEffect() { + String effectName = "NON_SYNONYMOUS_CODING"; + String[] effectMetadata = { "MEDIUM", "Aca/Gca", "T/A", "OR4F5", "protein_coding", "CODING", "ENST00000534990", "exon_1_69037_69829" }; + + SnpEffEffect effect = new SnpEffEffect(effectName, effectMetadata); + Assert.assertFalse(effect.isWellFormed()); + } + + @Test + public void testParseWrongNumberOfMetadataFieldsEffect() { + String effectName = "NON_SYNONYMOUS_CODING"; + String[] effectMetadata = { "MODERATE", "Aca/Gca", "T/A", "OR4F5", "protein_coding", "CODING", "ENST00000534990" }; + + SnpEffEffect effect = new SnpEffEffect(effectName, effectMetadata); + Assert.assertFalse(effect.isWellFormed()); + } + + @Test + public void testParseSnpEffWarningEffect() { + String effectName = "NON_SYNONYMOUS_CODING"; + String[] effectMetadata = { "MODERATE", "Aca/Gca", "T/A", "OR4F5", "protein_coding", "CODING", "ENST00000534990", "exon_1_69037_69829", "SNPEFF_WARNING" }; + + SnpEffEffect effect = new SnpEffEffect(effectName, effectMetadata); + Assert.assertTrue( ! effect.isWellFormed() && effect.getParseError().equals("SnpEff issued the following warning: SNPEFF_WARNING") ); + } + + @Test + public void testParseSnpEffErrorEffect() { + String effectName = "NON_SYNONYMOUS_CODING"; + String[] effectMetadata = { "MODERATE", "Aca/Gca", "T/A", "OR4F5", "protein_coding", "CODING", "ENST00000534990", "exon_1_69037_69829", "", "SNPEFF_ERROR" }; + + SnpEffEffect effect = new SnpEffEffect(effectName, effectMetadata); + Assert.assertTrue( ! effect.isWellFormed() && effect.getParseError().equals("SnpEff issued the following error: SNPEFF_ERROR") ); + } +} diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorIntegrationTest.java index 832079807..f902ce276 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorIntegrationTest.java @@ -1,6 +1,7 @@ package org.broadinstitute.sting.gatk.walkers.annotator; import org.broadinstitute.sting.WalkerTest; +import org.broadinstitute.sting.utils.exceptions.UserException; import org.testng.annotations.Test; import java.util.Arrays; @@ -129,12 +130,24 @@ public class VariantAnnotatorIntegrationTest extends WalkerTest { @Test public void testSnpEffAnnotations() { WalkerTestSpec spec = new WalkerTestSpec( - "-T VariantAnnotator -R " + b37KGReference + " -NO_HEADER -o %s -A SnpEff --variant " + - validationDataLocation + "1000G.exomes.vcf --snpEffFile " + validationDataLocation + - "snpEff_1.9.6_1000G.exomes.vcf_hg37.61.out -L 1:26,000,000-26,500,000", + "-T VariantAnnotator -R " + hg19Reference + " -NO_HEADER -o %s -A SnpEff --variant " + + validationDataLocation + "1kg_exomes_unfiltered.AFR.unfiltered.vcf --snpEffFile " + validationDataLocation + + "snpEff.AFR.unfiltered.vcf -L 1:1-1,500,000", 1, - Arrays.asList("03eae1dab19a9358250890594bf53607") + Arrays.asList("a1c3ba9efc28ee0606339604095076ea") ); executeTest("Testing SnpEff annotations", spec); } + + @Test + public void testSnpEffAnnotationsUnsupportedVersion() { + WalkerTestSpec spec = new WalkerTestSpec( + "-T VariantAnnotator -R " + hg19Reference + " -NO_HEADER -o %s -A SnpEff --variant " + + validationDataLocation + "1kg_exomes_unfiltered.AFR.unfiltered.vcf --snpEffFile " + validationDataLocation + + "snpEff.AFR.unfiltered.unsupported.version.vcf -L 1:1-1,500,000", + 1, + UserException.class + ); + executeTest("Testing SnpEff annotations (unsupported version)", spec); + } } diff --git a/public/java/test/org/broadinstitute/sting/utils/codecs/snpEff/SnpEffCodecUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/codecs/snpEff/SnpEffCodecUnitTest.java deleted file mode 100644 index 6d492565b..000000000 --- a/public/java/test/org/broadinstitute/sting/utils/codecs/snpEff/SnpEffCodecUnitTest.java +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright (c) 2011, The Broad Institute - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -package org.broadinstitute.sting.utils.codecs.snpEff; - -import org.apache.commons.io.input.ReaderInputStream; -import org.broad.tribble.TribbleException; -import org.broad.tribble.readers.AsciiLineReader; -import org.broad.tribble.readers.LineReader; -import org.testng.Assert; -import org.testng.annotations.Test; - -import static org.broadinstitute.sting.utils.codecs.snpEff.SnpEffConstants.EffectType; -import static org.broadinstitute.sting.utils.codecs.snpEff.SnpEffConstants.ChangeType; -import static org.broadinstitute.sting.utils.codecs.snpEff.SnpEffConstants.Zygosity; - -import java.io.StringReader; - -public class SnpEffCodecUnitTest { - - @Test - public void testParseWellFormedSnpEffHeaderLine() { - String wellFormedSnpEffHeaderLine = "# Chromo\tPosition\tReference\tChange\tChange type\t" + - "Homozygous\tQuality\tCoverage\tWarnings\tGene_ID\tGene_name\tBio_type\tTrancript_ID\tExon_ID\t" + - "Exon_Rank\tEffect\told_AA/new_AA\tOld_codon/New_codon\tCodon_Num(CDS)\tCDS_size\tCodons around\t" + - "AAs around\tCustom_interval_ID"; - - SnpEffCodec codec = new SnpEffCodec(); - LineReader reader = new AsciiLineReader(new ReaderInputStream(new StringReader(wellFormedSnpEffHeaderLine))); - String headerReturned = (String)codec.readHeader(reader); - - Assert.assertEquals(headerReturned, wellFormedSnpEffHeaderLine); - } - - @Test(expectedExceptions = TribbleException.InvalidHeader.class) - public void testParseWrongNumberOfFieldsSnpEffHeaderLine() { - String wrongNumberOfFieldsSnpEffHeaderLine = "# Chromo\tPosition\tReference\tChange\tChange type\t" + - "Homozygous\tQuality\tCoverage\tWarnings\tGene_ID\tGene_name\tBio_type\tTrancript_ID\tExon_ID\t" + - "Exon_Rank\tEffect\told_AA/new_AA\tOld_codon/New_codon\tCodon_Num(CDS)\tCDS_size\tCodons around\t" + - "AAs around"; - - SnpEffCodec codec = new SnpEffCodec(); - LineReader reader = new AsciiLineReader(new ReaderInputStream(new StringReader(wrongNumberOfFieldsSnpEffHeaderLine))); - codec.readHeader(reader); - } - - @Test(expectedExceptions = TribbleException.InvalidHeader.class) - public void testParseMisnamedColumnSnpEffHeaderLine() { - String misnamedColumnSnpEffHeaderLine = "# Chromo\tPosition\tRef\tChange\tChange type\t" + - "Homozygous\tQuality\tCoverage\tWarnings\tGene_ID\tGene_name\tBio_type\tTrancript_ID\tExon_ID\t" + - "Exon_Rank\tEffect\told_AA/new_AA\tOld_codon/New_codon\tCodon_Num(CDS)\tCDS_size\tCodons around\t" + - "AAs around\tCustom_interval_ID"; - - SnpEffCodec codec = new SnpEffCodec(); - LineReader reader = new AsciiLineReader(new ReaderInputStream(new StringReader(misnamedColumnSnpEffHeaderLine))); - codec.readHeader(reader); - } - - @Test - public void testParseSimpleEffectSnpEffLine() { - String simpleEffectSnpEffLine = "1\t69428\tT\tG\tSNP\tHom\t6049.69\t61573\t\tENSG00000177693\t" + - "OR4F5\tmRNA\tENST00000326183\texon_1_69055_70108\t1\tNON_SYNONYMOUS_CODING\tF/C\tTTT/TGT\t113\t918\t\t\t"; - - SnpEffFeature expectedFeature = new SnpEffFeature("1", - 69428l, - "T", - "G", - ChangeType.SNP, - Zygosity.Hom, - 6049.69, - 61573l, - null, - "ENSG00000177693", - "OR4F5", - "mRNA", - "ENST00000326183", - "exon_1_69055_70108", - 1, - false, - EffectType.NON_SYNONYMOUS_CODING, - null, - "F/C", - "TTT/TGT", - 113, - 918, - null, - null, - null - ); - - SnpEffCodec codec = new SnpEffCodec(); - SnpEffFeature feature = (SnpEffFeature)codec.decode(simpleEffectSnpEffLine); - - Assert.assertEquals(feature, expectedFeature); - } - - @Test - public void testParseNonCodingRegionSnpEffLine() { - String nonCodingRegionSnpEffLine = "1\t1337592\tG\tC\tSNP\tHom\t1935.52\t21885\t\tENSG00000250188\t" + - "RP4-758J18.5\tmRNA\tENST00000514958\texon_1_1337454_1338076\t2\tWITHIN_NON_CODING_GENE, NON_SYNONYMOUS_CODING\t" + - "L/V\tCTA/GTA\t272\t952\t\t\t"; - - SnpEffFeature expectedFeature = new SnpEffFeature("1", - 1337592l, - "G", - "C", - ChangeType.SNP, - Zygosity.Hom, - 1935.52, - 21885l, - null, - "ENSG00000250188", - "RP4-758J18.5", - "mRNA", - "ENST00000514958", - "exon_1_1337454_1338076", - 2, - true, - EffectType.NON_SYNONYMOUS_CODING, - null, - "L/V", - "CTA/GTA", - 272, - 952, - null, - null, - null - ); - - SnpEffCodec codec = new SnpEffCodec(); - SnpEffFeature feature = (SnpEffFeature)codec.decode(nonCodingRegionSnpEffLine); - - Assert.assertEquals(feature, expectedFeature); - } - - @Test - public void testParseExtraEffectInformationSnpEffLine() { - String extraEffectInformationSnpEffLine = "1\t879537\tT\tC\tSNP\tHom\t341.58\t13733\t\tENSG00000187634\tSAMD11\t" + - "mRNA\tENST00000341065\t\t\tUTR_3_PRIME: 4 bases from transcript end\t\t\t\t\t\t\t"; - - SnpEffFeature expectedFeature = new SnpEffFeature("1", - 879537l, - "T", - "C", - ChangeType.SNP, - Zygosity.Hom, - 341.58, - 13733l, - null, - "ENSG00000187634", - "SAMD11", - "mRNA", - "ENST00000341065", - null, - null, - false, - EffectType.UTR_3_PRIME, - "4 bases from transcript end", - null, - null, - null, - null, - null, - null, - null - ); - - SnpEffCodec codec = new SnpEffCodec(); - SnpEffFeature feature = (SnpEffFeature)codec.decode(extraEffectInformationSnpEffLine); - - Assert.assertEquals(feature, expectedFeature); - } - - @Test - public void testParseMultiEffectSnpEffLine() { - String multiEffectSnpEffLine = "1\t901901\tC\tT\tSNP\tHom\t162.91\t4646\t\tENSG00000187583\tPLEKHN1\tmRNA\t" + - "ENST00000379410\texon_1_901877_901994\t1\tSTART_GAINED: ATG, UTR_5_PRIME: 11 bases from TSS\t\t\t\t\t\t\t"; - - SnpEffFeature expectedFeature = new SnpEffFeature("1", - 901901l, - "C", - "T", - ChangeType.SNP, - Zygosity.Hom, - 162.91, - 4646l, - null, - "ENSG00000187583", - "PLEKHN1", - "mRNA", - "ENST00000379410", - "exon_1_901877_901994", - 1, - false, - EffectType.START_GAINED, - "ATG, UTR_5_PRIME: 11 bases from TSS", - null, - null, - null, - null, - null, - null, - null - ); - - SnpEffCodec codec = new SnpEffCodec(); - SnpEffFeature feature = (SnpEffFeature)codec.decode(multiEffectSnpEffLine); - - Assert.assertEquals(feature, expectedFeature); - } - - @Test(expectedExceptions = TribbleException.InvalidDecodeLine.class) - public void testParseWrongNumberOfFieldsSnpEffLine() { - String wrongNumberOfFieldsSnpEffLine = "1\t69428\tT\tG\tSNP\tHom\t6049.69\t61573\t\tENSG00000177693\t" + - "OR4F5\tmRNA\tENST00000326183\texon_1_69055_70108\t1\tNON_SYNONYMOUS_CODING\tF/C\tTTT/TGT\t113\t918\t\t"; - - SnpEffCodec codec = new SnpEffCodec(); - SnpEffFeature feature = (SnpEffFeature)codec.decode(wrongNumberOfFieldsSnpEffLine); - } - - @Test(expectedExceptions = TribbleException.InvalidDecodeLine.class) - public void testParseBlankEffectFieldSnpEffLine() { - String blankEffectFieldSnpEffLine = "1\t69428\tT\tG\tSNP\tHom\t6049.69\t61573\t\tENSG00000177693\t" + - "OR4F5\tmRNA\tENST00000326183\texon_1_69055_70108\t1\t\tF/C\tTTT/TGT\t113\t918\t\t\t"; - - SnpEffCodec codec = new SnpEffCodec(); - SnpEffFeature feature = (SnpEffFeature)codec.decode(blankEffectFieldSnpEffLine); - } - - @Test(expectedExceptions = TribbleException.InvalidDecodeLine.class) - public void testParseInvalidNumericFieldSnpEffLine() { - String invalidNumericFieldSnpEffLine = "1\t69428\tT\tG\tSNP\tHom\t6049.69\t61573\t\tENSG00000177693\t" + - "OR4F5\tmRNA\tENST00000326183\texon_1_69055_70108\t1\tNON_SYNONYMOUS_CODING\tF/C\tTTT/TGT\t113\tfoo\t\t\t";; - - SnpEffCodec codec = new SnpEffCodec(); - SnpEffFeature feature = (SnpEffFeature)codec.decode(invalidNumericFieldSnpEffLine); - } -} From e0c8c0ddcb48617d5f9146c2d9ffe57d967dd899 Mon Sep 17 00:00:00 2001 From: David Roazen Date: Wed, 14 Sep 2011 06:04:32 -0400 Subject: [PATCH 065/196] Modified VariantEval FunctionalClass stratification to remove hardcoded GenomicAnnotator keynames This is a temporary and hopefully short-lived solution. I've modified the FunctionalClass stratification to stratify by effect impact as defined by SnpEff annotations (high, moderate, and low impact) rather than by the silent/missense/nonsense categories. If we want to bring back the silent/missense/nonsense stratification, we should probably take the approach of asking the SnpEff author to add it as a feature to SnpEff rather than coding it ourselves, since the whole point of moving to SnpEff was to outsource genomic annotation. --- .../stratifications/FunctionalClass.java | 53 ++++++++----------- .../VariantEvalIntegrationTest.java | 7 ++- 2 files changed, 24 insertions(+), 36 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java index 193a65591..c675b111c 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java @@ -2,21 +2,29 @@ package org.broadinstitute.sting.gatk.walkers.varianteval.stratifications; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; +import org.broadinstitute.sting.gatk.walkers.annotator.SnpEff; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import java.util.ArrayList; import java.util.List; /** - * Stratifies by nonsense, missense, silent, and all annotations in the input ROD, from the INFO field annotation. + * Stratifies by low-, moderate-, and high-impact genomic effect using SnpEff annotations produced by VariantAnnotator */ public class FunctionalClass extends VariantStratifier { + + public static final String LOW_IMPACT_STATE_NAME = "low-impact"; + public static final String MODERATE_IMPACT_STATE_NAME = "moderate-impact"; + public static final String HIGH_IMPACT_STATE_NAME = "high-impact"; + + public static final String EFFECT_IMPACT_ATTRIBUTE_KEY = SnpEff.InfoFieldKey.EFF_IMPACT.toString(); + @Override public void initialize() { states.add("all"); - states.add("silent"); - states.add("missense"); - states.add("nonsense"); + states.add(LOW_IMPACT_STATE_NAME); + states.add(MODERATE_IMPACT_STATE_NAME); + states.add(HIGH_IMPACT_STATE_NAME); } @@ -25,36 +33,17 @@ public class FunctionalClass extends VariantStratifier { relevantStates.add("all"); - if (eval != null && eval.isVariant()) { - String type = null; + if ( eval != null && eval.isVariant() && eval.hasAttribute(EFFECT_IMPACT_ATTRIBUTE_KEY) ) { + String effectImpact = eval.getAttributeAsString(EFFECT_IMPACT_ATTRIBUTE_KEY); - if (eval.hasAttribute("refseq.functionalClass")) { - type = eval.getAttributeAsString("refseq.functionalClass"); - } else if (eval.hasAttribute("refseq.functionalClass_1")) { - int annotationId = 1; - String key; - - do { - key = String.format("refseq.functionalClass_%d", annotationId); - - String newtype = eval.getAttributeAsString(key); - - if ( newtype != null && !newtype.equalsIgnoreCase("null") && - ( type == null || - ( type.equals("silent") && !newtype.equals("silent") ) || - ( type.equals("missense") && newtype.equals("nonsense") ) ) - ) { - type = newtype; - } - - annotationId++; - } while (eval.hasAttribute(key)); + if ( effectImpact.equals(SnpEff.EffectImpact.LOW.toString()) ) { + relevantStates.add(LOW_IMPACT_STATE_NAME); } - - if (type != null) { - if (type.equals("silent")) { relevantStates.add("silent"); } - else if (type.equals("missense")) { relevantStates.add("missense"); } - else if (type.equals("nonsense")) { relevantStates.add("nonsense"); } + else if ( effectImpact.equals(SnpEff.EffectImpact.MODERATE.toString()) ) { + relevantStates.add(MODERATE_IMPACT_STATE_NAME); + } + else if ( effectImpact.equals(SnpEff.EffectImpact.HIGH.toString()) ) { + relevantStates.add(HIGH_IMPACT_STATE_NAME); } } diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java index e992684bc..00ecd5b67 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java @@ -123,9 +123,8 @@ public class VariantEvalIntegrationTest extends WalkerTest { WalkerTestSpec spec = new WalkerTestSpec( buildCommandLine( "-T VariantEval", - "-R " + b37KGReference, - "--dbsnp " + b37dbSNP132, - "--eval " + fundamentalTestVCF, + "-R " + hg19Reference, + "--eval " + validationDataLocation + "snpEff.AFR.unfiltered.VariantAnnotator.output.vcf", "-noEV", "-EV CountVariants", "-noST", @@ -134,7 +133,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { "-o %s" ), 1, - Arrays.asList("e40b77e7ed6581328e373a24b93cd170") + Arrays.asList("e93b3d66a5c150cbf1ae4262ec075d2d") ); executeTest("testFundamentalsCountVariantsSNPsandIndelsWithFunctionalClass", spec); } From 3db457ed01f7d99eaa0d62b9924cba6f1d269dad Mon Sep 17 00:00:00 2001 From: David Roazen Date: Wed, 14 Sep 2011 10:47:28 -0400 Subject: [PATCH 066/196] Revert "Modified VariantEval FunctionalClass stratification to remove hardcoded GenomicAnnotator keynames" After discussing this with Mark, it seems clear that the old version of the VariantEval FunctionalClass stratification is preferable to this version. By reverting, we maintain backwards compatibility with legacy output files from the old GenomicAnnotator, and can add SnpEff support later without breaking that backwards compatibility. This reverts commit b44acd1abd9ab6eec37111a19fa797f9e2ca3326. --- .../stratifications/FunctionalClass.java | 53 +++++++++++-------- .../VariantEvalIntegrationTest.java | 7 +-- 2 files changed, 36 insertions(+), 24 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java index c675b111c..193a65591 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java @@ -2,29 +2,21 @@ package org.broadinstitute.sting.gatk.walkers.varianteval.stratifications; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; -import org.broadinstitute.sting.gatk.walkers.annotator.SnpEff; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import java.util.ArrayList; import java.util.List; /** - * Stratifies by low-, moderate-, and high-impact genomic effect using SnpEff annotations produced by VariantAnnotator + * Stratifies by nonsense, missense, silent, and all annotations in the input ROD, from the INFO field annotation. */ public class FunctionalClass extends VariantStratifier { - - public static final String LOW_IMPACT_STATE_NAME = "low-impact"; - public static final String MODERATE_IMPACT_STATE_NAME = "moderate-impact"; - public static final String HIGH_IMPACT_STATE_NAME = "high-impact"; - - public static final String EFFECT_IMPACT_ATTRIBUTE_KEY = SnpEff.InfoFieldKey.EFF_IMPACT.toString(); - @Override public void initialize() { states.add("all"); - states.add(LOW_IMPACT_STATE_NAME); - states.add(MODERATE_IMPACT_STATE_NAME); - states.add(HIGH_IMPACT_STATE_NAME); + states.add("silent"); + states.add("missense"); + states.add("nonsense"); } @@ -33,17 +25,36 @@ public class FunctionalClass extends VariantStratifier { relevantStates.add("all"); - if ( eval != null && eval.isVariant() && eval.hasAttribute(EFFECT_IMPACT_ATTRIBUTE_KEY) ) { - String effectImpact = eval.getAttributeAsString(EFFECT_IMPACT_ATTRIBUTE_KEY); + if (eval != null && eval.isVariant()) { + String type = null; - if ( effectImpact.equals(SnpEff.EffectImpact.LOW.toString()) ) { - relevantStates.add(LOW_IMPACT_STATE_NAME); + if (eval.hasAttribute("refseq.functionalClass")) { + type = eval.getAttributeAsString("refseq.functionalClass"); + } else if (eval.hasAttribute("refseq.functionalClass_1")) { + int annotationId = 1; + String key; + + do { + key = String.format("refseq.functionalClass_%d", annotationId); + + String newtype = eval.getAttributeAsString(key); + + if ( newtype != null && !newtype.equalsIgnoreCase("null") && + ( type == null || + ( type.equals("silent") && !newtype.equals("silent") ) || + ( type.equals("missense") && newtype.equals("nonsense") ) ) + ) { + type = newtype; + } + + annotationId++; + } while (eval.hasAttribute(key)); } - else if ( effectImpact.equals(SnpEff.EffectImpact.MODERATE.toString()) ) { - relevantStates.add(MODERATE_IMPACT_STATE_NAME); - } - else if ( effectImpact.equals(SnpEff.EffectImpact.HIGH.toString()) ) { - relevantStates.add(HIGH_IMPACT_STATE_NAME); + + if (type != null) { + if (type.equals("silent")) { relevantStates.add("silent"); } + else if (type.equals("missense")) { relevantStates.add("missense"); } + else if (type.equals("nonsense")) { relevantStates.add("nonsense"); } } } diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java index 00ecd5b67..e992684bc 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java @@ -123,8 +123,9 @@ public class VariantEvalIntegrationTest extends WalkerTest { WalkerTestSpec spec = new WalkerTestSpec( buildCommandLine( "-T VariantEval", - "-R " + hg19Reference, - "--eval " + validationDataLocation + "snpEff.AFR.unfiltered.VariantAnnotator.output.vcf", + "-R " + b37KGReference, + "--dbsnp " + b37dbSNP132, + "--eval " + fundamentalTestVCF, "-noEV", "-EV CountVariants", "-noST", @@ -133,7 +134,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { "-o %s" ), 1, - Arrays.asList("e93b3d66a5c150cbf1ae4262ec075d2d") + Arrays.asList("e40b77e7ed6581328e373a24b93cd170") ); executeTest("testFundamentalsCountVariantsSNPsandIndelsWithFunctionalClass", spec); } From a942fa38ef87c1e1565d6a1cff041d8a62aaeb0a Mon Sep 17 00:00:00 2001 From: Guillermo del Angel Date: Thu, 15 Sep 2011 10:22:28 -0400 Subject: [PATCH 067/196] Refine the way we merge records in CombineVariants of different types. As of before, two records of different types were not combined and were kept separate. This is still the case, except when the alleles of one record are a strict subset of alleles of another record. For example, a SNP with alleles {A*,T} and a mixed record with alleles {A*,T, AAT} are now combined when start position matches. --- .../walkers/variantutils/CombineVariants.java | 35 +++++++++++++++++-- .../variantcontext/VariantContextUtils.java | 12 +++++++ 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariants.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariants.java index 7062f17e5..3e3b29a7f 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariants.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariants.java @@ -234,16 +234,47 @@ public class CombineVariants extends RodWalker { if (minimumN > 1 && (vcs.size() - numFilteredRecords < minimumN)) return 0; - List mergedVCs = new ArrayList(); + List preMergedVCs = new ArrayList(); Map> VCsByType = VariantContextUtils.separateVariantContextsByType(vcs); // iterate over the types so that it's deterministic for ( VariantContext.Type type : VariantContext.Type.values() ) { if ( VCsByType.containsKey(type) ) - mergedVCs.add(VariantContextUtils.simpleMerge(getToolkit().getGenomeLocParser(), VCsByType.get(type), + preMergedVCs.add(VariantContextUtils.simpleMerge(getToolkit().getGenomeLocParser(), VCsByType.get(type), priority, filteredRecordsMergeType, genotypeMergeOption, true, printComplexMerges, SET_KEY, filteredAreUncalled, MERGE_INFO_WITH_MAX_AC)); } + List mergedVCs = new ArrayList(); + // se have records merged but separated by type. If a particular record is for example a snp but all alleles are a subset of an existing mixed record, + // we will still merge those records. + if (preMergedVCs.size() > 1) { + for (VariantContext vc1 : preMergedVCs) { + VariantContext newvc = vc1; + boolean merged = false; + for (int k=0; k < mergedVCs.size(); k++) { + VariantContext vc2 = mergedVCs.get(k); + + if (VariantContextUtils.allelesAreSubset(vc1,vc2) || VariantContextUtils.allelesAreSubset(vc2,vc1)) { + // all alleles of vc1 are contained in vc2 but they are of different type (say, vc1 is snp, vc2 is complex): try to merget v1 into v2 + List vcpair = new ArrayList(); + vcpair.add(vc1); + vcpair.add(vc2); + newvc = VariantContextUtils.simpleMerge(getToolkit().getGenomeLocParser(), vcpair, + priority, filteredRecordsMergeType, genotypeMergeOption, true, printComplexMerges, + SET_KEY, filteredAreUncalled, MERGE_INFO_WITH_MAX_AC); + mergedVCs.set(k,newvc); + merged = true; + break; + } + } + if (!merged) + mergedVCs.add(vc1); + } + } + else { + mergedVCs = preMergedVCs; + } + for ( VariantContext mergedVC : mergedVCs ) { // only operate at the start of events if ( mergedVC == null ) diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java index 986d6305c..506bb3b33 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java @@ -663,6 +663,18 @@ public class VariantContextUtils { return merged; } + public static boolean allelesAreSubset(VariantContext vc1, VariantContext vc2) { + // if all alleles of vc1 are a contained in alleles of vc2, return true + if (!vc1.getReference().equals(vc2.getReference())) + return false; + + for (Allele a :vc1.getAlternateAlleles()) { + if (!vc2.getAlternateAlleles().contains(a)) + return false; + } + + return true; + } public static VariantContext createVariantContextWithTrimmedAlleles(VariantContext inputVC) { // see if we need to trim common reference base from all alleles boolean trimVC; From 1e682deb26aad12d4421cf9ff7f08318c1cd4ab3 Mon Sep 17 00:00:00 2001 From: David Roazen Date: Thu, 15 Sep 2011 13:07:50 -0400 Subject: [PATCH 068/196] Minor html-formatting-related documentation fix to the SnpEff class. --- .../org/broadinstitute/sting/gatk/walkers/annotator/SnpEff.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SnpEff.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SnpEff.java index 14abbca5b..bb3685fb5 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SnpEff.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SnpEff.java @@ -45,7 +45,7 @@ import java.util.*; * (http://snpeff.sourceforge.net/). * * For each variant, chooses one of the effects of highest biological impact from the SnpEff - * output file (which must be provided on the command line via --snpEffFile .vcf), + * output file (which must be provided on the command line via --snpEffFile filename.vcf), * and adds annotations on that effect. * * @author David Roazen From 202405b1a165db3f1d97f687da10826df181c849 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Thu, 15 Sep 2011 13:52:31 -0400 Subject: [PATCH 069/196] Updating the FunctionalClass stratification in VariantEval to handle the snpEff annotations; this change really needs to be in before the release so that the pipeline can output semi-meaningful plots. This commit maintains backwards compatibility with the crappy Genomic Annotator output. However, I did clean up the code a bit so that we now use an Enum instead of hard-coded values (so it's now much easier to change things if we choose to do so in the future). I do not see this as the final commit on this topic - I think we need to make some changes to the snpEff annotator to preferentially choose certain annotations within effect classes; Mark, let's chat about this for a bit when you get back next week. Also, for the record, I should be blamed for David's temporary commit the other day because I gave him the green light (since when do you care about backwards compatibility anyways?). In any case, at least now we have something that works for both the old and new annotations. --- .../stratifications/FunctionalClass.java | 53 +++++++++++++------ .../VariantEvalIntegrationTest.java | 25 ++++++++- 2 files changed, 59 insertions(+), 19 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java index 193a65591..a32857ffc 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java @@ -2,6 +2,7 @@ package org.broadinstitute.sting.gatk.walkers.varianteval.stratifications; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; +import org.broadinstitute.sting.gatk.walkers.annotator.SnpEff; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import java.util.ArrayList; @@ -11,12 +12,19 @@ import java.util.List; * Stratifies by nonsense, missense, silent, and all annotations in the input ROD, from the INFO field annotation. */ public class FunctionalClass extends VariantStratifier { + + public enum FunctionalType { + silent, + missense, + nonsense + } + + @Override public void initialize() { states.add("all"); - states.add("silent"); - states.add("missense"); - states.add("nonsense"); + for ( FunctionalType type : FunctionalType.values() ) + states.add(type.name()); } @@ -26,10 +34,12 @@ public class FunctionalClass extends VariantStratifier { relevantStates.add("all"); if (eval != null && eval.isVariant()) { - String type = null; + FunctionalType type = null; if (eval.hasAttribute("refseq.functionalClass")) { - type = eval.getAttributeAsString("refseq.functionalClass"); + try { + type = FunctionalType.valueOf(eval.getAttributeAsString("refseq.functionalClass")); + } catch ( Exception e ) {} // don't error out if the type isn't supported } else if (eval.hasAttribute("refseq.functionalClass_1")) { int annotationId = 1; String key; @@ -37,24 +47,33 @@ public class FunctionalClass extends VariantStratifier { do { key = String.format("refseq.functionalClass_%d", annotationId); - String newtype = eval.getAttributeAsString(key); - - if ( newtype != null && !newtype.equalsIgnoreCase("null") && - ( type == null || - ( type.equals("silent") && !newtype.equals("silent") ) || - ( type.equals("missense") && newtype.equals("nonsense") ) ) - ) { - type = newtype; + String newtypeStr = eval.getAttributeAsString(key); + if ( newtypeStr != null && !newtypeStr.equalsIgnoreCase("null") ) { + try { + FunctionalType newType = FunctionalType.valueOf(newtypeStr); + if ( type == null || + ( type == FunctionalType.silent && newType != FunctionalType.silent ) || + ( type == FunctionalType.missense && newType == FunctionalType.nonsense ) ) { + type = newType; + } + } catch ( Exception e ) {} // don't error out if the type isn't supported } annotationId++; } while (eval.hasAttribute(key)); + + } else if ( eval.hasAttribute(SnpEff.InfoFieldKey.EFF.name() ) ) { + SnpEff.EffectType snpEffType = SnpEff.EffectType.valueOf(eval.getAttribute(SnpEff.InfoFieldKey.EFF.name()).toString()); + if ( snpEffType == SnpEff.EffectType.STOP_GAINED ) + type = FunctionalType.nonsense; + else if ( snpEffType == SnpEff.EffectType.NON_SYNONYMOUS_CODING ) + type = FunctionalType.missense; + else if ( snpEffType == SnpEff.EffectType.SYNONYMOUS_CODING ) + type = FunctionalType.silent; } - if (type != null) { - if (type.equals("silent")) { relevantStates.add("silent"); } - else if (type.equals("missense")) { relevantStates.add("missense"); } - else if (type.equals("nonsense")) { relevantStates.add("nonsense"); } + if ( type != null ) { + relevantStates.add(type.name()); } } diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java index e992684bc..d8f7ad3b6 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java @@ -6,7 +6,7 @@ import org.testng.annotations.Test; import java.util.Arrays; public class VariantEvalIntegrationTest extends WalkerTest { - private static String variantEvalTestDataRoot = validationDataLocation + "/VariantEval"; + private static String variantEvalTestDataRoot = validationDataLocation + "VariantEval"; private static String fundamentalTestVCF = variantEvalTestDataRoot + "/" + "FundamentalsTest.annotated.db.subset.snps_and_indels.vcf"; private static String fundamentalTestSNPsVCF = variantEvalTestDataRoot + "/" + "FundamentalsTest.annotated.db.subset.final.vcf"; private static String fundamentalTestSNPsOneSampleVCF = variantEvalTestDataRoot + "/" + "FundamentalsTest.annotated.db.subset.final.HG00625.vcf"; @@ -14,6 +14,27 @@ public class VariantEvalIntegrationTest extends WalkerTest { private static String cmdRoot = "-T VariantEval" + " -R " + b36KGReference; + @Test + public void testFunctionClassWithSnpeff() { + WalkerTestSpec spec = new WalkerTestSpec( + buildCommandLine( + "-T VariantEval", + "-R " + b37KGReference, + "--dbsnp " + b37dbSNP132, + "--eval " + validationDataLocation + "snpEff.AFR.unfiltered.VariantAnnotator.output.vcf", + "-noEV", + "-EV TiTvVariantEvaluator", + "-noST", + "-ST FunctionalClass", + "-BTI eval", + "-o %s" + ), + 1, + Arrays.asList("f5f811ceb973d7fd6c1b2b734f1b2b12") + ); + executeTest("testStratifySamplesAndExcludeMonomorphicSites", spec); + } + @Test public void testStratifySamplesAndExcludeMonomorphicSites() { WalkerTestSpec spec = new WalkerTestSpec( @@ -21,7 +42,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { "-T VariantEval", "-R " + b37KGReference, "--dbsnp " + b37dbSNP132, - "--eval " + variantEvalTestDataRoot + "/CEU.trio.callsForVE.vcf", + "--eval " + variantEvalTestDataRoot + "CEU.trio.callsForVE.vcf", "-noEV", "-EV TiTvVariantEvaluator", "-ST Sample", From d369d105932b457a01b47166b7b7a1cd41d6b337 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Thu, 15 Sep 2011 13:56:23 -0400 Subject: [PATCH 070/196] Adding documentation before the release for GATK wiki page --- .../broadinstitute/sting/gatk/filters/PlatformFilter.java | 2 +- .../sting/gatk/walkers/PrintReadsWalker.java | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/filters/PlatformFilter.java b/public/java/src/org/broadinstitute/sting/gatk/filters/PlatformFilter.java index 30b2f828d..8e241bb2c 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/filters/PlatformFilter.java +++ b/public/java/src/org/broadinstitute/sting/gatk/filters/PlatformFilter.java @@ -36,7 +36,7 @@ import org.broadinstitute.sting.utils.sam.ReadUtils; * @version 0.1 */ public class PlatformFilter extends ReadFilter { - @Argument(fullName = "PLFilterName", shortName = "PLFilterName", doc="Discard reads with RG:PL attribute containing this strign", required=false) + @Argument(fullName = "PLFilterName", shortName = "PLFilterName", doc="Discard reads with RG:PL attribute containing this string", required=false) protected String[] PLFilterNames; public boolean filterOut(SAMRecord rec) { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/PrintReadsWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/PrintReadsWalker.java index fdfac6bf7..4f072e88c 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/PrintReadsWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/PrintReadsWalker.java @@ -68,6 +68,13 @@ import org.broadinstitute.sting.gatk.refdata.ReadMetaDataTracker; * -I input1.bam \ * -I input2.bam \ * --read_filter MappingQualityZero + * + * java -Xmx2g -jar GenomeAnalysisTK.jar \ + * -R ref.fasta \ + * -T PrintReads \ + * -o output.bam \ + * -I input.bam \ + * -n 2000 *
* */ From ce73dc40712510a360738df49e802b4c21d95621 Mon Sep 17 00:00:00 2001 From: Christopher Hartl Date: Thu, 15 Sep 2011 15:33:09 -0400 Subject: [PATCH 073/196] Update to the bindings for liftOverVCF.pl (to -V from -B) --- public/perl/liftOverVCF.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/perl/liftOverVCF.pl b/public/perl/liftOverVCF.pl index 21cb8bb6b..ba4198292 100755 --- a/public/perl/liftOverVCF.pl +++ b/public/perl/liftOverVCF.pl @@ -36,7 +36,7 @@ my $unsorted_vcf = "$tmp_prefix.unsorted.vcf"; # lift over the file print "Lifting over the vcf..."; -my $cmd = "java -jar $gatk/dist/GenomeAnalysisTK.jar -T LiftoverVariants -R $oldRef.fasta -B:variant,vcf $in -o $unsorted_vcf -chain $chain -dict $newRef.dict"; +my $cmd = "java -jar $gatk/dist/GenomeAnalysisTK.jar -T LiftoverVariants -R $oldRef.fasta -V:variant $in -o $unsorted_vcf -chain $chain -dict $newRef.dict"; if ($recordOriginalLocation) { $cmd .= " -recordOriginalLocation"; } @@ -66,7 +66,7 @@ system($cmd) == 0 or quit("The sorting step failed. Please correct the necessar # Filter the VCF for bad records print "\nFixing/removing bad records...\n"; -$cmd = "java -jar $gatk/dist/GenomeAnalysisTK.jar -T FilterLiftedVariants -R $newRef.fasta -B:variant,vcf $sorted_vcf -o $out"; +$cmd = "java -jar $gatk/dist/GenomeAnalysisTK.jar -T FilterLiftedVariants -R $newRef.fasta -V:variant $sorted_vcf -o $out"; system($cmd) == 0 or quit("The filtering step failed. Please correct the necessary errors before retrying."); # clean up From f04e51c6c2b74a79644e9473230410a8ba85fe92 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Thu, 15 Sep 2011 15:38:56 -0400 Subject: [PATCH 074/196] Adding docs from Andrey since his repo was all screwed up. --- .../indels/SomaticIndelDetectorWalker.java | 143 ++++++++++++------ 1 file changed, 94 insertions(+), 49 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/SomaticIndelDetectorWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/SomaticIndelDetectorWalker.java index e5ad3106d..8bba8eac2 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/SomaticIndelDetectorWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/SomaticIndelDetectorWalker.java @@ -68,26 +68,59 @@ import org.broadinstitute.sting.utils.variantcontext.VariantContext; import java.io.*; import java.util.*; + /** + * Tool for calling indels in Tumor-Normal paired sample mode; this tool supports single-sample mode as well, + * but this latter functionality is now superceded by UnifiedGenotyper. + * + *

* This is a simple, counts-and-cutoffs based tool for calling indels from aligned (preferrably MSA cleaned) sequencing - * data. Two output formats supported are: BED format (minimal output, required), and extended output that includes read - * and mismtach statistics around the calls (tuned on with --verbose). The calls can be performed from a single/pooled sample, - * or from a matched pair of samples (with --somatic option). In the latter case, two input bam files must be specified, - * the order is important: indels are called from the second sample ("Tumor") and additionally annotated as germline - * if even a weak evidence for the same indel, not necessarily a confident call, exists in the first sample ("Normal"), or as somatic - * if first bam has coverage at the site but no indication for an indel. In the --somatic mode, BED output contains - * only somatic calls, while --verbose output contains all calls annotated with GERMLINE/SOMATIC keywords. + * data. Supported output formats are: BED format, extended verbose output (tab separated), and VCF. The latter two outputs + * include additional statistics such as mismtaches and base qualitites around the calls, read strandness (how many + * forward/reverse reads support ref and indel alleles) etc. It is highly recommended to use these additional + * statistics to perform post-filtering of the calls as the tool is tuned for sensitivity (in other words it will + * attempt to "call" anything remotely reasonable based only on read counts and will generate all the additional + * metrics for the post-processing tools to make the final decision). The calls are performed by default + * from a matched tumor-normal pair of samples. In this case, two (sets of) input bam files must be specified using tagged -I + * command line arguments: normal and tumor bam(s) must be passed with -I:normal and -I:tumor arguments, + * respectively. Indels are called from the tumor sample and annotated as germline + * if even a weak evidence for the same indel, not necessarily a confident call, exists in the normal sample, or as somatic + * if normal sample has coverage at the site but no indication for an indel. Note that strictly speaking the calling + * is not even attempted in normal sample: if there is an indel in normal that is not detected/does not pass a threshold + * in tumor sample, it will not be reported. * - * If any of the general usage of this tool or any of the command-line arguments for this tool are not clear to you, - * please email asivache at broadinstitute dot org and he will gladly explain everything in more detail. + * To make indel calls and associated metrics for a single sample, this tool can be run with --unpaired flag (input + * bam tagging is not required in this case, and tags are completely ignored if still used: all input bams will be merged + * on the fly and assumed to represent a single sample - this tool does not check for sample id in the read groups). * + *

Input

+ *

+ * Tumor and normal bam files (or single sample bam file(s) in --unpaired mode). + *

+ * + *

Output

+ *

+ * Indel calls with associated metrics. + *

+ * + *

Examples

+ *
+ * java -Xmx2g -jar GenomeAnalysisTK.jar \
+ *   -R ref.fasta \
+ *   -T SomaticIndelDetector \
+ *   -o indels.vcf \
+ *   -verbose indels.txt
+ *   -I:normal normal.bam \
+ *   -I:tumor tumor.bam
+ * 
* */ + @ReadFilters({Platform454Filter.class, MappingQualityZeroFilter.class, PlatformUnitFilter.class}) public class SomaticIndelDetectorWalker extends ReadWalker { // @Output // PrintStream out; - @Output(doc="File to which variants should be written",required=true) + @Output(doc="File to write variants (indels) in VCF format",required=true) protected VCFWriter vcf_writer = null; @Argument(fullName="outputFile", shortName="O", doc="output file name (BED format). DEPRECATED> Use --bed", required=true) @@ -102,68 +135,80 @@ public class SomaticIndelDetectorWalker extends ReadWalker { @Hidden @Argument(fullName = "genotype_intervals", shortName = "genotype", - doc = "Calls will be made at each position within the specified interval(s), whether there is an indel or it's the ref", required = false) + doc = "Calls will be made at each position within the specified interval(s), whether there is an indel or not", required = false) public String genotypeIntervalsFile = null; @Hidden @Argument(fullName="genotypeIntervalsAreNotSorted", shortName="giNotSorted", required=false, - doc="This tool assumes that the genotyping interval list (--genotype_intervals) is sorted; "+ - "if the list turns out to be unsorted, it will throw an exception. "+ - "Use this argument when your interval list is not sorted to instruct the IndelGenotyper "+ - "to sort and keep it in memory (increases memory usage!).") + doc="This tool assumes that the genotyping interval list (--genotype_intervals) is sorted; "+ + "if the list turns out to be unsorted, it will throw an exception. "+ + "Use this argument when your interval list is not sorted to instruct the IndelGenotyper "+ + "to sort and keep it in memory (increases memory usage!).") protected boolean GENOTYPE_NOT_SORTED = false; @Hidden - @Argument(fullName="unpaired", shortName="unpaired", - doc="Perform unpaired calls (no somatic status detection)", required=false) + @Argument(fullName="unpaired", shortName="unpaired", + doc="Perform unpaired calls (no somatic status detection)", required=false) boolean call_unpaired = false; - boolean call_somatic ; + boolean call_somatic ; - @Argument(fullName="verboseOutput", shortName="verbose", - doc="Verbose output file in text format", required=false) - java.io.File verboseOutput = null; + @Argument(fullName="verboseOutput", shortName="verbose", + doc="Verbose output file in text format", required=false) + java.io.File verboseOutput = null; @Argument(fullName="bedOutput", shortName="bed", - doc="Lightweight bed output file (only positions and events, no stats/annotations)", required=false) + doc="Lightweight bed output file (only positions and events, no stats/annotations)", required=false) java.io.File bedOutput = null; - @Argument(fullName="minCoverage", shortName="minCoverage", - doc="indel calls will be made only at sites with coverage of minCoverage or more reads; with --somatic this value is applied to tumor sample", required=false) - int minCoverage = 6; + @Argument(fullName="minCoverage", shortName="minCoverage", + doc="indel calls will be made only at sites with tumor coverage of minCoverage or more reads; "+ + "with --unpaired (single sample) option, this value is used for minimum sample coverage", required=false) + int minCoverage = 6; - @Argument(fullName="minNormalCoverage", shortName="minNormalCoverage", - doc="used only with --somatic; normal sample must have at least minNormalCoverage or more reads at the site to call germline/somatic indel, otherwise the indel (in tumor) is ignored", required=false) - int minNormalCoverage = 4; + @Argument(fullName="minNormalCoverage", shortName="minNormalCoverage", + doc="used only in default (somatic) mode; normal sample must have at least minNormalCoverage "+ + "or more reads at the site to call germline/somatic indel, otherwise the indel (in tumor) is ignored", required=false) + int minNormalCoverage = 4; - @Argument(fullName="minFraction", shortName="minFraction", - doc="Minimum fraction of reads with CONSENSUS indel at a site, out of all reads covering the site, required for making a call"+ - " (fraction of non-consensus indels at the site is not considered here, see minConsensusFraction)", required=false) - double minFraction = 0.3; + @Argument(fullName="minFraction", shortName="minFraction", + doc="Minimum fraction of reads with CONSENSUS indel at a site, out of all reads covering the site, required for making a call"+ + " (fraction of non-consensus indels at the site is not considered here, see minConsensusFraction)", required=false) + double minFraction = 0.3; - @Argument(fullName="minConsensusFraction", shortName="minConsensusFraction", - doc="Indel call is made only if fraction of CONSENSUS indel observations at a site wrt all indel observations at the site exceeds this threshold", required=false) - double minConsensusFraction = 0.7; + @Argument(fullName="minConsensusFraction", shortName="minConsensusFraction", + doc="Indel call is made only if fraction of CONSENSUS indel observations at a site wrt "+ + "all indel observations at the site exceeds this threshold", required=false) + double minConsensusFraction = 0.7; - @Argument(fullName="minIndelCount", shortName="minCnt", - doc="Minimum count of reads supporting consensus indel required for making the call. "+ - " This filter supercedes minFraction, i.e. indels with acceptable minFraction at low coverage "+ - "(minIndelCount not met) will not pass.", required=false) - int minIndelCount = 0; + @Argument(fullName="minIndelCount", shortName="minCnt", + doc="Minimum count of reads supporting consensus indel required for making the call. "+ + " This filter supercedes minFraction, i.e. indels with acceptable minFraction at low coverage "+ + "(minIndelCount not met) will not pass.", required=false) + int minIndelCount = 0; - @Argument(fullName="refseq", shortName="refseq", - doc="Name of RefSeq transcript annotation file. If specified, indels will be annotated with GENOMIC/UTR/INTRON/CODING and with the gene name", required=false) - String RefseqFileName = null; + @Argument(fullName="refseq", shortName="refseq", + doc="Name of RefSeq transcript annotation file. If specified, indels will be annotated with "+ + "GENOMIC/UTR/INTRON/CODING and with the gene name", required=false) + String RefseqFileName = null; - @Argument(fullName="blacklistedLanes", shortName="BL", - doc="Name of lanes (platform units) that should be ignored. Reads coming from these lanes will never be seen "+ - "by this application, so they will not contribute indels to consider and will not be counted.", required=false) - PlatformUnitFilterHelper dummy; - @Argument(fullName="indel_debug", shortName="idebug", doc="Detailed printout for debugging, do not turn this on",required=false) Boolean DEBUG = false; +//@Argument(fullName="blacklistedLanes", shortName="BL", +// doc="Name of lanes (platform units) that should be ignored. Reads coming from these lanes will never be seen "+ +// "by this application, so they will not contribute indels to consider and will not be counted.", required=false) +//PlatformUnitFilterHelper dummy; + + @Hidden + @Argument(fullName="indel_debug", shortName="idebug", doc="Detailed printout for debugging, do not turn this on", + required=false) Boolean DEBUG = false; @Argument(fullName="window_size", shortName="ws", doc="Size (bp) of the sliding window used for accumulating the coverage. "+ - "May need to be increased to accomodate longer reads or longer deletions.",required=false) int WINDOW_SIZE = 200; + "May need to be increased to accomodate longer reads or longer deletions. A read can be fit into the "+ + "window if its length on the reference (i.e. read length + length of deletion gap(s) if any) is smaller "+ + "than the window size. Reads that do not fit will be ignored, so long deletions can not be called "+ + "if window is too small",required=false) int WINDOW_SIZE = 200; @Argument(fullName="maxNumberOfReads",shortName="mnr",doc="Maximum number of reads to cache in the window; if number of reads exceeds this number,"+ " the window will be skipped and no calls will be made from it",required=false) int MAX_READ_NUMBER = 10000; + + private WindowContext tumor_context; private WindowContext normal_context; private int currentContigIndex = -1; From fe474b77f85f325ed20d6cb6c50dc298d024d03e Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Thu, 15 Sep 2011 16:05:39 -0400 Subject: [PATCH 075/196] Updating docs so printing looks nicer --- .../gatk/walkers/variantutils/VariantValidationAssessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantValidationAssessor.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantValidationAssessor.java index b98646270..ea8549474 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantValidationAssessor.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantValidationAssessor.java @@ -41,7 +41,7 @@ import org.broadinstitute.sting.utils.variantcontext.VariantContextUtils; import java.util.*; /** - * Annotates a validation (from e.g. Sequenom) VCF with QC metrics (HW-equilibrium, % failed probes) + * Annotates a validation (from Sequenom for example) VCF with QC metrics (HW-equilibrium, % failed probes) * *

* The Variant Validation Assessor is a tool for vetting/assessing validation data (containing genotypes). From 4ef6a4598c3704fd5aac5f5302a148ddfedd3958 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Thu, 15 Sep 2011 16:10:34 -0400 Subject: [PATCH 076/196] Updating docs to include output --- .../walkers/varianteval/VariantEvalWalker.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalWalker.java index 266b97af0..28f4f2a56 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalWalker.java @@ -56,6 +56,22 @@ import java.util.*; *

Output

*

* Evaluation tables detailing the results of the eval modules which were applied. + * For example: + *

+ * output.eval.gatkreport:
+ * ##:GATKReport.v0.1 CountVariants : Counts different classes of variants in the sample
+ * CountVariants  CompRod   CpG      EvalRod  JexlExpression  Novelty  nProcessedLoci  nCalledLoci  nRefLoci  nVariantLoci  variantRate ...
+ * CountVariants  dbsnp     CpG      eval     none            all      65900028        135770       0         135770        0.00206024  ...
+ * CountVariants  dbsnp     CpG      eval     none            known    65900028        47068        0         47068         0.00071423  ...
+ * CountVariants  dbsnp     CpG      eval     none            novel    65900028        88702        0         88702         0.00134601  ...
+ * CountVariants  dbsnp     all      eval     none            all      65900028        330818       0         330818        0.00502000  ...
+ * CountVariants  dbsnp     all      eval     none            known    65900028        120685       0         120685        0.00183133  ...
+ * CountVariants  dbsnp     all      eval     none            novel    65900028        210133       0         210133        0.00318866  ...
+ * CountVariants  dbsnp     non_CpG  eval     none            all      65900028        195048       0         195048        0.00295976  ...
+ * CountVariants  dbsnp     non_CpG  eval     none            known    65900028        73617        0         73617         0.00111710  ...
+ * CountVariants  dbsnp     non_CpG  eval     none            novel    65900028        121431       0         121431        0.00184265  ...
+ * ...
+ * 
*

* *

Examples

From 6d02a34bfba1537f294f5a077b24702e539b87a5 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Thu, 15 Sep 2011 16:17:54 -0400 Subject: [PATCH 077/196] Updating docs to include output --- .../variantutils/VariantValidationAssessor.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantValidationAssessor.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantValidationAssessor.java index ea8549474..8eaf976d0 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantValidationAssessor.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantValidationAssessor.java @@ -57,7 +57,16 @@ import java.util.*; * *

Output

*

- * An annotated VCF. + * An annotated VCF. Additionally, a table like the following will be output: + *

+ *     Total number of samples assayed:                  185
+ *     Total number of records processed:                152
+ *     Number of Hardy-Weinberg violations:              34 (22%)
+ *     Number of no-call violations:                     12 (7%)
+ *     Number of homozygous variant violations:          0 (0%)
+ *     Number of records passing all filters:            106 (69%)
+ *     Number of passing records that are polymorphic:   98 (92%)
+ * 
*

* *

Examples

From fd1831b4a520e68b15b6b5b958aa2d04ade4e287 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Thu, 15 Sep 2011 16:25:03 -0400 Subject: [PATCH 078/196] Updating docs to include more details --- .../gatk/walkers/fasta/FastaAlternateReferenceWalker.java | 6 ++++-- .../sting/gatk/walkers/fasta/FastaReferenceWalker.java | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/fasta/FastaAlternateReferenceWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/fasta/FastaAlternateReferenceWalker.java index fd912334f..4e2c17bf6 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/fasta/FastaAlternateReferenceWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/fasta/FastaAlternateReferenceWalker.java @@ -43,8 +43,10 @@ import java.util.List; * Generates an alternative reference sequence over the specified interval. * *

- * Given variant ROD tracks, it replaces the reference bases at variation sites with the bases supplied by the ROD(s). - * Additionally, allows for a "snpmask" ROD to set overlapping bases to 'N'. + * Given variant tracks, it replaces the reference bases at variation sites with the bases supplied by the ROD(s). + * Additionally, allows for one or more "snpmask" VCFs to set overlapping bases to 'N'. + * Note that if there are multiple variants at a site, it takes the first one seen. + * Reference bases for each interval will be output as a separate fasta sequence (named numerically in order). * *

Input

*

diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/fasta/FastaReferenceWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/fasta/FastaReferenceWalker.java index 5f3b37cc8..7ae5c5c75 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/fasta/FastaReferenceWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/fasta/FastaReferenceWalker.java @@ -42,6 +42,9 @@ import java.io.PrintStream; * *

* The output format can be partially controlled using the provided command-line arguments. + * Specify intervals with the usual -L argument to output only the reference bases within your intervals. + * Overlapping intervals are automatically merged; reference bases for each disjoint interval will be output as a + * separate fasta sequence (named numerically in order). * *

Input

*

From 2f58fdb369a3cd4857281dd210427fac6352ca88 Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Thu, 15 Sep 2011 16:26:11 -0400 Subject: [PATCH 079/196] Adding expected output doc to CountCovariates --- .../recalibration/CountCovariatesWalker.java | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/CountCovariatesWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/CountCovariatesWalker.java index 98c8950e3..1bdb70bdd 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/CountCovariatesWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/CountCovariatesWalker.java @@ -76,6 +76,42 @@ import java.util.Map; *

Output

*

* A recalibration table file in CSV format that is used by the TableRecalibration walker. + * It is a comma-separated text file relating the desired covariates to the number of such bases and their rate of mismatch in the genome, and its implied empirical quality score. + * + * The first 20 lines of such a file is shown below. + * * The file begins with a series of comment lines describing: + * ** The number of counted loci + * ** The number of counted bases + * ** The number of skipped loci and the fraction skipped, due to presence in dbSNP or bad reference bases + * + * * After the comments appears a header line indicating which covariates were used as well as the ordering of elements in the subsequent records. + * + * * After the header, data records occur one per line until the end of the file. The first several items on a line are the values of the individual covariates and will change + * depending on which covariates were specified at runtime. The last three items are the data- that is, number of observations for this combination of covariates, number of + * reference mismatches, and the raw empirical quality score calculated by phred-scaling the mismatch rate. + * + *

+ * # Counted Sites    19451059
+ * # Counted Bases    56582018
+ * # Skipped Sites    82666
+ * # Fraction Skipped 1 / 235 bp
+ * ReadGroup,QualityScore,Cycle,Dinuc,nObservations,nMismatches,Qempirical
+ * SRR006446,11,65,CA,9,1,10
+ * SRR006446,11,48,TA,10,0,40
+ * SRR006446,11,67,AA,27,0,40
+ * SRR006446,11,61,GA,11,1,10
+ * SRR006446,12,34,CA,47,1,17
+ * SRR006446,12,30,GA,52,1,17
+ * SRR006446,12,36,AA,352,1,25
+ * SRR006446,12,17,TA,182,11,12
+ * SRR006446,11,48,TG,2,0,40
+ * SRR006446,11,67,AG,1,0,40
+ * SRR006446,12,34,CG,9,0,40
+ * SRR006446,12,30,GG,43,0,40
+ * ERR001876,4,31,AG,1,0,40
+ * ERR001876,4,31,AT,2,2,1
+ * ERR001876,4,31,CA,1,0,40
+ * 
*

* *

Examples

From 9dc6354130b23683c31a7b2c1ef8c2ed94da1946 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Thu, 15 Sep 2011 16:55:24 -0400 Subject: [PATCH 080/196] Oops didn't mean to touch this test before --- .../gatk/walkers/varianteval/VariantEvalIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java index d8f7ad3b6..99622cbf6 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java @@ -42,7 +42,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { "-T VariantEval", "-R " + b37KGReference, "--dbsnp " + b37dbSNP132, - "--eval " + variantEvalTestDataRoot + "CEU.trio.callsForVE.vcf", + "--eval " + variantEvalTestDataRoot + "/CEU.trio.callsForVE.vcf", "-noEV", "-EV TiTvVariantEvaluator", "-ST Sample", From d78e00e5b2cd5e8a1b1aa75209100b039e521442 Mon Sep 17 00:00:00 2001 From: David Roazen Date: Thu, 15 Sep 2011 16:09:07 -0400 Subject: [PATCH 081/196] Renaming VariantAnnotator SnpEff keys This is to head off potential confusion with the output from the SnpEff tool itself, which also uses a key named EFF. --- .../sting/gatk/walkers/annotator/SnpEff.java | 90 ++++++++++--------- .../stratifications/FunctionalClass.java | 4 +- .../VariantAnnotatorIntegrationTest.java | 2 +- .../VariantEvalIntegrationTest.java | 2 +- 4 files changed, 53 insertions(+), 45 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SnpEff.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SnpEff.java index bb3685fb5..4ead77506 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SnpEff.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SnpEff.java @@ -68,23 +68,31 @@ public class SnpEff extends InfoFieldAnnotation implements ExperimentalAnnotatio // Key names for the INFO field annotations we will add to each record, along // with parsing-related information: public enum InfoFieldKey { - EFF (-1), - EFF_IMPACT (0), - EFF_CODON_CHANGE (1), - EFF_AMINO_ACID_CHANGE (2), - EFF_GENE_NAME (3), - EFF_GENE_BIOTYPE (4), - EFF_TRANSCRIPT_ID (6), - EFF_EXON_ID (7); + EFFECT_KEY ("SNPEFF_EFFECT", -1), + IMPACT_KEY ("SNPEFF_IMPACT", 0), + CODON_CHANGE_KEY ("SNPEFF_CODON_CHANGE", 1), + AMINO_ACID_CHANGE_KEY ("SNPEFF_AMINO_ACID_CHANGE", 2), + GENE_NAME_KEY ("SNPEFF_GENE_NAME", 3), + GENE_BIOTYPE_KEY ("SNPEFF_GENE_BIOTYPE", 4), + TRANSCRIPT_ID_KEY ("SNPEFF_TRANSCRIPT_ID", 6), + EXON_ID_KEY ("SNPEFF_EXON_ID", 7); + + // Actual text of the key + private final String keyName; // Index within the effect metadata subfields from the SnpEff EFF annotation // where each key's associated value can be found during parsing. private final int fieldIndex; - InfoFieldKey ( int fieldIndex ) { + InfoFieldKey ( String keyName, int fieldIndex ) { + this.keyName = keyName; this.fieldIndex = fieldIndex; } + public String getKeyName() { + return keyName; + } + public int getFieldIndex() { return fieldIndex; } @@ -292,27 +300,27 @@ public class SnpEff extends InfoFieldAnnotation implements ExperimentalAnnotatio } public List getKeyNames() { - return Arrays.asList( InfoFieldKey.EFF.toString(), - InfoFieldKey.EFF_IMPACT.toString(), - InfoFieldKey.EFF_CODON_CHANGE.toString(), - InfoFieldKey.EFF_AMINO_ACID_CHANGE.toString(), - InfoFieldKey.EFF_GENE_NAME.toString(), - InfoFieldKey.EFF_GENE_BIOTYPE.toString(), - InfoFieldKey.EFF_TRANSCRIPT_ID.toString(), - InfoFieldKey.EFF_EXON_ID.toString() + return Arrays.asList( InfoFieldKey.EFFECT_KEY.getKeyName(), + InfoFieldKey.IMPACT_KEY.getKeyName(), + InfoFieldKey.CODON_CHANGE_KEY.getKeyName(), + InfoFieldKey.AMINO_ACID_CHANGE_KEY.getKeyName(), + InfoFieldKey.GENE_NAME_KEY.getKeyName(), + InfoFieldKey.GENE_BIOTYPE_KEY.getKeyName(), + InfoFieldKey.TRANSCRIPT_ID_KEY.getKeyName(), + InfoFieldKey.EXON_ID_KEY.getKeyName() ); } public List getDescriptions() { return Arrays.asList( - new VCFInfoHeaderLine(InfoFieldKey.EFF.toString(), 1, VCFHeaderLineType.String, "The highest-impact effect resulting from the current variant (or one of the highest-impact effects, if there is a tie)"), - new VCFInfoHeaderLine(InfoFieldKey.EFF_IMPACT.toString(), 1, VCFHeaderLineType.String, "Impact of the highest-impact effect resulting from the current variant " + Arrays.toString(EffectImpact.values())), - new VCFInfoHeaderLine(InfoFieldKey.EFF_CODON_CHANGE.toString(), 1, VCFHeaderLineType.String, "Old/New codon for the highest-impact effect resulting from the current variant"), - new VCFInfoHeaderLine(InfoFieldKey.EFF_AMINO_ACID_CHANGE.toString(), 1, VCFHeaderLineType.String, "Old/New amino acid for the highest-impact effect resulting from the current variant"), - new VCFInfoHeaderLine(InfoFieldKey.EFF_GENE_NAME.toString(), 1, VCFHeaderLineType.String, "Gene name for the highest-impact effect resulting from the current variant"), - new VCFInfoHeaderLine(InfoFieldKey.EFF_GENE_BIOTYPE.toString(), 1, VCFHeaderLineType.String, "Gene biotype for the highest-impact effect resulting from the current variant"), - new VCFInfoHeaderLine(InfoFieldKey.EFF_TRANSCRIPT_ID.toString(), 1, VCFHeaderLineType.String, "Transcript ID for the highest-impact effect resulting from the current variant"), - new VCFInfoHeaderLine(InfoFieldKey.EFF_EXON_ID.toString(), 1, VCFHeaderLineType.String, "Exon ID for the highest-impact effect resulting from the current variant") + new VCFInfoHeaderLine(InfoFieldKey.EFFECT_KEY.getKeyName(), 1, VCFHeaderLineType.String, "The highest-impact effect resulting from the current variant (or one of the highest-impact effects, if there is a tie)"), + new VCFInfoHeaderLine(InfoFieldKey.IMPACT_KEY.getKeyName(), 1, VCFHeaderLineType.String, "Impact of the highest-impact effect resulting from the current variant " + Arrays.toString(EffectImpact.values())), + new VCFInfoHeaderLine(InfoFieldKey.CODON_CHANGE_KEY.getKeyName(), 1, VCFHeaderLineType.String, "Old/New codon for the highest-impact effect resulting from the current variant"), + new VCFInfoHeaderLine(InfoFieldKey.AMINO_ACID_CHANGE_KEY.getKeyName(), 1, VCFHeaderLineType.String, "Old/New amino acid for the highest-impact effect resulting from the current variant"), + new VCFInfoHeaderLine(InfoFieldKey.GENE_NAME_KEY.getKeyName(), 1, VCFHeaderLineType.String, "Gene name for the highest-impact effect resulting from the current variant"), + new VCFInfoHeaderLine(InfoFieldKey.GENE_BIOTYPE_KEY.getKeyName(), 1, VCFHeaderLineType.String, "Gene biotype for the highest-impact effect resulting from the current variant"), + new VCFInfoHeaderLine(InfoFieldKey.TRANSCRIPT_ID_KEY.getKeyName(), 1, VCFHeaderLineType.String, "Transcript ID for the highest-impact effect resulting from the current variant"), + new VCFInfoHeaderLine(InfoFieldKey.EXON_ID_KEY.getKeyName(), 1, VCFHeaderLineType.String, "Exon ID for the highest-impact effect resulting from the current variant") ); } @@ -375,16 +383,16 @@ public class SnpEff extends InfoFieldAnnotation implements ExperimentalAnnotatio } try { - impact = EffectImpact.valueOf(effectMetadata[InfoFieldKey.EFF_IMPACT.getFieldIndex()]); + impact = EffectImpact.valueOf(effectMetadata[InfoFieldKey.IMPACT_KEY.getFieldIndex()]); } catch ( IllegalArgumentException e ) { - parseError(String.format("Unrecognized value for effect impact: %s", effectMetadata[InfoFieldKey.EFF_IMPACT.getFieldIndex()])); + parseError(String.format("Unrecognized value for effect impact: %s", effectMetadata[InfoFieldKey.IMPACT_KEY.getFieldIndex()])); } - codonChange = effectMetadata[InfoFieldKey.EFF_CODON_CHANGE.getFieldIndex()]; - aminoAcidChange = effectMetadata[InfoFieldKey.EFF_AMINO_ACID_CHANGE.getFieldIndex()]; - geneName = effectMetadata[InfoFieldKey.EFF_GENE_NAME.getFieldIndex()]; - geneBiotype = effectMetadata[InfoFieldKey.EFF_GENE_BIOTYPE.getFieldIndex()]; + codonChange = effectMetadata[InfoFieldKey.CODON_CHANGE_KEY.getFieldIndex()]; + aminoAcidChange = effectMetadata[InfoFieldKey.AMINO_ACID_CHANGE_KEY.getFieldIndex()]; + geneName = effectMetadata[InfoFieldKey.GENE_NAME_KEY.getFieldIndex()]; + geneBiotype = effectMetadata[InfoFieldKey.GENE_BIOTYPE_KEY.getFieldIndex()]; if ( effectMetadata[SNPEFF_CODING_FIELD_INDEX].trim().length() > 0 ) { try { @@ -398,8 +406,8 @@ public class SnpEff extends InfoFieldAnnotation implements ExperimentalAnnotatio coding = EffectCoding.UNKNOWN; } - transcriptID = effectMetadata[InfoFieldKey.EFF_TRANSCRIPT_ID.getFieldIndex()]; - exonID = effectMetadata[InfoFieldKey.EFF_EXON_ID.getFieldIndex()]; + transcriptID = effectMetadata[InfoFieldKey.TRANSCRIPT_ID_KEY.getFieldIndex()]; + exonID = effectMetadata[InfoFieldKey.EXON_ID_KEY.getFieldIndex()]; } private void parseError ( String message ) { @@ -443,14 +451,14 @@ public class SnpEff extends InfoFieldAnnotation implements ExperimentalAnnotatio public Map getAnnotations() { Map annotations = new LinkedHashMap(Utils.optimumHashSize(InfoFieldKey.values().length)); - addAnnotation(annotations, InfoFieldKey.EFF.toString(), effect.toString()); - addAnnotation(annotations, InfoFieldKey.EFF_IMPACT.toString(), impact.toString()); - addAnnotation(annotations, InfoFieldKey.EFF_CODON_CHANGE.toString(), codonChange); - addAnnotation(annotations, InfoFieldKey.EFF_AMINO_ACID_CHANGE.toString(), aminoAcidChange); - addAnnotation(annotations, InfoFieldKey.EFF_GENE_NAME.toString(), geneName); - addAnnotation(annotations, InfoFieldKey.EFF_GENE_BIOTYPE.toString(), geneBiotype); - addAnnotation(annotations, InfoFieldKey.EFF_TRANSCRIPT_ID.toString(), transcriptID); - addAnnotation(annotations, InfoFieldKey.EFF_EXON_ID.toString(), exonID); + addAnnotation(annotations, InfoFieldKey.EFFECT_KEY.getKeyName(), effect.toString()); + addAnnotation(annotations, InfoFieldKey.IMPACT_KEY.getKeyName(), impact.toString()); + addAnnotation(annotations, InfoFieldKey.CODON_CHANGE_KEY.getKeyName(), codonChange); + addAnnotation(annotations, InfoFieldKey.AMINO_ACID_CHANGE_KEY.getKeyName(), aminoAcidChange); + addAnnotation(annotations, InfoFieldKey.GENE_NAME_KEY.getKeyName(), geneName); + addAnnotation(annotations, InfoFieldKey.GENE_BIOTYPE_KEY.getKeyName(), geneBiotype); + addAnnotation(annotations, InfoFieldKey.TRANSCRIPT_ID_KEY.getKeyName(), transcriptID); + addAnnotation(annotations, InfoFieldKey.EXON_ID_KEY.getKeyName(), exonID); return annotations; } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java index a32857ffc..88ffcaaeb 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java @@ -62,8 +62,8 @@ public class FunctionalClass extends VariantStratifier { annotationId++; } while (eval.hasAttribute(key)); - } else if ( eval.hasAttribute(SnpEff.InfoFieldKey.EFF.name() ) ) { - SnpEff.EffectType snpEffType = SnpEff.EffectType.valueOf(eval.getAttribute(SnpEff.InfoFieldKey.EFF.name()).toString()); + } else if ( eval.hasAttribute(SnpEff.InfoFieldKey.EFFECT_KEY.getKeyName() ) ) { + SnpEff.EffectType snpEffType = SnpEff.EffectType.valueOf(eval.getAttribute(SnpEff.InfoFieldKey.EFFECT_KEY.getKeyName()).toString()); if ( snpEffType == SnpEff.EffectType.STOP_GAINED ) type = FunctionalType.nonsense; else if ( snpEffType == SnpEff.EffectType.NON_SYNONYMOUS_CODING ) diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorIntegrationTest.java index f902ce276..08baae7a7 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorIntegrationTest.java @@ -134,7 +134,7 @@ public class VariantAnnotatorIntegrationTest extends WalkerTest { validationDataLocation + "1kg_exomes_unfiltered.AFR.unfiltered.vcf --snpEffFile " + validationDataLocation + "snpEff.AFR.unfiltered.vcf -L 1:1-1,500,000", 1, - Arrays.asList("a1c3ba9efc28ee0606339604095076ea") + Arrays.asList("486fc6a5ca1819f5ab180d5d72b1ebc9") ); executeTest("Testing SnpEff annotations", spec); } diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java index 99622cbf6..b90e6d0ff 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java @@ -32,7 +32,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { 1, Arrays.asList("f5f811ceb973d7fd6c1b2b734f1b2b12") ); - executeTest("testStratifySamplesAndExcludeMonomorphicSites", spec); + executeTest("testFunctionClassWithSnpeff", spec); } @Test From e6e9b08c9a47640f9be32b47f495174118636a5c Mon Sep 17 00:00:00 2001 From: Menachem Fromer Date: Thu, 15 Sep 2011 18:51:09 -0400 Subject: [PATCH 083/196] Must provide alleles VCF to UGCallVariants --- .../sting/gatk/walkers/genotyper/UGCallVariants.java | 1 - 1 file changed, 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UGCallVariants.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UGCallVariants.java index 500b11360..d88e55687 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UGCallVariants.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UGCallVariants.java @@ -30,7 +30,6 @@ import org.broadinstitute.sting.commandline.Output; import org.broadinstitute.sting.commandline.RodBinding; import org.broadinstitute.sting.gatk.contexts.AlignmentContext; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; -import org.broadinstitute.sting.gatk.datasources.rmd.ReferenceOrderedDataSource; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; import org.broadinstitute.sting.gatk.walkers.RodWalker; import org.broadinstitute.sting.utils.SampleUtils; From 9fdf1f8eb663858cacafd8fb339d098cdce4b96d Mon Sep 17 00:00:00 2001 From: Christopher Hartl Date: Thu, 15 Sep 2011 21:05:22 -0400 Subject: [PATCH 084/196] Fix some doc formatting for Depth of Coverage --- .../gatk/walkers/coverage/DepthOfCoverageWalker.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/DepthOfCoverageWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/DepthOfCoverageWalker.java index 3a18fe610..86f97a36c 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/DepthOfCoverageWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/DepthOfCoverageWalker.java @@ -69,14 +69,23 @@ import java.util.*; *

Output

*

* Tables pertaining to different coverage summaries. Suffix on the table files declares the contents: + *

* - no suffix: per locus coverage + *

* - _summary: total, mean, median, quartiles, and threshold proportions, aggregated over all bases + *

* - _statistics: coverage histograms (# locus with X coverage), aggregated over all bases + *

* - _interval_summary: total, mean, median, quartiles, and threshold proportions, aggregated per interval + *

* - _interval_statistics: 2x2 table of # of intervals covered to >= X depth in >=Y samples + *

* - _gene_summary: total, mean, median, quartiles, and threshold proportions, aggregated per gene + *

* - _gene_statistics: 2x2 table of # of genes covered to >= X depth in >= Y samples + *

* - _cumulative_coverage_counts: coverage histograms (# locus with >= X coverage), aggregated over all bases + *

* - _cumulative_coverage_proportions: proprotions of loci with >= X coverage, aggregated over all bases *

* From 939babc820cc5174a1d97a8b6bdb992ca6cedc09 Mon Sep 17 00:00:00 2001 From: Christopher Hartl Date: Thu, 15 Sep 2011 21:05:51 -0400 Subject: [PATCH 085/196] Updating formating for ValidationAmplicons GATK docs --- .../sting/gatk/walkers/validation/ValidationAmplicons.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/validation/ValidationAmplicons.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/validation/ValidationAmplicons.java index 01e8cd321..48cba6a1a 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/validation/ValidationAmplicons.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/validation/ValidationAmplicons.java @@ -61,7 +61,7 @@ import java.util.List; * CACGTTCGGcttgtgcagagcctcaaggtcatccagaggtgatAGTTTAGGGCCCTCTCAAGTCTTTCCNGTGCGCATGG[GT/AC*]CAGCCCTGGGCACCTGTNNNNNNNNNNNNNTGCTCATGGCCTTCTAGATTCCCAGGAAATGTCAGAGCTTTTCAAAGCCC *
* are amplicon sequences resulting from running the tool. The flags (preceding the sequence itself) can be: - * + *
  * Valid                     // amplicon is valid
  * SITE_IS_FILTERED=1        // validation site is not marked 'PASS' or '.' in its filter field ("you are trying to validate a filtered variant")
  * VARIANT_TOO_NEAR_PROBE=1  // there is a variant too near to the variant to be validated, potentially shifting the mass-spec peak
@@ -72,10 +72,10 @@ import java.util.List;
  * END_TOO_CLOSE,            // variant is too close to the end of the amplicon region to give sequenom a good chance to find a suitable primer
  * NO_VARIANTS_FOUND,        // no variants found within the amplicon region
  * INDEL_OVERLAPS_VALIDATION_SITE, // an insertion or deletion interferes directly with the site to be validated (i.e. insertion directly preceding or postceding, or a deletion that spans the site itself)
- * 

+ *

* *

Examples

- *

+ * 
  *    java
  *      -jar GenomeAnalysisTK.jar
  *      -T ValidationAmplicons

From 33967a4e0c09e85cc4dc1d0eb83fe6feef80c46d Mon Sep 17 00:00:00 2001
From: Khalid Shakir 
Date: Fri, 16 Sep 2011 12:46:07 -0400
Subject: [PATCH 087/196] Fixed issue reported by chartl where cloned functions
 lost tags on @Inputs. Updated ExampleUnifiedGenotyper.scala with new syntax.

---
 .../examples/ExampleUnifiedGenotyper.scala    |  6 +--
 .../sting/queue/extensions/gatk/RodBind.scala |  2 +-
 .../queue/extensions/gatk/TaggedFile.scala    |  2 +-
 .../sting/queue/function/QFunction.scala      | 16 +-------
 .../{function => util}/FileExtension.scala    |  2 +-
 .../sting/queue/util/IOUtils.scala            | 40 ++++++++++++++-----
 6 files changed, 36 insertions(+), 32 deletions(-)
 rename public/scala/src/org/broadinstitute/sting/queue/{function => util}/FileExtension.scala (89%)

diff --git a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/examples/ExampleUnifiedGenotyper.scala b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/examples/ExampleUnifiedGenotyper.scala
index 1d473b210..9bddfd97c 100644
--- a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/examples/ExampleUnifiedGenotyper.scala
+++ b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/examples/ExampleUnifiedGenotyper.scala
@@ -56,15 +56,15 @@ class ExampleUnifiedGenotyper extends QScript {
     genotyper.input_file :+= qscript.bamFile
     genotyper.out = swapExt(qscript.bamFile, "bam", "unfiltered.vcf")
 
-    evalUnfiltered.rodBind :+= RodBind("eval", "VCF", genotyper.out)
+    evalUnfiltered.eval :+= genotyper.out
     evalUnfiltered.out = swapExt(genotyper.out, "vcf", "eval")
 
-    variantFilter.rodBind :+= RodBind("variant", "VCF", genotyper.out)
+    variantFilter.variant = genotyper.out
     variantFilter.out = swapExt(qscript.bamFile, "bam", "filtered.vcf")
     variantFilter.filterName = filterNames
     variantFilter.filterExpression = filterExpressions.map("\"" + _ + "\"")
 
-    evalFiltered.rodBind :+= RodBind("eval", "VCF", variantFilter.out)
+    evalFiltered.eval :+= variantFilter.out
     evalFiltered.out = swapExt(variantFilter.out, "vcf", "eval")
 
     add(genotyper, evalUnfiltered)
diff --git a/public/scala/src/org/broadinstitute/sting/queue/extensions/gatk/RodBind.scala b/public/scala/src/org/broadinstitute/sting/queue/extensions/gatk/RodBind.scala
index 42f63e225..b4c5d91d3 100644
--- a/public/scala/src/org/broadinstitute/sting/queue/extensions/gatk/RodBind.scala
+++ b/public/scala/src/org/broadinstitute/sting/queue/extensions/gatk/RodBind.scala
@@ -1,7 +1,7 @@
 package org.broadinstitute.sting.queue.extensions.gatk
 
 import java.io.File
-import org.broadinstitute.sting.queue.function.FileExtension
+import org.broadinstitute.sting.queue.util.FileExtension
 import java.lang.String
 
 /**
diff --git a/public/scala/src/org/broadinstitute/sting/queue/extensions/gatk/TaggedFile.scala b/public/scala/src/org/broadinstitute/sting/queue/extensions/gatk/TaggedFile.scala
index ed8158b49..b19f9e430 100644
--- a/public/scala/src/org/broadinstitute/sting/queue/extensions/gatk/TaggedFile.scala
+++ b/public/scala/src/org/broadinstitute/sting/queue/extensions/gatk/TaggedFile.scala
@@ -1,7 +1,7 @@
 package org.broadinstitute.sting.queue.extensions.gatk
 
 import java.io.File
-import org.broadinstitute.sting.queue.function.FileExtension
+import org.broadinstitute.sting.queue.util.FileExtension
 
 /**
  * Used to provide tagged -I input_file arguments to the GATK.
diff --git a/public/scala/src/org/broadinstitute/sting/queue/function/QFunction.scala b/public/scala/src/org/broadinstitute/sting/queue/function/QFunction.scala
index c905581fa..500f7b200 100644
--- a/public/scala/src/org/broadinstitute/sting/queue/function/QFunction.scala
+++ b/public/scala/src/org/broadinstitute/sting/queue/function/QFunction.scala
@@ -387,25 +387,11 @@ trait QFunction extends Logging with QJobReport {
    */
   protected def canon(value: Any) = {
     value match {
-      case fileExtension: FileExtension =>
-        val newFile = absolute(fileExtension);
-        val newFileExtension = fileExtension.withPath(newFile.getPath)
-        newFileExtension
-      case file: File =>
-        if (file.getClass != classOf[File])
-          throw new QException("Extensions of file must also extend with FileExtension so that the path can be modified.");
-        absolute(file)
+      case file: File => IOUtils.absolute(commandDirectory, file)
       case x => x
     }
   }
 
-  /**
-   * Returns the absolute path to the file relative to the run directory and the job command directory.
-   * @param file File to root relative to the command directory if it is not already absolute.
-   * @return The absolute path to file.
-   */
-  private def absolute(file: File) = IOUtils.absolute(commandDirectory, file)
-
   /**
    * Scala sugar type for checking annotation required and exclusiveOf.
    */
diff --git a/public/scala/src/org/broadinstitute/sting/queue/function/FileExtension.scala b/public/scala/src/org/broadinstitute/sting/queue/util/FileExtension.scala
similarity index 89%
rename from public/scala/src/org/broadinstitute/sting/queue/function/FileExtension.scala
rename to public/scala/src/org/broadinstitute/sting/queue/util/FileExtension.scala
index e2394a5bf..9b6e52c8e 100644
--- a/public/scala/src/org/broadinstitute/sting/queue/function/FileExtension.scala
+++ b/public/scala/src/org/broadinstitute/sting/queue/util/FileExtension.scala
@@ -1,4 +1,4 @@
-package org.broadinstitute.sting.queue.function
+package org.broadinstitute.sting.queue.util
 
 import java.io.File
 
diff --git a/public/scala/src/org/broadinstitute/sting/queue/util/IOUtils.scala b/public/scala/src/org/broadinstitute/sting/queue/util/IOUtils.scala
index 79ffa8cb9..b17ccc0d5 100644
--- a/public/scala/src/org/broadinstitute/sting/queue/util/IOUtils.scala
+++ b/public/scala/src/org/broadinstitute/sting/queue/util/IOUtils.scala
@@ -3,6 +3,7 @@ package org.broadinstitute.sting.queue.util
 import org.apache.commons.io.FileUtils
 import java.io.{FileReader, File}
 import org.broadinstitute.sting.utils.exceptions.UserException
+import org.broadinstitute.sting.queue.QException
 
 /**
  * A collection of utilities for modifying java.io.
@@ -12,7 +13,7 @@ object IOUtils extends Logging {
    * Checks if the temp directory has been setup and throws an exception if they user hasn't set it correctly.
    * @param tempDir Temporary directory.
    */
-  def checkTempDir(tempDir: File) = {
+  def checkTempDir(tempDir: File) {
     val tempDirPath = tempDir.getAbsolutePath
     // Keeps the user from leaving the temp directory as the default, and on Macs from having pluses
     // in the path which can cause problems with the Google Reflections library.
@@ -20,7 +21,7 @@ object IOUtils extends Logging {
     if (tempDirPath.startsWith("/var/folders/") || (tempDirPath == "/tmp") || (tempDirPath == "/tmp/"))
       throw new UserException.BadTmpDir("java.io.tmpdir must be explicitly set")
     if (!tempDir.exists && !tempDir.mkdirs)
-      throw new UserException.BadTmpDir("Could not create directory: " + tempDir.getAbsolutePath())
+      throw new UserException.BadTmpDir("Could not create directory: " + tempDir.getAbsolutePath)
   }
 
   /**
@@ -35,9 +36,9 @@ object IOUtils extends Logging {
        throw new UserException.BadTmpDir("Could not create temp directory: " + tempDirParent)
     val temp = File.createTempFile(prefix + "-", suffix, tempDirParent)
     if (!temp.delete)
-      throw new UserException.BadTmpDir("Could not delete sub file: " + temp.getAbsolutePath())
+      throw new UserException.BadTmpDir("Could not delete sub file: " + temp.getAbsolutePath)
     if (!temp.mkdir)
-      throw new UserException.BadTmpDir("Could not create sub directory: " + temp.getAbsolutePath())
+      throw new UserException.BadTmpDir("Could not create sub directory: " + temp.getAbsolutePath)
     absolute(temp)
   }
 
@@ -46,7 +47,7 @@ object IOUtils extends Logging {
    * @param file File to write to.
    * @param content Content to write.
    */
-  def writeContents(file: File, content: String) =  FileUtils.writeStringToFile(file, content)
+  def writeContents(file: File, content: String) { FileUtils.writeStringToFile(file, content) }
 
   /**
    * Reads content of a file into a string.
@@ -146,10 +147,12 @@ object IOUtils extends Logging {
    * @return The absolute path to the file in the parent dir if the path was not absolute, otherwise the original path.
    */
   def absolute(parent: File, file: File): File = {
-    if (file.isAbsolute)
-      absolute(file)
-    else
-      absolute(new File(parent, file.getPath))
+    val newPath =
+      if (file.isAbsolute)
+        absolutePath(file)
+      else
+        absolutePath(new File(parent, file.getPath))
+    replacePath(file, newPath)
   }
 
   /**
@@ -159,12 +162,16 @@ object IOUtils extends Logging {
    * @return the absolute path to the file.
    */
   def absolute(file: File) = {
+    replacePath(file, absolutePath(file))
+  }
+
+  private def absolutePath(file: File) = {
     var fileAbs = file.getAbsoluteFile
     var names = List.empty[String]
     while (fileAbs != null) {
       val name = fileAbs.getName
       fileAbs = fileAbs.getParentFile
-      
+
       if (name == ".") {
         /* skip */
 
@@ -190,7 +197,18 @@ object IOUtils extends Logging {
       }
     }
 
-    new File(names.mkString("/", "/", ""))
+    names.mkString("/", "/", "")
+  }
+
+  private def replacePath(file: File, path: String) = {
+    file match {
+      case fileExtension: FileExtension =>
+        fileExtension.withPath(path)
+      case file: File =>
+        if (file.getClass != classOf[File])
+          throw new QException("Sub classes of java.io.File must also implement FileExtension so that the path can be modified.")
+        new File(path)
+    }
   }
 
   /**

From 7fa1e237d9e1e41fbdcd42f069df7c658f523bc7 Mon Sep 17 00:00:00 2001
From: Guillermo del Angel 
Date: Fri, 16 Sep 2011 12:53:54 -0400
Subject: [PATCH 088/196] Forgot to git stash pop new MD5's for CombineVariants
 integration test

---
 .../walkers/variantutils/CombineVariantsIntegrationTest.java  | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java
index 3267173a7..35495d797 100755
--- a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java
+++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java
@@ -90,7 +90,7 @@ public class CombineVariantsIntegrationTest extends WalkerTest {
 
     @Test public void combineTrioCalls() { combine2("CEU.trio.2010_03.genotypes.vcf.gz", "YRI.trio.2010_03.genotypes.vcf.gz", "", "1d5a021387a8a86554db45a29f66140f"); } // official project VCF files in tabix format
     @Test public void combineTrioCallsMin() { combine2("CEU.trio.2010_03.genotypes.vcf.gz", "YRI.trio.2010_03.genotypes.vcf.gz", " -minimalVCF", "20163d60f18a46496f6da744ab5cc0f9"); } // official project VCF files in tabix format
-    @Test public void combine2Indels() { combine2("CEU.dindel.vcf4.trio.2010_06.indel.genotypes.vcf", "CEU.dindel.vcf4.low_coverage.2010_06.indel.genotypes.vcf", "", "f1cf095c2fe9641b7ca1f8ee2c46fd4a"); }
+    @Test public void combine2Indels() { combine2("CEU.dindel.vcf4.trio.2010_06.indel.genotypes.vcf", "CEU.dindel.vcf4.low_coverage.2010_06.indel.genotypes.vcf", "", "312a22aedb088b678bc891f1a1b03c91"); }
 
     @Test public void combineSNPsAndIndels() { combine2("CEU.trio.2010_03.genotypes.vcf.gz", "CEU.dindel.vcf4.low_coverage.2010_06.indel.genotypes.vcf", "", "e144b6283765494bfe8189ac59965083"); }
 
@@ -110,7 +110,7 @@ public class CombineVariantsIntegrationTest extends WalkerTest {
                         " -priority NA19240_BGI,NA19240_ILLUMINA,NA19240_WUGSC,denovoInfo" +
                         " -genotypeMergeOptions UNIQUIFY -L 1"),
                 1,
-                Arrays.asList("1de95f91ca15d2a8856de35dee0ce33e"));
+                Arrays.asList("35acb0f15f9cd18c653ede4e15e365c9"));
         executeTest("threeWayWithRefs", spec);
     }
 

From cb4a50b1478bb54c19ae57703733e4f330bd7a2f Mon Sep 17 00:00:00 2001
From: Ryan Poplin 
Date: Sat, 17 Sep 2011 16:42:49 -0400
Subject: [PATCH 089/196] Adding ability to try both small and large kmer
 lengths. Highest likelihood wins.

---
 .../sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java     | 1 +
 1 file changed, 1 insertion(+)

diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java
index 87dd37bf6..a5cb00a5d 100755
--- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java
@@ -40,6 +40,7 @@ import org.broadinstitute.sting.utils.codecs.vcf.VCFConstants;
 import org.broadinstitute.sting.utils.exceptions.ReviewedStingException;
 import org.broadinstitute.sting.utils.pileup.PileupElement;
 import org.broadinstitute.sting.utils.pileup.ReadBackedExtendedEventPileup;
+import org.broadinstitute.sting.utils.pileup.ReadBackedExtendedEventPileupImpl;
 import org.broadinstitute.sting.utils.pileup.ReadBackedPileup;
 import org.broadinstitute.sting.utils.variantcontext.*;
 

From 091c7197cdfa63e341e72230e41f187cf66931e0 Mon Sep 17 00:00:00 2001
From: Roger Zurawicki 
Date: Sun, 18 Sep 2011 19:21:51 -0400
Subject: [PATCH 090/196] Fixed memory leak and bug with deletions in clipping

The ClippingOp clip cigar function would run into a endless loop if the parameter were out of the reads range, I stopped the bug.
* There is no check to make sure the read coordinate are covered by the read though
When Hard clipping to interval, I added a check for deletions.
NOTE: method works for NA12878 WEx but needs to be more thoroughly tested/optimized
---
 .../sting/utils/clipreads/ClippingOp.java     |  4 +++
 .../sting/utils/clipreads/ReadClipper.java    | 25 ++++++++++++++++---
 2 files changed, 26 insertions(+), 3 deletions(-)

diff --git a/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingOp.java b/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingOp.java
index bc200372f..951ab265c 100644
--- a/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingOp.java
+++ b/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingOp.java
@@ -324,6 +324,8 @@ public class ClippingOp {
 
                 if (index <= stop && cigarElementIterator.hasNext())
                     cigarElement = cigarElementIterator.next();
+                else
+                    break;
             }
 
             // add the remaining cigar elements
@@ -363,6 +365,8 @@ public class ClippingOp {
                 index += shift;
                 if (index < start && cigarElementIterator.hasNext())
                     cigarElement = cigarElementIterator.next();
+                else
+                    break;
             }
 
             // check if we are hard clipping indels
diff --git a/public/java/src/org/broadinstitute/sting/utils/clipreads/ReadClipper.java b/public/java/src/org/broadinstitute/sting/utils/clipreads/ReadClipper.java
index 26c25850a..a8a3fad9e 100644
--- a/public/java/src/org/broadinstitute/sting/utils/clipreads/ReadClipper.java
+++ b/public/java/src/org/broadinstitute/sting/utils/clipreads/ReadClipper.java
@@ -1,6 +1,8 @@
 package org.broadinstitute.sting.utils.clipreads;
 
 import com.google.java.contract.Requires;
+import net.sf.samtools.CigarElement;
+import net.sf.samtools.CigarOperator;
 import net.sf.samtools.SAMRecord;
 import org.broadinstitute.sting.utils.exceptions.ReviewedStingException;
 import org.broadinstitute.sting.utils.sam.ReadUtils;
@@ -56,17 +58,34 @@ public class ReadClipper {
         return hardClipByReferenceCoordinates(refStart, -1);
     }
 
+    private int numDeletions(SAMRecord read) {
+        int result = 0;
+        for (CigarElement e: read.getCigar().getCigarElements()) {
+            if ( e.getOperator() == CigarOperator.DELETION || e.getOperator() == CigarOperator.D )
+                result =+ e.getLength();
+        }
+        return result;
+    }
+
     private SAMRecord hardClipByReferenceCoordinates(int refStart, int refStop) {
         int start = (refStart < 0) ? 0 : ReadUtils.getReadCoordinateForReferenceCoordinate(read, refStart);
-        int stop =  (refStop  < 0) ? read.getReadLength() - 1 : ReadUtils.getReadCoordinateForReferenceCoordinate(read, refStop);
+        int stop =  (refStop  < 0) ? read.getReadLength() - 1: ReadUtils.getReadCoordinateForReferenceCoordinate(read, refStop);
 
-        if (start < 0 || stop > read.getReadLength() - 1)
+        if (start < 0 || stop > read.getReadLength() - 1 + numDeletions(read))
             throw new ReviewedStingException("Trying to clip before the start or after the end of a read");
 
-        // TODO add requires statement/check in the Hardclip function
+        // TODO add check in the Hardclip function
         if ( start > stop )
             stop = ReadUtils.getReadCoordinateForReferenceCoordinate(read, ReadUtils.getRefCoordSoftUnclippedEnd(read));
 
+
+        //This tries to fix the bug where the deletion is counted a read base and as a result, the hardCLipper runs into
+        //an endless loop when hard clipping the cigar string because the read coordinates are not covered by the read
+        stop -= numDeletions(read);
+        if ( start > stop )
+            start -= numDeletions(read);
+
+
         //System.out.println("Clipping start/stop: " + start + "/" + stop);
         this.addOp(new ClippingOp(start, stop));
         SAMRecord clippedRead = clipRead(ClippingRepresentation.HARDCLIP_BASES);

From bed78b47e090e19274273a1a552e0e40c82e0161 Mon Sep 17 00:00:00 2001
From: Mark DePristo 
Date: Sun, 18 Sep 2011 20:18:18 -0400
Subject: [PATCH 091/196] Marginally better formating, with hours the default
 time

---
 public/R/queueJobReport.R | 26 ++++++++++++++------------
 1 file changed, 14 insertions(+), 12 deletions(-)

diff --git a/public/R/queueJobReport.R b/public/R/queueJobReport.R
index a24d269c9..9f37aa038 100644
--- a/public/R/queueJobReport.R
+++ b/public/R/queueJobReport.R
@@ -12,14 +12,14 @@ if ( onCMDLine ) {
   inputFileName = args[1]
   outputPDF = args[2]
 } else {
-  #inputFileName = "~/Desktop/broadLocal/GATK/unstable/report.txt"
-  inputFileName = "/humgen/gsa-hpprojects/dev/depristo/oneOffProjects/Q-25718@node1149.jobreport.txt"
+  inputFileName = "~/Desktop/Q-30033@gsa1.jobreport.txt"
+  #inputFileName = "/humgen/gsa-hpprojects/dev/depristo/oneOffProjects/Q-25718@node1149.jobreport.txt"
   #inputFileName = "/humgen/gsa-hpprojects/dev/depristo/oneOffProjects/rodPerformanceGoals/history/report.082711.txt"
   outputPDF = NA
 }
 
-RUNTIME_UNITS = "(sec)"
-ORIGINAL_UNITS_TO_SECONDS = 1/1000
+RUNTIME_UNITS = "(hours)"
+ORIGINAL_UNITS_TO_SECONDS = 1/1000/60/60
 
 # 
 # Helper function to aggregate all of the jobs in the report across all tables
@@ -33,7 +33,7 @@ allJobsFromReport <- function(report) {
 #
 # Creates segmentation plots of time (x) vs. job (y) with segments for the duration of the job
 #
-plotJobsGantt <- function(gatkReport, sortOverall) {
+plotJobsGantt <- function(gatkReport, sortOverall, includeText) {
   allJobs = allJobsFromReport(gatkReport)
   if ( sortOverall ) {
     title = "All jobs, by analysis, by start time"
@@ -44,16 +44,18 @@ plotJobsGantt <- function(gatkReport, sortOverall) {
   }
   allJobs$index = 1:nrow(allJobs)
   minTime = min(allJobs$startTime)
-  allJobs$relStartTime = allJobs$startTime - minTime
-  allJobs$relDoneTime = allJobs$doneTime - minTime
+  allJobs$relStartTime = (allJobs$startTime - minTime) * ORIGINAL_UNITS_TO_SECONDS
+  allJobs$relDoneTime = (allJobs$doneTime - minTime) * ORIGINAL_UNITS_TO_SECONDS
   allJobs$ganttName = paste(allJobs$jobName, "@", allJobs$exechosts)
   maxRelTime = max(allJobs$relDoneTime)
   p <- ggplot(data=allJobs, aes(x=relStartTime, y=index, color=analysisName))
-  p <- p + geom_segment(aes(xend=relDoneTime, yend=index), size=2, arrow=arrow(length = unit(0.1, "cm")))
-  p <- p + geom_text(aes(x=relDoneTime, label=ganttName, hjust=-0.2), size=2)
+  p <- p + theme_bw()
+  p <- p + geom_segment(aes(xend=relDoneTime, yend=index), size=1, arrow=arrow(length = unit(0.1, "cm")))
+  if ( includeText )
+    p <- p + geom_text(aes(x=relDoneTime, label=ganttName, hjust=-0.2), size=2)
   p <- p + xlim(0, maxRelTime * 1.1)
   p <- p + xlab(paste("Start time (relative to first job)", RUNTIME_UNITS))
-  p <- p + ylab("Job")
+  p <- p + ylab("Job number")
   p <- p + opts(title=title)
   print(p)
 }
@@ -155,8 +157,8 @@ if ( ! is.na(outputPDF) ) {
   pdf(outputPDF, height=8.5, width=11)
 } 
 
-plotJobsGantt(gatkReportData, T)
-plotJobsGantt(gatkReportData, F)
+plotJobsGantt(gatkReportData, T, F)
+plotJobsGantt(gatkReportData, F, F)
 plotProgressByTime(gatkReportData)
 for ( group in gatkReportData ) {
  plotGroup(group)

From 4ad330008ddb29e163089afa2c264f62dccd4c3f Mon Sep 17 00:00:00 2001
From: Mark DePristo 
Date: Mon, 19 Sep 2011 10:19:10 -0400
Subject: [PATCH 092/196] Final intervals cleanup

-- No functional changes (my algorithm wouldn't work)
-- Major structural cleanup (returning more basic data structures that allow us to development new algorithm)
-- Unit tests for the efficiency of interval partitioning
---
 build.xml                                     |  4 +-
 .../sting/utils/interval/IntervalUtils.java   | 70 ++++++++++++++-----
 .../utils/interval/IntervalUtilsUnitTest.java | 63 ++++++++---------
 3 files changed, 82 insertions(+), 55 deletions(-)

diff --git a/build.xml b/build.xml
index 1196f32dc..e5ad9daf0 100644
--- a/build.xml
+++ b/build.xml
@@ -852,8 +852,8 @@
                 
                 
                 
-                
-                
+
+
                 
                     
                     
diff --git a/public/java/src/org/broadinstitute/sting/utils/interval/IntervalUtils.java b/public/java/src/org/broadinstitute/sting/utils/interval/IntervalUtils.java
index 2cfcc19a9..41cbbe59f 100644
--- a/public/java/src/org/broadinstitute/sting/utils/interval/IntervalUtils.java
+++ b/public/java/src/org/broadinstitute/sting/utils/interval/IntervalUtils.java
@@ -333,6 +333,28 @@ public class IntervalUtils {
             throw new UserException.BadArgumentValue("scatterParts", String.format("Only able to write contigs into %d of %d files.", fileIndex + 1, scatterParts.size()));
     }
 
+    /**
+     * Splits an interval list into multiple sublists.
+     * @param locs The genome locs to split.
+     * @param splits The stop points for the genome locs returned by splitFixedIntervals.
+     * @return A list of lists of genome locs, split according to splits
+     */
+    public static List> splitIntervalsToSubLists(List locs, List splits) {
+        int locIndex = 1;
+        int start = 0;
+        List> sublists = new ArrayList>(splits.size());
+        for (Integer stop: splits) {
+            List curList = new ArrayList();
+            for (int i = start; i < stop; i++)
+                curList.add(locs.get(i));
+            start = stop;
+            sublists.add(curList);
+        }
+
+        return sublists;
+    }
+
+
     /**
      * Splits an interval list into multiple files.
      * @param fileHeader The sam file header.
@@ -362,27 +384,39 @@ public class IntervalUtils {
     public static List> splitFixedIntervals(List locs, int numParts) {
         if (locs.size() < numParts)
             throw new UserException.BadArgumentValue("scatterParts", String.format("Cannot scatter %d locs into %d parts.", locs.size(), numParts));
-
         final long locsSize = intervalSize(locs);
-        final double idealSplitSize = locsSize / numParts;
-        final List> splits = new ArrayList>(numParts);
-        final LinkedList remainingLocs = new LinkedList(locs);
+        final List splitPoints = new ArrayList();
+        addFixedSplit(splitPoints, locs, locsSize, 0, locs.size(), numParts);
+        Collections.sort(splitPoints);
+        splitPoints.add(locs.size());
+        return splitIntervalsToSubLists(locs, splitPoints);
+    }
 
-        for ( int i = 0; i < numParts; i++ ) {
-            long splitSize = 0;
-            List split = new ArrayList();
-            while ( ! remainingLocs.isEmpty() ) {
-                final GenomeLoc toAdd = remainingLocs.pop();
-                splitSize += toAdd.size();
-                split.add(toAdd);
-                final long nextEltSize = remainingLocs.isEmpty() ? 0 : remainingLocs.peek().size();
-                if ( splitSize + (i % 2 == 0 ? 0 : nextEltSize) > idealSplitSize )
-                    break;
-            }
-            splits.add(split);
+    private static void addFixedSplit(List splitPoints, List locs, long locsSize, int startIndex, int stopIndex, int numParts) {
+        if (numParts < 2)
+            return;
+        int halfParts = (numParts + 1) / 2;
+        Pair splitPoint = getFixedSplit(locs, locsSize, startIndex, stopIndex, halfParts, numParts - halfParts);
+        int splitIndex = splitPoint.first;
+        long splitSize = splitPoint.second;
+        splitPoints.add(splitIndex);
+        addFixedSplit(splitPoints, locs, splitSize, startIndex, splitIndex, halfParts);
+        addFixedSplit(splitPoints, locs, locsSize - splitSize, splitIndex, stopIndex, numParts - halfParts);
+    }
+
+    private static Pair getFixedSplit(List locs, long locsSize, int startIndex, int stopIndex, int minLocs, int maxLocs) {
+        int splitIndex = startIndex;
+        long splitSize = 0;
+        for (int i = 0; i < minLocs; i++) {
+            splitSize += locs.get(splitIndex).size();
+            splitIndex++;
         }
-
-        return splits;
+        long halfSize = locsSize / 2;
+        while (splitIndex < (stopIndex - maxLocs) && splitSize < halfSize) {
+            splitSize += locs.get(splitIndex).size();
+            splitIndex++;
+        }
+        return new Pair(splitIndex, splitSize);
     }
 
     /**
diff --git a/public/java/test/org/broadinstitute/sting/utils/interval/IntervalUtilsUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/interval/IntervalUtilsUnitTest.java
index 4809f1b5c..98b878d23 100644
--- a/public/java/test/org/broadinstitute/sting/utils/interval/IntervalUtilsUnitTest.java
+++ b/public/java/test/org/broadinstitute/sting/utils/interval/IntervalUtilsUnitTest.java
@@ -1,7 +1,6 @@
 package org.broadinstitute.sting.utils.interval;
 
 import net.sf.picard.reference.ReferenceSequenceFile;
-import net.sf.picard.util.IntervalUtil;
 import net.sf.samtools.SAMFileHeader;
 import org.broadinstitute.sting.BaseTest;
 import org.broadinstitute.sting.gatk.datasources.reference.ReferenceDataSource;
@@ -101,25 +100,18 @@ public class IntervalUtilsUnitTest extends BaseTest {
     @DataProvider(name = "intervalslicingdata")
     public Object[][] createTrees() {
         new IntervalSlicingTest(1, 0);
-        new IntervalSlicingTest(2, 0.1);
-        new IntervalSlicingTest(3, 0.1);
-        new IntervalSlicingTest(7, 0.1);
-        new IntervalSlicingTest(10, 0.1);
-        new IntervalSlicingTest(31, 0.1);
-        new IntervalSlicingTest(67, 0.1);
-        new IntervalSlicingTest(100, 0.1);
-        new IntervalSlicingTest(127, 0.1);
-        // starts to become a bit less efficiency with larger cuts
-        new IntervalSlicingTest(500, 0.5);
+        new IntervalSlicingTest(2, 1);
+        new IntervalSlicingTest(5, 1);
+        new IntervalSlicingTest(10, 1);
+        new IntervalSlicingTest(67, 1);
+        new IntervalSlicingTest(100, 1);
+        new IntervalSlicingTest(500, 1);
         new IntervalSlicingTest(1000, 1);
-        new IntervalSlicingTest(10000, 10);
         return IntervalSlicingTest.getTests(IntervalSlicingTest.class);
     }
 
-    @Test(dataProvider = "intervalslicingdata")
+    @Test(enabled = true, dataProvider = "intervalslicingdata")
     public void testFixedScatterIntervalsAlgorithm(IntervalSlicingTest test) {
-        Set locsSet = new HashSet(hg19exomeIntervals);
-        Set notFoundSet = new HashSet(hg19exomeIntervals);
         List> splits = IntervalUtils.splitFixedIntervals(hg19exomeIntervals, test.parts);
 
         long totalSize = IntervalUtils.intervalSize(hg19exomeIntervals);
@@ -134,15 +126,9 @@ public class IntervalUtilsUnitTest extends BaseTest {
             counter++;
             sumOfSplitSizes += splitSize;
             Assert.assertTrue(Math.abs(sigma) <= test.maxAllowableVariance, String.format("Interval %d (size %d ideal %d) has a variance %.2f outside of the tolerated range %.2f", counter, splitSize, idealSplitSize, sigma, test.maxAllowableVariance));
-
-            for ( final GenomeLoc loc : split ) {
-                Assert.assertTrue(locsSet.contains(loc), "Split location " + loc + " not found in set of input locs");
-                notFoundSet.remove(loc);
-            }
         }
 
-        Assert.assertEquals(sumOfSplitSizes, totalSize, "Split intervals don't contain the exact number of bases in the original intervals");
-        Assert.assertTrue(notFoundSet.isEmpty(), "Not all intervals were present in the split set");
+        Assert.assertEquals(totalSize, sumOfSplitSizes, "Split intervals don't contain the exact number of bases in the origianl intervals");
     }
 
     @Test(expectedExceptions=UserException.class)
@@ -246,7 +232,8 @@ public class IntervalUtilsUnitTest extends BaseTest {
         List files = testFiles("basic.", 3, ".intervals");
 
         List locs = getLocs("chr1", "chr2", "chr3");
-        IntervalUtils.scatterFixedIntervals(hg18Header, IntervalUtils.splitFixedIntervals(locs, files.size()), files);
+        List> splits = IntervalUtils.splitFixedIntervals(locs, files.size());
+        IntervalUtils.scatterFixedIntervals(hg18Header, splits, files);
 
         List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
         List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
@@ -271,20 +258,21 @@ public class IntervalUtilsUnitTest extends BaseTest {
         List files = testFiles("less.", 3, ".intervals");
 
         List locs = getLocs("chr1", "chr2", "chr3", "chr4");
-        IntervalUtils.scatterFixedIntervals(hg18Header, IntervalUtils.splitFixedIntervals(locs, files.size()), files);
+        List> splits = IntervalUtils.splitFixedIntervals(locs, files.size());
+        IntervalUtils.scatterFixedIntervals(hg18Header, splits, files);
 
         List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
         List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
         List locs3 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(2).toString()), false);
 
-        Assert.assertEquals(locs1.size(), 2);
+        Assert.assertEquals(locs1.size(), 1);
         Assert.assertEquals(locs2.size(), 1);
-        Assert.assertEquals(locs3.size(), 1);
+        Assert.assertEquals(locs3.size(), 2);
 
         Assert.assertEquals(locs1.get(0), chr1);
-        Assert.assertEquals(locs1.get(1), chr2);
-        Assert.assertEquals(locs2.get(0), chr3);
-        Assert.assertEquals(locs3.get(0), chr4);
+        Assert.assertEquals(locs2.get(0), chr2);
+        Assert.assertEquals(locs3.get(0), chr3);
+        Assert.assertEquals(locs3.get(1), chr4);
     }
 
     @Test(expectedExceptions=UserException.BadArgumentValue.class)
@@ -298,7 +286,8 @@ public class IntervalUtilsUnitTest extends BaseTest {
     public void testScatterFixedIntervalsMoreFiles() {
         List files = testFiles("more.", 3, ".intervals");
         List locs = getLocs("chr1", "chr2");
-        IntervalUtils.scatterFixedIntervals(hg18Header, IntervalUtils.splitFixedIntervals(locs, locs.size()), files);
+        List> splits = IntervalUtils.splitFixedIntervals(locs, locs.size()); // locs.size() instead of files.size()
+        IntervalUtils.scatterFixedIntervals(hg18Header, splits, files);
     }
     @Test
     public void testScatterFixedIntervalsStart() {
@@ -311,7 +300,8 @@ public class IntervalUtilsUnitTest extends BaseTest {
         List files = testFiles("split.", 3, ".intervals");
 
         List locs = getLocs(intervals);
-        IntervalUtils.scatterFixedIntervals(hg18Header, IntervalUtils.splitFixedIntervals(locs, files.size()), files);
+        List> splits = IntervalUtils.splitFixedIntervals(locs, files.size());
+        IntervalUtils.scatterFixedIntervals(hg18Header, splits, files);
 
         List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
         List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
@@ -338,7 +328,8 @@ public class IntervalUtilsUnitTest extends BaseTest {
         List files = testFiles("split.", 3, ".intervals");
 
         List locs = getLocs(intervals);
-        IntervalUtils.scatterFixedIntervals(hg18Header, IntervalUtils.splitFixedIntervals(locs, files.size()), files);
+        List> splits = IntervalUtils.splitFixedIntervals(locs, files.size());
+        IntervalUtils.scatterFixedIntervals(hg18Header, splits, files);
 
         List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
         List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
@@ -365,7 +356,8 @@ public class IntervalUtilsUnitTest extends BaseTest {
         List files = testFiles("split.", 3, ".intervals");
 
         List locs = getLocs(intervals);
-        IntervalUtils.scatterFixedIntervals(hg18Header, IntervalUtils.splitFixedIntervals(locs, files.size()), files);
+        List> splits = IntervalUtils.splitFixedIntervals(locs, files.size());
+        IntervalUtils.scatterFixedIntervals(hg18Header, splits, files);
 
         List locs1 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(0).toString()), false);
         List locs2 = IntervalUtils.parseIntervalArguments(hg18GenomeLocParser, Arrays.asList(files.get(1).toString()), false);
@@ -399,7 +391,7 @@ public class IntervalUtilsUnitTest extends BaseTest {
 
         //String splitCounts = "";
         for (int i = 0; i < splits.size(); i++) {
-            long splitCount = splits.get(i).size();
+            int splitCount = splits.get(i).size();
             Assert.assertEquals(splitCount, counts[i], "Num intervals in split " + i);
         }
         //System.out.println(splitCounts.substring(2));
@@ -420,7 +412,8 @@ public class IntervalUtilsUnitTest extends BaseTest {
     @Test
     public void testScatterFixedIntervalsMax() {
         List files = testFiles("sg.", 85, ".intervals");
-        IntervalUtils.scatterFixedIntervals(hg19Header, IntervalUtils.splitFixedIntervals(hg19ReferenceLocs, files.size()), files);
+        List> splits = IntervalUtils.splitFixedIntervals(hg19ReferenceLocs, files.size());
+        IntervalUtils.scatterFixedIntervals(hg19Header, splits, files);
 
         for (int i = 0; i < files.size(); i++) {
             String file = files.get(i).toString();

From ca1b30e4a4822672803421278eb8301b14cff417 Mon Sep 17 00:00:00 2001
From: Christopher Hartl 
Date: Mon, 19 Sep 2011 10:29:06 -0400
Subject: [PATCH 093/196] Fix the -T argument in the DepthOfCoverage docs Add
 documentation for the RefSeqCodec, pointing users to the wiki page describing
 how to create the file

---
 .../coverage/DepthOfCoverageWalker.java       |  9 ++++---
 .../utils/codecs/refseq/RefSeqCodec.java      | 24 +++++++++++++++----
 2 files changed, 26 insertions(+), 7 deletions(-)

diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/DepthOfCoverageWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/DepthOfCoverageWalker.java
index 86f97a36c..664c319ab 100644
--- a/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/DepthOfCoverageWalker.java
+++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/DepthOfCoverageWalker.java
@@ -63,9 +63,12 @@ import java.util.*;
  * 

Input

*

* One or more bam files (with proper headers) to be analyzed for coverage statistics - * (Optional) A REFSEQ Rod to aggregate coverage to the gene level *

- * + *

+ *(Optional) A REFSEQ Rod to aggregate coverage to the gene level + *

+ * (for information about creating the REFSEQ Rod, please consult the RefSeqCodec documentation) + *

*

Output

*

* Tables pertaining to different coverage summaries. Suffix on the table files declares the contents: @@ -93,7 +96,7 @@ import java.util.*; *

  * java -Xmx2g -jar GenomeAnalysisTK.jar \
  *   -R ref.fasta \
- *   -T VariantEval \
+ *   -T DepthOfCoverage \
  *   -o file_name_base \
  *   -I input_bams.list
  *   [-geneList refSeq.sorted.txt] \
diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/refseq/RefSeqCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/refseq/RefSeqCodec.java
index d94d9ff84..f142fa5aa 100644
--- a/public/java/src/org/broadinstitute/sting/utils/codecs/refseq/RefSeqCodec.java
+++ b/public/java/src/org/broadinstitute/sting/utils/codecs/refseq/RefSeqCodec.java
@@ -12,19 +12,35 @@ import org.broadinstitute.sting.utils.exceptions.UserException;
 import java.util.ArrayList;
 
 /**
- * TODO FOR CHRIS HARTL
+ * Allows for reading in RefSeq information
  *
  * 

- * Codec Description + * Parses a sorted UCSC RefSeq file (see below) into relevant features: the gene name, the unique gene name (if multiple transcrips get separate entries), exons, gene start/stop, coding start/stop, + * strandedness of transcription. *

* *

- * See also: link to file specification + * Instructions for generating a RefSeq file for use with the RefSeq codec can be found on the Wiki here + * http://www.broadinstitute.org/gsa/wiki/index.php/RefSeq *

+ *

Usage

+ * The RefSeq Rod can be bound as any other rod, and is specified by REFSEQ, for example + *
+ * -refSeqBinding:REFSEQ /path/to/refSeq.txt
+ * 
+ * + * You will need to consult individual walkers for the binding name ("refSeqBinding", above) * *

File format example

+ * If you want to define your own file for use, the format is (tab delimited): + * bin, name, chrom, strand, transcription start, transcription end, coding start, coding end, num exons, exon starts, exon ends, id, alt. name, coding start status (complete/incomplete), coding end status (complete,incomplete) + * and exon frames, for example: + *
+ * 76 NM_001011874 1 - 3204562 3661579 3206102 3661429 3 3204562,3411782,3660632, 3207049,3411982,3661579, 0 Xkr4 cmpl cmpl 1,2,0,
+ * 
+ * for more information see here *

- * A BAM file containing exactly one sample. + * *

* * @author Mark DePristo From 3e93f246f7b8849a3126fab5e0757cfdee22e661 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Mon, 19 Sep 2011 11:40:45 -0400 Subject: [PATCH 094/196] Support for sample sets in AssignSomaticStatus -- Also cleaned up SampleUtils.getSamplesFromCommandLine() to return a set, not a list, and trim the sample names. --- .../sting/utils/SampleUtils.java | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/SampleUtils.java b/public/java/src/org/broadinstitute/sting/utils/SampleUtils.java index f9997bfd8..1b4703e4a 100755 --- a/public/java/src/org/broadinstitute/sting/utils/SampleUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/SampleUtils.java @@ -190,11 +190,21 @@ public class SampleUtils { } - public static List getSamplesFromCommandLineInput(Collection sampleArgs) { + /** + * Returns a new set of samples, containing a final list of samples expanded from sampleArgs + * + * Each element E of sampleArgs can either be a literal sample name or a file. For each E, + * we try to read a file named E from disk, and if possible all lines from that file are expanded + * into unique sample names. + * + * @param sampleArgs + * @return + */ + public static Set getSamplesFromCommandLineInput(Collection sampleArgs) { if (sampleArgs != null) { // Let's first go through the list and see if we were given any files. We'll add every entry in the file to our // sample list set, and treat the entries as if they had been specified on the command line. - List samplesFromFiles = new ArrayList(); + Set samplesFromFiles = new HashSet(); for (String SAMPLE_EXPRESSION : sampleArgs) { File sampleFile = new File(SAMPLE_EXPRESSION); @@ -203,7 +213,7 @@ public class SampleUtils { List lines = reader.readLines(); for (String line : lines) { - samplesFromFiles.add(line); + samplesFromFiles.add(line.trim()); } } catch (FileNotFoundException e) { samplesFromFiles.add(SAMPLE_EXPRESSION); // not a file, so must be a sample @@ -212,7 +222,8 @@ public class SampleUtils { return samplesFromFiles; } - return new ArrayList(); + + return new HashSet(); } public static Set getSamplesFromCommandLineInput(Collection vcfSamples, Collection sampleExpressions) { From 034b8685889a879eda0e7c1a001358a26845755a Mon Sep 17 00:00:00 2001 From: Christopher Hartl Date: Mon, 19 Sep 2011 12:16:07 -0400 Subject: [PATCH 095/196] Revert "Fix the -T argument in the DepthOfCoverage docs" This reverts commit 0994efda998cf3a41b1a43696dbc852a441d5316. --- .../coverage/DepthOfCoverageWalker.java | 9 +++---- .../utils/codecs/refseq/RefSeqCodec.java | 24 ++++--------------- 2 files changed, 7 insertions(+), 26 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/DepthOfCoverageWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/DepthOfCoverageWalker.java index 664c319ab..86f97a36c 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/DepthOfCoverageWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/DepthOfCoverageWalker.java @@ -63,12 +63,9 @@ import java.util.*; *

Input

*

* One or more bam files (with proper headers) to be analyzed for coverage statistics + * (Optional) A REFSEQ Rod to aggregate coverage to the gene level *

- *

- *(Optional) A REFSEQ Rod to aggregate coverage to the gene level - *

- * (for information about creating the REFSEQ Rod, please consult the RefSeqCodec documentation) - *

+ * *

Output

*

* Tables pertaining to different coverage summaries. Suffix on the table files declares the contents: @@ -96,7 +93,7 @@ import java.util.*; *

  * java -Xmx2g -jar GenomeAnalysisTK.jar \
  *   -R ref.fasta \
- *   -T DepthOfCoverage \
+ *   -T VariantEval \
  *   -o file_name_base \
  *   -I input_bams.list
  *   [-geneList refSeq.sorted.txt] \
diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/refseq/RefSeqCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/refseq/RefSeqCodec.java
index f142fa5aa..d94d9ff84 100644
--- a/public/java/src/org/broadinstitute/sting/utils/codecs/refseq/RefSeqCodec.java
+++ b/public/java/src/org/broadinstitute/sting/utils/codecs/refseq/RefSeqCodec.java
@@ -12,35 +12,19 @@ import org.broadinstitute.sting.utils.exceptions.UserException;
 import java.util.ArrayList;
 
 /**
- * Allows for reading in RefSeq information
+ * TODO FOR CHRIS HARTL
  *
  * 

- * Parses a sorted UCSC RefSeq file (see below) into relevant features: the gene name, the unique gene name (if multiple transcrips get separate entries), exons, gene start/stop, coding start/stop, - * strandedness of transcription. + * Codec Description *

* *

- * Instructions for generating a RefSeq file for use with the RefSeq codec can be found on the Wiki here - * http://www.broadinstitute.org/gsa/wiki/index.php/RefSeq + * See also: link to file specification *

- *

Usage

- * The RefSeq Rod can be bound as any other rod, and is specified by REFSEQ, for example - *
- * -refSeqBinding:REFSEQ /path/to/refSeq.txt
- * 
- * - * You will need to consult individual walkers for the binding name ("refSeqBinding", above) * *

File format example

- * If you want to define your own file for use, the format is (tab delimited): - * bin, name, chrom, strand, transcription start, transcription end, coding start, coding end, num exons, exon starts, exon ends, id, alt. name, coding start status (complete/incomplete), coding end status (complete,incomplete) - * and exon frames, for example: - *
- * 76 NM_001011874 1 - 3204562 3661579 3206102 3661429 3 3204562,3411782,3660632, 3207049,3411982,3661579, 0 Xkr4 cmpl cmpl 1,2,0,
- * 
- * for more information see here *

- * + * A BAM file containing exactly one sample. *

* * @author Mark DePristo From 85626e7a5dbae8a263b8e2ff2e64bd25656d6e9c Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Mon, 19 Sep 2011 12:24:05 -0400 Subject: [PATCH 096/196] We no longer want people to use the August 2010 Dindel calls for indel realignment but instead Guillermo's new whole genome bi-allelic indel calls; updating the bundle accordingly. Also, there was some confusion by the 1000G data processing folks as to exactly what these indel files are, so I've renamed them so that it's clear. Wiki updated too. --- .../sting/queue/qscripts/GATKResourcesBundle.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/GATKResourcesBundle.scala b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/GATKResourcesBundle.scala index 59c00b8cd..e8b8258c1 100755 --- a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/GATKResourcesBundle.scala +++ b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/GATKResourcesBundle.scala @@ -131,11 +131,11 @@ class GATKResourcesBundle extends QScript { addResource(new Resource("/humgen/gsa-hpprojects/GATK/data/Comparisons/Validated/HapMap/3.3/genotypes_r27_nr.b37_fwd.vcf", "hapmap_3.3", b37, true, true)) - addResource(new Resource("/humgen/gsa-hpprojects/GATK/data/Comparisons/Unvalidated/AFR+EUR+ASN+1KG.dindel_august_release_merged_pilot1.20110126.sites.vcf", - "1000G_indels_for_realignment", b37, true, false)) + addResource(new Resource("/humgen/1kg/processing/official_release/phase1/ALL.wgs.VQSR_consensus_biallelic.20101123.indels.sites.vcf", + "1000G_biallelic.indels", b37, true, false)) addResource(new Resource("/humgen/gsa-hpprojects/GATK/data/Comparisons/Validated/Mills_Devine_Indels_2011/ALL.wgs.indels_mills_devine_hg19_leftAligned_collapsed_double_hit.sites.vcf", - "indels_mills_devine", b37, true, true)) + "Mills_Devine_2hit.indels", b37, true, true)) // // example call set for wiki tutorial From 8143def292a49844ab3ff302fbb00f5c866299f7 Mon Sep 17 00:00:00 2001 From: Christopher Hartl Date: Mon, 19 Sep 2011 10:29:06 -0400 Subject: [PATCH 097/196] Fix the -T argument in the DepthOfCoverage docs Add documentation for the RefSeqCodec, pointing users to the wiki page describing how to create the file --- .../coverage/DepthOfCoverageWalker.java | 9 ++++--- .../utils/codecs/refseq/RefSeqCodec.java | 24 +++++++++++++++---- 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/DepthOfCoverageWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/DepthOfCoverageWalker.java index 86f97a36c..664c319ab 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/DepthOfCoverageWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/DepthOfCoverageWalker.java @@ -63,9 +63,12 @@ import java.util.*; *

Input

*

* One or more bam files (with proper headers) to be analyzed for coverage statistics - * (Optional) A REFSEQ Rod to aggregate coverage to the gene level *

- * + *

+ *(Optional) A REFSEQ Rod to aggregate coverage to the gene level + *

+ * (for information about creating the REFSEQ Rod, please consult the RefSeqCodec documentation) + *

*

Output

*

* Tables pertaining to different coverage summaries. Suffix on the table files declares the contents: @@ -93,7 +96,7 @@ import java.util.*; *

  * java -Xmx2g -jar GenomeAnalysisTK.jar \
  *   -R ref.fasta \
- *   -T VariantEval \
+ *   -T DepthOfCoverage \
  *   -o file_name_base \
  *   -I input_bams.list
  *   [-geneList refSeq.sorted.txt] \
diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/refseq/RefSeqCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/refseq/RefSeqCodec.java
index d94d9ff84..f142fa5aa 100644
--- a/public/java/src/org/broadinstitute/sting/utils/codecs/refseq/RefSeqCodec.java
+++ b/public/java/src/org/broadinstitute/sting/utils/codecs/refseq/RefSeqCodec.java
@@ -12,19 +12,35 @@ import org.broadinstitute.sting.utils.exceptions.UserException;
 import java.util.ArrayList;
 
 /**
- * TODO FOR CHRIS HARTL
+ * Allows for reading in RefSeq information
  *
  * 

- * Codec Description + * Parses a sorted UCSC RefSeq file (see below) into relevant features: the gene name, the unique gene name (if multiple transcrips get separate entries), exons, gene start/stop, coding start/stop, + * strandedness of transcription. *

* *

- * See also: link to file specification + * Instructions for generating a RefSeq file for use with the RefSeq codec can be found on the Wiki here + * http://www.broadinstitute.org/gsa/wiki/index.php/RefSeq *

+ *

Usage

+ * The RefSeq Rod can be bound as any other rod, and is specified by REFSEQ, for example + *
+ * -refSeqBinding:REFSEQ /path/to/refSeq.txt
+ * 
+ * + * You will need to consult individual walkers for the binding name ("refSeqBinding", above) * *

File format example

+ * If you want to define your own file for use, the format is (tab delimited): + * bin, name, chrom, strand, transcription start, transcription end, coding start, coding end, num exons, exon starts, exon ends, id, alt. name, coding start status (complete/incomplete), coding end status (complete,incomplete) + * and exon frames, for example: + *
+ * 76 NM_001011874 1 - 3204562 3661579 3206102 3661429 3 3204562,3411782,3660632, 3207049,3411982,3661579, 0 Xkr4 cmpl cmpl 1,2,0,
+ * 
+ * for more information see here *

- * A BAM file containing exactly one sample. + * *

* * @author Mark DePristo From 5e832254a4e024378f7fdee252abf7df9e289c6a Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Mon, 19 Sep 2011 13:28:41 -0400 Subject: [PATCH 098/196] Fixing ReadAndInterval overlap comments. --- .../sting/utils/sam/ReadUtils.java | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java b/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java index 62bbb0307..18fcdabf2 100644 --- a/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java @@ -118,31 +118,40 @@ public class ReadUtils { /** * This enum represents all the different ways in which a read can overlap an interval. * - * NO_OVERLAP: + * NO_OVERLAP_CONTIG: + * read and interval are in different contigs. + * + * NO_OVERLAP_LEFT: + * the read does not overlap the interval. + * + * |----------------| (interval) + * <----------------> (read) + * + * NO_OVERLAP_RIGHT: * the read does not overlap the interval. * * |----------------| (interval) * <----------------> (read) * - * LEFT_OVERLAP: + * OVERLAP_LEFT: * the read starts before the beginning of the interval but ends inside of it * * |----------------| (interval) * <----------------> (read) * - * RIGHT_OVERLAP: + * OVERLAP_RIGHT: * the read starts inside the interval but ends outside of it * * |----------------| (interval) * <----------------> (read) * - * FULL_OVERLAP: + * OVERLAP_LEFT_AND_RIGHT: * the read starts before the interval and ends after the interval * * |-----------| (interval) * <-------------------> (read) * - * CONTAINED: + * OVERLAP_CONTAINED: * the read starts and ends inside the interval * * |----------------| (interval) From ba150570f3d7747256f634a2828ab673a98953f7 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Mon, 19 Sep 2011 13:30:32 -0400 Subject: [PATCH 099/196] Updating to use new rod system syntax plus name change for CountRODs --- .../sting/queue/qscripts/GATKResourcesBundle.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/GATKResourcesBundle.scala b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/GATKResourcesBundle.scala index e8b8258c1..036a77b58 100755 --- a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/GATKResourcesBundle.scala +++ b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/GATKResourcesBundle.scala @@ -300,9 +300,9 @@ class GATKResourcesBundle extends QScript { bamFile = bamIn } - class IndexVCF(@Input vcf: File, @Input ref: File) extends CountRod with UNIVERSAL_GATK_ARGS { + class IndexVCF(@Input vcf: File, @Input ref: File) extends CountRODs with UNIVERSAL_GATK_ARGS { //@Output val vcfIndex: File = swapExt(vcf.getParent, vcf, ".vcf", ".vcf.idx") - this.rodBind :+= RodBind(vcf.getName, "VCF", vcf) + this.rod :+= vcf this.reference_sequence = ref } @@ -313,7 +313,7 @@ class GATKResourcesBundle extends QScript { } class MakeDBSNP129(@Input dbsnp: File, @Input ref: File, @Output dbsnp129: File) extends SelectVariants with UNIVERSAL_GATK_ARGS { - this.rodBind :+= RodBind("variant", "VCF", dbsnp) + this.variant = dbsnp this.select ++= List("\"dbSNPBuildID <= 129\"") this.reference_sequence = ref this.out = dbsnp129 From 080c9575470c505e10f7b09d59fa22fcb668867d Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Mon, 19 Sep 2011 13:53:08 -0400 Subject: [PATCH 100/196] Fixing contracts for SoftUnclippedEnd utils Now accepts reads that are entirely contained inside an insertion. --- .../broadinstitute/sting/utils/sam/ReadUtils.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java b/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java index 18fcdabf2..2de17db14 100644 --- a/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java @@ -667,7 +667,7 @@ public class ReadUtils { return ReadAndIntervalOverlap.OVERLAP_RIGHT; } - @Ensures({"result >= read.getUnclippedStart()", "result <= read.getUnclippedEnd()"}) + @Ensures({"(result >= read.getUnclippedStart() && result <= read.getUnclippedEnd()) || readIsEntirelyInsertion(read)"}) public static int getRefCoordSoftUnclippedStart(SAMRecord read) { int start = read.getUnclippedStart(); for (CigarElement cigarElement : read.getCigar().getCigarElements()) { @@ -679,7 +679,7 @@ public class ReadUtils { return start; } - @Ensures({"result >= read.getUnclippedStart()", "result <= read.getUnclippedEnd()"}) + @Ensures({"(result >= read.getUnclippedStart() && result <= read.getUnclippedEnd()) || readIsEntirelyInsertion(read)"}) public static int getRefCoordSoftUnclippedEnd(SAMRecord read) { int stop = read.getUnclippedStart(); int shift = 0; @@ -695,6 +695,14 @@ public class ReadUtils { return (lastOperator == CigarOperator.HARD_CLIP) ? stop-1 : stop+shift-1 ; } + private static boolean readIsEntirelyInsertion(SAMRecord read) { + for (CigarElement cigarElement : read.getCigar().getCigarElements()) { + if (cigarElement.getOperator() != CigarOperator.INSERTION) + return false; + } + return true; + } + /** * Looks for a read coordinate that corresponds to the reference coordinate in the soft clipped region before * the alignment start of the read. From 56106d54ed620965aea0b39052de43c81671c817 Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Mon, 19 Sep 2011 14:00:00 -0400 Subject: [PATCH 101/196] Changing ReadUtils behavior to comply with GenomeLocParser Now the functions getRefCoordSoftUnclippedStart and getRefCoordSoftUnclippedEnd will return getUnclippedStart if the read is all contained within an insertion. Updated the contracts accordingly. This should give the same behavior as the GenomeLocParser now. --- .../src/org/broadinstitute/sting/utils/sam/ReadUtils.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java b/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java index 2de17db14..5d3ef3086 100644 --- a/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java @@ -667,7 +667,7 @@ public class ReadUtils { return ReadAndIntervalOverlap.OVERLAP_RIGHT; } - @Ensures({"(result >= read.getUnclippedStart() && result <= read.getUnclippedEnd()) || readIsEntirelyInsertion(read)"}) + @Ensures({"result >= read.getUnclippedStart()", "result <= read.getUnclippedEnd() || readIsEntirelyInsertion(read)"}) public static int getRefCoordSoftUnclippedStart(SAMRecord read) { int start = read.getUnclippedStart(); for (CigarElement cigarElement : read.getCigar().getCigarElements()) { @@ -679,9 +679,13 @@ public class ReadUtils { return start; } - @Ensures({"(result >= read.getUnclippedStart() && result <= read.getUnclippedEnd()) || readIsEntirelyInsertion(read)"}) + @Ensures({"result >= read.getUnclippedStart()", "result <= read.getUnclippedEnd() || readIsEntirelyInsertion(read)"}) public static int getRefCoordSoftUnclippedEnd(SAMRecord read) { int stop = read.getUnclippedStart(); + + if (readIsEntirelyInsertion(read)) + return stop; + int shift = 0; CigarOperator lastOperator = null; for (CigarElement cigarElement : read.getCigar().getCigarElements()) { From 61b89e236ab13b073a3572e983b6c730efd5331e Mon Sep 17 00:00:00 2001 From: Khalid Shakir Date: Tue, 20 Sep 2011 00:14:35 -0400 Subject: [PATCH 102/196] To work around potential problem with invalid javax.mail 1.4.1 in ivy cache, added explicit javax.mail 1.4.4 along with build.xml code to remove 1.4.1. --- build.xml | 8 ++++++++ ivy.xml | 6 ++---- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/build.xml b/build.xml index e5ad9daf0..1f26e7b7a 100644 --- a/build.xml +++ b/build.xml @@ -163,6 +163,14 @@ + + + + diff --git a/ivy.xml b/ivy.xml index 115f4062a..f90b9a010 100644 --- a/ivy.xml +++ b/ivy.xml @@ -15,10 +15,8 @@ - - - - + + From 5d0705acd654cfed6e9bf2ba690a0c75ee5a50d8 Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Tue, 20 Sep 2011 09:07:28 -0400 Subject: [PATCH 103/196] Adding quality scores to the VCF records created by the Haplotype Caller --- .../sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java | 1 - 1 file changed, 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java index a5cb00a5d..87dd37bf6 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java @@ -40,7 +40,6 @@ import org.broadinstitute.sting.utils.codecs.vcf.VCFConstants; import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; import org.broadinstitute.sting.utils.pileup.PileupElement; import org.broadinstitute.sting.utils.pileup.ReadBackedExtendedEventPileup; -import org.broadinstitute.sting.utils.pileup.ReadBackedExtendedEventPileupImpl; import org.broadinstitute.sting.utils.pileup.ReadBackedPileup; import org.broadinstitute.sting.utils.variantcontext.*; From b7511c5ff3b36e16037bfbbbd17b9fd4c9ea47af Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Tue, 20 Sep 2011 10:53:18 -0400 Subject: [PATCH 104/196] Fixed long-standing bug in tribble index creation -- Previously, on the fly indices didn't have dictionary set on the fly, so the GATK would read, add dictionary, and rewrite the index. This is now fixed, so that the on the fly index contains the reference dictionary when first written, avoiding the unnecessary read and write -- Added a GenomeAnalysisEngine and Walker function called getMasterSequenceDictionary() that fetches the reference sequence dictionary. This can be used conveniently everywhere, and is what's written into the Tribble index -- Refactored tribble index utilities from RMDTrackBuilder into IndexDictionaryUtils -- VCFWriter now requires the master sequence dictionary -- Updated walkers that create VCFWriters to provide the master sequence dictionary --- .../sting/gatk/GenomeAnalysisEngine.java | 8 ++ .../gatk/io/storage/VCFWriterStorage.java | 4 +- .../sting/gatk/io/stubs/VCFWriterStub.java | 10 ++ .../gatk/refdata/indexer/RMDIndexer.java | 2 +- .../refdata/tracks/IndexDictionaryUtils.java | 106 ++++++++++++++++ .../gatk/refdata/tracks/RMDTrackBuilder.java | 113 ++++-------------- .../sting/gatk/walkers/Walker.java | 10 ++ .../variantutils/LiftoverVariants.java | 2 +- .../variantutils/RandomlySplitVariants.java | 2 +- .../utils/codecs/vcf/IndexingVCFWriter.java | 38 ++++-- .../utils/codecs/vcf/StandardVCFWriter.java | 26 ++-- .../sting/utils/gcf/GCFWriter.java | 5 +- .../org/broadinstitute/sting/WalkerTest.java | 2 +- .../tracks/RMDTrackBuilderUnitTest.java | 4 +- .../codecs/vcf/IndexFactoryUnitTest.java | 22 +++- .../utils/genotype/vcf/VCFWriterUnitTest.java | 5 +- 16 files changed, 228 insertions(+), 131 deletions(-) create mode 100644 public/java/src/org/broadinstitute/sting/gatk/refdata/tracks/IndexDictionaryUtils.java diff --git a/public/java/src/org/broadinstitute/sting/gatk/GenomeAnalysisEngine.java b/public/java/src/org/broadinstitute/sting/gatk/GenomeAnalysisEngine.java index 5b9ebd99b..972943e26 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/GenomeAnalysisEngine.java +++ b/public/java/src/org/broadinstitute/sting/gatk/GenomeAnalysisEngine.java @@ -929,6 +929,14 @@ public class GenomeAnalysisEngine { return readsDataSource.getHeader(reader); } + /** + * Gets the master sequence dictionary for this GATK engine instance + * @return a never-null dictionary listing all of the contigs known to this engine instance + */ + public SAMSequenceDictionary getMasterSequenceDictionary() { + return getReferenceDataSource().getReference().getSequenceDictionary(); + } + /** * Returns data source object encapsulating all essential info and handlers used to traverse * reads; header merger, individual file readers etc can be accessed through the returned data source object. diff --git a/public/java/src/org/broadinstitute/sting/gatk/io/storage/VCFWriterStorage.java b/public/java/src/org/broadinstitute/sting/gatk/io/storage/VCFWriterStorage.java index ebb4cbe66..4ca7b935f 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/io/storage/VCFWriterStorage.java +++ b/public/java/src/org/broadinstitute/sting/gatk/io/storage/VCFWriterStorage.java @@ -46,7 +46,7 @@ public class VCFWriterStorage implements Storage, VCFWriter { else if ( stub.getOutputStream() != null ) { this.file = null; this.stream = stub.getOutputStream(); - writer = new StandardVCFWriter(stream, stub.doNotWriteGenotypes()); + writer = new StandardVCFWriter(stream, stub.getMasterSequenceDictionary(), stub.doNotWriteGenotypes()); } else throw new ReviewedStingException("Unable to create target to which to write; storage was provided with neither a file nor a stream."); @@ -71,7 +71,7 @@ public class VCFWriterStorage implements Storage, VCFWriter { } // The GATK/Tribble can't currently index block-compressed files on the fly. Disable OTF indexing even if the user explicitly asked for it. - return new StandardVCFWriter(file, this.stream, indexOnTheFly && !stub.isCompressed(), stub.doNotWriteGenotypes()); + return new StandardVCFWriter(file, this.stream, stub.getMasterSequenceDictionary(), indexOnTheFly && !stub.isCompressed(), stub.doNotWriteGenotypes()); } diff --git a/public/java/src/org/broadinstitute/sting/gatk/io/stubs/VCFWriterStub.java b/public/java/src/org/broadinstitute/sting/gatk/io/stubs/VCFWriterStub.java index 936243f9d..82cb43634 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/io/stubs/VCFWriterStub.java +++ b/public/java/src/org/broadinstitute/sting/gatk/io/stubs/VCFWriterStub.java @@ -25,6 +25,7 @@ package org.broadinstitute.sting.gatk.io.stubs; +import net.sf.samtools.SAMSequenceDictionary; import net.sf.samtools.SAMSequenceRecord; import org.broadinstitute.sting.gatk.CommandLineExecutable; import org.broadinstitute.sting.gatk.GenomeAnalysisEngine; @@ -150,6 +151,15 @@ public class VCFWriterStub implements Stub, VCFWriter { return isCompressed; } + /** + * Gets the master sequence dictionary from the engine associated with this stub + * @link GenomeAnalysisEngine.getMasterSequenceDictionary + * @return + */ + public SAMSequenceDictionary getMasterSequenceDictionary() { + return engine.getMasterSequenceDictionary(); + } + /** * Should we tell the VCF writer not to write genotypes? * @return true if the writer should not write genotypes. diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/indexer/RMDIndexer.java b/public/java/src/org/broadinstitute/sting/gatk/refdata/indexer/RMDIndexer.java index 029800aea..9e5a95d10 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/indexer/RMDIndexer.java +++ b/public/java/src/org/broadinstitute/sting/gatk/refdata/indexer/RMDIndexer.java @@ -101,7 +101,7 @@ public class RMDIndexer extends CommandLineProgram { Index index = IndexFactory.createIndex(inputFileSource, codec, approach); // add writing of the sequence dictionary, if supplied - builder.setIndexSequenceDictionary(inputFileSource, index, ref.getSequenceDictionary(), indexFile, false); + builder.validateAndUpdateIndexSequenceDictionary(inputFileSource, index, ref.getSequenceDictionary()); // create the output stream, and write the index LittleEndianOutputStream stream = new LittleEndianOutputStream(new FileOutputStream(indexFile)); diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/tracks/IndexDictionaryUtils.java b/public/java/src/org/broadinstitute/sting/gatk/refdata/tracks/IndexDictionaryUtils.java new file mode 100644 index 000000000..d133439dc --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/gatk/refdata/tracks/IndexDictionaryUtils.java @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2011, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.gatk.refdata.tracks; + +import net.sf.samtools.SAMSequenceDictionary; +import net.sf.samtools.SAMSequenceRecord; +import org.apache.log4j.Logger; +import org.broad.tribble.index.Index; +import org.broadinstitute.sting.gatk.arguments.ValidationExclusion; +import org.broadinstitute.sting.utils.SequenceDictionaryUtils; + +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; +import java.util.TreeSet; + +/** + * Utilities for working with Sequence Dictionaries embedded in tribble indices + * + * @author Your Name + * @since Date created + */ +public class IndexDictionaryUtils { + private final static Logger logger = Logger.getLogger(IndexDictionaryUtils.class); + + // a constant we use for marking sequence dictionary entries in the Tribble index property list + public static final String SequenceDictionaryPropertyPredicate = "DICT:"; + + /** + * get the sequence dictionary from the track, if available. If not, make it from the contig list that is always in the index + * @param index the index file to use + * @return a SAMSequenceDictionary if available, null if unavailable + */ + public static SAMSequenceDictionary getSequenceDictionaryFromProperties(Index index) { + SAMSequenceDictionary dict = new SAMSequenceDictionary(); + for (Map.Entry entry : index.getProperties().entrySet()) { + if (entry.getKey().startsWith(SequenceDictionaryPropertyPredicate)) + dict.addSequence(new SAMSequenceRecord(entry.getKey().substring(SequenceDictionaryPropertyPredicate.length() , entry.getKey().length()), + Integer.valueOf(entry.getValue()))); + } + return dict; + } + + /** + * create the sequence dictionary with the contig list; a backup approach + * @param index the index file to use + * @param dict the sequence dictionary to add contigs to + * @return the filled-in sequence dictionary + */ + static SAMSequenceDictionary createSequenceDictionaryFromContigList(Index index, SAMSequenceDictionary dict) { + LinkedHashSet seqNames = index.getSequenceNames(); + if (seqNames == null) { + return dict; + } + for (String name : seqNames) { + SAMSequenceRecord seq = new SAMSequenceRecord(name, 0); + dict.addSequence(seq); + } + return dict; + } + + public static void setIndexSequenceDictionary(Index index, SAMSequenceDictionary dict) { + for ( SAMSequenceRecord seq : dict.getSequences() ) { + final String contig = IndexDictionaryUtils.SequenceDictionaryPropertyPredicate + seq.getSequenceName(); + final String length = String.valueOf(seq.getSequenceLength()); + index.addProperty(contig,length); + } + } + + public static void validateTrackSequenceDictionary(final String trackName, + final SAMSequenceDictionary trackDict, + final SAMSequenceDictionary referenceDict, + final ValidationExclusion.TYPE validationExclusionType ) { + // if the sequence dictionary is empty (as well as null which means it doesn't have a dictionary), skip validation + if (trackDict == null || trackDict.size() == 0) + logger.info("Track " + trackName + " doesn't have a sequence dictionary built in, skipping dictionary validation"); + else { + Set trackSequences = new TreeSet(); + for (SAMSequenceRecord dictionaryEntry : trackDict.getSequences()) + trackSequences.add(dictionaryEntry.getSequenceName()); + SequenceDictionaryUtils.validateDictionaries(logger, validationExclusionType, trackName, trackDict, "reference", referenceDict); + } + } +} diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/tracks/RMDTrackBuilder.java b/public/java/src/org/broadinstitute/sting/gatk/refdata/tracks/RMDTrackBuilder.java index 46eb79aa7..3b4558579 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/tracks/RMDTrackBuilder.java +++ b/public/java/src/org/broadinstitute/sting/gatk/refdata/tracks/RMDTrackBuilder.java @@ -25,7 +25,6 @@ package org.broadinstitute.sting.gatk.refdata.tracks; import net.sf.samtools.SAMSequenceDictionary; -import net.sf.samtools.SAMSequenceRecord; import org.apache.log4j.Logger; import org.broad.tribble.FeatureCodec; import org.broad.tribble.FeatureSource; @@ -41,7 +40,6 @@ import org.broadinstitute.sting.gatk.arguments.ValidationExclusion; import org.broadinstitute.sting.gatk.refdata.utils.RMDTriplet; import org.broadinstitute.sting.gatk.refdata.utils.RMDTriplet.RMDStorageType; import org.broadinstitute.sting.utils.GenomeLocParser; -import org.broadinstitute.sting.utils.SequenceDictionaryUtils; import org.broadinstitute.sting.utils.collections.Pair; import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; import org.broadinstitute.sting.utils.exceptions.UserException; @@ -52,16 +50,11 @@ import org.broadinstitute.sting.utils.instrumentation.Sizeof; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; -import java.util.TreeSet; - /** - * - * @author aaron + * + * @author aaron * ` * Class RMDTrackBuilder * @@ -76,9 +69,6 @@ public class RMDTrackBuilder { // extends PluginManager { private final static Logger logger = Logger.getLogger(RMDTrackBuilder.class); public final static boolean MEASURE_TRIBBLE_QUERY_PERFORMANCE = false; - // a constant we use for marking sequence dictionary entries in the Tribble index property list - public static final String SequenceDictionaryPropertyPredicate = "DICT:"; - // private sequence dictionary we use to set our tracks with private SAMSequenceDictionary dict = null; @@ -210,13 +200,19 @@ public class RMDTrackBuilder { // extends PluginManager { try { logger.info(String.format(" Index for %s has size in bytes %d", inputFile, Sizeof.getObjectGraphSize(index))); } catch (ReviewedStingException e) { } - sequenceDictionary = getSequenceDictionaryFromProperties(index); + sequenceDictionary = IndexDictionaryUtils.getSequenceDictionaryFromProperties(index); // if we don't have a dictionary in the Tribble file, and we've set a dictionary for this builder, set it in the file if they match if (sequenceDictionary.size() == 0 && dict != null) { File indexFile = Tribble.indexFile(inputFile); - setIndexSequenceDictionary(inputFile,index,dict,indexFile,true); - sequenceDictionary = getSequenceDictionaryFromProperties(index); + validateAndUpdateIndexSequenceDictionary(inputFile, index, dict); + try { // re-write the index + writeIndexToDisk(index,indexFile,new FSLockWithShared(indexFile)); + } catch (IOException e) { + logger.warn("Unable to update index with the sequence dictionary for file " + indexFile + "; this will not effect your run of the GATK"); + } + + sequenceDictionary = IndexDictionaryUtils.getSequenceDictionaryFromProperties(index); } if ( MEASURE_TRIBBLE_QUERY_PERFORMANCE ) @@ -363,96 +359,31 @@ public class RMDTrackBuilder { // extends PluginManager { // this can take a while, let them know what we're doing logger.info("Creating Tribble index in memory for file " + inputFile); Index idx = IndexFactory.createIndex(inputFile, codec, IndexFactory.IndexBalanceApproach.FOR_SEEK_TIME); - setIndexSequenceDictionary(inputFile, idx, dict, null, false); + validateAndUpdateIndexSequenceDictionary(inputFile, idx, dict); return idx; } - - // --------------------------------------------------------------------------------------------------------- - // static functions to work with the sequence dictionaries of indexes - // --------------------------------------------------------------------------------------------------------- - - /** - * get the sequence dictionary from the track, if available. If not, make it from the contig list that is always in the index - * @param index the index file to use - * @return a SAMSequenceDictionary if available, null if unavailable - */ - public static SAMSequenceDictionary getSequenceDictionaryFromProperties(Index index) { - SAMSequenceDictionary dict = new SAMSequenceDictionary(); - for (Map.Entry entry : index.getProperties().entrySet()) { - if (entry.getKey().startsWith(SequenceDictionaryPropertyPredicate)) - dict.addSequence(new SAMSequenceRecord(entry.getKey().substring(SequenceDictionaryPropertyPredicate.length() , entry.getKey().length()), - Integer.valueOf(entry.getValue()))); - } - return dict; - } - - /** - * create the sequence dictionary with the contig list; a backup approach - * @param index the index file to use - * @param dict the sequence dictionary to add contigs to - * @return the filled-in sequence dictionary - */ - private static SAMSequenceDictionary createSequenceDictionaryFromContigList(Index index, SAMSequenceDictionary dict) { - LinkedHashSet seqNames = index.getSequenceNames(); - if (seqNames == null) { - return dict; - } - for (String name : seqNames) { - SAMSequenceRecord seq = new SAMSequenceRecord(name, 0); - dict.addSequence(seq); - } - return dict; - } - /** * set the sequence dictionary of the track. This function checks that the contig listing of the underlying file is compatible. * (that each contig in the index is in the sequence dictionary). * @param inputFile for proper error message formatting. * @param dict the sequence dictionary * @param index the index file - * @param indexFile the index file - * @param rewriteIndex should we rewrite the index when we're done? - * */ - public void setIndexSequenceDictionary(File inputFile, Index index, SAMSequenceDictionary dict, File indexFile, boolean rewriteIndex) { - if (dict == null) return; - - SAMSequenceDictionary currentDict = createSequenceDictionaryFromContigList(index, new SAMSequenceDictionary()); + public void validateAndUpdateIndexSequenceDictionary(final File inputFile, final Index index, final SAMSequenceDictionary dict) { + if (dict == null) throw new ReviewedStingException("BUG: dict cannot be null"); // check that every contig in the RMD contig list is at least in the sequence dictionary we're being asked to set - validateTrackSequenceDictionary(inputFile.getAbsolutePath(),currentDict,dict); + final SAMSequenceDictionary currentDict = IndexDictionaryUtils.createSequenceDictionaryFromContigList(index, new SAMSequenceDictionary()); + validateTrackSequenceDictionary(inputFile.getAbsolutePath(), currentDict, dict); - for (SAMSequenceRecord seq : currentDict.getSequences()) { - if (dict.getSequence(seq.getSequenceName()) == null) - continue; - index.addProperty(SequenceDictionaryPropertyPredicate + dict.getSequence(seq.getSequenceName()).getSequenceName(), String.valueOf(dict.getSequence(seq.getSequenceName()).getSequenceLength())); - } - // re-write the index - if (rewriteIndex) try { - writeIndexToDisk(index,indexFile,new FSLockWithShared(indexFile)); - } catch (IOException e) { - logger.warn("Unable to update index with the sequence dictionary for file " + indexFile + "; this will not effect your run of the GATK"); - } + // actually update the dictionary in the index + IndexDictionaryUtils.setIndexSequenceDictionary(index, dict); } - public void setIndexSequenceDictionary(Index index, SAMSequenceDictionary dict) { - for ( SAMSequenceRecord seq : dict.getSequences() ) { - final String contig = SequenceDictionaryPropertyPredicate + seq.getSequenceName(); - final String length = String.valueOf(seq.getSequenceLength()); - index.addProperty(contig,length); - } - } - - public void validateTrackSequenceDictionary(String trackName, SAMSequenceDictionary trackDict, SAMSequenceDictionary referenceDict) { - // if the sequence dictionary is empty (as well as null which means it doesn't have a dictionary), skip validation - if (trackDict == null || trackDict.size() == 0) - logger.info("Track " + trackName + " doesn't have a sequence dictionary built in, skipping dictionary validation"); - else { - Set trackSequences = new TreeSet(); - for (SAMSequenceRecord dictionaryEntry : trackDict.getSequences()) - trackSequences.add(dictionaryEntry.getSequenceName()); - SequenceDictionaryUtils.validateDictionaries(logger, validationExclusionType, trackName, trackDict, "reference", referenceDict); - } + public void validateTrackSequenceDictionary(final String trackName, + final SAMSequenceDictionary trackDict, + final SAMSequenceDictionary referenceDict ) { + IndexDictionaryUtils.validateTrackSequenceDictionary(trackName, trackDict, referenceDict, validationExclusionType); } } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/Walker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/Walker.java index c88c7c3c4..10261112c 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/Walker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/Walker.java @@ -25,6 +25,7 @@ package org.broadinstitute.sting.gatk.walkers; +import net.sf.samtools.SAMSequenceDictionary; import org.apache.log4j.Logger; import org.broadinstitute.sting.gatk.CommandLineGATK; import org.broadinstitute.sting.gatk.GenomeAnalysisEngine; @@ -77,6 +78,15 @@ public abstract class Walker { return toolkit; } + /** + * Gets the master sequence dictionary for this walker + * @link GenomeAnalysisEngine.getMasterSequenceDictionary + * @return + */ + protected SAMSequenceDictionary getMasterSequenceDictionary() { + return getToolkit().getMasterSequenceDictionary(); + } + /** * (conceptual static) method that states whether you want to see reads piling up at a locus * that contain a deletion at the locus. diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/LiftoverVariants.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/LiftoverVariants.java index 1c76a21ea..a932d44ed 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/LiftoverVariants.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/LiftoverVariants.java @@ -99,7 +99,7 @@ public class LiftoverVariants extends RodWalker { final VCFHeader vcfHeader = new VCFHeader(metaData, samples); - writer = new StandardVCFWriter(file, false); + writer = new StandardVCFWriter(file, getMasterSequenceDictionary(), false); writer.writeHeader(vcfHeader); } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/RandomlySplitVariants.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/RandomlySplitVariants.java index 1fefd20fc..fa5093839 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/RandomlySplitVariants.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/RandomlySplitVariants.java @@ -75,7 +75,7 @@ public class RandomlySplitVariants extends RodWalker { hInfo.addAll(VCFUtils.getHeaderFields(getToolkit(), inputNames)); vcfWriter1.writeHeader(new VCFHeader(hInfo, samples)); - vcfWriter2 = new StandardVCFWriter(file2, true); + vcfWriter2 = new StandardVCFWriter(file2, getMasterSequenceDictionary(), true); vcfWriter2.writeHeader(new VCFHeader(hInfo, samples)); } diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/IndexingVCFWriter.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/IndexingVCFWriter.java index 4ae87ddcb..71ec4ce1b 100644 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/IndexingVCFWriter.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/IndexingVCFWriter.java @@ -24,6 +24,9 @@ package org.broadinstitute.sting.utils.codecs.vcf; +import com.google.java.contract.Ensures; +import com.google.java.contract.Requires; +import net.sf.samtools.SAMSequenceDictionary; import org.broad.tribble.Tribble; import org.broad.tribble.TribbleException; import org.broad.tribble.index.DynamicIndexCreator; @@ -31,7 +34,9 @@ import org.broad.tribble.index.Index; import org.broad.tribble.index.IndexFactory; import org.broad.tribble.util.LittleEndianOutputStream; import org.broad.tribble.util.PositionalStream; +import org.broadinstitute.sting.gatk.refdata.tracks.IndexDictionaryUtils; import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; +import org.broadinstitute.sting.utils.exceptions.UserException; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import java.io.*; @@ -41,21 +46,24 @@ import java.io.*; */ public abstract class IndexingVCFWriter implements VCFWriter { final private String name; + private final SAMSequenceDictionary refDict; - private File indexFile = null; private OutputStream outputStream; private PositionalStream positionalStream = null; private DynamicIndexCreator indexer = null; private LittleEndianOutputStream idxStream = null; - protected IndexingVCFWriter(String name, File location, OutputStream output, boolean enableOnTheFlyIndexing) { + @Requires({"name != null", + "! ( location == null && output == null )", + "! ( enableOnTheFlyIndexing && location == null )"}) + protected IndexingVCFWriter(final String name, final File location, final OutputStream output, final SAMSequenceDictionary refDict, final boolean enableOnTheFlyIndexing) { outputStream = output; this.name = name; + this.refDict = refDict; if ( enableOnTheFlyIndexing ) { - indexFile = Tribble.indexFile(location); try { - idxStream = new LittleEndianOutputStream(new FileOutputStream(indexFile)); + idxStream = new LittleEndianOutputStream(new FileOutputStream(Tribble.indexFile(location))); //System.out.println("Creating index on the fly for " + location); indexer = new DynamicIndexCreator(IndexFactory.IndexBalanceApproach.FOR_SEEK_TIME); indexer.initialize(location, indexer.defaultBinSize()); @@ -66,15 +74,16 @@ public abstract class IndexingVCFWriter implements VCFWriter { idxStream = null; indexer = null; positionalStream = null; - indexFile = null; } } } + @Ensures("result != null") public OutputStream getOutputStream() { return outputStream; } + @Ensures("result != null") public String getStreamName() { return name; } @@ -89,6 +98,7 @@ public abstract class IndexingVCFWriter implements VCFWriter { if ( indexer != null ) { try { Index index = indexer.finalizeIndex(positionalStream.getPosition()); + IndexDictionaryUtils.setIndexSequenceDictionary(index, refDict); index.write(idxStream); idxStream.close(); } catch (IOException e) { @@ -108,15 +118,27 @@ public abstract class IndexingVCFWriter implements VCFWriter { indexer.addFeature(vc, positionalStream.getPosition()); } - protected static final String writerName(File location, OutputStream stream) { + /** + * Returns a reasonable "name" for this writer, to display to the user if something goes wrong + * + * @param location + * @param stream + * @return + */ + protected static final String writerName(final File location, final OutputStream stream) { return location == null ? stream.toString() : location.getAbsolutePath(); } - protected static OutputStream openOutputStream(File location) { + /** + * Returns a output stream writing to location, or throws a UserException if this fails + * @param location + * @return + */ + protected static OutputStream openOutputStream(final File location) { try { return new FileOutputStream(location); } catch (FileNotFoundException e) { - throw new ReviewedStingException("Unable to create VCF file at location: " + location, e); + throw new UserException.CouldNotCreateOutputFile(location, "Unable to create VCF writer", e); } } } diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/StandardVCFWriter.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/StandardVCFWriter.java index 7cba5fc3e..0da7a100f 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/StandardVCFWriter.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/StandardVCFWriter.java @@ -24,6 +24,7 @@ package org.broadinstitute.sting.utils.codecs.vcf; +import net.sf.samtools.SAMSequenceDictionary; import org.broad.tribble.Tribble; import org.broad.tribble.TribbleException; import org.broad.tribble.index.DynamicIndexCreator; @@ -62,21 +63,12 @@ public class StandardVCFWriter extends IndexingVCFWriter { * * @param location the file location to write to */ - public StandardVCFWriter(File location) { - this(location, openOutputStream(location), true, false); + public StandardVCFWriter(final File location, final SAMSequenceDictionary refDict) { + this(location, openOutputStream(location), refDict, true, false); } - public StandardVCFWriter(File location, boolean enableOnTheFlyIndexing) { - this(location, openOutputStream(location), enableOnTheFlyIndexing, false); - } - - /** - * create a VCF writer, given a stream to write to - * - * @param output the file location to write to - */ - public StandardVCFWriter(OutputStream output) { - this(output, false); + public StandardVCFWriter(File location, final SAMSequenceDictionary refDict, boolean enableOnTheFlyIndexing) { + this(location, openOutputStream(location), refDict, enableOnTheFlyIndexing, false); } /** @@ -85,12 +77,12 @@ public class StandardVCFWriter extends IndexingVCFWriter { * @param output the file location to write to * @param doNotWriteGenotypes do not write genotypes */ - public StandardVCFWriter(OutputStream output, boolean doNotWriteGenotypes) { - this(null, output, false, doNotWriteGenotypes); + public StandardVCFWriter(final OutputStream output, final SAMSequenceDictionary refDict, final boolean doNotWriteGenotypes) { + this(null, output, refDict, false, doNotWriteGenotypes); } - public StandardVCFWriter(File location, OutputStream output, boolean enableOnTheFlyIndexing, boolean doNotWriteGenotypes) { - super(writerName(location, output), location, output, enableOnTheFlyIndexing); + public StandardVCFWriter(final File location, final OutputStream output, final SAMSequenceDictionary refDict, final boolean enableOnTheFlyIndexing, boolean doNotWriteGenotypes) { + super(writerName(location, output), location, output, refDict, enableOnTheFlyIndexing); mWriter = new BufferedWriter(new OutputStreamWriter(getOutputStream())); // todo -- fix buffer size this.doNotWriteGenotypes = doNotWriteGenotypes; } diff --git a/public/java/src/org/broadinstitute/sting/utils/gcf/GCFWriter.java b/public/java/src/org/broadinstitute/sting/utils/gcf/GCFWriter.java index 7ff6e27a2..18fae18c4 100644 --- a/public/java/src/org/broadinstitute/sting/utils/gcf/GCFWriter.java +++ b/public/java/src/org/broadinstitute/sting/utils/gcf/GCFWriter.java @@ -24,6 +24,7 @@ package org.broadinstitute.sting.utils.gcf; +import net.sf.samtools.SAMSequenceDictionary; import org.broadinstitute.sting.utils.codecs.vcf.IndexingVCFWriter; import org.broadinstitute.sting.utils.codecs.vcf.VCFHeader; import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; @@ -52,8 +53,8 @@ public class GCFWriter extends IndexingVCFWriter { // // -------------------------------------------------------------------------------- - public GCFWriter(File location, boolean enableOnTheFlyIndexing, boolean doNotWriteGenotypes) { - super(writerName(location, null), location, null, enableOnTheFlyIndexing); + public GCFWriter(final File location, final SAMSequenceDictionary refDict, boolean enableOnTheFlyIndexing, boolean doNotWriteGenotypes) { + super(writerName(location, null), location, null, refDict, enableOnTheFlyIndexing); this.location = location; this.skipGenotypes = doNotWriteGenotypes; diff --git a/public/java/test/org/broadinstitute/sting/WalkerTest.java b/public/java/test/org/broadinstitute/sting/WalkerTest.java index 386c17659..a1817e3c7 100755 --- a/public/java/test/org/broadinstitute/sting/WalkerTest.java +++ b/public/java/test/org/broadinstitute/sting/WalkerTest.java @@ -75,7 +75,7 @@ public class WalkerTest extends BaseTest { Index indexFromOutputFile = IndexFactory.createIndex(resultFile, new VCFCodec()); Index dynamicIndex = IndexFactory.loadIndex(indexFile.getAbsolutePath()); - if ( ! indexFromOutputFile.equalsIgnoreTimestamp(dynamicIndex) ) { + if ( ! indexFromOutputFile.equalsIgnoreProperties(dynamicIndex) ) { Assert.fail(String.format("Index on disk from indexing on the fly not equal to the index created after the run completed. FileIndex %s vs. on-the-fly %s%n", indexFromOutputFile.getProperties(), dynamicIndex.getProperties())); diff --git a/public/java/test/org/broadinstitute/sting/gatk/refdata/tracks/RMDTrackBuilderUnitTest.java b/public/java/test/org/broadinstitute/sting/gatk/refdata/tracks/RMDTrackBuilderUnitTest.java index ae218e898..724c343e4 100644 --- a/public/java/test/org/broadinstitute/sting/gatk/refdata/tracks/RMDTrackBuilderUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/refdata/tracks/RMDTrackBuilderUnitTest.java @@ -29,7 +29,6 @@ import net.sf.picard.reference.IndexedFastaSequenceFile; import net.sf.samtools.SAMSequenceDictionary; import org.broad.tribble.Tribble; import org.broad.tribble.index.Index; -import org.broadinstitute.sting.gatk.refdata.tracks.RMDTrackBuilder; import org.broadinstitute.sting.utils.codecs.vcf.VCF3Codec; import org.broadinstitute.sting.utils.codecs.vcf.VCFCodec; import org.broadinstitute.sting.utils.exceptions.UserException; @@ -45,7 +44,6 @@ import org.testng.annotations.Test; import java.io.*; import java.nio.channels.FileChannel; -import java.util.Map; /** @@ -164,7 +162,7 @@ public class RMDTrackBuilderUnitTest extends BaseTest { try { Index idx = builder.loadIndex(vcfFile, new VCFCodec()); // catch any exception; this call should pass correctly - SAMSequenceDictionary dict = RMDTrackBuilder.getSequenceDictionaryFromProperties(idx); + SAMSequenceDictionary dict = IndexDictionaryUtils.getSequenceDictionaryFromProperties(idx); } catch (IOException e) { e.printStackTrace(); Assert.fail("IO exception unexpected" + e.getMessage()); diff --git a/public/java/test/org/broadinstitute/sting/utils/codecs/vcf/IndexFactoryUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/codecs/vcf/IndexFactoryUnitTest.java index 1809ab778..55bd4783b 100755 --- a/public/java/test/org/broadinstitute/sting/utils/codecs/vcf/IndexFactoryUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/utils/codecs/vcf/IndexFactoryUnitTest.java @@ -1,27 +1,45 @@ package org.broadinstitute.sting.utils.codecs.vcf; +import net.sf.samtools.SAMSequenceDictionary; import org.broad.tribble.Tribble; import org.broad.tribble.index.*; import org.broad.tribble.iterators.CloseableTribbleIterator; import org.broad.tribble.source.BasicFeatureSource; +import org.broadinstitute.sting.BaseTest; import org.broadinstitute.sting.WalkerTest; +import org.broadinstitute.sting.utils.exceptions.UserException; +import org.broadinstitute.sting.utils.fasta.CachingIndexedFastaSequenceFile; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import org.testng.Assert; +import org.testng.annotations.BeforeTest; import org.testng.annotations.Test; import java.io.File; +import java.io.FileNotFoundException; import java.io.IOException; import java.util.*; /** * tests out the various functions in the index factory class */ -public class IndexFactoryUnitTest { +public class IndexFactoryUnitTest extends BaseTest { File inputFile = new File("public/testdata/HiSeq.10000.vcf"); File outputFile = new File("public/testdata/onTheFlyOutputTest.vcf"); File outputFileIndex = Tribble.indexFile(outputFile); + private SAMSequenceDictionary dict; + + @BeforeTest + public void setup() { + try { + dict = new CachingIndexedFastaSequenceFile(new File(b37KGReference)).getSequenceDictionary(); + } + catch(FileNotFoundException ex) { + throw new UserException.CouldNotReadInputFile(b37KGReference,ex); + } + } + // // test out scoring the indexes // @@ -37,7 +55,7 @@ public class IndexFactoryUnitTest { BasicFeatureSource source = new BasicFeatureSource(inputFile.getAbsolutePath(), indexFromInputFile, new VCFCodec()); int counter = 0; - VCFWriter writer = new StandardVCFWriter(outputFile); + VCFWriter writer = new StandardVCFWriter(outputFile, dict); writer.writeHeader((VCFHeader)source.getHeader()); CloseableTribbleIterator it = source.iterator(); while (it.hasNext() && (counter++ < maxRecords || maxRecords == -1) ) { diff --git a/public/java/test/org/broadinstitute/sting/utils/genotype/vcf/VCFWriterUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/genotype/vcf/VCFWriterUnitTest.java index e3a926fb9..a8e6593b1 100644 --- a/public/java/test/org/broadinstitute/sting/utils/genotype/vcf/VCFWriterUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/utils/genotype/vcf/VCFWriterUnitTest.java @@ -38,12 +38,13 @@ public class VCFWriterUnitTest extends BaseTest { private Set additionalColumns = new HashSet(); private File fakeVCFFile = new File("FAKEVCFFILEFORTESTING.vcf"); private GenomeLocParser genomeLocParser; + private IndexedFastaSequenceFile seq; @BeforeClass public void beforeTests() { File referenceFile = new File(hg18Reference); try { - IndexedFastaSequenceFile seq = new CachingIndexedFastaSequenceFile(referenceFile); + seq = new CachingIndexedFastaSequenceFile(referenceFile); genomeLocParser = new GenomeLocParser(seq); } catch(FileNotFoundException ex) { @@ -55,7 +56,7 @@ public class VCFWriterUnitTest extends BaseTest { @Test public void testBasicWriteAndRead() { VCFHeader header = createFakeHeader(metaData,additionalColumns); - VCFWriter writer = new StandardVCFWriter(fakeVCFFile); + VCFWriter writer = new StandardVCFWriter(fakeVCFFile, seq.getSequenceDictionary()); writer.writeHeader(header); writer.add(createVC(header)); writer.add(createVC(header)); From 08ffb18b96b1d22f14c488cd71a1d02b5490f6a1 Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Tue, 20 Sep 2011 11:02:51 -0400 Subject: [PATCH 105/196] Renaming datasets in the MDCP Making dataset names and files generated by the MDCP more uniform. --- .../MethodsDevelopmentCallingPipeline.scala | 40 ++++++++++++------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala index 80bfe03d1..637d3edca 100755 --- a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala +++ b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala @@ -98,40 +98,52 @@ class MethodsDevelopmentCallingPipeline extends QScript { // BUGBUG: We no longer support b36/hg18 because several of the necessary files aren't available aligned to those references val targetDataSets: Map[String, Target] = Map( - "HiSeq" -> new Target("NA12878.HiSeq", hg18, dbSNP_hg18_129, hapmap_hg18, + "NA12878_wgs_b37" -> new Target("NA12878.HiSeq.WGS.b37", hg19, dbSNP_b37, hapmap_b37, indelMask_b37, + new File("/humgen/gsa-hpprojects/NA12878Collection/bams/NA12878.HiSeq.WGS.bwa.cleaned.recal.hg19.bam"), + new File("/humgen/gsa-hpprojects/dev/carneiro/hiseq19/analysis/snps/NA12878.HiSeq19.filtered.vcf"), + "/humgen/1kg/processing/pipeline_test_bams/whole_genome_chunked.noChrY.hg19.intervals", 2.14, 99.0, !lowPass, !exome, 1), + "NA12878_wgs_decoy" -> new Target("NA12878.HiSeq.WGS.b37_decoy", b37_decoy, dbSNP_b37, hapmap_b37, indelMask_b37, + new File("/humgen/gsa-hpprojects/NA12878Collection/bams/CEUTrio.HiSeq.WGS.b37_decoy.NA12878.clean.dedup.recal.bam"), + new File("/humgen/gsa-hpprojects/dev/carneiro/hiseq19/analysis/snps/NA12878.HiSeq19.filtered.vcf"), // ** THIS GOLD STANDARD NEEDS TO BE CORRECTED ** + "/humgen/1kg/processing/pipeline_test_bams/whole_genome_chunked.noChrY.hg19.intervals", 2.14, 99.0, !lowPass, !exome, 1), + "NA12878_wgs_hg18" -> new Target("NA12878.HiSeq.WGS.hg18", hg18, dbSNP_hg18_129, hapmap_hg18, "/humgen/gsa-hpprojects/dev/depristo/oneOffProjects/1000GenomesProcessingPaper/wgs.v13/HiSeq.WGS.cleaned.indels.10.mask", new File("/humgen/gsa-hpprojects/NA12878Collection/bams/NA12878.HiSeq.WGS.bwa.cleaned.recal.bam"), new File("/home/radon01/depristo/work/oneOffProjects/1000GenomesProcessingPaper/wgs.v13/HiSeq.WGS.cleaned.ug.snpfiltered.indelfiltered.vcf"), "/humgen/1kg/processing/pipeline_test_bams/whole_genome_chunked.hg18.intervals", 2.14, 99.0, !lowPass, !exome, 1), - "HiSeq19" -> new Target("NA12878.HiSeq19", hg19, dbSNP_b37, hapmap_b37, indelMask_b37, - new File("/humgen/gsa-hpprojects/NA12878Collection/bams/NA12878.HiSeq.WGS.bwa.cleaned.recal.hg19.bam"), - new File("/humgen/gsa-hpprojects/dev/carneiro/hiseq19/analysis/snps/NA12878.HiSeq19.filtered.vcf"), - "/humgen/1kg/processing/pipeline_test_bams/whole_genome_chunked.noChrY.hg19.intervals", 2.14, 99.0, !lowPass, !exome, 1), - "GA2hg19" -> new Target("NA12878.GA2.hg19", hg19, dbSNP_b37, hapmap_b37, indelMask_b37, - new File("/humgen/gsa-hpprojects/NA12878Collection/bams/NA12878.GA2.WGS.bwa.cleaned.hg19.bam"), - new File("/humgen/gsa-hpprojects/dev/carneiro/hiseq19/analysis/snps/NA12878.GA2.hg19.filtered.vcf"), - "/humgen/1kg/processing/pipeline_test_bams/whole_genome_chunked.hg19.intervals", 2.14, 99.0, !lowPass, !exome, 1), - "WEx" -> new Target("NA12878.WEx", hg18, dbSNP_hg18_129, hapmap_hg18, + "NA12878_wex_b37" -> new Target("NA12878.HiSeq.WEx.b37", hg19, dbSNP_b37, hapmap_b37, indelMask_b37, + new File("/seq/picard_aggregation/C339/NA12878/v3/NA12878.bam"), + new File("/humgen/gsa-hpprojects/dev/carneiro/trio/analysis/snps/CEUTrio.WEx.filtered.vcf"), // ** THIS GOLD STANDARD NEEDS TO BE CORRECTED ** + "/seq/references/HybSelOligos/whole_exome_agilent_1.1_refseq_plus_3_boosters/whole_exome_agilent_1.1_refseq_plus_3_boosters.Homo_sapiens_assembly19.targets.interval_list", 3.3, 98.0, !lowPass, exome, 1), + "NA12878_wex_hg18" -> new Target("NA12878.HiSeq.WEx.hg18", hg18, dbSNP_hg18_129, hapmap_hg18, "/humgen/gsa-hpprojects/dev/depristo/oneOffProjects/1000GenomesProcessingPaper/wgs.v13/GA2.WEx.cleaned.indels.10.mask", new File("/humgen/gsa-hpprojects/NA12878Collection/bams/NA12878.WEx.cleaned.recal.bam"), new File("/home/radon01/depristo/work/oneOffProjects/1000GenomesProcessingPaper/wgs.v13/GA2.WEx.cleaned.ug.snpfiltered.indelfiltered.vcf"), "/seq/references/HybSelOligos/whole_exome_agilent_1.1_refseq_plus_3_boosters/whole_exome_agilent_1.1_refseq_plus_3_boosters.targets.interval_list", 3.3, 98.0, !lowPass, exome, 1), - "WExTrio" -> new Target("CEUTrio.WEx", hg19, dbSNP_b37, hapmap_b37, indelMask_b37, + "NA12878_wex_decoy" -> new Target("NA12878.HiSeq.WEx.b37_decoy", b37_decoy, dbSNP_b37, hapmap_b37, indelMask_b37, + new File("/humgen/gsa-hpprojects/NA12878Collection/bams/CEUTrio.HiSeq.WEx.b37_decoy.NA12878.clean.dedup.recal.bam"), + new File("/humgen/gsa-hpprojects/dev/carneiro/trio/analysis/snps/CEUTrio.WEx.filtered.vcf"), // ** THIS GOLD STANDARD NEEDS TO BE CORRECTED ** + "/seq/references/HybSelOligos/whole_exome_agilent_1.1_refseq_plus_3_boosters/whole_exome_agilent_1.1_refseq_plus_3_boosters.Homo_sapiens_assembly19.targets.interval_list", 3.3, 98.0, !lowPass, exome, 1), + "CEUTrio_wex_b37" -> new Target("CEUTrio.HiSeq.WEx.b37", hg19, dbSNP_b37, hapmap_b37, indelMask_b37, new File("/humgen/gsa-hpprojects/NA12878Collection/bams/CEUTrio.HiSeq.WEx.bwa.cleaned.recal.bam"), new File("/humgen/gsa-hpprojects/dev/carneiro/trio/analysis/snps/CEUTrio.WEx.filtered.vcf"), "/seq/references/HybSelOligos/whole_exome_agilent_1.1_refseq_plus_3_boosters/whole_exome_agilent_1.1_refseq_plus_3_boosters.Homo_sapiens_assembly19.targets.interval_list", 3.3, 98.0, !lowPass, exome, 3), - "WGSTrio" -> new Target("CEUTrio.WGS", hg19, dbSNP_b37, hapmap_b37, indelMask_b37, + "CEUTrio_wgs_b37" -> new Target("CEUTrio.HiSeq.WGS.b37", hg19, dbSNP_b37, hapmap_b37, indelMask_b37, new File("/humgen/gsa-hpprojects/NA12878Collection/bams/CEUTrio.HiSeq.WGS.bwa.cleaned.recal.bam"), new File("/humgen/gsa-hpprojects/dev/carneiro/trio/analysis/snps/CEUTrio.WEx.filtered.vcf"), // ** THIS GOLD STANDARD NEEDS TO BE CORRECTED ** "/humgen/1kg/processing/pipeline_test_bams/whole_genome_chunked.hg19.intervals", 2.3, 99.0, !lowPass, !exome, 3), - "WExTrioDecoy" -> new Target("CEUTrio.HiSeq.WEx.b37_decoy", b37_decoy, dbSNP_b37, hapmap_b37, indelMask_b37, + "CEUTrio_wex_decoy" -> new Target("CEUTrio.HiSeq.WEx.b37_decoy", b37_decoy, dbSNP_b37, hapmap_b37, indelMask_b37, new File("/humgen/gsa-hpprojects/NA12878Collection/bams/CEUTrio.HiSeq.WEx.b37_decoy.list"), new File("/humgen/gsa-hpprojects/dev/carneiro/trio/analysis/snps/CEUTrio.WEx.filtered.vcf"), // ** THIS GOLD STANDARD NEEDS TO BE CORRECTED ** "/seq/references/HybSelOligos/whole_exome_agilent_1.1_refseq_plus_3_boosters/whole_exome_agilent_1.1_refseq_plus_3_boosters.Homo_sapiens_assembly19.targets.interval_list", 3.3, 98.0, !lowPass, exome, 3), - "WGSTrioDecoy" -> new Target("CEUTrio.HiSeq.WGS.b37_decoy", b37_decoy, dbSNP_b37, hapmap_b37, indelMask_b37, + "CEUTrio_wgs_decoy" -> new Target("CEUTrio.HiSeq.WGS.b37_decoy", b37_decoy, dbSNP_b37, hapmap_b37, indelMask_b37, new File("/humgen/gsa-hpprojects/NA12878Collection/bams/CEUTrio.HiSeq.WGS.b37_decoy.list"), new File("/humgen/gsa-hpprojects/dev/carneiro/trio/analysis/snps/CEUTrio.WEx.filtered.vcf"), // ** THIS GOLD STANDARD NEEDS TO BE CORRECTED ** "/humgen/1kg/processing/pipeline_test_bams/whole_genome_chunked.hg19.intervals", 2.3, 99.0, !lowPass, !exome, 3), + "GA2hg19" -> new Target("NA12878.GA2.hg19", hg19, dbSNP_b37, hapmap_b37, indelMask_b37, + new File("/humgen/gsa-hpprojects/NA12878Collection/bams/NA12878.GA2.WGS.bwa.cleaned.hg19.bam"), + new File("/humgen/gsa-hpprojects/dev/carneiro/hiseq19/analysis/snps/NA12878.GA2.hg19.filtered.vcf"), + "/humgen/1kg/processing/pipeline_test_bams/whole_genome_chunked.hg19.intervals", 2.14, 99.0, !lowPass, !exome, 1), "FIN" -> new Target("FIN", b37, dbSNP_b37, hapmap_b37, indelMask_b37, new File("/humgen/1kg/processing/pipeline_test_bams/FIN.79sample.Nov2010.chr20.bam"), new File("/humgen/gsa-hpprojects/dev/data/AugChr20Calls_v4_3state/ALL.august.v4.chr20.filtered.vcf"), // ** THIS GOLD STANDARD NEEDS TO BE CORRECTED ** From a1b4cafe7a63ecbeea9b06a94445f6bbf925d5e1 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Tue, 20 Sep 2011 13:59:59 -0400 Subject: [PATCH 106/196] Bug fix for NPE when timer wasn't initialized --- .../broadinstitute/sting/gatk/traversals/TraversalEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/traversals/TraversalEngine.java b/public/java/src/org/broadinstitute/sting/gatk/traversals/TraversalEngine.java index 27fd173cb..c6321e2ad 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/traversals/TraversalEngine.java +++ b/public/java/src/org/broadinstitute/sting/gatk/traversals/TraversalEngine.java @@ -358,7 +358,7 @@ public abstract class TraversalEngine,Provide public void printOnTraversalDone() { printProgress(null, null, true); - final double elapsed = timer.getElapsedTime(); + final double elapsed = timer == null ? 0 : timer.getElapsedTime(); ReadMetrics cumulativeMetrics = engine.getCumulativeMetrics(); From 827c942c8027d7888b29ddff8399834b7bcf94e4 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Tue, 20 Sep 2011 14:01:14 -0400 Subject: [PATCH 107/196] Rev tribble --- .../{tribble-24.jar => tribble-25.jar} | Bin 299210 -> 305986 bytes .../{tribble-24.xml => tribble-25.xml} | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename settings/repository/org.broad/{tribble-24.jar => tribble-25.jar} (89%) rename settings/repository/org.broad/{tribble-24.xml => tribble-25.xml} (51%) diff --git a/settings/repository/org.broad/tribble-24.jar b/settings/repository/org.broad/tribble-25.jar similarity index 89% rename from settings/repository/org.broad/tribble-24.jar rename to settings/repository/org.broad/tribble-25.jar index b1c39e60a14ce895e515cadb07c356d6d428bbc4..6d467ba3fdd6e6285e8244a87aefa4a0d232cfe1 100644 GIT binary patch delta 11585 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 HISu{?jA+l@ delta 11458 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 I_~7V&0me&b8~^|S diff --git a/settings/repository/org.broad/tribble-24.xml b/settings/repository/org.broad/tribble-25.xml similarity index 51% rename from settings/repository/org.broad/tribble-24.xml rename to settings/repository/org.broad/tribble-25.xml index 9b2b967f8..ed7a1fd69 100644 --- a/settings/repository/org.broad/tribble-24.xml +++ b/settings/repository/org.broad/tribble-25.xml @@ -1,3 +1,3 @@ - + From bffd3cca6fe26fe31a6fa5a33745dcaee0139c56 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Tue, 20 Sep 2011 15:07:06 -0400 Subject: [PATCH 109/196] Bug fix for reduced read; only adds regular bases for calculation -- No longer passes on deletions for genotyping --- .../walkers/genotyper/DiploidSNPGenotypeLikelihoods.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidSNPGenotypeLikelihoods.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidSNPGenotypeLikelihoods.java index 5f6865d04..ec180f0cd 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidSNPGenotypeLikelihoods.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidSNPGenotypeLikelihoods.java @@ -276,8 +276,11 @@ public class DiploidSNPGenotypeLikelihoods implements Cloneable { if ( elt.isReducedRead() ) { // reduced read representation byte qual = elt.getReducedQual(); - add(obsBase, qual, (byte)0, (byte)0, elt.getReducedCount()); // fast calculation of n identical likelihoods - return elt.getReducedCount(); // we added nObs bases here + if ( BaseUtils.isRegularBase( elt.getBase() )) { + add(obsBase, qual, (byte)0, (byte)0, elt.getReducedCount()); // fast calculation of n identical likelihoods + return elt.getReducedCount(); // we added nObs bases here + } else // odd bases or deletions => don't use them + return 0; } else { byte qual = qualToUse(elt, ignoreBadBases, capBaseQualsAtMappingQual, minBaseQual); return qual > 0 ? add(obsBase, qual, (byte)0, (byte)0, 1) : 0; From d9ea764611c0e6b63e8b6e2022fafc52ee56bea9 Mon Sep 17 00:00:00 2001 From: David Roazen Date: Tue, 20 Sep 2011 15:22:27 -0400 Subject: [PATCH 110/196] SnpEff annotator now adds OriginalSnpEffVersion and OriginalSnpEffCmd lines to the header of the VCF output file. This change is urgently required for production, which is why it's going into Stable+Unstable instead of just Unstable. The keys for the SnpEff version and command header lines in the VCF file output by VariantAnnotator (OriginalSnpEffVersion and OriginalSnpEffCmd) are intentionally different from the keys for those same lines in the SnpEff output file (SnpEffVersion and SnpEffCmd), so that output files from VariantAnnotator won't be confused with output files from SnpEff itself. --- .../sting/gatk/walkers/annotator/SnpEff.java | 49 +++++++++++++++---- .../walkers/annotator/VariantAnnotator.java | 4 +- .../annotator/VariantAnnotatorEngine.java | 6 +-- .../VariantAnnotatorAnnotation.java | 4 +- .../VariantAnnotatorIntegrationTest.java | 2 +- 5 files changed, 48 insertions(+), 17 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SnpEff.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SnpEff.java index 4ead77506..8f99c6118 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SnpEff.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SnpEff.java @@ -58,6 +58,13 @@ public class SnpEff extends InfoFieldAnnotation implements ExperimentalAnnotatio // lacking a SnpEff version number in the VCF header: public static final String[] SUPPORTED_SNPEFF_VERSIONS = { "2.0.2" }; public static final String SNPEFF_VCF_HEADER_VERSION_LINE_KEY = "SnpEffVersion"; + public static final String SNPEFF_VCF_HEADER_COMMAND_LINE_KEY = "SnpEffCmd"; + + // When we write the SnpEff version number and command line to the output VCF, we change + // the key name slightly so that the output VCF won't be confused in the future for an + // output file produced by SnpEff directly: + public static final String OUTPUT_VCF_HEADER_VERSION_LINE_KEY = "Original" + SNPEFF_VCF_HEADER_VERSION_LINE_KEY; + public static final String OUTPUT_VCF_HEADER_COMMAND_LINE_KEY = "Original" + SNPEFF_VCF_HEADER_COMMAND_LINE_KEY; // SnpEff aggregates all effects (and effect metadata) together into a single INFO // field annotation with the key EFF: @@ -165,10 +172,26 @@ public class SnpEff extends InfoFieldAnnotation implements ExperimentalAnnotatio UNKNOWN } - - public void initialize ( AnnotatorCompatibleWalker walker, GenomeAnalysisEngine toolkit ) { + public void initialize ( AnnotatorCompatibleWalker walker, GenomeAnalysisEngine toolkit, Set headerLines ) { + // Make sure that we actually have a valid SnpEff rod binding (just in case the user specified -A SnpEff + // without providing a SnpEff rod via --snpEffFile): validateRodBinding(walker.getSnpEffRodBinding()); - checkSnpEffVersion(walker, toolkit); + RodBinding snpEffRodBinding = walker.getSnpEffRodBinding(); + + // Make sure that the SnpEff version number and command-line header lines are present in the VCF header of + // the SnpEff rod, and that the file was generated by a supported version of SnpEff: + VCFHeader snpEffVCFHeader = VCFUtils.getVCFHeadersFromRods(toolkit, Arrays.asList(snpEffRodBinding.getName())).get(snpEffRodBinding.getName()); + VCFHeaderLine snpEffVersionLine = snpEffVCFHeader.getOtherHeaderLine(SNPEFF_VCF_HEADER_VERSION_LINE_KEY); + VCFHeaderLine snpEffCommandLine = snpEffVCFHeader.getOtherHeaderLine(SNPEFF_VCF_HEADER_COMMAND_LINE_KEY); + + checkSnpEffVersion(snpEffVersionLine); + checkSnpEffCommandLine(snpEffCommandLine); + + // If everything looks ok, add the SnpEff version number and command-line header lines to the + // header of the VCF output file, changing the key names so that our output file won't be + // mistaken in the future for a SnpEff output file: + headerLines.add(new VCFHeaderLine(OUTPUT_VCF_HEADER_VERSION_LINE_KEY, snpEffVersionLine.getValue())); + headerLines.add(new VCFHeaderLine(OUTPUT_VCF_HEADER_COMMAND_LINE_KEY, snpEffCommandLine.getValue())); } public Map annotate ( RefMetaDataTracker tracker, AnnotatorCompatibleWalker walker, ReferenceContext ref, Map stratifiedContexts, VariantContext vc ) { @@ -204,12 +227,7 @@ public class SnpEff extends InfoFieldAnnotation implements ExperimentalAnnotatio } } - private void checkSnpEffVersion ( AnnotatorCompatibleWalker walker, GenomeAnalysisEngine toolkit ) { - RodBinding snpEffRodBinding = walker.getSnpEffRodBinding(); - - VCFHeader snpEffVCFHeader = VCFUtils.getVCFHeadersFromRods(toolkit, Arrays.asList(snpEffRodBinding.getName())).get(snpEffRodBinding.getName()); - VCFHeaderLine snpEffVersionLine = snpEffVCFHeader.getOtherHeaderLine(SNPEFF_VCF_HEADER_VERSION_LINE_KEY); - + private void checkSnpEffVersion ( VCFHeaderLine snpEffVersionLine ) { if ( snpEffVersionLine == null || snpEffVersionLine.getValue() == null || snpEffVersionLine.getValue().trim().length() == 0 ) { throw new UserException("Could not find a " + SNPEFF_VCF_HEADER_VERSION_LINE_KEY + " entry in the VCF header for the SnpEff " + "input file, and so could not verify that the file was generated by a supported version of SnpEff (" + @@ -224,6 +242,14 @@ public class SnpEff extends InfoFieldAnnotation implements ExperimentalAnnotatio } } + private void checkSnpEffCommandLine ( VCFHeaderLine snpEffCommandLine ) { + if ( snpEffCommandLine == null || snpEffCommandLine.getValue() == null || snpEffCommandLine.getValue().trim().length() == 0 ) { + throw new UserException("Could not find a " + SNPEFF_VCF_HEADER_COMMAND_LINE_KEY + " entry in the VCF header for the SnpEff " + + "input file, which should be added by all supported versions of SnpEff (" + + Arrays.toString(SUPPORTED_SNPEFF_VERSIONS) + ")"); + } + } + private boolean isSupportedSnpEffVersion ( String versionString ) { for ( String supportedVersion : SUPPORTED_SNPEFF_VERSIONS ) { if ( supportedVersion.equals(versionString) ) { @@ -248,10 +274,13 @@ public class SnpEff extends InfoFieldAnnotation implements ExperimentalAnnotatio List parsedEffects = new ArrayList(); Object effectFieldValue = snpEffRecord.getAttribute(SNPEFF_INFO_FIELD_KEY); - List individualEffects; + if ( effectFieldValue == null ) { + return parsedEffects; + } // The VCF codec stores multi-valued fields as a List, and single-valued fields as a String. // We can have either in the case of SnpEff, since there may be one or more than one effect in this record. + List individualEffects; if ( effectFieldValue instanceof List ) { individualEffects = (List)effectFieldValue; } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotator.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotator.java index fb3dbc3cf..f6a1c4f31 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotator.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotator.java @@ -208,8 +208,6 @@ public class VariantAnnotator extends RodWalker implements Ann engine = new VariantAnnotatorEngine(annotationGroupsToUse, annotationsToUse, this, getToolkit()); engine.initializeExpressions(expressionsToUse); - engine.invokeAnnotationInitializationMethods(); - // setup the header fields // note that if any of the definitions conflict with our new ones, then we want to overwrite the old ones Set hInfo = new HashSet(); @@ -219,6 +217,8 @@ public class VariantAnnotator extends RodWalker implements Ann hInfo.add(line); } + engine.invokeAnnotationInitializationMethods(hInfo); + VCFHeader vcfHeader = new VCFHeader(hInfo, samples); vcfWriter.writeHeader(vcfHeader); diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorEngine.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorEngine.java index 68cd07803..e5effe6d8 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorEngine.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorEngine.java @@ -114,13 +114,13 @@ public class VariantAnnotatorEngine { dbAnnotations.put(rod, rod.getName()); } - public void invokeAnnotationInitializationMethods() { + public void invokeAnnotationInitializationMethods( Set headerLines ) { for ( VariantAnnotatorAnnotation annotation : requestedInfoAnnotations ) { - annotation.initialize(walker, toolkit); + annotation.initialize(walker, toolkit, headerLines); } for ( VariantAnnotatorAnnotation annotation : requestedGenotypeAnnotations ) { - annotation.initialize(walker, toolkit); + annotation.initialize(walker, toolkit, headerLines); } } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/interfaces/VariantAnnotatorAnnotation.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/interfaces/VariantAnnotatorAnnotation.java index 160a3d258..521f89016 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/interfaces/VariantAnnotatorAnnotation.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/interfaces/VariantAnnotatorAnnotation.java @@ -25,9 +25,11 @@ package org.broadinstitute.sting.gatk.walkers.annotator.interfaces; import org.broadinstitute.sting.gatk.GenomeAnalysisEngine; +import org.broadinstitute.sting.utils.codecs.vcf.VCFHeaderLine; import org.broadinstitute.sting.utils.help.DocumentedGATKFeature; import java.util.List; +import java.util.Set; @DocumentedGATKFeature(enable = true, groupName = "VariantAnnotator annotations", summary = "VariantAnnotator annotations") public abstract class VariantAnnotatorAnnotation { @@ -35,5 +37,5 @@ public abstract class VariantAnnotatorAnnotation { public abstract List getKeyNames(); // initialization method (optional for subclasses, and therefore non-abstract) - public void initialize ( AnnotatorCompatibleWalker walker, GenomeAnalysisEngine toolkit ) { } + public void initialize ( AnnotatorCompatibleWalker walker, GenomeAnalysisEngine toolkit, Set headerLines ) { } } \ No newline at end of file diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorIntegrationTest.java index 08baae7a7..2c06c6b7f 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorIntegrationTest.java @@ -134,7 +134,7 @@ public class VariantAnnotatorIntegrationTest extends WalkerTest { validationDataLocation + "1kg_exomes_unfiltered.AFR.unfiltered.vcf --snpEffFile " + validationDataLocation + "snpEff.AFR.unfiltered.vcf -L 1:1-1,500,000", 1, - Arrays.asList("486fc6a5ca1819f5ab180d5d72b1ebc9") + Arrays.asList("ed9d1b37b0bd8b65ff9ce2688e0e102e") ); executeTest("Testing SnpEff annotations", spec); } From a91ac0c5dbb28667c38d69230827112af569ab55 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Wed, 21 Sep 2011 10:15:05 -0400 Subject: [PATCH 114/196] Intermediate commit of bugfixes to CombineVariants --- .../org/broadinstitute/sting/utils/Utils.java | 40 ++++--- .../variantcontext/VariantContextUtils.java | 106 ++++++++---------- .../CombineVariantsIntegrationTest.java | 11 +- 3 files changed, 82 insertions(+), 75 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/Utils.java b/public/java/src/org/broadinstitute/sting/utils/Utils.java index f6edb319f..6ce492c63 100755 --- a/public/java/src/org/broadinstitute/sting/utils/Utils.java +++ b/public/java/src/org/broadinstitute/sting/utils/Utils.java @@ -240,22 +240,34 @@ public class Utils { return ret.toString(); } - //public static String join(String separator, Collection strings) { - // return join( separator, strings.toArray(new String[0]) ); - //} - - public static String join(String separator, Collection objects) { - if(objects.isEmpty()) { + /** + * Returns a string of the form elt1.toString() [sep elt2.toString() ... sep elt.toString()] for a collection of + * elti objects (note there's no actual space between sep and the elti elements). Returns + * "" if collection is empty. If collection contains just elt, then returns elt.toString() + * + * @param separator the string to use to separate objects + * @param objects a collection of objects. the element order is defined by the iterator over objects + * @param the type of the objects + * @return a non-null string + */ + public static String join(final String separator, final Collection objects) { + if (objects.isEmpty()) { // fast path for empty collection return ""; - } - Iterator iter = objects.iterator(); - final StringBuilder ret = new StringBuilder(iter.next().toString()); - while(iter.hasNext()) { - ret.append(separator); - ret.append(iter.next().toString()); - } + } else { + final Iterator iter = objects.iterator(); + final T first = iter.next(); - return ret.toString(); + if ( ! iter.hasNext() ) // fast path for singleton collections + return first.toString(); + else { // full path for 2+ collection that actually need a join + final StringBuilder ret = new StringBuilder(first.toString()); + while(iter.hasNext()) { + ret.append(separator); + ret.append(iter.next().toString()); + } + return ret.toString(); + } + } } public static String dupString(char c, int nCopies) { diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java index 986d6305c..147538253 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java @@ -316,19 +316,22 @@ public class VariantContextUtils { return pruneVariantContext(vc, null); } - public static VariantContext pruneVariantContext(VariantContext vc, Collection keysToPreserve ) { - MutableVariantContext mvc = new MutableVariantContext(vc); + public static VariantContext pruneVariantContext(final VariantContext vc, final Collection keysToPreserve ) { + final MutableVariantContext mvc = new MutableVariantContext(vc); if ( keysToPreserve == null || keysToPreserve.size() == 0 ) mvc.clearAttributes(); else { - Map d = mvc.getAttributes(); + final Map d = mvc.getAttributes(); mvc.clearAttributes(); for ( String key : keysToPreserve ) if ( d.containsKey(key) ) mvc.putAttribute(key, d.get(key)); } + // this must be done as the ID is stored in the attributes field + if ( vc.hasID() ) mvc.setID(vc.getID()); + Collection gs = mvc.getGenotypes().values(); mvc.clearGenotypes(); for ( Genotype g : gs ) { @@ -443,34 +446,6 @@ public class VariantContextUtils { throw new ReviewedStingException(String.format("Couldn't find master VCF %s at %s", masterName, unsortedVCs.iterator().next())); } - - public static VariantContext simpleMerge(GenomeLocParser genomeLocParser, Collection unsortedVCs, byte refBase) { - return simpleMerge(genomeLocParser, unsortedVCs, null, FilteredRecordMergeType.KEEP_IF_ANY_UNFILTERED, GenotypeMergeType.UNSORTED, false, false, refBase); - } - - - /** - * Merges VariantContexts into a single hybrid. Takes genotypes for common samples in priority order, if provided. - * If uniqifySamples is true, the priority order is ignored and names are created by concatenating the VC name with - * the sample name - * - * @param genomeLocParser loc parser - * @param unsortedVCs collection of unsorted VCs - * @param priorityListOfVCs priority list detailing the order in which we should grab the VCs - * @param filteredRecordMergeType merge type for filtered records - * @param genotypeMergeOptions merge option for genotypes - * @param annotateOrigin should we annotate the set it came from? - * @param printMessages should we print messages? - * @param inputRefBase the ref base - * @return new VariantContext - */ - public static VariantContext simpleMerge(GenomeLocParser genomeLocParser, Collection unsortedVCs, List priorityListOfVCs, - FilteredRecordMergeType filteredRecordMergeType, GenotypeMergeType genotypeMergeOptions, - boolean annotateOrigin, boolean printMessages, byte inputRefBase ) { - - return simpleMerge(genomeLocParser, unsortedVCs, priorityListOfVCs, filteredRecordMergeType, genotypeMergeOptions, annotateOrigin, printMessages, "set", false, false); - } - /** * Merges VariantContexts into a single hybrid. Takes genotypes for common samples in priority order, if provided. * If uniqifySamples is true, the priority order is ignored and names are created by concatenating the VC name with @@ -486,12 +461,18 @@ public class VariantContextUtils { * @param setKey the key name of the set * @param filteredAreUncalled are filtered records uncalled? * @param mergeInfoWithMaxAC should we merge in info from the VC with maximum allele count? - * @return new VariantContext + * @return new VariantContext representing the merge of unsortedVCs */ - public static VariantContext simpleMerge(GenomeLocParser genomeLocParser, Collection unsortedVCs, List priorityListOfVCs, - FilteredRecordMergeType filteredRecordMergeType, GenotypeMergeType genotypeMergeOptions, - boolean annotateOrigin, boolean printMessages, String setKey, - boolean filteredAreUncalled, boolean mergeInfoWithMaxAC ) { + public static VariantContext simpleMerge(final GenomeLocParser genomeLocParser, + final Collection unsortedVCs, + final List priorityListOfVCs, + final FilteredRecordMergeType filteredRecordMergeType, + final GenotypeMergeType genotypeMergeOptions, + final boolean annotateOrigin, + final boolean printMessages, + final String setKey, + final boolean filteredAreUncalled, + final boolean mergeInfoWithMaxAC ) { if ( unsortedVCs == null || unsortedVCs.size() == 0 ) return null; @@ -514,26 +495,28 @@ public class VariantContextUtils { return null; // establish the baseline info from the first VC - VariantContext first = VCs.get(0); - String name = first.getSource(); - GenomeLoc loc = getLocation(genomeLocParser,first); + final VariantContext first = VCs.get(0); + final String name = first.getSource(); + final Allele refAllele = determineReferenceAllele(VCs); - Set alleles = new TreeSet(); - Map genotypes = new TreeMap(); - double negLog10PError = -1; - Set filters = new TreeSet(); - Map attributes = new TreeMap(); - Set inconsistentAttributes = new HashSet(); - String rsID = null; + final Set alleles = new TreeSet(); + final Set filters = new TreeSet(); + final Map attributes = new TreeMap(); + final Set inconsistentAttributes = new HashSet(); + final Set variantSources = new HashSet(); // contains the set of sources we found in our set of VCs that are variant + final Set rsIDs = new LinkedHashSet(1); // most of the time there's one id + + GenomeLoc loc = getLocation(genomeLocParser,first); int depth = 0; int maxAC = -1; - Map attributesWithMaxAC = new TreeMap(); + final Map attributesWithMaxAC = new TreeMap(); + double negLog10PError = -1; VariantContext vcWithMaxAC = null; + Map genotypes = new TreeMap(); // counting the number of filtered and variant VCs - int nFiltered = 0, nVariant = 0; + int nFiltered = 0; - Allele refAllele = determineReferenceAllele(VCs); boolean remapped = false; // cycle through and add info from the other VCs, making sure the loc/reference matches @@ -546,7 +529,7 @@ public class VariantContextUtils { loc = getLocation(genomeLocParser,vc); // get the longest location nFiltered += vc.isFiltered() ? 1 : 0; - nVariant += vc.isVariant() ? 1 : 0; + if ( vc.isVariant() ) variantSources.add(vc.getSource()); AlleleMapper alleleMapping = resolveIncompatibleAlleles(refAllele, vc, alleles); remapped = remapped || alleleMapping.needsRemapping(); @@ -566,8 +549,10 @@ public class VariantContextUtils { // if (vc.hasAttribute(VCFConstants.DEPTH_KEY)) depth += Integer.valueOf(vc.getAttributeAsString(VCFConstants.DEPTH_KEY)); - if (rsID == null && vc.hasID()) - rsID = vc.getID(); + + // TODO -- REVERT CHANGE + if (rsIDs.isEmpty() && vc.hasID()) rsIDs.add(vc.getID()); + if (mergeInfoWithMaxAC && vc.hasAttribute(VCFConstants.ALLELE_COUNT_KEY)) { String rawAlleleCounts = vc.getAttributeAsString(VCFConstants.ALLELE_COUNT_KEY); // lets see if the string contains a , separator @@ -627,17 +612,16 @@ public class VariantContextUtils { if ( filteredRecordMergeType == FilteredRecordMergeType.KEEP_IF_ANY_UNFILTERED && nFiltered != VCs.size() ) filters.clear(); - // we care about where the call came from - if ( annotateOrigin ) { + if ( annotateOrigin ) { // we care about where the call came from String setValue; - if ( nFiltered == 0 && nVariant == priorityListOfVCs.size() ) // nothing was unfiltered + if ( nFiltered == 0 && variantSources.size() == priorityListOfVCs.size() ) // nothing was unfiltered setValue = "Intersection"; else if ( nFiltered == VCs.size() ) // everything was filtered out setValue = "FilteredInAll"; - else if ( nVariant == 0 ) // everyone was reference + else if ( variantSources.isEmpty() ) // everyone was reference setValue = "ReferenceInAll"; - else { // we are filtered in some subset - List s = new ArrayList(); + else { + LinkedHashSet s = new LinkedHashSet(); for ( VariantContext vc : VCs ) if ( vc.isVariant() ) s.add( vc.isFiltered() ? "filterIn" + vc.getSource() : vc.getSource() ); @@ -652,8 +636,10 @@ public class VariantContextUtils { if ( depth > 0 ) attributes.put(VCFConstants.DEPTH_KEY, String.valueOf(depth)); - if ( rsID != null ) - attributes.put(VariantContext.ID_KEY, rsID); + + if ( ! rsIDs.isEmpty() ) { + attributes.put(VariantContext.ID_KEY, Utils.join(",", rsIDs)); + } VariantContext merged = new VariantContext(name, loc.getContig(), loc.getStart(), loc.getStop(), alleles, genotypes, negLog10PError, filters, (mergeInfoWithMaxAC ? attributesWithMaxAC : attributes) ); // Trim the padded bases of all alleles if necessary diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java index 3267173a7..c6cf8454c 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java @@ -96,7 +96,7 @@ public class CombineVariantsIntegrationTest extends WalkerTest { @Test public void uniqueSNPs() { combine2("pilot2.snps.vcf4.genotypes.vcf", "yri.trio.gatk_glftrio.intersection.annotated.filtered.chr1.vcf", "", "89f55abea8f59e39d1effb908440548c"); } - @Test public void omniHM3Union() { combineSites(" -filteredRecordsMergeType KEEP_IF_ANY_UNFILTERED", "4836086891f6cbdd40eebef3076d215a"); } + @Test public void omniHM3Union() { combineSites(" -filteredRecordsMergeType KEEP_IF_ANY_UNFILTERED", "e6a053129c5f7b13129beefed9282155"); } @Test public void omniHM3Intersect() { combineSites(" -filteredRecordsMergeType KEEP_IF_ALL_UNFILTERED", "6a34b5d743efda8b2f3b639f3a2f5de8"); } @Test public void threeWayWithRefs() { @@ -131,4 +131,13 @@ public class CombineVariantsIntegrationTest extends WalkerTest { @Test public void complexTestMinimal() { combineComplexSites(" -minimalVCF", "df96cb3beb2dbb5e02f80abec7d3571e"); } @Test public void complexTestSitesOnly() { combineComplexSites(" -sites_only", "f704caeaaaed6711943014b847fe381a"); } @Test public void complexTestSitesOnlyMinimal() { combineComplexSites(" -sites_only -minimalVCF", "f704caeaaaed6711943014b847fe381a"); } + + @Test + public void combineDBSNPDuplicateSites() { + WalkerTestSpec spec = new WalkerTestSpec( + "-T CombineVariants -NO_HEADER -L 1:902000-903000 -o %s -R " + b37KGReference + " -V:v1 " + b37dbSNP132, + 1, + Arrays.asList("")); + executeTest("combineDBSNPDuplicateSites:", spec); + } } \ No newline at end of file From 34f435565ca294be93792e6998243116bf14f755 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Wed, 21 Sep 2011 10:16:17 -0400 Subject: [PATCH 115/196] Accidentally committed unclean tribble jar to repo --- settings/repository/org.broad/tribble-25.jar | Bin 305986 -> 299110 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/settings/repository/org.broad/tribble-25.jar b/settings/repository/org.broad/tribble-25.jar index 6d467ba3fdd6e6285e8244a87aefa4a0d232cfe1..e260764a5b7e4c676c5b260010ccad0dd46becfe 100644 GIT binary patch delta 5549 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 From 7d11f93b8270913e471ad92cc969c4c6257f9843 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Wed, 21 Sep 2011 10:58:32 -0400 Subject: [PATCH 116/196] Final bugfix for CombineVariants -- Now handles multiple records at a site, so that you don't see records like set=dbsnp-dbsnp-dbsnp when combining something with dbsnp -- Proper handling of ids. If you are merging files with multiple ids for the same record, the ids are merged into a comma separated list --- .../sting/utils/variantcontext/VariantContextUtils.java | 3 +-- .../variantutils/CombineVariantsIntegrationTest.java | 6 +++--- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java index 147538253..e0e27b4f7 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java @@ -550,8 +550,7 @@ public class VariantContextUtils { if (vc.hasAttribute(VCFConstants.DEPTH_KEY)) depth += Integer.valueOf(vc.getAttributeAsString(VCFConstants.DEPTH_KEY)); - // TODO -- REVERT CHANGE - if (rsIDs.isEmpty() && vc.hasID()) rsIDs.add(vc.getID()); + if ( vc.hasID() && ! vc.getID().equals(VCFConstants.EMPTY_ID_FIELD) ) rsIDs.add(vc.getID()); if (mergeInfoWithMaxAC && vc.hasAttribute(VCFConstants.ALLELE_COUNT_KEY)) { String rawAlleleCounts = vc.getAttributeAsString(VCFConstants.ALLELE_COUNT_KEY); diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java index c6cf8454c..74ff850a4 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java @@ -90,14 +90,14 @@ public class CombineVariantsIntegrationTest extends WalkerTest { @Test public void combineTrioCalls() { combine2("CEU.trio.2010_03.genotypes.vcf.gz", "YRI.trio.2010_03.genotypes.vcf.gz", "", "1d5a021387a8a86554db45a29f66140f"); } // official project VCF files in tabix format @Test public void combineTrioCallsMin() { combine2("CEU.trio.2010_03.genotypes.vcf.gz", "YRI.trio.2010_03.genotypes.vcf.gz", " -minimalVCF", "20163d60f18a46496f6da744ab5cc0f9"); } // official project VCF files in tabix format - @Test public void combine2Indels() { combine2("CEU.dindel.vcf4.trio.2010_06.indel.genotypes.vcf", "CEU.dindel.vcf4.low_coverage.2010_06.indel.genotypes.vcf", "", "f1cf095c2fe9641b7ca1f8ee2c46fd4a"); } + @Test public void combine2Indels() { combine2("CEU.dindel.vcf4.trio.2010_06.indel.genotypes.vcf", "CEU.dindel.vcf4.low_coverage.2010_06.indel.genotypes.vcf", "", "d76cd5b3ced7745d42fe0af39ce0b32e"); } @Test public void combineSNPsAndIndels() { combine2("CEU.trio.2010_03.genotypes.vcf.gz", "CEU.dindel.vcf4.low_coverage.2010_06.indel.genotypes.vcf", "", "e144b6283765494bfe8189ac59965083"); } @Test public void uniqueSNPs() { combine2("pilot2.snps.vcf4.genotypes.vcf", "yri.trio.gatk_glftrio.intersection.annotated.filtered.chr1.vcf", "", "89f55abea8f59e39d1effb908440548c"); } - @Test public void omniHM3Union() { combineSites(" -filteredRecordsMergeType KEEP_IF_ANY_UNFILTERED", "e6a053129c5f7b13129beefed9282155"); } - @Test public void omniHM3Intersect() { combineSites(" -filteredRecordsMergeType KEEP_IF_ALL_UNFILTERED", "6a34b5d743efda8b2f3b639f3a2f5de8"); } + @Test public void omniHM3Union() { combineSites(" -filteredRecordsMergeType KEEP_IF_ANY_UNFILTERED", "c6adeda751cb2a08690dd9202356629f"); } + @Test public void omniHM3Intersect() { combineSites(" -filteredRecordsMergeType KEEP_IF_ALL_UNFILTERED", "3a08fd5ee18993dfc8882156ccf5d2e9"); } @Test public void threeWayWithRefs() { WalkerTestSpec spec = new WalkerTestSpec( From ecc7f34774df37969a7d333d298bc803963c6159 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Wed, 21 Sep 2011 11:09:54 -0400 Subject: [PATCH 117/196] Putative fix for BAQ problem. --- .../java/src/org/broadinstitute/sting/utils/baq/BAQ.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/baq/BAQ.java b/public/java/src/org/broadinstitute/sting/utils/baq/BAQ.java index ef7cf751e..1723557c4 100644 --- a/public/java/src/org/broadinstitute/sting/utils/baq/BAQ.java +++ b/public/java/src/org/broadinstitute/sting/utils/baq/BAQ.java @@ -5,6 +5,7 @@ import net.sf.picard.reference.ReferenceSequence; import net.sf.samtools.CigarElement; import net.sf.samtools.CigarOperator; import net.sf.samtools.SAMRecord; +import net.sf.samtools.SAMUtils; import org.broadinstitute.sting.utils.collections.Pair; import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; import org.broadinstitute.sting.utils.exceptions.UserException; @@ -131,19 +132,18 @@ public class BAQ { private final static double EM = 0.33333333333; private final static double EI = 0.25; - private double[][][] EPSILONS = new double[256][256][64]; + private double[][][] EPSILONS = new double[256][256][SAMUtils.MAX_PHRED_SCORE+1]; private void initializeCachedData() { for ( int i = 0; i < 256; i++ ) for ( int j = 0; j < 256; j++ ) - for ( int q = 0; q < 64; q++ ) { - double qual = qual2prob[q < minBaseQual ? minBaseQual : q]; + for ( int q = 0; q <= SAMUtils.MAX_PHRED_SCORE; q++ ) { EPSILONS[i][j][q] = 1.0; } for ( char b1 : "ACGTacgt".toCharArray() ) { for ( char b2 : "ACGTacgt".toCharArray() ) { - for ( int q = 0; q < 64; q++ ) { + for ( int q = 0; q <= SAMUtils.MAX_PHRED_SCORE; q++ ) { double qual = qual2prob[q < minBaseQual ? minBaseQual : q]; double e = Character.toLowerCase(b1) == Character.toLowerCase(b2) ? 1 - qual : qual * EM; EPSILONS[(byte)b1][(byte)b2][q] = e; From 174859fc68c434ee568884ea9d9e53541070b24a Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Wed, 21 Sep 2011 11:14:54 -0400 Subject: [PATCH 118/196] Don't allow whitespace in the INFO field --- .../sting/utils/codecs/vcf/AbstractVCFCodec.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java index 624d06a71..18646b057 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java @@ -215,7 +215,7 @@ public abstract class AbstractVCFCodec implements FeatureCodec, NameAwareCodec, int nParts = ParsingUtils.split(line, parts, VCFConstants.FIELD_SEPARATOR_CHAR, true); // if we have don't have a header, or we have a header with no genotyping data check that we have eight columns. Otherwise check that we have nine (normal colummns + genotyping data) - if (( (header == null || (header != null && !header.hasGenotypingData())) && nParts != NUM_STANDARD_FIELDS) || + if (( (header == null || !header.hasGenotypingData()) && nParts != NUM_STANDARD_FIELDS) || (header != null && header.hasGenotypingData() && nParts != (NUM_STANDARD_FIELDS + 1)) ) throw new UserException.MalformedVCF("there aren't enough columns for line " + line + " (we expected " + (header == null ? NUM_STANDARD_FIELDS : NUM_STANDARD_FIELDS + 1) + " tokens, and saw " + nParts + " )", lineNo); @@ -345,6 +345,9 @@ public abstract class AbstractVCFCodec implements FeatureCodec, NameAwareCodec, generateException("The VCF specification requires a valid info field"); if ( !infoField.equals(VCFConstants.EMPTY_INFO_FIELD) ) { + if ( infoField.indexOf("\t") != -1 || infoField.indexOf(" ") != -1 ) + generateException("The VCF specification does not allow for whitespace in the INFO field"); + int infoValueSplitSize = ParsingUtils.split(infoField, infoValueArray, VCFConstants.INFO_FIELD_SEPARATOR_CHAR); for (int i = 0; i < infoValueSplitSize; i++) { String key; From 6592972f82bab1bc0903460241b7b1656902e4cd Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Wed, 21 Sep 2011 11:25:08 -0400 Subject: [PATCH 119/196] Putative fix for BAQ array out of bounds -- Old code required qual to be <64, which isn't strictly necessary. Now uses the Picard SAMUtils.MAX_PHRED_SCORE constant -- Unittest to enforce this behavior --- .../src/org/broadinstitute/sting/utils/baq/BAQ.java | 2 +- .../broadinstitute/sting/utils/baq/BAQUnitTest.java | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/baq/BAQ.java b/public/java/src/org/broadinstitute/sting/utils/baq/BAQ.java index 1723557c4..4f096f86e 100644 --- a/public/java/src/org/broadinstitute/sting/utils/baq/BAQ.java +++ b/public/java/src/org/broadinstitute/sting/utils/baq/BAQ.java @@ -152,7 +152,7 @@ public class BAQ { } } - private double calcEpsilon( byte ref, byte read, byte qualB ) { + protected double calcEpsilon( byte ref, byte read, byte qualB ) { return EPSILONS[ref][read][qualB]; } diff --git a/public/java/test/org/broadinstitute/sting/utils/baq/BAQUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/baq/BAQUnitTest.java index 2e4dac6da..67943ccb4 100644 --- a/public/java/test/org/broadinstitute/sting/utils/baq/BAQUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/utils/baq/BAQUnitTest.java @@ -172,6 +172,17 @@ public class BAQUnitTest extends BaseTest { } } + @Test(enabled = true) + public void testBAQQualRange() { + BAQ baq = new BAQ(1e-3, 0.1, 7, (byte)4, false); // matches current samtools parameters + final byte ref = (byte)'A'; + final byte alt = (byte)'A'; + + for ( int i = 0; i <= SAMUtils.MAX_PHRED_SCORE; i++ ) + Assert.assertTrue(baq.calcEpsilon( ref, alt, (byte)i) >= 0.0, "Failed to get baq epsilon range"); + } + + public void testBAQ(BAQTest test, boolean lookupWithFasta) { BAQ baqHMM = new BAQ(1e-3, 0.1, 7, (byte)4, false); // matches current samtools parameters From 2585fc3d6cc4dd873eed2868fef8335d7e40a504 Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Wed, 21 Sep 2011 15:22:26 -0400 Subject: [PATCH 120/196] Updating Rscript path doc text for Broad users --- .../sting/analyzecovariates/AnalyzeCovariates.java | 2 +- .../gatk/walkers/variantrecalibration/VariantRecalibrator.java | 2 +- .../src/org/broadinstitute/sting/utils/R/RScriptExecutor.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/analyzecovariates/AnalyzeCovariates.java b/public/java/src/org/broadinstitute/sting/analyzecovariates/AnalyzeCovariates.java index 7ea515591..1ef452a5c 100755 --- a/public/java/src/org/broadinstitute/sting/analyzecovariates/AnalyzeCovariates.java +++ b/public/java/src/org/broadinstitute/sting/analyzecovariates/AnalyzeCovariates.java @@ -114,7 +114,7 @@ public class AnalyzeCovariates extends CommandLineProgram { private String RECAL_FILE = "output.recal_data.csv"; @Argument(fullName = "output_dir", shortName = "outputDir", doc = "The directory in which to output all the plots and intermediate data files", required = false) private String OUTPUT_DIR = "analyzeCovariates/"; - @Argument(fullName = "path_to_Rscript", shortName = "Rscript", doc = "The path to your implementation of Rscript. For Broad users this is maybe /broad/tools/apps/R-2.6.0/bin/Rscript", required = false) + @Argument(fullName = "path_to_Rscript", shortName = "Rscript", doc = "The path to your implementation of Rscript. For Broad users this is maybe /broad/software/free/Linux/redhat_5_x86_64/pkgs/r_2.12.0/bin/Rscript", required = false) private String PATH_TO_RSCRIPT = "Rscript"; @Argument(fullName = "path_to_resources", shortName = "resources", doc = "Path to resources folder holding the Sting R scripts.", required = false) private String PATH_TO_RESOURCES = "public/R/"; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibrator.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibrator.java index 529d17285..89e702b64 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibrator.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibrator.java @@ -155,7 +155,7 @@ public class VariantRecalibrator extends RodWalker Date: Wed, 21 Sep 2011 15:25:01 -0400 Subject: [PATCH 121/196] Marginally cleaner isVCFStream() function -- cleanup trying to debug minor bug. Failed to fix the bug, but the code is nicer now --- .../sting/utils/codecs/vcf/AbstractVCFCodec.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java index 18646b057..e8d1dc6d6 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java @@ -601,12 +601,15 @@ public abstract class AbstractVCFCodec implements FeatureCodec, NameAwareCodec, private final static boolean isVCFStream(final InputStream stream, final String MAGIC_HEADER_LINE) { try { byte[] buff = new byte[MAGIC_HEADER_LINE.length()]; - stream.read(buff, 0, MAGIC_HEADER_LINE.length()); - String firstLine = new String(buff); - stream.close(); - return firstLine.startsWith(MAGIC_HEADER_LINE); + int nread = stream.read(buff, 0, MAGIC_HEADER_LINE.length()); + boolean eq = Arrays.equals(buff, MAGIC_HEADER_LINE.getBytes()); + return eq; +// String firstLine = new String(buff); +// return firstLine.startsWith(MAGIC_HEADER_LINE); } catch ( IOException e ) { return false; + } finally { + try { stream.close(); } catch ( IOException e ) {} } } } From 6bcfce225f42e31a03d721b119410da7c9063f5d Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Wed, 21 Sep 2011 15:39:19 -0400 Subject: [PATCH 122/196] Fix for dynamic type determination for bgzip files -- GZipInputStream handles bgzip files under linux, but not mac -- Added BlockCompressedInputStream test as well, which works properly on bgzip files --- .../sting/utils/codecs/vcf/AbstractVCFCodec.java | 6 +++++- .../sting/gatk/refdata/tracks/FeatureManagerUnitTest.java | 2 ++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java index e8d1dc6d6..83c7083d0 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java @@ -6,6 +6,7 @@ import org.broad.tribble.FeatureCodec; import org.broad.tribble.NameAwareCodec; import org.broad.tribble.TribbleException; import org.broad.tribble.readers.LineReader; +import org.broad.tribble.util.BlockCompressedInputStream; import org.broad.tribble.util.ParsingUtils; import org.broadinstitute.sting.gatk.refdata.SelfScopingFeatureCodec; import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; @@ -590,7 +591,8 @@ public abstract class AbstractVCFCodec implements FeatureCodec, NameAwareCodec, public final static boolean canDecodeFile(final File potentialInput, final String MAGIC_HEADER_LINE) { try { return isVCFStream(new FileInputStream(potentialInput), MAGIC_HEADER_LINE) || - isVCFStream(new GZIPInputStream(new FileInputStream(potentialInput)), MAGIC_HEADER_LINE); + isVCFStream(new GZIPInputStream(new FileInputStream(potentialInput)), MAGIC_HEADER_LINE) || + isVCFStream(new BlockCompressedInputStream(new FileInputStream(potentialInput)), MAGIC_HEADER_LINE); } catch ( FileNotFoundException e ) { return false; } catch ( IOException e ) { @@ -608,6 +610,8 @@ public abstract class AbstractVCFCodec implements FeatureCodec, NameAwareCodec, // return firstLine.startsWith(MAGIC_HEADER_LINE); } catch ( IOException e ) { return false; + } catch ( RuntimeException e ) { + return false; } finally { try { stream.close(); } catch ( IOException e ) {} } diff --git a/public/java/test/org/broadinstitute/sting/gatk/refdata/tracks/FeatureManagerUnitTest.java b/public/java/test/org/broadinstitute/sting/gatk/refdata/tracks/FeatureManagerUnitTest.java index bae8e99ed..e8799e2ab 100644 --- a/public/java/test/org/broadinstitute/sting/gatk/refdata/tracks/FeatureManagerUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/refdata/tracks/FeatureManagerUnitTest.java @@ -56,6 +56,7 @@ public class FeatureManagerUnitTest extends BaseTest { private static final File VCF3_FILE = new File(validationDataLocation + "vcfexample3.vcf"); private static final File VCF4_FILE = new File(testDir + "HiSeq.10000.vcf"); private static final File VCF4_FILE_GZ = new File(testDir + "HiSeq.10000.vcf.gz"); + private static final File VCF4_FILE_BGZIP = new File(testDir + "HiSeq.10000.bgzip.vcf.gz"); private FeatureManager manager; private GenomeLocParser genomeLocParser; @@ -109,6 +110,7 @@ public class FeatureManagerUnitTest extends BaseTest { new FMTest(VariantContext.class, VCF3Codec.class, "VCF3", VCF3_FILE); new FMTest(VariantContext.class, VCFCodec.class, "VCF", VCF4_FILE); new FMTest(VariantContext.class, VCFCodec.class, "VCF", VCF4_FILE_GZ); + new FMTest(VariantContext.class, VCFCodec.class, "VCF", VCF4_FILE_BGZIP); new FMTest(TableFeature.class, BedTableCodec.class, "bedtable", null); return FMTest.getTests(FMTest.class); } From c6ba94471985e3956b2108063c9f65d350d170cb Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Wed, 21 Sep 2011 15:39:45 -0400 Subject: [PATCH 123/196] Adding bgzip vcf file for unit tests --- public/testdata/HiSeq.10000.bgzip.vcf.gz | Bin 0 -> 509234 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 public/testdata/HiSeq.10000.bgzip.vcf.gz diff --git a/public/testdata/HiSeq.10000.bgzip.vcf.gz b/public/testdata/HiSeq.10000.bgzip.vcf.gz new file mode 100644 index 0000000000000000000000000000000000000000..3f2b9bf14a3e046fe2b7dd71ffdbf23e6c521bd9 GIT binary patch literal 509234 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

=T8%=q#g)u1|{e#n`5@9uKDV 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/CMx0M2+F7BPV85k)|aM9j9Ih6+^dVP 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 z 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%0DyVm3sY`Enwj8yX2< zZgaEJ9jCfvyU^caRe}g;Mg6KRAb(sKq9+0&gK~Ky)yGb+DAFg36|J?6A@>o)gUA>? z5!;>!E2oK!Pg)F>s2x^1Oh%~l=rD;L6Zok^Nx{pI8=B1Nt7S#6!N-kFK28~!mv zujC0q0Z|0QR)@6J*^b;LO1wr8c&uF`;C^4<2a%d+jeydQ1Is9spHS<91FemK!i@k7 z64rAkARUJI*oP;*=ayfJV^VN)E@M$kml;-wTDc)Fh*%WhTH&y0r_3+ZHZA9oNMEd+ z2M=301|$$YeS>OaJ}jx>utbKgfL?Yz*b|_-d?8V#HakP1I#og>Ae~hkDzM1n-^AzfRY>~Y-#JCb}Rzot2fIB|Ft^$5KkizO? z!G>6k@>nx7Aikb-arF4Z=(GS)+JUr?m{_ms(rKZ%N`8%#Drql29!M}e?I(5!)h%H= zz|Q%;tHJmswZ+71%~Kanw?g#qloC2I$x)= zypX3Ioo~eA8>0wbu@=UL>=n5xvt|p118wSRUXSMyQ|w7@t_m5q8P)k(`72?s6N2|x zqoPPlQ^Q={WvjVyd7yo0M|$)WLt^0um6#!lkG7_5u6~Ps5thUS%6(L^g4aT69mrpi zt1OeNq6h(jb%^(|c;|`WGf-BwE?b_S`}_er1$MFz`=Y}jvOA_C5raNii$HoFF6Gy> zrWlLDshQrAao>1&mhoWw^oVEtoDE@ ztttyKFg)~vWlW>=rlXf%J8v>)#_Y*J4sxRtcfVyNDj6+eM01CLX}Qw`NYHp zo>uo=SsozXjZANsP3kJUS!!5WWL3U4{#{i}p#3fH8+sI4^>>`DU&lYZi&%82M|&5e zpMo0uVTf(AYh{;v>@(XWlLK(KBQ-49(qzrMxAn_`AVyl?#8#y1yNSnA-2~>li0D> zP`IT-;G5*J9odH{opTg#Ww>as8VAt-;IFa zyeyTd^Fgw_Kq!b;n&y=6K_3!(lth9}WKw;Y6n0=maovh4U@DsJXzvYdYunWLto~I! zqeUUSsnT(X=1MKt@br5UX%QECCkm)#jDDEi&&`l5N(88mZG`@B&h-Q;L(O;d=vE}SgREtuvvDZez&Lxtt=9T!bo&yu$OV3!qkHEx92$bZt^ax~-u?a|{oK z_4SR)3_=JNmeA~gbZvsh*JMYkNn>7it}>G%v9|JaJNB2#w!xq|b^|@DbkM zUL%AvQ8a_R_JnOS_INZqq;2EHr24N8 z`kjLvC34(|^xl2a#0iw@jf~LQSv|#sesoqz?EtS>%`jKduYjftVdnthcNTUI95g0s zlrM!@>oFFSk&GjS4R)+ht%=fmKxmQL=_{Ysd9+?&YGVYcUc3!U`4WW=QGylrqT@Zn zaq?8v4x3?~OSIqlY}+N;hhUc|i!eM&1FPPm>!TZ>0gQMfFe?F*isguNm`K>= z5_hUlwG{=&I2UBYlQMX8a1?sL>y_rE@2pbX6qqEPpaC)3VGRp8Q;b$s?QfyAy#-D; zss@cJh$7S|e}qk*RUNA~$ULj(T-853(=5>4X7Pur#V9vlr(g@KyGZ>%i`-(^E1TWu zB=??WnRfG(Vp?{Wb@ePk*?gr(6&T8LOFZ)W=BAPtr!^`tgtwuIAzI)}aU4RqX~=5m zvH`(STN`qJdL*T#M+Dz4wuqqsQdw>Xn4D?KqPK%mL!1n$DfD31Dz)tHo)_2lASxW4 zr7+~%g{LOq!eUZfRaLRUN`){{SkrB@;B!y4+u(yxV7LsTSPHWBn^Ys;Nx`#R zuX)o=1@|s!flVh=Akw&&QotU~`j(ySM~jV`Ev{?a@j;eEmI4$@1l@+f^@*=-i(8#% zd_Yqck;ArqdtQ^jXfIZq(@P<2iv{#7n_^@GJx?i9l$`HZFrC-__6?<5nI$I0{9JBRWk4)}viBj_nI&PFXu<8fep{$TbOMHz-#Yzt)mUOY33x}eceNy1|e%tJ0%-paE!LcRuhMZv`*3TcDo2ZC4 z1p*gAC9QJ7f-Ol+P*#UcJ-OF)Od(I)*U!ej(1dph#%VzgEa5GDVu^rAWIAToMcd1d z*n@REPXA)iSA@Fheq=@U$hnuEIPj)QZlcj4pbpxaXwYz+EFtd|`uN5Z#C40=+C5gC zn3Ngf)UA~n4+MO%t4m5fl?6bn?Px2P2mBjrVm;YfYjl}~x)Is)<`CY4e`kejY~NEM z)6~$GH}yg|Q`UuYDHm;M*Gst(a)F~E$#P{~3~9o8aT^V92n1q_^5R`&8LXAKt_?tE z(n6S^^_vnK0*IV-gq9AZ&I(C^{#f9X8UmK(@D4caLfuCP0X-u#L5x!?XsJvml4I@F(4v~GVUn}CS=7B8PkoRViiahxZK4IS zYP~Z@s=5&00zk7}dn@N@w*%T{IdwW{RfWB<^|<-Ym`5+1}>KmEb`*E>vf9ZRjLk_2t_(JCo86w<|D7RceX4?9RajtkYfJ zxW+&(f|gNN?)l`ubN>b#*D3IVB) zKVco)_`qW@u{Se|bO~&!+W>y1>RoP$dxRq3J9n4cVqm`7=T%sjKeDb+9RzLMwD6Sd zu*a}X)J-~ei`dWIjs-XjElnTA4#6aLjkfNzD2Ma7TwCtAYm;45(KHe{^7JHf4KkJf z?FV`;)gFtze*N=*{dhI~dIIYwqFx{Ehyts0`ZeoVie+Z}x)d$bm{yczbsE#oT1fQS zJVlobJ!2@^|G=vS{N~rUC-1sgjxBgLIB~Omxq}MYbJj!#5SCr%7uLf7rbu#yCvZOQ z>x?|WENsdP=EJYP``zDu`NPkD{L_z^zWv*udGhn0E+(f>eWufIpM+DvHcSiSRfxf> z=%>Rt@kL#oY~aL|{b^)UccAbdd1`tv18($Hc_B~gpR1d{xHD_H(%8kuU8CAMUI2U* zp{v|{%z4Poha z5f)eprg9fw_y~|kBM|j!^I=AHzL}v3Dn!+HvsrfnbM4!(#;I7uDc{sFTes4Lw2&n} zD8n91TU{O+o0{)*9KJH{L!U1abdoLc;S2aiD?=K7oz^k;88fg0)ed_37ow|?iC=~n ztjXv&+Lb{B5e_qgOaTd5M|gMv{n^d6+FQ|uOCFN(K`3y-ZedsmlnB27;N%pQog--V}Ek=M+s=n!N zMfWVNyR{a<>Hz7(g5!A5g%8;f9Kka3dEP^Vwjb7v_^ud>x!9?QmU3dv*ir%(maB`Z zn<=bY>S0ScMncNWuvZ0N9Wud!%ZuS93VRaytSqKoN50~D!p~aDMy=KAjYpYnlS=Fj z85-k$z~HHlsYD~$x+`{P;$1bBbxXd;N^(a;Gg;I!$;u1NKS{)GF~D`&!Bs3C{** zJEK>}GvTN@f!{rbiR{ra)|M+^C1YB*Up@G)dA_lEOz|jcp&xR9>RrjC^J=CC6lJ9q z7I_fh!Mo;Y4hjB}q=`h?O4x}gpRUqMB&Bg8M^p>_X(bb4FSIUT2b1M$p3m_B8N|6` z9mSySWQR?CP_%UF+D>}-#+s0LYz?;Kcx4HVx`U^o+T2i&z1rIn^krvuO;^Ve_*$hv zITHp>S!^8C^SXIn;6mx8PMY{W zErV(BD?ukZ46p05I0x^GPewb6snSDop_XL0#EgygyW_Y+8181&$&9{#7jomep(46P ziBruGB0BHiO;@@*#TtGb4l^3nGh-M@`^M>>JKYO(TN=r5+;8L@aFICOL7q6R7I$`` zYnG*QRkl)hS*kGEfdK~Yd zUdCv|`0?acQ;*;}$cJ=(w?${~X)OZvS$m@d+w90qMI=BqV7)EN_NgU6?b_cqsEDJJ zDn7c-S=BuuA%}5k=RX)4+Qj<;HOo*)RR9~VoUK4Z&|pw~RSK2YU431YbRM9g9ak$pguX%nwb4J!b4L1|X=AxcApZ?JY;&7q z?{f^nTyP=XOH?snE}5$su+MbC=nRjKuGKWo3WSlL3Ux2irG4aPxn(E<$2L_dbH=8j zC+yZ(7yhm2FlX^aJjj57z&A)$bp6qiumnVxIPmJWr(b;M8T+Oyu5%2ksjJ%`ua*Xf z7Y~8%%v~yLFnJYmP&s$o*zqU`Tn*J{>Cail*3zFt zu`jzyg2?1|=U4 zM@hGG*)08Hf0ML~PrywR>BvzyE1+|2nQn3m@vGxP@q#NBh?Ck9ChhWMM&xn4WKHmR z)LR|+yW9A5<0+P1(~6U(U%n56D#n$~&8V@btYZCeNI_oCWwOG?5^9hW@Q|3<5Seb#7W)MH zNEpw@*iLL6Uk|?cGqxYS>KZwE>3&L7*d{1P$RTw@lBE9;BdWWxop-H19T{0AXH+l6 z5G7=RpnmbP3#MIo=Nsjz3`#|7uoepHlO-~T&Y-kPre*b&nFrB%(@DY<;R|Autoh_DFmc!Xn$QWM- zou{mJbRlNrMKKC2&Hp}`CEt}lJj=rIwMuTmvz}E3!88h^%HVV!Ue_WrVmC=?|83_?NXe%rJmmSt1-jMoIl>E>sUNk#}6tKRYprPtJU&W6jtK=9&DSu zb60n^6NNT$ntwW)of*pt7UfVLye5Aa7kf0PdGg5 zIo_d&=RUtSJd2R3YHydAd9)vx=*;kjrl?_;wv z7Ao^gMM29;*S$bmlx!*sz&*5rsxoWXh%8%-GiGo%^rl;boa$KfyxoI=f6|cMc;NDVJv&x(=1{|Nwv}0@cTs%or;&6WGcwE_A@i2%W=G~@Pm7RqTNmo_)ENt6!@(66 zl=av^<~-sRnq!^qC@j{6Rw*4$X1W=sSL`NNI&b%QmMe`C2f(kjH6oJkzga2+>yWS$ zsY(lWv*9iZJ1nrC#|lElNkExBmK2N?+06h5xf`v0m&)-x=H(k@8Btpbun`%@dWP!JJC08GTK1*1ApHe3ndeByV;OkPTDHGykbM(e@DwSPwgQHNV_IZoMxg5LmsIYNm zq{yNYDU4WF7nuwxoH3Q+6RT%@|DoFLG{vnI47n@w4Ow`>s9hgEKXx2njO6eN1~g33 zy)Rw43+tKeXoUj{5U{qOl^I3Vj%*I^PW&!+>OLfX(X#NoASzunh9%PFa+icw*^&%u zYvFTN-Gpw2NU4dkfh@a$?1Qms# z5j)(s`&QRO}(KO&dp_Nlh06g>(w!sB%zHLYE6E7z{#zE{&!{Cjw>8hTy%bNO?Q1 z!&twQJ9S*s3I0PmSkY%{htgD}ApEj^@rhqN>u48lmMIfEyGieZEQ!&!)}`^!Gf(A3P4Ib7e*laKE6yTv3cnE}_54|PUoteHLFa+;Bfb1wpH<&RSyeT5{ z43YS0$+A1qS`|7-kfe=Xd zzQ$=HWfp`@o#a6x#opJ5DuY}v3svpuR8nLqLWPP#p^dI=pUMrGasd@9@{y)RXo?C0 zTuF2~k4N8Pd-TUZy_x25$&zFb&+(1#oKM#))f#o^(7Pbhtk^}fd-5kst_i4f8d5uW=Q#wW@LZ+ovT`?S$-(#o zZl!5R-J5Kx0!M!Rny%8lToMmueQ&)sA8o9N8S$ zGo3^?M7AaR97X`VzFPbGqCEoRxUj2`hmS3qXqD8*75TA66DQHcR1vk!JY$7%j_HjQ zTe}OcNtt43+P}90wJ;<#UhTB2)>Yr4Nrs8fABm5wkHahP1Ba0&t_GI#O5v^NgoL4)3uCPit=qvHl z9R8P%uXphHFutBsYtvTf0tsU&UbNXTzMf_1>Told%Pn+CvvZAfODDl=PtV%INWuD73YUtz3-VTjDR2^dQN(Bg}!S>`= z>tytN4Sqhzyt^=2YHQmE@5A_W@#@@k@r(>H;UzgD!VhI;{x{EK? zvmMOu7BfI-m<1Hcj_idGlY&o5w!-+(u0(o=G}g80>$G(q%YAAa;*%ywF@B*ybF2_W zIgUP^S+1TZ+E3?^eeQN>SrMUi(@DQIRC#EOj=rkIu2PXvL1FS62&`8s6;&G0rHV?$ zssT-QR3`+JL`kja#CeC8xp^P^TEkU-PC%zLQ3R*;AM7BydLH4;^A-xPf5)ND1*%cO1{ttY_&VHN?KxVlslA7S5z@`G_|j z$&l8LEA9fS)J^rZ?T#u}qh+ANMULvxGZR2o+{Jk>D%laMu=u`zCm-Mj84Ke|!o~^{ zZzjdY%vTk&kQbs^T7S2ORkKh{OLj5GJ|dHXK${0I+&D*?0hL*@wO`+QHGz!)SLoF?5p~mXF@>XO8E0M@J<_{uOyqa z=(F=WUf?kICb1BLV^{m;!&?M4$eMLzjx;UhQPV_gFC;w9#=*dktRa z(L_%~*stNDJ4Klj#CWrGLW=O&d0ql#hAD;wSADm{@d^zz=j@Jlc_$e5<=T_1TtxTQ z>1>xSc}UiC*Cxr9Xy*W zZ~}oEfRpM4EfyXYG&A$ZMmi$V`Aa@#6{uliMR%YwGSO!9jj*8VQjM`b1a|N^I(XzZmBcRod?v6O1ng^ zC*nV*@?c142a;?Xl-M&Mn?Mm=<-=LR zz0O=F%Nz~eLCEU{X1(g?BiT2Yus9cCYPcq^Z)GN1xYaz@yRsm;m&7ewLI7D=`Yc6n zp!UbZV22SHX_m8y#z}qJ#jsshcB|MB`;xD5%K-s)nHRK77njq(F1f25#LAX~#Oa_; zt)zJ`mS+);{yIcY-%k4#znrwcTq}c-S6w{sO3TqBWOS}Hl#SGWP|ZGyCiCb4nGv{# z#i?x-Uxf-}-BhRVwFg&gKrEiL;>2DTO@!rlG6|IO$y1Y)5qEgRVqQK?zLI z_TAz3JJCZ|uBA3bVfqw>&J>j zrTM1c1hU(?2gQ8R{9+vs(X!BCpP|K?oVmC_AD&bT!&#mi+kNJN zGzCE7cucDV3^UFKWUFI0A&fcR;upW86d|kAu#|Dgi$L<$NloA4frI+)`qfq@zBq{o zNt7bMm&TURhg*}ooXxvxgg0(|Y?B4S%|pws4PU!<4wsu_^!`(d2Wh2pq->vgnA}ez?pQ4%e zr}2tu!A;6GMge+xBGJASPF8{4eHT-K*zLmuCwQzHZFu~{Qlh8n-3?io)RrsxLyeoW zJNa}7ZY|$8T_%BhPKKA8v_8t%<}H)!2*cOsCX#2zG3+n;U}Yj89W6tF7TjUiwopr2w zy)Jin9o0Y` zoIyW~XCm2v6G@hkSHn;SaDag%=z<{sK3pWLcGcOtPVKLIjKq?qnd!|_MHacZxcJ$o z@b1#vXJ_6+efw-(c{lM<>wo#!1^B&E2-~Z1q?zAN3)`-T3mAAkD!Z}guV^W8uH+duyFh5qF~ z{_^vm|Nj5}^3(7C_~*a=ME_OE-~Er@eTntm;NKYfOMdgaFWI)(Ac|II#Jeslg2(&KZ>_Sp8m0KpL2T8 zX|y8;Q^^i%O>)_M`u@|eKUqu0rf!FW9Twj^*x~C9J5sYF*uxHMt9vg2hIsmQDjO1g zeh=Z<5KD%bH(wt`*pQ6Bw;K}agPh@O72iW)J6x`0+tszQ@y(JU?(sRr_eciR_eB3Y>7p4$$>s`JdNt;hiGP4y;Kyd(rY`c-`YV(81+((vC7gnR|`7|4va%p zdQYd*fEDE}KCGw1C=?9dKGWYwmeALgGMNy9{q#;w{rx9v%6l-&Pyg)~ce+OxvB{YB z``6>UT_G==6QaMSpa9AN=Lr3H&}pejkeE_aV~zkm`HR)5lY2 z?sv!V0n|)?&_|!S3s>e>0nY>7^lnDU4)(ze@@c(0^kSUjYBM7}0s^av^dgWQf&MOX zX?lIkWJfZY--nn>#hXcnka6_7zqZ8fA8a}huTMl6f|8}ns5;=3hdJbAL4t#Cc-D3xDpN55Zv9awKUM2%`ga>MIT`Eer~$RnE(^g85t z9ax=t(EIS2t{oz3WK;O1rT}66VMaazQYm~E;ld8OR=8P~{I*uPTA3}G-V7JRaT=9R!_pm2 zc5|`f4LfX0p~i$Z7|NVH%%FO~ilbM9Kk({W`8P*CZ7B07oQ+fNX!ur9t}=OuE7syT zN~!c;_FH8)$8j>_BJ)V)>NsMghm8G$gC`r3gNI9AA4hzIK*xcfBl0uoqUM5w8QvP0 z5o5MKc+1y{ew?y7jZ*@3QC?Kv+ z)8|T0DGZ@H1U^D3RT%3e7jksXJ-k+}o*u$Yvsc> zdoiu*J(nmg;b>LppH(?UOjJYq*Jhl(+^yd8C-*=C@$R_7@LUJ7dpNvu*Je;55h*!$ zlw20ieqQsb_;xuM+$(wloe6RrAy4o5^>L&d_ev7=lba{2nY#Bd6?PQk-_t4E)#FH< z>{@t8m25X%*-=ns$aQ*!<&aP8i#!fQ5^3`gX$xhYrTIkCM4ha#m$?Ylr#?JVLWWBZ zO=71gY(jeS?i^~;6qgEO&uJBdL*-BT<~P zAu(>?C~UwKgED0_rFav$WI~vw(EF<&|LyO8{f|HW@jriZ^p$`Av&F|QDyY-JoSZ`| z4z5~SDLErAne3?E_Bhp^jV%sxa*LA% z^m{chCk@3@C9g=5u2XI+uQ-L$vTzC?83w7S`PL|itCURPUYSr`rE2g8)PVHFd>97t z%QAe12vK#|teBKm9nJw7xh$Ouo<9WJ>^V2oSc{M`m9mw>6k`Y!WRYgBo%?(O7tS48 zq2bf;>Atq|HBf#hpH4rPyYvVf`O_)DCi=xK?oka4r-d|qdCjR`BPa2RTxeJK-2j^% zlD=~lZarNAvBTvC;iN)k-71Z$5?_(PJBhD_FVhG_c|%z**%1;MLMb^vcMhe?YkxS^ z7#G=Kua@FiEd_eojML{X=yS)_+`=OY<7qZf>1qT}3#V+*8)tGq4$sFCDH@ke_Hnjj ziA$Y}3`(%QK6hHbCIHWiA9mA>kd049pp4;cOLtvZ)Jy}S}aGs zR#md|7y^CQ+74VRWU^JT|8y#ryh#ev!j35EN8+63wR_y@Ve|my2At3)ikVV{1F4jI zmE1bg#`=h`gCZ1AbDJ3Jq`w4y(JT8AHa84go z^yMQ*)Uq#91$chH^NdBzjyHdVe@E(BvG#ifFr ze!`6%cS2TfE)xA-hYic|Shy_Dj(Uv&AWkmXVe+91nv9D#gs z2Y{wK-Qymy{7`gc^c|y`VI^SMNMYf!JKz~skH`(_!~`|W+xG zvxo+pn&S>mb>xOaW|9F71Z|NerwNurk#J!Mfj?(B3H^04BswC>0;d^>}R;@;vI?YP~w>Nrhx294<+iM@Nc)qtH_cZ1dI+`*rE=p zqane2q`e84uCtQ&R&GALcJ{p;9@&&)(u1=He$b?4lfAZV&=Fb1Eb|BeLFpX%BhU)89T!d~qKZWl#=}WjOK)CIMjF-X#c?ZQh z3ci$(xyj2Z8;RWloC7`QrfiXg8_!_(^0|-=^vD*c{Tc<;;TqStA^rGd13iVP`J&}K z(FJa1rZU-)ZD4%ohmXSwd(2HMLD&+;rgF!70;Sf9D_kQaF5*g|0-@9;+uqSH^tMi! zVsfUm23pRHlf=l6(zklX7eihve&7wb+Xb+kFAE;*V(Y)5I4%HxT@rX836WcZ78l}CB@;VzRq?Nl^+yu zofWhMJhd|#4Ljf*mQEt(T)8Vt_t0<1VCOE!OFIVO(4}0^G=q!d*A?8YF>%s>U8?`8x(4RX$1r)J+ebCRcj;eO&+n^U~CRWTnguXxOeT1zWD$Y7MiSaI=u zZVd!_xw#NDDLQ)2**WOkhE~l@%EFpMHFJ?79Y!&lu?Oh^I;s}^HJ`^c5R^(LH6us? zHG!aHhp+uK9NXb3gS5L_bw5yXn|g#2siLm@{xMZWu#yDh7Uz+rBTFw%)=Kc|R;7bN zlCl&Y^d5a2^I6+bC2j9?p3EqUkPH=E&jnS*8uNi2WIkvxlwqkIezGGwq*jz-S#+0$ zB}R@}Hsm~i$IV@DWsXrdfyN@_JfoBzq+hF-92~d895=jxg(DWwwKALGxgkr=9vLU0 z_b+YP(B}m4qSN79>uDrcC6r7?J5N<>r@*b#t9RT0UI5^eJ@*C#@8Ry%XoU|o^I;U9 zajTeah;({)7$-X}yu7q#pfh8|;k4pFUG6o}HDE4Zd$|pq=*GZ38zj15d{Qi&yu;^c zx_1a7N>4}JynttvEr(i|PE^qQCuvyKey++ zG$xF#by3ke4p=Cls_;RzfI`Q5gh)biso$(9Ed!)sAD(--Xw7yaWt@00RJ?k2bSlT4 zRXmg`odtmea}QbQZh+s8PL!R@2=y>Sus64LbGz^KTY-0;Z&jp>HMQ<$(%g#vvlXq? zRt8B>x2Fb@unD0IwAPSPJiNnkRgM&K$Id+!vlow`ZHF;YD9N8w%SaSJ3~bSYLYe73 zrz<6-*2KML&T|tE=P;wNlT-afZbB8M5Z8g+zXq)Ky@G|4hYIF=^9^Oh)C(>Tzvb@D zMy7(IH6~P0AXfl~#w(Q#zvVHON_D1mX~7V(_S3)o>3{$9^BU0IKjvPJftfw+6_h7D zD)?zAj9!!V0;W$;#aaWhhE%cm7y8#Sy&Aa)pU32yeRVw36p+wwYL1WfH5IB7p{rBZrbEG?>NV;$}N@@w@}2M45544*?N?NpaR4nv@1FG$)OcG63$^7G0PFa zGCM{^dyQZ5%o9kHL3!`w7m}Wjns9@=}KxNqw7wkCIaa}y!_wXn9(0NzX zl3@CT7M+q^^k?Y;p<(Voaw}uQe0s$z6^Y#GFs6FGSrn-%IEvpqNYINXCMaeGD2_HcHB9S&|H1Qb4vl! zs70)-5exPCr6C4}q^gV(#|%VnqJh3M#;j;k42=p}#{~C+3c7cTAiZ(^`mvcA(K6Bw zppz`!M}GlTX$KHbN8olpe|4ir2dV<6k1zQEM-2CCY{X2?FW}#Dot~k?LU%ewMRJX# zg#}Z93y_CijfjX_1#JWUA9P=FH zPTc2+?B^i%q$f)_H&jc!;-Fy5Z7jJnx>b9{F<1g#gb+ze>&ZGW3@#%s=AG;#QbnEu zH#99;qi;O@DwRIw!`%uz6(>)JYiVM*=ymXk)YcfO|*mB z12$qZ%#^m6Hei~uor0E2lt;aDmRrLs7pHH()5i>LQrwT3VZ?xI^KYFLryuybJK%ZR zmtxiDs*~bLGQ&{kswsM-GX%wy)>-!GqwJVTcl^8SK_oxkCvx!8CVq_$5l{xZpx*Nn z4jK#+BxF;K2SYXRe4jk|bDJ6GuE?u5{LV@A_>`yb2Sg(+<Fiu6QrB3rwS@4M=r{4X?(7TD>un_Ndx-IXs z<`d!;eJ-h`h*5~G-jI`Q-tlsWxL5PVJ5C{m+vmdRsgfc_h;&@zBo#ATg=Y$+qY2E8 zQZlP1Ks?3uU?gEmg6 z%R~$rTS?l6f{?@ODr7Hb;|H?tgkN!+Qmc?GRc(Y=#iE-QCFi9_TZWh;kE1=G36)d+ zI1}l4>>JHDsM84-M_-fCODu~gDc8zq>Yf&3T$Gkj1QsceqS*;k7;>Duv@zByd1AG` z;3vVmh{|ij3q2E+UcHNUl=3hkwVBP-SjPdXB-dn&%5S`t9K{R62W1FVyKV%2Vgl(6 z1KBlM;}|cYmvSpga${uENt^i#z^SpoMaiKOH@Wq1BBqP3qc9IL%gCnZyM=58mV}wB z8&gP4poi{kb)WwA&;R##|M-uezx(~4{_?}m|M=VAfBNf!Sh>#}lUD*YokDIzSF(U1 zESWi7Nc4~!2r(3?rSJW63~FN19MO2R0EiDzb_ZhiZ3}LUt$@VBX?*D8g3SWuAeL1p zu)GBks|wr*lqtdS8eMYAwRidbray2aWp~F+hj-(nNAGzpZJbU{Al1B_ z7D|PE0K;)$<*E-nq0uZXg!oGTB>MlNdPZ@4gYgVR9={|jDCr_kQHlP@mf@OKi2D_W z3y!pB3QUOHzZ42g%5$$K=2~r^K@*uI%jwETtMFIDzT*@`#H?krBI_#wSnzs=+yhbv zQk5Mmg#oEzeFdOIqano@-*1&sL*V|u08r)I@IanK&qB~?c;^}h8uChkiw=YW6D$Gj zq~Zzy-9Rp${G&J(AF0OK<{%cy&o96|tXEoM39xu819M|n*~Jh6$O=y6lCrSYMOH7D zCkLTNt{+Jf*(cyF+Zu4+&va61JgX&D%wiaQw)JiW^!cytNDM9Du|wNl#-8-Cb5Dj8 z;L>Q7Q)*zFU_{=z`o7a|h0FSUAf72<3WDnN7Ki3bJ1px0(yIrV1#_^(fYA+R&}n$1 zNR@f5c41F1#nQc>uTs%YZ0gm;6-^!Ao#>@wT!&{Kg#AgLWYhG`C5SVWSdtALX4_0@ zxjQ{^-+DA-g%nNiFy%(vaY7Zk^6J2Eo@k&U<&*as`Q(@`CFi}+iL0qKq(C?bIA>D z%j6sH2nse3-kbMpb=^qHi{7>g zOxL=74nld?P9d^@j%5Lz^kk4p0jnMh?S#pVMxZK52vjbGy?5W4wyv$WG-?WE+w~3I zQEFewbu{_5z`9P9XEFCE@;LwVJ4c2v$({^}#V3dY(u*B2GXKppu~zE}T&;2b-I_*| z3|;;+va;8;A?S#s0ZY%XIYwzLt4U6#6ix?Yasi@re7W*8hw`;Fd*8(ITwC9^iyn4&JiX?BO;U;d$f(3cf1s{x!WUiO&+>T-I6G>PT>>kd;gv43VCtWyOaC!8=DQik#t zUHv?rNb(G-QHd05G%~^Hjl)U5Hsp?AalJAmncBp|Trd2BSrr+9pK_=?6=6gNX0g^Z zONUYA%~vZ{TbTU9CxWT*4^L{nf)l3ccY2bOZH*0O6KA)2*B#IJR-Q%SYDp+ClrPRc zlHF0j7Ue`ch$C<8xFuM8m8yHLJhjMBZd(j1jMFu48#1!e2hLD&je%Db0ehyK1)r)T zVdU!v&JJqZ2-YTWWLrx^C+9q;zw3cBH2$&?X$S;`Q^PDfO5Ykh$Z7MBzx~z!`T6fZ z{q;M=3BP)7JTw+-J?6$48%&{1i(iAAjv<}E)BC~ovGp*z*#BZ)ys&duDK|`KyHr1OE3yw9Zhfhc=8?M;COw|( zSNy(i$}=^c$^qN*krt$kUF0Lyqw+@ehePXVEoP(AWJYwNni5S70)*hL-%am-U_5IU zv9Si@K%5_fx+!TD#4gS=m<^f|L0RBuZ!Rv^OD zqKA@OHi8WlN<|V?*HP%lGQZ7*!iX zXmW1g;c8!_#zkMlvCh30y=5=UTmX(>cfxE(Eq#133Iy%weXKh_zr#gJUAA#(Y!nB6 zcFPTwWzS6Kl}bm{j;j$AU2-ZUCUrKX){donk*o4$9Jc=YZ2frkw0rJ>_M(@^q~ zC}R)S∑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 z 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

+~Sk2b|YGiLbR9gf$f#HnUjijz9X{D1UV%2T5dt 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

N1i?HSp@qz&UMS7PfFz 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 z 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 literal 0 HcmV?d00001 From 3b51d9106aa4035a0bb6743ae025d8620eaf6247 Mon Sep 17 00:00:00 2001 From: Christopher Hartl Date: Wed, 21 Sep 2011 16:40:29 -0400 Subject: [PATCH 124/196] Adding in likelihood calculations for mendelian violations. Also fixing a minor and rare bug in SelectVariants when specifying family structure on the command line. --- .../walkers/annotator/VariantAnnotator.java | 6 +++ .../walkers/variantutils/SelectVariants.java | 2 +- .../sting/utils/MendelianViolation.java | 42 +++++++++++++++++++ 3 files changed, 49 insertions(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotator.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotator.java index fb3dbc3cf..27a750f99 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotator.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotator.java @@ -162,6 +162,12 @@ public class VariantAnnotator extends RodWalker implements Ann @Argument(fullName="vcfContainsOnlyIndels", shortName="dels",doc="Use if you are annotating an indel vcf, currently VERY experimental", required = false) protected boolean indelsOnly = false; + @Argument(fullName="family_string",shortName="family",required=false,doc="A family string of the form mom+dad=child for use with the mendelian violation ratio annotation") + public String familyStr = null; + + @Argument(fullName="MendelViolationGenotypeQualityThreshold",shortName="mvq",required=false,doc="The genotype quality treshold in order to annotate mendelian violation ratio") + public double minGenotypeQualityP = 0.0; + private VariantAnnotatorEngine engine; private Collection indelBufferContext; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/SelectVariants.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/SelectVariants.java index 018c4dcc2..e1de06ec0 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/SelectVariants.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/SelectVariants.java @@ -452,7 +452,7 @@ public class SelectVariants extends RodWalker { throw new UserException.CouldNotCreateOutputFile(outMVFile, "Can't open output file", e); } } else - mvSet.add(new MendelianViolation(getToolkit(), MENDELIAN_VIOLATION_QUAL_THRESHOLD)); + mvSet.add(new MendelianViolation(FAMILY_STRUCTURE, MENDELIAN_VIOLATION_QUAL_THRESHOLD)); } else if (!FAMILY_STRUCTURE.isEmpty()) { mvSet.add(new MendelianViolation(FAMILY_STRUCTURE, MENDELIAN_VIOLATION_QUAL_THRESHOLD)); diff --git a/public/java/src/org/broadinstitute/sting/utils/MendelianViolation.java b/public/java/src/org/broadinstitute/sting/utils/MendelianViolation.java index c6a07b5ce..8da118174 100755 --- a/public/java/src/org/broadinstitute/sting/utils/MendelianViolation.java +++ b/public/java/src/org/broadinstitute/sting/utils/MendelianViolation.java @@ -1,11 +1,13 @@ package org.broadinstitute.sting.utils; +import org.apache.commons.lang.ArrayUtils; import org.broadinstitute.sting.gatk.GenomeAnalysisEngine; import org.broadinstitute.sting.gatk.datasources.sample.Sample; import org.broadinstitute.sting.utils.exceptions.UserException; import org.broadinstitute.sting.utils.variantcontext.Genotype; import org.broadinstitute.sting.utils.variantcontext.VariantContext; +import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.regex.Matcher; @@ -32,6 +34,9 @@ public class MendelianViolation { private static Pattern FAMILY_PATTERN = Pattern.compile("(.*)\\+(.*)=(.*)"); + static final int[] mvOffsets = new int[] { 1,2,5,6,8,11,15,18,20,21,24,25 }; + static final int[] nonMVOffsets = new int[]{ 0,3,4,7,9,10,12,13,14,16,17,19,22,23,26 }; + public String getSampleMom() { return sampleMom; @@ -168,4 +173,41 @@ public class MendelianViolation { return true; } + /** + * @return the likelihood ratio for a mendelian violation + */ + public double violationLikelihoodRatio(VariantContext vc) { + double[] logLikAssignments = new double[27]; + // the matrix to set up is + // MOM DAD CHILD + // |- AA + // AA AA | AB + // |- BB + // |- AA + // AA AB | AB + // |- BB + // etc. The leaves are counted as 0-11 for MVs and 0-14 for non-MVs + double[] momGL = vc.getGenotype(sampleMom).getLikelihoods().getAsVector(); + double[] dadGL = vc.getGenotype(sampleDad).getLikelihoods().getAsVector(); + double[] childGL = vc.getGenotype(sampleChild).getLikelihoods().getAsVector(); + int offset = 0; + for ( int oMom = 0; oMom < 3; oMom++ ) { + for ( int oDad = 0; oDad < 3; oDad++ ) { + for ( int oChild = 0; oChild < 3; oChild ++ ) { + logLikAssignments[offset++] = momGL[oMom] + dadGL[oDad] + childGL[oChild]; + } + } + } + double[] mvLiks = new double[12]; + double[] nonMVLiks = new double[15]; + for ( int i = 0; i < 12; i ++ ) { + mvLiks[i] = logLikAssignments[mvOffsets[i]]; + } + + for ( int i = 0; i < 15; i++) { + nonMVLiks[i] = logLikAssignments[nonMVOffsets[i]]; + } + + return MathUtils.log10sumLog10(mvLiks) - MathUtils.log10sumLog10(nonMVLiks); + } } From 70335b2b0a967f31b1aa063ace4845bff413b086 Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Wed, 21 Sep 2011 17:12:01 -0400 Subject: [PATCH 126/196] Hard clipping soft clipped reads to fix misalignments. Pre-softclipped reads (with high qual) are a complicated event to deal with in the Reduced Reads environment. I chose to hard clip them out for now and added a todo item to bring them back on in the future, perhaps as a variant region. --- .../sting/utils/clipreads/ReadClipper.java | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/public/java/src/org/broadinstitute/sting/utils/clipreads/ReadClipper.java b/public/java/src/org/broadinstitute/sting/utils/clipreads/ReadClipper.java index a8a3fad9e..9b9ce4fdf 100644 --- a/public/java/src/org/broadinstitute/sting/utils/clipreads/ReadClipper.java +++ b/public/java/src/org/broadinstitute/sting/utils/clipreads/ReadClipper.java @@ -1,6 +1,7 @@ package org.broadinstitute.sting.utils.clipreads; import com.google.java.contract.Requires; +import net.sf.samtools.Cigar; import net.sf.samtools.CigarElement; import net.sf.samtools.CigarOperator; import net.sf.samtools.SAMRecord; @@ -8,6 +9,7 @@ import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; import org.broadinstitute.sting.utils.sam.ReadUtils; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; /** @@ -128,6 +130,39 @@ public class ReadClipper { return this.clipRead(ClippingRepresentation.HARDCLIP_BASES); } + public SAMRecord hardClipSoftClippedBases () { + int readIndex = 0; + int cutLeft = -1; // first position to hard clip (inclusive) + int cutRight = -1; // first position to hard clip (inclusive) + boolean rightTail = false; // trigger to stop clipping the left tail and start cutting the right tail + + for (CigarElement cigarElement : read.getCigar().getCigarElements()) { + if (cigarElement.getOperator() == CigarOperator.SOFT_CLIP) { + if (rightTail) { + cutRight = readIndex; + } + else { + cutLeft = readIndex + cigarElement.getLength() - 1; + } + } + else + rightTail = true; + + if (cigarElement.getOperator().consumesReadBases()) + readIndex += cigarElement.getLength(); + } + + // It is extremely important that we cut the end first otherwise the read coordinates change. + if (cutRight >= 0) + this.addOp(new ClippingOp(cutRight, read.getReadLength() - 1)); + if (cutLeft >= 0) + this.addOp(new ClippingOp(0, cutLeft)); + + return clipRead(ClippingRepresentation.HARDCLIP_BASES); + } + + + /** * Return a new read corresponding to this.read that's been clipped according to ops, if any are present. * From faff6e401998ca39ed00553185fcc20e312f3130 Mon Sep 17 00:00:00 2001 From: Christopher Hartl Date: Wed, 21 Sep 2011 18:15:23 -0400 Subject: [PATCH 127/196] Failed to commit changes to the GATKReport required for more easy access when using the files as data sources (read: histograms) for walkers --- .../sting/gatk/report/GATKReportColumns.java | 14 +++++++++++++- .../sting/gatk/report/GATKReportTable.java | 4 ++++ .../sting/gatk/report/GATKReportVersion.java | 0 3 files changed, 17 insertions(+), 1 deletion(-) mode change 100644 => 100755 public/java/src/org/broadinstitute/sting/gatk/report/GATKReportColumns.java mode change 100644 => 100755 public/java/src/org/broadinstitute/sting/gatk/report/GATKReportVersion.java diff --git a/public/java/src/org/broadinstitute/sting/gatk/report/GATKReportColumns.java b/public/java/src/org/broadinstitute/sting/gatk/report/GATKReportColumns.java old mode 100644 new mode 100755 index a33631c85..a73123b6c --- a/public/java/src/org/broadinstitute/sting/gatk/report/GATKReportColumns.java +++ b/public/java/src/org/broadinstitute/sting/gatk/report/GATKReportColumns.java @@ -24,12 +24,14 @@ package org.broadinstitute.sting.gatk.report; +import org.broadinstitute.sting.utils.collections.Pair; + import java.util.*; /** * Tracks a linked list of GATKReportColumn in order by name. */ -public class GATKReportColumns extends LinkedHashMap { +public class GATKReportColumns extends LinkedHashMap implements Iterable { private List columnNames = new ArrayList(); /** @@ -52,4 +54,14 @@ public class GATKReportColumns extends LinkedHashMap { columnNames.add(key); return super.put(key, value); } + + @Override + public Iterator iterator() { + return new Iterator() { + int offset = 0; + public boolean hasNext() { return offset < columnNames.size() ; } + public GATKReportColumn next() { return getByIndex(offset++); } + public void remove() { throw new UnsupportedOperationException("Cannot remove from a GATKReportColumn iterator"); } + }; + } } diff --git a/public/java/src/org/broadinstitute/sting/gatk/report/GATKReportTable.java b/public/java/src/org/broadinstitute/sting/gatk/report/GATKReportTable.java index 3e3aa29a7..2fd5ad7e3 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/report/GATKReportTable.java +++ b/public/java/src/org/broadinstitute/sting/gatk/report/GATKReportTable.java @@ -286,6 +286,10 @@ public class GATKReportTable { } } + public boolean containsKey(Object primaryKey) { + return primaryKeyColumn.contains(primaryKey); + } + /** * Set the value for a given position in the table * diff --git a/public/java/src/org/broadinstitute/sting/gatk/report/GATKReportVersion.java b/public/java/src/org/broadinstitute/sting/gatk/report/GATKReportVersion.java old mode 100644 new mode 100755 From f9cdc119afec2d0d4dd76e04f82e138c9cf96dca Mon Sep 17 00:00:00 2001 From: Christopher Hartl Date: Wed, 21 Sep 2011 18:16:42 -0400 Subject: [PATCH 128/196] Added a method to ReadUtils that converts reads of the form 10S20M10S to 40M (just unclips the soft-clips). Be careful when using this - if you're writing a bam file it will be potentially written out of order (since the previous alignment start was at the M, not the S). --- .../sting/utils/sam/ReadUtils.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) mode change 100644 => 100755 public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java diff --git a/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java b/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java old mode 100644 new mode 100755 index 5d3ef3086..60c9c1780 --- a/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java @@ -681,6 +681,9 @@ public class ReadUtils { @Ensures({"result >= read.getUnclippedStart()", "result <= read.getUnclippedEnd() || readIsEntirelyInsertion(read)"}) public static int getRefCoordSoftUnclippedEnd(SAMRecord read) { + if ( read.getCigar().numCigarElements() == 1 && read.getCigar().getCigarElement(0).getOperator().equals(CigarOperator.INSERTION)) { + return read.getUnclippedEnd(); + } int stop = read.getUnclippedStart(); if (readIsEntirelyInsertion(read)) @@ -787,5 +790,47 @@ public class ReadUtils { return readBases; } + public static SAMRecord unclipSoftClippedBases(SAMRecord rec) { + int newReadStart = rec.getAlignmentStart(); + int newReadEnd = rec.getAlignmentEnd(); + List newCigarElements = new ArrayList(rec.getCigar().getCigarElements().size()); + int heldOver = -1; + boolean sSeen = false; + for ( CigarElement e : rec.getCigar().getCigarElements() ) { + if ( e.getOperator().equals(CigarOperator.S) ) { + newCigarElements.add(new CigarElement(e.getLength(),CigarOperator.M)); + if ( sSeen ) { + newReadEnd += e.getLength(); + sSeen = true; + } else { + newReadStart -= e.getLength(); + } + } else { + newCigarElements.add(e); + } + } + // merge duplicate operators together + int idx = 0; + List finalCigarElements = new ArrayList(rec.getCigar().getCigarElements().size()); + while ( idx < newCigarElements.size() -1 ) { + if ( newCigarElements.get(idx).getOperator().equals(newCigarElements.get(idx+1).getOperator()) ) { + int combSize = newCigarElements.get(idx).getLength(); + int offset = 0; + while ( idx + offset < newCigarElements.size()-1 && newCigarElements.get(idx+offset).getOperator().equals(newCigarElements.get(idx+1+offset).getOperator()) ) { + combSize += newCigarElements.get(idx+offset+1).getLength(); + offset++; + } + finalCigarElements.add(new CigarElement(combSize,newCigarElements.get(idx).getOperator())); + idx = idx + offset -1; + } else { + finalCigarElements.add(newCigarElements.get(idx)); + } + idx++; + } + rec.setCigar(new Cigar(finalCigarElements)); + rec.setAlignmentStart(newReadStart); + + return rec; + } } From 5d0f284305ec4274ec255474a1cd536574fc1a39 Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Wed, 21 Sep 2011 20:26:28 -0400 Subject: [PATCH 129/196] Fixing exome specific arguments to the VQSR in the methods development calling pipeline --- .../qscripts/MethodsDevelopmentCallingPipeline.scala | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala index 29ee2769b..d187c1e1a 100755 --- a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala +++ b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala @@ -266,13 +266,18 @@ class MethodsDevelopmentCallingPipeline extends QScript { this.resource :+= new TaggedFile( t.dbsnpFile, "known=true,prior=2.0" ) this.resource :+= new TaggedFile( projectConsensus_1000G, "prior=8.0" ) this.use_annotation ++= List("QD", "HaplotypeScore", "MQRankSum", "ReadPosRankSum", "MQ", "FS") - if(t.nSamples >= 10) { + if(t.nSamples >= 10) { // InbreedingCoeff is a population-wide statistic that requires at least 10 samples to calculate this.use_annotation ++= List("InbreedingCoeff") } if(!t.isExome) { this.use_annotation ++= List("DP") - } else { + } else { // exome specific parameters + this.resource :+= new TaggedFile( badSites_1000G, "bad=true,prior=2.0" ) this.mG = 6 + if(t.nSamples <= 3) { // very few exome samples means very few variants + this.mG = 4 + this.percentBad = 0.04 + } } this.tranches_file = if ( goldStandard ) { t.goldStandardTranchesFile } else { t.tranchesFile } this.recal_file = if ( goldStandard ) { t.goldStandardRecalFile } else { t.recalFile } From 8f8b59a932923d29fdeb91a7ef90d1eef24ca1fd Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Wed, 21 Sep 2011 22:23:28 -0400 Subject: [PATCH 130/196] My interpretation of the VCF spec is that the FORMAT field should only be present if there is genotype/sample data. So the VCFCodec now throws an exception when it encounters such a case. I had to fix one of the integration test VCFs. --- .../utils/codecs/vcf/AbstractVCFCodec.java | 38 ++++++++++--------- .../sting/utils/codecs/vcf/VCFHeader.java | 17 +++------ .../sting/utils/exceptions/UserException.java | 6 +++ 3 files changed, 32 insertions(+), 29 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java index 83c7083d0..43b07476d 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java @@ -115,15 +115,21 @@ public abstract class AbstractVCFCodec implements FeatureCodec, NameAwareCodec, } arrayIndex++; } + + boolean sawFormatTag = false; if ( arrayIndex < strings.length ) { if ( !strings[arrayIndex].equals("FORMAT") ) throw new TribbleException.InvalidHeader("we were expecting column name 'FORMAT' but we saw '" + strings[arrayIndex] + "'"); + sawFormatTag = true; arrayIndex++; } - while (arrayIndex < strings.length) + while ( arrayIndex < strings.length ) auxTags.add(strings[arrayIndex++]); + if ( sawFormatTag && auxTags.size() == 0 ) + throw new UserException.MalformedVCFHeader("The FORMAT field was provided but there is no genotype/sample data"); + } else { if ( str.startsWith("##INFO=") ) { VCFInfoHeaderLine info = new VCFInfoHeaderLine(str.substring(7),version); @@ -200,28 +206,24 @@ public abstract class AbstractVCFCodec implements FeatureCodec, NameAwareCodec, * @return a VariantContext */ public Feature decode(String line) { - return reallyDecode(line); - } + // the same line reader is not used for parsing the header and parsing lines, if we see a #, we've seen a header line + if (line.startsWith(VCFHeader.HEADER_INDICATOR)) return null; - private Feature reallyDecode(String line) { - // the same line reader is not used for parsing the header and parsing lines, if we see a #, we've seen a header line - if (line.startsWith(VCFHeader.HEADER_INDICATOR)) return null; + // our header cannot be null, we need the genotype sample names and counts + if (header == null) throw new ReviewedStingException("VCF Header cannot be null when decoding a record"); - // our header cannot be null, we need the genotype sample names and counts - if (header == null) throw new ReviewedStingException("VCF Header cannot be null when decoding a record"); + if (parts == null) + parts = new String[Math.min(header.getColumnCount(), NUM_STANDARD_FIELDS+1)]; - if (parts == null) - parts = new String[Math.min(header.getColumnCount(), NUM_STANDARD_FIELDS+1)]; + int nParts = ParsingUtils.split(line, parts, VCFConstants.FIELD_SEPARATOR_CHAR, true); - int nParts = ParsingUtils.split(line, parts, VCFConstants.FIELD_SEPARATOR_CHAR, true); + // if we have don't have a header, or we have a header with no genotyping data check that we have eight columns. Otherwise check that we have nine (normal colummns + genotyping data) + if (( (header == null || !header.hasGenotypingData()) && nParts != NUM_STANDARD_FIELDS) || + (header != null && header.hasGenotypingData() && nParts != (NUM_STANDARD_FIELDS + 1)) ) + throw new UserException.MalformedVCF("there aren't enough columns for line " + line + " (we expected " + (header == null ? NUM_STANDARD_FIELDS : NUM_STANDARD_FIELDS + 1) + + " tokens, and saw " + nParts + " )", lineNo); - // if we have don't have a header, or we have a header with no genotyping data check that we have eight columns. Otherwise check that we have nine (normal colummns + genotyping data) - if (( (header == null || !header.hasGenotypingData()) && nParts != NUM_STANDARD_FIELDS) || - (header != null && header.hasGenotypingData() && nParts != (NUM_STANDARD_FIELDS + 1)) ) - throw new UserException.MalformedVCF("there aren't enough columns for line " + line + " (we expected " + (header == null ? NUM_STANDARD_FIELDS : NUM_STANDARD_FIELDS + 1) + - " tokens, and saw " + nParts + " )", lineNo); - - return parseVCFLine(parts); + return parseVCFLine(parts); } protected void generateException(String message) { diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFHeader.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFHeader.java index fd1c74993..66e11bc1e 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFHeader.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFHeader.java @@ -35,9 +35,6 @@ public class VCFHeader { // the header string indicator public static final String HEADER_INDICATOR = "#"; - /** do we have genotying data? */ - private boolean hasGenotypingData = false; - // were the input samples sorted originally (or are we sorting them)? private boolean samplesWereAlreadySorted = true; @@ -57,17 +54,15 @@ public class VCFHeader { * create a VCF header, given a list of meta data and auxillary tags * * @param metaData the meta data associated with this header - * @param genotypeSampleNames the genotype format field, and the sample names + * @param genotypeSampleNames the sample names */ public VCFHeader(Set metaData, Set genotypeSampleNames) { mMetaData = new TreeSet(); if ( metaData != null ) mMetaData.addAll(metaData); - for (String col : genotypeSampleNames) { - if (!col.equals("FORMAT")) - mGenotypeSampleNames.add(col); - } - if (genotypeSampleNames.size() > 0) hasGenotypingData = true; + + mGenotypeSampleNames.addAll(genotypeSampleNames); + loadVCFVersion(); loadMetaDataMaps(); @@ -157,7 +152,7 @@ public class VCFHeader { * @return true if we have genotyping columns, false otherwise */ public boolean hasGenotypingData() { - return hasGenotypingData; + return mGenotypeSampleNames.size() > 0; } /** @@ -171,7 +166,7 @@ public class VCFHeader { /** @return the column count */ public int getColumnCount() { - return HEADER_FIELDS.values().length + ((hasGenotypingData) ? mGenotypeSampleNames.size() + 1 : 0); + return HEADER_FIELDS.values().length + (hasGenotypingData() ? mGenotypeSampleNames.size() + 1 : 0); } /** diff --git a/public/java/src/org/broadinstitute/sting/utils/exceptions/UserException.java b/public/java/src/org/broadinstitute/sting/utils/exceptions/UserException.java index 274c64f42..70f7387f4 100755 --- a/public/java/src/org/broadinstitute/sting/utils/exceptions/UserException.java +++ b/public/java/src/org/broadinstitute/sting/utils/exceptions/UserException.java @@ -174,6 +174,12 @@ public class UserException extends ReviewedStingException { } } + public static class MalformedVCFHeader extends UserException { + public MalformedVCFHeader(String message) { + super(String.format("The provided VCF file has a malformed header: %s", message)); + } + } + public static class ReadMissingReadGroup extends MalformedBAM { public ReadMissingReadGroup(SAMRecord read) { super(read, String.format("Read %s is either missing the read group or its read group is not defined in the BAM header, both of which are required by the GATK. Please use http://www.broadinstitute.org/gsa/wiki/index.php/ReplaceReadGroups to fix this problem", read.getReadName())); From b8ea9ceb68f3ded503bac08cd95e3a15237d5930 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Wed, 21 Sep 2011 22:43:31 -0400 Subject: [PATCH 131/196] Adding integration test that uses the -V:dbsnp binding to make sure it won't fail later on if someone messes with Tribble. --- .../SelectVariantsIntegrationTest.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/SelectVariantsIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/SelectVariantsIntegrationTest.java index 20409d4ca..e4ded491b 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/SelectVariantsIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/SelectVariantsIntegrationTest.java @@ -16,7 +16,7 @@ public class SelectVariantsIntegrationTest extends WalkerTest { String samplesFile = validationDataLocation + "SelectVariants.samples.txt"; WalkerTestSpec spec = new WalkerTestSpec( - baseTestString(" -sn A -se '[CDH]' -sf " + samplesFile + " -env -ef -select 'DP < 250' --variant:VCF3 " + testfile), + baseTestString(" -sn A -se '[CDH]' -sf " + samplesFile + " -env -ef -select 'DP < 250' --variant " + testfile), 1, Arrays.asList("d18516c1963802e92cb9e425c0b75fd6") ); @@ -30,7 +30,7 @@ public class SelectVariantsIntegrationTest extends WalkerTest { String samplesFile = validationDataLocation + "SelectVariants.samples.txt"; WalkerTestSpec spec = new WalkerTestSpec( - "-T SelectVariants -R " + b36KGReference + " -L 1:1-1000000 -o %s -NO_HEADER -xl_sn A -xl_sf " + samplesFile + " --variant:VCF3 " + testfile, + "-T SelectVariants -R " + b36KGReference + " -L 1:1-1000000 -o %s -NO_HEADER -xl_sn A -xl_sf " + samplesFile + " --variant " + testfile, 1, Arrays.asList("730f021fd6ecf1d195dabbee2e233bfd") ); @@ -43,7 +43,7 @@ public class SelectVariantsIntegrationTest extends WalkerTest { String testfile = validationDataLocation + "test.dup.vcf"; WalkerTestSpec spec = new WalkerTestSpec( - baseTestString(" -sn A -sn B -sn C --variant:VCF3 " + testfile), + baseTestString(" -sn A -sn B -sn C --variant " + testfile), 1, Arrays.asList("b74038779fe6485dbb8734ae48178356") ); @@ -56,7 +56,7 @@ public class SelectVariantsIntegrationTest extends WalkerTest { String testFile = validationDataLocation + "NA12878.hg19.example1.vcf"; WalkerTestSpec spec = new WalkerTestSpec( - "-T SelectVariants -R " + hg19Reference + " -sn NA12878 -L 20:1012700-1020000 --variant:VCF " + b37hapmapGenotypes + " -disc:VCF " + testFile + " -o %s -NO_HEADER", + "-T SelectVariants -R " + hg19Reference + " -sn NA12878 -L 20:1012700-1020000 --variant " + b37hapmapGenotypes + " -disc " + testFile + " -o %s -NO_HEADER", 1, Arrays.asList("78e6842325f1f1bc9ab30d5e7737ee6e") ); @@ -69,7 +69,7 @@ public class SelectVariantsIntegrationTest extends WalkerTest { String testFile = validationDataLocation + "NA12878.hg19.example1.vcf"; WalkerTestSpec spec = new WalkerTestSpec( - "-T SelectVariants -R " + hg19Reference + " -sn NA12878 -L 20:1012700-1020000 -conc:VCF " + b37hapmapGenotypes + " --variant " + testFile + " -o %s -NO_HEADER", + "-T SelectVariants -R " + hg19Reference + " -sn NA12878 -L 20:1012700-1020000 -conc " + b37hapmapGenotypes + " --variant " + testFile + " -o %s -NO_HEADER", 1, Arrays.asList("d2ba3ea30a810f6f0fbfb1b643292b6a") ); @@ -90,16 +90,16 @@ public class SelectVariantsIntegrationTest extends WalkerTest { executeTest("testVariantTypeSelection--" + testFile, spec); } - @Test(enabled=false) - public void testRemovePLs() { + @Test + public void testUsingDbsnpName() { String testFile = validationDataLocation + "combine.3.vcf"; WalkerTestSpec spec = new WalkerTestSpec( - "-T SelectVariants -R " + b36KGReference + " -sn NA12892 --variant " + testFile + " -o %s -NO_HEADER", + "-T SelectVariants -R " + b36KGReference + " -sn NA12892 --variant:dbsnp " + testFile + " -o %s -NO_HEADER", 1, - Arrays.asList("") + Arrays.asList("167a1265df820978a74c267df44d5c43") ); - executeTest("testWithPLs--" + testFile, spec); + executeTest("testUsingDbsnpName--" + testFile, spec); } } From f81a41b889faec164f2034a70427a3bbd1934db2 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 22 Sep 2011 10:30:25 -0400 Subject: [PATCH 132/196] Updating MD5s for CombineVariants -- Old version had broken RSIDs, new version is fixed. No longer see rs1234,. as it is now just rs1234 --- .../variantutils/CombineVariantsIntegrationTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java index 74ff850a4..b65de9d36 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java @@ -89,8 +89,8 @@ public class CombineVariantsIntegrationTest extends WalkerTest { @Test public void combineWithPLs() { combinePLs("combine.3.vcf", "combine.4.vcf", "0f873fed02aa99db5b140bcd6282c10a"); } @Test public void combineTrioCalls() { combine2("CEU.trio.2010_03.genotypes.vcf.gz", "YRI.trio.2010_03.genotypes.vcf.gz", "", "1d5a021387a8a86554db45a29f66140f"); } // official project VCF files in tabix format - @Test public void combineTrioCallsMin() { combine2("CEU.trio.2010_03.genotypes.vcf.gz", "YRI.trio.2010_03.genotypes.vcf.gz", " -minimalVCF", "20163d60f18a46496f6da744ab5cc0f9"); } // official project VCF files in tabix format - @Test public void combine2Indels() { combine2("CEU.dindel.vcf4.trio.2010_06.indel.genotypes.vcf", "CEU.dindel.vcf4.low_coverage.2010_06.indel.genotypes.vcf", "", "d76cd5b3ced7745d42fe0af39ce0b32e"); } + @Test public void combineTrioCallsMin() { combine2("CEU.trio.2010_03.genotypes.vcf.gz", "YRI.trio.2010_03.genotypes.vcf.gz", " -minimalVCF", "96941ee177b0614a9879af0ac3218963"); } // official project VCF files in tabix format + @Test public void combine2Indels() { combine2("CEU.dindel.vcf4.trio.2010_06.indel.genotypes.vcf", "CEU.dindel.vcf4.low_coverage.2010_06.indel.genotypes.vcf", "", "a8a6e7589f22e0b6c5d222066b9a2093"); } @Test public void combineSNPsAndIndels() { combine2("CEU.trio.2010_03.genotypes.vcf.gz", "CEU.dindel.vcf4.low_coverage.2010_06.indel.genotypes.vcf", "", "e144b6283765494bfe8189ac59965083"); } @@ -110,7 +110,7 @@ public class CombineVariantsIntegrationTest extends WalkerTest { " -priority NA19240_BGI,NA19240_ILLUMINA,NA19240_WUGSC,denovoInfo" + " -genotypeMergeOptions UNIQUIFY -L 1"), 1, - Arrays.asList("1de95f91ca15d2a8856de35dee0ce33e")); + Arrays.asList("212d9d3df10bb29e2c7fb226da422dc0")); executeTest("threeWayWithRefs", spec); } From 982c47bfa7ce48e2d4d53a445cff958212354ab0 Mon Sep 17 00:00:00 2001 From: Christopher Hartl Date: Thu, 22 Sep 2011 10:58:26 -0400 Subject: [PATCH 133/196] Remove duplicate effort in ReadUtils (with apologies to Mauricio) Big (but not major) cleanup of code in ILG - mostly excising the old likelihood model Activated the early-abort check for ILG. I think it should be better this way. --- .../java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java b/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java index 60c9c1780..c328bbc5a 100755 --- a/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java @@ -681,9 +681,6 @@ public class ReadUtils { @Ensures({"result >= read.getUnclippedStart()", "result <= read.getUnclippedEnd() || readIsEntirelyInsertion(read)"}) public static int getRefCoordSoftUnclippedEnd(SAMRecord read) { - if ( read.getCigar().numCigarElements() == 1 && read.getCigar().getCigarElement(0).getOperator().equals(CigarOperator.INSERTION)) { - return read.getUnclippedEnd(); - } int stop = read.getUnclippedStart(); if (readIsEntirelyInsertion(read)) From 623c49765d7a70cd87e68cee913bd133eaf168aa Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Thu, 22 Sep 2011 11:13:40 -0400 Subject: [PATCH 135/196] NO BAQ ON EXOMES! says the boss. --- .../queue/qscripts/MethodsDevelopmentCallingPipeline.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala index 29ee2769b..34bc43ff5 100755 --- a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala +++ b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala @@ -221,7 +221,7 @@ class MethodsDevelopmentCallingPipeline extends QScript { this.max_deletion_fraction = qscript.deletions this.out = t.rawVCF this.glm = org.broadinstitute.sting.gatk.walkers.genotyper.GenotypeLikelihoodsCalculationModel.Model.SNP - this.baq = if (noBAQ) {org.broadinstitute.sting.utils.baq.BAQ.CalculationMode.OFF} else {org.broadinstitute.sting.utils.baq.BAQ.CalculationMode.CALCULATE_AS_NECESSARY} + this.baq = if (noBAQ || t.isExome) {org.broadinstitute.sting.utils.baq.BAQ.CalculationMode.OFF} else {org.broadinstitute.sting.utils.baq.BAQ.CalculationMode.CALCULATE_AS_NECESSARY} this.analysisName = t.name + "_UGs" this.jobName = queueLogDir + t.name + ".snpcall" } From 3fdee2b9ed36072163f7284c3296a2f6d353c5d1 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 22 Sep 2011 11:19:43 -0400 Subject: [PATCH 136/196] Merge from stable into unstable --- .../walkers/variantutils/CombineVariantsIntegrationTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java index b65de9d36..0205fe0eb 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java @@ -90,7 +90,7 @@ public class CombineVariantsIntegrationTest extends WalkerTest { @Test public void combineTrioCalls() { combine2("CEU.trio.2010_03.genotypes.vcf.gz", "YRI.trio.2010_03.genotypes.vcf.gz", "", "1d5a021387a8a86554db45a29f66140f"); } // official project VCF files in tabix format @Test public void combineTrioCallsMin() { combine2("CEU.trio.2010_03.genotypes.vcf.gz", "YRI.trio.2010_03.genotypes.vcf.gz", " -minimalVCF", "96941ee177b0614a9879af0ac3218963"); } // official project VCF files in tabix format - @Test public void combine2Indels() { combine2("CEU.dindel.vcf4.trio.2010_06.indel.genotypes.vcf", "CEU.dindel.vcf4.low_coverage.2010_06.indel.genotypes.vcf", "", "a8a6e7589f22e0b6c5d222066b9a2093"); } + @Test public void combine2Indels() { combine2("CEU.dindel.vcf4.trio.2010_06.indel.genotypes.vcf", "CEU.dindel.vcf4.low_coverage.2010_06.indel.genotypes.vcf", "", "f1c8720fde62687c2e861217670d8b3c"); } @Test public void combineSNPsAndIndels() { combine2("CEU.trio.2010_03.genotypes.vcf.gz", "CEU.dindel.vcf4.low_coverage.2010_06.indel.genotypes.vcf", "", "e144b6283765494bfe8189ac59965083"); } @@ -137,7 +137,7 @@ public class CombineVariantsIntegrationTest extends WalkerTest { WalkerTestSpec spec = new WalkerTestSpec( "-T CombineVariants -NO_HEADER -L 1:902000-903000 -o %s -R " + b37KGReference + " -V:v1 " + b37dbSNP132, 1, - Arrays.asList("")); + Arrays.asList("5969446769cb8377daa2db29304ae6b5")); executeTest("combineDBSNPDuplicateSites:", spec); } } \ No newline at end of file From a05c959e5a9729a023260ffc019dd2df44ce14b8 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 22 Sep 2011 11:20:07 -0400 Subject: [PATCH 137/196] Empty unit tests for VariantContextUtils -- will be expanded over the day --- .../sting/utils/variantcontext/Genotype.java | 19 ++++- .../VariantContextUnitTest.java | 6 +- .../VariantContextUtilsUnitTest.java | 84 +++++++++++++++++++ 3 files changed, 104 insertions(+), 5 deletions(-) create mode 100644 public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/Genotype.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/Genotype.java index 85d752003..577168578 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/Genotype.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/Genotype.java @@ -25,15 +25,32 @@ public class Genotype { protected boolean isPhased = false; protected boolean filtersWereAppliedToContext; - public Genotype(String sampleName, List alleles, double negLog10PError, Set filters, Map attributes, boolean isPhased) { + public Genotype(String sampleName, List alleles, double negLog10PError, Set filters, Map attributes, boolean isPhased) { + this(sampleName, alleles, negLog10PError, filters, attributes, isPhased, null); + } + + public Genotype(String sampleName, List alleles, double negLog10PError, Set filters, Map attributes, boolean isPhased, double[] log10Likelihoods) { if ( alleles != null ) this.alleles = Collections.unmodifiableList(alleles); + if ( log10Likelihoods != null ) + attributes.put(VCFConstants.PHRED_GENOTYPE_LIKELIHOODS_KEY, GenotypeLikelihoods.fromLog10Likelihoods(log10Likelihoods)); commonInfo = new InferredGeneticContext(sampleName, negLog10PError, filters, attributes); filtersWereAppliedToContext = filters != null; this.isPhased = isPhased; validate(); } + /** + * Creates a new Genotype for sampleName with genotype according to alleles. + * @param sampleName + * @param alleles + * @param negLog10PError the confidence in these alleles + * @param log10Likelihoods a log10 likelihoods for each of the genotype combinations possible for alleles, in the standard VCF ordering, or null if not known + */ + public Genotype(String sampleName, List alleles, double negLog10PError, double[] log10Likelihoods) { + this(sampleName, alleles, negLog10PError, null, null, false, log10Likelihoods); + } + public Genotype(String sampleName, List alleles, double negLog10PError) { this(sampleName, alleles, negLog10PError, null, null, false); } diff --git a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUnitTest.java index f8e6da20a..663eb9ef6 100755 --- a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUnitTest.java @@ -5,6 +5,7 @@ package org.broadinstitute.sting.utils.variantcontext; // the imports for unit testing. +import org.broadinstitute.sting.BaseTest; import org.testng.Assert; import org.testng.annotations.BeforeSuite; import org.testng.annotations.BeforeTest; @@ -14,10 +15,7 @@ import java.util.Arrays; import java.util.List; -/** - * Basic unit test for RecalData - */ -public class VariantContextUnitTest { +public class VariantContextUnitTest extends BaseTest { Allele A, Aref, T, Tref; Allele del, delRef, ATC, ATCref; diff --git a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java new file mode 100644 index 000000000..9ca29d41c --- /dev/null +++ b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2011, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +// our package +package org.broadinstitute.sting.utils.variantcontext; + + +// the imports for unit testing. + + +import net.sf.picard.reference.IndexedFastaSequenceFile; +import org.broadinstitute.sting.BaseTest; +import org.broadinstitute.sting.gatk.refdata.tracks.FeatureManager; +import org.broadinstitute.sting.utils.GenomeLocParser; +import org.broadinstitute.sting.utils.codecs.vcf.VCFConstants; +import org.broadinstitute.sting.utils.exceptions.UserException; +import org.broadinstitute.sting.utils.fasta.CachingIndexedFastaSequenceFile; +import org.testng.Assert; +import org.testng.annotations.BeforeSuite; +import org.testng.annotations.Test; + +import java.io.File; +import java.io.FileNotFoundException; +import java.util.*; + + +public class VariantContextUtilsUnitTest extends BaseTest { + Allele Aref, T, delRef, ATC; + Genotype snp1, snp2, indel1; + private GenomeLocParser genomeLocParser; + + @BeforeSuite + public void setup() { + final File referenceFile = new File(b37KGReference); + try { + IndexedFastaSequenceFile seq = new CachingIndexedFastaSequenceFile(referenceFile); + genomeLocParser = new GenomeLocParser(seq); + } + catch(FileNotFoundException ex) { + throw new UserException.CouldNotReadInputFile(referenceFile,ex); + } + + // alleles + Aref = Allele.create("A", true); + delRef = Allele.create("-", true); + T = Allele.create("T"); + ATC = Allele.create("ATC"); + + snp1 = new Genotype("snp1", Arrays.asList(Aref,T), 10, new double[]{10, 0, 20}); + snp2 = new Genotype("snp2", Arrays.asList(T,T), 15, new double[]{25, 15, 0}); + indel1 = new Genotype("indel1", Arrays.asList(delRef,ATC), 20, new double[]{20, 0, 30}); + } + + private VariantContext makeVC(String source, List alleles) { + return makeVC(source, alleles, null, null); + } + + private VariantContext makeVC(String source, List alleles, Collection genotypes, Set filters) { + int start = 10; + int stop = alleles.contains(ATC) ? start + 3 : start; + return new VariantContext(source, "1", start, stop, alleles, genotypes, 1.0, filters, null); + } +} From 5e06a456286ca2ac4b24abb7c541fa3cbab102d5 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Thu, 22 Sep 2011 11:55:40 -0400 Subject: [PATCH 138/196] Fix the AnalayzeCovariates packaging. --- public/packages/AnalyzeCovariates.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/packages/AnalyzeCovariates.xml b/public/packages/AnalyzeCovariates.xml index a6675a63d..e8d58862a 100644 --- a/public/packages/AnalyzeCovariates.xml +++ b/public/packages/AnalyzeCovariates.xml @@ -6,7 +6,7 @@ - + From ba5f83fee2487f06d3969f8a55b9ee0214986c5f Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 22 Sep 2011 12:10:39 -0400 Subject: [PATCH 139/196] start of VariantContextUtils UnitTest -- tests rsID merging --- .../sting/utils/variantcontext/Genotype.java | 8 +- .../VariantContextUtilsUnitTest.java | 124 +++++++++++++++++- 2 files changed, 125 insertions(+), 7 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/Genotype.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/Genotype.java index 577168578..2e233c18e 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/Genotype.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/Genotype.java @@ -25,16 +25,16 @@ public class Genotype { protected boolean isPhased = false; protected boolean filtersWereAppliedToContext; - public Genotype(String sampleName, List alleles, double negLog10PError, Set filters, Map attributes, boolean isPhased) { + public Genotype(String sampleName, List alleles, double negLog10PError, Set filters, Map attributes, boolean isPhased) { this(sampleName, alleles, negLog10PError, filters, attributes, isPhased, null); } - public Genotype(String sampleName, List alleles, double negLog10PError, Set filters, Map attributes, boolean isPhased, double[] log10Likelihoods) { + public Genotype(String sampleName, List alleles, double negLog10PError, Set filters, Map attributes, boolean isPhased, double[] log10Likelihoods) { if ( alleles != null ) this.alleles = Collections.unmodifiableList(alleles); - if ( log10Likelihoods != null ) - attributes.put(VCFConstants.PHRED_GENOTYPE_LIKELIHOODS_KEY, GenotypeLikelihoods.fromLog10Likelihoods(log10Likelihoods)); commonInfo = new InferredGeneticContext(sampleName, negLog10PError, filters, attributes); + if ( log10Likelihoods != null ) + commonInfo.putAttribute(VCFConstants.PHRED_GENOTYPE_LIKELIHOODS_KEY, GenotypeLikelihoods.fromLog10Likelihoods(log10Likelihoods)); filtersWereAppliedToContext = filters != null; this.isPhased = isPhased; validate(); diff --git a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java index 9ca29d41c..81007f9ff 100644 --- a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java @@ -30,6 +30,7 @@ package org.broadinstitute.sting.utils.variantcontext; import net.sf.picard.reference.IndexedFastaSequenceFile; +import org.apache.log4j.Priority; import org.broadinstitute.sting.BaseTest; import org.broadinstitute.sting.gatk.refdata.tracks.FeatureManager; import org.broadinstitute.sting.utils.GenomeLocParser; @@ -39,6 +40,7 @@ import org.broadinstitute.sting.utils.fasta.CachingIndexedFastaSequenceFile; import org.testng.Assert; import org.testng.annotations.BeforeSuite; import org.testng.annotations.Test; +import org.testng.annotations.DataProvider; import java.io.File; import java.io.FileNotFoundException; @@ -47,8 +49,9 @@ import java.util.*; public class VariantContextUtilsUnitTest extends BaseTest { Allele Aref, T, delRef, ATC; - Genotype snp1, snp2, indel1; + Genotype ref1, snp1, snp2, indel1, indelref; private GenomeLocParser genomeLocParser; + VariantContext refVC, snpVC1, snpVC2, snpVC3, snpVC4, indelVC1, indelVC2, indelVC3; @BeforeSuite public void setup() { @@ -67,18 +70,133 @@ public class VariantContextUtilsUnitTest extends BaseTest { T = Allele.create("T"); ATC = Allele.create("ATC"); + ref1 = new Genotype("ref1", Arrays.asList(Aref, Aref), 5, new double[]{0, 5, 10}); snp1 = new Genotype("snp1", Arrays.asList(Aref,T), 10, new double[]{10, 0, 20}); snp2 = new Genotype("snp2", Arrays.asList(T,T), 15, new double[]{25, 15, 0}); + indelref = new Genotype("indelref", Arrays.asList(delRef,delRef), 25, new double[]{0, 25, 30}); indel1 = new Genotype("indel1", Arrays.asList(delRef,ATC), 20, new double[]{20, 0, 30}); + + refVC = makeVC("refvc", Arrays.asList(Aref), Arrays.asList(ref1)); + snpVC1 = makeVC("snpvc1", Arrays.asList(Aref, T), Arrays.asList(snp1)); + snpVC2 = makeVC("snpvc2", Arrays.asList(Aref, T), Arrays.asList(snp1, snp2)); + snpVC3 = makeVC("snpvc3", Arrays.asList(Aref, T), Arrays.asList(ref1, snp1)); + snpVC4 = makeVC("snpvc4", Arrays.asList(Aref, T), Arrays.asList(ref1, snp1, snp2)); + indelVC1 = makeVC("indelvc1", Arrays.asList(delRef), Arrays.asList(indelref)); + indelVC2 = makeVC("indelvc2", Arrays.asList(delRef, ATC), Arrays.asList(indel1)); + indelVC3 = makeVC("indelvc3", Arrays.asList(delRef, ATC), Arrays.asList(indelref, indel1)); } private VariantContext makeVC(String source, List alleles) { return makeVC(source, alleles, null, null); } + private VariantContext makeVC(String source, List alleles, Collection genotypes) { + return makeVC(source, alleles, genotypes, null); + } + private VariantContext makeVC(String source, List alleles, Collection genotypes, Set filters) { int start = 10; - int stop = alleles.contains(ATC) ? start + 3 : start; - return new VariantContext(source, "1", start, stop, alleles, genotypes, 1.0, filters, null); + int stop = start; // alleles.contains(ATC) ? start + 3 : start; + return new VariantContext(source, "1", start, stop, alleles, + VariantContext.genotypeCollectionToMap(new TreeMap(), genotypes), + 1.0, filters, null, (byte)'C'); } + + private class SimpleMergeTest extends TestDataProvider { + List inputVCs; + VariantContext expectedVC; + + private SimpleMergeTest(VariantContext... vcsArg) { + super(SimpleMergeTest.class); + LinkedList allVCs = new LinkedList(Arrays.asList(vcsArg)); + expectedVC = allVCs.pollLast(); + inputVCs = allVCs; + } + + public String toString() { + return String.format("SimpleMergeTest vc=%s expected=%s", inputVCs, expectedVC); + } + } + + @DataProvider(name = "simplemergedata") + public Object[][] createSimpleMergeData() { + // first, do no harm + new SimpleMergeTest(refVC, refVC); + new SimpleMergeTest(snpVC1, snpVC1); + new SimpleMergeTest(indelVC1, indelVC1); + new SimpleMergeTest(indelVC3, indelVC3); + + new SimpleMergeTest(refVC, snpVC1, snpVC3); + new SimpleMergeTest(snpVC1, snpVC2, snpVC2); + new SimpleMergeTest(refVC, snpVC2, snpVC4); + + new SimpleMergeTest(indelVC1, indelVC2, indelVC3); + new SimpleMergeTest(indelVC1, indelVC3, indelVC3); + new SimpleMergeTest(indelVC2, indelVC3, indelVC3); + + return SimpleMergeTest.getTests(SimpleMergeTest.class); + } + + private class SimpleMergeRSIDTest extends TestDataProvider { + List inputs; + String expected; + + private SimpleMergeRSIDTest(String... arg) { + super(SimpleMergeRSIDTest.class); + LinkedList allStrings = new LinkedList(Arrays.asList(arg)); + expected = allStrings.pollLast(); + inputs = allStrings; + } + + public String toString() { + return String.format("SimpleMergeRSIDTest vc=%s expected=%s", inputs, expected); + } + } + + @DataProvider(name = "simplemergersiddata") + public Object[][] createSimpleMergeRSIDData() { + new SimpleMergeRSIDTest(".", "."); + new SimpleMergeRSIDTest("rs1", "rs1"); + new SimpleMergeRSIDTest(".", "rs1", "rs1"); + new SimpleMergeRSIDTest("rs1", ".", "rs1"); + new SimpleMergeRSIDTest("rs1", "rs2", "rs1,rs2"); + new SimpleMergeRSIDTest("rs2", "rs1", "rs2,rs1"); + new SimpleMergeRSIDTest("rs2", "rs1", ".", "rs2,rs1"); + new SimpleMergeRSIDTest("rs2", ".", "rs1", "rs2,rs1"); + new SimpleMergeRSIDTest("rs1", ".", ".", "rs1"); + new SimpleMergeRSIDTest("rs1", "rs2", "rs3", "rs1,rs2,rs3"); + + return SimpleMergeRSIDTest.getTests(SimpleMergeRSIDTest.class); + } + + @Test(dataProvider = "simplemergersiddata") + public void testRSIDMerge(SimpleMergeRSIDTest cfg) { + List inputs = new ArrayList(); + for ( String id : cfg.inputs ) { + MutableVariantContext vc = new MutableVariantContext(snpVC1); + if ( ! id.equals(".") ) vc.setID(id); + inputs.add(vc); + + } + + VariantContext merged = myMerge(inputs); + Assert.assertEquals(merged.getID(), cfg.expected.equals(".") ? null : cfg.expected); + } + + private VariantContext myMerge(List inputs) { + List priority = new ArrayList(); + for ( VariantContext vc : inputs ) priority.add(vc.getSource()); + + return VariantContextUtils.simpleMerge(genomeLocParser, + inputs, priority, + VariantContextUtils.FilteredRecordMergeType.KEEP_IF_ANY_UNFILTERED, + VariantContextUtils.GenotypeMergeType.PRIORITIZE, true, false, "set", false, false); + } + + // todo -- add tests for subset merging, especially with correct PLs + // todo -- test priority list + // todo -- test FilteredRecordMergeType + // todo -- no annotate origin + // todo -- test set key + // todo -- test filtered are uncalled } From 15a410b24b3ed42d6d05698da33f3824cf6f14cf Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Thu, 22 Sep 2011 13:15:41 -0400 Subject: [PATCH 140/196] Updating md5 for fixed file --- .../sting/gatk/walkers/CNV/SymbolicAllelesIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/CNV/SymbolicAllelesIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/CNV/SymbolicAllelesIntegrationTest.java index b4a8498e1..1b2a6e82e 100644 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/CNV/SymbolicAllelesIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/CNV/SymbolicAllelesIntegrationTest.java @@ -33,7 +33,7 @@ public class SymbolicAllelesIntegrationTest extends WalkerTest { WalkerTestSpec spec = new WalkerTestSpec( baseTestString(b36KGReference, "symbolic_alleles_2.vcf"), 1, - Arrays.asList("6645babc8c7d46be0da223477c7b1291")); + Arrays.asList("3008d6f5044bc14801e5c58d985dec72")); executeTest("Test symbolic alleles mixed in with non-symbolic alleles", spec); } } From 9c1728416cf6773e510fc8c8843055189e1b03a4 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Thu, 22 Sep 2011 13:16:42 -0400 Subject: [PATCH 141/196] Revert "Updating md5 for fixed file" because this was fixed properly in unstable (but will break SnpEff if put into Stable). This reverts commit 6b4182c6ab3e214da4c73bc6f3687ac6d1c0b72c. --- .../sting/gatk/walkers/CNV/SymbolicAllelesIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/CNV/SymbolicAllelesIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/CNV/SymbolicAllelesIntegrationTest.java index 1b2a6e82e..b4a8498e1 100644 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/CNV/SymbolicAllelesIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/CNV/SymbolicAllelesIntegrationTest.java @@ -33,7 +33,7 @@ public class SymbolicAllelesIntegrationTest extends WalkerTest { WalkerTestSpec spec = new WalkerTestSpec( baseTestString(b36KGReference, "symbolic_alleles_2.vcf"), 1, - Arrays.asList("3008d6f5044bc14801e5c58d985dec72")); + Arrays.asList("6645babc8c7d46be0da223477c7b1291")); executeTest("Test symbolic alleles mixed in with non-symbolic alleles", spec); } } From 4e9020c9f7bd6fc429090213ad06c9a6b46ecc70 Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Thu, 22 Sep 2011 13:28:25 -0400 Subject: [PATCH 142/196] Fixed alignment start for hard clipping insertions --- .../sting/utils/clipreads/ClippingOp.java | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingOp.java b/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingOp.java index 951ab265c..0c2def1c6 100644 --- a/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingOp.java +++ b/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingOp.java @@ -432,25 +432,37 @@ public class ClippingOp { } private int calculateAlignmentStartShift(Cigar oldCigar, Cigar newCigar) { - int shift = 0; + int newShift = 0; + int oldShift = 0; + int deletionShift = 0; - // Rewind to previous start (by counting everything that was already clipped in this read) - for (CigarElement cigarElement : oldCigar.getCigarElements()) { - if (!cigarElement.getOperator().consumesReferenceBases()) - shift -= cigarElement.getLength(); - else - break; - } - - // Advance to new start (by counting everything new that has been clipped ) for (CigarElement cigarElement : newCigar.getCigarElements()) { - if (!cigarElement.getOperator().consumesReferenceBases()) - shift += cigarElement.getLength(); + if (cigarElement.getOperator() == CigarOperator.HARD_CLIP || cigarElement.getOperator() == CigarOperator.SOFT_CLIP) + newShift += cigarElement.getLength(); else break; } - return shift; + for (CigarElement cigarElement : oldCigar.getCigarElements()) { + if (cigarElement.getOperator() == CigarOperator.HARD_CLIP || cigarElement.getOperator() == CigarOperator.SOFT_CLIP ) + oldShift += Math.min(cigarElement.getLength(), newShift - oldShift); + else + break; + } + + int basesClipped = 0; + for (CigarElement cigarElement : oldCigar.getCigarElements()) { + if (basesClipped > newShift) // are we beyond the clipped region? + break; + + else if (cigarElement.getOperator() == CigarOperator.DELETION) // if this is a deletion, we have to adjust the starting shift + deletionShift += cigarElement.getLength(); + + else if (cigarElement.getOperator() != CigarOperator.INSERTION) // if it's not an insertion or deletion, than it counts as hard clipped base. + basesClipped += cigarElement.getLength(); + } + + return newShift - oldShift + deletionShift; } private int calculateHardClippingAlignmentShift(CigarElement cigarElement, int clippedLength) { From 80d7300de4acaa172e256e91f02a962d6479c6e0 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Thu, 22 Sep 2011 13:28:42 -0400 Subject: [PATCH 143/196] Unit test was passing in FORMAT as one of the sample names. There used to be a hack in the VCFHeader to check for this and remove it and I couldn't figure out why, but now I know. Hack was removed and now the unit test passes in only the sample names as per the contract. --- .../sting/utils/genotype/vcf/VCFWriterUnitTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/public/java/test/org/broadinstitute/sting/utils/genotype/vcf/VCFWriterUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/genotype/vcf/VCFWriterUnitTest.java index a8e6593b1..35c6a4993 100644 --- a/public/java/test/org/broadinstitute/sting/utils/genotype/vcf/VCFWriterUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/utils/genotype/vcf/VCFWriterUnitTest.java @@ -105,7 +105,6 @@ public class VCFWriterUnitTest extends BaseTest { public static VCFHeader createFakeHeader(Set metaData, Set additionalColumns) { metaData.add(new VCFHeaderLine(VCFHeaderVersion.VCF4_0.getFormatString(), VCFHeaderVersion.VCF4_0.getVersionString())); metaData.add(new VCFHeaderLine("two", "2")); - additionalColumns.add("FORMAT"); additionalColumns.add("extra1"); additionalColumns.add("extra2"); return new VCFHeader(metaData, additionalColumns); @@ -159,6 +158,6 @@ public class VCFWriterUnitTest extends BaseTest { Assert.assertTrue(additionalColumns.contains(key)); index++; } - Assert.assertEquals(index+1, additionalColumns.size() /* for the header field we don't see */); + Assert.assertEquals(index, additionalColumns.size()); } } From 1acf7945c544a923c5024b06a8322d351c889584 Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Thu, 22 Sep 2011 14:51:14 -0400 Subject: [PATCH 144/196] Fixed hard clipped cigar and alignment start * Hard clipped Cigar now includes all insertions that were hard clipped and not the deletions. * The alignment start is now recalculated according to the new hard clipped cigar representation --- .../sting/utils/clipreads/ClippingOp.java | 6 +++--- .../sting/utils/clipreads/ReadClipper.java | 18 +++++++++--------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingOp.java b/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingOp.java index 0c2def1c6..47ce8165c 100644 --- a/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingOp.java +++ b/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingOp.java @@ -458,7 +458,7 @@ public class ClippingOp { else if (cigarElement.getOperator() == CigarOperator.DELETION) // if this is a deletion, we have to adjust the starting shift deletionShift += cigarElement.getLength(); - else if (cigarElement.getOperator() != CigarOperator.INSERTION) // if it's not an insertion or deletion, than it counts as hard clipped base. + else basesClipped += cigarElement.getLength(); } @@ -474,8 +474,8 @@ public class ClippingOp { return -clippedLength; } - if (cigarElement.getOperator() == CigarOperator.DELETION) - return cigarElement.getLength(); +// if (cigarElement.getOperator() == CigarOperator.DELETION) +// return cigarElement.getLength(); return 0; } diff --git a/public/java/src/org/broadinstitute/sting/utils/clipreads/ReadClipper.java b/public/java/src/org/broadinstitute/sting/utils/clipreads/ReadClipper.java index 9b9ce4fdf..e2ecbe46b 100644 --- a/public/java/src/org/broadinstitute/sting/utils/clipreads/ReadClipper.java +++ b/public/java/src/org/broadinstitute/sting/utils/clipreads/ReadClipper.java @@ -71,21 +71,21 @@ public class ReadClipper { private SAMRecord hardClipByReferenceCoordinates(int refStart, int refStop) { int start = (refStart < 0) ? 0 : ReadUtils.getReadCoordinateForReferenceCoordinate(read, refStart); - int stop = (refStop < 0) ? read.getReadLength() - 1: ReadUtils.getReadCoordinateForReferenceCoordinate(read, refStop); + int stop = (refStop < 0) ? read.getReadLength() - 1 : ReadUtils.getReadCoordinateForReferenceCoordinate(read, refStop); - if (start < 0 || stop > read.getReadLength() - 1 + numDeletions(read)) + if (start < 0 || stop > read.getReadLength() - 1) throw new ReviewedStingException("Trying to clip before the start or after the end of a read"); - // TODO add check in the Hardclip function - if ( start > stop ) - stop = ReadUtils.getReadCoordinateForReferenceCoordinate(read, ReadUtils.getRefCoordSoftUnclippedEnd(read)); - + if ( start > stop ) { +// stop = ReadUtils.getReadCoordinateForReferenceCoordinate(read, ReadUtils.getRefCoordSoftUnclippedEnd(read)); + throw new ReviewedStingException("START > STOP -- this should never happen -- call Mauricio!"); + } //This tries to fix the bug where the deletion is counted a read base and as a result, the hardCLipper runs into //an endless loop when hard clipping the cigar string because the read coordinates are not covered by the read - stop -= numDeletions(read); - if ( start > stop ) - start -= numDeletions(read); +// stop -= numDeletions(read); +// if ( start > stop ) +// start -= numDeletions(read); //System.out.println("Clipping start/stop: " + start + "/" + stop); From 68da555932761567f79f0b3bfdbc808dcf72ca5e Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 22 Sep 2011 15:16:37 -0400 Subject: [PATCH 145/196] UnitTest for simpleMerge for alleles --- .../VariantContextUtilsUnitTest.java | 176 ++++++++++++------ 1 file changed, 118 insertions(+), 58 deletions(-) diff --git a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java index 81007f9ff..01d398c1a 100644 --- a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java @@ -48,10 +48,8 @@ import java.util.*; public class VariantContextUtilsUnitTest extends BaseTest { - Allele Aref, T, delRef, ATC; - Genotype ref1, snp1, snp2, indel1, indelref; + Allele Aref, T, C, delRef, ATC, ATCATC; private GenomeLocParser genomeLocParser; - VariantContext refVC, snpVC1, snpVC2, snpVC3, snpVC4, indelVC1, indelVC2, indelVC3; @BeforeSuite public void setup() { @@ -68,22 +66,9 @@ public class VariantContextUtilsUnitTest extends BaseTest { Aref = Allele.create("A", true); delRef = Allele.create("-", true); T = Allele.create("T"); + C = Allele.create("C"); ATC = Allele.create("ATC"); - - ref1 = new Genotype("ref1", Arrays.asList(Aref, Aref), 5, new double[]{0, 5, 10}); - snp1 = new Genotype("snp1", Arrays.asList(Aref,T), 10, new double[]{10, 0, 20}); - snp2 = new Genotype("snp2", Arrays.asList(T,T), 15, new double[]{25, 15, 0}); - indelref = new Genotype("indelref", Arrays.asList(delRef,delRef), 25, new double[]{0, 25, 30}); - indel1 = new Genotype("indel1", Arrays.asList(delRef,ATC), 20, new double[]{20, 0, 30}); - - refVC = makeVC("refvc", Arrays.asList(Aref), Arrays.asList(ref1)); - snpVC1 = makeVC("snpvc1", Arrays.asList(Aref, T), Arrays.asList(snp1)); - snpVC2 = makeVC("snpvc2", Arrays.asList(Aref, T), Arrays.asList(snp1, snp2)); - snpVC3 = makeVC("snpvc3", Arrays.asList(Aref, T), Arrays.asList(ref1, snp1)); - snpVC4 = makeVC("snpvc4", Arrays.asList(Aref, T), Arrays.asList(ref1, snp1, snp2)); - indelVC1 = makeVC("indelvc1", Arrays.asList(delRef), Arrays.asList(indelref)); - indelVC2 = makeVC("indelvc2", Arrays.asList(delRef, ATC), Arrays.asList(indel1)); - indelVC3 = makeVC("indelvc3", Arrays.asList(delRef, ATC), Arrays.asList(indelref, indel1)); + ATCATC = Allele.create("ATCATC"); } private VariantContext makeVC(String source, List alleles) { @@ -98,45 +83,124 @@ public class VariantContextUtilsUnitTest extends BaseTest { int start = 10; int stop = start; // alleles.contains(ATC) ? start + 3 : start; return new VariantContext(source, "1", start, stop, alleles, - VariantContext.genotypeCollectionToMap(new TreeMap(), genotypes), + genotypes == null ? null : VariantContext.genotypeCollectionToMap(new TreeMap(), genotypes), 1.0, filters, null, (byte)'C'); } - private class SimpleMergeTest extends TestDataProvider { - List inputVCs; - VariantContext expectedVC; + // -------------------------------------------------------------------------------- + // + // Test allele merging + // + // -------------------------------------------------------------------------------- - private SimpleMergeTest(VariantContext... vcsArg) { - super(SimpleMergeTest.class); - LinkedList allVCs = new LinkedList(Arrays.asList(vcsArg)); - expectedVC = allVCs.pollLast(); - inputVCs = allVCs; + private class MergeAllelesTest extends TestDataProvider { + List> inputs; + List expected; + + private MergeAllelesTest(List... arg) { + super(MergeAllelesTest.class); + LinkedList> all = new LinkedList>(Arrays.asList(arg)); + expected = all.pollLast(); + inputs = all; } public String toString() { - return String.format("SimpleMergeTest vc=%s expected=%s", inputVCs, expectedVC); + return String.format("MergeAllelesTest input=%s expected=%s", inputs, expected); } } - - @DataProvider(name = "simplemergedata") - public Object[][] createSimpleMergeData() { + @DataProvider(name = "mergeAlleles") + public Object[][] mergeAllelesData() { // first, do no harm - new SimpleMergeTest(refVC, refVC); - new SimpleMergeTest(snpVC1, snpVC1); - new SimpleMergeTest(indelVC1, indelVC1); - new SimpleMergeTest(indelVC3, indelVC3); + new MergeAllelesTest(Arrays.asList(Aref), + Arrays.asList(Aref)); - new SimpleMergeTest(refVC, snpVC1, snpVC3); - new SimpleMergeTest(snpVC1, snpVC2, snpVC2); - new SimpleMergeTest(refVC, snpVC2, snpVC4); + new MergeAllelesTest(Arrays.asList(Aref), + Arrays.asList(Aref), + Arrays.asList(Aref)); - new SimpleMergeTest(indelVC1, indelVC2, indelVC3); - new SimpleMergeTest(indelVC1, indelVC3, indelVC3); - new SimpleMergeTest(indelVC2, indelVC3, indelVC3); + new MergeAllelesTest(Arrays.asList(Aref), + Arrays.asList(Aref, T), + Arrays.asList(Aref, T)); - return SimpleMergeTest.getTests(SimpleMergeTest.class); + new MergeAllelesTest(Arrays.asList(Aref, C), + Arrays.asList(Aref, T), + Arrays.asList(Aref, C, T)); + + new MergeAllelesTest(Arrays.asList(Aref, T), + Arrays.asList(Aref, C), + Arrays.asList(Aref, C, T)); // sorted by allele + + new MergeAllelesTest(Arrays.asList(Aref, C, T), + Arrays.asList(Aref, C), + Arrays.asList(Aref, C, T)); + + new MergeAllelesTest(Arrays.asList(Aref, T, C), + Arrays.asList(Aref, C), + Arrays.asList(Aref, C, T)); // sorted by allele + + new MergeAllelesTest(Arrays.asList(delRef), + Arrays.asList(delRef)); // todo -- FIXME me GdA + + new MergeAllelesTest(Arrays.asList(delRef), + Arrays.asList(delRef, ATC), + Arrays.asList(delRef, ATC)); + + new MergeAllelesTest(Arrays.asList(delRef), + Arrays.asList(delRef, ATC, ATCATC), + Arrays.asList(delRef, ATC, ATCATC)); + + new MergeAllelesTest(Arrays.asList(delRef, ATCATC), + Arrays.asList(delRef, ATC, ATCATC), + Arrays.asList(delRef, ATC, ATCATC)); + + new MergeAllelesTest(Arrays.asList(delRef, ATC), + Arrays.asList(delRef, ATCATC), + Arrays.asList(delRef, ATC, ATCATC)); + + return MergeAllelesTest.getTests(MergeAllelesTest.class); } + @Test(dataProvider = "mergeAlleles") + public void testMergeAlleles(MergeAllelesTest cfg) { + final List priority = new ArrayList(); + final List inputs = new ArrayList(); + + int i = 0; + for ( final List alleles : cfg.inputs ) { + final String name = "vcf" + ++i; + priority.add(name); + inputs.add(makeVC(name, alleles)); + } + + final VariantContext merged = VariantContextUtils.simpleMerge(genomeLocParser, + inputs, priority, + VariantContextUtils.FilteredRecordMergeType.KEEP_IF_ANY_UNFILTERED, + VariantContextUtils.GenotypeMergeType.PRIORITIZE, false, false, "set", false, false); + Assert.assertEquals(merged.getAlleles(), cfg.expected); + } + +// VariantContext refVC, snpVC1, snpVC2, snpVC3, snpVC4, indelVC1, indelVC2, indelVC3; +// ref1 = new Genotype("ref1", Arrays.asList(Aref, Aref), 5, new double[]{0, 5, 10}); +// snp1 = new Genotype("snp1", Arrays.asList(Aref,T), 10, new double[]{10, 0, 20}); +// snp2 = new Genotype("snp2", Arrays.asList(T,T), 15, new double[]{25, 15, 0}); +// indelref = new Genotype("indelref", Arrays.asList(delRef,delRef), 25, new double[]{0, 25, 30}); +// indel1 = new Genotype("indel1", Arrays.asList(delRef,ATC), 20, new double[]{20, 0, 30}); +// +// refVC = makeVC("refvc", Arrays.asList(Aref), Arrays.asList(ref1)); +// snpVC1 = makeVC("snpvc1", Arrays.asList(Aref, T), Arrays.asList(snp1)); +// snpVC2 = makeVC("snpvc2", Arrays.asList(Aref, T), Arrays.asList(snp1, snp2)); +// snpVC3 = makeVC("snpvc3", Arrays.asList(Aref, T), Arrays.asList(ref1, snp1)); +// snpVC4 = makeVC("snpvc4", Arrays.asList(Aref, T), Arrays.asList(ref1, snp1, snp2)); +// indelVC1 = makeVC("indelvc1", Arrays.asList(delRef), Arrays.asList(indelref)); +// indelVC2 = makeVC("indelvc2", Arrays.asList(delRef, ATC), Arrays.asList(indel1)); +// indelVC3 = makeVC("indelvc3", Arrays.asList(delRef, ATC), Arrays.asList(indelref, indel1)); + + // -------------------------------------------------------------------------------- + // + // Test rsID merging + // + // -------------------------------------------------------------------------------- + private class SimpleMergeRSIDTest extends TestDataProvider { List inputs; String expected; @@ -156,10 +220,13 @@ public class VariantContextUtilsUnitTest extends BaseTest { @DataProvider(name = "simplemergersiddata") public Object[][] createSimpleMergeRSIDData() { new SimpleMergeRSIDTest(".", "."); + new SimpleMergeRSIDTest(".", ".", "."); new SimpleMergeRSIDTest("rs1", "rs1"); + new SimpleMergeRSIDTest("rs1", "rs1", "rs1"); new SimpleMergeRSIDTest(".", "rs1", "rs1"); new SimpleMergeRSIDTest("rs1", ".", "rs1"); new SimpleMergeRSIDTest("rs1", "rs2", "rs1,rs2"); + new SimpleMergeRSIDTest("rs1", "rs2", "rs1", "rs1,rs2"); // duplicates new SimpleMergeRSIDTest("rs2", "rs1", "rs2,rs1"); new SimpleMergeRSIDTest("rs2", "rs1", ".", "rs2,rs1"); new SimpleMergeRSIDTest("rs2", ".", "rs1", "rs2,rs1"); @@ -171,32 +238,25 @@ public class VariantContextUtilsUnitTest extends BaseTest { @Test(dataProvider = "simplemergersiddata") public void testRSIDMerge(SimpleMergeRSIDTest cfg) { - List inputs = new ArrayList(); - for ( String id : cfg.inputs ) { + final VariantContext snpVC1 = makeVC("snpvc1", Arrays.asList(Aref, T)); + final List inputs = new ArrayList(); + + for ( final String id : cfg.inputs ) { MutableVariantContext vc = new MutableVariantContext(snpVC1); if ( ! id.equals(".") ) vc.setID(id); inputs.add(vc); - } - VariantContext merged = myMerge(inputs); + final VariantContext merged = VariantContextUtils.simpleMerge(genomeLocParser, + inputs, null, + VariantContextUtils.FilteredRecordMergeType.KEEP_IF_ANY_UNFILTERED, + VariantContextUtils.GenotypeMergeType.UNSORTED, false, false, "set", false, false); Assert.assertEquals(merged.getID(), cfg.expected.equals(".") ? null : cfg.expected); } - private VariantContext myMerge(List inputs) { - List priority = new ArrayList(); - for ( VariantContext vc : inputs ) priority.add(vc.getSource()); - - return VariantContextUtils.simpleMerge(genomeLocParser, - inputs, priority, - VariantContextUtils.FilteredRecordMergeType.KEEP_IF_ANY_UNFILTERED, - VariantContextUtils.GenotypeMergeType.PRIORITIZE, true, false, "set", false, false); - } - // todo -- add tests for subset merging, especially with correct PLs - // todo -- test priority list + // todo -- test priority list: intersection, filtered in all, reference in all, X-filteredInX, X // todo -- test FilteredRecordMergeType // todo -- no annotate origin // todo -- test set key - // todo -- test filtered are uncalled } From 39b54211d079dc2c70a2a9b4a38b9e08880b24df Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Thu, 22 Sep 2011 15:46:55 -0400 Subject: [PATCH 147/196] Fixed hard clipping soft clipped bases after hard clips if soft clipped bases were after a hard clipped section of the read, the hard clip was clipping the left soft clip tail as if it were a right tail. Mayhem. --- .../org/broadinstitute/sting/utils/clipreads/ReadClipper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/clipreads/ReadClipper.java b/public/java/src/org/broadinstitute/sting/utils/clipreads/ReadClipper.java index e2ecbe46b..d0e7f8371 100644 --- a/public/java/src/org/broadinstitute/sting/utils/clipreads/ReadClipper.java +++ b/public/java/src/org/broadinstitute/sting/utils/clipreads/ReadClipper.java @@ -145,7 +145,7 @@ public class ReadClipper { cutLeft = readIndex + cigarElement.getLength() - 1; } } - else + else if (cigarElement.getOperator() != CigarOperator.HARD_CLIP) rightTail = true; if (cigarElement.getOperator().consumesReadBases()) From 46ca33dc047d6a52406142d9dc323185938d7dab Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 22 Sep 2011 17:04:32 -0400 Subject: [PATCH 150/196] TestDataProvider now can be named --- .../test/org/broadinstitute/sting/BaseTest.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/public/java/test/org/broadinstitute/sting/BaseTest.java b/public/java/test/org/broadinstitute/sting/BaseTest.java index 63faf1ab9..35a81770d 100755 --- a/public/java/test/org/broadinstitute/sting/BaseTest.java +++ b/public/java/test/org/broadinstitute/sting/BaseTest.java @@ -132,15 +132,21 @@ public abstract class BaseTest { */ public static class TestDataProvider { private static final Map> tests = new HashMap>(); + private final String name; /** * Create a new TestDataProvider instance bound to the class variable C * @param c */ - public TestDataProvider(Class c) { + public TestDataProvider(Class c, String name) { if ( ! tests.containsKey(c) ) tests.put(c, new ArrayList()); tests.get(c).add(this); + this.name = name; + } + + public TestDataProvider(Class c) { + this(c, ""); } /** @@ -153,6 +159,11 @@ public abstract class BaseTest { for ( Object x : tests.get(c) ) params2.add(new Object[]{x}); return params2.toArray(new Object[][]{}); } + + @Override + public String toString() { + return "TestDataProvider("+name+")"; + } } /** From 5cf82f923615b2dae0dee1b80f043fadacdfbc05 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 22 Sep 2011 17:05:12 -0400 Subject: [PATCH 151/196] simpleMerge UnitTest tests filtered VC merging --- .../variantcontext/VariantContextUtils.java | 14 +- .../VariantContextUtilsUnitTest.java | 141 ++++++++++++++++-- 2 files changed, 135 insertions(+), 20 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java index ca9c71eba..03fe969b5 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java @@ -44,6 +44,11 @@ import java.io.Serializable; import java.util.*; public class VariantContextUtils { + public final static String MERGE_INTERSECTION = "Intersection"; + public final static String MERGE_FILTER_IN_ALL = "FilteredInAll"; + public final static String MERGE_REF_IN_ALL = "ReferenceInAll"; + public final static String MERGE_FILTER_PREFIX = "filterIn"; + final public static JexlEngine engine = new JexlEngine(); static { engine.setSilent(false); // will throw errors now for selects that don't evaluate properly @@ -609,19 +614,20 @@ public class VariantContextUtils { if ( filteredRecordMergeType == FilteredRecordMergeType.KEEP_IF_ANY_UNFILTERED && nFiltered != VCs.size() ) filters.clear(); + if ( annotateOrigin ) { // we care about where the call came from String setValue; if ( nFiltered == 0 && variantSources.size() == priorityListOfVCs.size() ) // nothing was unfiltered - setValue = "Intersection"; + setValue = MERGE_INTERSECTION; else if ( nFiltered == VCs.size() ) // everything was filtered out - setValue = "FilteredInAll"; + setValue = MERGE_FILTER_IN_ALL; else if ( variantSources.isEmpty() ) // everyone was reference - setValue = "ReferenceInAll"; + setValue = MERGE_REF_IN_ALL; else { LinkedHashSet s = new LinkedHashSet(); for ( VariantContext vc : VCs ) if ( vc.isVariant() ) - s.add( vc.isFiltered() ? "filterIn" + vc.getSource() : vc.getSource() ); + s.add( vc.isFiltered() ? MERGE_FILTER_PREFIX + vc.getSource() : vc.getSource() ); setValue = Utils.join("-", s); } diff --git a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java index 01d398c1a..ff26d7245 100644 --- a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java @@ -41,6 +41,7 @@ import org.testng.Assert; import org.testng.annotations.BeforeSuite; import org.testng.annotations.Test; import org.testng.annotations.DataProvider; +import org.yaml.snakeyaml.Yaml; import java.io.File; import java.io.FileNotFoundException; @@ -75,6 +76,14 @@ public class VariantContextUtilsUnitTest extends BaseTest { return makeVC(source, alleles, null, null); } + private VariantContext makeVC(String source, List alleles, String filter) { + return makeVC(source, alleles, filter.equals(".") ? null : new HashSet(Arrays.asList(filter))); + } + + private VariantContext makeVC(String source, List alleles, Set filters) { + return makeVC(source, alleles, null, filters); + } + private VariantContext makeVC(String source, List alleles, Collection genotypes) { return makeVC(source, alleles, genotypes, null); } @@ -179,22 +188,6 @@ public class VariantContextUtilsUnitTest extends BaseTest { Assert.assertEquals(merged.getAlleles(), cfg.expected); } -// VariantContext refVC, snpVC1, snpVC2, snpVC3, snpVC4, indelVC1, indelVC2, indelVC3; -// ref1 = new Genotype("ref1", Arrays.asList(Aref, Aref), 5, new double[]{0, 5, 10}); -// snp1 = new Genotype("snp1", Arrays.asList(Aref,T), 10, new double[]{10, 0, 20}); -// snp2 = new Genotype("snp2", Arrays.asList(T,T), 15, new double[]{25, 15, 0}); -// indelref = new Genotype("indelref", Arrays.asList(delRef,delRef), 25, new double[]{0, 25, 30}); -// indel1 = new Genotype("indel1", Arrays.asList(delRef,ATC), 20, new double[]{20, 0, 30}); -// -// refVC = makeVC("refvc", Arrays.asList(Aref), Arrays.asList(ref1)); -// snpVC1 = makeVC("snpvc1", Arrays.asList(Aref, T), Arrays.asList(snp1)); -// snpVC2 = makeVC("snpvc2", Arrays.asList(Aref, T), Arrays.asList(snp1, snp2)); -// snpVC3 = makeVC("snpvc3", Arrays.asList(Aref, T), Arrays.asList(ref1, snp1)); -// snpVC4 = makeVC("snpvc4", Arrays.asList(Aref, T), Arrays.asList(ref1, snp1, snp2)); -// indelVC1 = makeVC("indelvc1", Arrays.asList(delRef), Arrays.asList(indelref)); -// indelVC2 = makeVC("indelvc2", Arrays.asList(delRef, ATC), Arrays.asList(indel1)); -// indelVC3 = makeVC("indelvc3", Arrays.asList(delRef, ATC), Arrays.asList(indelref, indel1)); - // -------------------------------------------------------------------------------- // // Test rsID merging @@ -254,6 +247,122 @@ public class VariantContextUtilsUnitTest extends BaseTest { Assert.assertEquals(merged.getID(), cfg.expected.equals(".") ? null : cfg.expected); } + // -------------------------------------------------------------------------------- + // + // Test filtered merging + // + // -------------------------------------------------------------------------------- + + private class MergeFilteredTest extends TestDataProvider { + List inputs; + VariantContext expected; + String setExpected; + VariantContextUtils.FilteredRecordMergeType type; + + + private MergeFilteredTest(String name, VariantContext input1, VariantContext input2, VariantContext expected, String setExpected) { + this(name, input1, input2, expected, VariantContextUtils.FilteredRecordMergeType.KEEP_IF_ANY_UNFILTERED, setExpected); + } + + private MergeFilteredTest(String name, VariantContext input1, VariantContext input2, VariantContext expected, VariantContextUtils.FilteredRecordMergeType type, String setExpected) { + super(MergeFilteredTest.class, name); + LinkedList all = new LinkedList(Arrays.asList(input1, input2)); + this.expected = expected; + this.type = type; + inputs = all; + this.setExpected = setExpected; + } + + public String toString() { + return String.format("%s input=%s expected=%s", super.toString(), inputs, expected); + } + } + + @DataProvider(name = "mergeFiltered") + public Object[][] mergeFilteredData() { + new MergeFilteredTest("AllPass", + makeVC("1", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), + makeVC("2", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), + makeVC("3", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), + VariantContextUtils.MERGE_INTERSECTION); + + new MergeFilteredTest("noFilters", + makeVC("1", Arrays.asList(Aref, T), "."), + makeVC("2", Arrays.asList(Aref, T), "."), + makeVC("3", Arrays.asList(Aref, T), "."), + VariantContextUtils.MERGE_INTERSECTION); + + new MergeFilteredTest("oneFiltered", + makeVC("1", Arrays.asList(Aref, T), "."), + makeVC("2", Arrays.asList(Aref, T), "FAIL"), + makeVC("3", Arrays.asList(Aref, T), "."), + String.format("1-%s2", VariantContextUtils.MERGE_FILTER_PREFIX)); + + new MergeFilteredTest("onePassOneFail", + makeVC("1", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), + makeVC("2", Arrays.asList(Aref, T), "FAIL"), + makeVC("3", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), + String.format("1-%s2", VariantContextUtils.MERGE_FILTER_PREFIX)); + + new MergeFilteredTest("FailOneUnfiltered", + makeVC("1", Arrays.asList(Aref, T), "FAIL"), + makeVC("2", Arrays.asList(Aref, T), "."), + makeVC("3", Arrays.asList(Aref, T), "."), + String.format("%s1-2", VariantContextUtils.MERGE_FILTER_PREFIX)); + + new MergeFilteredTest("AllFiltered", + makeVC("1", Arrays.asList(Aref, T), "FAIL"), + makeVC("2", Arrays.asList(Aref, T), "FAIL"), + makeVC("3", Arrays.asList(Aref, T), "FAIL"), + VariantContextUtils.MERGE_FILTER_IN_ALL); + + // test ALL vs. ANY + new MergeFilteredTest("OneFailAllUnfilteredArg", + makeVC("1", Arrays.asList(Aref, T), "FAIL"), + makeVC("2", Arrays.asList(Aref, T), "."), + makeVC("3", Arrays.asList(Aref, T), "FAIL"), + VariantContextUtils.FilteredRecordMergeType.KEEP_IF_ALL_UNFILTERED, + String.format("%s1-2", VariantContextUtils.MERGE_FILTER_PREFIX)); + + // test excluding allele in filtered record + new MergeFilteredTest("DontIncludeAlleleOfFilteredRecords", + makeVC("1", Arrays.asList(Aref, T), "."), + makeVC("2", Arrays.asList(Aref, T), "FAIL"), + makeVC("3", Arrays.asList(Aref, T), "."), + String.format("1-%s2", VariantContextUtils.MERGE_FILTER_PREFIX)); + + // promotion of site from unfiltered to PASSES + new MergeFilteredTest("UnfilteredPlusPassIsPass", + makeVC("1", Arrays.asList(Aref, T), "."), + makeVC("2", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), + makeVC("3", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), + VariantContextUtils.MERGE_INTERSECTION); + + return MergeFilteredTest.getTests(MergeFilteredTest.class); + } + + @Test(dataProvider = "mergeFiltered") + public void testMergeFiltered(MergeFilteredTest cfg) { + final List priority = new ArrayList(); + + for ( final VariantContext vc : cfg.inputs ) { + priority.add(vc.getSource()); + } + + final VariantContext merged = VariantContextUtils.simpleMerge(genomeLocParser, + cfg.inputs, priority, cfg.type, VariantContextUtils.GenotypeMergeType.PRIORITIZE, true, false, "set", false, false); + + // test alleles are equal + Assert.assertEquals(merged.getAlleles(), cfg.expected.getAlleles()); + + // test set field + Assert.assertEquals(merged.getAttribute("set"), cfg.setExpected); + + // test filter field + Assert.assertEquals(merged.getFilters(), cfg.expected.getFilters()); + } + + // todo -- add tests for subset merging, especially with correct PLs // todo -- test priority list: intersection, filtered in all, reference in all, X-filteredInX, X // todo -- test FilteredRecordMergeType From 30ab3af0c88d4d22a6d894c8d5e45863b4957445 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 22 Sep 2011 17:14:59 -0400 Subject: [PATCH 152/196] A few more simpleMerge UnitTest tests for filtered vcs --- .../VariantContextUtilsUnitTest.java | 25 ++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java index ff26d7245..070fdaca4 100644 --- a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java @@ -304,12 +304,6 @@ public class VariantContextUtilsUnitTest extends BaseTest { makeVC("3", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), String.format("1-%s2", VariantContextUtils.MERGE_FILTER_PREFIX)); - new MergeFilteredTest("FailOneUnfiltered", - makeVC("1", Arrays.asList(Aref, T), "FAIL"), - makeVC("2", Arrays.asList(Aref, T), "."), - makeVC("3", Arrays.asList(Aref, T), "."), - String.format("%s1-2", VariantContextUtils.MERGE_FILTER_PREFIX)); - new MergeFilteredTest("AllFiltered", makeVC("1", Arrays.asList(Aref, T), "FAIL"), makeVC("2", Arrays.asList(Aref, T), "FAIL"), @@ -317,6 +311,13 @@ public class VariantContextUtilsUnitTest extends BaseTest { VariantContextUtils.MERGE_FILTER_IN_ALL); // test ALL vs. ANY + new MergeFilteredTest("FailOneUnfiltered", + makeVC("1", Arrays.asList(Aref, T), "FAIL"), + makeVC("2", Arrays.asList(Aref, T), "."), + makeVC("3", Arrays.asList(Aref, T), "."), + VariantContextUtils.FilteredRecordMergeType.KEEP_IF_ANY_UNFILTERED, + String.format("%s1-2", VariantContextUtils.MERGE_FILTER_PREFIX)); + new MergeFilteredTest("OneFailAllUnfilteredArg", makeVC("1", Arrays.asList(Aref, T), "FAIL"), makeVC("2", Arrays.asList(Aref, T), "."), @@ -338,6 +339,18 @@ public class VariantContextUtilsUnitTest extends BaseTest { makeVC("3", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), VariantContextUtils.MERGE_INTERSECTION); + new MergeFilteredTest("RefInAll", + makeVC("1", Arrays.asList(Aref), VariantContext.PASSES_FILTERS), + makeVC("2", Arrays.asList(Aref), VariantContext.PASSES_FILTERS), + makeVC("3", Arrays.asList(Aref), VariantContext.PASSES_FILTERS), + VariantContextUtils.MERGE_REF_IN_ALL); + + new MergeFilteredTest("RefInOne", + makeVC("1", Arrays.asList(Aref), VariantContext.PASSES_FILTERS), + makeVC("2", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), + makeVC("3", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), + "2"); + return MergeFilteredTest.getTests(MergeFilteredTest.class); } From dab7232e9af623a3d085988bac751ee4df229748 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 22 Sep 2011 17:26:11 -0400 Subject: [PATCH 153/196] simpleMerge UnitTest for not annotating and annotating to different info key --- .../VariantContextUtilsUnitTest.java | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java index 070fdaca4..77e4391a3 100644 --- a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java @@ -375,10 +375,26 @@ public class VariantContextUtilsUnitTest extends BaseTest { Assert.assertEquals(merged.getFilters(), cfg.expected.getFilters()); } + @Test + public void testAnnotationSet() { + for ( final boolean annotate : Arrays.asList(true, false)) { + for ( final String set : Arrays.asList("set", "combine", "x")) { + final List priority = Arrays.asList("1", "2"); + VariantContext vc1 = makeVC("1", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS); + VariantContext vc2 = makeVC("2", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS); + + final VariantContext merged = VariantContextUtils.simpleMerge(genomeLocParser, + Arrays.asList(vc1, vc2), priority, VariantContextUtils.FilteredRecordMergeType.KEEP_IF_ANY_UNFILTERED, + VariantContextUtils.GenotypeMergeType.PRIORITIZE, annotate, false, set, false, false); + + if ( annotate ) + Assert.assertEquals(merged.getAttribute(set), VariantContextUtils.MERGE_INTERSECTION); + else + Assert.assertFalse(merged.hasAttribute(set)); + } + } + } // todo -- add tests for subset merging, especially with correct PLs - // todo -- test priority list: intersection, filtered in all, reference in all, X-filteredInX, X - // todo -- test FilteredRecordMergeType - // todo -- no annotate origin - // todo -- test set key + // todo -- test priority list } From a8e0fb26ea309a6620d540310f1224fb08ec7440 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Fri, 23 Sep 2011 07:33:20 -0400 Subject: [PATCH 154/196] Updating md5 because the file changed --- .../sting/gatk/walkers/CNV/SymbolicAllelesIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/CNV/SymbolicAllelesIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/CNV/SymbolicAllelesIntegrationTest.java index b4a8498e1..1b2a6e82e 100644 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/CNV/SymbolicAllelesIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/CNV/SymbolicAllelesIntegrationTest.java @@ -33,7 +33,7 @@ public class SymbolicAllelesIntegrationTest extends WalkerTest { WalkerTestSpec spec = new WalkerTestSpec( baseTestString(b36KGReference, "symbolic_alleles_2.vcf"), 1, - Arrays.asList("6645babc8c7d46be0da223477c7b1291")); + Arrays.asList("3008d6f5044bc14801e5c58d985dec72")); executeTest("Test symbolic alleles mixed in with non-symbolic alleles", spec); } } From 4397ce8653eab67546e261faef5ebd5619057b79 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 23 Sep 2011 08:23:46 -0400 Subject: [PATCH 156/196] Moved removePLs to VariantContextUtils --- .../sting/utils/variantcontext/Genotype.java | 7 ------- .../sting/utils/variantcontext/VariantContextUtils.java | 9 ++++++++- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/Genotype.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/Genotype.java index 2e233c18e..fd8c70abe 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/Genotype.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/Genotype.java @@ -74,13 +74,6 @@ public class Genotype { return new Genotype(g.getSampleName(), g.getAlleles(), g.getNegLog10PError(), g.filtersWereApplied() ? g.getFilters() : null, attributes, g.isPhased()); } - public static Genotype removePLs(Genotype g) { - Map attrs = new HashMap(g.getAttributes()); - attrs.remove(VCFConstants.PHRED_GENOTYPE_LIKELIHOODS_KEY); - attrs.remove(VCFConstants.GENOTYPE_LIKELIHOODS_KEY); - return new Genotype(g.getSampleName(), g.getAlleles(), g.getNegLog10PError(), g.filtersWereApplied() ? g.getFilters() : null, attrs, g.isPhased()); - } - public static Genotype modifyAlleles(Genotype g, List alleles) { return new Genotype(g.getSampleName(), alleles, g.getNegLog10PError(), g.filtersWereApplied() ? g.getFilters() : null, g.getAttributes(), g.isPhased()); } diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java index 03fe969b5..93d4f6f5c 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java @@ -159,6 +159,13 @@ public class VariantContextUtils { return "%." + precision + "f"; } + public static Genotype removePLs(Genotype g) { + Map attrs = new HashMap(g.getAttributes()); + attrs.remove(VCFConstants.PHRED_GENOTYPE_LIKELIHOODS_KEY); + attrs.remove(VCFConstants.GENOTYPE_LIKELIHOODS_KEY); + return new Genotype(g.getSampleName(), g.getAlleles(), g.getNegLog10PError(), g.filtersWereApplied() ? g.getFilters() : null, attrs, g.isPhased()); + } + /** * A simple but common wrapper for matching VariantContext objects using JEXL expressions */ @@ -740,7 +747,7 @@ public class VariantContextUtils { Map newGs = new HashMap(genotypes.size()); for ( Map.Entry g : genotypes.entrySet() ) { - newGs.put(g.getKey(), g.getValue().hasLikelihoods() ? Genotype.removePLs(g.getValue()) : g.getValue()); + newGs.put(g.getKey(), g.getValue().hasLikelihoods() ? removePLs(g.getValue()) : g.getValue()); } return newGs; From a9f073fa68f500fbad4e068b75e3ff924a0d7d66 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 23 Sep 2011 08:24:49 -0400 Subject: [PATCH 157/196] Genotype merging unit tests for simpleMerge -- Remaining TODOs are all for GdA --- .../VariantContextUtilsUnitTest.java | 319 +++++++++++++----- 1 file changed, 234 insertions(+), 85 deletions(-) diff --git a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java index 77e4391a3..c1025a7de 100644 --- a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java @@ -21,14 +21,8 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ - -// our package package org.broadinstitute.sting.utils.variantcontext; - -// the imports for unit testing. - - import net.sf.picard.reference.IndexedFastaSequenceFile; import org.apache.log4j.Priority; import org.broadinstitute.sting.BaseTest; @@ -47,7 +41,6 @@ import java.io.File; import java.io.FileNotFoundException; import java.util.*; - public class VariantContextUtilsUnitTest extends BaseTest { Allele Aref, T, C, delRef, ATC, ATCATC; private GenomeLocParser genomeLocParser; @@ -72,10 +65,22 @@ public class VariantContextUtilsUnitTest extends BaseTest { ATCATC = Allele.create("ATCATC"); } + private Genotype makeG(String sample, Allele a1, Allele a2) { + return new Genotype(sample, Arrays.asList(a1, a2)); + } + + private Genotype makeG(String sample, Allele a1, Allele a2, double log10pError) { + return new Genotype(sample, Arrays.asList(a1, a2), log10pError); + } + private VariantContext makeVC(String source, List alleles) { return makeVC(source, alleles, null, null); } + private VariantContext makeVC(String source, List alleles, Genotype... g1) { + return makeVC(source, alleles, Arrays.asList(g1)); + } + private VariantContext makeVC(String source, List alleles, String filter) { return makeVC(source, alleles, filter.equals(".") ? null : new HashSet(Arrays.asList(filter))); } @@ -121,66 +126,66 @@ public class VariantContextUtilsUnitTest extends BaseTest { public Object[][] mergeAllelesData() { // first, do no harm new MergeAllelesTest(Arrays.asList(Aref), - Arrays.asList(Aref)); + Arrays.asList(Aref)); new MergeAllelesTest(Arrays.asList(Aref), - Arrays.asList(Aref), - Arrays.asList(Aref)); + Arrays.asList(Aref), + Arrays.asList(Aref)); new MergeAllelesTest(Arrays.asList(Aref), - Arrays.asList(Aref, T), - Arrays.asList(Aref, T)); + Arrays.asList(Aref, T), + Arrays.asList(Aref, T)); new MergeAllelesTest(Arrays.asList(Aref, C), - Arrays.asList(Aref, T), - Arrays.asList(Aref, C, T)); + Arrays.asList(Aref, T), + Arrays.asList(Aref, C, T)); new MergeAllelesTest(Arrays.asList(Aref, T), - Arrays.asList(Aref, C), - Arrays.asList(Aref, C, T)); // sorted by allele + Arrays.asList(Aref, C), + Arrays.asList(Aref, C, T)); // sorted by allele new MergeAllelesTest(Arrays.asList(Aref, C, T), - Arrays.asList(Aref, C), - Arrays.asList(Aref, C, T)); + Arrays.asList(Aref, C), + Arrays.asList(Aref, C, T)); new MergeAllelesTest(Arrays.asList(Aref, T, C), - Arrays.asList(Aref, C), - Arrays.asList(Aref, C, T)); // sorted by allele + Arrays.asList(Aref, C), + Arrays.asList(Aref, C, T)); // sorted by allele new MergeAllelesTest(Arrays.asList(delRef), - Arrays.asList(delRef)); // todo -- FIXME me GdA + Arrays.asList(delRef)); // todo -- FIXME me GdA new MergeAllelesTest(Arrays.asList(delRef), - Arrays.asList(delRef, ATC), - Arrays.asList(delRef, ATC)); + Arrays.asList(delRef, ATC), + Arrays.asList(delRef, ATC)); new MergeAllelesTest(Arrays.asList(delRef), - Arrays.asList(delRef, ATC, ATCATC), - Arrays.asList(delRef, ATC, ATCATC)); + Arrays.asList(delRef, ATC, ATCATC), + Arrays.asList(delRef, ATC, ATCATC)); new MergeAllelesTest(Arrays.asList(delRef, ATCATC), - Arrays.asList(delRef, ATC, ATCATC), - Arrays.asList(delRef, ATC, ATCATC)); + Arrays.asList(delRef, ATC, ATCATC), + Arrays.asList(delRef, ATC, ATCATC)); new MergeAllelesTest(Arrays.asList(delRef, ATC), - Arrays.asList(delRef, ATCATC), - Arrays.asList(delRef, ATC, ATCATC)); + Arrays.asList(delRef, ATCATC), + Arrays.asList(delRef, ATC, ATCATC)); return MergeAllelesTest.getTests(MergeAllelesTest.class); } @Test(dataProvider = "mergeAlleles") public void testMergeAlleles(MergeAllelesTest cfg) { - final List priority = new ArrayList(); final List inputs = new ArrayList(); int i = 0; for ( final List alleles : cfg.inputs ) { final String name = "vcf" + ++i; - priority.add(name); inputs.add(makeVC(name, alleles)); } + final List priority = vcs2priority(inputs); + final VariantContext merged = VariantContextUtils.simpleMerge(genomeLocParser, inputs, priority, VariantContextUtils.FilteredRecordMergeType.KEEP_IF_ANY_UNFILTERED, @@ -281,87 +286,82 @@ public class VariantContextUtilsUnitTest extends BaseTest { @DataProvider(name = "mergeFiltered") public Object[][] mergeFilteredData() { new MergeFilteredTest("AllPass", - makeVC("1", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), - makeVC("2", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), - makeVC("3", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), - VariantContextUtils.MERGE_INTERSECTION); + makeVC("1", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), + makeVC("2", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), + makeVC("3", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), + VariantContextUtils.MERGE_INTERSECTION); new MergeFilteredTest("noFilters", - makeVC("1", Arrays.asList(Aref, T), "."), - makeVC("2", Arrays.asList(Aref, T), "."), - makeVC("3", Arrays.asList(Aref, T), "."), - VariantContextUtils.MERGE_INTERSECTION); + makeVC("1", Arrays.asList(Aref, T), "."), + makeVC("2", Arrays.asList(Aref, T), "."), + makeVC("3", Arrays.asList(Aref, T), "."), + VariantContextUtils.MERGE_INTERSECTION); new MergeFilteredTest("oneFiltered", - makeVC("1", Arrays.asList(Aref, T), "."), - makeVC("2", Arrays.asList(Aref, T), "FAIL"), - makeVC("3", Arrays.asList(Aref, T), "."), - String.format("1-%s2", VariantContextUtils.MERGE_FILTER_PREFIX)); + makeVC("1", Arrays.asList(Aref, T), "."), + makeVC("2", Arrays.asList(Aref, T), "FAIL"), + makeVC("3", Arrays.asList(Aref, T), "."), + String.format("1-%s2", VariantContextUtils.MERGE_FILTER_PREFIX)); new MergeFilteredTest("onePassOneFail", - makeVC("1", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), - makeVC("2", Arrays.asList(Aref, T), "FAIL"), - makeVC("3", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), - String.format("1-%s2", VariantContextUtils.MERGE_FILTER_PREFIX)); + makeVC("1", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), + makeVC("2", Arrays.asList(Aref, T), "FAIL"), + makeVC("3", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), + String.format("1-%s2", VariantContextUtils.MERGE_FILTER_PREFIX)); new MergeFilteredTest("AllFiltered", - makeVC("1", Arrays.asList(Aref, T), "FAIL"), - makeVC("2", Arrays.asList(Aref, T), "FAIL"), - makeVC("3", Arrays.asList(Aref, T), "FAIL"), - VariantContextUtils.MERGE_FILTER_IN_ALL); + makeVC("1", Arrays.asList(Aref, T), "FAIL"), + makeVC("2", Arrays.asList(Aref, T), "FAIL"), + makeVC("3", Arrays.asList(Aref, T), "FAIL"), + VariantContextUtils.MERGE_FILTER_IN_ALL); // test ALL vs. ANY new MergeFilteredTest("FailOneUnfiltered", - makeVC("1", Arrays.asList(Aref, T), "FAIL"), - makeVC("2", Arrays.asList(Aref, T), "."), - makeVC("3", Arrays.asList(Aref, T), "."), - VariantContextUtils.FilteredRecordMergeType.KEEP_IF_ANY_UNFILTERED, - String.format("%s1-2", VariantContextUtils.MERGE_FILTER_PREFIX)); + makeVC("1", Arrays.asList(Aref, T), "FAIL"), + makeVC("2", Arrays.asList(Aref, T), "."), + makeVC("3", Arrays.asList(Aref, T), "."), + VariantContextUtils.FilteredRecordMergeType.KEEP_IF_ANY_UNFILTERED, + String.format("%s1-2", VariantContextUtils.MERGE_FILTER_PREFIX)); new MergeFilteredTest("OneFailAllUnfilteredArg", - makeVC("1", Arrays.asList(Aref, T), "FAIL"), - makeVC("2", Arrays.asList(Aref, T), "."), - makeVC("3", Arrays.asList(Aref, T), "FAIL"), - VariantContextUtils.FilteredRecordMergeType.KEEP_IF_ALL_UNFILTERED, - String.format("%s1-2", VariantContextUtils.MERGE_FILTER_PREFIX)); + makeVC("1", Arrays.asList(Aref, T), "FAIL"), + makeVC("2", Arrays.asList(Aref, T), "."), + makeVC("3", Arrays.asList(Aref, T), "FAIL"), + VariantContextUtils.FilteredRecordMergeType.KEEP_IF_ALL_UNFILTERED, + String.format("%s1-2", VariantContextUtils.MERGE_FILTER_PREFIX)); // test excluding allele in filtered record new MergeFilteredTest("DontIncludeAlleleOfFilteredRecords", - makeVC("1", Arrays.asList(Aref, T), "."), - makeVC("2", Arrays.asList(Aref, T), "FAIL"), - makeVC("3", Arrays.asList(Aref, T), "."), - String.format("1-%s2", VariantContextUtils.MERGE_FILTER_PREFIX)); + makeVC("1", Arrays.asList(Aref, T), "."), + makeVC("2", Arrays.asList(Aref, T), "FAIL"), + makeVC("3", Arrays.asList(Aref, T), "."), + String.format("1-%s2", VariantContextUtils.MERGE_FILTER_PREFIX)); // promotion of site from unfiltered to PASSES new MergeFilteredTest("UnfilteredPlusPassIsPass", - makeVC("1", Arrays.asList(Aref, T), "."), - makeVC("2", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), - makeVC("3", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), - VariantContextUtils.MERGE_INTERSECTION); + makeVC("1", Arrays.asList(Aref, T), "."), + makeVC("2", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), + makeVC("3", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), + VariantContextUtils.MERGE_INTERSECTION); new MergeFilteredTest("RefInAll", - makeVC("1", Arrays.asList(Aref), VariantContext.PASSES_FILTERS), - makeVC("2", Arrays.asList(Aref), VariantContext.PASSES_FILTERS), - makeVC("3", Arrays.asList(Aref), VariantContext.PASSES_FILTERS), - VariantContextUtils.MERGE_REF_IN_ALL); + makeVC("1", Arrays.asList(Aref), VariantContext.PASSES_FILTERS), + makeVC("2", Arrays.asList(Aref), VariantContext.PASSES_FILTERS), + makeVC("3", Arrays.asList(Aref), VariantContext.PASSES_FILTERS), + VariantContextUtils.MERGE_REF_IN_ALL); new MergeFilteredTest("RefInOne", - makeVC("1", Arrays.asList(Aref), VariantContext.PASSES_FILTERS), - makeVC("2", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), - makeVC("3", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), - "2"); + makeVC("1", Arrays.asList(Aref), VariantContext.PASSES_FILTERS), + makeVC("2", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), + makeVC("3", Arrays.asList(Aref, T), VariantContext.PASSES_FILTERS), + "2"); return MergeFilteredTest.getTests(MergeFilteredTest.class); } @Test(dataProvider = "mergeFiltered") public void testMergeFiltered(MergeFilteredTest cfg) { - final List priority = new ArrayList(); - - for ( final VariantContext vc : cfg.inputs ) { - priority.add(vc.getSource()); - } - + final List priority = vcs2priority(cfg.inputs); final VariantContext merged = VariantContextUtils.simpleMerge(genomeLocParser, cfg.inputs, priority, cfg.type, VariantContextUtils.GenotypeMergeType.PRIORITIZE, true, false, "set", false, false); @@ -375,6 +375,148 @@ public class VariantContextUtilsUnitTest extends BaseTest { Assert.assertEquals(merged.getFilters(), cfg.expected.getFilters()); } + // -------------------------------------------------------------------------------- + // + // Test genotype merging + // + // -------------------------------------------------------------------------------- + + private class MergeGenotypesTest extends TestDataProvider { + List inputs; + VariantContext expected; + List priority; + + private MergeGenotypesTest(String name, String priority, VariantContext... arg) { + super(MergeGenotypesTest.class, name); + LinkedList all = new LinkedList(Arrays.asList(arg)); + this.expected = all.pollLast(); + inputs = all; + this.priority = Arrays.asList(priority.split(",")); + } + + public String toString() { + return String.format("%s input=%s expected=%s", super.toString(), inputs, expected); + } + } + + @DataProvider(name = "mergeGenotypes") + public Object[][] mergeGenotypesData() { + new MergeGenotypesTest("TakeGenotypeByPriority-1,2", "1,2", + makeVC("1", Arrays.asList(Aref, T), makeG("s1", Aref, T, 1)), + makeVC("2", Arrays.asList(Aref, T), makeG("s1", Aref, T, 2)), + makeVC("3", Arrays.asList(Aref, T), makeG("s1", Aref, T, 1))); + + new MergeGenotypesTest("TakeGenotypeByPriority-1,2-nocall", "1,2", + makeVC("1", Arrays.asList(Aref, T), makeG("s1", Allele.NO_CALL, Allele.NO_CALL, 1)), + makeVC("2", Arrays.asList(Aref, T), makeG("s1", Aref, T, 2)), + makeVC("3", Arrays.asList(Aref, T), makeG("s1", Allele.NO_CALL, Allele.NO_CALL, 1))); + + new MergeGenotypesTest("TakeGenotypeByPriority-2,1", "2,1", + makeVC("1", Arrays.asList(Aref, T), makeG("s1", Aref, T, 1)), + makeVC("2", Arrays.asList(Aref, T), makeG("s1", Aref, T, 2)), + makeVC("3", Arrays.asList(Aref, T), makeG("s1", Aref, T, 2))); + + new MergeGenotypesTest("NonOverlappingGenotypes", "1,2", + makeVC("1", Arrays.asList(Aref, T), makeG("s1", Aref, T, 1)), + makeVC("2", Arrays.asList(Aref, T), makeG("s2", Aref, T, 2)), + makeVC("3", Arrays.asList(Aref, T), makeG("s1", Aref, T, 1), makeG("s2", Aref, T, 2))); + + new MergeGenotypesTest("PreserveNoCall", "1,2", + makeVC("1", Arrays.asList(Aref, T), makeG("s1", Allele.NO_CALL, Allele.NO_CALL, 1)), + makeVC("2", Arrays.asList(Aref, T), makeG("s2", Aref, T, 2)), + makeVC("3", Arrays.asList(Aref, T), makeG("s1", Allele.NO_CALL, Allele.NO_CALL, 1), makeG("s2", Aref, T, 2))); + + new MergeGenotypesTest("PerserveAlleles", "1,2", + makeVC("1", Arrays.asList(Aref, T), makeG("s1", Aref, T, 1)), + makeVC("2", Arrays.asList(Aref, C), makeG("s2", Aref, C, 2)), + makeVC("3", Arrays.asList(Aref, C, T), makeG("s1", Aref, T, 1), makeG("s2", Aref, C, 2))); + + new MergeGenotypesTest("TakeGenotypePartialOverlap-1,2", "1,2", + makeVC("1", Arrays.asList(Aref, T), makeG("s1", Aref, T, 1)), + makeVC("2", Arrays.asList(Aref, T), makeG("s1", Aref, T, 2), makeG("s3", Aref, T, 3)), + makeVC("3", Arrays.asList(Aref, T), makeG("s1", Aref, T, 1), makeG("s3", Aref, T, 3))); + + new MergeGenotypesTest("TakeGenotypePartialOverlap-2,1", "2,1", + makeVC("1", Arrays.asList(Aref, T), makeG("s1", Aref, T, 1)), + makeVC("2", Arrays.asList(Aref, T), makeG("s1", Aref, T, 2), makeG("s3", Aref, T, 3)), + makeVC("3", Arrays.asList(Aref, T), makeG("s1", Aref, T, 2), makeG("s3", Aref, T, 3))); + + // todo -- GDA -- add tests for merging correct PLs + + return MergeGenotypesTest.getTests(MergeGenotypesTest.class); + } + + // todo -- add test for GenotypeMergeType UNIQUIFY, REQUIRE_UNIQUE + + @Test(dataProvider = "mergeGenotypes") + public void testMergeGenotypes(MergeGenotypesTest cfg) { + final VariantContext merged = VariantContextUtils.simpleMerge(genomeLocParser, + cfg.inputs, cfg.priority, VariantContextUtils.FilteredRecordMergeType.KEEP_IF_ANY_UNFILTERED, + VariantContextUtils.GenotypeMergeType.PRIORITIZE, true, false, "set", false, false); + + // test alleles are equal + Assert.assertEquals(merged.getAlleles(), cfg.expected.getAlleles()); + + // test genotypes + assertGenotypesAreMostlyEqual(merged.getGenotypes(), cfg.expected.getGenotypes()); + } + + // necessary to not overload equals for genotypes + private void assertGenotypesAreMostlyEqual(Map actual, Map expected) { + if (actual == expected) { + return; + } + + if (actual == null || expected == null) { + Assert.fail("Maps not equal: expected: " + expected + " and actual: " + actual); + } + + if (actual.size() != expected.size()) { + Assert.fail("Maps do not have the same size:" + actual.size() + " != " + expected.size()); + } + + for (Map.Entry entry : actual.entrySet()) { + String key = entry.getKey(); + Genotype value = entry.getValue(); + Genotype expectedValue = expected.get(key); + + Assert.assertEquals(value.alleles, expectedValue.alleles); + Assert.assertEquals(value.getNegLog10PError(), expectedValue.getNegLog10PError()); + Assert.assertEquals(value.hasLikelihoods(), expectedValue.hasLikelihoods()); + if ( value.hasLikelihoods() ) + Assert.assertEquals(value.getLikelihoods(), expectedValue.getLikelihoods()); + } + } + + @Test + public void testMergeGenotypesUniquify() { + final VariantContext vc1 = makeVC("1", Arrays.asList(Aref, T), makeG("s1", Aref, T, 1)); + final VariantContext vc2 = makeVC("2", Arrays.asList(Aref, T), makeG("s1", Aref, T, 2)); + + final VariantContext merged = VariantContextUtils.simpleMerge(genomeLocParser, + Arrays.asList(vc1, vc2), null, VariantContextUtils.FilteredRecordMergeType.KEEP_IF_ANY_UNFILTERED, + VariantContextUtils.GenotypeMergeType.UNIQUIFY, false, false, "set", false, false); + + // test genotypes + Assert.assertEquals(merged.getGenotypes().keySet(), new HashSet(Arrays.asList("s1.1", "s1.2"))); + } + + @Test(expectedExceptions = UserException.class) + public void testMergeGenotypesRequireUnique() { + final VariantContext vc1 = makeVC("1", Arrays.asList(Aref, T), makeG("s1", Aref, T, 1)); + final VariantContext vc2 = makeVC("2", Arrays.asList(Aref, T), makeG("s1", Aref, T, 2)); + + final VariantContext merged = VariantContextUtils.simpleMerge(genomeLocParser, + Arrays.asList(vc1, vc2), null, VariantContextUtils.FilteredRecordMergeType.KEEP_IF_ANY_UNFILTERED, + VariantContextUtils.GenotypeMergeType.REQUIRE_UNIQUE, false, false, "set", false, false); + } + + // -------------------------------------------------------------------------------- + // + // Misc. tests + // + // -------------------------------------------------------------------------------- + @Test public void testAnnotationSet() { for ( final boolean annotate : Arrays.asList(true, false)) { @@ -395,6 +537,13 @@ public class VariantContextUtilsUnitTest extends BaseTest { } } - // todo -- add tests for subset merging, especially with correct PLs - // todo -- test priority list + private static final List vcs2priority(final Collection vcs) { + final List priority = new ArrayList(); + + for ( final VariantContext vc : vcs ) { + priority.add(vc.getSource()); + } + + return priority; + } } From 106a26c42da8780cd33d7186ec0a658830ad6819 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 23 Sep 2011 08:25:20 -0400 Subject: [PATCH 158/196] Minor file cleanup --- .../utils/variantcontext/VariantContextUtilsUnitTest.java | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java index c1025a7de..15a5549b2 100644 --- a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java @@ -24,18 +24,14 @@ package org.broadinstitute.sting.utils.variantcontext; import net.sf.picard.reference.IndexedFastaSequenceFile; -import org.apache.log4j.Priority; import org.broadinstitute.sting.BaseTest; -import org.broadinstitute.sting.gatk.refdata.tracks.FeatureManager; import org.broadinstitute.sting.utils.GenomeLocParser; -import org.broadinstitute.sting.utils.codecs.vcf.VCFConstants; import org.broadinstitute.sting.utils.exceptions.UserException; import org.broadinstitute.sting.utils.fasta.CachingIndexedFastaSequenceFile; import org.testng.Assert; import org.testng.annotations.BeforeSuite; -import org.testng.annotations.Test; import org.testng.annotations.DataProvider; -import org.yaml.snakeyaml.Yaml; +import org.testng.annotations.Test; import java.io.File; import java.io.FileNotFoundException; @@ -446,8 +442,6 @@ public class VariantContextUtilsUnitTest extends BaseTest { return MergeGenotypesTest.getTests(MergeGenotypesTest.class); } - // todo -- add test for GenotypeMergeType UNIQUIFY, REQUIRE_UNIQUE - @Test(dataProvider = "mergeGenotypes") public void testMergeGenotypes(MergeGenotypesTest cfg) { final VariantContext merged = VariantContextUtils.simpleMerge(genomeLocParser, From dfce301bebd62275ad55d326dd2801cbbce47d81 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 23 Sep 2011 09:03:04 -0400 Subject: [PATCH 159/196] Looks for @Hidden annotation on all classes and excludes them from the docs --- .../sting/utils/help/GenericDocumentationHandler.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/help/GenericDocumentationHandler.java b/public/java/src/org/broadinstitute/sting/utils/help/GenericDocumentationHandler.java index 4f1e95499..7ea77a939 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/GenericDocumentationHandler.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/GenericDocumentationHandler.java @@ -66,7 +66,8 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { public boolean includeInDocs(ClassDoc doc) { try { Class type = HelpUtils.getClassForDoc(doc); - return JVMUtils.isConcrete(type); + boolean hidden = ! getDoclet().showHiddenFeatures() && type.isAnnotationPresent(Hidden.class); + return ! hidden && JVMUtils.isConcrete(type); } catch ( ClassNotFoundException e ) { return false; } From dd65ba5baee8df3b57819e684dd3b63843809836 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 23 Sep 2011 09:03:37 -0400 Subject: [PATCH 160/196] @Hidden for DocumentationTest and GATKDocsExample --- .../org/broadinstitute/sting/gatk/examples/GATKDocsExample.java | 2 ++ .../broadinstitute/sting/gatk/walkers/qc/DocumentationTest.java | 1 + 2 files changed, 3 insertions(+) diff --git a/public/java/src/org/broadinstitute/sting/gatk/examples/GATKDocsExample.java b/public/java/src/org/broadinstitute/sting/gatk/examples/GATKDocsExample.java index 4541a0537..db4f477c2 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/examples/GATKDocsExample.java +++ b/public/java/src/org/broadinstitute/sting/gatk/examples/GATKDocsExample.java @@ -26,6 +26,7 @@ package org.broadinstitute.sting.gatk.examples; import org.broadinstitute.sting.commandline.Argument; import org.broadinstitute.sting.commandline.ArgumentCollection; +import org.broadinstitute.sting.commandline.Hidden; import org.broadinstitute.sting.gatk.arguments.StandardVariantContextInputArgumentCollection; import org.broadinstitute.sting.gatk.contexts.AlignmentContext; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; @@ -59,6 +60,7 @@ import org.broadinstitute.sting.gatk.walkers.RodWalker; * @author Your Name * @since Date created */ +@Hidden public class GATKDocsExample extends RodWalker { /** * Put detailed documentation about the argument here. No need to duplicate the summary information diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/DocumentationTest.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/DocumentationTest.java index 933e24784..5b60a9db5 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/DocumentationTest.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/DocumentationTest.java @@ -42,6 +42,7 @@ import java.util.*; * *

Body test

*/ +@Hidden public class DocumentationTest extends RodWalker { // the docs for the arguments are in the collection @ArgumentCollection protected StandardVariantContextInputArgumentCollection variantCollection = new StandardVariantContextInputArgumentCollection(); From 2bb77a7978881db4ff1d0b57fa7e2afd9a271295 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 23 Sep 2011 09:04:16 -0400 Subject: [PATCH 162/196] Docs for all VariantAnnotator annotations --- .../gatk/walkers/annotator/AlleleBalance.java | 3 + .../annotator/AlleleBalanceBySample.java | 3 + .../walkers/annotator/AnnotationByDepth.java | 5 +- .../gatk/walkers/annotator/BaseCounts.java | 3 + .../annotator/BaseQualityRankSumTest.java | 3 + .../walkers/annotator/ChromosomeCounts.java | 5 + .../walkers/annotator/DepthOfCoverage.java | 7 +- .../annotator/DepthPerAlleleBySample.java | 19 + .../gatk/walkers/annotator/FisherStrand.java | 5 + .../gatk/walkers/annotator/GCContent.java | 3 + .../walkers/annotator/HaplotypeScore.java | 4 + .../gatk/walkers/annotator/HardyWeinberg.java | 3 + .../walkers/annotator/HomopolymerRun.java | 4 +- .../{GLstats.java => InbreedingCoeff.java} | 15 +- .../gatk/walkers/annotator/IndelType.java | 6 +- .../sting/gatk/walkers/annotator/LowMQ.java | 3 + .../annotator/MappingQualityRankSumTest.java | 3 + .../walkers/annotator/MappingQualityZero.java | 3 + .../annotator/MappingQualityZeroBySample.java | 166 ++++--- .../annotator/MappingQualityZeroFraction.java | 5 +- .../gatk/walkers/annotator/NBaseCount.java | 5 +- .../gatk/walkers/annotator/QualByDepth.java | 7 +- .../walkers/annotator/RMSMappingQuality.java | 3 + .../gatk/walkers/annotator/RankSumTest.java | 4 +- .../ReadDepthAndAllelicFractionBySample.java | 416 +++++++++--------- .../walkers/annotator/ReadPosRankSumTest.java | 5 +- .../gatk/walkers/annotator/SBByDepth.java | 5 +- .../gatk/walkers/annotator/SampleList.java | 4 +- .../walkers/annotator/SpanningDeletions.java | 3 + .../annotator/TechnologyComposition.java | 8 +- 30 files changed, 398 insertions(+), 330 deletions(-) rename public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/{GLstats.java => InbreedingCoeff.java} (90%) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/AlleleBalance.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/AlleleBalance.java index cf68a9121..e501258c5 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/AlleleBalance.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/AlleleBalance.java @@ -43,6 +43,9 @@ import java.util.List; import java.util.Map; +/** + * The allele balance (fraction of ref bases over ref + alt bases) across all bialleleic het-called samples + */ public class AlleleBalance extends InfoFieldAnnotation { public Map annotate(RefMetaDataTracker tracker, AnnotatorCompatibleWalker walker, ReferenceContext ref, Map stratifiedContexts, VariantContext vc) { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/AlleleBalanceBySample.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/AlleleBalanceBySample.java index ddb7ab828..75c4037d5 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/AlleleBalanceBySample.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/AlleleBalanceBySample.java @@ -16,6 +16,9 @@ import org.broadinstitute.sting.utils.variantcontext.VariantContext; import java.util.*; +/** + * The allele balance (fraction of ref bases over ref + alt bases) separately for each bialleleic het-called sample + */ public class AlleleBalanceBySample extends GenotypeAnnotation implements ExperimentalAnnotation { public Map annotate(RefMetaDataTracker tracker, AnnotatorCompatibleWalker walker, ReferenceContext ref, AlignmentContext stratifiedContext, VariantContext vc, Genotype g) { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/AnnotationByDepth.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/AnnotationByDepth.java index dc41dbc81..353fd1c2c 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/AnnotationByDepth.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/AnnotationByDepth.java @@ -6,8 +6,9 @@ import org.broadinstitute.sting.utils.variantcontext.Genotype; import java.util.Map; - - +/** + * Abstract base class for all annotations that are normalized by depth + */ public abstract class AnnotationByDepth extends InfoFieldAnnotation { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/BaseCounts.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/BaseCounts.java index ecfd9b707..46aa6d0f3 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/BaseCounts.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/BaseCounts.java @@ -47,6 +47,9 @@ import java.util.List; import java.util.Map; +/** + * Count of A, C, G, T bases across all samples + */ public class BaseCounts extends InfoFieldAnnotation { public Map annotate(RefMetaDataTracker tracker, AnnotatorCompatibleWalker walker, ReferenceContext ref, Map stratifiedContexts, VariantContext vc) { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/BaseQualityRankSumTest.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/BaseQualityRankSumTest.java index 2a5c996f7..6cab6d95f 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/BaseQualityRankSumTest.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/BaseQualityRankSumTest.java @@ -13,6 +13,9 @@ import java.util.LinkedHashMap; import java.util.List; +/** + * The phred-scaled p-value (u-based z-approximation) from the Mann-Whitney Rank Sum Test for base qualities (ref bases vs. bases of the alternate allele) + */ public class BaseQualityRankSumTest extends RankSumTest { public List getKeyNames() { return Arrays.asList("BaseQRankSum"); } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/ChromosomeCounts.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/ChromosomeCounts.java index ad06dcf52..5ed2a6761 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/ChromosomeCounts.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/ChromosomeCounts.java @@ -44,6 +44,11 @@ import java.util.List; import java.util.Map; +/** + * Allele count in genotypes, for each ALT allele, in the same order as listed; + * allele Frequency, for each ALT allele, in the same order as listed; total number + * of alleles in called genotypes. + */ public class ChromosomeCounts extends InfoFieldAnnotation implements StandardAnnotation { private String[] keyNames = { VCFConstants.ALLELE_NUMBER_KEY, VCFConstants.ALLELE_COUNT_KEY, VCFConstants.ALLELE_FREQUENCY_KEY }; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/DepthOfCoverage.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/DepthOfCoverage.java index a4d8db5bd..8826de232 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/DepthOfCoverage.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/DepthOfCoverage.java @@ -16,7 +16,12 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - +/** + * Total (unfiltered) depth over all samples. + * + * Affected by downsampling (-dcov) though, so the max value one can obtain for N samples with -dcov D + * is N * D + */ public class DepthOfCoverage extends InfoFieldAnnotation implements StandardAnnotation { public Map annotate(RefMetaDataTracker tracker, AnnotatorCompatibleWalker walker, ReferenceContext ref, Map stratifiedContexts, VariantContext vc) { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/DepthPerAlleleBySample.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/DepthPerAlleleBySample.java index 1652c8de7..1cd30c51d 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/DepthPerAlleleBySample.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/DepthPerAlleleBySample.java @@ -23,6 +23,25 @@ import java.util.List; import java.util.Map; +/** + * The depth of coverage of each VCF allele in this sample + * + * Complementary fields that two important ways of thinking about the depth of the data for this sample + * at this site. The DP field describe the total depth of reads that passed the Unified Genotypers internal + * quality control metrics (like MAPQ > 17, for example), whatever base was present in the read at this site. + * The AD values (one for each of REF and ALT fields) is the count of all reads that carried with them the + * REF and ALT alleles. The reason for this distinction is that the DP is in some sense reflective of the + * power I have to determine the genotype of the sample at this site, while the AD tells me how many times + * I saw each of the REF and ALT alleles in the reads, free of any bias potentially introduced by filtering + * the reads. If, for example, I believe there really is a an A/T polymorphism at a site, then I would like + * to know the counts of A and T bases in this sample, even for reads with poor mapping quality that would + * normally be excluded from the statistical calculations going into GQ and QUAL. Please note, however, that + * the AD isn't necessarily calculated exactly for indels (it counts as non-reference only those indels that + * are actually present and correctly left-aligned in the alignments themselves). Because of this fact and + * because the AD includes reads and bases that were filtered by the Unified Genotyper, one should not base + * assumptions about the underlying genotype based on it; instead, the genotype likelihoods (PLs) are what + * determine the genotype calls (see below). + */ public class DepthPerAlleleBySample extends GenotypeAnnotation implements StandardAnnotation { private static String REF_ALLELE = "REF"; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/FisherStrand.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/FisherStrand.java index 0cfca48fa..393eb549c 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/FisherStrand.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/FisherStrand.java @@ -43,6 +43,11 @@ import org.broadinstitute.sting.utils.variantcontext.VariantContext; import java.util.*; +/** + * Phred-scaled p-value using Fisher's Exact Test to detect strand bias (the variation + * being seen on only the forward or only the reverse strand) in the reads? More bias is + * indicative of false positive calls. + */ public class FisherStrand extends InfoFieldAnnotation implements StandardAnnotation { private static final String FS = "FS"; private static final double MIN_PVALUE = 1E-320; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/GCContent.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/GCContent.java index a46473f60..11a64b49f 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/GCContent.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/GCContent.java @@ -17,6 +17,9 @@ import java.util.List; import java.util.Map; +/** + * The GC content (# GC bases / # all bases) of the reference within 50 bp +/- this site + */ public class GCContent extends InfoFieldAnnotation implements ExperimentalAnnotation { public Map annotate(RefMetaDataTracker tracker, AnnotatorCompatibleWalker walker, ReferenceContext ref, Map stratifiedContexts, VariantContext vc) { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/HaplotypeScore.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/HaplotypeScore.java index 9af3b8e8e..df6da3b85 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/HaplotypeScore.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/HaplotypeScore.java @@ -49,6 +49,10 @@ import org.broadinstitute.sting.utils.variantcontext.VariantContext; import java.util.*; +/** + * Consistency of the site with two (and only two) segregating haplotypes. Higher scores + * are indicative of regions with bad alignments, often leading to artifactual SNP and indel calls. + */ public class HaplotypeScore extends InfoFieldAnnotation implements StandardAnnotation { private final static boolean DEBUG = false; private final static int MIN_CONTEXT_WING_SIZE = 10; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/HardyWeinberg.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/HardyWeinberg.java index 045505698..f068ed895 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/HardyWeinberg.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/HardyWeinberg.java @@ -19,6 +19,9 @@ import java.util.List; import java.util.Map; +/** + * Phred-scaled P value of genotype-based (using GT field) test for Hardy-Weinberg test for disequilibrium + */ public class HardyWeinberg extends InfoFieldAnnotation implements WorkInProgressAnnotation { private static final int MIN_SAMPLES = 10; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/HomopolymerRun.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/HomopolymerRun.java index 463f7a645..197a00243 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/HomopolymerRun.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/HomopolymerRun.java @@ -16,7 +16,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - +/** + * Largest contiguous homopolymer run of the variant allele in either direction on the reference. + */ public class HomopolymerRun extends InfoFieldAnnotation implements StandardAnnotation { private boolean ANNOTATE_INDELS = true; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/GLstats.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/InbreedingCoeff.java similarity index 90% rename from public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/GLstats.java rename to public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/InbreedingCoeff.java index 5295d6d21..a14007147 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/GLstats.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/InbreedingCoeff.java @@ -17,14 +17,15 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -/** - * Created by IntelliJ IDEA. - * User: rpoplin - * Date: 5/16/11 - */ -// A set of annotations calculated directly from the GLs -public class GLstats extends InfoFieldAnnotation implements StandardAnnotation { +/** + * Likelihood-based (using PL field) test for the inbreeding among samples. + * + * A continuous generalization of the Hardy-Weinberg test for disequilibrium that works + * well with limited coverage per sample. See the 1000 Genomes Phase I release for + * more information. + */ +public class InbreedingCoeff extends InfoFieldAnnotation implements StandardAnnotation { private static final int MIN_SAMPLES = 10; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/IndelType.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/IndelType.java index bfede40d2..e0abfcf3c 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/IndelType.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/IndelType.java @@ -14,11 +14,7 @@ import org.broadinstitute.sting.utils.variantcontext.VariantContext; import java.util.*; /** - * Created by IntelliJ IDEA. - * User: delangel - * Date: Mar 11, 2011 - * Time: 11:47:33 AM - * To change this template use File | Settings | File Templates. + * Rough category of indel type (insertion, deletion, multi-allelic, other) */ public class IndelType extends InfoFieldAnnotation implements ExperimentalAnnotation { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/LowMQ.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/LowMQ.java index 09ffe0fb6..753740258 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/LowMQ.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/LowMQ.java @@ -17,6 +17,9 @@ import java.util.List; import java.util.Map; +/** + * Triplet annotation: fraction of MAQP == 0, MAPQ < 10, and count of all mapped reads + */ public class LowMQ extends InfoFieldAnnotation { public Map annotate(RefMetaDataTracker tracker, AnnotatorCompatibleWalker walker, ReferenceContext ref, Map stratifiedContexts, VariantContext vc) { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/MappingQualityRankSumTest.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/MappingQualityRankSumTest.java index cc62580a9..157c615d7 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/MappingQualityRankSumTest.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/MappingQualityRankSumTest.java @@ -14,6 +14,9 @@ import java.util.LinkedHashMap; import java.util.List; +/** + * The phred-scaled p-value (u-based z-approximation) from the Mann-Whitney Rank Sum Test for mapping qualities (reads with ref bases vs. those with the alternate allele) + */ public class MappingQualityRankSumTest extends RankSumTest { public List getKeyNames() { return Arrays.asList("MQRankSum"); } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/MappingQualityZero.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/MappingQualityZero.java index f9caae227..3a3efc4e8 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/MappingQualityZero.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/MappingQualityZero.java @@ -19,6 +19,9 @@ import java.util.List; import java.util.Map; +/** + * Total count across all samples of mapping quality zero reads + */ public class MappingQualityZero extends InfoFieldAnnotation implements StandardAnnotation { public Map annotate(RefMetaDataTracker tracker, AnnotatorCompatibleWalker walker, ReferenceContext ref, Map stratifiedContexts, VariantContext vc) { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/MappingQualityZeroBySample.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/MappingQualityZeroBySample.java index 3d234a1e3..f14d7a8a5 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/MappingQualityZeroBySample.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/MappingQualityZeroBySample.java @@ -1,85 +1,81 @@ -/* - * Copyright (c) 2010 The Broad Institute - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -package org.broadinstitute.sting.gatk.walkers.annotator; - -import org.broadinstitute.sting.gatk.contexts.AlignmentContext; -import org.broadinstitute.sting.gatk.contexts.ReferenceContext; -import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; -import org.broadinstitute.sting.gatk.walkers.annotator.interfaces.AnnotatorCompatibleWalker; -import org.broadinstitute.sting.gatk.walkers.annotator.interfaces.GenotypeAnnotation; -import org.broadinstitute.sting.utils.codecs.vcf.VCFConstants; -import org.broadinstitute.sting.utils.codecs.vcf.VCFFormatHeaderLine; -import org.broadinstitute.sting.utils.codecs.vcf.VCFHeaderLineType; -import org.broadinstitute.sting.utils.pileup.PileupElement; -import org.broadinstitute.sting.utils.pileup.ReadBackedPileup; -import org.broadinstitute.sting.utils.variantcontext.Genotype; -import org.broadinstitute.sting.utils.variantcontext.VariantContext; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Created by IntelliJ IDEA. - * User: asivache - * Date: Feb 4, 2011 - * Time: 6:46:25 PM - * To change this template use File | Settings | File Templates. - */ -public class MappingQualityZeroBySample extends GenotypeAnnotation { - public Map annotate(RefMetaDataTracker tracker, - AnnotatorCompatibleWalker walker, ReferenceContext ref, AlignmentContext context, VariantContext vc, Genotype g) { - if ( g == null || !g.isCalled() ) - return null; - - int mq0 = 0; - ReadBackedPileup pileup = null; - if (vc.isIndel() && context.hasExtendedEventPileup()) - pileup = context.getExtendedEventPileup(); - else if (context.hasBasePileup()) - pileup = context.getBasePileup(); - else return null; - - if (pileup != null) { - for (PileupElement p : pileup ) { - if ( p.getMappingQual() == 0 ) - mq0++; - } - } - Map map = new HashMap(); - map.put(getKeyNames().get(0), String.format("%d", mq0)); - return map; - } - - public List getKeyNames() { return Arrays.asList(VCFConstants.MAPPING_QUALITY_ZERO_KEY); } - - public List getDescriptions() { return Arrays.asList( - new VCFFormatHeaderLine(getKeyNames().get(0), 1, - VCFHeaderLineType.Integer, "Number of Mapping Quality Zero Reads per sample")); } - - -} +/* + * Copyright (c) 2010 The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.gatk.walkers.annotator; + +import org.broadinstitute.sting.gatk.contexts.AlignmentContext; +import org.broadinstitute.sting.gatk.contexts.ReferenceContext; +import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; +import org.broadinstitute.sting.gatk.walkers.annotator.interfaces.AnnotatorCompatibleWalker; +import org.broadinstitute.sting.gatk.walkers.annotator.interfaces.GenotypeAnnotation; +import org.broadinstitute.sting.utils.codecs.vcf.VCFConstants; +import org.broadinstitute.sting.utils.codecs.vcf.VCFFormatHeaderLine; +import org.broadinstitute.sting.utils.codecs.vcf.VCFHeaderLineType; +import org.broadinstitute.sting.utils.pileup.PileupElement; +import org.broadinstitute.sting.utils.pileup.ReadBackedPileup; +import org.broadinstitute.sting.utils.variantcontext.Genotype; +import org.broadinstitute.sting.utils.variantcontext.VariantContext; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Count for each sample of mapping quality zero reads + */ +public class MappingQualityZeroBySample extends GenotypeAnnotation { + public Map annotate(RefMetaDataTracker tracker, + AnnotatorCompatibleWalker walker, ReferenceContext ref, AlignmentContext context, VariantContext vc, Genotype g) { + if ( g == null || !g.isCalled() ) + return null; + + int mq0 = 0; + ReadBackedPileup pileup = null; + if (vc.isIndel() && context.hasExtendedEventPileup()) + pileup = context.getExtendedEventPileup(); + else if (context.hasBasePileup()) + pileup = context.getBasePileup(); + else return null; + + if (pileup != null) { + for (PileupElement p : pileup ) { + if ( p.getMappingQual() == 0 ) + mq0++; + } + } + Map map = new HashMap(); + map.put(getKeyNames().get(0), String.format("%d", mq0)); + return map; + } + + public List getKeyNames() { return Arrays.asList(VCFConstants.MAPPING_QUALITY_ZERO_KEY); } + + public List getDescriptions() { return Arrays.asList( + new VCFFormatHeaderLine(getKeyNames().get(0), 1, + VCFHeaderLineType.Integer, "Number of Mapping Quality Zero Reads per sample")); } + + +} diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/MappingQualityZeroFraction.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/MappingQualityZeroFraction.java index 3e8fe8998..2164537b8 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/MappingQualityZeroFraction.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/MappingQualityZeroFraction.java @@ -17,8 +17,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - - +/** + * Fraction of all reads across samples that have mapping quality zero + */ public class MappingQualityZeroFraction extends InfoFieldAnnotation implements ExperimentalAnnotation { public Map annotate(RefMetaDataTracker tracker, AnnotatorCompatibleWalker walker, ReferenceContext ref, Map stratifiedContexts, VariantContext vc) { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/NBaseCount.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/NBaseCount.java index 74c562045..891e9ae56 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/NBaseCount.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/NBaseCount.java @@ -17,11 +17,8 @@ import java.util.List; import java.util.Map; /** - * Created by IntelliJ IDEA. - * User: rpoplin - * Date: 5/16/11 + * The number of N bases, counting only SOLiD data */ - public class NBaseCount extends InfoFieldAnnotation { public Map annotate(RefMetaDataTracker tracker, AnnotatorCompatibleWalker walker, ReferenceContext ref, Map stratifiedContexts, VariantContext vc) { if( stratifiedContexts.size() == 0 ) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/QualByDepth.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/QualByDepth.java index 9a292c39a..552289309 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/QualByDepth.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/QualByDepth.java @@ -1,5 +1,6 @@ package org.broadinstitute.sting.gatk.walkers.annotator; +import org.broadinstitute.sting.commandline.Hidden; import org.broadinstitute.sting.gatk.contexts.AlignmentContext; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; @@ -15,7 +16,11 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - +/** + * Variant confidence (given as (AB+BB)/AA from the PLs) / unfiltered depth. + * + * Low scores are indicative of false positive calls and artifacts. + */ public class QualByDepth extends AnnotationByDepth implements StandardAnnotation { public Map annotate(RefMetaDataTracker tracker, AnnotatorCompatibleWalker walker, ReferenceContext ref, Map stratifiedContexts, VariantContext vc) { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/RMSMappingQuality.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/RMSMappingQuality.java index 668129888..40f6d20d3 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/RMSMappingQuality.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/RMSMappingQuality.java @@ -21,6 +21,9 @@ import java.util.List; import java.util.Map; +/** + * Root Mean Square of the mapping quality of the reads across all samples. + */ public class RMSMappingQuality extends InfoFieldAnnotation implements StandardAnnotation { public Map annotate(RefMetaDataTracker tracker, AnnotatorCompatibleWalker walker, ReferenceContext ref, Map stratifiedContexts, VariantContext vc) { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/RankSumTest.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/RankSumTest.java index 52c704055..93e093248 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/RankSumTest.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/RankSumTest.java @@ -21,7 +21,9 @@ import java.util.List; import java.util.Map; - +/** + * Abstract root for all RankSum based annotations + */ public abstract class RankSumTest extends InfoFieldAnnotation implements StandardAnnotation { static final double INDEL_LIKELIHOOD_THRESH = 0.1; static final boolean DEBUG = false; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/ReadDepthAndAllelicFractionBySample.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/ReadDepthAndAllelicFractionBySample.java index 26ca08380..772541eb6 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/ReadDepthAndAllelicFractionBySample.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/ReadDepthAndAllelicFractionBySample.java @@ -1,209 +1,207 @@ -/* - * Copyright (c) 2010 The Broad Institute - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation - * files (the "Software"), to deal in the Software without - * restriction, including without limitation the rights to use, - * copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following - * conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES - * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT - * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, - * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - */ - -package org.broadinstitute.sting.gatk.walkers.annotator; - -import org.broadinstitute.sting.gatk.contexts.AlignmentContext; -import org.broadinstitute.sting.gatk.contexts.ReferenceContext; -import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; -import org.broadinstitute.sting.gatk.walkers.annotator.interfaces.AnnotatorCompatibleWalker; -import org.broadinstitute.sting.gatk.walkers.annotator.interfaces.GenotypeAnnotation; -import org.broadinstitute.sting.utils.codecs.vcf.VCFFormatHeaderLine; -import org.broadinstitute.sting.utils.codecs.vcf.VCFHeaderLineCount; -import org.broadinstitute.sting.utils.codecs.vcf.VCFHeaderLineType; -import org.broadinstitute.sting.utils.pileup.ExtendedEventPileupElement; -import org.broadinstitute.sting.utils.pileup.PileupElement; -import org.broadinstitute.sting.utils.pileup.ReadBackedExtendedEventPileup; -import org.broadinstitute.sting.utils.pileup.ReadBackedPileup; -import org.broadinstitute.sting.utils.variantcontext.Allele; -import org.broadinstitute.sting.utils.variantcontext.Genotype; -import org.broadinstitute.sting.utils.variantcontext.VariantContext; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -/** - * Created by IntelliJ IDEA. - * User: asivache - * Date: Feb 4, 2011 - * Time: 3:59:27 PM - * To change this template use File | Settings | File Templates. - */ -public class ReadDepthAndAllelicFractionBySample extends GenotypeAnnotation { - - private static String REF_ALLELE = "REF"; - - private static String DEL = "DEL"; // constant, for speed: no need to create a key string for deletion allele every time - - public Map annotate(RefMetaDataTracker tracker, AnnotatorCompatibleWalker walker, ReferenceContext ref, - AlignmentContext stratifiedContext, VariantContext vc, Genotype g) { - if ( g == null || !g.isCalled() ) - return null; - - if ( vc.isSNP() ) - return annotateSNP(stratifiedContext, vc); - if ( vc.isIndel() ) - return annotateIndel(stratifiedContext, vc); - - return null; - } - - private Map annotateSNP(AlignmentContext stratifiedContext, VariantContext vc) { - - if ( ! stratifiedContext.hasBasePileup() ) return null; - - HashMap alleleCounts = new HashMap(); - for ( Allele allele : vc.getAlternateAlleles() ) - alleleCounts.put(allele.getBases()[0], 0); - - ReadBackedPileup pileup = stratifiedContext.getBasePileup(); - int totalDepth = pileup.size(); - - Map map = new HashMap(); - map.put(getKeyNames().get(0), totalDepth); // put total depth in right away - - if ( totalDepth == 0 ) return map; // done, can not compute FA at 0 coverage!! - - int mq0 = 0; // number of "ref" reads that are acually mq0 - for ( PileupElement p : pileup ) { - if ( p.getMappingQual() == 0 ) { - mq0++; - continue; - } - if ( alleleCounts.containsKey(p.getBase()) ) // non-mq0 read and it's an alt - alleleCounts.put(p.getBase(), alleleCounts.get(p.getBase())+1); - } - - if ( mq0 == totalDepth ) return map; // if all reads are mq0, there is nothing left to do - - // we need to add counts in the correct order - String[] fracs = new String[alleleCounts.size()]; - for (int i = 0; i < vc.getAlternateAlleles().size(); i++) { - fracs[i] = String.format("%.3f", ((float)alleleCounts.get(vc.getAlternateAllele(i).getBases()[0]))/(totalDepth-mq0)); - } - - map.put(getKeyNames().get(1), fracs); - return map; - } - - private Map annotateIndel(AlignmentContext - stratifiedContext, VariantContext - vc) { - - if ( ! stratifiedContext.hasExtendedEventPileup() ) { - return null; - } - - ReadBackedExtendedEventPileup pileup = stratifiedContext.getExtendedEventPileup(); - if ( pileup == null ) - return null; - int totalDepth = pileup.size(); - - Map map = new HashMap(); - map.put(getKeyNames().get(0), totalDepth); // put total depth in right away - - if ( totalDepth == 0 ) return map; - int mq0 = 0; // number of "ref" reads that are acually mq0 - - HashMap alleleCounts = new HashMap(); - Allele refAllele = vc.getReference(); - - for ( Allele allele : vc.getAlternateAlleles() ) { - - if ( allele.isNoCall() ) { - continue; // this does not look so good, should we die??? - } - - alleleCounts.put(getAlleleRepresentation(allele), 0); - } - - for ( ExtendedEventPileupElement e : pileup.toExtendedIterable() ) { - - if ( e.getMappingQual() == 0 ) { - mq0++; - continue; - } - - if ( e.isInsertion() ) { - - final String b = e.getEventBases(); - if ( alleleCounts.containsKey(b) ) { - alleleCounts.put(b, alleleCounts.get(b)+1); - } - - } else { - if ( e.isDeletion() ) { - if ( e.getEventLength() == refAllele.length() ) { - // this is indeed the deletion allele recorded in VC - final String b = DEL; - if ( alleleCounts.containsKey(b) ) { - alleleCounts.put(b, alleleCounts.get(b)+1); - } - } -// else { -// System.out.print(" deletion of WRONG length found"); -// } - } - } - } - - if ( mq0 == totalDepth ) return map; - - String[] fracs = new String[alleleCounts.size()]; - for (int i = 0; i < vc.getAlternateAlleles().size(); i++) - fracs[i] = String.format("%.3f", - ((float)alleleCounts.get(getAlleleRepresentation(vc.getAlternateAllele(i))))/(totalDepth-mq0)); - - map.put(getKeyNames().get(1), fracs); - - //map.put(getKeyNames().get(0), counts); - return map; - } - - private String getAlleleRepresentation(Allele allele) { - if ( allele.isNull() ) { // deletion wrt the ref - return DEL; - } else { // insertion, pass actual bases - return allele.getBaseString(); - } - - } - - // public String getIndelBases() - public List getKeyNames() { return Arrays.asList("DP","FA"); } - - public List getDescriptions() { - return Arrays.asList(new VCFFormatHeaderLine(getKeyNames().get(0), - 1, - VCFHeaderLineType.Integer, - "Total read depth per sample, including MQ0"), - new VCFFormatHeaderLine(getKeyNames().get(1), - VCFHeaderLineCount.UNBOUNDED, - VCFHeaderLineType.Float, - "Fractions of reads (excluding MQ0 from both ref and alt) supporting each reported alternative allele, per sample")); - } -} +/* + * Copyright (c) 2010 The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +package org.broadinstitute.sting.gatk.walkers.annotator; + +import org.broadinstitute.sting.commandline.Hidden; +import org.broadinstitute.sting.gatk.contexts.AlignmentContext; +import org.broadinstitute.sting.gatk.contexts.ReferenceContext; +import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; +import org.broadinstitute.sting.gatk.walkers.annotator.interfaces.AnnotatorCompatibleWalker; +import org.broadinstitute.sting.gatk.walkers.annotator.interfaces.GenotypeAnnotation; +import org.broadinstitute.sting.utils.codecs.vcf.VCFFormatHeaderLine; +import org.broadinstitute.sting.utils.codecs.vcf.VCFHeaderLineCount; +import org.broadinstitute.sting.utils.codecs.vcf.VCFHeaderLineType; +import org.broadinstitute.sting.utils.pileup.ExtendedEventPileupElement; +import org.broadinstitute.sting.utils.pileup.PileupElement; +import org.broadinstitute.sting.utils.pileup.ReadBackedExtendedEventPileup; +import org.broadinstitute.sting.utils.pileup.ReadBackedPileup; +import org.broadinstitute.sting.utils.variantcontext.Allele; +import org.broadinstitute.sting.utils.variantcontext.Genotype; +import org.broadinstitute.sting.utils.variantcontext.VariantContext; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Unsupported + */ +@Hidden +public class ReadDepthAndAllelicFractionBySample extends GenotypeAnnotation { + + private static String REF_ALLELE = "REF"; + + private static String DEL = "DEL"; // constant, for speed: no need to create a key string for deletion allele every time + + public Map annotate(RefMetaDataTracker tracker, AnnotatorCompatibleWalker walker, ReferenceContext ref, + AlignmentContext stratifiedContext, VariantContext vc, Genotype g) { + if ( g == null || !g.isCalled() ) + return null; + + if ( vc.isSNP() ) + return annotateSNP(stratifiedContext, vc); + if ( vc.isIndel() ) + return annotateIndel(stratifiedContext, vc); + + return null; + } + + private Map annotateSNP(AlignmentContext stratifiedContext, VariantContext vc) { + + if ( ! stratifiedContext.hasBasePileup() ) return null; + + HashMap alleleCounts = new HashMap(); + for ( Allele allele : vc.getAlternateAlleles() ) + alleleCounts.put(allele.getBases()[0], 0); + + ReadBackedPileup pileup = stratifiedContext.getBasePileup(); + int totalDepth = pileup.size(); + + Map map = new HashMap(); + map.put(getKeyNames().get(0), totalDepth); // put total depth in right away + + if ( totalDepth == 0 ) return map; // done, can not compute FA at 0 coverage!! + + int mq0 = 0; // number of "ref" reads that are acually mq0 + for ( PileupElement p : pileup ) { + if ( p.getMappingQual() == 0 ) { + mq0++; + continue; + } + if ( alleleCounts.containsKey(p.getBase()) ) // non-mq0 read and it's an alt + alleleCounts.put(p.getBase(), alleleCounts.get(p.getBase())+1); + } + + if ( mq0 == totalDepth ) return map; // if all reads are mq0, there is nothing left to do + + // we need to add counts in the correct order + String[] fracs = new String[alleleCounts.size()]; + for (int i = 0; i < vc.getAlternateAlleles().size(); i++) { + fracs[i] = String.format("%.3f", ((float)alleleCounts.get(vc.getAlternateAllele(i).getBases()[0]))/(totalDepth-mq0)); + } + + map.put(getKeyNames().get(1), fracs); + return map; + } + + private Map annotateIndel(AlignmentContext + stratifiedContext, VariantContext + vc) { + + if ( ! stratifiedContext.hasExtendedEventPileup() ) { + return null; + } + + ReadBackedExtendedEventPileup pileup = stratifiedContext.getExtendedEventPileup(); + if ( pileup == null ) + return null; + int totalDepth = pileup.size(); + + Map map = new HashMap(); + map.put(getKeyNames().get(0), totalDepth); // put total depth in right away + + if ( totalDepth == 0 ) return map; + int mq0 = 0; // number of "ref" reads that are acually mq0 + + HashMap alleleCounts = new HashMap(); + Allele refAllele = vc.getReference(); + + for ( Allele allele : vc.getAlternateAlleles() ) { + + if ( allele.isNoCall() ) { + continue; // this does not look so good, should we die??? + } + + alleleCounts.put(getAlleleRepresentation(allele), 0); + } + + for ( ExtendedEventPileupElement e : pileup.toExtendedIterable() ) { + + if ( e.getMappingQual() == 0 ) { + mq0++; + continue; + } + + if ( e.isInsertion() ) { + + final String b = e.getEventBases(); + if ( alleleCounts.containsKey(b) ) { + alleleCounts.put(b, alleleCounts.get(b)+1); + } + + } else { + if ( e.isDeletion() ) { + if ( e.getEventLength() == refAllele.length() ) { + // this is indeed the deletion allele recorded in VC + final String b = DEL; + if ( alleleCounts.containsKey(b) ) { + alleleCounts.put(b, alleleCounts.get(b)+1); + } + } +// else { +// System.out.print(" deletion of WRONG length found"); +// } + } + } + } + + if ( mq0 == totalDepth ) return map; + + String[] fracs = new String[alleleCounts.size()]; + for (int i = 0; i < vc.getAlternateAlleles().size(); i++) + fracs[i] = String.format("%.3f", + ((float)alleleCounts.get(getAlleleRepresentation(vc.getAlternateAllele(i))))/(totalDepth-mq0)); + + map.put(getKeyNames().get(1), fracs); + + //map.put(getKeyNames().get(0), counts); + return map; + } + + private String getAlleleRepresentation(Allele allele) { + if ( allele.isNull() ) { // deletion wrt the ref + return DEL; + } else { // insertion, pass actual bases + return allele.getBaseString(); + } + + } + + // public String getIndelBases() + public List getKeyNames() { return Arrays.asList("DP","FA"); } + + public List getDescriptions() { + return Arrays.asList(new VCFFormatHeaderLine(getKeyNames().get(0), + 1, + VCFHeaderLineType.Integer, + "Total read depth per sample, including MQ0"), + new VCFFormatHeaderLine(getKeyNames().get(1), + VCFHeaderLineCount.UNBOUNDED, + VCFHeaderLineType.Float, + "Fractions of reads (excluding MQ0 from both ref and alt) supporting each reported alternative allele, per sample")); + } +} diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/ReadPosRankSumTest.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/ReadPosRankSumTest.java index aabfb2970..27a9306d4 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/ReadPosRankSumTest.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/ReadPosRankSumTest.java @@ -19,11 +19,8 @@ import java.util.LinkedHashMap; import java.util.List; /** - * Created by IntelliJ IDEA. - * User: rpoplin - * Date: 3/30/11 + * The phred-scaled p-value (u-based z-approximation) from the Mann-Whitney Rank Sum Test for the distance from the end of the read for reads with the alternate allele; if the alternate allele is only seen near the ends of reads this is indicative of error). */ - public class ReadPosRankSumTest extends RankSumTest { public List getKeyNames() { return Arrays.asList("ReadPosRankSum"); } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SBByDepth.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SBByDepth.java index 180bed24d..efe96f226 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SBByDepth.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SBByDepth.java @@ -15,8 +15,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - - +/** + * SB annotation value by depth of alt containing samples + */ public class SBByDepth extends AnnotationByDepth { public Map annotate(RefMetaDataTracker tracker, AnnotatorCompatibleWalker walker, ReferenceContext ref, Map stratifiedContexts, VariantContext vc) { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SampleList.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SampleList.java index cd396036f..ff409484d 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SampleList.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SampleList.java @@ -41,7 +41,9 @@ import java.util.HashMap; import java.util.List; import java.util.Map; - +/** + * List all of the samples in the info field + */ public class SampleList extends InfoFieldAnnotation { public Map annotate(RefMetaDataTracker tracker, AnnotatorCompatibleWalker walker, ReferenceContext ref, Map stratifiedContexts, VariantContext vc) { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SpanningDeletions.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SpanningDeletions.java index 42203824f..f747fbc2e 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SpanningDeletions.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SpanningDeletions.java @@ -17,6 +17,9 @@ import java.util.List; import java.util.Map; +/** + * Fraction of reads containing spanning deletions at this site. + */ public class SpanningDeletions extends InfoFieldAnnotation implements StandardAnnotation { public Map annotate(RefMetaDataTracker tracker, AnnotatorCompatibleWalker walker, ReferenceContext ref, Map stratifiedContexts, VariantContext vc) { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/TechnologyComposition.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/TechnologyComposition.java index fa48c57a3..1f5508f4c 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/TechnologyComposition.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/TechnologyComposition.java @@ -1,5 +1,6 @@ package org.broadinstitute.sting.gatk.walkers.annotator; +import org.broadinstitute.sting.commandline.Hidden; import org.broadinstitute.sting.gatk.contexts.AlignmentContext; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; @@ -19,12 +20,9 @@ import java.util.List; import java.util.Map; /** - * Created by IntelliJ IDEA. - * User: delangel - * Date: 6/29/11 - * Time: 3:14 PM - * To change this template use File | Settings | File Templates. + * Counts of bases from SLX, 454, and SOLiD at this site */ +@Hidden public class TechnologyComposition extends InfoFieldAnnotation implements ExperimentalAnnotation { private String nSLX = "NumSLX"; private String n454 ="Num454"; From e3d4efb2834b6aa5049a36b3e7ad4ca9458140d2 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 23 Sep 2011 11:55:21 -0400 Subject: [PATCH 164/196] Remove N2 EXACT model code, which should never be used --- .../genotyper/ExactAFCalculationModel.java | 251 +----------------- .../genotyper/UnifiedArgumentCollection.java | 5 - 2 files changed, 9 insertions(+), 247 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/ExactAFCalculationModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/ExactAFCalculationModel.java index 6ae437b27..8a3a97823 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/ExactAFCalculationModel.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/ExactAFCalculationModel.java @@ -48,27 +48,12 @@ public class ExactAFCalculationModel extends AlleleFrequencyCalculationModel { // code for testing purposes // private final static boolean DEBUG = false; - private final static boolean PRINT_LIKELIHOODS = false; - private final static int N_CYCLES = 1; - private SimpleTimer timerExpt = new SimpleTimer("linearExactBanded"); - private SimpleTimer timerGS = new SimpleTimer("linearExactGS"); - private final static boolean COMPARE_TO_GS = false; - - public enum ExactCalculation { - N2_GOLD_STANDARD, - LINEAR_EXPERIMENTAL - } - private final static double MAX_LOG10_ERROR_TO_STOP_EARLY = 6; // we want the calculation to be accurate to 1 / 10^6 - - private boolean SIMPLE_GREEDY_GENOTYPER = false; - + private final boolean SIMPLE_GREEDY_GENOTYPER = false; private final static double SUM_GL_THRESH_NOCALL = -0.001; // if sum(gl) is bigger than this threshold, we treat GL's as non-informative and will force a no-call. - final private ExactCalculation calcToUse; protected ExactAFCalculationModel(UnifiedArgumentCollection UAC, int N, Logger logger, PrintStream verboseWriter) { super(UAC, N, logger, verboseWriter); - calcToUse = UAC.EXACT_CALCULATION_TYPE; } public void getLog10PNonRef(RefMetaDataTracker tracker, @@ -76,43 +61,12 @@ public class ExactAFCalculationModel extends AlleleFrequencyCalculationModel { Map GLs, Setalleles, double[] log10AlleleFrequencyPriors, double[] log10AlleleFrequencyPosteriors) { - // todo -- REMOVE ME AFTER TESTING - // todo -- REMOVE ME AFTER TESTING - // todo -- REMOVE ME AFTER TESTING - double[] gsPosteriors; - if ( COMPARE_TO_GS ) // due to annoying special values in incoming array, we have to clone up here - gsPosteriors = log10AlleleFrequencyPosteriors.clone(); - - int idxAA = GenotypeType.AA.ordinal(); - int idxAB = GenotypeType.AB.ordinal(); - int idxBB = GenotypeType.BB.ordinal(); - - // todo -- remove me after testing - if ( N_CYCLES > 1 ) { - for ( int i = 0; i < N_CYCLES; i++) { - timerGS.restart(); - linearExact(GLs, log10AlleleFrequencyPriors, log10AlleleFrequencyPosteriors.clone(), idxAA, idxAB, idxBB); - timerGS.stop(); - - timerExpt.restart(); - linearExactBanded(GLs, log10AlleleFrequencyPriors, log10AlleleFrequencyPosteriors.clone()); - timerExpt.stop(); - } - - System.out.printf("good = %.2f, expt = %.2f, delta = %.2f%n", - timerGS.getElapsedTime(), timerExpt.getElapsedTime(), timerExpt.getElapsedTime()-timerGS.getElapsedTime()); - } - - int lastK = -1; - - int numAlleles = alleles.size(); + final int numAlleles = alleles.size(); + final double[][] posteriorCache = numAlleles > 2 ? new double[numAlleles-1][] : null; + final double[] bestAFguess = numAlleles > 2 ? new double[numAlleles-1] : null; int idxDiag = numAlleles; int incr = numAlleles - 1; - - double[][] posteriorCache = new double[numAlleles-1][]; - double[] bestAFguess = new double[numAlleles-1]; - for (int k=1; k < numAlleles; k++) { // multi-allelic approximation, part 1: Ideally // for each alt allele compute marginal (suboptimal) posteriors - @@ -121,24 +75,17 @@ public class ExactAFCalculationModel extends AlleleFrequencyCalculationModel { // So, for example, with 2 alt alleles, likelihoods have AA,AB,AC,BB,BC,CC. // 3 alt alleles: AA,AB,AC,AD BB BC BD CC CD DD - idxAA = 0; - idxAB = k; + final int idxAA = 0; + final int idxAB = k; // yy is always element on the diagonal. // 2 alleles: BBelement 2 // 3 alleles: BB element 3. CC element 5 // 4 alleles: - idxBB = idxDiag; + final int idxBB = idxDiag; idxDiag += incr--; - // todo - possible cleanup - switch ( calcToUse ) { - case N2_GOLD_STANDARD: - lastK = gdaN2GoldStandard(GLs, log10AlleleFrequencyPriors, log10AlleleFrequencyPosteriors, idxAA, idxAB, idxBB); - break; - case LINEAR_EXPERIMENTAL: - lastK = linearExact(GLs, log10AlleleFrequencyPriors, log10AlleleFrequencyPosteriors, idxAA, idxAB, idxBB); - break; - } + final int lastK = linearExact(GLs, log10AlleleFrequencyPriors, log10AlleleFrequencyPosteriors, idxAA, idxAB, idxBB); + if (numAlleles > 2) { posteriorCache[k-1] = log10AlleleFrequencyPosteriors.clone(); bestAFguess[k-1] = (double)MathUtils.maxElementIndex(log10AlleleFrequencyPosteriors); @@ -153,39 +100,14 @@ public class ExactAFCalculationModel extends AlleleFrequencyCalculationModel { log10AlleleFrequencyPosteriors[k] = (posteriorCache[mostLikelyAlleleIdx][k]); } - // todo -- REMOVE ME AFTER TESTING - // todo -- REMOVE ME AFTER TESTING - // todo -- REMOVE ME AFTER TESTING - if ( COMPARE_TO_GS ) { - gdaN2GoldStandard(GLs, log10AlleleFrequencyPriors, gsPosteriors, idxAA, idxAB, idxBB); - - double log10thisPVar = Math.log10(MathUtils.normalizeFromLog10(log10AlleleFrequencyPosteriors)[0]); - double log10gsPVar = Math.log10(MathUtils.normalizeFromLog10(gsPosteriors)[0]); - boolean eq = (log10thisPVar == Double.NEGATIVE_INFINITY && log10gsPVar == Double.NEGATIVE_INFINITY) || MathUtils.compareDoubles(log10thisPVar, log10gsPVar, 1e-4) == 0; - - if ( ! eq || PRINT_LIKELIHOODS ) { - System.out.printf("----------------------------------------%n"); - for (int k=0; k < log10AlleleFrequencyPosteriors.length; k++) { - double x = log10AlleleFrequencyPosteriors[k]; - System.out.printf(" %d\t%.2f\t%.2f\t%b%n", k, - x < -1e10 ? Double.NEGATIVE_INFINITY : x, gsPosteriors[k], - log10AlleleFrequencyPosteriors[k] == gsPosteriors[k]); - } - System.out.printf("MAD_AC\t%d\t%d\t%.2f\t%.2f\t%.6f%n", - ref.getLocus().getStart(), lastK, log10thisPVar, log10gsPVar, log10thisPVar - log10gsPVar); - } - } - } private static final ArrayList getGLs(Map GLs) { ArrayList genotypeLikelihoods = new ArrayList(); - //int j = 0; genotypeLikelihoods.add(new double[]{0.0,0.0,0.0}); // dummy for ( Genotype sample : GLs.values() ) { if ( sample.hasLikelihoods() ) { - //double[] genotypeLikelihoods = MathUtils.normalizeFromLog10(GLs.get(sample).getLikelihoods()); double[] gls = sample.getLikelihoods().getAsVector(); if (MathUtils.sum(gls) < SUM_GL_THRESH_NOCALL) @@ -240,84 +162,6 @@ public class ExactAFCalculationModel extends AlleleFrequencyCalculationModel { } } - // now with banding - public int linearExactBanded(Map GLs, - double[] log10AlleleFrequencyPriors, - double[] log10AlleleFrequencyPosteriors) { - throw new NotImplementedException(); -// final int numSamples = GLs.size(); -// final int numChr = 2*numSamples; -// final double[][] genotypeLikelihoods = getGLs(GLs); -// -// final ExactACCache logY = new ExactACCache(numSamples+1); -// logY.getkMinus0()[0] = 0.0; // the zero case -// -// double maxLog10L = Double.NEGATIVE_INFINITY; -// boolean done = false; -// int lastK = -1; -// final int BAND_SIZE = 10; -// -// for (int k=0; k <= numChr && ! done; k++ ) { -// final double[] kMinus0 = logY.getkMinus0(); -// int jStart = Math.max(k - BAND_SIZE, 1); -// int jStop = Math.min(k + BAND_SIZE, numSamples); -// -// if ( k == 0 ) { // special case for k = 0 -// for ( int j=1; j <= numSamples; j++ ) { -// kMinus0[j] = kMinus0[j-1] + genotypeLikelihoods[j][GenotypeType.AA.ordinal()]; -// } -// } else { // k > 0 -// final double[] kMinus1 = logY.getkMinus1(); -// final double[] kMinus2 = logY.getkMinus2(); -// Arrays.fill(kMinus0,0); -// -// for ( int j = jStart; j <= jStop; j++ ) { -// final double[] gl = genotypeLikelihoods[j]; -// final double logDenominator = log10Cache[2*j] + log10Cache[2*j-1]; -// -// double aa = Double.NEGATIVE_INFINITY; -// double ab = Double.NEGATIVE_INFINITY; -// if (k < 2*j-1) -// aa = log10Cache[2*j-k] + log10Cache[2*j-k-1] + kMinus0[j-1] + gl[GenotypeType.AA.ordinal()]; -// -// if (k < 2*j) -// ab = log10Cache[2*k] + log10Cache[2*j-k]+ kMinus1[j-1] + gl[GenotypeType.AB.ordinal()]; -// -// double log10Max; -// if (k > 1) { -// final double bb = log10Cache[k] + log10Cache[k-1] + kMinus2[j-1] + gl[GenotypeType.BB.ordinal()]; -// log10Max = approximateLog10SumLog10(aa, ab, bb); -// } else { -// // we know we aren't considering the BB case, so we can use an optimized log10 function -// log10Max = approximateLog10SumLog10(aa, ab); -// } -// -// // finally, update the L(j,k) value -// kMinus0[j] = log10Max - logDenominator; -// -// String offset = Utils.dupString(' ',k); -// System.out.printf("%s%3d %3d %.2f%n", offset, k, j, kMinus0[j]); -// } -// } -// -// // update the posteriors vector -// final double log10LofK = kMinus0[jStop]; -// log10AlleleFrequencyPosteriors[k] = log10LofK + log10AlleleFrequencyPriors[k]; -// -// // can we abort early? -// lastK = k; -// maxLog10L = Math.max(maxLog10L, log10LofK); -// if ( log10LofK < maxLog10L - MAX_LOG10_ERROR_TO_STOP_EARLY ) { -// if ( DEBUG ) System.out.printf(" *** breaking early k=%d log10L=%.2f maxLog10L=%.2f%n", k, log10LofK, maxLog10L); -// done = true; -// } -// -// logY.rotate(); -// } -// -// return lastK; - } - public int linearExact(Map GLs, double[] log10AlleleFrequencyPriors, double[] log10AlleleFrequencyPosteriors, int idxAA, int idxAB, int idxBB) { @@ -605,82 +449,6 @@ public class ExactAFCalculationModel extends AlleleFrequencyCalculationModel { return calls; } - // ------------------------------------------------------------------------------------- - // - // Gold standard, but O(N^2), implementation. - // - // TODO -- remove me for clarity in this code - // - // ------------------------------------------------------------------------------------- - public int gdaN2GoldStandard(Map GLs, - double[] log10AlleleFrequencyPriors, - double[] log10AlleleFrequencyPosteriors, int idxAA, int idxAB, int idxBB) { - int numSamples = GLs.size(); - int numChr = 2*numSamples; - - double[][] logYMatrix = new double[1+numSamples][1+numChr]; - - for (int i=0; i <=numSamples; i++) - for (int j=0; j <=numChr; j++) - logYMatrix[i][j] = Double.NEGATIVE_INFINITY; - - //YMatrix[0][0] = 1.0; - logYMatrix[0][0] = 0.0; - int j=0; - - for ( Map.Entry sample : GLs.entrySet() ) { - j++; - - if ( !sample.getValue().hasLikelihoods() ) - continue; - - //double[] genotypeLikelihoods = MathUtils.normalizeFromLog10(GLs.get(sample).getLikelihoods()); - double[] genotypeLikelihoods = sample.getValue().getLikelihoods().getAsVector(); - //double logDenominator = Math.log10(2.0*j*(2.0*j-1)); - double logDenominator = MathUtils.log10Cache[2*j] + MathUtils.log10Cache[2*j-1]; - - // special treatment for k=0: iteration reduces to: - //YMatrix[j][0] = YMatrix[j-1][0]*genotypeLikelihoods[GenotypeType.AA.ordinal()]; - logYMatrix[j][0] = logYMatrix[j-1][0] + genotypeLikelihoods[idxAA]; - - for (int k=1; k <= 2*j; k++ ) { - - //double num = (2.0*j-k)*(2.0*j-k-1)*YMatrix[j-1][k] * genotypeLikelihoods[GenotypeType.AA.ordinal()]; - double logNumerator[]; - logNumerator = new double[3]; - if (k < 2*j-1) - logNumerator[0] = MathUtils.log10Cache[2*j-k] + MathUtils.log10Cache[2*j-k-1] + logYMatrix[j-1][k] + - genotypeLikelihoods[idxAA]; - else - logNumerator[0] = Double.NEGATIVE_INFINITY; - - - if (k < 2*j) - logNumerator[1] = MathUtils.log10Cache[2*k] + MathUtils.log10Cache[2*j-k]+ logYMatrix[j-1][k-1] + - genotypeLikelihoods[idxAB]; - else - logNumerator[1] = Double.NEGATIVE_INFINITY; - - if (k > 1) - logNumerator[2] = MathUtils.log10Cache[k] + MathUtils.log10Cache[k-1] + logYMatrix[j-1][k-2] + - genotypeLikelihoods[idxBB]; - else - logNumerator[2] = Double.NEGATIVE_INFINITY; - - double logNum = MathUtils.softMax(logNumerator); - - //YMatrix[j][k] = num/den; - logYMatrix[j][k] = logNum - logDenominator; - } - - } - - for (int k=0; k <= numChr; k++) - log10AlleleFrequencyPosteriors[k] = logYMatrix[j][k] + log10AlleleFrequencyPriors[k]; - - return numChr; - } - private final static void printLikelihoods(int numChr, double[][] logYMatrix, double[] log10AlleleFrequencyPriors) { int j = logYMatrix.length - 1; System.out.printf("-----------------------------------%n"); @@ -689,5 +457,4 @@ public class ExactAFCalculationModel extends AlleleFrequencyCalculationModel { System.out.printf(" %4d\t%8.2f\t%8.2f\t%8.2f%n", k, logYMatrix[j][k], log10AlleleFrequencyPriors[k], posterior); } } - } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedArgumentCollection.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedArgumentCollection.java index 7b8045581..3c8fd4451 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedArgumentCollection.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedArgumentCollection.java @@ -168,10 +168,6 @@ public class UnifiedArgumentCollection { @Argument(fullName = "GSA_PRODUCTION_ONLY", shortName = "GSA_PRODUCTION_ONLY", doc = "don't ever use me", required = false) public boolean GSA_PRODUCTION_ONLY = false; - @Hidden - @Argument(fullName = "exactCalculation", shortName = "exactCalculation", doc = "expt", required = false) - public ExactAFCalculationModel.ExactCalculation EXACT_CALCULATION_TYPE = ExactAFCalculationModel.ExactCalculation.LINEAR_EXPERIMENTAL; - @Hidden @Argument(fullName = "ignoreSNPAlleles", shortName = "ignoreSNPAlleles", doc = "expt", required = false) public boolean IGNORE_SNP_ALLELES = false; @@ -191,7 +187,6 @@ public class UnifiedArgumentCollection { uac.GLmodel = GLmodel; uac.AFmodel = AFmodel; - uac.EXACT_CALCULATION_TYPE = EXACT_CALCULATION_TYPE; uac.heterozygosity = heterozygosity; uac.PCR_error = PCR_error; uac.GenotypingMode = GenotypingMode; From cc23b0b8a9b184eee6908a2fcbfdf63318be11b1 Mon Sep 17 00:00:00 2001 From: Matt Hanna Date: Fri, 23 Sep 2011 14:52:31 -0400 Subject: [PATCH 168/196] Fix for recent change modelling unmapped shards: don't invoke optimization to combine mapped and unmapped shards. --- .../sting/gatk/datasources/reads/LowMemoryIntervalSharder.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/datasources/reads/LowMemoryIntervalSharder.java b/public/java/src/org/broadinstitute/sting/gatk/datasources/reads/LowMemoryIntervalSharder.java index ba6321121..bf5f33dc3 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/datasources/reads/LowMemoryIntervalSharder.java +++ b/public/java/src/org/broadinstitute/sting/gatk/datasources/reads/LowMemoryIntervalSharder.java @@ -59,7 +59,7 @@ public class LowMemoryIntervalSharder implements Iterator { */ public FilePointer next() { FilePointer current = wrappedIterator.next(); - while(wrappedIterator.hasNext() && current.minus(wrappedIterator.peek()) == 0) + while(wrappedIterator.hasNext() && current.isRegionUnmapped == wrappedIterator.peek().isRegionUnmapped && current.minus(wrappedIterator.peek()) == 0) current = current.combine(parser,wrappedIterator.next()); return current; } From e1cb5f6459a9e1c534acb583dd94f9412f35322e Mon Sep 17 00:00:00 2001 From: David Roazen Date: Fri, 23 Sep 2011 15:00:03 -0400 Subject: [PATCH 169/196] SnpEff annotator now assigns a functional class to each effect and distinguishes between actual effects and mere modifiers. -We now assign a functional class (nonsense, missense, silent, or none) to each SnpEff effect, and add a SNPEFF_FUNCTIONAL_CLASS annotation to the INFO field of the output VCF. -Effects are now prioritized according to both biological impact and functional class, instead of impact only. -Many of SnpEff's "low-impact" effects are now classified as "modifiers" with lower priority than every other effect. This includes such "effects" as DOWNSTREAM, UPSTREAM, INTRON, GENE, EXON, and others that really describe the location of the variant rather than its biological effect. This code will be short-lived (likely 1.2-only), as the next version of SnpEff will include most of these features directly. Checking this change into Stable+Unstable instead of Unstable because the current functional class stratification in VariantEval is basically broken and urgently needs to be fixed for production purposes. --- .../sting/gatk/walkers/annotator/SnpEff.java | 160 ++++++++++++------ .../stratifications/FunctionalClass.java | 19 ++- .../VariantAnnotatorIntegrationTest.java | 4 +- .../VariantEvalIntegrationTest.java | 2 +- 4 files changed, 126 insertions(+), 59 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SnpEff.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SnpEff.java index 8f99c6118..973b3277d 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SnpEff.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/SnpEff.java @@ -82,7 +82,8 @@ public class SnpEff extends InfoFieldAnnotation implements ExperimentalAnnotatio GENE_NAME_KEY ("SNPEFF_GENE_NAME", 3), GENE_BIOTYPE_KEY ("SNPEFF_GENE_BIOTYPE", 4), TRANSCRIPT_ID_KEY ("SNPEFF_TRANSCRIPT_ID", 6), - EXON_ID_KEY ("SNPEFF_EXON_ID", 7); + EXON_ID_KEY ("SNPEFF_EXON_ID", 7), + FUNCTIONAL_CLASS_KEY ("SNPEFF_FUNCTIONAL_CLASS", -1); // Actual text of the key private final String keyName; @@ -108,48 +109,73 @@ public class SnpEff extends InfoFieldAnnotation implements ExperimentalAnnotatio // Possible SnpEff biological effects. All effect names found in the SnpEff input file // are validated against this list. public enum EffectType { - NONE, - CHROMOSOME, - INTERGENIC, - UPSTREAM, - UTR_5_PRIME, - UTR_5_DELETED, - START_GAINED, - SPLICE_SITE_ACCEPTOR, - SPLICE_SITE_DONOR, - START_LOST, - SYNONYMOUS_START, - NON_SYNONYMOUS_START, - CDS, - GENE, - TRANSCRIPT, - EXON, - EXON_DELETED, - NON_SYNONYMOUS_CODING, - SYNONYMOUS_CODING, - FRAME_SHIFT, - CODON_CHANGE, - CODON_INSERTION, - CODON_CHANGE_PLUS_CODON_INSERTION, - CODON_DELETION, - CODON_CHANGE_PLUS_CODON_DELETION, - STOP_GAINED, - SYNONYMOUS_STOP, - NON_SYNONYMOUS_STOP, - STOP_LOST, - INTRON, - UTR_3_PRIME, - UTR_3_DELETED, - DOWNSTREAM, - INTRON_CONSERVED, - INTERGENIC_CONSERVED, - REGULATION, - CUSTOM, - WITHIN_NON_CODING_GENE + // High-impact effects: + FRAME_SHIFT (EffectFunctionalClass.NONE, false), + STOP_GAINED (EffectFunctionalClass.NONSENSE, false), + START_LOST (EffectFunctionalClass.NONE, false), + SPLICE_SITE_ACCEPTOR (EffectFunctionalClass.NONE, false), + SPLICE_SITE_DONOR (EffectFunctionalClass.NONE, false), + EXON_DELETED (EffectFunctionalClass.NONE, false), + STOP_LOST (EffectFunctionalClass.NONE, false), + + // Moderate-impact effects: + NON_SYNONYMOUS_CODING (EffectFunctionalClass.MISSENSE, false), + CODON_CHANGE (EffectFunctionalClass.NONE, false), + CODON_INSERTION (EffectFunctionalClass.NONE, false), + CODON_CHANGE_PLUS_CODON_INSERTION (EffectFunctionalClass.NONE, false), + CODON_DELETION (EffectFunctionalClass.NONE, false), + CODON_CHANGE_PLUS_CODON_DELETION (EffectFunctionalClass.NONE, false), + UTR_5_DELETED (EffectFunctionalClass.NONE, false), + UTR_3_DELETED (EffectFunctionalClass.NONE, false), + + // Low-impact effects: + SYNONYMOUS_CODING (EffectFunctionalClass.SILENT, false), + SYNONYMOUS_START (EffectFunctionalClass.SILENT, false), + NON_SYNONYMOUS_START (EffectFunctionalClass.SILENT, false), + SYNONYMOUS_STOP (EffectFunctionalClass.SILENT, false), + NON_SYNONYMOUS_STOP (EffectFunctionalClass.SILENT, false), + START_GAINED (EffectFunctionalClass.NONE, false), + + // Modifiers: + NONE (EffectFunctionalClass.NONE, true), + CHROMOSOME (EffectFunctionalClass.NONE, true), + INTERGENIC (EffectFunctionalClass.NONE, true), + UPSTREAM (EffectFunctionalClass.NONE, true), + UTR_5_PRIME (EffectFunctionalClass.NONE, true), + CDS (EffectFunctionalClass.NONE, true), + GENE (EffectFunctionalClass.NONE, true), + TRANSCRIPT (EffectFunctionalClass.NONE, true), + EXON (EffectFunctionalClass.NONE, true), + INTRON (EffectFunctionalClass.NONE, true), + UTR_3_PRIME (EffectFunctionalClass.NONE, true), + DOWNSTREAM (EffectFunctionalClass.NONE, true), + INTRON_CONSERVED (EffectFunctionalClass.NONE, true), + INTERGENIC_CONSERVED (EffectFunctionalClass.NONE, true), + REGULATION (EffectFunctionalClass.NONE, true), + CUSTOM (EffectFunctionalClass.NONE, true), + WITHIN_NON_CODING_GENE (EffectFunctionalClass.NONE, true); + + private final EffectFunctionalClass functionalClass; + private final boolean isModifier; + + EffectType ( EffectFunctionalClass functionalClass, boolean isModifier ) { + this.functionalClass = functionalClass; + this.isModifier = isModifier; + } + + public EffectFunctionalClass getFunctionalClass() { + return functionalClass; + } + + public boolean isModifier() { + return isModifier; + } } - // SnpEff labels each effect as either LOW, MODERATE, or HIGH impact. + // SnpEff labels each effect as either LOW, MODERATE, or HIGH impact. We take the additional step of + // classifying some of the LOW impact effects as MODIFIERs. public enum EffectImpact { + MODIFIER (0), LOW (1), MODERATE (2), HIGH (3); @@ -163,6 +189,10 @@ public class SnpEff extends InfoFieldAnnotation implements ExperimentalAnnotatio public boolean isHigherImpactThan ( EffectImpact other ) { return this.severityRating > other.severityRating; } + + public boolean isSameImpactAs ( EffectImpact other ) { + return this.severityRating == other.severityRating; + } } // SnpEff labels most effects as either CODING or NON_CODING, but sometimes omits this information. @@ -172,6 +202,24 @@ public class SnpEff extends InfoFieldAnnotation implements ExperimentalAnnotatio UNKNOWN } + // We assign a functional class to each SnpEff effect. + public enum EffectFunctionalClass { + NONE (0), + SILENT (1), + MISSENSE (2), + NONSENSE (3); + + private final int priority; + + EffectFunctionalClass ( int priority ) { + this.priority = priority; + } + + public boolean isHigherPriorityThan ( EffectFunctionalClass other ) { + return this.priority > other.priority; + } + } + public void initialize ( AnnotatorCompatibleWalker walker, GenomeAnalysisEngine toolkit, Set headerLines ) { // Make sure that we actually have a valid SnpEff rod binding (just in case the user specified -A SnpEff // without providing a SnpEff rod via --snpEffFile): @@ -336,7 +384,8 @@ public class SnpEff extends InfoFieldAnnotation implements ExperimentalAnnotatio InfoFieldKey.GENE_NAME_KEY.getKeyName(), InfoFieldKey.GENE_BIOTYPE_KEY.getKeyName(), InfoFieldKey.TRANSCRIPT_ID_KEY.getKeyName(), - InfoFieldKey.EXON_ID_KEY.getKeyName() + InfoFieldKey.EXON_ID_KEY.getKeyName(), + InfoFieldKey.FUNCTIONAL_CLASS_KEY.getKeyName() ); } @@ -349,7 +398,8 @@ public class SnpEff extends InfoFieldAnnotation implements ExperimentalAnnotatio new VCFInfoHeaderLine(InfoFieldKey.GENE_NAME_KEY.getKeyName(), 1, VCFHeaderLineType.String, "Gene name for the highest-impact effect resulting from the current variant"), new VCFInfoHeaderLine(InfoFieldKey.GENE_BIOTYPE_KEY.getKeyName(), 1, VCFHeaderLineType.String, "Gene biotype for the highest-impact effect resulting from the current variant"), new VCFInfoHeaderLine(InfoFieldKey.TRANSCRIPT_ID_KEY.getKeyName(), 1, VCFHeaderLineType.String, "Transcript ID for the highest-impact effect resulting from the current variant"), - new VCFInfoHeaderLine(InfoFieldKey.EXON_ID_KEY.getKeyName(), 1, VCFHeaderLineType.String, "Exon ID for the highest-impact effect resulting from the current variant") + new VCFInfoHeaderLine(InfoFieldKey.EXON_ID_KEY.getKeyName(), 1, VCFHeaderLineType.String, "Exon ID for the highest-impact effect resulting from the current variant"), + new VCFInfoHeaderLine(InfoFieldKey.FUNCTIONAL_CLASS_KEY.getKeyName(), 1, VCFHeaderLineType.String, "Functional class of the highest-impact effect resulting from the current variant: " + Arrays.toString(EffectFunctionalClass.values())) ); } @@ -411,11 +461,16 @@ public class SnpEff extends InfoFieldAnnotation implements ExperimentalAnnotatio return; } - try { - impact = EffectImpact.valueOf(effectMetadata[InfoFieldKey.IMPACT_KEY.getFieldIndex()]); + if ( effect != null && effect.isModifier() ) { + impact = EffectImpact.MODIFIER; } - catch ( IllegalArgumentException e ) { - parseError(String.format("Unrecognized value for effect impact: %s", effectMetadata[InfoFieldKey.IMPACT_KEY.getFieldIndex()])); + else { + try { + impact = EffectImpact.valueOf(effectMetadata[InfoFieldKey.IMPACT_KEY.getFieldIndex()]); + } + catch ( IllegalArgumentException e ) { + parseError(String.format("Unrecognized value for effect impact: %s", effectMetadata[InfoFieldKey.IMPACT_KEY.getFieldIndex()])); + } } codonChange = effectMetadata[InfoFieldKey.CODON_CHANGE_KEY.getFieldIndex()]; @@ -472,9 +527,17 @@ public class SnpEff extends InfoFieldAnnotation implements ExperimentalAnnotatio } // Otherwise, both effects are either in or not in a coding gene, so we compare the impacts - // of the effects themselves: + // of the effects themselves. Effects with the same impact are tie-broken using the + // functional class of the effect: - return impact.isHigherImpactThan(other.impact); + if ( impact.isHigherImpactThan(other.impact) ) { + return true; + } + else if ( impact.isSameImpactAs(other.impact) ) { + return effect.getFunctionalClass().isHigherPriorityThan(other.effect.getFunctionalClass()); + } + + return false; } public Map getAnnotations() { @@ -488,6 +551,7 @@ public class SnpEff extends InfoFieldAnnotation implements ExperimentalAnnotatio addAnnotation(annotations, InfoFieldKey.GENE_BIOTYPE_KEY.getKeyName(), geneBiotype); addAnnotation(annotations, InfoFieldKey.TRANSCRIPT_ID_KEY.getKeyName(), transcriptID); addAnnotation(annotations, InfoFieldKey.EXON_ID_KEY.getKeyName(), exonID); + addAnnotation(annotations, InfoFieldKey.FUNCTIONAL_CLASS_KEY.getKeyName(), effect.getFunctionalClass().toString()); return annotations; } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java index 88ffcaaeb..1dc047b5d 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/FunctionalClass.java @@ -62,14 +62,17 @@ public class FunctionalClass extends VariantStratifier { annotationId++; } while (eval.hasAttribute(key)); - } else if ( eval.hasAttribute(SnpEff.InfoFieldKey.EFFECT_KEY.getKeyName() ) ) { - SnpEff.EffectType snpEffType = SnpEff.EffectType.valueOf(eval.getAttribute(SnpEff.InfoFieldKey.EFFECT_KEY.getKeyName()).toString()); - if ( snpEffType == SnpEff.EffectType.STOP_GAINED ) - type = FunctionalType.nonsense; - else if ( snpEffType == SnpEff.EffectType.NON_SYNONYMOUS_CODING ) - type = FunctionalType.missense; - else if ( snpEffType == SnpEff.EffectType.SYNONYMOUS_CODING ) - type = FunctionalType.silent; + } else if ( eval.hasAttribute(SnpEff.InfoFieldKey.FUNCTIONAL_CLASS_KEY.getKeyName()) ) { + try { + SnpEff.EffectFunctionalClass snpEffFunctionalClass = SnpEff.EffectFunctionalClass.valueOf(eval.getAttribute(SnpEff.InfoFieldKey.FUNCTIONAL_CLASS_KEY.getKeyName()).toString()); + if ( snpEffFunctionalClass == SnpEff.EffectFunctionalClass.NONSENSE ) + type = FunctionalType.nonsense; + else if ( snpEffFunctionalClass == SnpEff.EffectFunctionalClass.MISSENSE ) + type = FunctionalType.missense; + else if ( snpEffFunctionalClass == SnpEff.EffectFunctionalClass.SILENT ) + type = FunctionalType.silent; + } + catch ( Exception e ) {} // don't error out if the type isn't supported } if ( type != null ) { diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorIntegrationTest.java index 2c06c6b7f..04bff8d41 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/annotator/VariantAnnotatorIntegrationTest.java @@ -132,9 +132,9 @@ public class VariantAnnotatorIntegrationTest extends WalkerTest { WalkerTestSpec spec = new WalkerTestSpec( "-T VariantAnnotator -R " + hg19Reference + " -NO_HEADER -o %s -A SnpEff --variant " + validationDataLocation + "1kg_exomes_unfiltered.AFR.unfiltered.vcf --snpEffFile " + validationDataLocation + - "snpEff.AFR.unfiltered.vcf -L 1:1-1,500,000", + "snpEff.AFR.unfiltered.vcf -L 1:1-1,500,000 -L 2:232,325,429", 1, - Arrays.asList("ed9d1b37b0bd8b65ff9ce2688e0e102e") + Arrays.asList("122321a85e448f21679f6ca15c5e22ad") ); executeTest("Testing SnpEff annotations", spec); } diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java index b90e6d0ff..9fe253ecb 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/varianteval/VariantEvalIntegrationTest.java @@ -30,7 +30,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { "-o %s" ), 1, - Arrays.asList("f5f811ceb973d7fd6c1b2b734f1b2b12") + Arrays.asList("d9dcb352c53106f54fcc981f15d35a90") ); executeTest("testFunctionClassWithSnpeff", spec); } From 9ea40f2e416727564849b749c0746fb688f6f1be Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Fri, 23 Sep 2011 16:37:08 -0400 Subject: [PATCH 170/196] Deletions/Insertions in hard clip and bug fixes * Deletions now count as hard clipped bases in order to recover the original alignment start of a clipped read. * Insertions do not count as hard clipped bases for the same reason. * This created a bug in the previous cigar cleaning function. Fixed. --- .../sting/utils/clipreads/ClippingOp.java | 59 ++++++++++--------- 1 file changed, 32 insertions(+), 27 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingOp.java b/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingOp.java index 47ce8165c..81d00d9d7 100644 --- a/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingOp.java +++ b/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingOp.java @@ -398,7 +398,9 @@ public class ClippingOp { for (int i = 1; i <= 2; i++) { int shift = 0; + int totalHardClip = 0; boolean readHasStarted = false; + boolean addedHardClips = false; while(!cigarStack.empty()) { CigarElement cigarElement = cigarStack.pop(); @@ -408,14 +410,33 @@ public class ClippingOp { cigarElement.getOperator() != CigarOperator.DELETION && cigarElement.getOperator() != CigarOperator.HARD_CLIP) readHasStarted = true; + + else if ( !readHasStarted && cigarElement.getOperator() == CigarOperator.HARD_CLIP) + totalHardClip += cigarElement.getLength(); + else if ( !readHasStarted && cigarElement.getOperator() == CigarOperator.INSERTION) shift += cigarElement.getLength(); - if (readHasStarted || cigarElement.getOperator() == CigarOperator.HARD_CLIP) { - if (i==1) + else if ( !readHasStarted && cigarElement.getOperator() == CigarOperator.DELETION) + totalHardClip += cigarElement.getLength(); + + if (readHasStarted) { + if (i==1) { + if (!addedHardClips) { + if (totalHardClip > 0) + inverseCigarStack.push(new CigarElement(totalHardClip, CigarOperator.HARD_CLIP)); + addedHardClips = true; + } inverseCigarStack.push(cigarElement); - else + } + else { + if (!addedHardClips) { + if (totalHardClip > 0) + cleanCigar.add(new CigarElement(totalHardClip, CigarOperator.HARD_CLIP)); + addedHardClips = true; + } cleanCigar.add(cigarElement); + } } } // first pass (i=1) is from end to start of the cigar elements @@ -434,7 +455,6 @@ public class ClippingOp { private int calculateAlignmentStartShift(Cigar oldCigar, Cigar newCigar) { int newShift = 0; int oldShift = 0; - int deletionShift = 0; for (CigarElement cigarElement : newCigar.getCigarElements()) { if (cigarElement.getOperator() == CigarOperator.HARD_CLIP || cigarElement.getOperator() == CigarOperator.SOFT_CLIP) @@ -449,34 +469,19 @@ public class ClippingOp { else break; } - - int basesClipped = 0; - for (CigarElement cigarElement : oldCigar.getCigarElements()) { - if (basesClipped > newShift) // are we beyond the clipped region? - break; - - else if (cigarElement.getOperator() == CigarOperator.DELETION) // if this is a deletion, we have to adjust the starting shift - deletionShift += cigarElement.getLength(); - - else - basesClipped += cigarElement.getLength(); - } - - return newShift - oldShift + deletionShift; + return newShift - oldShift; } private int calculateHardClippingAlignmentShift(CigarElement cigarElement, int clippedLength) { - if (cigarElement.getOperator() == CigarOperator.INSERTION) { - int cigarElementLength = cigarElement.getLength(); - if (clippedLength >= cigarElementLength) - return -cigarElement.getLength(); - else - return -clippedLength; - } + // Insertions should be discounted from the total hard clip count + if (cigarElement.getOperator() == CigarOperator.INSERTION) + return -clippedLength; -// if (cigarElement.getOperator() == CigarOperator.DELETION) -// return cigarElement.getLength(); + // Deletions should be added to the total hard clip count + else if (cigarElement.getOperator() == CigarOperator.DELETION) + return cigarElement.getLength(); + // There is no shift if we are not clipping an indel return 0; } From b66841f17941fcd2639c081e8b3d4eeec94b9721 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 23 Sep 2011 17:29:34 -0400 Subject: [PATCH 171/196] Static cache for binomial probability -- Very low level performance optimization --- .../genotyper/UnifiedGenotyperEngine.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java index 87dd37bf6..9e8cc07a6 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperEngine.java @@ -544,6 +544,21 @@ public class UnifiedGenotyperEngine { AFs[i] = AlleleFrequencyCalculationModel.VALUE_NOT_CALCULATED; } + private final static double[] binomialProbabilityDepthCache = new double[10000]; + static { + for ( int i = 1; i < binomialProbabilityDepthCache.length; i++ ) { + binomialProbabilityDepthCache[i] = MathUtils.binomialProbability(0, i, 0.5); + } + } + + private final double getRefBinomialProb(final int depth) { + if ( depth < binomialProbabilityDepthCache.length ) + return binomialProbabilityDepthCache[depth]; + else + return MathUtils.binomialProbability(0, depth, 0.5); + } + + private VariantCallContext estimateReferenceConfidence(VariantContext vc, Map contexts, double theta, boolean ignoreCoveredSamples, double initialPofRef) { if ( contexts == null ) return null; @@ -567,7 +582,7 @@ public class UnifiedGenotyperEngine { depth = context.getExtendedEventPileup().size(); } - P_of_ref *= 1.0 - (theta / 2.0) * MathUtils.binomialProbability(0, depth, 0.5); + P_of_ref *= 1.0 - (theta / 2.0) * getRefBinomialProb(depth); } return new VariantCallContext(vc, QualityUtils.phredScaleErrorRate(1.0 - P_of_ref) >= UAC.STANDARD_CONFIDENCE_FOR_CALLING, false); From fbe3c1e0b3175f7bf838bd93182a7bcbb4c67e9a Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Fri, 23 Sep 2011 19:00:19 -0400 Subject: [PATCH 173/196] Adding warning on HardClipping Hard Clipping is still under heavy development and should not be used by anyone less prepared than MacGyver. --- .../sting/utils/clipreads/ClippingRepresentation.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingRepresentation.java b/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingRepresentation.java index 0dbe55726..d574ba2f0 100644 --- a/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingRepresentation.java +++ b/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingRepresentation.java @@ -21,6 +21,8 @@ public enum ClippingRepresentation { SOFTCLIP_BASES, /** + * WARNING: THIS OPTION IS STILL UNDER DEVELOPMENT AND IS NOT SUPPORTED. + * * Change the read's cigar string to hard clip (H, see sam-spec) away the bases. * Hard clipping, unlike soft clipping, actually removes bases from the read, * reducing the resulting file's size but introducing an irrevesible (i.e., From 8ceb93b8ace3ba9d2cb60800a457ebe77c00b32d Mon Sep 17 00:00:00 2001 From: Khalid Shakir Date: Fri, 23 Sep 2011 21:03:22 -0400 Subject: [PATCH 174/196] Fixed an integration test which crashed on the out of date LSF DRMAA library when run against the obsolete LSF dotkit instead of .combined_LSF_SGE --- .../drmaa/v1_0/JnaSessionIntegrationTest.java | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/public/java/test/org/broadinstitute/sting/jna/drmaa/v1_0/JnaSessionIntegrationTest.java b/public/java/test/org/broadinstitute/sting/jna/drmaa/v1_0/JnaSessionIntegrationTest.java index 3dfd0550d..48f4c3777 100644 --- a/public/java/test/org/broadinstitute/sting/jna/drmaa/v1_0/JnaSessionIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/jna/drmaa/v1_0/JnaSessionIntegrationTest.java @@ -34,6 +34,7 @@ import java.io.File; import java.util.*; public class JnaSessionIntegrationTest extends BaseTest { + private String implementation = null; private static final SessionFactory factory = new JnaSessionFactory(); @Test @@ -44,10 +45,23 @@ public class JnaSessionIntegrationTest extends BaseTest { System.out.println(String.format("DRMAA contact(s): %s", session.getContact())); System.out.println(String.format("DRM system(s): %s", session.getDrmSystem())); System.out.println(String.format("DRMAA implementation(s): %s", session.getDrmaaImplementation())); + this.implementation = session.getDrmaaImplementation(); } - @Test + @Test(dependsOnMethods = { "testDrmaa" }) public void testSubmitEcho() throws Exception { + if (implementation.contains("LSF")) { + System.err.println(" ***********************************************************"); + System.err.println(" *************************************************************"); + System.err.println(" **** ****"); + System.err.println(" **** Skipping JnaSessionIntegrationTest.testSubmitEcho() ****"); + System.err.println(" **** Are you using the dotkit .combined_LSF_SGE? ****"); + System.err.println(" **** ****"); + System.err.println(" *************************************************************"); + System.err.println(" ***********************************************************"); + return; + } + File outFile = createNetworkTempFile("JnaSessionIntegrationTest-", ".out"); Session session = factory.getSession(); session.init(null); From 0e74cc3c749b7dada37a4f26b6943666e6b4c743 Mon Sep 17 00:00:00 2001 From: Guillermo del Angel Date: Fri, 23 Sep 2011 21:58:20 -0400 Subject: [PATCH 175/196] a) Treat SNP genotype likelihoods just as indels, in the sense that they're always normalized as PL's so one of them will always be zero. This creates minor numerical differences in Qual and annotations due to numerical approximations in AF computation. b) Intermediate CombineVariants fixes, not ready yet --- .../genotyper/SNPGenotypeLikelihoodsCalculationModel.java | 6 ++++++ .../sting/gatk/walkers/variantutils/CombineVariants.java | 2 +- .../sting/utils/variantcontext/VariantContextUtils.java | 4 +++- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/SNPGenotypeLikelihoodsCalculationModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/SNPGenotypeLikelihoodsCalculationModel.java index 6905ce4a4..3fed4542c 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/SNPGenotypeLikelihoodsCalculationModel.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/SNPGenotypeLikelihoodsCalculationModel.java @@ -31,6 +31,7 @@ import org.broadinstitute.sting.gatk.contexts.AlignmentContextUtils; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; import org.broadinstitute.sting.utils.BaseUtils; +import org.broadinstitute.sting.utils.MathUtils; import org.broadinstitute.sting.utils.baq.BAQ; import org.broadinstitute.sting.utils.exceptions.StingException; import org.broadinstitute.sting.utils.genotype.DiploidGenotype; @@ -122,6 +123,11 @@ public class SNPGenotypeLikelihoodsCalculationModel extends GenotypeLikelihoodsC aList.add(refAllele); aList.add(altAllele); double[] dlike = new double[]{likelihoods[refGenotype.ordinal()],likelihoods[hetGenotype.ordinal()],likelihoods[homGenotype.ordinal()]} ; + double maxElement = MathUtils.max(dlike[AlleleFrequencyCalculationModel.GenotypeType.AA.ordinal()], + dlike[AlleleFrequencyCalculationModel.GenotypeType.AB.ordinal()],dlike[AlleleFrequencyCalculationModel.GenotypeType.BB.ordinal()]); + for (int i=0; i < dlike.length; i++) + dlike[i] -= maxElement; + GLs.put(sample.getKey(), new MultiallelicGenotypeLikelihoods(sample.getKey(), aList, dlike, getFilteredDepth(pileup))); } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariants.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariants.java index 3e3b29a7f..40b704570 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariants.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariants.java @@ -274,11 +274,11 @@ public class CombineVariants extends RodWalker { else { mergedVCs = preMergedVCs; } - for ( VariantContext mergedVC : mergedVCs ) { // only operate at the start of events if ( mergedVC == null ) continue; + System.out.println(mergedVC.toString()); HashMap attributes = new HashMap(mergedVC.getAttributes()); // re-compute chromosome counts diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java index ca9c71eba..806bd2013 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java @@ -594,7 +594,9 @@ public class VariantContextUtils { // if we have more alternate alleles in the merged VC than in one or more of the original VCs, we need to strip out the GL/PLs (because they are no longer accurate) for ( VariantContext vc : VCs ) { - if ( vc.alleles.size() != alleles.size() ) { + if (vc.alleles.size() == 1) + continue; + if ( vc.alleles.size() != alleles.size()) { genotypes = stripPLs(genotypes); break; } From c0bb0cb465c6d0761d524e4b139f182325b7814d Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Sat, 24 Sep 2011 08:48:33 -0400 Subject: [PATCH 176/196] Make DiploidGenotype enum private to walkers.genotyper --- .../genotype => gatk/walkers/genotyper}/DiploidGenotype.java | 4 ++-- .../gatk/walkers/genotyper/DiploidIndelGenotypePriors.java | 1 - .../gatk/walkers/genotyper/DiploidSNPGenotypeLikelihoods.java | 1 - .../gatk/walkers/genotyper/DiploidSNPGenotypePriors.java | 1 - .../genotyper/SNPGenotypeLikelihoodsCalculationModel.java | 1 - .../walkers/genotyper}/DiploidGenotypeUnitTest.java | 3 ++- .../gatk/walkers/genotyper/GenotypeLikelihoodsUnitTest.java | 1 - 7 files changed, 4 insertions(+), 8 deletions(-) rename public/java/src/org/broadinstitute/sting/{utils/genotype => gatk/walkers/genotyper}/DiploidGenotype.java (98%) rename public/java/test/org/broadinstitute/sting/{utils/genotype => gatk/walkers/genotyper}/DiploidGenotypeUnitTest.java (95%) diff --git a/public/java/src/org/broadinstitute/sting/utils/genotype/DiploidGenotype.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidGenotype.java similarity index 98% rename from public/java/src/org/broadinstitute/sting/utils/genotype/DiploidGenotype.java rename to public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidGenotype.java index 1c2cfe2e1..b5987963f 100755 --- a/public/java/src/org/broadinstitute/sting/utils/genotype/DiploidGenotype.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidGenotype.java @@ -23,7 +23,7 @@ * THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package org.broadinstitute.sting.utils.genotype; +package org.broadinstitute.sting.gatk.walkers.genotyper; import org.broadinstitute.sting.utils.BaseUtils; @@ -34,7 +34,7 @@ import org.broadinstitute.sting.utils.BaseUtils; * Time: 6:46:09 PM * To change this template use File | Settings | File Templates. */ -public enum DiploidGenotype { +enum DiploidGenotype { AA ('A', 'A'), AC ('A', 'C'), AG ('A', 'G'), diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidIndelGenotypePriors.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidIndelGenotypePriors.java index 696a74de8..d8c911092 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidIndelGenotypePriors.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidIndelGenotypePriors.java @@ -2,7 +2,6 @@ package org.broadinstitute.sting.gatk.walkers.genotyper; import org.broadinstitute.sting.gatk.walkers.indels.HaplotypeIndelErrorModel; import org.broadinstitute.sting.utils.MathUtils; -import org.broadinstitute.sting.utils.genotype.DiploidGenotype; /** * Created by IntelliJ IDEA. diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidSNPGenotypeLikelihoods.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidSNPGenotypeLikelihoods.java index ec180f0cd..71eea2467 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidSNPGenotypeLikelihoods.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidSNPGenotypeLikelihoods.java @@ -30,7 +30,6 @@ import org.broadinstitute.sting.utils.BaseUtils; import org.broadinstitute.sting.utils.MathUtils; import org.broadinstitute.sting.utils.QualityUtils; import org.broadinstitute.sting.utils.exceptions.UserException; -import org.broadinstitute.sting.utils.genotype.DiploidGenotype; import org.broadinstitute.sting.utils.pileup.FragmentPileup; import org.broadinstitute.sting.utils.pileup.PileupElement; import org.broadinstitute.sting.utils.pileup.ReadBackedPileup; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidSNPGenotypePriors.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidSNPGenotypePriors.java index b9ed17d3e..71854591f 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidSNPGenotypePriors.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidSNPGenotypePriors.java @@ -26,7 +26,6 @@ package org.broadinstitute.sting.gatk.walkers.genotyper; import org.broadinstitute.sting.utils.MathUtils; -import org.broadinstitute.sting.utils.genotype.DiploidGenotype; import java.util.Arrays; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/SNPGenotypeLikelihoodsCalculationModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/SNPGenotypeLikelihoodsCalculationModel.java index 6905ce4a4..30acd6896 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/SNPGenotypeLikelihoodsCalculationModel.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/SNPGenotypeLikelihoodsCalculationModel.java @@ -33,7 +33,6 @@ import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; import org.broadinstitute.sting.utils.BaseUtils; import org.broadinstitute.sting.utils.baq.BAQ; import org.broadinstitute.sting.utils.exceptions.StingException; -import org.broadinstitute.sting.utils.genotype.DiploidGenotype; import org.broadinstitute.sting.utils.pileup.PileupElement; import org.broadinstitute.sting.utils.pileup.ReadBackedPileup; import org.broadinstitute.sting.utils.pileup.ReadBackedPileupImpl; diff --git a/public/java/test/org/broadinstitute/sting/utils/genotype/DiploidGenotypeUnitTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidGenotypeUnitTest.java similarity index 95% rename from public/java/test/org/broadinstitute/sting/utils/genotype/DiploidGenotypeUnitTest.java rename to public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidGenotypeUnitTest.java index e4f8b12e3..4e72b37a4 100644 --- a/public/java/test/org/broadinstitute/sting/utils/genotype/DiploidGenotypeUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/DiploidGenotypeUnitTest.java @@ -1,5 +1,6 @@ -package org.broadinstitute.sting.utils.genotype; +package org.broadinstitute.sting.gatk.walkers.genotyper; +import org.broadinstitute.sting.gatk.walkers.genotyper.DiploidGenotype; import org.testng.Assert; import org.broadinstitute.sting.BaseTest; diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/GenotypeLikelihoodsUnitTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/GenotypeLikelihoodsUnitTest.java index 9882ce869..425b969e2 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/GenotypeLikelihoodsUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/GenotypeLikelihoodsUnitTest.java @@ -1,7 +1,6 @@ package org.broadinstitute.sting.gatk.walkers.genotyper; import org.testng.Assert; -import org.broadinstitute.sting.utils.genotype.DiploidGenotype; import org.broadinstitute.sting.BaseTest; import org.testng.annotations.Test; From f792353dcd59948aab4da10521b32adeb990487d Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Sat, 24 Sep 2011 08:56:45 -0400 Subject: [PATCH 177/196] Framework for genotype unit test --- .../sting/utils/variantcontext/Genotype.java | 3 +- .../variantcontext/GenotypeUnitTest.java | 84 +++++++++++++++++++ 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 public/java/test/org/broadinstitute/sting/utils/variantcontext/GenotypeUnitTest.java diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/Genotype.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/Genotype.java index fd8c70abe..7ab3f81f0 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/Genotype.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/Genotype.java @@ -268,7 +268,8 @@ public class Genotype { * @param the value type * @return a sting, enclosed in {}, with comma seperated key value pairs in order of the keys */ - public static , V> String sortedString(Map c) { + private static , V> String sortedString(Map c) { + // NOTE -- THIS IS COPIED FROM GATK UTILS TO ALLOW US TO KEEP A SEPARATION BETWEEN THE GATK AND VCF CODECS List t = new ArrayList(c.keySet()); Collections.sort(t); diff --git a/public/java/test/org/broadinstitute/sting/utils/variantcontext/GenotypeUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/variantcontext/GenotypeUnitTest.java new file mode 100644 index 000000000..c4f1efd04 --- /dev/null +++ b/public/java/test/org/broadinstitute/sting/utils/variantcontext/GenotypeUnitTest.java @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2011, The Broad Institute + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +// our package +package org.broadinstitute.sting.utils.variantcontext; + + +// the imports for unit testing. + + +import org.broadinstitute.sting.BaseTest; +import org.testng.Assert; +import org.testng.annotations.BeforeSuite; +import org.testng.annotations.Test; + +import java.util.Arrays; +import java.util.List; + + +public class GenotypeUnitTest extends BaseTest { + Allele A, Aref, T; + + @BeforeSuite + public void before() { + A = Allele.create("A"); + Aref = Allele.create("A", true); + T = Allele.create("T"); + } + +// public Genotype(String sampleName, List alleles, double negLog10PError, Set filters, Map attributes, boolean isPhased) { +// public Genotype(String sampleName, List alleles, double negLog10PError, Set filters, Map attributes, boolean isPhased, double[] log10Likelihoods) { +// public Genotype(String sampleName, List alleles, double negLog10PError, double[] log10Likelihoods) +// public Genotype(String sampleName, List alleles, double negLog10PError) +// public Genotype(String sampleName, List alleles) +// public List getAlleles() +// public List getAlleles(Allele allele) +// public Allele getAllele(int i) +// public boolean isPhased() +// public int getPloidy() +// public Type getType() +// public boolean isHom() +// public boolean isHomRef() +// public boolean isHomVar() +// public boolean isHet() +// public boolean isNoCall() +// public boolean isCalled() +// public boolean isAvailable() +// public boolean hasLikelihoods() +// public GenotypeLikelihoods getLikelihoods() +// public boolean sameGenotype(Genotype other) +// public boolean sameGenotype(Genotype other, boolean ignorePhase) +// public String getSampleName() +// public boolean hasNegLog10PError() +// public double getNegLog10PError() +// public double getPhredScaledQual() +// public boolean hasAttribute(String key) +// public Object getAttribute(String key) +// public Object getAttribute(String key, Object defaultValue) +// public String getAttributeAsString(String key, String defaultValue) +// public int getAttributeAsInt(String key, int defaultValue) +// public double getAttributeAsDouble(String key, double defaultValue) +// public boolean getAttributeAsBoolean(String key, boolean defaultValue) +} From 92acff46e5e99a8875eafa626c45a83b3751d01b Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Sat, 24 Sep 2011 09:14:05 -0400 Subject: [PATCH 178/196] Moved Haplotype into Utils root --- .../sting/gatk/walkers/annotator/HaplotypeScore.java | 2 +- .../IndelGenotypeLikelihoodsCalculationModel.java | 7 +++---- .../gatk/walkers/indels/HaplotypeIndelErrorModel.java | 2 +- .../sting/gatk/walkers/indels/PairHMMIndelErrorModel.java | 2 +- .../sting/utils/{genotype => }/Haplotype.java | 3 +-- 5 files changed, 7 insertions(+), 9 deletions(-) rename public/java/src/org/broadinstitute/sting/utils/{genotype => }/Haplotype.java (98%) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/HaplotypeScore.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/HaplotypeScore.java index df6da3b85..c142109fa 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/HaplotypeScore.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/HaplotypeScore.java @@ -34,12 +34,12 @@ import org.broadinstitute.sting.gatk.walkers.annotator.interfaces.InfoFieldAnnot import org.broadinstitute.sting.gatk.walkers.annotator.interfaces.StandardAnnotation; import org.broadinstitute.sting.gatk.walkers.genotyper.IndelGenotypeLikelihoodsCalculationModel; import org.broadinstitute.sting.utils.BaseUtils; +import org.broadinstitute.sting.utils.Haplotype; import org.broadinstitute.sting.utils.MathUtils; import org.broadinstitute.sting.utils.QualityUtils; import org.broadinstitute.sting.utils.codecs.vcf.VCFHeaderLineType; import org.broadinstitute.sting.utils.codecs.vcf.VCFInfoHeaderLine; import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; -import org.broadinstitute.sting.utils.genotype.Haplotype; import org.broadinstitute.sting.utils.pileup.PileupElement; import org.broadinstitute.sting.utils.pileup.ReadBackedPileup; import org.broadinstitute.sting.utils.sam.AlignmentUtils; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/IndelGenotypeLikelihoodsCalculationModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/IndelGenotypeLikelihoodsCalculationModel.java index ec5eefd60..11cde5ffe 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/IndelGenotypeLikelihoodsCalculationModel.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/IndelGenotypeLikelihoodsCalculationModel.java @@ -34,10 +34,9 @@ import org.broadinstitute.sting.gatk.walkers.indels.HaplotypeIndelErrorModel; import org.broadinstitute.sting.gatk.walkers.indels.PairHMMIndelErrorModel; import org.broadinstitute.sting.utils.BaseUtils; import org.broadinstitute.sting.utils.GenomeLoc; -import org.broadinstitute.sting.utils.MathUtils; +import org.broadinstitute.sting.utils.Haplotype; import org.broadinstitute.sting.utils.collections.Pair; import org.broadinstitute.sting.utils.exceptions.StingException; -import org.broadinstitute.sting.utils.genotype.Haplotype; import org.broadinstitute.sting.utils.pileup.ExtendedEventPileupElement; import org.broadinstitute.sting.utils.pileup.PileupElement; import org.broadinstitute.sting.utils.pileup.ReadBackedExtendedEventPileup; @@ -396,8 +395,8 @@ public class IndelGenotypeLikelihoodsCalculationModel extends GenotypeLikelihood System.out.format("hsize: %d eventLength: %d refSize: %d, locStart: %d numpr: %d\n",hsize,eventLength, (int)ref.getWindow().size(), loc.getStart(), numPrefBases); //System.out.println(eventLength); - haplotypeMap = Haplotype.makeHaplotypeListFromAlleles( alleleList, loc.getStart(), - ref, hsize, numPrefBases); + haplotypeMap = Haplotype.makeHaplotypeListFromAlleles(alleleList, loc.getStart(), + ref, hsize, numPrefBases); // For each sample, get genotype likelihoods based on pileup // compute prior likelihoods on haplotypes, and initialize haplotype likelihood matrix with them. diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/HaplotypeIndelErrorModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/HaplotypeIndelErrorModel.java index 232e468f9..3b3f54b05 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/HaplotypeIndelErrorModel.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/HaplotypeIndelErrorModel.java @@ -26,9 +26,9 @@ package org.broadinstitute.sting.gatk.walkers.indels; import net.sf.samtools.SAMRecord; +import org.broadinstitute.sting.utils.Haplotype; import org.broadinstitute.sting.utils.MathUtils; import org.broadinstitute.sting.utils.QualityUtils; -import org.broadinstitute.sting.utils.genotype.Haplotype; import org.broadinstitute.sting.utils.pileup.ReadBackedPileup; import org.broadinstitute.sting.utils.sam.ReadUtils; import org.broadinstitute.sting.utils.variantcontext.Allele; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/PairHMMIndelErrorModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/PairHMMIndelErrorModel.java index 2d7969230..4f9bccc42 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/PairHMMIndelErrorModel.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/PairHMMIndelErrorModel.java @@ -29,8 +29,8 @@ import net.sf.samtools.Cigar; import net.sf.samtools.CigarElement; import net.sf.samtools.CigarOperator; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; +import org.broadinstitute.sting.utils.Haplotype; import org.broadinstitute.sting.utils.MathUtils; -import org.broadinstitute.sting.utils.genotype.Haplotype; import org.broadinstitute.sting.utils.pileup.PileupElement; import org.broadinstitute.sting.utils.pileup.ReadBackedPileup; import org.broadinstitute.sting.utils.sam.GATKSAMRecord; diff --git a/public/java/src/org/broadinstitute/sting/utils/genotype/Haplotype.java b/public/java/src/org/broadinstitute/sting/utils/Haplotype.java similarity index 98% rename from public/java/src/org/broadinstitute/sting/utils/genotype/Haplotype.java rename to public/java/src/org/broadinstitute/sting/utils/Haplotype.java index a17e81461..ce2ca2c28 100755 --- a/public/java/src/org/broadinstitute/sting/utils/genotype/Haplotype.java +++ b/public/java/src/org/broadinstitute/sting/utils/Haplotype.java @@ -22,10 +22,9 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -package org.broadinstitute.sting.utils.genotype; +package org.broadinstitute.sting.utils; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; -import org.broadinstitute.sting.utils.GenomeLoc; import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; import org.broadinstitute.sting.utils.variantcontext.Allele; From 6804ab6d2f23afeb573e12f1238ebfc17c32fe33 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Sat, 24 Sep 2011 09:25:29 -0400 Subject: [PATCH 179/196] Bug fix for NPE in very short GATK runs -- Was already in unstable, but not stable... --- .../broadinstitute/sting/gatk/traversals/TraversalEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/traversals/TraversalEngine.java b/public/java/src/org/broadinstitute/sting/gatk/traversals/TraversalEngine.java index 27fd173cb..c6321e2ad 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/traversals/TraversalEngine.java +++ b/public/java/src/org/broadinstitute/sting/gatk/traversals/TraversalEngine.java @@ -358,7 +358,7 @@ public abstract class TraversalEngine,Provide public void printOnTraversalDone() { printProgress(null, null, true); - final double elapsed = timer.getElapsedTime(); + final double elapsed = timer == null ? 0 : timer.getElapsedTime(); ReadMetrics cumulativeMetrics = engine.getCumulativeMetrics(); From cd058dd10f337ab83919eaaba1f525e1f619b9f7 Mon Sep 17 00:00:00 2001 From: Guillermo del Angel Date: Sat, 24 Sep 2011 13:40:11 -0400 Subject: [PATCH 180/196] a) Fixed md5 for legit change in UG output that now also no-calls genotypes w/0,0,0 in PL's in SNP case. b) First reimplementation of new vc merger of different types. Previous version did it in two steps, first merging all vc's per type and then trying to see if resulting vc's would be merged if alleles of one type were a subset of another, but this won't work when uniquifying genotypes since sample names would be messed up and GT sample names wouldn't match VC sample names. Now, it's actually simpler: when splitting vc's by type before merging, we check for alleles of one vc being a subset of alleles of vc of another type and if so we put them together in same list. --- .../walkers/variantutils/CombineVariants.java | 36 ++-------------- .../variantcontext/VariantContextUtils.java | 42 +++++++++++++++++-- .../UnifiedGenotyperIntegrationTest.java | 2 +- 3 files changed, 43 insertions(+), 37 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariants.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariants.java index 40b704570..707f940fe 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariants.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariants.java @@ -234,47 +234,17 @@ public class CombineVariants extends RodWalker { if (minimumN > 1 && (vcs.size() - numFilteredRecords < minimumN)) return 0; - List preMergedVCs = new ArrayList(); + List mergedVCs = new ArrayList(); Map> VCsByType = VariantContextUtils.separateVariantContextsByType(vcs); // iterate over the types so that it's deterministic for ( VariantContext.Type type : VariantContext.Type.values() ) { if ( VCsByType.containsKey(type) ) - preMergedVCs.add(VariantContextUtils.simpleMerge(getToolkit().getGenomeLocParser(), VCsByType.get(type), + mergedVCs.add(VariantContextUtils.simpleMerge(getToolkit().getGenomeLocParser(), VCsByType.get(type), priority, filteredRecordsMergeType, genotypeMergeOption, true, printComplexMerges, SET_KEY, filteredAreUncalled, MERGE_INFO_WITH_MAX_AC)); } - List mergedVCs = new ArrayList(); - // se have records merged but separated by type. If a particular record is for example a snp but all alleles are a subset of an existing mixed record, - // we will still merge those records. - if (preMergedVCs.size() > 1) { - for (VariantContext vc1 : preMergedVCs) { - VariantContext newvc = vc1; - boolean merged = false; - for (int k=0; k < mergedVCs.size(); k++) { - VariantContext vc2 = mergedVCs.get(k); - - if (VariantContextUtils.allelesAreSubset(vc1,vc2) || VariantContextUtils.allelesAreSubset(vc2,vc1)) { - // all alleles of vc1 are contained in vc2 but they are of different type (say, vc1 is snp, vc2 is complex): try to merget v1 into v2 - List vcpair = new ArrayList(); - vcpair.add(vc1); - vcpair.add(vc2); - newvc = VariantContextUtils.simpleMerge(getToolkit().getGenomeLocParser(), vcpair, - priority, filteredRecordsMergeType, genotypeMergeOption, true, printComplexMerges, - SET_KEY, filteredAreUncalled, MERGE_INFO_WITH_MAX_AC); - mergedVCs.set(k,newvc); - merged = true; - break; - } - } - if (!merged) - mergedVCs.add(vc1); - } - } - else { - mergedVCs = preMergedVCs; - } - for ( VariantContext mergedVC : mergedVCs ) { + for ( VariantContext mergedVC : mergedVCs ) { // only operate at the start of events if ( mergedVC == null ) continue; diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java index 0ca067cec..f4f03e8f0 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java @@ -758,9 +758,45 @@ public class VariantContextUtils { public static Map> separateVariantContextsByType(Collection VCs) { HashMap> mappedVCs = new HashMap>(); for ( VariantContext vc : VCs ) { - if ( !mappedVCs.containsKey(vc.getType()) ) - mappedVCs.put(vc.getType(), new ArrayList()); - mappedVCs.get(vc.getType()).add(vc); + + // look at previous variant contexts of different type. If: + // a) otherVC has alleles which are subset of vc, remove otherVC from its list and add otherVC to vc's list + // b) vc has alleles which are subset of otherVC. Then, add vc to otherVC's type list (rather, do nothing since vc will be added automatically to its list) + // c) neither: do nothing, just add vc to its own list + boolean addtoOwnList = true; + for (VariantContext.Type type : VariantContext.Type.values()) { + if (type.equals(vc.getType())) + continue; + + if (!mappedVCs.containsKey(type)) + continue; + + List vcList = mappedVCs.get(type); + for (int k=0; k < vcList.size(); k++) { + VariantContext otherVC = vcList.get(k); + if (allelesAreSubset(otherVC,vc)) { + // otherVC has a type different than vc and its alleles are a subset of vc: remove otherVC from its list and add it to vc's type list + vcList.remove(k); + // avoid having empty lists + if (vcList.size() == 0) + mappedVCs.remove(vcList); + if ( !mappedVCs.containsKey(vc.getType()) ) + mappedVCs.put(vc.getType(), new ArrayList()); + mappedVCs.get(vc.getType()).add(otherVC); + break; + } + else if (allelesAreSubset(vc,otherVC)) { + // vc has a type different than otherVC and its alleles are a subset of VC: add vc to otherVC's type list and don't add to its own + mappedVCs.get(type).add(vc); + addtoOwnList = false; + } + } + } + if (addtoOwnList) { + if ( !mappedVCs.containsKey(vc.getType()) ) + mappedVCs.put(vc.getType(), new ArrayList()); + mappedVCs.get(vc.getType()).add(vc); + } } return mappedVCs; diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperIntegrationTest.java index 41496bdf1..488b3ccd9 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedGenotyperIntegrationTest.java @@ -42,7 +42,7 @@ public class UnifiedGenotyperIntegrationTest extends WalkerTest { WalkerTest.WalkerTestSpec spec2 = new WalkerTest.WalkerTestSpec( baseCommand + " --output_mode EMIT_ALL_SITES --genotyping_mode GENOTYPE_GIVEN_ALLELES -alleles " + validationDataLocation + "allelesForUG.vcf -I " + validationDataLocation + "pilot2_daughters.chr20.10k-11k.bam -o %s -L 20:10,000,000-10,025,000", 1, - Arrays.asList("ec43daadfb15b00b41aeb0017a45df0b")); + Arrays.asList("6458f3b8fe4954e2ffc2af972aaab19e")); executeTest("test MultiSample Pilot2 with alleles passed in and emitting all sites", spec2); } From c31f4cb2f6cee438719a62a6216603a591ca0829 Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Sat, 24 Sep 2011 14:33:32 -0400 Subject: [PATCH 181/196] Cleaning leading insertions With the current implementation, a read cannot start with a deletion or an insertion. Maybe this will change in the future, but for now, chop the leading insertion off. --- .../sting/utils/clipreads/ReadClipper.java | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/clipreads/ReadClipper.java b/public/java/src/org/broadinstitute/sting/utils/clipreads/ReadClipper.java index d0e7f8371..3b83617cf 100644 --- a/public/java/src/org/broadinstitute/sting/utils/clipreads/ReadClipper.java +++ b/public/java/src/org/broadinstitute/sting/utils/clipreads/ReadClipper.java @@ -1,7 +1,6 @@ package org.broadinstitute.sting.utils.clipreads; import com.google.java.contract.Requires; -import net.sf.samtools.Cigar; import net.sf.samtools.CigarElement; import net.sf.samtools.CigarOperator; import net.sf.samtools.SAMRecord; @@ -9,7 +8,6 @@ import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; import org.broadinstitute.sting.utils.sam.ReadUtils; import java.util.ArrayList; -import java.util.Iterator; import java.util.List; /** @@ -185,4 +183,21 @@ public class ReadClipper { } } } + + public SAMRecord hardClipLeadingInsertions() { + for(CigarElement cigarElement : read.getCigar().getCigarElements()) { + if (cigarElement.getOperator() != CigarOperator.HARD_CLIP && cigarElement.getOperator() != CigarOperator.SOFT_CLIP && + cigarElement.getOperator() != CigarOperator.INSERTION && cigarElement.getOperator() != CigarOperator.DELETION) + break; + + else if (cigarElement.getOperator() == CigarOperator.INSERTION) { + this.addOp(new ClippingOp(0, cigarElement.getLength() - 1)); + } + + else if (cigarElement.getOperator() == CigarOperator.DELETION) { + throw new ReviewedStingException("No read should start with a deletion. Aligner bug?"); + } + } + return clipRead(ClippingRepresentation.HARDCLIP_BASES); + } } From 203517fbb7c69f087c67b529e2bff8874c584e2d Mon Sep 17 00:00:00 2001 From: Guillermo del Angel Date: Sat, 24 Sep 2011 19:08:00 -0400 Subject: [PATCH 183/196] a) Cleanups/bug fixes to previous commit to CombineVariants. b) Change md5 to reflect records that are now merged correctly. c) Change unit merge alleles test to reflect the fact that a null non-variant vc object is not valid and not supported because there's no way to codify such object in a vcf. The code correctly converts this to a non-variant single-base event with whatever the reference is at that location. --- .../walkers/variantutils/CombineVariants.java | 3 +-- .../utils/variantcontext/VariantContext.java | 2 +- .../variantcontext/VariantContextUtils.java | 1 + .../CombineVariantsIntegrationTest.java | 2 +- .../VariantContextUtilsUnitTest.java | 17 ++++++++++++++--- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariants.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariants.java index 707f940fe..ce03dfffe 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariants.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariants.java @@ -233,7 +233,7 @@ public class CombineVariants extends RodWalker { if (minimumN > 1 && (vcs.size() - numFilteredRecords < minimumN)) return 0; - + List mergedVCs = new ArrayList(); Map> VCsByType = VariantContextUtils.separateVariantContextsByType(vcs); // iterate over the types so that it's deterministic @@ -248,7 +248,6 @@ public class CombineVariants extends RodWalker { // only operate at the start of events if ( mergedVC == null ) continue; - System.out.println(mergedVC.toString()); HashMap attributes = new HashMap(mergedVC.getAttributes()); // re-compute chromosome counts diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java index c1623d2cb..412cbd90b 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java @@ -1495,7 +1495,7 @@ public class VariantContext implements Feature { // to enable tribble intergrati // Do not change the filter state if filters were not applied to this context Set inputVCFilters = inputVC.filtersWereAppliedToContext ? inputVC.getFilters() : null; - return new VariantContext(inputVC.getSource(), inputVC.getChr(), inputVC.getStart(), inputVC.getEnd(), alleles, genotypes, inputVC.getNegLog10PError(), inputVCFilters, inputVC.getAttributes()); + return new VariantContext(inputVC.getSource(), inputVC.getChr(), inputVC.getStart(), inputVC.getEnd(), alleles, genotypes, inputVC.getNegLog10PError(), inputVCFilters, inputVC.getAttributes(),refByte); } else return inputVC; diff --git a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java index f4f03e8f0..ae648d547 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java @@ -789,6 +789,7 @@ public class VariantContextUtils { // vc has a type different than otherVC and its alleles are a subset of VC: add vc to otherVC's type list and don't add to its own mappedVCs.get(type).add(vc); addtoOwnList = false; + break; } } } diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java index 0205fe0eb..3b1a97369 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/variantutils/CombineVariantsIntegrationTest.java @@ -110,7 +110,7 @@ public class CombineVariantsIntegrationTest extends WalkerTest { " -priority NA19240_BGI,NA19240_ILLUMINA,NA19240_WUGSC,denovoInfo" + " -genotypeMergeOptions UNIQUIFY -L 1"), 1, - Arrays.asList("212d9d3df10bb29e2c7fb226da422dc0")); + Arrays.asList("b14f8cbb5d03a2e613b12da4da9efd9a")); executeTest("threeWayWithRefs", spec); } diff --git a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java index 15a5549b2..498f7c12c 100644 --- a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java @@ -38,7 +38,7 @@ import java.io.FileNotFoundException; import java.util.*; public class VariantContextUtilsUnitTest extends BaseTest { - Allele Aref, T, C, delRef, ATC, ATCATC; + Allele Aref, T, C, delRef, Cref, ATC, ATCATC; private GenomeLocParser genomeLocParser; @BeforeSuite @@ -54,6 +54,7 @@ public class VariantContextUtilsUnitTest extends BaseTest { // alleles Aref = Allele.create("A", true); + Cref = Allele.create("C", true); delRef = Allele.create("-", true); T = Allele.create("T"); C = Allele.create("C"); @@ -94,7 +95,7 @@ public class VariantContextUtilsUnitTest extends BaseTest { int stop = start; // alleles.contains(ATC) ? start + 3 : start; return new VariantContext(source, "1", start, stop, alleles, genotypes == null ? null : VariantContext.genotypeCollectionToMap(new TreeMap(), genotypes), - 1.0, filters, null, (byte)'C'); + 1.0, filters, null, Cref.getBases()[0]); } // -------------------------------------------------------------------------------- @@ -148,8 +149,10 @@ public class VariantContextUtilsUnitTest extends BaseTest { Arrays.asList(Aref, C), Arrays.asList(Aref, C, T)); // sorted by allele + // The following is actually a pathological case - there's no way on a vcf to represent a null allele that's non-variant. + // The code converts this (correctly) to a single-base non-variant vc with whatever base was there as a reference. new MergeAllelesTest(Arrays.asList(delRef), - Arrays.asList(delRef)); // todo -- FIXME me GdA + Arrays.asList(Cref)); new MergeAllelesTest(Arrays.asList(delRef), Arrays.asList(delRef, ATC), @@ -186,6 +189,14 @@ public class VariantContextUtilsUnitTest extends BaseTest { inputs, priority, VariantContextUtils.FilteredRecordMergeType.KEEP_IF_ANY_UNFILTERED, VariantContextUtils.GenotypeMergeType.PRIORITIZE, false, false, "set", false, false); + System.out.println("expected:"); + System.out.println(cfg.expected.toString()); + System.out.println("inputs"); + for (VariantContext vc:inputs) + System.out.println(vc.toString()); + System.out.println("merged:"); + System.out.println(merged.toString()); + Assert.assertEquals(merged.getAlleles(), cfg.expected); } From 4707ab4a7d00168bb0ae4145bf4ebde0c5263cff Mon Sep 17 00:00:00 2001 From: Guillermo del Angel Date: Sat, 24 Sep 2011 21:17:15 -0400 Subject: [PATCH 184/196] Added unit tests to test genotype merges with PL's --- .../VariantContextUtilsUnitTest.java | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java index 498f7c12c..08db7bcde 100644 --- a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUtilsUnitTest.java @@ -66,6 +66,11 @@ public class VariantContextUtilsUnitTest extends BaseTest { return new Genotype(sample, Arrays.asList(a1, a2)); } + private Genotype makeG(String sample, Allele a1, Allele a2, double log10pError, double l1, double l2, double l3) { + return new Genotype(sample, Arrays.asList(a1, a2), log10pError, new double[]{l1,l2,l3}); + } + + private Genotype makeG(String sample, Allele a1, Allele a2, double log10pError) { return new Genotype(sample, Arrays.asList(a1, a2), log10pError); } @@ -189,13 +194,6 @@ public class VariantContextUtilsUnitTest extends BaseTest { inputs, priority, VariantContextUtils.FilteredRecordMergeType.KEEP_IF_ANY_UNFILTERED, VariantContextUtils.GenotypeMergeType.PRIORITIZE, false, false, "set", false, false); - System.out.println("expected:"); - System.out.println(cfg.expected.toString()); - System.out.println("inputs"); - for (VariantContext vc:inputs) - System.out.println(vc.toString()); - System.out.println("merged:"); - System.out.println(merged.toString()); Assert.assertEquals(merged.getAlleles(), cfg.expected); } @@ -448,7 +446,17 @@ public class VariantContextUtilsUnitTest extends BaseTest { makeVC("2", Arrays.asList(Aref, T), makeG("s1", Aref, T, 2), makeG("s3", Aref, T, 3)), makeVC("3", Arrays.asList(Aref, T), makeG("s1", Aref, T, 2), makeG("s3", Aref, T, 3))); - // todo -- GDA -- add tests for merging correct PLs + // merging genothpes with PLs + new MergeGenotypesTest("TakeGenotypePartialOverlapWithPLs-2,1", "2,1", + makeVC("1", Arrays.asList(Aref, T), makeG("s1", Aref, T, 1,5,0,3)), + makeVC("2", Arrays.asList(Aref, T), makeG("s1", Aref, T, 2,4,0,2), makeG("s3", Aref, T, 3,3,0,2)), + makeVC("3", Arrays.asList(Aref, T), makeG("s1", Aref, T, 2,4,0,2), makeG("s3", Aref, T, 3,3,0,2))); + + new MergeGenotypesTest("TakeGenotypePartialOverlapWithPLs-1,2", "1,2", + makeVC("1", Arrays.asList(Aref,ATC), makeG("s1", Aref, ATC, 1,5,0,3)), + makeVC("2", Arrays.asList(Aref, T), makeG("s1", Aref, T, 2,4,0,2), makeG("s3", Aref, T, 3,3,0,2)), + // no likelihoods on result since type changes to mixed multiallelic + makeVC("3", Arrays.asList(Aref, ATC, T), makeG("s1", Aref, ATC, 1), makeG("s3", Aref, T, 3))); return MergeGenotypesTest.getTests(MergeGenotypesTest.class); } @@ -489,7 +497,7 @@ public class VariantContextUtilsUnitTest extends BaseTest { Assert.assertEquals(value.getNegLog10PError(), expectedValue.getNegLog10PError()); Assert.assertEquals(value.hasLikelihoods(), expectedValue.hasLikelihoods()); if ( value.hasLikelihoods() ) - Assert.assertEquals(value.getLikelihoods(), expectedValue.getLikelihoods()); + Assert.assertEquals(value.getLikelihoods().getAsVector(), expectedValue.getLikelihoods().getAsVector()); } } From 9afccd11b1b52bbf37c639e7944f7d1b12ba0a20 Mon Sep 17 00:00:00 2001 From: Guillermo del Angel Date: Sun, 25 Sep 2011 21:18:56 -0400 Subject: [PATCH 187/196] Minor refactoring: add ability to MathUtils.normalizeFromLog10 to not go to linear domain but just substract max value from log values and return. Use this function in snp and indel GL computation. --- .../SNPGenotypeLikelihoodsCalculationModel.java | 7 ++----- .../walkers/indels/PairHMMIndelErrorModel.java | 10 ++-------- .../broadinstitute/sting/utils/MathUtils.java | 16 +++++++++++++++- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/SNPGenotypeLikelihoodsCalculationModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/SNPGenotypeLikelihoodsCalculationModel.java index 742d4aadf..9bdc754e9 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/SNPGenotypeLikelihoodsCalculationModel.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/SNPGenotypeLikelihoodsCalculationModel.java @@ -122,13 +122,10 @@ public class SNPGenotypeLikelihoodsCalculationModel extends GenotypeLikelihoodsC aList.add(refAllele); aList.add(altAllele); double[] dlike = new double[]{likelihoods[refGenotype.ordinal()],likelihoods[hetGenotype.ordinal()],likelihoods[homGenotype.ordinal()]} ; - double maxElement = MathUtils.max(dlike[AlleleFrequencyCalculationModel.GenotypeType.AA.ordinal()], - dlike[AlleleFrequencyCalculationModel.GenotypeType.AB.ordinal()],dlike[AlleleFrequencyCalculationModel.GenotypeType.BB.ordinal()]); - for (int i=0; i < dlike.length; i++) - dlike[i] -= maxElement; + // normalize in log space so that max element is zero. GLs.put(sample.getKey(), new MultiallelicGenotypeLikelihoods(sample.getKey(), - aList, dlike, getFilteredDepth(pileup))); + aList, MathUtils.normalizeFromLog10(dlike, false, true), getFilteredDepth(pileup))); } return refAllele; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/PairHMMIndelErrorModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/PairHMMIndelErrorModel.java index 4f9bccc42..31e9819ab 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/PairHMMIndelErrorModel.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/PairHMMIndelErrorModel.java @@ -1041,20 +1041,14 @@ public class PairHMMIndelErrorModel { double[] genotypeLikelihoods = new double[hSize*(hSize+1)/2]; int k=0; - double maxElement = Double.NEGATIVE_INFINITY; for (int j=0; j < hSize; j++) { for (int i=0; i <= j; i++){ genotypeLikelihoods[k++] = haplotypeLikehoodMatrix[i][j]; - if (haplotypeLikehoodMatrix[i][j] > maxElement) - maxElement = haplotypeLikehoodMatrix[i][j]; } } - // renormalize - for (int i=0; i < genotypeLikelihoods.length; i++) - genotypeLikelihoods[i] -= maxElement; - - return genotypeLikelihoods; + // renormalize so that max element is zero. + return MathUtils.normalizeFromLog10(genotypeLikelihoods, false, true); } /** diff --git a/public/java/src/org/broadinstitute/sting/utils/MathUtils.java b/public/java/src/org/broadinstitute/sting/utils/MathUtils.java index 0d85f9606..17e74c4f1 100644 --- a/public/java/src/org/broadinstitute/sting/utils/MathUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/MathUtils.java @@ -444,11 +444,25 @@ public class MathUtils { * @return a newly allocated array corresponding the normalized values in array, maybe log10 transformed */ public static double[] normalizeFromLog10(double[] array, boolean takeLog10OfOutput) { - double[] normalized = new double[array.length]; + return normalizeFromLog10(array, takeLog10OfOutput, false); + } + + public static double[] normalizeFromLog10(double[] array, boolean takeLog10OfOutput, boolean keepInLogSpace) { // for precision purposes, we need to add (or really subtract, since they're // all negative) the largest value; also, we need to convert to normal-space. double maxValue = Utils.findMaxEntry(array); + + // we may decide to just normalize in log space with converting to linear space + if (keepInLogSpace) { + for (int i = 0; i < array.length; i++) + array[i] -= maxValue; + return array; + } + + // default case: go to linear space + double[] normalized = new double[array.length]; + for (int i = 0; i < array.length; i++) normalized[i] = Math.pow(10, array[i] - maxValue); From b76dbc72f0d856866689e8b150b0acc39b4ece59 Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Mon, 26 Sep 2011 08:13:44 -0400 Subject: [PATCH 188/196] Fixed interval navigation bug. If a read was hard clipped away from the current interval, all subsequent reads within that interval (not hardclipped) would be filtered out. Fixed. --- .../sting/utils/sam/ReadUtils.java | 28 ++++++++++++------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java b/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java index c328bbc5a..49d79a72c 100755 --- a/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java @@ -157,7 +157,7 @@ public class ReadUtils { * |----------------| (interval) * <--------> (read) */ - public enum ReadAndIntervalOverlap {NO_OVERLAP_CONTIG, NO_OVERLAP_LEFT, NO_OVERLAP_RIGHT, OVERLAP_LEFT, OVERLAP_RIGHT, OVERLAP_LEFT_AND_RIGHT, OVERLAP_CONTAINED} + public enum ReadAndIntervalOverlap {NO_OVERLAP_CONTIG, NO_OVERLAP_LEFT, NO_OVERLAP_RIGHT, NO_OVERLAP_HARDCLIPPED_LEFT, NO_OVERLAP_HARDCLIPPED_RIGHT, OVERLAP_LEFT, OVERLAP_RIGHT, OVERLAP_LEFT_AND_RIGHT, OVERLAP_CONTAINED} /** * God, there's a huge information asymmetry in SAM format: @@ -640,27 +640,35 @@ public class ReadUtils { */ public static ReadAndIntervalOverlap getReadAndIntervalOverlapType(SAMRecord read, GenomeLoc interval) { - int start = getRefCoordSoftUnclippedStart(read); - int stop = getRefCoordSoftUnclippedEnd(read); + int sStart = getRefCoordSoftUnclippedStart(read); + int sStop = getRefCoordSoftUnclippedEnd(read); + int uStart = read.getUnclippedStart(); + int uStop = read.getUnclippedEnd(); if ( !read.getReferenceName().equals(interval.getContig()) ) return ReadAndIntervalOverlap.NO_OVERLAP_CONTIG; - else if ( stop < interval.getStart() ) + else if ( uStop < interval.getStart() ) return ReadAndIntervalOverlap.NO_OVERLAP_LEFT; - else if ( start > interval.getStop() ) + else if ( uStart > interval.getStop() ) return ReadAndIntervalOverlap.NO_OVERLAP_RIGHT; - else if ( (start >= interval.getStart()) && - (stop <= interval.getStop()) ) + else if ( sStop < interval.getStart() ) + return ReadAndIntervalOverlap.NO_OVERLAP_HARDCLIPPED_LEFT; + + else if ( sStart > interval.getStop() ) + return ReadAndIntervalOverlap.NO_OVERLAP_HARDCLIPPED_RIGHT; + + else if ( (sStart >= interval.getStart()) && + (sStop <= interval.getStop()) ) return ReadAndIntervalOverlap.OVERLAP_CONTAINED; - else if ( (start < interval.getStart()) && - (stop > interval.getStop()) ) + else if ( (sStart < interval.getStart()) && + (sStop > interval.getStop()) ) return ReadAndIntervalOverlap.OVERLAP_LEFT_AND_RIGHT; - else if ( (start < interval.getStart()) ) + else if ( (sStart < interval.getStart()) ) return ReadAndIntervalOverlap.OVERLAP_LEFT; else From 317b95fa5738ee0434aa8e122408abe88c0b9242 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Mon, 26 Sep 2011 11:46:45 -0500 Subject: [PATCH 190/196] Fixing some annotator docs --- .../gatk/walkers/annotator/DepthOfCoverage.java | 15 +++++++++++++-- .../walkers/annotator/DepthPerAlleleBySample.java | 8 ++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/DepthOfCoverage.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/DepthOfCoverage.java index 8826de232..864be55b7 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/DepthOfCoverage.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/DepthOfCoverage.java @@ -19,8 +19,19 @@ import java.util.Map; /** * Total (unfiltered) depth over all samples. * - * Affected by downsampling (-dcov) though, so the max value one can obtain for N samples with -dcov D - * is N * D + * This and AD are complementary fields that are two important ways of thinking about the depth of the data for this sample + * at this site. The DP field describe the total depth of reads that passed the Unified Genotypers internal + * quality control metrics (like MAPQ > 17, for example), whatever base was present in the read at this site. + * The AD values (one for each of REF and ALT fields) is the count of all reads that carried with them the + * REF and ALT alleles. The reason for this distinction is that the DP is in some sense reflective of the + * power I have to determine the genotype of the sample at this site, while the AD tells me how many times + * I saw each of the REF and ALT alleles in the reads, free of any bias potentially introduced by filtering + * the reads. If, for example, I believe there really is a an A/T polymorphism at a site, then I would like + * to know the counts of A and T bases in this sample, even for reads with poor mapping quality that would + * normally be excluded from the statistical calculations going into GQ and QUAL. + * + * Note that the DP is affected by downsampling (-dcov) though, so the max value one can obtain for N samples with + * -dcov D is N * D */ public class DepthOfCoverage extends InfoFieldAnnotation implements StandardAnnotation { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/DepthPerAlleleBySample.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/DepthPerAlleleBySample.java index 1cd30c51d..5d706d9c5 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/DepthPerAlleleBySample.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/annotator/DepthPerAlleleBySample.java @@ -24,9 +24,9 @@ import java.util.Map; /** - * The depth of coverage of each VCF allele in this sample + * The depth of coverage of each VCF allele in this sample. * - * Complementary fields that two important ways of thinking about the depth of the data for this sample + * This and DP are complementary fields that are two important ways of thinking about the depth of the data for this sample * at this site. The DP field describe the total depth of reads that passed the Unified Genotypers internal * quality control metrics (like MAPQ > 17, for example), whatever base was present in the read at this site. * The AD values (one for each of REF and ALT fields) is the count of all reads that carried with them the @@ -38,8 +38,8 @@ import java.util.Map; * normally be excluded from the statistical calculations going into GQ and QUAL. Please note, however, that * the AD isn't necessarily calculated exactly for indels (it counts as non-reference only those indels that * are actually present and correctly left-aligned in the alignments themselves). Because of this fact and - * because the AD includes reads and bases that were filtered by the Unified Genotyper, one should not base - * assumptions about the underlying genotype based on it; instead, the genotype likelihoods (PLs) are what + * because the AD includes reads and bases that were filtered by the Unified Genotyper, one should not base + * assumptions about the underlying genotype based on it; instead, the genotype likelihoods (PLs) are what * determine the genotype calls (see below). */ public class DepthPerAlleleBySample extends GenotypeAnnotation implements StandardAnnotation { From 4f09453470d3f86cd985eb31215623ae8d62a1d8 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Mon, 26 Sep 2011 12:58:31 -0400 Subject: [PATCH 191/196] Refactored reduced read utilities -- UnitTests for key functions on reduced reads -- PileupElement calls static functions in ReadUtils -- Simple routine that takes a reduced read and fills in its quals with its reduced qual --- .../sting/utils/pileup/PileupElement.java | 9 ++-- .../sting/utils/sam/ReadUtils.java | 37 ++++++++++++++- .../sting/utils/ReadUtilsUnitTest.java | 45 ++++++++++++++++++- 3 files changed, 82 insertions(+), 9 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/pileup/PileupElement.java b/public/java/src/org/broadinstitute/sting/utils/pileup/PileupElement.java index 12899e898..053864791 100755 --- a/public/java/src/org/broadinstitute/sting/utils/pileup/PileupElement.java +++ b/public/java/src/org/broadinstitute/sting/utils/pileup/PileupElement.java @@ -81,20 +81,17 @@ public class PileupElement { // // -------------------------------------------------------------------------- - private Integer getReducedReadQualityTagValue() { - return getRead().getIntegerAttribute(ReadUtils.REDUCED_READ_QUALITY_TAG); - } - public boolean isReducedRead() { - return getReducedReadQualityTagValue() != null; + return ReadUtils.isReducedRead(getRead()); } public int getReducedCount() { + if ( ! isReducedRead() ) throw new IllegalArgumentException("Cannot get reduced count for non-reduced read " + getRead().getReadName()); return (int)getQual(); } public byte getReducedQual() { - return (byte)(int)getReducedReadQualityTagValue(); + return (byte)(int)ReadUtils.getReducedReadQualityTagValue(getRead()); } } \ No newline at end of file diff --git a/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java b/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java index 49d79a72c..8beb1b21a 100755 --- a/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/sam/ReadUtils.java @@ -43,10 +43,43 @@ import java.util.*; * @version 0.1 */ public class ReadUtils { - public static final String REDUCED_READ_QUALITY_TAG = "RQ"; - private ReadUtils() { } + // ---------------------------------------------------------------------------------------------------- + // + // Reduced read utilities + // + // ---------------------------------------------------------------------------------------------------- + + public static final String REDUCED_READ_QUALITY_TAG = "RQ"; + + public final static Integer getReducedReadQualityTagValue(final SAMRecord read) { + return read.getIntegerAttribute(ReadUtils.REDUCED_READ_QUALITY_TAG); + } + + public final static boolean isReducedRead(final SAMRecord read) { + return getReducedReadQualityTagValue(read) != null; + } + + public final static SAMRecord reducedReadWithReducedQuals(final SAMRecord read) { + if ( ! isReducedRead(read) ) throw new IllegalArgumentException("read must be a reduced read"); + try { + SAMRecord newRead = (SAMRecord)read.clone(); + byte reducedQual = (byte)(int)getReducedReadQualityTagValue(read); + byte[] newQuals = new byte[read.getBaseQualities().length]; + Arrays.fill(newQuals, reducedQual); + newRead.setBaseQualities(newQuals); + return newRead; + } catch ( CloneNotSupportedException e ) { + throw new ReviewedStingException("SAMRecord no longer supports clone", e); + } + } + + // ---------------------------------------------------------------------------------------------------- + // + // General utilities + // + // ---------------------------------------------------------------------------------------------------- public static SAMFileHeader copySAMFileHeader(SAMFileHeader toCopy) { SAMFileHeader copy = new SAMFileHeader(); diff --git a/public/java/test/org/broadinstitute/sting/utils/ReadUtilsUnitTest.java b/public/java/test/org/broadinstitute/sting/utils/ReadUtilsUnitTest.java index 7cb7fec98..e1fdadadc 100755 --- a/public/java/test/org/broadinstitute/sting/utils/ReadUtilsUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/utils/ReadUtilsUnitTest.java @@ -3,6 +3,7 @@ package org.broadinstitute.sting.utils; import net.sf.samtools.SAMFileHeader; import net.sf.samtools.SAMRecord; import org.broadinstitute.sting.BaseTest; +import org.broadinstitute.sting.utils.pileup.PileupElement; import org.broadinstitute.sting.utils.sam.ArtificialSAMUtils; import org.broadinstitute.sting.utils.sam.ReadUtils; import org.testng.Assert; @@ -12,9 +13,10 @@ import org.testng.annotations.Test; public class ReadUtilsUnitTest extends BaseTest { - SAMRecord read; + SAMRecord read, reducedRead; final static String BASES = "ACTG"; final static String QUALS = "!+5?"; + final private static int REDUCED_READ_QUAL = 20; @BeforeTest public void init() { @@ -23,6 +25,11 @@ public class ReadUtilsUnitTest extends BaseTest { read.setReadUnmappedFlag(true); read.setReadBases(new String(BASES).getBytes()); read.setBaseQualityString(new String(QUALS)); + + reducedRead = ArtificialSAMUtils.createArtificialRead(header, "reducedRead", 0, 1, BASES.length()); + reducedRead.setReadBases(BASES.getBytes()); + reducedRead.setBaseQualityString(QUALS); + reducedRead.setAttribute(ReadUtils.REDUCED_READ_QUALITY_TAG, REDUCED_READ_QUAL); } private void testReadBasesAndQuals(SAMRecord read, int expectedStart, int expectedStop) { @@ -38,4 +45,40 @@ public class ReadUtilsUnitTest extends BaseTest { @Test public void testClip2Front() { testReadBasesAndQuals(read, 2, 4); } @Test public void testClip1Back() { testReadBasesAndQuals(read, 0, 3); } @Test public void testClip2Back() { testReadBasesAndQuals(read, 0, 2); } + + @Test + public void testReducedReads() { + Assert.assertFalse(ReadUtils.isReducedRead(read), "isReducedRead is false for normal read"); + Assert.assertEquals(ReadUtils.getReducedReadQualityTagValue(read), null, "No reduced read tag in normal read"); + + Assert.assertTrue(ReadUtils.isReducedRead(reducedRead), "isReducedRead is true for reduced read"); + Assert.assertEquals((int) ReadUtils.getReducedReadQualityTagValue(reducedRead), REDUCED_READ_QUAL, "Reduced read tag is set to expected value"); + } + + @Test + public void testreducedReadWithReducedQualsWithReducedRead() { + SAMRecord replacedRead = ReadUtils.reducedReadWithReducedQuals(reducedRead); + Assert.assertEquals(replacedRead.getReadBases(), reducedRead.getReadBases()); + Assert.assertEquals(replacedRead.getBaseQualities().length, reducedRead.getBaseQualities().length); + for ( int i = 0; i < replacedRead.getBaseQualities().length; i++) + Assert.assertEquals(replacedRead.getBaseQualities()[i], REDUCED_READ_QUAL); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void testreducedReadWithReducedQualsWithNormalRead() { + ReadUtils.reducedReadWithReducedQuals(read); + } + + @Test + public void testReducedReadPileupElement() { + PileupElement readp = new PileupElement(read,0); + PileupElement reducedreadp = new PileupElement(reducedRead,0); + + Assert.assertFalse(readp.isReducedRead()); + + Assert.assertTrue(reducedreadp.isReducedRead()); + Assert.assertEquals(reducedreadp.getReducedCount(), 0); + Assert.assertEquals(reducedreadp.getReducedQual(), REDUCED_READ_QUAL); + + } } From fa0efbc4ca9b304e6e67326912e65263a996c56a Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Mon, 26 Sep 2011 13:28:56 -0400 Subject: [PATCH 192/196] Refactoring of PairHMM to support reduced reads --- .../indels/PairHMMIndelErrorModel.java | 295 +++++++++--------- 1 file changed, 151 insertions(+), 144 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/PairHMMIndelErrorModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/PairHMMIndelErrorModel.java index 31e9819ab..6e4db9303 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/PairHMMIndelErrorModel.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/PairHMMIndelErrorModel.java @@ -28,6 +28,7 @@ package org.broadinstitute.sting.gatk.walkers.indels; import net.sf.samtools.Cigar; import net.sf.samtools.CigarElement; import net.sf.samtools.CigarOperator; +import net.sf.samtools.SAMRecord; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.utils.Haplotype; import org.broadinstitute.sting.utils.MathUtils; @@ -244,31 +245,31 @@ public class PairHMMIndelErrorModel { /** * For each covariate read in a value and parse it. Associate those values with the data itself (num observation and num mismatches) */ - /* - private void addCSVData(final File file, final String line) { - final String[] vals = line.split(","); + /* + private void addCSVData(final File file, final String line) { + final String[] vals = line.split(","); - // Check if the data line is malformed, for example if the read group string contains a comma then it won't be parsed correctly - if( vals.length != requestedCovariates.size() + 3 ) { // +3 because of nObservations, nMismatch, and Qempirical - throw new UserException.MalformedFile(file, "Malformed input recalibration file. Found data line with too many fields: " + line + - " --Perhaps the read group string contains a comma and isn't being parsed correctly."); + // Check if the data line is malformed, for example if the read group string contains a comma then it won't be parsed correctly + if( vals.length != requestedCovariates.size() + 3 ) { // +3 because of nObservations, nMismatch, and Qempirical + throw new UserException.MalformedFile(file, "Malformed input recalibration file. Found data line with too many fields: " + line + + " --Perhaps the read group string contains a comma and isn't being parsed correctly."); + } + + final Object[] key = new Object[requestedCovariates.size()]; + Covariate cov; + int iii; + for( iii = 0; iii < requestedCovariates.size(); iii++ ) { + cov = requestedCovariates.get( iii ); + key[iii] = cov.getValue( vals[iii] ); + } + + // Create a new datum using the number of observations, number of mismatches, and reported quality score + final RecalDatum datum = new RecalDatum( Long.parseLong( vals[iii] ), Long.parseLong( vals[iii + 1] ), Double.parseDouble( vals[1] ), 0.0 ); + // Add that datum to all the collapsed tables which will be used in the sequential calculation + dataManager.addToAllTables( key, datum, PRESERVE_QSCORES_LESS_THAN ); } - final Object[] key = new Object[requestedCovariates.size()]; - Covariate cov; - int iii; - for( iii = 0; iii < requestedCovariates.size(); iii++ ) { - cov = requestedCovariates.get( iii ); - key[iii] = cov.getValue( vals[iii] ); - } - - // Create a new datum using the number of observations, number of mismatches, and reported quality score - final RecalDatum datum = new RecalDatum( Long.parseLong( vals[iii] ), Long.parseLong( vals[iii + 1] ), Double.parseDouble( vals[1] ), 0.0 ); - // Add that datum to all the collapsed tables which will be used in the sequential calculation - dataManager.addToAllTables( key, datum, PRESERVE_QSCORES_LESS_THAN ); - } - -*/ + */ public PairHMMIndelErrorModel(double indelGOP, double indelGCP, boolean deb, boolean doCDP, boolean dovit) { this(indelGOP, indelGCP, deb, doCDP); this.doViterbi = dovit; @@ -588,7 +589,7 @@ public class PairHMMIndelErrorModel { } else { c = currentGOP[jm1]; - d = currentGCP[jm1]; + d = currentGCP[jm1]; } if (indI == X_METRIC_LENGTH-1) c = d = END_GAP_COST; @@ -707,12 +708,12 @@ public class PairHMMIndelErrorModel { } } public synchronized double[] computeReadHaplotypeLikelihoods(ReadBackedPileup pileup, LinkedHashMap haplotypeMap, - ReferenceContext ref, int eventLength, - HashMap> indelLikelihoodMap){ + ReferenceContext ref, int eventLength, + HashMap> indelLikelihoodMap){ int numHaplotypes = haplotypeMap.size(); - double[][] haplotypeLikehoodMatrix = new double[numHaplotypes][numHaplotypes]; - double readLikelihoods[][] = new double[pileup.getReads().size()][numHaplotypes]; + final double readLikelihoods[][] = new double[pileup.size()][numHaplotypes]; + final int readCounts[] = new int[pileup.size()]; int readIdx=0; LinkedHashMap gapOpenProbabilityMap = new LinkedHashMap(); @@ -751,6 +752,9 @@ public class PairHMMIndelErrorModel { } } for (PileupElement p: pileup) { + // > 1 when the read is a consensus read representing multiple independent observations + final boolean isReduced = ReadUtils.isReducedRead(p.getRead()); + readCounts[readIdx] = isReduced ? p.getReducedCount() : 1; // check if we've already computed likelihoods for this pileup element (i.e. for this read at this location) if (indelLikelihoodMap.containsKey(p)) { @@ -762,61 +766,65 @@ public class PairHMMIndelErrorModel { } else { //System.out.format("%d %s\n",p.getRead().getAlignmentStart(), p.getRead().getClass().getName()); - GATKSAMRecord read = ReadUtils.hardClipAdaptorSequence(p.getRead()); + SAMRecord read = ReadUtils.hardClipAdaptorSequence(p.getRead()); if (read == null) continue; + if ( isReduced ) { + read = ReadUtils.reducedReadWithReducedQuals(read); + } + if(ReadUtils.is454Read(read) && !getGapPenaltiesFromFile) { continue; } double[] recalQuals = null; - /* - if (getGapPenaltiesFromFile) { - RecalDataManager.parseSAMRecord( read, RAC ); + /* + if (getGapPenaltiesFromFile) { + RecalDataManager.parseSAMRecord( read, RAC ); - recalQuals = new double[read.getReadLength()]; + recalQuals = new double[read.getReadLength()]; - //compute all covariate values for this read - final Comparable[][] covariateValues_offset_x_covar = - RecalDataManager.computeCovariates((GATKSAMRecord) read, requestedCovariates); - // For each base in the read - for( int offset = 0; offset < read.getReadLength(); offset++ ) { + //compute all covariate values for this read + final Comparable[][] covariateValues_offset_x_covar = + RecalDataManager.computeCovariates((GATKSAMRecord) read, requestedCovariates); + // For each base in the read + for( int offset = 0; offset < read.getReadLength(); offset++ ) { - final Object[] fullCovariateKey = covariateValues_offset_x_covar[offset]; + final Object[] fullCovariateKey = covariateValues_offset_x_covar[offset]; - Byte qualityScore = (Byte) qualityScoreByFullCovariateKey.get(fullCovariateKey); - if(qualityScore == null) - { - qualityScore = performSequentialQualityCalculation( fullCovariateKey ); - qualityScoreByFullCovariateKey.put(qualityScore, fullCovariateKey); - } + Byte qualityScore = (Byte) qualityScoreByFullCovariateKey.get(fullCovariateKey); + if(qualityScore == null) + { + qualityScore = performSequentialQualityCalculation( fullCovariateKey ); + qualityScoreByFullCovariateKey.put(qualityScore, fullCovariateKey); + } - recalQuals[offset] = -((double)qualityScore)/10.0; - } + recalQuals[offset] = -((double)qualityScore)/10.0; + } - // for each read/haplotype combination, compute likelihoods, ie -10*log10(Pr(R | Hi)) - // = sum_j(-10*log10(Pr(R_j | Hi) since reads are assumed to be independent - if (DEBUG) { - System.out.format("\n\nStarting read:%s S:%d US:%d E:%d UE:%d C:%s\n",read.getReadName(), - read.getAlignmentStart(), - read.getUnclippedStart(), read.getAlignmentEnd(), read.getUnclippedEnd(), - read.getCigarString()); + // for each read/haplotype combination, compute likelihoods, ie -10*log10(Pr(R | Hi)) + // = sum_j(-10*log10(Pr(R_j | Hi) since reads are assumed to be independent + if (DEBUG) { + System.out.format("\n\nStarting read:%s S:%d US:%d E:%d UE:%d C:%s\n",read.getReadName(), + read.getAlignmentStart(), + read.getUnclippedStart(), read.getAlignmentEnd(), read.getUnclippedEnd(), + read.getCigarString()); - byte[] bases = read.getReadBases(); - for (int k = 0; k < recalQuals.length; k++) { - System.out.format("%c",bases[k]); - } - System.out.println(); + byte[] bases = read.getReadBases(); + for (int k = 0; k < recalQuals.length; k++) { + System.out.format("%c",bases[k]); + } + System.out.println(); - for (int k = 0; k < recalQuals.length; k++) { - System.out.format("%.0f ",recalQuals[k]); - } - System.out.println(); - } - } */ + for (int k = 0; k < recalQuals.length; k++) { + System.out.format("%.0f ",recalQuals[k]); + } + System.out.println(); + } + } */ // get bases of candidate haplotypes that overlap with reads final int trailingBases = 3; @@ -971,7 +979,7 @@ public class PairHMMIndelErrorModel { System.out.println(new String(haplotypeBases)); } - Double readLikelihood = 0.0; + double readLikelihood = 0.0; if (useAffineGapModel) { double[] currentContextGOP = null; @@ -979,14 +987,14 @@ public class PairHMMIndelErrorModel { if (doContextDependentPenalties) { - if (getGapPenaltiesFromFile) { - readLikelihood = computeReadLikelihoodGivenHaplotypeAffineGaps(haplotypeBases, readBases, readQuals, recalCDP, null); + if (getGapPenaltiesFromFile) { + readLikelihood = computeReadLikelihoodGivenHaplotypeAffineGaps(haplotypeBases, readBases, readQuals, recalCDP, null); - } else { - currentContextGOP = Arrays.copyOfRange(gapOpenProbabilityMap.get(a), (int)indStart, (int)indStop); - currentContextGCP = Arrays.copyOfRange(gapContProbabilityMap.get(a), (int)indStart, (int)indStop); - readLikelihood = computeReadLikelihoodGivenHaplotypeAffineGaps(haplotypeBases, readBases, readQuals, currentContextGOP, currentContextGCP); - } + } else { + currentContextGOP = Arrays.copyOfRange(gapOpenProbabilityMap.get(a), (int)indStart, (int)indStop); + currentContextGCP = Arrays.copyOfRange(gapContProbabilityMap.get(a), (int)indStart, (int)indStop); + readLikelihood = computeReadLikelihoodGivenHaplotypeAffineGaps(haplotypeBases, readBases, readQuals, currentContextGOP, currentContextGCP); + } } } @@ -1004,7 +1012,7 @@ public class PairHMMIndelErrorModel { if (DEBUG) { System.out.println("\nLikelihood summary"); - for (readIdx=0; readIdx < pileup.getReads().size(); readIdx++) { + for (readIdx=0; readIdx < pileup.size(); readIdx++) { System.out.format("Read Index: %d ",readIdx); for (int i=0; i < readLikelihoods[readIdx].length; i++) System.out.format("L%d: %f ",i,readLikelihoods[readIdx][i]); @@ -1012,36 +1020,35 @@ public class PairHMMIndelErrorModel { } } + + return getHaplotypeLikelihoods(numHaplotypes, readCounts, readLikelihoods); + } + + private final static double[] getHaplotypeLikelihoods(final int numHaplotypes, final int readCounts[], final double readLikelihoods[][]) { + final double[][] haplotypeLikehoodMatrix = new double[numHaplotypes][numHaplotypes]; + + // todo: MAD 09/26/11 -- I'm almost certain this calculation can be simplied to just a single loop without the intermediate NxN matrix for (int i=0; i < numHaplotypes; i++) { for (int j=i; j < numHaplotypes; j++){ // combine likelihoods of haplotypeLikelihoods[i], haplotypeLikelihoods[j] // L(Hi, Hj) = sum_reads ( Pr(R|Hi)/2 + Pr(R|Hj)/2) //readLikelihoods[k][j] has log10(Pr(R_k) | H[j] ) - for (readIdx=0; readIdx < pileup.getReads().size(); readIdx++) { - + for (int readIdx = 0; readIdx < readLikelihoods.length; readIdx++) { // Compute log10(10^x1/2 + 10^x2/2) = log10(10^x1+10^x2)-log10(2) // First term is approximated by Jacobian log with table lookup. if (Double.isInfinite(readLikelihoods[readIdx][i]) && Double.isInfinite(readLikelihoods[readIdx][j])) continue; - haplotypeLikehoodMatrix[i][j] += ( MathUtils.softMax(readLikelihoods[readIdx][i], - readLikelihoods[readIdx][j]) + LOG_ONE_HALF); - + final double li = readLikelihoods[readIdx][i]; + final double lj = readLikelihoods[readIdx][j]; + final int readCount = readCounts[readIdx]; + haplotypeLikehoodMatrix[i][j] += readCount * (MathUtils.softMax(li, lj) + LOG_ONE_HALF); } - - } } - return getHaplotypeLikelihoods(haplotypeLikehoodMatrix); - - } - - public static double[] getHaplotypeLikelihoods(double[][] haplotypeLikehoodMatrix) { - int hSize = haplotypeLikehoodMatrix.length; - double[] genotypeLikelihoods = new double[hSize*(hSize+1)/2]; - + final double[] genotypeLikelihoods = new double[numHaplotypes*(numHaplotypes+1)/2]; int k=0; - for (int j=0; j < hSize; j++) { + for (int j=0; j < numHaplotypes; j++) { for (int i=0; i <= j; i++){ genotypeLikelihoods[k++] = haplotypeLikehoodMatrix[i][j]; } @@ -1066,63 +1073,63 @@ public class PairHMMIndelErrorModel { * @param key The list of Comparables that were calculated from the covariates * @return A recalibrated quality score as a byte */ - /* - private byte performSequentialQualityCalculation( final Object... key ) { + /* + private byte performSequentialQualityCalculation( final Object... key ) { - final byte qualFromRead = (byte)Integer.parseInt(key[1].toString()); - final Object[] readGroupCollapsedKey = new Object[1]; - final Object[] qualityScoreCollapsedKey = new Object[2]; - final Object[] covariateCollapsedKey = new Object[3]; + final byte qualFromRead = (byte)Integer.parseInt(key[1].toString()); + final Object[] readGroupCollapsedKey = new Object[1]; + final Object[] qualityScoreCollapsedKey = new Object[2]; + final Object[] covariateCollapsedKey = new Object[3]; - // The global quality shift (over the read group only) - readGroupCollapsedKey[0] = key[0]; - final RecalDatum globalRecalDatum = ((RecalDatum)dataManager.getCollapsedTable(0).get( readGroupCollapsedKey )); - double globalDeltaQ = 0.0; - if( globalRecalDatum != null ) { - final double globalDeltaQEmpirical = globalRecalDatum.getEmpiricalQuality(); - final double aggregrateQReported = globalRecalDatum.getEstimatedQReported(); - globalDeltaQ = globalDeltaQEmpirical - aggregrateQReported; - } - - // The shift in quality between reported and empirical - qualityScoreCollapsedKey[0] = key[0]; - qualityScoreCollapsedKey[1] = key[1]; - final RecalDatum qReportedRecalDatum = ((RecalDatum)dataManager.getCollapsedTable(1).get( qualityScoreCollapsedKey )); - double deltaQReported = 0.0; - if( qReportedRecalDatum != null ) { - final double deltaQReportedEmpirical = qReportedRecalDatum.getEmpiricalQuality(); - deltaQReported = deltaQReportedEmpirical - qualFromRead - globalDeltaQ; - } - - // The shift in quality due to each covariate by itself in turn - double deltaQCovariates = 0.0; - double deltaQCovariateEmpirical; - covariateCollapsedKey[0] = key[0]; - covariateCollapsedKey[1] = key[1]; - for( int iii = 2; iii < key.length; iii++ ) { - covariateCollapsedKey[2] = key[iii]; // The given covariate - final RecalDatum covariateRecalDatum = ((RecalDatum)dataManager.getCollapsedTable(iii).get( covariateCollapsedKey )); - if( covariateRecalDatum != null ) { - deltaQCovariateEmpirical = covariateRecalDatum.getEmpiricalQuality(); - deltaQCovariates += ( deltaQCovariateEmpirical - qualFromRead - (globalDeltaQ + deltaQReported) ); + // The global quality shift (over the read group only) + readGroupCollapsedKey[0] = key[0]; + final RecalDatum globalRecalDatum = ((RecalDatum)dataManager.getCollapsedTable(0).get( readGroupCollapsedKey )); + double globalDeltaQ = 0.0; + if( globalRecalDatum != null ) { + final double globalDeltaQEmpirical = globalRecalDatum.getEmpiricalQuality(); + final double aggregrateQReported = globalRecalDatum.getEstimatedQReported(); + globalDeltaQ = globalDeltaQEmpirical - aggregrateQReported; } + + // The shift in quality between reported and empirical + qualityScoreCollapsedKey[0] = key[0]; + qualityScoreCollapsedKey[1] = key[1]; + final RecalDatum qReportedRecalDatum = ((RecalDatum)dataManager.getCollapsedTable(1).get( qualityScoreCollapsedKey )); + double deltaQReported = 0.0; + if( qReportedRecalDatum != null ) { + final double deltaQReportedEmpirical = qReportedRecalDatum.getEmpiricalQuality(); + deltaQReported = deltaQReportedEmpirical - qualFromRead - globalDeltaQ; + } + + // The shift in quality due to each covariate by itself in turn + double deltaQCovariates = 0.0; + double deltaQCovariateEmpirical; + covariateCollapsedKey[0] = key[0]; + covariateCollapsedKey[1] = key[1]; + for( int iii = 2; iii < key.length; iii++ ) { + covariateCollapsedKey[2] = key[iii]; // The given covariate + final RecalDatum covariateRecalDatum = ((RecalDatum)dataManager.getCollapsedTable(iii).get( covariateCollapsedKey )); + if( covariateRecalDatum != null ) { + deltaQCovariateEmpirical = covariateRecalDatum.getEmpiricalQuality(); + deltaQCovariates += ( deltaQCovariateEmpirical - qualFromRead - (globalDeltaQ + deltaQReported) ); + } + } + + final double newQuality = qualFromRead + globalDeltaQ + deltaQReported + deltaQCovariates; + return QualityUtils.boundQual( (int)Math.round(newQuality), (byte)MAX_QUALITY_SCORE ); + + // Verbose printouts used to validate with old recalibrator + //if(key.contains(null)) { + // System.out.println( key + String.format(" => %d + %.2f + %.2f + %.2f + %.2f = %d", + // qualFromRead, globalDeltaQ, deltaQReported, deltaQPos, deltaQDinuc, newQualityByte)); + //} + //else { + // System.out.println( String.format("%s %s %s %s => %d + %.2f + %.2f + %.2f + %.2f = %d", + // key.get(0).toString(), key.get(3).toString(), key.get(2).toString(), key.get(1).toString(), qualFromRead, globalDeltaQ, deltaQReported, deltaQPos, deltaQDinuc, newQualityByte) ); + //} + + //return newQualityByte; + } - - final double newQuality = qualFromRead + globalDeltaQ + deltaQReported + deltaQCovariates; - return QualityUtils.boundQual( (int)Math.round(newQuality), (byte)MAX_QUALITY_SCORE ); - - // Verbose printouts used to validate with old recalibrator - //if(key.contains(null)) { - // System.out.println( key + String.format(" => %d + %.2f + %.2f + %.2f + %.2f = %d", - // qualFromRead, globalDeltaQ, deltaQReported, deltaQPos, deltaQDinuc, newQualityByte)); - //} - //else { - // System.out.println( String.format("%s %s %s %s => %d + %.2f + %.2f + %.2f + %.2f = %d", - // key.get(0).toString(), key.get(3).toString(), key.get(2).toString(), key.get(1).toString(), qualFromRead, globalDeltaQ, deltaQReported, deltaQPos, deltaQDinuc, newQualityByte) ); - //} - - //return newQualityByte; - - } -*/ + */ } From 059cdcb1bee9c8fa8aac4bff79706d514082097a Mon Sep 17 00:00:00 2001 From: Matt Hanna Date: Mon, 26 Sep 2011 14:58:19 -0400 Subject: [PATCH 193/196] Changing packaging system path for GATK-only Tribble codecs. --- public/packages/GATKEngine.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/packages/GATKEngine.xml b/public/packages/GATKEngine.xml index 4f635f7fb..4364988e7 100644 --- a/public/packages/GATKEngine.xml +++ b/public/packages/GATKEngine.xml @@ -29,7 +29,7 @@ - + From 648b959361dc15247a1cb9b487f9093f96d2c967 Mon Sep 17 00:00:00 2001 From: Khalid Shakir Date: Tue, 27 Sep 2011 00:50:19 -0400 Subject: [PATCH 194/196] Minor change to log an info message when a signal such as Ctrl-C is caught. --- .../broadinstitute/sting/queue/QCommandLine.scala | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) mode change 100755 => 100644 public/scala/src/org/broadinstitute/sting/queue/QCommandLine.scala diff --git a/public/scala/src/org/broadinstitute/sting/queue/QCommandLine.scala b/public/scala/src/org/broadinstitute/sting/queue/QCommandLine.scala old mode 100755 new mode 100644 index 297da8cc9..a3e83871e --- a/public/scala/src/org/broadinstitute/sting/queue/QCommandLine.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/QCommandLine.scala @@ -37,7 +37,7 @@ import org.broadinstitute.sting.utils.exceptions.UserException /** * Entry point of Queue. Compiles and runs QScripts passed in to the command line. */ -object QCommandLine { +object QCommandLine extends Logging { /** * Main. * @param argv Arguments. @@ -45,22 +45,23 @@ object QCommandLine { def main(argv: Array[String]) { val qCommandLine = new QCommandLine - Runtime.getRuntime.addShutdownHook(new Thread { - /** Cleanup as the JVM shuts down. */ + val shutdownHook = new Thread { override def run() { + logger.info("Shutting down jobs. Please wait...") ProcessController.shutdown() qCommandLine.shutdown() } - }) + } + + Runtime.getRuntime.addShutdownHook(shutdownHook) try { CommandLineProgram.start(qCommandLine, argv); + Runtime.getRuntime.removeShutdownHook(shutdownHook) if (CommandLineProgram.result != 0) System.exit(CommandLineProgram.result); } catch { case e: Exception => CommandLineProgram.exitSystemWithError(e) - } finally { - } } } From e99ff3caae467d562d3b1e00be022d34c410a189 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Tue, 27 Sep 2011 10:08:40 -0400 Subject: [PATCH 195/196] Removed lots of old, and not to be used, HMM options -- resulted in massive code cleanup -- GdA will integrate his new banded algorithm here -- Removed: DO_CONTEXT_DEPENDENT_PENALTIES, GET_GAP_PENALTIES_FROM_DATA, INDEL_RECAL_FILE, dovit, GSA_PRODUCTION_ONLY --- ...elGenotypeLikelihoodsCalculationModel.java | 43 +- .../genotyper/UnifiedArgumentCollection.java | 31 +- .../indels/PairHMMIndelErrorModel.java | 702 ++---------------- 3 files changed, 64 insertions(+), 712 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/IndelGenotypeLikelihoodsCalculationModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/IndelGenotypeLikelihoodsCalculationModel.java index 11cde5ffe..6d917325e 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/IndelGenotypeLikelihoodsCalculationModel.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/IndelGenotypeLikelihoodsCalculationModel.java @@ -71,9 +71,6 @@ public class IndelGenotypeLikelihoodsCalculationModel extends GenotypeLikelihood // gdebug removeme // todo -cleanup - private HaplotypeIndelErrorModel model; - private boolean useOldWrongHorribleHackedUpLikelihoodModel = false; -// private GenomeLoc lastSiteVisited; private ArrayList alleleList; @@ -84,26 +81,7 @@ public class IndelGenotypeLikelihoodsCalculationModel extends GenotypeLikelihood protected IndelGenotypeLikelihoodsCalculationModel(UnifiedArgumentCollection UAC, Logger logger) { super(UAC, logger); - if (UAC.GSA_PRODUCTION_ONLY == false) { - pairModel = new PairHMMIndelErrorModel(UAC.INDEL_GAP_OPEN_PENALTY,UAC.INDEL_GAP_CONTINUATION_PENALTY, - UAC.OUTPUT_DEBUG_INDEL_INFO, UAC.DO_CONTEXT_DEPENDENT_PENALTIES, UAC.dovit, UAC.GET_GAP_PENALTIES_FROM_DATA, UAC.INDEL_RECAL_FILE); - useOldWrongHorribleHackedUpLikelihoodModel = false; - } - else { - useOldWrongHorribleHackedUpLikelihoodModel = true; - double INSERTION_START_PROBABILITY = 1e-3; - - double INSERTION_END_PROBABILITY = 0.5; - - double ALPHA_DELETION_PROBABILITY = 1e-3; - - - model = new HaplotypeIndelErrorModel(3, INSERTION_START_PROBABILITY, - INSERTION_END_PROBABILITY,ALPHA_DELETION_PROBABILITY,UAC.INDEL_HAPLOTYPE_SIZE, false, UAC.OUTPUT_DEBUG_INDEL_INFO); - } - - pairModel = new PairHMMIndelErrorModel(UAC.INDEL_GAP_OPEN_PENALTY,UAC.INDEL_GAP_CONTINUATION_PENALTY, - UAC.OUTPUT_DEBUG_INDEL_INFO, UAC.DO_CONTEXT_DEPENDENT_PENALTIES, UAC.dovit, UAC.GET_GAP_PENALTIES_FROM_DATA, UAC.INDEL_RECAL_FILE); + pairModel = new PairHMMIndelErrorModel(UAC.INDEL_GAP_OPEN_PENALTY,UAC.INDEL_GAP_CONTINUATION_PENALTY,UAC.OUTPUT_DEBUG_INDEL_INFO); alleleList = new ArrayList(); getAlleleListFromVCF = UAC.GenotypingMode == GENOTYPING_MODE.GENOTYPE_GIVEN_ALLELES; minIndelCountForGenotyping = UAC.MIN_INDEL_COUNT_FOR_GENOTYPING; @@ -383,14 +361,11 @@ public class IndelGenotypeLikelihoodsCalculationModel extends GenotypeLikelihood } } } - int eventLength = altAllele.getBaseString().length() - refAllele.getBaseString().length(); - int hsize = (int)ref.getWindow().size()-Math.abs(eventLength)-1; - int numPrefBases= ref.getLocus().getStart()-ref.getWindow().getStart()+1; - if (useOldWrongHorribleHackedUpLikelihoodModel) { - numPrefBases = 20; - hsize=80; - } + final int eventLength = altAllele.getBaseString().length() - refAllele.getBaseString().length(); + final int hsize = (int)ref.getWindow().size()-Math.abs(eventLength)-1; + final int numPrefBases= ref.getLocus().getStart()-ref.getWindow().getStart()+1; + if (DEBUG) System.out.format("hsize: %d eventLength: %d refSize: %d, locStart: %d numpr: %d\n",hsize,eventLength, (int)ref.getWindow().size(), loc.getStart(), numPrefBases); @@ -413,13 +388,7 @@ public class IndelGenotypeLikelihoodsCalculationModel extends GenotypeLikelihood pileup = context.getBasePileup(); if (pileup != null ) { - double[] genotypeLikelihoods; - - if (useOldWrongHorribleHackedUpLikelihoodModel) - genotypeLikelihoods = model.computeReadHaplotypeLikelihoods( pileup, haplotypeMap); - else - genotypeLikelihoods = pairModel.computeReadHaplotypeLikelihoods( pileup, haplotypeMap, ref, eventLength, getIndelLikelihoodMap()); - + final double[] genotypeLikelihoods = pairModel.computeReadHaplotypeLikelihoods( pileup, haplotypeMap, ref, eventLength, getIndelLikelihoodMap()); GLs.put(sample.getKey(), new MultiallelicGenotypeLikelihoods(sample.getKey(), alleleList, diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedArgumentCollection.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedArgumentCollection.java index 3c8fd4451..f5a107ee7 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedArgumentCollection.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/UnifiedArgumentCollection.java @@ -143,31 +143,21 @@ public class UnifiedArgumentCollection { @Hidden @Argument(fullName = "indelHaplotypeSize", shortName = "indelHSize", doc = "Indel haplotype size", required = false) public int INDEL_HAPLOTYPE_SIZE = 80; - @Hidden - @Argument(fullName = "doContextDependentGapPenalties", shortName = "doCDP", doc = "Vary gap penalties by context", required = false) - public boolean DO_CONTEXT_DEPENDENT_PENALTIES = true; + //gdebug+ // experimental arguments, NOT TO BE USED BY ANYONE WHOSE INITIALS AREN'T GDA!!! - @Hidden - @Argument(fullName = "getGapPenaltiesFromData", shortName = "dataGP", doc = "Vary gap penalties by context - EXPERIMENTAL, DO NO USE", required = false) - public boolean GET_GAP_PENALTIES_FROM_DATA = false; - - @Hidden - @Argument(fullName="indel_recal_file", shortName="recalFile", required=false, doc="Filename for the input covariates table recalibration .csv file - EXPERIMENTAL, DO NO USE") - public File INDEL_RECAL_FILE = new File("indel.recal_data.csv"); +// @Hidden +// @Argument(fullName = "getGapPenaltiesFromData", shortName = "dataGP", doc = "Vary gap penalties by context - EXPERIMENTAL, DO NO USE", required = false) +// public boolean GET_GAP_PENALTIES_FROM_DATA = false; +// +// @Hidden +// @Argument(fullName="indel_recal_file", shortName="recalFile", required=false, doc="Filename for the input covariates table recalibration .csv file - EXPERIMENTAL, DO NO USE") +// public File INDEL_RECAL_FILE = new File("indel.recal_data.csv"); @Hidden @Argument(fullName = "indelDebug", shortName = "indelDebug", doc = "Output indel debug info", required = false) public boolean OUTPUT_DEBUG_INDEL_INFO = false; - @Hidden - @Argument(fullName = "dovit", shortName = "dovit", doc = "Perform full Viterbi calculation when evaluating the HMM", required = false) - public boolean dovit = false; - - @Hidden - @Argument(fullName = "GSA_PRODUCTION_ONLY", shortName = "GSA_PRODUCTION_ONLY", doc = "don't ever use me", required = false) - public boolean GSA_PRODUCTION_ONLY = false; - @Hidden @Argument(fullName = "ignoreSNPAlleles", shortName = "ignoreSNPAlleles", doc = "expt", required = false) public boolean IGNORE_SNP_ALLELES = false; @@ -204,15 +194,10 @@ public class UnifiedArgumentCollection { uac.INDEL_GAP_CONTINUATION_PENALTY = INDEL_GAP_CONTINUATION_PENALTY; uac.OUTPUT_DEBUG_INDEL_INFO = OUTPUT_DEBUG_INDEL_INFO; uac.INDEL_HAPLOTYPE_SIZE = INDEL_HAPLOTYPE_SIZE; - uac.DO_CONTEXT_DEPENDENT_PENALTIES = DO_CONTEXT_DEPENDENT_PENALTIES; uac.alleles = alleles; - uac.GET_GAP_PENALTIES_FROM_DATA = GET_GAP_PENALTIES_FROM_DATA; - uac.INDEL_RECAL_FILE = INDEL_RECAL_FILE; // todo- arguments to remove uac.COVERAGE_AT_WHICH_TO_ABORT = COVERAGE_AT_WHICH_TO_ABORT; - uac.dovit = dovit; - uac.GSA_PRODUCTION_ONLY = GSA_PRODUCTION_ONLY; uac.IGNORE_SNP_ALLELES = IGNORE_SNP_ALLELES; return uac; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/PairHMMIndelErrorModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/PairHMMIndelErrorModel.java index 6e4db9303..68cbd4fb7 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/PairHMMIndelErrorModel.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/PairHMMIndelErrorModel.java @@ -51,36 +51,8 @@ import org.broadinstitute.sting.oneoffprojects.walkers.IndelCountCovariates.Reca public class PairHMMIndelErrorModel { - - public static final int BASE_QUAL_THRESHOLD = 20; - - private static final int MATCH_OFFSET = 0; - private static final int X_OFFSET = 1; - private static final int Y_OFFSET = 2; - - private static final int DIAG = 0; - private static final int UP = 1; - private static final int LEFT = 2; - - private static final int DIAG_GOTO_M = 0; - private static final int DIAG_GOTO_X = 1; - private static final int DIAG_GOTO_Y = 2; - - private static final int UP_GOTO_M = 4; - private static final int UP_GOTO_X = 5; - private static final int UP_GOTO_Y = 6; - - private static final int LEFT_GOTO_M = 8; - private static final int LEFT_GOTO_X = 9; - private static final int LEFT_GOTO_Y = 10; - - private static final int[] ACTIONS_M = {DIAG_GOTO_M, DIAG_GOTO_X, DIAG_GOTO_Y}; - private static final int[] ACTIONS_X = {UP_GOTO_M, UP_GOTO_X, UP_GOTO_Y}; - private static final int[] ACTIONS_Y = {LEFT_GOTO_M, LEFT_GOTO_X, LEFT_GOTO_Y}; - - private final double logGapOpenProbability; private final double logGapContinuationProbability; @@ -101,36 +73,13 @@ public class PairHMMIndelErrorModel { private static final double MIN_GAP_CONT_PENALTY = 10.0; private static final double GAP_PENALTY_HRUN_STEP = 1.0; // each increase in hrun decreases gap penalty by this. - - private boolean doViterbi = false; - - private final boolean useAffineGapModel = true; - private boolean doContextDependentPenalties = false; - private final double[] GAP_OPEN_PROB_TABLE; private final double[] GAP_CONT_PROB_TABLE; - private boolean getGapPenaltiesFromFile = false; - - private int SMOOTHING = 1; - private int MAX_QUALITY_SCORE = 50; - private int PRESERVE_QSCORES_LESS_THAN = 5; - ///////////////////////////// // Private Member Variables ///////////////////////////// -//copy+ -/* private RecalDataManager dataManager; // Holds the data HashMap, mostly used by TableRecalibrationWalker to create collapsed data hashmaps - private final ArrayList requestedCovariates = new ArrayList(); // List of covariates to be used in this calculation - private static final Pattern COMMENT_PATTERN = Pattern.compile("^#.*"); - private static final Pattern OLD_RECALIBRATOR_HEADER = Pattern.compile("^rg,.*"); - private static final Pattern COVARIATE_PATTERN = Pattern.compile("^ReadGroup,QualityScore,.*"); - protected static final String EOF_MARKER = "EOF"; - private long numReadsWithMalformedColorSpace = 0; - private RecalibrationArgumentCollection RAC = new RecalibrationArgumentCollection(); - private NestedHashMap qualityScoreByFullCovariateKey = new NestedHashMap(); // Caches the result of performSequentialQualityCalculation(..) for all sets of covariate values. - */ -//copy- + static { LOG_ONE_HALF= -Math.log10(2.0); END_GAP_COST = LOG_ONE_HALF; @@ -146,141 +95,9 @@ public class PairHMMIndelErrorModel { } } - public PairHMMIndelErrorModel(double indelGOP, double indelGCP, boolean deb, boolean doCDP, boolean dovit,boolean gpf, File RECAL_FILE) { - - this(indelGOP, indelGCP, deb, doCDP, dovit); - this.getGapPenaltiesFromFile = gpf; - - // read data from recal file - // gdebug - start copy from TableRecalibrationWalker -/* if (gpf) { - boolean sawEOF = false; - boolean REQUIRE_EOF = false; - - int lineNumber = 0; - boolean foundAllCovariates = false; - // Get a list of all available covariates - final List> classes = new PluginManager(Covariate.class).getPlugins(); - - try { - for ( String line : new XReadLines(RECAL_FILE) ) { - lineNumber++; - if ( EOF_MARKER.equals(line) ) { - sawEOF = true; - } else if( COMMENT_PATTERN.matcher(line).matches() || OLD_RECALIBRATOR_HEADER.matcher(line).matches() ) { - ; // Skip over the comment lines, (which start with '#') - } - // Read in the covariates that were used from the input file - else if( COVARIATE_PATTERN.matcher(line).matches() ) { // The line string is either specifying a covariate or is giving csv data - if( foundAllCovariates ) { - throw new UserException.MalformedFile( RECAL_FILE, "Malformed input recalibration file. Found covariate names intermingled with data in file: " + RECAL_FILE ); - } else { // Found the covariate list in input file, loop through all of them and instantiate them - String[] vals = line.split(","); - for( int iii = 0; iii < vals.length - 3; iii++ ) { // There are n-3 covariates. The last three items are nObservations, nMismatch, and Qempirical - boolean foundClass = false; - for( Class covClass : classes ) { - if( (vals[iii] + "Covariate").equalsIgnoreCase( covClass.getSimpleName() ) ) { - foundClass = true; - try { - Covariate covariate = (Covariate)covClass.newInstance(); - requestedCovariates.add( covariate ); - } catch (Exception e) { - throw new DynamicClassResolutionException(covClass, e); - } - - } - } - - if( !foundClass ) { - throw new UserException.MalformedFile(RECAL_FILE, "Malformed input recalibration file. The requested covariate type (" + (vals[iii] + "Covariate") + ") isn't a valid covariate option." ); - } - } - } - - } else { // Found a line of data - if( !foundAllCovariates ) { - foundAllCovariates = true; - - // At this point all the covariates should have been found and initialized - if( requestedCovariates.size() < 2 ) { - throw new UserException.MalformedFile(RECAL_FILE, "Malformed input recalibration csv file. Covariate names can't be found in file: " + RECAL_FILE ); - } - - final boolean createCollapsedTables = true; - - // Initialize any covariate member variables using the shared argument collection - for( Covariate cov : requestedCovariates ) { - cov.initialize( RAC ); - } - // Initialize the data hashMaps - dataManager = new RecalDataManager( createCollapsedTables, requestedCovariates.size() ); - - } - addCSVData(RECAL_FILE, line); // Parse the line and add the data to the HashMap - } - } - - } catch ( FileNotFoundException e ) { - throw new UserException.CouldNotReadInputFile(RECAL_FILE, "Can not find input file", e); - } catch ( NumberFormatException e ) { - throw new UserException.MalformedFile(RECAL_FILE, "Error parsing recalibration data at line " + lineNumber + ". Perhaps your table was generated by an older version of CovariateCounterWalker."); - } - - if ( !sawEOF ) { - final String errorMessage = "No EOF marker was present in the recal covariates table; this could mean that the file is corrupted or was generated with an old version of the CountCovariates tool."; - if ( REQUIRE_EOF ) - throw new UserException.MalformedFile(RECAL_FILE, errorMessage); - } - - if( dataManager == null ) { - throw new UserException.MalformedFile(RECAL_FILE, "Can't initialize the data manager. Perhaps the recal csv file contains no data?"); - } - - // Create the tables of empirical quality scores that will be used in the sequential calculation - dataManager.generateEmpiricalQualities( SMOOTHING, MAX_QUALITY_SCORE ); - } - // debug end copy - */ - } - /** - * For each covariate read in a value and parse it. Associate those values with the data itself (num observation and num mismatches) - */ - /* - private void addCSVData(final File file, final String line) { - final String[] vals = line.split(","); - - // Check if the data line is malformed, for example if the read group string contains a comma then it won't be parsed correctly - if( vals.length != requestedCovariates.size() + 3 ) { // +3 because of nObservations, nMismatch, and Qempirical - throw new UserException.MalformedFile(file, "Malformed input recalibration file. Found data line with too many fields: " + line + - " --Perhaps the read group string contains a comma and isn't being parsed correctly."); - } - - final Object[] key = new Object[requestedCovariates.size()]; - Covariate cov; - int iii; - for( iii = 0; iii < requestedCovariates.size(); iii++ ) { - cov = requestedCovariates.get( iii ); - key[iii] = cov.getValue( vals[iii] ); - } - - // Create a new datum using the number of observations, number of mismatches, and reported quality score - final RecalDatum datum = new RecalDatum( Long.parseLong( vals[iii] ), Long.parseLong( vals[iii + 1] ), Double.parseDouble( vals[1] ), 0.0 ); - // Add that datum to all the collapsed tables which will be used in the sequential calculation - dataManager.addToAllTables( key, datum, PRESERVE_QSCORES_LESS_THAN ); - } - - */ - public PairHMMIndelErrorModel(double indelGOP, double indelGCP, boolean deb, boolean doCDP, boolean dovit) { - this(indelGOP, indelGCP, deb, doCDP); - this.doViterbi = dovit; - } - - public PairHMMIndelErrorModel(double indelGOP, double indelGCP, boolean deb, boolean doCDP) { - - + public PairHMMIndelErrorModel(double indelGOP, double indelGCP, boolean deb) { this.logGapOpenProbability = -indelGOP/10.0; // QUAL to log prob this.logGapContinuationProbability = -indelGCP/10.0; // QUAL to log prob - this.doContextDependentPenalties = doCDP; this.DEBUG = deb; @@ -314,132 +131,6 @@ public class PairHMMIndelErrorModel { } - private double computeReadLikelihoodGivenHaplotype(byte[] haplotypeBases, byte[] readBases, byte[] readQuals) { - final int X_METRIC_LENGTH = readBases.length+1; - final int Y_METRIC_LENGTH = haplotypeBases.length+1; - - // initialize path metric and traceback memories for likelihood computation - double[][] pathMetricArray = new double[X_METRIC_LENGTH][Y_METRIC_LENGTH]; - int[][] bestMetricArray = new int[X_METRIC_LENGTH][Y_METRIC_LENGTH]; - - pathMetricArray[0][0]= 0;//Double.NEGATIVE_INFINITY; - - for (int i=1; i < X_METRIC_LENGTH; i++) { - pathMetricArray[i][0] = 0; - bestMetricArray[i][0] = UP; - } - - for (int j=1; j < Y_METRIC_LENGTH; j++) { - pathMetricArray[0][j] = 0;//logGapOpenProbability + (j-1) * logGapContinuationProbability; - bestMetricArray[0][j] = LEFT; - } - - for (int indI=1; indI < X_METRIC_LENGTH; indI++) { - for (int indJ=1; indJ < Y_METRIC_LENGTH; indJ++) { - - byte x = readBases[indI-1]; - byte y = haplotypeBases[indJ-1]; - byte qual = readQuals[indI-1]; - - double bestMetric = 0.0; - int bestMetricIdx = 0; - - // compute metric for match/mismatch - // workaround for reads whose bases quality = 0, - if (qual < 1) - qual = 1; - - if (qual > MAX_CACHED_QUAL) - qual = MAX_CACHED_QUAL; - - double pBaseRead = (x == y)? baseMatchArray[(int)qual]:baseMismatchArray[(int)qual]; - double[] metrics = new double[3]; - - metrics[DIAG] = pathMetricArray[indI-1][indJ-1] + pBaseRead; - metrics[UP] = pathMetricArray[indI-1][indJ] + logGapOpenProbability;//(end?0.0:logGapOpenProbability); - metrics[LEFT] = pathMetricArray[indI][indJ-1] + logGapOpenProbability;//(end?0.0:logGapOpenProbability); - - if (doViterbi) { - bestMetricIdx = MathUtils.maxElementIndex(metrics); - bestMetric = metrics[bestMetricIdx]; - } - else - bestMetric = MathUtils.softMax(metrics); - - pathMetricArray[indI][indJ] = bestMetric; - bestMetricArray[indI][indJ] = bestMetricIdx; - - } - } - - - double bestMetric=0.0; - int bestMetricIdx=0,bestI=X_METRIC_LENGTH - 1, bestJ=Y_METRIC_LENGTH - 1; - - for (int i=0; i < X_METRIC_LENGTH; i ++ ) { - int j= Y_METRIC_LENGTH-1; - - if (pathMetricArray[i][j] > bestMetric) { - bestMetric = pathMetricArray[i][j]; - bestI = i; - bestJ = j; - } - } - for (int j=0; j < Y_METRIC_LENGTH; j++ ) { - int i= X_METRIC_LENGTH-1; - if (pathMetricArray[i][j] >= bestMetric) { - bestMetric = pathMetricArray[i][j]; - bestI = i; - bestJ = j; - } - } - - if (DEBUG && doViterbi) { - - String haplotypeString = new String (haplotypeBases); - String readString = new String(readBases); - - - int i = bestI; - int j = bestJ; - - - System.out.println("Simple NW"); - - while (i >0 || j >0) { - bestMetricIdx = bestMetricArray[i][j]; - System.out.print(bestMetricIdx); - if (bestMetricIdx == UP) { - // insert gap in Y - haplotypeString = haplotypeString.substring(0,j)+"-"+haplotypeString.substring(j); - i--; - } else if (bestMetricIdx == LEFT) { - readString = readString.substring(0,i)+"-"+readString.substring(i); - j--; - } - else { - i--; j--; - } - } - - - - - System.out.println("\nAlignment: "); - System.out.println("R:"+readString); - System.out.println("H:"+haplotypeString); - System.out.println(); - - - } - if (DEBUG) - System.out.format("Likelihood: %5.4f\n", bestMetric); - - return bestMetric; - - - } - static private void getContextHomopolymerLength(final byte[] refBytes, int[] hrunArray) { // compute forward hrun length, example: // AGGTGACCCCCCTGAGAG @@ -480,14 +171,10 @@ public class PairHMMIndelErrorModel { final int Y_METRIC_LENGTH = haplotypeBases.length+1; // initialize path metric and traceback memories for likelihood computation - double[][] matchMetricArray = new double[X_METRIC_LENGTH][Y_METRIC_LENGTH]; - double[][] XMetricArray = new double[X_METRIC_LENGTH][Y_METRIC_LENGTH]; - double[][] YMetricArray = new double[X_METRIC_LENGTH][Y_METRIC_LENGTH]; - int[][] bestActionArrayM = new int[X_METRIC_LENGTH][Y_METRIC_LENGTH]; - int[][] bestActionArrayX = new int[X_METRIC_LENGTH][Y_METRIC_LENGTH]; - int[][] bestActionArrayY = new int[X_METRIC_LENGTH][Y_METRIC_LENGTH]; + final double[][] matchMetricArray = new double[X_METRIC_LENGTH][Y_METRIC_LENGTH]; + final double[][] XMetricArray = new double[X_METRIC_LENGTH][Y_METRIC_LENGTH]; + final double[][] YMetricArray = new double[X_METRIC_LENGTH][Y_METRIC_LENGTH]; - double c,d; matchMetricArray[0][0]= END_GAP_COST;//Double.NEGATIVE_INFINITY; for (int i=1; i < X_METRIC_LENGTH; i++) { @@ -495,8 +182,6 @@ public class PairHMMIndelErrorModel { matchMetricArray[i][0] = Double.NEGATIVE_INFINITY; YMetricArray[i][0] = Double.NEGATIVE_INFINITY; XMetricArray[i][0] = END_GAP_COST*(i);//logGapOpenProbability + (i-1)*logGapContinuationProbability; - - bestActionArrayX[i][0] = bestActionArrayY[i][0] = bestActionArrayM[i][0] = UP_GOTO_X; } for (int j=1; j < Y_METRIC_LENGTH; j++) { @@ -504,188 +189,46 @@ public class PairHMMIndelErrorModel { matchMetricArray[0][j] = Double.NEGATIVE_INFINITY; XMetricArray[0][j] = Double.NEGATIVE_INFINITY; YMetricArray[0][j] = END_GAP_COST*(j);//logGapOpenProbability + (j-1) * logGapContinuationProbability; - - bestActionArrayY[0][j] = bestActionArrayM[0][j] = bestActionArrayX[0][j] = LEFT_GOTO_Y; } for (int indI=1; indI < X_METRIC_LENGTH; indI++) { - int im1 = indI-1; + final int im1 = indI-1; for (int indJ=1; indJ < Y_METRIC_LENGTH; indJ++) { - int jm1 = indJ-1; - byte x = readBases[im1]; - byte y = haplotypeBases[jm1]; - byte qual = readQuals[im1]; - - double bestMetric = 0.0; - int bestMetricIdx = 0; - - // compute metric for match/mismatch - // workaround for reads whose bases quality = 0, - if (qual < 1) - qual = 1; - - if (qual > MAX_CACHED_QUAL) - qual = MAX_CACHED_QUAL; - - double pBaseRead = (x == y)? baseMatchArray[(int)qual]:baseMismatchArray[(int)qual]; - - - double[] metrics = new double[3]; - - - if (doViterbi) { - // update match array - metrics[MATCH_OFFSET] = matchMetricArray[im1][jm1] + pBaseRead; - metrics[X_OFFSET] = XMetricArray[im1][jm1] + pBaseRead; - metrics[Y_OFFSET] = YMetricArray[im1][jm1] + pBaseRead; - - bestMetricIdx = MathUtils.maxElementIndex(metrics); - bestMetric = metrics[bestMetricIdx]; - } - else - bestMetric = MathUtils.softMax(matchMetricArray[im1][jm1] + pBaseRead, XMetricArray[im1][jm1] + pBaseRead, - YMetricArray[im1][jm1] + pBaseRead); + final int jm1 = indJ-1; + final byte x = readBases[im1]; + final byte y = haplotypeBases[jm1]; + final byte qual = readQuals[im1] < 1 ? 1 : (readQuals[im1] > MAX_CACHED_QUAL ? MAX_CACHED_QUAL : readQuals[im1]); + final double pBaseRead = (x == y)? baseMatchArray[(int)qual]:baseMismatchArray[(int)qual]; + double bestMetric = MathUtils.softMax(matchMetricArray[im1][jm1] + pBaseRead, + XMetricArray[im1][jm1] + pBaseRead, + YMetricArray[im1][jm1] + pBaseRead); matchMetricArray[indI][indJ] = bestMetric; - bestActionArrayM[indI][indJ] = ACTIONS_M[bestMetricIdx]; // update X array // State X(i,j): X(1:i) aligned to a gap in Y(1:j). // When in last column of X, ie X(1:i) aligned to full Y, we don't want to penalize gaps - //c = (indJ==Y_METRIC_LENGTH-1? END_GAP_COST: currentGOP[jm1]); - //d = (indJ==Y_METRIC_LENGTH-1? END_GAP_COST: currentGCP[jm1]); - if (getGapPenaltiesFromFile) { - c = currentGOP[im1]; - d = logGapContinuationProbability; - - } else { - c = currentGOP[jm1]; - d = currentGCP[jm1]; - } - if (indJ == Y_METRIC_LENGTH-1) - c = d = END_GAP_COST; - - if (doViterbi) { - metrics[MATCH_OFFSET] = matchMetricArray[im1][indJ] + c; - metrics[X_OFFSET] = XMetricArray[im1][indJ] + d; - metrics[Y_OFFSET] = Double.NEGATIVE_INFINITY; //YMetricArray[indI-1][indJ] + logGapOpenProbability; - - bestMetricIdx = MathUtils.maxElementIndex(metrics); - bestMetric = metrics[bestMetricIdx]; - } - else - bestMetric = MathUtils.softMax(matchMetricArray[im1][indJ] + c, XMetricArray[im1][indJ] + d); - + final double c1 = indJ == Y_METRIC_LENGTH-1 ? END_GAP_COST : currentGOP[jm1]; + final double d1 = indJ == Y_METRIC_LENGTH-1 ? END_GAP_COST : currentGCP[jm1]; + bestMetric = MathUtils.softMax(matchMetricArray[im1][indJ] + c1, XMetricArray[im1][indJ] + d1); XMetricArray[indI][indJ] = bestMetric; - bestActionArrayX[indI][indJ] = ACTIONS_X[bestMetricIdx]; // update Y array //c = (indI==X_METRIC_LENGTH-1? END_GAP_COST: currentGOP[jm1]); //d = (indI==X_METRIC_LENGTH-1? END_GAP_COST: currentGCP[jm1]); - if (getGapPenaltiesFromFile) { - c = currentGOP[im1]; - d = logGapContinuationProbability; - } - else { - c = currentGOP[jm1]; - d = currentGCP[jm1]; - } - if (indI == X_METRIC_LENGTH-1) - c = d = END_GAP_COST; - - - - if (doViterbi) { - metrics[MATCH_OFFSET] = matchMetricArray[indI][jm1] + c; - metrics[X_OFFSET] = Double.NEGATIVE_INFINITY; //XMetricArray[indI][indJ-1] + logGapOpenProbability; - metrics[Y_OFFSET] = YMetricArray[indI][jm1] + d; - - bestMetricIdx = MathUtils.maxElementIndex(metrics); - bestMetric = metrics[bestMetricIdx]; - } - else - bestMetric = MathUtils.softMax(matchMetricArray[indI][jm1] + c, YMetricArray[indI][jm1] + d); - + final double c2 = indI == X_METRIC_LENGTH-1 ? END_GAP_COST : currentGOP[jm1]; + final double d2 = indI == X_METRIC_LENGTH-1 ? END_GAP_COST : currentGCP[jm1]; + bestMetric = MathUtils.softMax(matchMetricArray[indI][jm1] + c2, YMetricArray[indI][jm1] + d2); YMetricArray[indI][indJ] = bestMetric; - bestActionArrayY[indI][indJ] = ACTIONS_Y[bestMetricIdx]; - - - } } - double bestMetric; - double metrics[] = new double[3]; - int bestTable=0, bestI=X_METRIC_LENGTH - 1, bestJ=Y_METRIC_LENGTH - 1; - metrics[MATCH_OFFSET] = matchMetricArray[bestI][bestJ]; - metrics[X_OFFSET] = XMetricArray[bestI][bestJ]; - metrics[Y_OFFSET] = YMetricArray[bestI][bestJ]; - if (doViterbi) { - bestTable = MathUtils.maxElementIndex(metrics); - bestMetric = metrics[bestTable]; - } - else - bestMetric = MathUtils.softMax(metrics); + final int bestI = X_METRIC_LENGTH - 1, bestJ = Y_METRIC_LENGTH - 1; + final double bestMetric = MathUtils.softMax(matchMetricArray[bestI][bestJ], + XMetricArray[bestI][bestJ], + YMetricArray[bestI][bestJ]); - // Do traceback (needed only for debugging!) - if (DEBUG && doViterbi) { - - int bestAction; - int i = bestI; - int j = bestJ; - - - System.out.println("Affine gap NW"); - - - String haplotypeString = new String (haplotypeBases); - String readString = new String(readBases); - - - while (i >0 || j >0) { - if (bestTable == X_OFFSET) { - // insert gap in Y - haplotypeString = haplotypeString.substring(0,j)+"-"+haplotypeString.substring(j); - bestAction = bestActionArrayX[i][j]; - } - else if (bestTable == Y_OFFSET) { - readString = readString.substring(0,i)+"-"+readString.substring(i); - bestAction = bestActionArrayY[i][j]; - - } - else { - bestAction = bestActionArrayM[i][j]; - } - System.out.print(bestAction); - - - // bestAction contains action to take at next step - // encoding of bestAction: upper 2 bits = direction, lower 2 bits = next table - - // bestTable and nextDirection for next step - bestTable = bestAction & 0x3; - int nextDirection = bestAction >> 2; - if (nextDirection == UP) { - i--; - } else if (nextDirection == LEFT) { - j--; - } else { // if (nextDirection == DIAG) - i--; j--; - } - - } - - - - - System.out.println("\nAlignment: "); - System.out.println("R:"+readString); - System.out.println("H:"+haplotypeString); - System.out.println(); - - - } if (DEBUG) System.out.format("Likelihood: %5.4f\n", bestMetric); @@ -724,33 +267,31 @@ public class PairHMMIndelErrorModel { System.out.println(new String(ref.getBases())); } - if (doContextDependentPenalties && !getGapPenaltiesFromFile) { - // will context dependent probabilities based on homopolymer run. Probabilities are filled based on total complete haplotypes. - - - for (Allele a: haplotypeMap.keySet()) { - Haplotype haplotype = haplotypeMap.get(a); - byte[] haplotypeBases = haplotype.getBasesAsBytes(); - double[] contextLogGapOpenProbabilities = new double[haplotypeBases.length]; - double[] contextLogGapContinuationProbabilities = new double[haplotypeBases.length]; - - // get homopolymer length profile for current haplotype - int[] hrunProfile = new int[haplotypeBases.length]; - getContextHomopolymerLength(haplotypeBases,hrunProfile); - if (DEBUG) { - System.out.println("Haplotype bases:"); - System.out.println(new String(haplotypeBases)); - for (int i=0; i < hrunProfile.length; i++) - System.out.format("%d",hrunProfile[i]); - System.out.println(); - } - fillGapProbabilities(hrunProfile, contextLogGapOpenProbabilities, contextLogGapContinuationProbabilities); - - gapOpenProbabilityMap.put(a,contextLogGapOpenProbabilities); - gapContProbabilityMap.put(a,contextLogGapContinuationProbabilities); + // will context dependent probabilities based on homopolymer run. Probabilities are filled based on total complete haplotypes. + // todo -- refactor into separate function + for (Allele a: haplotypeMap.keySet()) { + Haplotype haplotype = haplotypeMap.get(a); + byte[] haplotypeBases = haplotype.getBasesAsBytes(); + double[] contextLogGapOpenProbabilities = new double[haplotypeBases.length]; + double[] contextLogGapContinuationProbabilities = new double[haplotypeBases.length]; + // get homopolymer length profile for current haplotype + int[] hrunProfile = new int[haplotypeBases.length]; + getContextHomopolymerLength(haplotypeBases,hrunProfile); + if (DEBUG) { + System.out.println("Haplotype bases:"); + System.out.println(new String(haplotypeBases)); + for (int i=0; i < hrunProfile.length; i++) + System.out.format("%d",hrunProfile[i]); + System.out.println(); } + fillGapProbabilities(hrunProfile, contextLogGapOpenProbabilities, contextLogGapContinuationProbabilities); + + gapOpenProbabilityMap.put(a,contextLogGapOpenProbabilities); + gapContProbabilityMap.put(a,contextLogGapContinuationProbabilities); + } + for (PileupElement p: pileup) { // > 1 when the read is a consensus read representing multiple independent observations final boolean isReduced = ReadUtils.isReducedRead(p.getRead()); @@ -774,57 +315,12 @@ public class PairHMMIndelErrorModel { read = ReadUtils.reducedReadWithReducedQuals(read); } - if(ReadUtils.is454Read(read) && !getGapPenaltiesFromFile) { + if(ReadUtils.is454Read(read)) { continue; } double[] recalQuals = null; - /* - if (getGapPenaltiesFromFile) { - RecalDataManager.parseSAMRecord( read, RAC ); - - - recalQuals = new double[read.getReadLength()]; - - //compute all covariate values for this read - final Comparable[][] covariateValues_offset_x_covar = - RecalDataManager.computeCovariates((GATKSAMRecord) read, requestedCovariates); - // For each base in the read - for( int offset = 0; offset < read.getReadLength(); offset++ ) { - - final Object[] fullCovariateKey = covariateValues_offset_x_covar[offset]; - - Byte qualityScore = (Byte) qualityScoreByFullCovariateKey.get(fullCovariateKey); - if(qualityScore == null) - { - qualityScore = performSequentialQualityCalculation( fullCovariateKey ); - qualityScoreByFullCovariateKey.put(qualityScore, fullCovariateKey); - } - - recalQuals[offset] = -((double)qualityScore)/10.0; - } - - // for each read/haplotype combination, compute likelihoods, ie -10*log10(Pr(R | Hi)) - // = sum_j(-10*log10(Pr(R_j | Hi) since reads are assumed to be independent - if (DEBUG) { - System.out.format("\n\nStarting read:%s S:%d US:%d E:%d UE:%d C:%s\n",read.getReadName(), - read.getAlignmentStart(), - read.getUnclippedStart(), read.getAlignmentEnd(), read.getUnclippedEnd(), - read.getCigarString()); - - byte[] bases = read.getReadBases(); - for (int k = 0; k < recalQuals.length; k++) { - System.out.format("%c",bases[k]); - } - System.out.println(); - - for (int k = 0; k < recalQuals.length; k++) { - System.out.format("%.0f ",recalQuals[k]); - } - System.out.println(); - } - } */ // get bases of candidate haplotypes that overlap with reads final int trailingBases = 3; @@ -945,11 +441,6 @@ public class PairHMMIndelErrorModel { unclippedReadBases.length-numEndClippedBases); double[] recalCDP = null; - if (getGapPenaltiesFromFile) { - recalCDP = Arrays.copyOfRange(recalQuals,numStartClippedBases, - unclippedReadBases.length-numEndClippedBases); - - } if (DEBUG) { System.out.println("Read bases:"); @@ -979,27 +470,9 @@ public class PairHMMIndelErrorModel { System.out.println(new String(haplotypeBases)); } - double readLikelihood = 0.0; - if (useAffineGapModel) { - - double[] currentContextGOP = null; - double[] currentContextGCP = null; - - if (doContextDependentPenalties) { - - if (getGapPenaltiesFromFile) { - readLikelihood = computeReadLikelihoodGivenHaplotypeAffineGaps(haplotypeBases, readBases, readQuals, recalCDP, null); - - } else { - currentContextGOP = Arrays.copyOfRange(gapOpenProbabilityMap.get(a), (int)indStart, (int)indStop); - currentContextGCP = Arrays.copyOfRange(gapContProbabilityMap.get(a), (int)indStart, (int)indStop); - readLikelihood = computeReadLikelihoodGivenHaplotypeAffineGaps(haplotypeBases, readBases, readQuals, currentContextGOP, currentContextGCP); - } - } - - } - else - readLikelihood = computeReadLikelihoodGivenHaplotype(haplotypeBases, readBases, readQuals); + final double[] currentContextGOP = Arrays.copyOfRange(gapOpenProbabilityMap.get(a), (int)indStart, (int)indStop); + final double[] currentContextGCP = Arrays.copyOfRange(gapContProbabilityMap.get(a), (int)indStart, (int)indStop); + final double readLikelihood = computeReadLikelihoodGivenHaplotypeAffineGaps(haplotypeBases, readBases, readQuals, currentContextGOP, currentContextGCP); readEl.put(a,readLikelihood); readLikelihoods[readIdx][j++] = readLikelihood; @@ -1057,79 +530,4 @@ public class PairHMMIndelErrorModel { // renormalize so that max element is zero. return MathUtils.normalizeFromLog10(genotypeLikelihoods, false, true); } - - /** - * Implements a serial recalibration of the reads using the combinational table. - * First, we perform a positional recalibration, and then a subsequent dinuc correction. - * - * Given the full recalibration table, we perform the following preprocessing steps: - * - * - calculate the global quality score shift across all data [DeltaQ] - * - calculate for each of cycle and dinuc the shift of the quality scores relative to the global shift - * -- i.e., DeltaQ(dinuc) = Sum(pos) Sum(Qual) Qempirical(pos, qual, dinuc) - Qreported(pos, qual, dinuc) / Npos * Nqual - * - The final shift equation is: - * - * Qrecal = Qreported + DeltaQ + DeltaQ(pos) + DeltaQ(dinuc) + DeltaQ( ... any other covariate ... ) - * @param key The list of Comparables that were calculated from the covariates - * @return A recalibrated quality score as a byte - */ - /* - private byte performSequentialQualityCalculation( final Object... key ) { - - final byte qualFromRead = (byte)Integer.parseInt(key[1].toString()); - final Object[] readGroupCollapsedKey = new Object[1]; - final Object[] qualityScoreCollapsedKey = new Object[2]; - final Object[] covariateCollapsedKey = new Object[3]; - - // The global quality shift (over the read group only) - readGroupCollapsedKey[0] = key[0]; - final RecalDatum globalRecalDatum = ((RecalDatum)dataManager.getCollapsedTable(0).get( readGroupCollapsedKey )); - double globalDeltaQ = 0.0; - if( globalRecalDatum != null ) { - final double globalDeltaQEmpirical = globalRecalDatum.getEmpiricalQuality(); - final double aggregrateQReported = globalRecalDatum.getEstimatedQReported(); - globalDeltaQ = globalDeltaQEmpirical - aggregrateQReported; - } - - // The shift in quality between reported and empirical - qualityScoreCollapsedKey[0] = key[0]; - qualityScoreCollapsedKey[1] = key[1]; - final RecalDatum qReportedRecalDatum = ((RecalDatum)dataManager.getCollapsedTable(1).get( qualityScoreCollapsedKey )); - double deltaQReported = 0.0; - if( qReportedRecalDatum != null ) { - final double deltaQReportedEmpirical = qReportedRecalDatum.getEmpiricalQuality(); - deltaQReported = deltaQReportedEmpirical - qualFromRead - globalDeltaQ; - } - - // The shift in quality due to each covariate by itself in turn - double deltaQCovariates = 0.0; - double deltaQCovariateEmpirical; - covariateCollapsedKey[0] = key[0]; - covariateCollapsedKey[1] = key[1]; - for( int iii = 2; iii < key.length; iii++ ) { - covariateCollapsedKey[2] = key[iii]; // The given covariate - final RecalDatum covariateRecalDatum = ((RecalDatum)dataManager.getCollapsedTable(iii).get( covariateCollapsedKey )); - if( covariateRecalDatum != null ) { - deltaQCovariateEmpirical = covariateRecalDatum.getEmpiricalQuality(); - deltaQCovariates += ( deltaQCovariateEmpirical - qualFromRead - (globalDeltaQ + deltaQReported) ); - } - } - - final double newQuality = qualFromRead + globalDeltaQ + deltaQReported + deltaQCovariates; - return QualityUtils.boundQual( (int)Math.round(newQuality), (byte)MAX_QUALITY_SCORE ); - - // Verbose printouts used to validate with old recalibrator - //if(key.contains(null)) { - // System.out.println( key + String.format(" => %d + %.2f + %.2f + %.2f + %.2f = %d", - // qualFromRead, globalDeltaQ, deltaQReported, deltaQPos, deltaQDinuc, newQualityByte)); - //} - //else { - // System.out.println( String.format("%s %s %s %s => %d + %.2f + %.2f + %.2f + %.2f = %d", - // key.get(0).toString(), key.get(3).toString(), key.get(2).toString(), key.get(1).toString(), qualFromRead, globalDeltaQ, deltaQReported, deltaQPos, deltaQDinuc, newQualityByte) ); - //} - - //return newQualityByte; - - } - */ } From 26e71f6688781b4f4de4c9e13269b64f0409d13d Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Tue, 27 Sep 2011 11:03:17 -0400 Subject: [PATCH 196/196] The Omni files have multiple records (with the same ALT) at a particular location, with one PASSing and the other(s) filtered. Chris, this is why using this file as both eval and comp leads to ref/no-call cells in the GenotypeConcordance table. However, this led to non-determinism in VE because the VCs were placed in a HashSet; we use a LinkedHashMap instead to bring back determinism. --- .../sting/gatk/walkers/varianteval/util/VariantEvalUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/util/VariantEvalUtils.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/util/VariantEvalUtils.java index 92e7c6554..6a057a456 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/util/VariantEvalUtils.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/util/VariantEvalUtils.java @@ -354,7 +354,7 @@ public class VariantEvalUtils { private void addMapping(HashMap> mappings, String sample, VariantContext vc) { if ( !mappings.containsKey(sample) ) - mappings.put(sample, new HashSet()); + mappings.put(sample, new LinkedHashSet()); mappings.get(sample).add(vc); }