From 9ddbfdcb9fbd0c519ed801c6d759d48fab447301 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Mon, 15 Aug 2011 12:25:23 -0400 Subject: [PATCH 002/138] Check filtered status before applying to alt reference --- .../sting/gatk/walkers/fasta/FastaAlternateReferenceWalker.java | 2 ++ 1 file changed, 2 insertions(+) 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 efc101618..17426d4c1 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 @@ -61,6 +61,8 @@ public class FastaAlternateReferenceWalker extends FastaReferenceWalker { // Check to see if we have a called snp for ( VariantContext vc : vcs ) { + if ( vc.isFiltered() ) + continue; if ( !vc.getSource().startsWith("snpmask") ) { if ( vc.isDeletion()) { deletionBasesRemaining = vc.getReference().length(); From fc2c21433b6af1c98ab89c4ce18ba9450bd43d2c Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Mon, 15 Aug 2011 13:29:31 -0400 Subject: [PATCH 003/138] Updating random walkers to new rod system --- .../sting/gatk/walkers/qc/CountIntervals.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountIntervals.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountIntervals.java index 640cff2ba..29b649afe 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountIntervals.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountIntervals.java @@ -2,16 +2,18 @@ package org.broadinstitute.sting.gatk.walkers.qc; import org.broad.tribble.Feature; import org.broadinstitute.sting.commandline.Argument; +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.utils.GATKFeature; import org.broadinstitute.sting.gatk.walkers.RefWalker; import org.broadinstitute.sting.utils.GenomeLoc; import org.broadinstitute.sting.utils.collections.Pair; import java.io.PrintStream; +import java.util.Collections; import java.util.List; /** @@ -23,6 +25,9 @@ public class CountIntervals extends RefWalker { @Output PrintStream out; + @Input(fullName="check", shortName = "check", doc="Any number of RODs", required=false) + public List> features = Collections.emptyList(); + @Argument(fullName="numOverlaps",shortName="no",doc="Count all occurrences of X or more overlapping intervals; defaults to 2", required=false) int numOverlaps = 2; @@ -37,7 +42,7 @@ public class CountIntervals extends RefWalker { return null; } - List checkIntervals = tracker.getValues(Feature.class, "check"); + List checkIntervals = tracker.getValues(features); return (long) checkIntervals.size(); } From 045e8a045eb7aa750c885e80d34a26fa2c634a5a Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Mon, 15 Aug 2011 14:05:23 -0400 Subject: [PATCH 004/138] Updating random walkers to new rod system; removing unused GenotypeAndValidateWalker --- .../broadinstitute/sting/gatk/refdata/RefMetaDataTracker.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/RefMetaDataTracker.java b/public/java/src/org/broadinstitute/sting/gatk/refdata/RefMetaDataTracker.java index b9aaf47de..286e22369 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/RefMetaDataTracker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/refdata/RefMetaDataTracker.java @@ -333,10 +333,6 @@ public class RefMetaDataTracker { return addValues(name, type, new ArrayList(), getTrackDataByName(name), onlyAtThisLoc, true, false); } @Deprecated - public List getValues(final Class type, final Collection names, final GenomeLoc onlyAtThisLoc) { - return addValues(names, type, new ArrayList(), onlyAtThisLoc, true, false); - } - @Deprecated public T getFirstValue(final Class type, final String name) { return safeGetFirst(getValues(type, name)); } From 1246b8904908d3e05329032be7263d0408ab76d9 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Mon, 15 Aug 2011 16:00:43 -0400 Subject: [PATCH 005/138] Forgot to initialize variants on the merge --- .../gatk/walkers/fasta/FastaAlternateReferenceWalker.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 365d3c2c9..a57fabc39 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 @@ -35,6 +35,7 @@ import org.broadinstitute.sting.utils.GenomeLoc; import org.broadinstitute.sting.utils.collections.Pair; import org.broadinstitute.sting.utils.variantcontext.VariantContext; +import java.util.Collections; import java.util.List; @@ -49,7 +50,7 @@ import java.util.List; public class FastaAlternateReferenceWalker extends FastaReferenceWalker { @Input(fullName = "variant", shortName = "V", doc="variants to model", required=false) - public List> variants; + public List> variants = Collections.emptyList(); @Input(fullName="snpmask", shortName = "snpmask", doc="SNP mask VCF file", required=false) public RodBinding snpmask; From 36c7f832082c57281d21ba9e2fa023ad2b47f5a0 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Mon, 15 Aug 2011 16:31:57 -0400 Subject: [PATCH 006/138] Refactoring VE stratifications so that they don't pass around bulky data; instead just pull needed data from the VE parent. This allows us stop using deprecated features of the rod system. --- .../varianteval/VariantEvalWalker.java | 27 +++++++++---------- .../stratifications/AlleleCount.java | 10 ++++--- .../stratifications/AlleleFrequency.java | 4 +-- .../varianteval/stratifications/CompRod.java | 12 +++------ .../varianteval/stratifications/Contig.java | 6 ++--- .../varianteval/stratifications/CpG.java | 6 ++--- .../stratifications/Degeneracy.java | 4 +-- .../varianteval/stratifications/EvalRod.java | 12 +++------ .../varianteval/stratifications/Filter.java | 4 +-- .../stratifications/FunctionalClass.java | 4 +-- .../stratifications/JexlExpression.java | 4 +-- .../varianteval/stratifications/Novelty.java | 20 +++++++------- .../varianteval/stratifications/Sample.java | 6 ++--- .../stratifications/VariantStratifier.java | 4 +-- .../varianteval/util/VariantEvalUtils.java | 2 +- 15 files changed, 50 insertions(+), 75 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 253c6e6d0..633c21320 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 @@ -68,7 +68,8 @@ public class VariantEvalWalker extends RodWalker implements Tr protected Set SAMPLE_EXPRESSIONS; @Argument(shortName="knownName", doc="Name of ROD bindings containing variant sites that should be treated as known when splitting eval rods into known and novel subsets", required=false) - protected String[] KNOWN_NAMES = {}; + protected HashSet KNOWN_NAMES = new HashSet(); + List> knowns = new ArrayList>(); // Stratification arguments @Argument(fullName="stratificationModule", shortName="ST", doc="One or more specific stratification modules to apply to the eval track(s) (in addition to the standard stratifications, unless -noS is specified)", required=false) @@ -108,9 +109,6 @@ public class VariantEvalWalker extends RodWalker implements Tr // Variables private Set jexlExpressions = new TreeSet(); - private Set compNames = new TreeSet(); - private Set knownNames = new TreeSet(); - private Set evalNames = new TreeSet(); private Set sampleNamesForEvaluation = new TreeSet(); private Set sampleNamesForStratification = new TreeSet(); @@ -149,23 +147,24 @@ public class VariantEvalWalker extends RodWalker implements Tr comps.addAll(compsProvided); if ( dbsnp.dbsnp.isBound() ) { comps.add(dbsnp.dbsnp); - knownNames.add(dbsnp.dbsnp.getName()); + knowns.add(dbsnp.dbsnp); } // Add a dummy comp track if none exists if ( comps.size() == 0 ) comps.add(new RodBinding(VariantContext.class, "none", "UNBOUND", "", new Tags())); - // Cache the rod names - for ( RodBinding compRod : comps ) - compNames.add(compRod.getName()); + // Set up set of additional knowns + for ( RodBinding compRod : comps ) { + if ( KNOWN_NAMES.contains(compRod.getName()) ) + knowns.add(compRod); + } + // Collect the eval rod names + Set evalNames = new TreeSet(); for ( RodBinding evalRod : evals ) evalNames.add(evalRod.getName()); - // Set up set of additional known names - knownNames.addAll(Arrays.asList(KNOWN_NAMES)); - // Now that we have all the rods categorized, determine the sample list from the eval rods. Map vcfRods = VCFUtils.getVCFHeadersFromRods(getToolkit(), evalNames); Set vcfSamples = SampleUtils.getSampleList(vcfRods, VariantContextUtils.GenotypeMergeType.REQUIRE_UNIQUE); @@ -462,15 +461,15 @@ public class VariantEvalWalker extends RodWalker implements Tr public static String getAllSampleName() { return ALL_SAMPLE_NAME; } - public Set getKnownNames() { return knownNames; } + public List> getKnowns() { return knowns; } - public Set getEvalNames() { return evalNames; } + public List> getEvals() { return evals; } public Set getSampleNamesForEvaluation() { return sampleNamesForEvaluation; } public Set getSampleNamesForStratification() { return sampleNamesForStratification; } - public Set getCompNames() { return compNames; } + public List> getComps() { return comps; } public Set getJexlExpressions() { return jexlExpressions; } 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 411493d4f..5cdea4e00 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 @@ -1,23 +1,25 @@ package org.broadinstitute.sting.gatk.walkers.varianteval.stratifications; +import org.broadinstitute.sting.commandline.RodBinding; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; -import org.broadinstitute.sting.gatk.walkers.varianteval.util.SortableJexlVCMatchExp; import org.broadinstitute.sting.utils.exceptions.UserException; import org.broadinstitute.sting.utils.variantcontext.Allele; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import java.util.ArrayList; -import java.util.Set; +import java.util.List; public class AlleleCount extends VariantStratifier { // needs to know the variant context private ArrayList states = new ArrayList(); @Override - public void initialize(Set jexlExpressions, Set compNames, Set knownNames, Set evalNames, Set sampleNames, Set contigNames) { + public void initialize() { + List> evals = getVariantEvalWalker().getEvals(); + // we can only work with a single eval VCF, and it must have genotypes - if ( evalNames.size() != 1 ) + if ( evals.size() != 1 ) throw new UserException.BadArgumentValue("AlleleCount", "AlleleCount stratification only works with a single eval vcf"); // There are 2 x n sample chromosomes for diploids 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 2ffc7716c..96d9f30ec 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 @@ -2,19 +2,17 @@ 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.varianteval.util.SortableJexlVCMatchExp; import org.broadinstitute.sting.utils.MathUtils; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import java.util.ArrayList; -import java.util.Set; public class AlleleFrequency extends VariantStratifier { // needs to know the variant context private ArrayList states; @Override - public void initialize(Set jexlExpressions, Set compNames, Set knownNames, Set evalNames, Set sampleNames, Set contigNames) { + public void initialize() { states = new ArrayList(); for( double a = 0.000; a <= 1.005; a += 0.005 ) { states.add(String.format("%.3f", a)); diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/CompRod.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/CompRod.java index c6975808f..9f4123589 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/CompRod.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/CompRod.java @@ -1,24 +1,20 @@ package org.broadinstitute.sting.gatk.walkers.varianteval.stratifications; +import org.broadinstitute.sting.commandline.RodBinding; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; -import org.broadinstitute.sting.gatk.walkers.varianteval.util.SortableJexlVCMatchExp; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import java.util.ArrayList; -import java.util.Set; public class CompRod extends VariantStratifier implements RequiredStratification { - // Needs to know the comp rods - private Set compNames; private ArrayList states; @Override - public void initialize(Set jexlExpressions, Set compNames, Set knownNames, Set evalNames, Set sampleNames, Set contigNames) { - this.compNames = compNames; - + public void initialize() { states = new ArrayList(); - states.addAll(compNames); + for ( RodBinding rod : getVariantEvalWalker().getComps() ) + states.add(rod.getName()); } public ArrayList getAllStates() { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Contig.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Contig.java index c14355035..e12a1ba97 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Contig.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Contig.java @@ -2,20 +2,18 @@ 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.varianteval.util.SortableJexlVCMatchExp; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import java.util.ArrayList; -import java.util.Set; public class Contig extends VariantStratifier { // needs to know the variant context private ArrayList states; @Override - public void initialize(Set jexlExpressions, Set compNames, Set knownNames, Set evalNames, Set sampleNames, Set contigNames) { + public void initialize() { states = new ArrayList(); - states.addAll(contigNames); + states.addAll(getVariantEvalWalker().getContigNames()); states.add("all"); } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/CpG.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/CpG.java index e1f2ae983..ff49c8ba9 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/CpG.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/CpG.java @@ -2,11 +2,9 @@ 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.varianteval.util.SortableJexlVCMatchExp; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import java.util.ArrayList; -import java.util.Set; /** * CpG is a stratification module for VariantEval that divides the input data by within/not within a CpG site @@ -24,7 +22,7 @@ public class CpG extends VariantStratifier { private ArrayList states; @Override - public void initialize(Set jexlExpressions, Set compNames, Set knownNames, Set evalNames, Set sampleNames, Set contigNames) { + public void initialize() { states = new ArrayList(); states.add("all"); states.add("CpG"); @@ -40,7 +38,7 @@ public class CpG extends VariantStratifier { if (ref != null && ref.getBases() != null) { String fwRefBases = new String(ref.getBases()); - String leftFlank = fwRefBases.substring((fwRefBases.length()/2) - 1, (fwRefBases.length()/2) + 1); + //String leftFlank = fwRefBases.substring((fwRefBases.length()/2) - 1, (fwRefBases.length()/2) + 1); String rightFlank = fwRefBases.substring((fwRefBases.length()/2), (fwRefBases.length()/2) + 2); //if (leftFlank.equalsIgnoreCase("CG") || leftFlank.equalsIgnoreCase("GC") || rightFlank.equalsIgnoreCase("CG") || rightFlank.equalsIgnoreCase("GC")) { 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 155a66186..cc878e975 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 @@ -2,13 +2,11 @@ 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.varianteval.util.SortableJexlVCMatchExp; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; -import java.util.Set; public class Degeneracy extends VariantStratifier { private ArrayList states; @@ -16,7 +14,7 @@ public class Degeneracy extends VariantStratifier { private HashMap> degeneracies; @Override - public void initialize(Set jexlExpressions, Set compNames, Set knownNames, Set evalNames, Set sampleNames, Set contigNames) { + public void initialize() { states = new ArrayList(); states.add("1-fold"); states.add("2-fold"); diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/EvalRod.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/EvalRod.java index 40f952fd2..0bfecee25 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/EvalRod.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/EvalRod.java @@ -1,24 +1,20 @@ package org.broadinstitute.sting.gatk.walkers.varianteval.stratifications; +import org.broadinstitute.sting.commandline.RodBinding; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; -import org.broadinstitute.sting.gatk.walkers.varianteval.util.SortableJexlVCMatchExp; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import java.util.ArrayList; -import java.util.Set; public class EvalRod extends VariantStratifier implements RequiredStratification { - // needs to know the eval rods - private Set evalNames; private ArrayList states; @Override - public void initialize(Set jexlExpressions, Set compNames, Set knownNames, Set evalNames, Set sampleNames, Set contigNames) { - this.evalNames = evalNames; - + public void initialize() { states = new ArrayList(); - states.addAll(evalNames); + for ( RodBinding rod : getVariantEvalWalker().getEvals() ) + states.add(rod.getName()); } public ArrayList getAllStates() { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Filter.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Filter.java index 3b7a419f2..3e3cbc224 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Filter.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Filter.java @@ -2,18 +2,16 @@ 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.varianteval.util.SortableJexlVCMatchExp; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import java.util.ArrayList; -import java.util.Set; public class Filter extends VariantStratifier { // needs to know the variant context private ArrayList states; @Override - public void initialize(Set jexlExpressions, Set compNames, Set knownNames, Set evalNames, Set sampleNames, Set contigNames) { + public void initialize() { states = new ArrayList(); states.add("called"); states.add("filtered"); 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 c6c094f8e..0de871fe6 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,18 +2,16 @@ 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.varianteval.util.SortableJexlVCMatchExp; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import java.util.ArrayList; -import java.util.Set; public class FunctionalClass extends VariantStratifier { // needs to know the variant context private ArrayList states; @Override - public void initialize(Set jexlExpressions, Set compNames, Set knownNames, Set evalNames, Set sampleNames, Set contigNames) { + public void initialize() { states = new ArrayList(); states.add("all"); states.add("silent"); diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/JexlExpression.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/JexlExpression.java index 76efedbf4..59b991c4d 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/JexlExpression.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/JexlExpression.java @@ -15,8 +15,8 @@ public class JexlExpression extends VariantStratifier implements StandardStratif private ArrayList states; @Override - public void initialize(Set jexlExpressions, Set compNames, Set knownNames, Set evalNames, Set sampleNames, Set contigNames) { - this.jexlExpressions = jexlExpressions; + public void initialize() { + jexlExpressions = getVariantEvalWalker().getJexlExpressions(); states = new ArrayList(); states.add("none"); diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Novelty.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Novelty.java index d2e4392a5..a3810a4c0 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Novelty.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Novelty.java @@ -1,21 +1,21 @@ package org.broadinstitute.sting.gatk.walkers.varianteval.stratifications; +import org.broadinstitute.sting.commandline.RodBinding; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; -import org.broadinstitute.sting.gatk.walkers.varianteval.util.SortableJexlVCMatchExp; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import java.util.*; public class Novelty extends VariantStratifier implements StandardStratification { // needs the variant contexts and known names - private Set knownNames; + private List> knowns; final private ArrayList states = new ArrayList(Arrays.asList("all", "known", "novel")); @Override - public void initialize(Set jexlExpressions, Set compNames, Set knownNames, Set evalNames, Set sampleNames, Set contigNames) { - this.knownNames = knownNames; + public void initialize() { + knowns = getVariantEvalWalker().getKnowns(); } public ArrayList getAllStates() { @@ -24,13 +24,11 @@ public class Novelty extends VariantStratifier implements StandardStratification public ArrayList getRelevantStates(ReferenceContext ref, RefMetaDataTracker tracker, VariantContext comp, String compName, VariantContext eval, String evalName, String sampleName) { if (tracker != null && eval != null) { - for (final String knownName : knownNames) { - final Collection knownComps = tracker.getValues(VariantContext.class, knownName, ref.getLocus()); - for ( final VariantContext c : knownComps ) { - // loop over sites, looking for something that matches the type eval - if ( eval.getType() == c.getType() ) { - return new ArrayList(Arrays.asList("all", "known")); - } + final Collection knownComps = tracker.getValues(knowns, ref.getLocus()); + for ( final VariantContext c : knownComps ) { + // loop over sites, looking for something that matches the type eval + if ( eval.getType() == c.getType() ) { + return new ArrayList(Arrays.asList("all", "known")); } } } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Sample.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Sample.java index a2a3eb3fb..49e67eec9 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Sample.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Sample.java @@ -2,20 +2,18 @@ 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.varianteval.util.SortableJexlVCMatchExp; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import java.util.ArrayList; -import java.util.Set; public class Sample extends VariantStratifier { // needs the sample names private ArrayList samples; @Override - public void initialize(Set jexlExpressions, Set compNames, Set knownNames, Set evalNames, Set sampleNames, Set contigNames) { + public void initialize() { samples = new ArrayList(); - samples.addAll(sampleNames); + samples.addAll(getVariantEvalWalker().getSampleNamesForEvaluation()); } public ArrayList getAllStates() { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/VariantStratifier.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/VariantStratifier.java index 2c4b8bc46..df6523207 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/VariantStratifier.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/VariantStratifier.java @@ -3,11 +3,9 @@ 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.varianteval.VariantEvalWalker; -import org.broadinstitute.sting.gatk.walkers.varianteval.util.SortableJexlVCMatchExp; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import java.util.ArrayList; -import java.util.Set; public abstract class VariantStratifier implements Comparable { private VariantEvalWalker variantEvalWalker; @@ -27,7 +25,7 @@ public abstract class VariantStratifier implements Comparable { this.variantEvalWalker = variantEvalWalker; } - public abstract void initialize(Set jexlExpressions, Set compNames, Set knownNames, Set evalNames, Set sampleNames, Set contigNames); + public abstract void initialize(); public ArrayList getAllStates() { return new ArrayList(); 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 33fb008ca..ed0e8d7f6 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 @@ -103,7 +103,7 @@ public class VariantEvalUtils { try { VariantStratifier vs = c.newInstance(); vs.setVariantEvalWalker(variantEvalWalker); - vs.initialize(variantEvalWalker.getJexlExpressions(), variantEvalWalker.getCompNames(), variantEvalWalker.getKnownNames(), variantEvalWalker.getEvalNames(), variantEvalWalker.getSampleNamesForStratification(), variantEvalWalker.getContigNames()); + vs.initialize(); strats.add(vs); } catch (InstantiationException e) { From cf3e826a6978fcd50dfd3410a22ebf304e511e2f Mon Sep 17 00:00:00 2001 From: Christopher Hartl Date: Fri, 29 Jul 2011 15:53:56 -0400 Subject: [PATCH 007/138] 1) RFCombine switched to the new ROD system 2) TreeReduce added to useful RODWalkers, but doesn't help very much due to scaling problems 3) RFA refactored, and a genotype-free calculation model added to calculate skew in a genotype-free way (still needs generalization to any ploidy) 4) Added walker to genotype intron loss events, calls into the UG engine to do so. This is very much a first-pass walker. 5) Documentation added for ValidationAmplicons --- .../filters/VariantFiltrationWalker.java | 9 +- .../genotyper/ExactAFCalculationModel.java | 69 ++++++++- .../validation/ValidationAmplicons.java | 72 ++++++++-- .../walkers/variantutils/CombineVariants.java | 6 +- .../broadinstitute/sting/utils/MathUtils.java | 135 +++++++++++++++++- 5 files changed, 279 insertions(+), 12 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/filters/VariantFiltrationWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/filters/VariantFiltrationWalker.java index c555e88cd..e34fa772b 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/filters/VariantFiltrationWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/filters/VariantFiltrationWalker.java @@ -28,6 +28,9 @@ package org.broadinstitute.sting.gatk.walkers.filters; import org.broad.tribble.Feature; import org.broadinstitute.sting.commandline.*; import org.broadinstitute.sting.gatk.arguments.StandardVariantContextInputArgumentCollection; +import org.broadinstitute.sting.gatk.walkers.TreeReducible; +import org.broadinstitute.sting.commandline.Argument; +import org.broadinstitute.sting.commandline.Output; import org.broadinstitute.sting.gatk.contexts.AlignmentContext; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; @@ -47,7 +50,7 @@ import java.util.*; * Filters variant calls using a number of user-selectable, parameterizable criteria. */ @Reference(window=@Window(start=-50,stop=50)) -public class VariantFiltrationWalker extends RodWalker { +public class VariantFiltrationWalker extends RodWalker implements TreeReducible { @ArgumentCollection protected StandardVariantContextInputArgumentCollection variantCollection = new StandardVariantContextInputArgumentCollection(); @@ -278,6 +281,10 @@ public class VariantFiltrationWalker extends RodWalker { return sum + value; } + public Integer treeReduce(Integer left, Integer right) { + return left + right; + } + /** * Tell the user the number of loci processed and close out the new variants file. * 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..fa4863330 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 @@ -34,6 +34,7 @@ import org.broadinstitute.sting.utils.Utils; 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.GenotypeLikelihoods; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import sun.reflect.generics.reflectiveObjects.NotImplementedException; @@ -580,7 +581,7 @@ public class ExactAFCalculationModel extends AlleleFrequencyCalculationModel { // TODO -- remove me for clarity in this code // // ------------------------------------------------------------------------------------- - public int gdaN2GoldStandard(Map GLs, + public static int gdaN2GoldStandard(Map GLs, double[] log10AlleleFrequencyPriors, double[] log10AlleleFrequencyPosteriors, int idxAA, int idxAB, int idxBB) { int numSamples = GLs.size(); @@ -658,4 +659,70 @@ public class ExactAFCalculationModel extends AlleleFrequencyCalculationModel { } } + // todo -- generalize and merge into gdaN2GoldStandard + public static int rfaN2GoldStandard(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++; + + //double[] genotypeLikelihoods = MathUtils.normalizeFromLog10(GLs.get(sample).getLikelihoods()); + double[] genotypeLikelihoods = sample.getValue().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; + } } 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 7653f511f..82c5fc593 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 @@ -31,21 +31,77 @@ import java.util.LinkedList; import java.util.List; /** - * Created by IntelliJ IDEA. - * User: chartl - * Date: 6/13/11 - * Time: 2:12 PM - * To change this template use File | Settings | File Templates. + * Creates FASTA sequences for use in Seqenom or PCR utilities for site amplification and subsequent validation + * + *

+ * ValidationAmplicons consumes a VCF and an Interval list and produces FASTA sequences from which PCR primers or probe + * sequences can be designed. In addition, ValidationAmplicons uses BWA to check for specificity of tracts of bases within + * the output amplicon, lower-casing non-specific tracts, allows for users to provide sites to mask out, and specifies + * reasons why the site may fail validation (nearby variation, for example). + *

+ * + *

Input

+ *

+ * Requires a VCF containing alleles to design amplicons towards, a VCF of variants to mask out of the amplicons, and an + * interval list defining the size of the amplicons around the sites to be validated + *

+ * + *

Output

+ *

+ * Output is a FASTA-formatted file with some modifications at probe sites. For instance: + * + * >20:207414 INSERTION=1,VARIANT_TOO_NEAR_PROBE=1, 20_207414 + * CCAACGTTAAGAAAGAGACATGCGACTGGGTgcggtggctcatgcctggaaccccagcactttgggaggccaaggtgggc[A/G*]gNNcacttgaggtcaggagtttgagaccagcctggccaacatggtgaaaccccgtctctactgaaaatacaaaagttagC + * >20:792122 Valid 20_792122 + * TTTTTTTTTagatggagtctcgctcttatcgcccaggcNggagtgggtggtgtgatcttggctNactgcaacttctgcct[-/CCC*]cccaggttcaagtgattNtcctgcctcagccacctgagtagctgggattacaggcatccgccaccatgcctggctaatTT + * >20:994145 Valid 20_994145 + * TCCATGGCCTCCCCCTGGCCCACGAAGTCCTCAGCCACCTCCTTCCTGGAGGGCTCAGCCAAAATCAGACTGAGGAAGAAG[AAG/-*]TGGTGGGCACCCACCTTCTGGCCTTCCTCAGCCCCTTATTCCTAGGACCAGTCCCCATCTAGGGGTCCTCACTGCCTCCC + * >20:1074230 SITE_IS_FILTERED=1, 20_1074230 + * ACCTGATTACCATCAATCAGAACTCATTTCTGTTCCTATCTTCCACCCACAATTGTAATGCCTTTTCCATTTTAACCAAG[T/C*]ACTTATTATAtactatggccataacttttgcagtttgaggtatgacagcaaaaTTAGCATACATTTCATTTTCCTTCTTC + * >20:1084330 DELETION=1, 20_1084330 + * 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 + * MULTIPLE_PROBES=1, // multiple variants to be validated found inside the same amplicon + * DELETION=6,INSERTION=5, // 6 deletions and 5 insertions found inside the amplicon region (from the "mask" VCF), will be potentially difficult to validate + * DELETION=1, // deletion found inside the amplicon region, could shift mass-spec peak + * START_TOO_CLOSE, // variant is too close to the start of the amplicon region to give sequenom a good chance to find a suitable primer + * 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

+ * PRE-TAG + * java + * -jar GenomeAnalysisTK.jar + * -T ValidationAmplicons + * -R /humgen/1kg/reference/human_g1k_v37.fasta + * -BTI ProbeIntervals + * -ProbeIntervals:table interval_table.table + * -ValidateAlleles:vcf sites_to_validate.vcf + * -MaskAlleles:vcf mask_sites.vcf + * --virtualPrimerSize 30 + * -o probes.fasta + * PRE-TAG + * + * @author chartl + * @since July 2011 */ @Requires(value={DataSource.REFERENCE}) public class ValidationAmplicons extends RodWalker { - @Input(fullName = "ProbeIntervals", doc="Chris document me", required=true) + @Input(fullName = "ProbeIntervals", doc="A collection of intervals in table format with optional names that represent the "+ + "intervals surrounding the probe sites amplicons should be designed for", required=true) RodBinding probeIntervals; - @Input(fullName = "ValidateAlleles", doc="Chris document me", required=true) + @Input(fullName = "ValidateAlleles", doc="A VCF containing the sites and alleles you want to validate. Restricted to *BI-Allelic* sites", required=true) RodBinding validateAlleles; - @Input(fullName = "MaskAlleles", doc="Chris document me", required=true) + @Input(fullName = "MaskAlleles", doc="A VCF containing the sites you want to MASK from the designed amplicon (e.g. by Ns or lower-cased bases)", required=true) RodBinding maskAlleles; 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 fb172e1b7..d46387084 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 @@ -31,6 +31,8 @@ import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.io.stubs.VCFWriterStub; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; import org.broadinstitute.sting.gatk.walkers.Reference; +import org.broadinstitute.sting.gatk.walkers.TreeReducible; +import org.broadinstitute.sting.gatk.walkers.Requires; import org.broadinstitute.sting.gatk.walkers.RodWalker; import org.broadinstitute.sting.gatk.walkers.Window; import org.broadinstitute.sting.utils.SampleUtils; @@ -49,7 +51,7 @@ import java.util.*; * priority list (if provided), emits a single record instance at every position represented in the rods. */ @Reference(window=@Window(start=-50,stop=50)) -public class CombineVariants extends RodWalker { +public class CombineVariants extends RodWalker implements TreeReducible{ /** * The VCF files to merge together * @@ -210,6 +212,8 @@ public class CombineVariants extends RodWalker { return 0; } + public Integer treeReduce(Integer left, Integer right) { return left + right; } + public Integer reduce(Integer counter, Integer sum) { return counter + sum; } diff --git a/public/java/src/org/broadinstitute/sting/utils/MathUtils.java b/public/java/src/org/broadinstitute/sting/utils/MathUtils.java index e197bb973..8c35119dd 100644 --- a/public/java/src/org/broadinstitute/sting/utils/MathUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/MathUtils.java @@ -25,10 +25,13 @@ package org.broadinstitute.sting.utils; +import cern.jet.random.ChiSquare; +import cern.jet.math.Arithmetic; import com.google.java.contract.Requires; import net.sf.samtools.SAMRecord; import org.apache.lucene.messages.NLS; import org.broadinstitute.sting.gatk.GenomeAnalysisEngine; +import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; import org.broadinstitute.sting.utils.exceptions.UserException; import java.math.BigDecimal; @@ -893,6 +896,7 @@ public class MathUtils { return orderStatisticSearch((int) Math.ceil(list.size()/2), list); } + /* public static byte getQScoreOrderStatistic(List reads, List offsets, int k) { // version of the order statistic calculator for SAMRecord/Integer lists, where the // list index maps to a q-score only through the offset index @@ -937,7 +941,7 @@ public class MathUtils { public static byte getQScoreMedian(List reads, List offsets) { return getQScoreOrderStatistic(reads, offsets, (int)Math.floor(reads.size()/2.)); - } + }*/ /** A utility class that computes on the fly average and standard deviation for a stream of numbers. * The number of observations does not have to be known in advance, and can be also very big (so that @@ -1355,4 +1359,133 @@ public class MathUtils { public static double log10Factorial (int x) { return log10Gamma(x+1); } + + /** + * Computes the p-value for the null hypothesis that the rows of the table are i.i.d. using a pearson chi-square test + * @param contingencyTable - a contingency table + * @return - the ensuing p-value (via chi-square) + */ + public static double contingencyChiSquare(short[][] contingencyTable ) { + int[] rowSum = new int[contingencyTable.length]; + int[] colSum = new int[contingencyTable[0].length]; + int total = 0; + for ( int i = 0; i < contingencyTable.length; i++ ) { + for ( int j = 0; j < contingencyTable[0].length; j++ ) { + rowSum[i] += contingencyTable[i][j]; + colSum[j] += contingencyTable[i][j]; + total += contingencyTable[i][j]; + } + } + + double chi = 0; + for ( int i = 0; i < contingencyTable.length; i ++ ) { + for ( int j = 0; j < contingencyTable[0].length; j++ ) { + double expected = (((double) colSum[j])/total)*rowSum[i]; + double resid = contingencyTable[i][j] - expected; + chi += resid*resid/expected; + } + } + + return 1.0-(new ChiSquare(contingencyTable.length*contingencyTable[0].length,null)).cdf(chi); + } + + /** + * Exactly the same as above, but using int arrays rather than short arrays on input + * @param contingencyTable + * @return + */ + public static double contingencyChiSquare(int[][] contingencyTable ) { + int[] rowSum = new int[contingencyTable.length]; + int[] colSum = new int[contingencyTable[0].length]; + int total = 0; + for ( int i = 0; i < contingencyTable.length; i++ ) { + for ( int j = 0; j < contingencyTable[0].length; j++ ) { + rowSum[i] += contingencyTable[i][j]; + colSum[j] += contingencyTable[i][j]; + total += contingencyTable[i][j]; + } + } + + double chi = 0; + for ( int i = 0; i < contingencyTable.length; i ++ ) { + for ( int j = 0; j < contingencyTable[0].length; j++ ) { + double expected = (((double) colSum[j])/total)*rowSum[i]; + double resid = contingencyTable[i][j] - expected; + chi += resid*resid/expected; + } + } + + return 1.0-(new ChiSquare(contingencyTable.length*contingencyTable[0].length,null)).cdf(chi); + } + +======= + public static double marginalizedFisherExact(double[] spectrum1, double[] spectrum2, int ns1, int ns2) { + int N = ns1 + ns2; + int[] rowSums = { ns1, ns2 }; + double logP = Double.NEGATIVE_INFINITY; + // todo -- sorting and strobing should chage this n^2 loop to a nlog(n) algorithm + for ( int ac1 = 0; ac1 < spectrum1.length; ac1++ ) { + for ( int ac2 = 0; ac2 < spectrum2.length; ac2++ ) { + double logPTable = spectrum1[ac1] + spectrum2[ac2]; + int[][] table = { + { ac1, ns1-ac1 }, + { ac2, ns2-ac2 } + }; + int[] colSums = { ac1 + ac2, N-ac1-ac2}; + double logPH0 = Math.log10(fisherExact(table,rowSums,colSums,N)); + logP = log10sumLog10(new double[]{logP,logPTable+logPH0}); + } + } + + return Math.pow(10,logP); + } + + /** + * Calculates the p-value for a fisher exact test for a 2x2 contingency table + */ + public static double fisherExact(int[][] table) { + if ( table.length > 2 || table[0].length > 2 ) { + throw new ReviewedStingException("Fisher exact is only implemented for 2x2 contingency tables"); + } + + int[] rowSums = { sumRow(table, 0), sumRow(table, 1) }; + int[] colSums = { sumColumn(table, 0), sumColumn(table, 1) }; + int N = rowSums[0] + rowSums[1]; + + return fisherExact(table,rowSums,colSums,N); + + } + + public static double fisherExact(int[][] table, int[] rowSums, int[] colSums, int N ) { + + // calculate in log space so we don't die with high numbers + double pCutoff = Arithmetic.logFactorial(rowSums[0]) + + Arithmetic.logFactorial(rowSums[1]) + + Arithmetic.logFactorial(colSums[0]) + + Arithmetic.logFactorial(colSums[1]) + - Arithmetic.logFactorial(table[0][0]) + - Arithmetic.logFactorial(table[0][1]) + - Arithmetic.logFactorial(table[1][0]) + - Arithmetic.logFactorial(table[1][1]) + - Arithmetic.logFactorial(N); + return Math.exp(pCutoff); + } + + public static int sumRow(int[][] table, int column) { + int sum = 0; + for (int r = 0; r < table.length; r++) { + sum += table[r][column]; + } + + return sum; + } + + public static int sumColumn(int[][] table, int row) { + int sum = 0; + for (int c = 0; c < table[row].length; c++) { + sum += table[row][c]; + } + + return sum; + } } From 5aa61fefec8442de1826343154e4f83c89704ede Mon Sep 17 00:00:00 2001 From: Christopher Hartl Date: Mon, 15 Aug 2011 16:53:05 -0400 Subject: [PATCH 008/138] Remove merge-added ======'s so this compiles --- public/java/src/org/broadinstitute/sting/utils/MathUtils.java | 1 - 1 file changed, 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/MathUtils.java b/public/java/src/org/broadinstitute/sting/utils/MathUtils.java index 8c35119dd..75bb0151a 100644 --- a/public/java/src/org/broadinstitute/sting/utils/MathUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/MathUtils.java @@ -1418,7 +1418,6 @@ public class MathUtils { return 1.0-(new ChiSquare(contingencyTable.length*contingencyTable[0].length,null)).cdf(chi); } -======= public static double marginalizedFisherExact(double[] spectrum1, double[] spectrum2, int ns1, int ns2) { int N = ns1 + ns2; int[] rowSums = { ns1, ns2 }; From 1968b65ca86f6a5bf5142049366462aa86a0768c Mon Sep 17 00:00:00 2001 From: David Roazen Date: Mon, 15 Aug 2011 18:45:21 -0400 Subject: [PATCH 009/138] Revert "Remove merge-added ======'s so this compiles" This reverts commit be028b6513a129f81aa6f3593ea7d396c0e8fc25. --- public/java/src/org/broadinstitute/sting/utils/MathUtils.java | 1 + 1 file changed, 1 insertion(+) diff --git a/public/java/src/org/broadinstitute/sting/utils/MathUtils.java b/public/java/src/org/broadinstitute/sting/utils/MathUtils.java index 75bb0151a..8c35119dd 100644 --- a/public/java/src/org/broadinstitute/sting/utils/MathUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/MathUtils.java @@ -1418,6 +1418,7 @@ public class MathUtils { return 1.0-(new ChiSquare(contingencyTable.length*contingencyTable[0].length,null)).cdf(chi); } +======= public static double marginalizedFisherExact(double[] spectrum1, double[] spectrum2, int ns1, int ns2) { int N = ns1 + ns2; int[] rowSums = { ns1, ns2 }; From 3e9ef0622de51e5ffbfb88e0be2a6825f33d43f4 Mon Sep 17 00:00:00 2001 From: David Roazen Date: Mon, 15 Aug 2011 18:45:38 -0400 Subject: [PATCH 010/138] Revert "1) RFCombine switched to the new ROD system" This reverts commit cf989bd3cfae119ba9011873c5f5d5b80e37f67b. --- .../filters/VariantFiltrationWalker.java | 9 +- .../genotyper/ExactAFCalculationModel.java | 69 +-------- .../validation/ValidationAmplicons.java | 72 ++-------- .../walkers/variantutils/CombineVariants.java | 6 +- .../broadinstitute/sting/utils/MathUtils.java | 135 +----------------- 5 files changed, 12 insertions(+), 279 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/filters/VariantFiltrationWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/filters/VariantFiltrationWalker.java index e34fa772b..c555e88cd 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/filters/VariantFiltrationWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/filters/VariantFiltrationWalker.java @@ -28,9 +28,6 @@ package org.broadinstitute.sting.gatk.walkers.filters; import org.broad.tribble.Feature; import org.broadinstitute.sting.commandline.*; import org.broadinstitute.sting.gatk.arguments.StandardVariantContextInputArgumentCollection; -import org.broadinstitute.sting.gatk.walkers.TreeReducible; -import org.broadinstitute.sting.commandline.Argument; -import org.broadinstitute.sting.commandline.Output; import org.broadinstitute.sting.gatk.contexts.AlignmentContext; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; @@ -50,7 +47,7 @@ import java.util.*; * Filters variant calls using a number of user-selectable, parameterizable criteria. */ @Reference(window=@Window(start=-50,stop=50)) -public class VariantFiltrationWalker extends RodWalker implements TreeReducible { +public class VariantFiltrationWalker extends RodWalker { @ArgumentCollection protected StandardVariantContextInputArgumentCollection variantCollection = new StandardVariantContextInputArgumentCollection(); @@ -281,10 +278,6 @@ public class VariantFiltrationWalker extends RodWalker impleme return sum + value; } - public Integer treeReduce(Integer left, Integer right) { - return left + right; - } - /** * Tell the user the number of loci processed and close out the new variants file. * 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 fa4863330..cd006a3cf 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 @@ -34,7 +34,6 @@ import org.broadinstitute.sting.utils.Utils; 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.GenotypeLikelihoods; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import sun.reflect.generics.reflectiveObjects.NotImplementedException; @@ -581,7 +580,7 @@ public class ExactAFCalculationModel extends AlleleFrequencyCalculationModel { // TODO -- remove me for clarity in this code // // ------------------------------------------------------------------------------------- - public static int gdaN2GoldStandard(Map GLs, + public int gdaN2GoldStandard(Map GLs, double[] log10AlleleFrequencyPriors, double[] log10AlleleFrequencyPosteriors, int idxAA, int idxAB, int idxBB) { int numSamples = GLs.size(); @@ -659,70 +658,4 @@ public class ExactAFCalculationModel extends AlleleFrequencyCalculationModel { } } - // todo -- generalize and merge into gdaN2GoldStandard - public static int rfaN2GoldStandard(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++; - - //double[] genotypeLikelihoods = MathUtils.normalizeFromLog10(GLs.get(sample).getLikelihoods()); - double[] genotypeLikelihoods = sample.getValue().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; - } } 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 82c5fc593..7653f511f 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 @@ -31,77 +31,21 @@ import java.util.LinkedList; import java.util.List; /** - * Creates FASTA sequences for use in Seqenom or PCR utilities for site amplification and subsequent validation - * - *

- * ValidationAmplicons consumes a VCF and an Interval list and produces FASTA sequences from which PCR primers or probe - * sequences can be designed. In addition, ValidationAmplicons uses BWA to check for specificity of tracts of bases within - * the output amplicon, lower-casing non-specific tracts, allows for users to provide sites to mask out, and specifies - * reasons why the site may fail validation (nearby variation, for example). - *

- * - *

Input

- *

- * Requires a VCF containing alleles to design amplicons towards, a VCF of variants to mask out of the amplicons, and an - * interval list defining the size of the amplicons around the sites to be validated - *

- * - *

Output

- *

- * Output is a FASTA-formatted file with some modifications at probe sites. For instance: - * - * >20:207414 INSERTION=1,VARIANT_TOO_NEAR_PROBE=1, 20_207414 - * CCAACGTTAAGAAAGAGACATGCGACTGGGTgcggtggctcatgcctggaaccccagcactttgggaggccaaggtgggc[A/G*]gNNcacttgaggtcaggagtttgagaccagcctggccaacatggtgaaaccccgtctctactgaaaatacaaaagttagC - * >20:792122 Valid 20_792122 - * TTTTTTTTTagatggagtctcgctcttatcgcccaggcNggagtgggtggtgtgatcttggctNactgcaacttctgcct[-/CCC*]cccaggttcaagtgattNtcctgcctcagccacctgagtagctgggattacaggcatccgccaccatgcctggctaatTT - * >20:994145 Valid 20_994145 - * TCCATGGCCTCCCCCTGGCCCACGAAGTCCTCAGCCACCTCCTTCCTGGAGGGCTCAGCCAAAATCAGACTGAGGAAGAAG[AAG/-*]TGGTGGGCACCCACCTTCTGGCCTTCCTCAGCCCCTTATTCCTAGGACCAGTCCCCATCTAGGGGTCCTCACTGCCTCCC - * >20:1074230 SITE_IS_FILTERED=1, 20_1074230 - * ACCTGATTACCATCAATCAGAACTCATTTCTGTTCCTATCTTCCACCCACAATTGTAATGCCTTTTCCATTTTAACCAAG[T/C*]ACTTATTATAtactatggccataacttttgcagtttgaggtatgacagcaaaaTTAGCATACATTTCATTTTCCTTCTTC - * >20:1084330 DELETION=1, 20_1084330 - * 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 - * MULTIPLE_PROBES=1, // multiple variants to be validated found inside the same amplicon - * DELETION=6,INSERTION=5, // 6 deletions and 5 insertions found inside the amplicon region (from the "mask" VCF), will be potentially difficult to validate - * DELETION=1, // deletion found inside the amplicon region, could shift mass-spec peak - * START_TOO_CLOSE, // variant is too close to the start of the amplicon region to give sequenom a good chance to find a suitable primer - * 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

- * PRE-TAG - * java - * -jar GenomeAnalysisTK.jar - * -T ValidationAmplicons - * -R /humgen/1kg/reference/human_g1k_v37.fasta - * -BTI ProbeIntervals - * -ProbeIntervals:table interval_table.table - * -ValidateAlleles:vcf sites_to_validate.vcf - * -MaskAlleles:vcf mask_sites.vcf - * --virtualPrimerSize 30 - * -o probes.fasta - * PRE-TAG - * - * @author chartl - * @since July 2011 + * Created by IntelliJ IDEA. + * User: chartl + * Date: 6/13/11 + * Time: 2:12 PM + * To change this template use File | Settings | File Templates. */ @Requires(value={DataSource.REFERENCE}) public class ValidationAmplicons extends RodWalker { - @Input(fullName = "ProbeIntervals", doc="A collection of intervals in table format with optional names that represent the "+ - "intervals surrounding the probe sites amplicons should be designed for", required=true) + @Input(fullName = "ProbeIntervals", doc="Chris document me", required=true) RodBinding probeIntervals; - @Input(fullName = "ValidateAlleles", doc="A VCF containing the sites and alleles you want to validate. Restricted to *BI-Allelic* sites", required=true) + @Input(fullName = "ValidateAlleles", doc="Chris document me", required=true) RodBinding validateAlleles; - @Input(fullName = "MaskAlleles", doc="A VCF containing the sites you want to MASK from the designed amplicon (e.g. by Ns or lower-cased bases)", required=true) + @Input(fullName = "MaskAlleles", doc="Chris document me", required=true) RodBinding maskAlleles; 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 d46387084..fb172e1b7 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 @@ -31,8 +31,6 @@ import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.io.stubs.VCFWriterStub; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; import org.broadinstitute.sting.gatk.walkers.Reference; -import org.broadinstitute.sting.gatk.walkers.TreeReducible; -import org.broadinstitute.sting.gatk.walkers.Requires; import org.broadinstitute.sting.gatk.walkers.RodWalker; import org.broadinstitute.sting.gatk.walkers.Window; import org.broadinstitute.sting.utils.SampleUtils; @@ -51,7 +49,7 @@ import java.util.*; * priority list (if provided), emits a single record instance at every position represented in the rods. */ @Reference(window=@Window(start=-50,stop=50)) -public class CombineVariants extends RodWalker implements TreeReducible{ +public class CombineVariants extends RodWalker { /** * The VCF files to merge together * @@ -212,8 +210,6 @@ public class CombineVariants extends RodWalker implements Tree return 0; } - public Integer treeReduce(Integer left, Integer right) { return left + right; } - public Integer reduce(Integer counter, Integer sum) { return counter + sum; } diff --git a/public/java/src/org/broadinstitute/sting/utils/MathUtils.java b/public/java/src/org/broadinstitute/sting/utils/MathUtils.java index 8c35119dd..e197bb973 100644 --- a/public/java/src/org/broadinstitute/sting/utils/MathUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/MathUtils.java @@ -25,13 +25,10 @@ package org.broadinstitute.sting.utils; -import cern.jet.random.ChiSquare; -import cern.jet.math.Arithmetic; import com.google.java.contract.Requires; import net.sf.samtools.SAMRecord; import org.apache.lucene.messages.NLS; import org.broadinstitute.sting.gatk.GenomeAnalysisEngine; -import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; import org.broadinstitute.sting.utils.exceptions.UserException; import java.math.BigDecimal; @@ -896,7 +893,6 @@ public class MathUtils { return orderStatisticSearch((int) Math.ceil(list.size()/2), list); } - /* public static byte getQScoreOrderStatistic(List reads, List offsets, int k) { // version of the order statistic calculator for SAMRecord/Integer lists, where the // list index maps to a q-score only through the offset index @@ -941,7 +937,7 @@ public class MathUtils { public static byte getQScoreMedian(List reads, List offsets) { return getQScoreOrderStatistic(reads, offsets, (int)Math.floor(reads.size()/2.)); - }*/ + } /** A utility class that computes on the fly average and standard deviation for a stream of numbers. * The number of observations does not have to be known in advance, and can be also very big (so that @@ -1359,133 +1355,4 @@ public class MathUtils { public static double log10Factorial (int x) { return log10Gamma(x+1); } - - /** - * Computes the p-value for the null hypothesis that the rows of the table are i.i.d. using a pearson chi-square test - * @param contingencyTable - a contingency table - * @return - the ensuing p-value (via chi-square) - */ - public static double contingencyChiSquare(short[][] contingencyTable ) { - int[] rowSum = new int[contingencyTable.length]; - int[] colSum = new int[contingencyTable[0].length]; - int total = 0; - for ( int i = 0; i < contingencyTable.length; i++ ) { - for ( int j = 0; j < contingencyTable[0].length; j++ ) { - rowSum[i] += contingencyTable[i][j]; - colSum[j] += contingencyTable[i][j]; - total += contingencyTable[i][j]; - } - } - - double chi = 0; - for ( int i = 0; i < contingencyTable.length; i ++ ) { - for ( int j = 0; j < contingencyTable[0].length; j++ ) { - double expected = (((double) colSum[j])/total)*rowSum[i]; - double resid = contingencyTable[i][j] - expected; - chi += resid*resid/expected; - } - } - - return 1.0-(new ChiSquare(contingencyTable.length*contingencyTable[0].length,null)).cdf(chi); - } - - /** - * Exactly the same as above, but using int arrays rather than short arrays on input - * @param contingencyTable - * @return - */ - public static double contingencyChiSquare(int[][] contingencyTable ) { - int[] rowSum = new int[contingencyTable.length]; - int[] colSum = new int[contingencyTable[0].length]; - int total = 0; - for ( int i = 0; i < contingencyTable.length; i++ ) { - for ( int j = 0; j < contingencyTable[0].length; j++ ) { - rowSum[i] += contingencyTable[i][j]; - colSum[j] += contingencyTable[i][j]; - total += contingencyTable[i][j]; - } - } - - double chi = 0; - for ( int i = 0; i < contingencyTable.length; i ++ ) { - for ( int j = 0; j < contingencyTable[0].length; j++ ) { - double expected = (((double) colSum[j])/total)*rowSum[i]; - double resid = contingencyTable[i][j] - expected; - chi += resid*resid/expected; - } - } - - return 1.0-(new ChiSquare(contingencyTable.length*contingencyTable[0].length,null)).cdf(chi); - } - -======= - public static double marginalizedFisherExact(double[] spectrum1, double[] spectrum2, int ns1, int ns2) { - int N = ns1 + ns2; - int[] rowSums = { ns1, ns2 }; - double logP = Double.NEGATIVE_INFINITY; - // todo -- sorting and strobing should chage this n^2 loop to a nlog(n) algorithm - for ( int ac1 = 0; ac1 < spectrum1.length; ac1++ ) { - for ( int ac2 = 0; ac2 < spectrum2.length; ac2++ ) { - double logPTable = spectrum1[ac1] + spectrum2[ac2]; - int[][] table = { - { ac1, ns1-ac1 }, - { ac2, ns2-ac2 } - }; - int[] colSums = { ac1 + ac2, N-ac1-ac2}; - double logPH0 = Math.log10(fisherExact(table,rowSums,colSums,N)); - logP = log10sumLog10(new double[]{logP,logPTable+logPH0}); - } - } - - return Math.pow(10,logP); - } - - /** - * Calculates the p-value for a fisher exact test for a 2x2 contingency table - */ - public static double fisherExact(int[][] table) { - if ( table.length > 2 || table[0].length > 2 ) { - throw new ReviewedStingException("Fisher exact is only implemented for 2x2 contingency tables"); - } - - int[] rowSums = { sumRow(table, 0), sumRow(table, 1) }; - int[] colSums = { sumColumn(table, 0), sumColumn(table, 1) }; - int N = rowSums[0] + rowSums[1]; - - return fisherExact(table,rowSums,colSums,N); - - } - - public static double fisherExact(int[][] table, int[] rowSums, int[] colSums, int N ) { - - // calculate in log space so we don't die with high numbers - double pCutoff = Arithmetic.logFactorial(rowSums[0]) - + Arithmetic.logFactorial(rowSums[1]) - + Arithmetic.logFactorial(colSums[0]) - + Arithmetic.logFactorial(colSums[1]) - - Arithmetic.logFactorial(table[0][0]) - - Arithmetic.logFactorial(table[0][1]) - - Arithmetic.logFactorial(table[1][0]) - - Arithmetic.logFactorial(table[1][1]) - - Arithmetic.logFactorial(N); - return Math.exp(pCutoff); - } - - public static int sumRow(int[][] table, int column) { - int sum = 0; - for (int r = 0; r < table.length; r++) { - sum += table[r][column]; - } - - return sum; - } - - public static int sumColumn(int[][] table, int row) { - int sum = 0; - for (int c = 0; c < table[row].length; c++) { - sum += table[row][c]; - } - - return sum; - } } From ab1e3d6a98669dbae7ce5311ad0240018b7b51bf Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Tue, 16 Aug 2011 01:03:05 -0400 Subject: [PATCH 012/138] Use the right set of sample names --- .../sting/gatk/walkers/varianteval/stratifications/Sample.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Sample.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Sample.java index 49e67eec9..b714fa291 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Sample.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/stratifications/Sample.java @@ -13,7 +13,7 @@ public class Sample extends VariantStratifier { @Override public void initialize() { samples = new ArrayList(); - samples.addAll(getVariantEvalWalker().getSampleNamesForEvaluation()); + samples.addAll(getVariantEvalWalker().getSampleNamesForStratification()); } public ArrayList getAllStates() { From 9e1d443c47122828412d374dc8f2869e06b40ab0 Mon Sep 17 00:00:00 2001 From: Andrey Sivachenko Date: Tue, 16 Aug 2011 10:55:51 -0400 Subject: [PATCH 015/138] fixing read group name collision: before writing the read into respective stream in nway-out mode we now retrieve the original rg, not the merged/modified one --- .../broadinstitute/sting/utils/sam/NWaySAMFileWriter.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/public/java/src/org/broadinstitute/sting/utils/sam/NWaySAMFileWriter.java b/public/java/src/org/broadinstitute/sting/utils/sam/NWaySAMFileWriter.java index 3718345a4..70417889b 100644 --- a/public/java/src/org/broadinstitute/sting/utils/sam/NWaySAMFileWriter.java +++ b/public/java/src/org/broadinstitute/sting/utils/sam/NWaySAMFileWriter.java @@ -135,6 +135,11 @@ public class NWaySAMFileWriter implements SAMFileWriter { public void addAlignment(SAMRecord samRecord) { final SAMReaderID id = toolkit.getReaderIDForRead(samRecord); + String rg = samRecord.getStringAttribute("RG"); + if ( rg != null ) { + String rg_orig = toolkit.getReadsDataSource().getOriginalReadGroupId(rg); + samRecord.setAttribute("RG",rg_orig); + } writerMap.get(id).addAlignment(samRecord); } From ef9216011ea1f7e82a40145dbdc16edf008cda87 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Tue, 16 Aug 2011 12:24:53 -0400 Subject: [PATCH 017/138] Added docs to IR --- .../gatk/walkers/indels/IndelRealigner.java | 163 ++++++++++++++---- 1 file changed, 126 insertions(+), 37 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java index fa3991694..426c10604 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java @@ -65,10 +65,51 @@ import java.util.*; /** * Performs local realignment of reads based on misalignments due to the presence of indels. - * Unlike most mappers, this walker uses the full alignment context to determine whether an - * appropriate alternate reference (i.e. indel) exists and updates SAMRecords accordingly. + * + *

+ * The local realignment tool is designed to consume one or more BAM files and to locally realign reads such that the number of mismatching bases + * is minimized across all the reads. In general, a large percent of regions requiring local realignment are due to the presence of an insertion + * or deletion (indels) in the individualÕs genome with respect to the reference genome. Such alignment artifacts result in many bases mismatching + * the reference near the misalignment, which are easily mistaken as SNPs. Moreover, since read mapping algorithms operate on each read independently, + * it is impossible to place reads on the reference genome such at mismatches are minimized across all reads. Consequently, even when some reads are + * correctly mapped with indels, reads covering the indel near just the start or end of the read are often incorrectly mapped with respect the true indel, + * also requiring realignment. Local realignment serves to transform regions with misalignments due to indels into clean reads containing a consensus + * indel suitable for standard variant discovery approaches. Unlike most mappers, this walker uses the full alignment context to determine whether an + * appropriate alternate reference (i.e. indel) exists. Following local realignment, the GATK tool Unified Genotyper can be used to sensitively and + * specifically identify indels. + *

+ *

    There are 2 steps to the realignment process: + *
  1. Determining (small) suspicious intervals which are likely in need of realignment (see the RealignerTargetCreator tool)
  2. + *
  3. Running the realigner over those intervals (IndelRealigner)
  4. + *
+ *

+ * An important note: because reads produced from the 454 technology inherently contain false indels, the realigner will not currently work with them + * (or with reads from similar technologies). + * + *

Input

+ *

+ * One or more aligned BAM files and optionally one or more lists of known indels. + *

+ * + *

Output

+ *

+ * A realigned version of your input BAM file(s). + *

+ * + *

Examples

+ *
+ * java -Xmx4g -jar GenomeAnalysisTK.jar \
+ *   -I  \
+ *   -R  \
+ *   -T IndelRealigner \
+ *   -targetIntervals  \
+ *   -o  \
+ *   [--known /path/to/indels.vcf] \
+ *   [-compress 0]    (this argument recommended to speed up the process *if* this is only a temporary file; otherwise, use the default value)
+ * 
+ * + * @author ebanks */ -//Reference(window=@Window(start=-30,stop=30)) @BAQMode(QualityMode = BAQ.QualityMode.ADD_TAG, ApplicationTime = BAQ.ApplicationTime.ON_OUTPUT) public class IndelRealigner extends ReadWalker { @@ -77,88 +118,136 @@ public class IndelRealigner extends ReadWalker { public static final String PROGRAM_RECORD_NAME = "GATK IndelRealigner"; public enum ConsensusDeterminationModel { + /** + * Uses only indels from a provided ROD of known indels. + */ KNOWNS_ONLY, + /** + * Additionally uses indels already present in the original alignments of the reads. + */ USE_READS, + /** + * Additionally uses 'Smith-Waterman' to generate alternate consenses. + */ USE_SW } - @Input(fullName="known", shortName = "known", doc="Input VCF file with known indels", required=false) + /** + * Any number of VCF files representing known indels to be used for constructing alternate consenses. + * Could be e.g. dbSNP and/or official 1000 Genomes indel calls. Non-indel variants in these files will be ignored. + */ + @Input(fullName="known", shortName = "known", doc="Input VCF file(s) with known indels", required=false) public List> known = Collections.emptyList(); + /** + * The interval list output from the RealignerTargetCreator tool using the same bam(s), reference, and known indel file(s). + */ @Input(fullName="targetIntervals", shortName="targetIntervals", doc="intervals file output from RealignerTargetCreator", required=true) protected String intervalsFile = null; + /** + * This term is equivalent to "significance" - i.e. is the improvement significant enough to merit realignment? Note that this number + * should be adjusted based on your particular data set. For low coverage and/or when looking for indels with low allele frequency, + * this number should be smaller. + */ @Argument(fullName="LODThresholdForCleaning", shortName="LOD", doc="LOD threshold above which the cleaner will clean", required=false) protected double LOD_THRESHOLD = 5.0; - @Argument(fullName="entropyThreshold", shortName="entropy", doc="percentage of mismatches at a locus to be considered having high entropy", required=false) - protected double MISMATCH_THRESHOLD = 0.15; - + /** + * The realigned bam file. + */ @Output(required=false, doc="Output bam") protected StingSAMFileWriter writer = null; protected ConstrainedMateFixingManager manager = null; protected SAMFileWriter writerToUse = null; - @Argument(fullName = "consensusDeterminationModel", shortName = "model", doc = "How should we determine the possible alternate consenses? -- in the order of least permissive to most permissive there is KNOWNS_ONLY (use only indels from known indels provided in RODs), USE_READS (additionally use indels already present in the original alignments of the reads), and USE_SW (additionally use 'Smith-Waterman' to generate alternate consenses). The default is USE_READS", required = false) + /** + * We recommend that users run with USE_READS when trying to realign high quality longer read data mapped with a gapped aligner; + * Smith-Waterman is really only necessary when using an ungapped aligner (e.g. MAQ in the case of single-end read data). + */ + @Argument(fullName = "consensusDeterminationModel", shortName = "model", doc = "Determines how to compute the possible alternate consenses", required = false) public ConsensusDeterminationModel consensusModel = ConsensusDeterminationModel.USE_READS; // ADVANCED OPTIONS FOLLOW - @Argument(fullName="maxReadsInMemory", shortName="maxInMemory", doc="max reads allowed to be kept in memory at a time by the SAMFileWriter. "+ - "Keep it low to minimize memory consumption (but the tool may skip realignment on regions with too much coverage. If it is too low, it may generate errors during realignment); keep it high to maximize realignment (but make sure to give Java enough memory).", required=false) + /** + * For expert users only! This is similar to the argument in the RealignerTargetCreator walker. The point here is that the realigner + * will only proceed with the realignment (even above the given threshold) if it minimizes entropy among the reads (and doesn't simply + * push the mismatch column to another position). This parameter is just a heuristic and should be adjusted based on your particular data set. + */ + @Argument(fullName="entropyThreshold", shortName="entropy", doc="percentage of mismatches at a locus to be considered having high entropy", required=false) + protected double MISMATCH_THRESHOLD = 0.15; + + /** + * For expert users only! To minimize memory consumption you can lower this number (but then the tool may skip realignment on regions with too much coverage; + * and if the number is too low, it may generate errors during realignment). Just make sure to give Java enough memory! 4Gb should be enough with the default value. + */ + @Argument(fullName="maxReadsInMemory", shortName="maxInMemory", doc="max reads allowed to be kept in memory at a time by the SAMFileWriter", required=false) protected int MAX_RECORDS_IN_MEMORY = 150000; + /** + * For expert users only! + */ @Argument(fullName="maxIsizeForMovement", shortName="maxIsize", doc="maximum insert size of read pairs that we attempt to realign", required=false) protected int MAX_ISIZE_FOR_MOVEMENT = 3000; + /** + * For expert users only! + */ @Argument(fullName="maxPositionalMoveAllowed", shortName="maxPosMove", doc="maximum positional move in basepairs that a read can be adjusted during realignment", required=false) protected int MAX_POS_MOVE_ALLOWED = 200; + /** + * For expert users only! If you need to find the optimal solution regardless of running time, use a higher number. + */ @Argument(fullName="maxConsensuses", shortName="maxConsensuses", doc="max alternate consensuses to try (necessary to improve performance in deep coverage)", required=false) protected int MAX_CONSENSUSES = 30; + /** + * For expert users only! If you need to find the optimal solution regardless of running time, use a higher number. + */ @Argument(fullName="maxReadsForConsensuses", shortName="greedy", doc="max reads used for finding the alternate consensuses (necessary to improve performance in deep coverage)", required=false) protected int MAX_READS_FOR_CONSENSUSES = 120; - @Argument(fullName="maxReadsForRealignment", shortName="maxReads", doc="max reads allowed at an interval for realignment; "+ - "if this value is exceeded, realignment is not attempted and the reads are passed to the output file(s) as-is", required=false) + /** + * For expert users only! If this value is exceeded at a given interval, realignment is not attempted and the reads are passed to the output file(s) as-is. + * If you need to allow more reads (e.g. with very deep coverage) regardless of memory, use a higher number. + */ + @Argument(fullName="maxReadsForRealignment", shortName="maxReads", doc="max reads allowed at an interval for realignment", required=false) protected int MAX_READS = 20000; - @Argument(fullName="noPGTag", shortName="noPG", required=false, - doc="Don't output the usual PG tag in the realigned bam file header. FOR DEBUGGING PURPOSES ONLY. "+ - "This option is required in order to pass integration tests.") - protected boolean NO_PG_TAG = false; - - @Argument(fullName="noOriginalAlignmentTags", shortName="noTags", required=false, - doc="Don't output the original cigar or alignment start tags for each realigned read in the output bam.") + @Argument(fullName="noOriginalAlignmentTags", shortName="noTags", required=false, doc="Don't output the original cigar or alignment start tags for each realigned read in the output bam") protected boolean NO_ORIGINAL_ALIGNMENT_TAGS = false; - @Argument(fullName="targetIntervalsAreNotSorted", shortName="targetNotSorted", required=false, - doc="This tool assumes that the target interval list 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 Realigner to first sort it in memory.") + /** + * For expert users only! This tool assumes that the target interval list 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 Realigner to first sort it in memory. + */ + @Argument(fullName="targetIntervalsAreNotSorted", shortName="targetNotSorted", required=false, doc="The target intervals are not sorted") protected boolean TARGET_NOT_SORTED = false; - //NWay output: testing, not ready for the prime time, hence hidden: - - @Hidden - @Argument(fullName="nWayOut", shortName="nWayOut", required=false, - doc="Generate one output file for each input (-I) bam file. Reads from all input files "+ - "will be realigned together, but then each read will be saved in the output file corresponding to "+ - "the input file the read came from. There are two ways to generate output bam file names: 1) if the "+ - "value of this argument is a general string (e.g. '.cleaned.bam'), then "+ - "extensions (\".bam\" or \".sam\") will be stripped from the input file names and the provided string value "+ - "will be pasted on instead; 2) if the value ends with a '.map' (e.g. input_output.map), then " + - "the two-column tab-separated file with the specified name must exist and list unique output file name (2nd column)" + - "for each input file name (1st column).") + /** + * Reads from all input files will be realigned together, but then each read will be saved in the output file corresponding to the input file that + * the read came from. There are two ways to generate output bam file names: 1) if the value of this argument is a general string (e.g. '.cleaned.bam'), + * then extensions (".bam" or ".sam") will be stripped from the input file names and the provided string value will be pasted on instead; 2) if the + * value ends with a '.map' (e.g. input_output.map), then the two-column tab-separated file with the specified name must exist and list unique output + * file name (2nd column) for each input file name (1st column). + */ + @Argument(fullName="nWayOut", shortName="nWayOut", required=false, doc="Generate one output file for each input (-I) bam file") protected String N_WAY_OUT = null; + + + // DEBUGGING OPTIONS FOLLOW + @Hidden @Argument(fullName="check_early",shortName="check_early",required=false,doc="Do early check of reads against existing consensuses") protected boolean CHECKEARLY = false; - - // DEBUGGING OPTIONS FOLLOW + @Hidden + @Argument(fullName="noPGTag", shortName="noPG", required=false, + doc="Don't output the usual PG tag in the realigned bam file header. FOR DEBUGGING PURPOSES ONLY. This option is required in order to pass integration tests.") + protected boolean NO_PG_TAG = false; @Hidden @Output(fullName="indelsFileForDebugging", shortName="indels", required=false, doc="Output file (text) for the indels found; FOR DEBUGGING PURPOSES ONLY") From 125ad0bcfabbb7459f5ba54413681e318d93df57 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Tue, 16 Aug 2011 12:46:48 -0400 Subject: [PATCH 018/138] Added docs to RTC --- .../gatk/walkers/indels/IndelRealigner.java | 4 +- .../indels/RealignerTargetCreator.java | 70 +++++++++++++++++-- .../sting/utils/help/GATKDoclet.java | 4 +- 3 files changed, 68 insertions(+), 10 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java index 426c10604..0f1c0dd3b 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java @@ -83,7 +83,9 @@ import java.util.*; *
  • Running the realigner over those intervals (IndelRealigner)
  • * *

    - * An important note: because reads produced from the 454 technology inherently contain false indels, the realigner will not currently work with them + * An important note: the input bam(s), reference, and known indel file(s) should be the same ones used for the RealignerTargetCreator step. + * + * Another important note: because reads produced from the 454 technology inherently contain false indels, the realigner will not currently work with them * (or with reads from similar technologies). * *

    Input

    diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/RealignerTargetCreator.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/RealignerTargetCreator.java index fbb62f17e..7b654e3f8 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/RealignerTargetCreator.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/RealignerTargetCreator.java @@ -52,7 +52,51 @@ import java.util.Collections; import java.util.List; /** - * Emits intervals for the Local Indel Realigner to target for cleaning. Ignores 454 reads, MQ0 reads, and reads with consecutive indel operators in the CIGAR string. + * Emits intervals for the Local Indel Realigner to target for cleaning. + * + *

    + * The local realignment tool is designed to consume one or more BAM files and to locally realign reads such that the number of mismatching bases + * is minimized across all the reads. In general, a large percent of regions requiring local realignment are due to the presence of an insertion + * or deletion (indels) in the individualÕs genome with respect to the reference genome. Such alignment artifacts result in many bases mismatching + * the reference near the misalignment, which are easily mistaken as SNPs. Moreover, since read mapping algorithms operate on each read independently, + * it is impossible to place reads on the reference genome such at mismatches are minimized across all reads. Consequently, even when some reads are + * correctly mapped with indels, reads covering the indel near just the start or end of the read are often incorrectly mapped with respect the true indel, + * also requiring realignment. Local realignment serves to transform regions with misalignments due to indels into clean reads containing a consensus + * indel suitable for standard variant discovery approaches. Unlike most mappers, this walker uses the full alignment context to determine whether an + * appropriate alternate reference (i.e. indel) exists. Following local realignment, the GATK tool Unified Genotyper can be used to sensitively and + * specifically identify indels. + *

    + *

      There are 2 steps to the realignment process: + *
    1. Determining (small) suspicious intervals which are likely in need of realignment (RealignerTargetCreator)
    2. + *
    3. Running the realigner over those intervals (see the IndelRealigner tool)
    4. + *
    + *

    + * An important note: the input bam(s), reference, and known indel file(s) should be the same ones to be used for the IndelRealigner step. + * + * Another important note: because reads produced from the 454 technology inherently contain false indels, the realigner will not currently work with them + * (or with reads from similar technologies). This tool also ignores MQ0 reads and reads with consecutive indel operators in the CIGAR string. + * + *

    Input

    + *

    + * One or more aligned BAM files and optionally one or more lists of known indels. + *

    + * + *

    Output

    + *

    + * A list of target intervals to pass to the Indel Realigner. + *

    + * + *

    Examples

    + *
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -I  \
    + *   -R  \
    + *   -T RealignerTargetCreator \
    + *   -o  \
    + *   [--known /path/to/indels.vcf]
    + * 
    + * + * @author ebanks */ @ReadFilters({Platform454Filter.class, MappingQualityZeroReadFilter.class, BadCigarFilter.class}) @Reference(window=@Window(start=-1,stop=50)) @@ -61,29 +105,41 @@ import java.util.List; @BAQMode(ApplicationTime = BAQ.ApplicationTime.FORBIDDEN) public class RealignerTargetCreator extends RodWalker { + /** + * The target intervals for realignment. + */ @Output protected PrintStream out; + /** + * Any number of VCF files representing known SNPs and/or indels. Could be e.g. dbSNP and/or official 1000 Genomes indel calls. + * SNPs in these files will be ignored unless the --mismatchFraction argument is used. + */ @Input(fullName="known", shortName = "known", doc="Input VCF file with known indels", required=false) public List> known = Collections.emptyList(); - // mismatch/entropy/SNP arguments + /** + * Any two SNP calls and/or high entropy positions are considered clustered when they occur no more than this many basepairs apart. + */ @Argument(fullName="windowSize", shortName="window", doc="window size for calculating entropy or SNP clusters", required=false) protected int windowSize = 10; - @Argument(fullName="mismatchFraction", shortName="mismatch", doc="fraction of base qualities needing to mismatch for a position to have high entropy; to disable set to <= 0 or > 1", required=false) + /** + * To disable this behavior, set this value to <= 0 or > 1. This feature is really only necessary when using an ungapped aligner + * (e.g. MAQ in the case of single-end read data) and should be used in conjunction with '--model USE_SW' in the IndelRealigner. + */ + @Argument(fullName="mismatchFraction", shortName="mismatch", doc="fraction of base qualities needing to mismatch for a position to have high entropy", required=false) protected double mismatchThreshold = 0.0; @Argument(fullName="minReadsAtLocus", shortName="minReads", doc="minimum reads at a locus to enable using the entropy calculation", required=false) protected int minReadsAtLocus = 4; - // interval merging arguments + /** + * Because the realignment algorithm is N^2, allowing too large an interval might take too long to completely realign. + */ @Argument(fullName="maxIntervalSize", shortName="maxInterval", doc="maximum interval size", required=false) protected int maxIntervalSize = 500; - @Deprecated - @Argument(fullName="realignReadsWithBadMates", doc="This argument is no longer used.", required=false) - protected boolean DEPRECATED_REALIGN_MATES = false; @Override public boolean generateExtendedEvents() { return true; } diff --git a/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java b/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java index d071be105..8f3ec293a 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java @@ -96,8 +96,8 @@ public class GATKDoclet { //logger.debug("Considering " + doc); Class clazz = getClassForClassDoc(doc); - if ( clazz != null && clazz.getName().equals("org.broadinstitute.sting.gatk.walkers.annotator.AlleleBalance")) - logger.debug("foo"); + //if ( clazz != null && clazz.getName().equals("org.broadinstitute.sting.gatk.walkers.annotator.AlleleBalance")) + // logger.debug("foo"); DocumentedGATKFeature feature = getFeatureForClassDoc(doc); DocumentedGATKFeatureHandler handler = createHandler(doc, feature); From ab0b56ed11611c1c5dc9292f2184170573bf1f44 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Tue, 16 Aug 2011 12:55:45 -0400 Subject: [PATCH 019/138] Minor doc fixes --- .../org/broadinstitute/sting/commandline/Output.java | 2 +- .../sting/gatk/walkers/indels/IndelRealigner.java | 10 +++++----- .../gatk/walkers/indels/RealignerTargetCreator.java | 10 +++++----- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/commandline/Output.java b/public/java/src/org/broadinstitute/sting/commandline/Output.java index 22565dbf5..f8aef0355 100644 --- a/public/java/src/org/broadinstitute/sting/commandline/Output.java +++ b/public/java/src/org/broadinstitute/sting/commandline/Output.java @@ -55,7 +55,7 @@ public @interface Output { * --help argument is specified. * @return Doc string associated with this command-line argument. */ - String doc() default "An output file presented to the walker. Will overwrite contents if file exists."; + String doc() default "An output file created by the walker. Will overwrite contents if file exists"; /** * Is this argument required. If true, the command-line argument system will diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java index 0f1c0dd3b..029b6deaf 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java @@ -84,7 +84,7 @@ import java.util.*; * *

    * An important note: the input bam(s), reference, and known indel file(s) should be the same ones used for the RealignerTargetCreator step. - * + *

    * Another important note: because reads produced from the 454 technology inherently contain false indels, the realigner will not currently work with them * (or with reads from similar technologies). * @@ -101,11 +101,11 @@ import java.util.*; *

    Examples

    *
      * java -Xmx4g -jar GenomeAnalysisTK.jar \
    - *   -I  \
    - *   -R  \
    + *   -I input.bam \
    + *   -R ref.fasta \
      *   -T IndelRealigner \
    - *   -targetIntervals  \
    - *   -o  \
    + *   -targetIntervals intervalListFromRTC.intervals \
    + *   -o realignedBam.bam \
      *   [--known /path/to/indels.vcf] \
      *   [-compress 0]    (this argument recommended to speed up the process *if* this is only a temporary file; otherwise, use the default value)
      * 
    diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/RealignerTargetCreator.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/RealignerTargetCreator.java index 7b654e3f8..08ed1af52 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/RealignerTargetCreator.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/RealignerTargetCreator.java @@ -52,7 +52,7 @@ import java.util.Collections; import java.util.List; /** - * Emits intervals for the Local Indel Realigner to target for cleaning. + * Emits intervals for the Local Indel Realigner to target for realignment. * *

    * The local realignment tool is designed to consume one or more BAM files and to locally realign reads such that the number of mismatching bases @@ -72,7 +72,7 @@ import java.util.List; * *

    * An important note: the input bam(s), reference, and known indel file(s) should be the same ones to be used for the IndelRealigner step. - * + *

    * Another important note: because reads produced from the 454 technology inherently contain false indels, the realigner will not currently work with them * (or with reads from similar technologies). This tool also ignores MQ0 reads and reads with consecutive indel operators in the CIGAR string. * @@ -89,10 +89,10 @@ import java.util.List; *

    Examples

    *
      * java -Xmx2g -jar GenomeAnalysisTK.jar \
    - *   -I  \
    - *   -R  \
    + *   -I input.bam \
    + *   -R ref.fasta \
      *   -T RealignerTargetCreator \
    - *   -o  \
    + *   -o forIndelRealigner.intervals \
      *   [--known /path/to/indels.vcf]
      * 
    * From c71a4e1832edf1283ac7484742b12644f07745ae Mon Sep 17 00:00:00 2001 From: Andrey Sivachenko Date: Tue, 16 Aug 2011 13:40:35 -0400 Subject: [PATCH 021/138] this is a bug fix; reverting in unstable and pushing from stable instead --- .../broadinstitute/sting/utils/sam/NWaySAMFileWriter.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/sam/NWaySAMFileWriter.java b/public/java/src/org/broadinstitute/sting/utils/sam/NWaySAMFileWriter.java index 70417889b..3718345a4 100644 --- a/public/java/src/org/broadinstitute/sting/utils/sam/NWaySAMFileWriter.java +++ b/public/java/src/org/broadinstitute/sting/utils/sam/NWaySAMFileWriter.java @@ -135,11 +135,6 @@ public class NWaySAMFileWriter implements SAMFileWriter { public void addAlignment(SAMRecord samRecord) { final SAMReaderID id = toolkit.getReaderIDForRead(samRecord); - String rg = samRecord.getStringAttribute("RG"); - if ( rg != null ) { - String rg_orig = toolkit.getReadsDataSource().getOriginalReadGroupId(rg); - samRecord.setAttribute("RG",rg_orig); - } writerMap.get(id).addAlignment(samRecord); } From 9f3328db5365f676604a02b1f9b60105602bd9dd Mon Sep 17 00:00:00 2001 From: Andrey Sivachenko Date: Tue, 16 Aug 2011 13:45:40 -0400 Subject: [PATCH 022/138] fixing read group name collision: before writing the read into respective stream in nway-out mode we now retrieve the original rg, not the merged/modified one --- .../broadinstitute/sting/utils/sam/NWaySAMFileWriter.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/public/java/src/org/broadinstitute/sting/utils/sam/NWaySAMFileWriter.java b/public/java/src/org/broadinstitute/sting/utils/sam/NWaySAMFileWriter.java index 3718345a4..70417889b 100644 --- a/public/java/src/org/broadinstitute/sting/utils/sam/NWaySAMFileWriter.java +++ b/public/java/src/org/broadinstitute/sting/utils/sam/NWaySAMFileWriter.java @@ -135,6 +135,11 @@ public class NWaySAMFileWriter implements SAMFileWriter { public void addAlignment(SAMRecord samRecord) { final SAMReaderID id = toolkit.getReaderIDForRead(samRecord); + String rg = samRecord.getStringAttribute("RG"); + if ( rg != null ) { + String rg_orig = toolkit.getReadsDataSource().getOriginalReadGroupId(rg); + samRecord.setAttribute("RG",rg_orig); + } writerMap.get(id).addAlignment(samRecord); } From 170d1ff7b677fddd2002a86f0bbca29da4b7f076 Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Tue, 16 Aug 2011 14:17:46 -0400 Subject: [PATCH 023/138] Fix in UG for trying to call indels at IUPAC code bases when in EMIT_ALL_SITES mode --- .../gatk/walkers/genotyper/UnifiedGenotyperEngine.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 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 a10897172..1b72aae25 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 @@ -485,6 +485,9 @@ public class UnifiedGenotyperEngine { Map stratifiedContexts = null; + if ( !BaseUtils.isRegularBase( refContext.getBase() ) ) + return null; + if ( model == GenotypeLikelihoodsCalculationModel.Model.INDEL ) { if (UAC.GenotypingMode == GenotypeLikelihoodsCalculationModel.GENOTYPING_MODE.GENOTYPE_GIVEN_ALLELES) { @@ -499,6 +502,7 @@ public class UnifiedGenotyperEngine { stratifiedContexts = AlignmentContextUtils.splitContextBySampleName(pileup, UAC.ASSUME_SINGLE_SAMPLE); } else { + // todo - tmp will get rid of extended events so this wont be needed if (!rawContext.hasExtendedEventPileup()) return null; @@ -516,9 +520,6 @@ public class UnifiedGenotyperEngine { } } else if ( model == GenotypeLikelihoodsCalculationModel.Model.SNP ) { - if ( !BaseUtils.isRegularBase( refContext.getBase() ) ) - return null; - // stratify the AlignmentContext and cut by sample stratifiedContexts = AlignmentContextUtils.splitContextBySampleName(rawContext.getBasePileup(), UAC.ASSUME_SINGLE_SAMPLE); From 6e828260a019b18134ae27950f7a1faa08319af0 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Tue, 16 Aug 2011 16:13:47 -0400 Subject: [PATCH 030/138] Removed -B support. Now explodes with error if -B provided. --- .../sting/gatk/CommandLineExecutable.java | 9 ++++----- .../sting/gatk/arguments/GATKArgumentCollection.java | 10 ++-------- .../gatk/arguments/GATKArgumentCollectionUnitTest.java | 8 -------- 3 files changed, 6 insertions(+), 21 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/CommandLineExecutable.java b/public/java/src/org/broadinstitute/sting/gatk/CommandLineExecutable.java index 32132c7ca..32002e093 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/CommandLineExecutable.java +++ b/public/java/src/org/broadinstitute/sting/gatk/CommandLineExecutable.java @@ -96,24 +96,23 @@ public abstract class CommandLineExecutable extends CommandLineProgram { loadArgumentsIntoObject(walker); argumentSources.add(walker); - Collection newStyle = ListFileUtils.unpackRODBindings(parser.getRodBindings(), parser); + Collection rodBindings = ListFileUtils.unpackRODBindings(parser.getRodBindings(), parser); // todo: remove me when the old style system is removed if ( getArgumentCollection().RODBindings.size() > 0 ) { logger.warn("################################################################################"); logger.warn("################################################################################"); - logger.warn("Deprecated -B rod binding syntax detected. This syntax will be retired in GATK 1.2."); + logger.warn("Deprecated -B rod binding syntax detected. This syntax has been eliminated in GATK 1.2."); logger.warn("Please use arguments defined by each specific walker instead."); for ( String oldStyleRodBinding : getArgumentCollection().RODBindings ) { logger.warn(" -B rod binding with value " + oldStyleRodBinding + " tags: " + parser.getTags(oldStyleRodBinding).getPositionalTags()); } logger.warn("################################################################################"); logger.warn("################################################################################"); + System.exit(1); } - Collection oldStyle = ListFileUtils.unpackRODBindingsOldStyle(getArgumentCollection().RODBindings, parser); - oldStyle.addAll(newStyle); - engine.setReferenceMetaDataFiles(oldStyle); + engine.setReferenceMetaDataFiles(rodBindings); for (ReadFilter filter: filters) { loadArgumentsIntoObject(filter); diff --git a/public/java/src/org/broadinstitute/sting/gatk/arguments/GATKArgumentCollection.java b/public/java/src/org/broadinstitute/sting/gatk/arguments/GATKArgumentCollection.java index 62135f21b..fd39d46b0 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/arguments/GATKArgumentCollection.java +++ b/public/java/src/org/broadinstitute/sting/gatk/arguments/GATKArgumentCollection.java @@ -101,6 +101,8 @@ public class GATKArgumentCollection { @Input(fullName = "reference_sequence", shortName = "R", doc = "Reference sequence file", required = false) public File referenceFile = null; + @Deprecated + @Hidden @ElementList(required = false) @Input(fullName = "rodBind", shortName = "B", doc = "Bindings for reference-ordered data, in the form :, ", required = false) public ArrayList RODBindings = new ArrayList(); @@ -340,14 +342,6 @@ public class GATKArgumentCollection { return false; } } - if (other.RODBindings.size() != RODBindings.size()) { - return false; - } - for (int x = 0; x < RODBindings.size(); x++) { - if (!RODBindings.get(x).equals(other.RODBindings.get(x))) { - return false; - } - } if (!other.samFiles.equals(this.samFiles)) { return false; } diff --git a/public/java/test/org/broadinstitute/sting/gatk/arguments/GATKArgumentCollectionUnitTest.java b/public/java/test/org/broadinstitute/sting/gatk/arguments/GATKArgumentCollectionUnitTest.java index f3e868474..3a242cb13 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/arguments/GATKArgumentCollectionUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/arguments/GATKArgumentCollectionUnitTest.java @@ -88,14 +88,6 @@ public class GATKArgumentCollectionUnitTest extends BaseTest { collect.intervals.add("intervals".toLowerCase()); collect.excludeIntervals = new ArrayList(); collect.numberOfThreads = 1; - - // make some rod bindings up - ArrayList fakeBindings = new ArrayList(); - fakeBindings.add("Bind1"); - fakeBindings.add("Bind2"); - fakeBindings.add("Bind3"); - - collect.RODBindings = fakeBindings; } From 946f5c53fe1bc3f648d4571bf283d12f8e0195e9 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Tue, 16 Aug 2011 16:26:26 -0400 Subject: [PATCH 031/138] Adding docs to more walkers --- .../arguments/DbsnpArgumentCollection.java | 3 +- .../walkers/annotator/VariantAnnotator.java | 55 ++++++++++---- .../filters/VariantFiltrationWalker.java | 74 +++++++++++++++++-- .../varianteval/VariantEvalWalker.java | 55 +++++++++++++- 4 files changed, 161 insertions(+), 26 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/arguments/DbsnpArgumentCollection.java b/public/java/src/org/broadinstitute/sting/gatk/arguments/DbsnpArgumentCollection.java index ce638ff2b..2f4dd06e2 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/arguments/DbsnpArgumentCollection.java +++ b/public/java/src/org/broadinstitute/sting/gatk/arguments/DbsnpArgumentCollection.java @@ -39,8 +39,7 @@ import org.simpleframework.xml.*; public class DbsnpArgumentCollection { /** - * A dbSNP VCF file. Variants in this track will be treated as "known" variants - * in tools using this track. + * A dbSNP VCF file. */ @Input(fullName="dbsnp", shortName = "D", doc="dbSNP file", required=false) public RodBinding dbsnp; 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 8c8bd19d0..96a400c68 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 @@ -49,7 +49,34 @@ import java.util.*; /** - * Annotates variant calls with context information. Users can specify which of the available annotations to use. + * Annotates variant calls with context information. + * + *

    + * VariantAnnotator is a GATK tool for annotating variant calls based on their context. + * The tool is modular; new annotations can be written easily without modifying VariantAnnotator itself. + * + *

    Input

    + *

    + * A variant set to annotate and optionally one or more BAM files. + *

    + * + *

    Output

    + *

    + * An annotated VCF. + *

    + * + *

    Examples

    + *
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T VariantAnnotator \
    + *   -I input.bam \
    + *   -o output.vcf \
    + *   -A DepthOfCoverage
    + *   --variant input.vcf \
    + *   --dbsnp dbsnp.vcf
    + * 
    + * */ @Requires(value={}) @Allows(value={DataSource.READS, DataSource.REFERENCE}) @@ -69,8 +96,6 @@ public class VariantAnnotator extends RodWalker implements Ann public RodBinding getSnpEffRodBinding() { return snpEffFile; } /** - * A dbSNP VCF file from which to annotate. - * * rsIDs from this file are used to populate the ID column of the output. Also, the DB INFO flag will be set when appropriate. */ @ArgumentCollection @@ -101,15 +126,25 @@ public class VariantAnnotator extends RodWalker implements Ann @Output(doc="File to which variants should be written",required=true) protected VCFWriter vcfWriter = null; - @Argument(fullName="sampleName", shortName="sample", doc="The sample (NA-ID) corresponding to the variant input (for non-VCF input only)", required=false) - protected String sampleName = null; - + /** + * See the -list argument to view available annotations. + */ @Argument(fullName="annotation", shortName="A", doc="One or more specific annotations to apply to variant calls", required=false) protected List annotationsToUse = new ArrayList(); + /** + * See the -list argument to view available groups. + */ @Argument(fullName="group", shortName="G", doc="One or more classes/groups of annotations to apply to variant calls", required=false) protected List annotationGroupsToUse = new ArrayList(); + /** + * This option enables you to add annotations from one VCF to another. + * + * For example, if you want to annotate your 'variant' VCF with the AC field value from the rod bound to 'resource', + * you can specify '-E resource.AC' and records in the output VCF will be annotated with 'resource.AC=N' when a record exists in that rod at the given position. + * If multiple records in the rod overlap the given position, one is chosen arbitrarily. + */ @Argument(fullName="expression", shortName="E", doc="One or more specific expressions to apply to variant calls; see documentation for more details", required=false) protected List expressionsToUse = new ArrayList(); @@ -127,8 +162,6 @@ 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; - private HashMap nonVCFsampleName = new HashMap(); - private VariantAnnotatorEngine engine; private Collection indelBufferContext; @@ -164,12 +197,6 @@ public class VariantAnnotator extends RodWalker implements Ann List rodName = Arrays.asList(variantCollection.variants.getName()); Set samples = SampleUtils.getUniqueSamplesFromRods(getToolkit(), rodName); - // add the non-VCF sample from the command-line, if applicable - if ( sampleName != null ) { - nonVCFsampleName.put(sampleName.toUpperCase(), "variant"); - samples.add(sampleName.toUpperCase()); - } - // if there are no valid samples, warn the user if ( samples.size() == 0 ) { logger.warn("There are no samples input at all; use the --sampleName argument to specify one if desired."); diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/filters/VariantFiltrationWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/filters/VariantFiltrationWalker.java index c555e88cd..bf3606b54 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/filters/VariantFiltrationWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/filters/VariantFiltrationWalker.java @@ -45,6 +45,34 @@ import java.util.*; /** * Filters variant calls using a number of user-selectable, parameterizable criteria. + * + *

    + * VariantFiltration is a GATK tool for hard-filtering variant calls based on certain criteria. + * Records are hard-filtered by changing the value in the FILTER field to something other than PASS. + * + *

    Input

    + *

    + * A variant set to filter. + *

    + * + *

    Output

    + *

    + * A filtered VCF. + *

    + * + *

    Examples

    + *
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T VariantFiltration \
    + *   -o output.vcf \
    + *   --variant input.vcf \
    + *   --filterExpression "AB < 0.2 || MQ0 > 50" \
    + *   --filterName "Nov09filters" \
    + *   --mask mask.vcf \
    + *   --maskName InDel
    + * 
    + * */ @Reference(window=@Window(start=-50,stop=50)) public class VariantFiltrationWalker extends RodWalker { @@ -52,33 +80,65 @@ public class VariantFiltrationWalker extends RodWalker { @ArgumentCollection protected StandardVariantContextInputArgumentCollection variantCollection = new StandardVariantContextInputArgumentCollection(); + /** + * Any variant which overlaps entries from the provided mask rod will be filtered. + */ @Input(fullName="mask", doc="Input ROD mask", required=false) public RodBinding mask; @Output(doc="File to which variants should be written", required=true) protected VCFWriter writer = null; - @Argument(fullName="filterExpression", shortName="filter", doc="One or more expression used with INFO fields to filter (see wiki docs for more info)", required=false) + /** + * VariantFiltration accepts any number of JEXL expressions (so you can have two named filters by using + * --filterName One --filterExpression "X < 1" --filterName Two --filterExpression "X > 2"). + */ + @Argument(fullName="filterExpression", shortName="filter", doc="One or more expression used with INFO fields to filter", required=false) protected ArrayList FILTER_EXPS = new ArrayList(); - @Argument(fullName="filterName", shortName="filterName", doc="Names to use for the list of filters (must be a 1-to-1 mapping); this name is put in the FILTER field for variants that get filtered", required=false) + + /** + * This name is put in the FILTER field for variants that get filtered. Note that there must be a 1-to-1 mapping between filter expressions and filter names. + */ + @Argument(fullName="filterName", shortName="filterName", doc="Names to use for the list of filters", required=false) protected ArrayList FILTER_NAMES = new ArrayList(); + /** + * Similar to the INFO field based expressions, but used on the FORMAT (genotype) fields instead. + * VariantFiltration will add the sample-level FT tag to the FORMAT field of filtered samples (this does not affect the record's FILTER tag). + * One can filter normally based on most fields (e.g. "GQ < 5.0"), but the GT (genotype) field is an exception. We have put in convenience + * methods so that one can now filter out hets ("isHet == 1"), refs ("isHomRef == 1"), or homs ("isHomVar == 1"). + */ @Argument(fullName="genotypeFilterExpression", shortName="G_filter", doc="One or more expression used with FORMAT (sample/genotype-level) fields to filter (see wiki docs for more info)", required=false) protected ArrayList GENOTYPE_FILTER_EXPS = new ArrayList(); + + /** + * Similar to the INFO field based expressions, but used on the FORMAT (genotype) fields instead. + */ @Argument(fullName="genotypeFilterName", shortName="G_filterName", doc="Names to use for the list of sample/genotype filters (must be a 1-to-1 mapping); this name is put in the FILTER field for variants that get filtered", required=false) protected ArrayList GENOTYPE_FILTER_NAMES = new ArrayList(); - @Argument(fullName="clusterSize", shortName="cluster", doc="The number of SNPs which make up a cluster (see also --clusterWindowSize); [default:3]", required=false) + /** + * Works together with the --clusterWindowSize argument. + */ + @Argument(fullName="clusterSize", shortName="cluster", doc="The number of SNPs which make up a cluster", required=false) protected Integer clusterSize = 3; - @Argument(fullName="clusterWindowSize", shortName="window", doc="The window size (in bases) in which to evaluate clustered SNPs (to disable the clustered SNP filter, set this value to less than 1); [default:0]", required=false) + + /** + * Works together with the --clusterSize argument. To disable the clustered SNP filter, set this value to less than 1. + */ + @Argument(fullName="clusterWindowSize", shortName="window", doc="The window size (in bases) in which to evaluate clustered SNPs", required=false) protected Integer clusterWindow = 0; - @Argument(fullName="maskExtension", shortName="maskExtend", doc="How many bases beyond records from a provided 'mask' rod should variants be filtered; [default:0]", required=false) + @Argument(fullName="maskExtension", shortName="maskExtend", doc="How many bases beyond records from a provided 'mask' rod should variants be filtered", required=false) protected Integer MASK_EXTEND = 0; - @Argument(fullName="maskName", shortName="maskName", doc="The text to put in the FILTER field if a 'mask' rod is provided and overlaps with a variant call; [default:'Mask']", required=false) + @Argument(fullName="maskName", shortName="maskName", doc="The text to put in the FILTER field if a 'mask' rod is provided and overlaps with a variant call", required=false) protected String MASK_NAME = "Mask"; - @Argument(fullName="missingValuesInExpressionsShouldEvaluateAsFailing", doc="When evaluating the JEXL expressions, should missing values be considered failing the expression (by default they are considered passing)?", required=false) + /** + * By default, if JEXL cannot evaluate your expression for a particular record because one of the annotations is not present, the whole expression evaluates as PASSing. + * Use this argument to have it evaluate as failing filters instead for these cases. + */ + @Argument(fullName="missingValuesInExpressionsShouldEvaluateAsFailing", doc="When evaluating the JEXL expressions, missing values should be considered failing the expression", required=false) protected Boolean FAIL_MISSING_VALUES = false; // JEXL expressions for the filters 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 633c21320..d1fa3f4df 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 @@ -36,20 +36,61 @@ import java.util.*; /** * General-purpose tool for variant evaluation (% in dbSNP, genotype concordance, Ti/Tv ratios, and a lot more) + * + *

    + * Given a variant callset, it is common to calculate various quality control metrics. These metrics include the number of + * raw or filtered SNP counts; ratio of transition mutations to transversions; concordance of a particular sample's calls + * to a genotyping chip; number of singletons per sample; etc. Furthermore, it is often useful to stratify these metrics + * by various criteria like functional class (missense, nonsense, silent), whether the site is CpG site, the amino acid + * degeneracy of the site, etc. VariantEval facilitates these calculations in two ways: by providing several built-in + * evaluation and stratification modules, and by providing a framework that permits the easy development of new evaluation + * and stratification modules. + * + *

    Input

    + *

    + * One or more variant sets to evaluate plus any number of comparison sets. + *

    + * + *

    Output

    + *

    + * Evaluation tables. + *

    + * + *

    Examples

    + *
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T VariantEval \
    + *   -o output.eval.gatkreport \
    + *   --eval:set1 set1.vcf \
    + *   --eval:set2 set2.vcf \
    + *   [--comp comp.vcf]
    + * 
    + * */ @Reference(window=@Window(start=-50, stop=50)) public class VariantEvalWalker extends RodWalker implements TreeReducible { - // Output arguments + @Output protected PrintStream out; + /** + * The variant file(s) to evaluate. + */ @Input(fullName="eval", shortName = "eval", doc="Input evaluation file(s)", required=true) public List> evals; + /** + * The variant file(s) to compare against. + */ @Input(fullName="comp", shortName = "comp", doc="Input comparison file(s)", required=false) public List> compsProvided = Collections.emptyList(); private List> comps = new ArrayList>(); + /** + * dbSNP comparison VCF. By default, the dbSNP file is used to specify the set of "known" variants. + * Other sets can be specified with the -knownName (--known_names) argument. + */ @ArgumentCollection protected DbsnpArgumentCollection dbsnp = new DbsnpArgumentCollection(); @@ -67,6 +108,9 @@ public class VariantEvalWalker extends RodWalker implements Tr @Argument(fullName="sample", shortName="sn", doc="Derive eval and comp contexts using only these sample genotypes, when genotypes are available in the original context", required=false) protected Set SAMPLE_EXPRESSIONS; + /** + * List of rod tracks to be used for specifying "known" variants other than dbSNP. + */ @Argument(shortName="knownName", doc="Name of ROD bindings containing variant sites that should be treated as known when splitting eval rods into known and novel subsets", required=false) protected HashSet KNOWN_NAMES = new HashSet(); List> knowns = new ArrayList>(); @@ -81,7 +125,9 @@ public class VariantEvalWalker extends RodWalker implements Tr @Argument(fullName="onlyVariantsOfType", shortName="VT", doc="If provided, only variants of these types will be considered during the evaluation, in ", required=false) protected Set typesToUse = null; - // Evaluator arguments + /** + * See the -list argument to view available modules. + */ @Argument(fullName="evalModule", shortName="EV", doc="One or more specific eval modules to apply to the eval track(s) (in addition to the standard modules, unless -noE is specified)", required=false) protected String[] MODULES_TO_USE = {}; @@ -95,7 +141,10 @@ public class VariantEvalWalker extends RodWalker implements Tr @Argument(fullName="minPhaseQuality", shortName="mpq", doc="Minimum phasing quality", required=false) protected double MIN_PHASE_QUALITY = 10.0; - @Argument(shortName="family", doc="If provided, genotypes in will be examined for mendelian violations: this argument is a string formatted as dad+mom=child where these parameters determine which sample names are examined", required=false) + /** + * This argument is a string formatted as dad+mom=child where these parameters determine which sample names are examined. + */ + @Argument(shortName="family", doc="If provided, genotypes in will be examined for mendelian violations", required=false) protected String FAMILY_STRUCTURE; @Argument(shortName="mvq", fullName="mendelianViolationQualThreshold", doc="Minimum genotype QUAL score for each trio member required to accept a site as a violation", required=false) From fadcbf68fd72d936f7b4532b57bf37ae98ea10d1 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Wed, 17 Aug 2011 09:39:33 -0400 Subject: [PATCH 032/138] Adding docs to QC walkers --- .../sting/gatk/walkers/PrintReadsWalker.java | 51 ++++++++++++++++--- .../coverage/GCContentByIntervalWalker.java | 24 ++++++++- .../gatk/walkers/qc/CountLociWalker.java | 24 +++++++++ .../gatk/walkers/qc/CountPairsWalker.java | 20 ++++++++ .../gatk/walkers/qc/CountReadsWalker.java | 24 +++++++++ .../sting/gatk/walkers/qc/CountRodWalker.java | 42 ++++++++++++--- 6 files changed, 169 insertions(+), 16 deletions(-) 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 7e1dcd707..fdfac6bf7 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/PrintReadsWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/PrintReadsWalker.java @@ -40,26 +40,65 @@ import java.util.TreeSet; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.refdata.ReadMetaDataTracker; + /** - * Renders, in SAM/BAM format, all reads from the input data set in the order in which they appear - * in the input file. It can dynamically merge the contents of multiple input BAM files, resulting - * in merged output sorted in coordinate order. Can also optionally filter reads based on the --read-filter - * command line argument. + * Renders, in SAM/BAM format, all reads from the input data set in the order in which they appear in the input file. + * + *

    + * PrintReads can dynamically merge the contents of multiple input BAM files, resulting + * in merged output sorted in coordinate order. Can also optionally filter reads based on the + * --read_filter command line argument. + * + *

    Input

    + *

    + * One or more bam files. + *

    + * + *

    Output

    + *

    + * A single processed bam file. + *

    + * + *

    Examples

    + *
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T PrintReads \
    + *   -o output.bam \
    + *   -I input1.bam \
    + *   -I input2.bam \
    + *   --read_filter MappingQualityZero
    + * 
    + * */ @BAQMode(QualityMode = BAQ.QualityMode.ADD_TAG, ApplicationTime = BAQ.ApplicationTime.ON_OUTPUT) @Requires({DataSource.READS, DataSource.REFERENCE}) public class PrintReadsWalker extends ReadWalker { - /** an optional argument to dump the reads out to a BAM file */ + @Output(doc="Write output to this BAM filename instead of STDOUT") SAMFileWriter out; + @Argument(fullName = "readGroup", shortName = "readGroup", doc="Exclude all reads with this read group from the output", required = false) String readGroup = null; + + /** + * For example, --platform ILLUMINA or --platform 454. + */ @Argument(fullName = "platform", shortName = "platform", doc="Exclude all reads with this platform from the output", required = false) - String platform = null; // E.g. ILLUMINA, 454 + String platform = null; + @Argument(fullName = "number", shortName = "n", doc="Print the first n reads from the file, discarding the rest", required = false) int nReadsToPrint = -1; + + /** + * Only reads from samples listed in the provided file(s) will be included in the output. + */ @Argument(fullName="sample_file", shortName="sf", doc="File containing a list of samples (one per line). Can be specified multiple times", required=false) public Set sampleFile = new TreeSet(); + + /** + * Only reads from the sample(s) will be included in the output. + */ @Argument(fullName="sample_name", shortName="sn", doc="Sample name to be included in the analysis. Can be specified multiple times.", required=false) public Set sampleNames = new TreeSet(); diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/GCContentByIntervalWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/GCContentByIntervalWalker.java index a4944e939..5c2a967b9 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/GCContentByIntervalWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/GCContentByIntervalWalker.java @@ -38,12 +38,32 @@ import java.util.List; /** * Walks along reference and calculates the GC content for each interval. + * + * + *

    Input

    + *

    + * One or more BAM files. + *

    + * + *

    Output

    + *

    + * GC content calculations per interval. + *

    + * + *

    Examples

    + *
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T GCContentByInterval \
    + *   -o output.txt \
    + *   -I input.bam \
    + *   -L input.intervals
    + * 
    + * */ @Allows(value = {DataSource.REFERENCE}) @Requires(value = {DataSource.REFERENCE}) - @By(DataSource.REFERENCE) - public class GCContentByIntervalWalker extends LocusWalker { @Output protected PrintStream out; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountLociWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountLociWalker.java index 0d68c8493..09113704a 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountLociWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountLociWalker.java @@ -11,7 +11,31 @@ import java.io.PrintStream; /** * Walks over the input data set, calculating the total number of covered loci for diagnostic purposes. + * + *

    * Simplest example of a locus walker. + * + * + *

    Input

    + *

    + * One or more BAM files. + *

    + * + *

    Output

    + *

    + * Number of loci traversed. + *

    + * + *

    Examples

    + *
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T CountLoci \
    + *   -o output.txt \
    + *   -I input.bam \
    + *   [-L input.intervals]
    + * 
    + * */ public class CountLociWalker extends LocusWalker implements TreeReducible { @Output(doc="Write count to this file instead of STDOUT") diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountPairsWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountPairsWalker.java index 26fa9a258..e770418c1 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountPairsWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountPairsWalker.java @@ -39,6 +39,26 @@ import java.util.List; * query name order. Breaks counts down by total pairs and number * of paired reads. * + * + *

    Input

    + *

    + * One or more bam files. + *

    + * + *

    Output

    + *

    + * Number of pairs seen. + *

    + * + *

    Examples

    + *
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T CountPairs \
    + *   -o output.txt \
    + *   -I input.bam
    + * 
    + * * @author mhanna */ public class CountPairsWalker extends ReadPairWalker { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountReadsWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountReadsWalker.java index 87c0409b9..9ce9c4eec 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountReadsWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountReadsWalker.java @@ -9,8 +9,32 @@ import org.broadinstitute.sting.gatk.walkers.Requires; /** * Walks over the input data set, calculating the number of reads seen for diagnostic purposes. + * + *

    * Can also count the number of reads matching a given criterion using read filters (see the * --read-filter command line argument). Simplest example of a read-backed analysis. + * + * + *

    Input

    + *

    + * One or more BAM files. + *

    + * + *

    Output

    + *

    + * Number of reads seen. + *

    + * + *

    Examples

    + *
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T CountReads \
    + *   -o output.txt \
    + *   -I input.bam \
    + *   [-L input.intervals]
    + * 
    + * */ @Requires({DataSource.READS, DataSource.REFERENCE}) public class CountReadsWalker extends ReadWalker { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountRodWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountRodWalker.java index 8a03dea44..04d04c2c4 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountRodWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountRodWalker.java @@ -27,8 +27,11 @@ package org.broadinstitute.sting.gatk.walkers.qc; import net.sf.samtools.SAMSequenceDictionary; import net.sf.samtools.SAMSequenceRecord; +import org.broad.tribble.Feature; import org.broadinstitute.sting.commandline.Argument; +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; @@ -41,23 +44,46 @@ import org.broadinstitute.sting.utils.collections.ExpandingArrayList; import org.broadinstitute.sting.utils.collections.Pair; import java.io.PrintStream; -import java.util.ArrayList; -import java.util.Collection; -import java.util.LinkedList; -import java.util.List; +import java.util.*; /** - * Prints out counts of the number of reference ordered data objects are - * each locus for debugging RodWalkers. + * Prints out counts of the number of reference ordered data objects encountered. + * + * + *

    Input

    + *

    + * One or more rod files. + *

    + * + *

    Output

    + *

    + * Number of rods seen. + *

    + * + *

    Examples

    + *
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T CountRod \
    + *   -o output.txt \
    + *   --rod input.vcf
    + * 
    + * */ public class CountRodWalker extends RodWalker, Long>> implements TreeReducible, Long>> { @Output public PrintStream out; - @Argument(fullName = "verbose", shortName = "v", doc="If true, Countrod will print out detailed information about the rods it finds and locations", required = false) + /** + * One or more input rod files + */ + @Input(fullName="rod", shortName = "rod", doc="Input VCF file(s)", required=false) + public List> rods = Collections.emptyList(); + + @Argument(fullName = "verbose", shortName = "v", doc="If true, CountRod will print out detailed information about the rods it finds and locations", required = false) public boolean verbose = false; - @Argument(fullName = "showSkipped", shortName = "s", doc="If true, CountRod will print out the skippped locations", required = false) + @Argument(fullName = "showSkipped", shortName = "s", doc="If true, CountRod will print out the skipped locations", required = false) public boolean showSkipped = false; @Override From b3b5d608caf8b0db652111a4581fc3c609cce277 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Wed, 17 Aug 2011 09:57:19 -0400 Subject: [PATCH 033/138] Adding docs to yet more walkers --- .../fasta/FastaAlternateReferenceWalker.java | 36 ++++++++++++++++-- .../walkers/fasta/FastaReferenceWalker.java | 38 +++++++++++++++++-- .../walkers/variantutils/VariantsToVCF.java | 35 ++++++++++++++++- 3 files changed, 101 insertions(+), 8 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 a57fabc39..8f333a2b3 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 @@ -40,18 +40,48 @@ 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'. + * 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'. + * + *

    Input

    + *

    + * The reference, requested intervals, and any number of variant rod files. + *

    + * + *

    Output

    + *

    + * A fasta file representing the requested intervals. + *

    + * + *

    Examples

    + *
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T FastaAlternateReferenceMaker \
    + *   -o output.fasta \
    + *   -L input.intervals \
    + *   --variant input.vcf \
    + *   [--snpmask mask.vcf]
    + * 
    + * */ @WalkerName("FastaAlternateReferenceMaker") @Reference(window=@Window(start=-1,stop=50)) @Requires(value={DataSource.REFERENCE}) public class FastaAlternateReferenceWalker extends FastaReferenceWalker { + /** + * Variants from these input files are used by this tool to construct an alternate reference. + */ @Input(fullName = "variant", shortName = "V", doc="variants to model", required=false) public List> variants = Collections.emptyList(); + /** + * Snps from this file are used as a mask when constructing the alternate reference. + */ @Input(fullName="snpmask", shortName = "snpmask", doc="SNP mask VCF file", required=false) public RodBinding snpmask; 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 2dbfc76ff..5f3b37cc8 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 @@ -38,14 +38,44 @@ import org.broadinstitute.sting.utils.collections.Pair; import java.io.PrintStream; /** - * Renders a new reference in FASTA format consisting of only those loci provided in the input data set. Has optional - * features to control the output format. + * Renders a new reference in FASTA format consisting of only those loci provided in the input data set. + * + *

    + * The output format can be partially controlled using the provided command-line arguments. + * + *

    Input

    + *

    + * The reference and requested intervals. + *

    + * + *

    Output

    + *

    + * A fasta file representing the requested intervals. + *

    + * + *

    Examples

    + *
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T FastaReference \
    + *   -o output.fasta \
    + *   -L input.intervals
    + * 
    + * */ @WalkerName("FastaReferenceMaker") public class FastaReferenceWalker extends RefWalker, GenomeLoc> { + @Output PrintStream out; - @Argument(fullName="lineWidth", shortName="lw", doc="Maximum length of sequence to write per line", required=false) public int fastaLineWidth=60; - @Argument(fullName="rawOnelineSeq", shortName="raw", doc="Print sequences with no FASTA header lines, one line per interval (i.e. lineWidth = infinity) - CAUTION: adjacent intervals will automatically be merged", required=false) public boolean fastaRawSeqs=false; + + @Argument(fullName="lineWidth", shortName="lw", doc="Maximum length of sequence to write per line", required=false) + public int fastaLineWidth=60; + + /** + * Please note that when using this argument adjacent intervals will automatically be merged. + */ + @Argument(fullName="rawOnelineSeq", shortName="raw", doc="Print sequences with no FASTA header lines, one line per interval (i.e. lineWidth = infinity)", required=false) + public boolean fastaRawSeqs=false; protected FastaSequence fasta; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantsToVCF.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantsToVCF.java index 1684dccfb..61851abe2 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantsToVCF.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantsToVCF.java @@ -53,6 +53,30 @@ import java.util.*; /** * Converts variants from other file formats to VCF format. + * + *

    + * Note that there must be a Tribble feature/codec for the file format as well as an adaptor. + * + *

    Input

    + *

    + * A variant file to filter. + *

    + * + *

    Output

    + *

    + * A VCF file. + *

    + * + *

    Examples

    + *
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T VariantsToVCF \
    + *   -o output.vcf \
    + *   --variant:RawHapMap input.hapmap \
    + *   --dbsnp dbsnp.vcf
    + * 
    + * */ @Reference(window=@Window(start=-40,stop=40)) public class VariantsToVCF extends RodWalker { @@ -61,15 +85,24 @@ public class VariantsToVCF extends RodWalker { protected VCFWriter baseWriter = null; private SortingVCFWriter vcfwriter; // needed because hapmap/dbsnp indel records move + /** + * Variants from this input file are used by this tool as input. + */ @Input(fullName="variant", shortName = "V", doc="Input variant file", required=true) public RodBinding variants; @ArgumentCollection protected DbsnpArgumentCollection dbsnp = new DbsnpArgumentCollection(); - @Argument(fullName="sample", shortName="sample", doc="The sample name represented by the variant rod (for data like GELI with genotypes)", required=false) + /** + * This argument is used for data (like GELI) with genotypes but no sample names encoded within. + */ + @Argument(fullName="sample", shortName="sample", doc="The sample name represented by the variant rod", required=false) protected String sampleName = null; + /** + * This argument is useful for fixing input VCFs with bad reference bases (the output will be a fixed version of the VCF). + */ @Argument(fullName="fixRef", shortName="fixRef", doc="Fix common reference base in case there's an indel without padding", required=false) protected boolean fixReferenceBase = false; From 79dcfca25fdedd3aea29e36a9e8a2736c601d717 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Wed, 17 Aug 2011 11:56:51 -0400 Subject: [PATCH 035/138] Fixed bad character in documentation --- .../sting/gatk/walkers/indels/IndelRealigner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java index 029b6deaf..d766ae8bd 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java @@ -69,7 +69,7 @@ import java.util.*; *

    * The local realignment tool is designed to consume one or more BAM files and to locally realign reads such that the number of mismatching bases * is minimized across all the reads. In general, a large percent of regions requiring local realignment are due to the presence of an insertion - * or deletion (indels) in the individualÕs genome with respect to the reference genome. Such alignment artifacts result in many bases mismatching + * or deletion (indels) in the individual's genome with respect to the reference genome. Such alignment artifacts result in many bases mismatching * the reference near the misalignment, which are easily mistaken as SNPs. Moreover, since read mapping algorithms operate on each read independently, * it is impossible to place reads on the reference genome such at mismatches are minimized across all reads. Consequently, even when some reads are * correctly mapped with indels, reads covering the indel near just the start or end of the read are often incorrectly mapped with respect the true indel, From 78deb3f19595816d08fbde35db5f62189727048e Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Wed, 17 Aug 2011 11:57:00 -0400 Subject: [PATCH 036/138] Fixed bad character in documentation --- .../walkers/variantutils/VariantsToTable.java | 226 ++++++++++++------ 1 file changed, 153 insertions(+), 73 deletions(-) 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 af3593ce4..51515b2d3 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 @@ -40,29 +40,97 @@ import java.io.PrintStream; import java.util.*; /** - * Emits specific fields as dictated by the user from one or more VCF files. + * Emits specific fields from a VCF file to a table-deliminated format + * + *

    + * This walker accepts a single VCF file and writes out user-selected fields from the + * VCF as a header-containing, tab-deliminated file. The user specifies one or more + * fields to print with the -F NAME, each of which appears as a single column in + * the output file, with a header named NAME, and the value of this field in the VCF + * one per line. NAME can be any standard VCF column (CHROM, ID, QUAL) or any binding + * in the INFO field (AC=10). Note that this tool does not support capturing any + * GENOTYPE field values. If a VCF record is missing a value, then the tool by + * default throws an error, but the special value NA can be emitted instead with + * appropriate tool arguments. + * + *

    + * + *

    Input

    + *

    + *

      + *
    • A VCF file
    • + *
    • A list of -F fields to write
    • + *
    + *

    + * + *

    Output

    + *

    + * A table deliminated file containing the values of the requested fields in the VCF file + *

    + * + *

    Examples

    + *
    + *     -T $WalkerName \
    + *     -V file.vcf \
    + *     -F CHROM -F POS -F ID -F QUAL -F AC \
    + *     -o results.table
    + *
    + *     would produce a file that looks like:
    + *
    + *     CHROM    POS ID      QUAL    AC
    + *     1        10  .       50      1
    + *     1        20  rs10    99      10
    + *     et cetera...
    + * 
    + * + * @author Mark DePristo + * @since 2010 */ public class VariantsToTable extends RodWalker { - @ArgumentCollection protected StandardVariantContextInputArgumentCollection variantCollection = new StandardVariantContextInputArgumentCollection(); @Output(doc="File to which results should be written",required=true) protected PrintStream out; - @Argument(fullName="fields", shortName="F", doc="Fields to emit from the VCF, allows any VCF field, any info field, and some meta fields like nHets", required=true) - public ArrayList fieldsToTake = new ArrayList(); + /** + * -F NAME can be any standard VCF column (CHROM, ID, QUAL) or any binding in the INFO field (e.g., AC=10). + * Note that this tool does not support capturing any GENOTYPE field values. Note this argument + * accepts any number of inputs. So -F CHROM -F POS is allowed. + */ + @Argument(fullName="fields", shortName="F", doc="The name of each field to capture for output in the table", required=true) + public List fieldsToTake = new ArrayList(); - @Argument(fullName="showFiltered", shortName="raw", doc="Include filtered records") + /** + * By default this tool only emits values for fields where the FILTER field is either PASS or . (unfiltered). + * Throwing this flag will cause $WalkerName to emit values regardless of the FILTER field value. + */ + @Argument(fullName="showFiltered", shortName="raw", doc="If provided, field values from filtered records will be included in the output", required=false) public boolean showFiltered = false; - @Argument(fullName="maxRecords", shortName="M", doc="Maximum number of records to emit, if provided", required=false) + /** + * If provided, then this tool will exit with success after this number of records have been emitted to the file. + */ + @Argument(fullName="maxRecords", shortName="M", doc="If provided, we will emit at most maxRecord records to the table", required=false) public int MAX_RECORDS = -1; int nRecords = 0; + /** + * By default, only biallelic (REF=A, ALT=B) sites are including in the output. If this flag is provided, then + * VariantsToTable will emit field values for records with multiple ALT alleles. Note that in general this + * can make your resulting file unreadable and malformated according to tools like R, as the representation of + * multi-allelic INFO field values can be lists of values. + */ @Argument(fullName="keepMultiAllelic", shortName="KMA", doc="If provided, we will not require the site to be biallelic", required=false) public boolean keepMultiAllelic = false; + /** + * By default, this tool throws a UserException when it encounters a field without a value in some record. This + * is generally useful when you mistype -F CHRMO, so that you get a friendly warning about CHRMO not being + * found before the tool runs through 40M 1000G records. However, in some cases you genuinely want to allow such + * fields (e.g., AC not being calculated for filtered records, if included). When provided, this argument + * will cause VariantsToTable to write out NA values for missing fields instead of throwing an error. + */ @Argument(fullName="allowMissingData", shortName="AMD", doc="If provided, we will not require every record to contain every field", required=false) public boolean ALLOW_MISSING_DATA = false; @@ -70,65 +138,6 @@ public class VariantsToTable extends RodWalker { out.println(Utils.join("\t", fieldsToTake)); } - public static abstract class Getter { public abstract String get(VariantContext vc); } - public static Map getters = new HashMap(); - - static { - // #CHROM POS ID REF ALT QUAL FILTER INFO FORMAT - getters.put("CHROM", new Getter() { public String get(VariantContext vc) { return vc.getChr(); } }); - getters.put("POS", new Getter() { public String get(VariantContext vc) { return Integer.toString(vc.getStart()); } }); - getters.put("REF", new Getter() { - public String get(VariantContext vc) { - String x = ""; - if ( vc.hasReferenceBaseForIndel() ) { - Byte refByte = vc.getReferenceBaseForIndel(); - x=x+new String(new byte[]{refByte}); - } - return x+vc.getReference().getDisplayString(); - } - }); - getters.put("ALT", new Getter() { - public String get(VariantContext vc) { - StringBuilder x = new StringBuilder(); - int n = vc.getAlternateAlleles().size(); - if ( n == 0 ) return "."; - if ( vc.hasReferenceBaseForIndel() ) { - Byte refByte = vc.getReferenceBaseForIndel(); - x.append(new String(new byte[]{refByte})); - } - - for ( int i = 0; i < n; i++ ) { - if ( i != 0 ) x.append(","); - x.append(vc.getAlternateAllele(i).getDisplayString()); - } - return x.toString(); - } - }); - getters.put("QUAL", new Getter() { public String get(VariantContext vc) { return Double.toString(vc.getPhredScaledQual()); } }); - getters.put("TRANSITION", new Getter() { public String get(VariantContext vc) { - if ( vc.isSNP() && vc.isBiallelic() ) - return VariantContextUtils.isTransition(vc) ? "1" : "0"; - else - return "-1"; - }}); - getters.put("FILTER", new Getter() { public String get(VariantContext vc) { - return vc.isNotFiltered() ? "PASS" : Utils.join(",", vc.getFilters()); } - }); - - getters.put("HET", new Getter() { public String get(VariantContext vc) { return Integer.toString(vc.getHetCount()); } }); - 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("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()); } }); - getters.put("GQ", new Getter() { public String get(VariantContext vc) { - if ( vc.getNSamples() > 1 ) throw new UserException("Cannot get GQ values for multi-sample VCF"); - return String.format("%.2f", 10 * vc.getGenotype(0).getNegLog10PError()); - }}); - } - - public Integer map(RefMetaDataTracker tracker, ReferenceContext ref, AlignmentContext context) { if ( tracker == null ) // RodWalkers can make funky map calls return 0; @@ -155,6 +164,15 @@ public class VariantsToTable extends RodWalker { return s.endsWith("*"); } + /** + * Utility function that returns the list of values for each field in fields from vc. + * + * @param vc the VariantContext whose field values we can to capture + * @param fields a non-null list of fields to capture from VC + * @param allowMissingData if false, then throws a UserException if any field isn't found in vc. Otherwise + * provides a value of NA + * @return + */ public static List extractFields(VariantContext vc, List fields, boolean allowMissingData) { List vals = new ArrayList(); @@ -213,13 +231,75 @@ public class VariantsToTable extends RodWalker { return vals; } - public Integer reduceInit() { - return 0; - } - - public Integer reduce(Integer counter, Integer sum) { - return counter + sum; - } - + // + // default reduce -- doesn't do anything at all + // + public Integer reduceInit() { return 0; } + public Integer reduce(Integer counter, Integer sum) { return counter + sum; } public void onTraversalDone(Integer sum) {} + + // ---------------------------------------------------------------------------------------------------- + // + // static system for getting values from VC by name. + // + // ---------------------------------------------------------------------------------------------------- + + public static abstract class Getter { public abstract String get(VariantContext vc); } + public static Map getters = new HashMap(); + + static { + // #CHROM POS ID REF ALT QUAL FILTER INFO FORMAT + getters.put("CHROM", new Getter() { public String get(VariantContext vc) { return vc.getChr(); } }); + getters.put("POS", new Getter() { public String get(VariantContext vc) { return Integer.toString(vc.getStart()); } }); + getters.put("REF", new Getter() { + public String get(VariantContext vc) { + String x = ""; + if ( vc.hasReferenceBaseForIndel() ) { + Byte refByte = vc.getReferenceBaseForIndel(); + x=x+new String(new byte[]{refByte}); + } + return x+vc.getReference().getDisplayString(); + } + }); + getters.put("ALT", new Getter() { + public String get(VariantContext vc) { + StringBuilder x = new StringBuilder(); + int n = vc.getAlternateAlleles().size(); + if ( n == 0 ) return "."; + if ( vc.hasReferenceBaseForIndel() ) { + Byte refByte = vc.getReferenceBaseForIndel(); + x.append(new String(new byte[]{refByte})); + } + + for ( int i = 0; i < n; i++ ) { + if ( i != 0 ) x.append(","); + x.append(vc.getAlternateAllele(i).getDisplayString()); + } + return x.toString(); + } + }); + getters.put("QUAL", new Getter() { public String get(VariantContext vc) { return Double.toString(vc.getPhredScaledQual()); } }); + getters.put("TRANSITION", new Getter() { public String get(VariantContext vc) { + if ( vc.isSNP() && vc.isBiallelic() ) + return VariantContextUtils.isTransition(vc) ? "1" : "0"; + else + return "-1"; + }}); + getters.put("FILTER", new Getter() { public String get(VariantContext vc) { + return vc.isNotFiltered() ? "PASS" : Utils.join(",", vc.getFilters()); } + }); + + getters.put("HET", new Getter() { public String get(VariantContext vc) { return Integer.toString(vc.getHetCount()); } }); + 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("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()); } }); + getters.put("GQ", new Getter() { public String get(VariantContext vc) { + if ( vc.getNSamples() > 1 ) throw new UserException("Cannot get GQ values for multi-sample VCF"); + return String.format("%.2f", 10 * vc.getGenotype(0).getNegLog10PError()); + }}); + } + } From 9d1d5bd27a5747a8dfd430ec042dee76e7fce835 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Wed, 17 Aug 2011 11:57:31 -0400 Subject: [PATCH 037/138] Revert "Fixed bad character in documentation" This reverts commit a1f50c82d3cb25e5e83d36e9054d74cdee957d87. --- .../sting/gatk/walkers/indels/IndelRealigner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java index d766ae8bd..029b6deaf 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java @@ -69,7 +69,7 @@ import java.util.*; *

    * The local realignment tool is designed to consume one or more BAM files and to locally realign reads such that the number of mismatching bases * is minimized across all the reads. In general, a large percent of regions requiring local realignment are due to the presence of an insertion - * or deletion (indels) in the individual's genome with respect to the reference genome. Such alignment artifacts result in many bases mismatching + * or deletion (indels) in the individualÕs genome with respect to the reference genome. Such alignment artifacts result in many bases mismatching * the reference near the misalignment, which are easily mistaken as SNPs. Moreover, since read mapping algorithms operate on each read independently, * it is impossible to place reads on the reference genome such at mismatches are minimized across all reads. Consequently, even when some reads are * correctly mapped with indels, reads covering the indel near just the start or end of the read are often incorrectly mapped with respect the true indel, From 5f794d16a7df2aab7e761adbe9d75e3dbdfc1dae Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Wed, 17 Aug 2011 12:00:32 -0400 Subject: [PATCH 038/138] Fixed bad character in documentation --- .../sting/gatk/walkers/indels/IndelRealigner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java index 029b6deaf..d766ae8bd 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java @@ -69,7 +69,7 @@ import java.util.*; *

    * The local realignment tool is designed to consume one or more BAM files and to locally realign reads such that the number of mismatching bases * is minimized across all the reads. In general, a large percent of regions requiring local realignment are due to the presence of an insertion - * or deletion (indels) in the individualÕs genome with respect to the reference genome. Such alignment artifacts result in many bases mismatching + * or deletion (indels) in the individual's genome with respect to the reference genome. Such alignment artifacts result in many bases mismatching * the reference near the misalignment, which are easily mistaken as SNPs. Moreover, since read mapping algorithms operate on each read independently, * it is impossible to place reads on the reference genome such at mismatches are minimized across all reads. Consequently, even when some reads are * correctly mapped with indels, reads covering the indel near just the start or end of the read are often incorrectly mapped with respect the true indel, From c6fb215faf42d04ba86341b0351613d7ef717ad3 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Wed, 17 Aug 2011 12:02:41 -0400 Subject: [PATCH 039/138] GATKDocs for VariantsToTable -- Made a previously required argument optional, as this was a long-standing bug --- .../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 51515b2d3..a37cfa6ba 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 @@ -135,6 +135,7 @@ public class VariantsToTable extends RodWalker { public boolean ALLOW_MISSING_DATA = false; public void initialize() { + // print out the header out.println(Utils.join("\t", fieldsToTake)); } From 3da71a9bb6ed5384641ea98e36fd3affca87ac8a Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Wed, 17 Aug 2011 12:04:45 -0400 Subject: [PATCH 040/138] Clean up summary --- .../sting/gatk/walkers/variantutils/VariantsToTable.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 a37cfa6ba..b3fd540bc 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 @@ -40,7 +40,7 @@ import java.io.PrintStream; import java.util.*; /** - * Emits specific fields from a VCF file to a table-deliminated format + * Emits specific fields from a VCF file to a tab-deliminated table * *

    * This walker accepts a single VCF file and writes out user-selected fields from the From d1bb302d12144991650f90f9ffc57bac5747c005 Mon Sep 17 00:00:00 2001 From: Menachem Fromer Date: Wed, 17 Aug 2011 12:21:37 -0400 Subject: [PATCH 041/138] Added GatkDocs documentation --- .../phasing/ReadBackedPhasingWalker.java | 53 +++++++++++++++---- 1 file changed, 43 insertions(+), 10 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/phasing/ReadBackedPhasingWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/phasing/ReadBackedPhasingWalker.java index ac4fba4b4..34c7912d9 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/phasing/ReadBackedPhasingWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/phasing/ReadBackedPhasingWalker.java @@ -23,7 +23,10 @@ */ package org.broadinstitute.sting.gatk.walkers.phasing; -import org.broadinstitute.sting.commandline.*; +import org.broadinstitute.sting.commandline.Argument; +import org.broadinstitute.sting.commandline.ArgumentCollection; +import org.broadinstitute.sting.commandline.Hidden; +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; @@ -49,16 +52,46 @@ import java.util.*; import static org.broadinstitute.sting.utils.codecs.vcf.VCFUtils.getVCFHeadersFromRods; - /** * Walks along all variant ROD loci, caching a user-defined window of VariantContext sites, and then finishes phasing them when they go out of range (using upstream and downstream reads). + * + *

    + * Performs physical phasing of SNP calls, based on sequencing reads. + *

    + * + *

    Input

    + *

    + * VCF file of SNP calls, BAM file of sequence reads. + *

    + * + *

    Output

    + *

    + * Phased VCF file. + *

    + * + *

    Examples

    + *
    + *    java
    + *      -jar GenomeAnalysisTK.jar
    + *      -T ReadBackedPhasing
    + *      -R reference.fasta
    + *      -I reads.bam
    + *      --variant:vcf SNPs.vcf
    + *      -BTI variant
    + *      -BTIMR INTERSECTION
    + *      -o phased_SNPs.vcf
    + *      --phaseQualityThresh 20.0
    + * 
    + * + * @author Menachem Fromer + * @since July 2010 */ @Allows(value = {DataSource.READS, DataSource.REFERENCE}) @Requires(value = {DataSource.READS, DataSource.REFERENCE}) @By(DataSource.READS) -@ReadFilters({MappingQualityZeroReadFilter.class}) // Filter out all reads with zero mapping quality +@ReadFilters({MappingQualityZeroReadFilter.class}) public class ReadBackedPhasingWalker extends RodWalker { private static final boolean DEBUG = false; @@ -73,13 +106,13 @@ public class ReadBackedPhasingWalker extends RodWalker P(error) = 10^(-10/10) = 0.1, P(correct) = 0.9 @Hidden @@ -87,10 +120,10 @@ public class ReadBackedPhasingWalker extends RodWalker Date: Wed, 17 Aug 2011 12:35:08 -0400 Subject: [PATCH 042/138] Adding docs to 3 more walkers --- .../gatk/walkers/indels/LeftAlignIndels.java | 36 +++++++++++++++++-- .../variantutils/LeftAlignVariants.java | 25 +++++++++++++ .../variantutils/ValidateVariants.java | 36 ++++++++++++++++--- 3 files changed, 89 insertions(+), 8 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/LeftAlignIndels.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/LeftAlignIndels.java index af8051334..17d5a8e9b 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/LeftAlignIndels.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/LeftAlignIndels.java @@ -35,16 +35,46 @@ import org.broadinstitute.sting.gatk.refdata.ReadMetaDataTracker; import org.broadinstitute.sting.gatk.walkers.ReadWalker; import org.broadinstitute.sting.utils.sam.AlignmentUtils; + /** - * Left aligns indels in reads. + * Left-aligns indels from reads in a bam file. + * + *

    + * LeftAlignIndels is a tool that takes a bam file and left-aligns any indels inside it. The same indel can often be + * placed at multiple positions and still represent the same haplotype. While a standard convention is to place an + * indel at the left-most position this doesn't always happen, so this tool can be used to left-align them. + * + *

    Input

    + *

    + * A bam file to left-align. + *

    + * + *

    Output

    + *

    + * A left-aligned bam. + *

    + * + *

    Examples

    + *
    + * java -Xmx3g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T LeftAlignIndels \
    + *   -I input.bam \
    + *   -o output.vcf
    + * 
    + * */ public class LeftAlignIndels extends ReadWalker { @Output(required=false, doc="Output bam") protected StingSAMFileWriter writer = null; - @Argument(fullName="maxReadsInRam", shortName="maxInRam", doc="max reads allowed to be kept in memory at a time by the SAMFileWriter. "+ - "If too low, the tool may run out of system file descriptors needed to perform sorting; if too high, the tool may run out of memory.", required=false) + /** + * If set too low, the tool may run out of system file descriptors needed to perform sorting; if too high, the tool + * may run out of memory. We recommend that you additionally tell Java to use a temp directory with plenty of available + * space (by setting java.io.tempdir on the command-line). + */ + @Argument(fullName="maxReadsInRam", shortName="maxInRam", doc="max reads allowed to be kept in memory at a time by the output writer", required=false) protected int MAX_RECORDS_IN_RAM = 500000; public void initialize() { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/LeftAlignVariants.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/LeftAlignVariants.java index c47a015c6..9fae71e4e 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/LeftAlignVariants.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/LeftAlignVariants.java @@ -46,6 +46,31 @@ import java.util.*; /** * Left-aligns indels from a variants file. + * + *

    + * LeftAlignVariants is a tool that takes a VCF file and left-aligns any indels inside it. The same indel can often be + * placed at multiple positions and still represent the same haplotype. While the standard convention with VCF is to + * place an indel at the left-most position this doesn't always happen, so this tool can be used to left-align them. + * + *

    Input

    + *

    + * A variant set to left-align. + *

    + * + *

    Output

    + *

    + * A left-aligned VCF. + *

    + * + *

    Examples

    + *
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T LeftAlignVariants \
    + *   --variant input.vcf \
    + *   -o output.vcf
    + * 
    + * */ @Reference(window=@Window(start=-200,stop=200)) public class LeftAlignVariants extends RodWalker { 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 5c7fb268c..01a6e2f70 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 @@ -25,7 +25,6 @@ package org.broadinstitute.sting.gatk.walkers.variantutils; -import org.broad.tribble.Feature; import org.broad.tribble.TribbleException; import org.broad.tribble.dbsnp.DbSNPFeature; import org.broadinstitute.sting.commandline.*; @@ -34,7 +33,6 @@ import org.broadinstitute.sting.gatk.arguments.StandardVariantContextInputArgume 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.features.DbSNPHelper; import org.broadinstitute.sting.gatk.walkers.*; import org.broadinstitute.sting.utils.exceptions.UserException; import org.broadinstitute.sting.utils.variantcontext.Allele; @@ -48,7 +46,32 @@ import java.util.Set; /** - * Validates a variants file. + * Strictly validates a variants file. + * + *

    + * ValidateVariants is a GATK tool that takes a VCF file and validates much of the information inside it. + * Checks include the correctness of the reference base(s), accuracy of AC & AN values, tests against rsIDs + * when a dbSNP file is provided, and that all alternate alleles are present in at least one sample. + * + *

    Input

    + *

    + * A variant set to filter. + *

    + * + *

    Output

    + *

    + * A filtered VCF. + *

    + * + *

    Examples

    + *
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T ValidateVariants \
    + *   --variant input.vcf \
    + *   --dbsnp dbsnp.vcf
    + * 
    + * */ @Reference(window=@Window(start=0,stop=100)) public class ValidateVariants extends RodWalker { @@ -67,10 +90,13 @@ public class ValidateVariants extends RodWalker { @Argument(fullName = "validationType", shortName = "type", doc = "which validation type to run", required = false) protected ValidationType type = ValidationType.ALL; - @Argument(fullName = "doNotValidateFilteredRecords", shortName = "doNotValidateFilteredRecords", doc = "should we skip validation on filtered records?", required = false) + /** + * By default, even filtered records are validated. + */ + @Argument(fullName = "doNotValidateFilteredRecords", shortName = "doNotValidateFilteredRecords", doc = "skip validation on filtered records", required = false) protected Boolean DO_NOT_VALIDATE_FILTERED = false; - @Argument(fullName = "warnOnErrors", shortName = "warnOnErrors", doc = "should we just emit warnings on errors instead of terminating the run?", required = false) + @Argument(fullName = "warnOnErrors", shortName = "warnOnErrors", doc = "just emit warnings on errors instead of terminating the run at the first instance", required = false) protected Boolean WARN_ON_ERROR = false; private long numErrors = 0; From 6d629c176c2842414fd093546f68fdaf4c1b1c3f Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Wed, 17 Aug 2011 13:27:36 -0400 Subject: [PATCH 043/138] Adding docs --- ...fWalker.java => CountRODsByRefWalker.java} | 0 ...untRodWalker.java => CountRODsWalker.java} | 0 .../VariantValidationAssessor.java | 62 ++++++++++++++----- 3 files changed, 48 insertions(+), 14 deletions(-) rename public/java/src/org/broadinstitute/sting/gatk/walkers/qc/{CountRodByRefWalker.java => CountRODsByRefWalker.java} (100%) rename public/java/src/org/broadinstitute/sting/gatk/walkers/qc/{CountRodWalker.java => CountRODsWalker.java} (100%) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountRodByRefWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountRODsByRefWalker.java similarity index 100% rename from public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountRodByRefWalker.java rename to public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountRODsByRefWalker.java diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountRodWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountRODsWalker.java similarity index 100% rename from public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountRodWalker.java rename to public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountRODsWalker.java 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 6ed0bbd16..b98646270 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 @@ -25,10 +25,8 @@ package org.broadinstitute.sting.gatk.walkers.variantutils; -import org.broadinstitute.sting.commandline.Argument; -import org.broadinstitute.sting.commandline.Input; -import org.broadinstitute.sting.commandline.Output; -import org.broadinstitute.sting.commandline.RodBinding; +import org.broadinstitute.sting.commandline.*; +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; @@ -43,21 +41,57 @@ import org.broadinstitute.sting.utils.variantcontext.VariantContextUtils; import java.util.*; /** - * Converts Sequenom files to a VCF annotated with QC metrics (HW-equilibrium, % failed probes) + * Annotates a validation (from e.g. Sequenom) VCF with QC metrics (HW-equilibrium, % failed probes) + * + *

    + * The Variant Validation Assessor is a tool for vetting/assessing validation data (containing genotypes). + * The tool produces a VCF that is annotated with information pertaining to plate quality control and by + * default is soft-filtered by high no-call rate or low Hardy-Weinberg probability. + * If you have .ped files, please first convert them to VCF format + * (see http://www.broadinstitute.org/gsa/wiki/index.php/Converting_ped_to_vcf). + * + *

    Input

    + *

    + * A validation VCF to annotate. + *

    + * + *

    Output

    + *

    + * An annotated VCF. + *

    + * + *

    Examples

    + *
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T VariantValidationAssessor \
    + *   --variant input.vcf \
    + *   -o output.vcf
    + * 
    + * */ @Reference(window=@Window(start=0,stop=40)) public class VariantValidationAssessor extends RodWalker { - @Input(fullName="variants", shortName = "V", doc="Input VCF file", required=true) - public RodBinding variants; + + @ArgumentCollection + protected StandardVariantContextInputArgumentCollection variantCollection = new StandardVariantContextInputArgumentCollection(); @Output(doc="File to which variants should be written",required=true) protected VCFWriter vcfwriter = null; - @Argument(fullName="maxHardy", doc="Maximum phred-scaled Hardy-Weinberg violation pvalue to consider an assay valid [default:20]", required=false) + @Argument(fullName="maxHardy", doc="Maximum phred-scaled Hardy-Weinberg violation pvalue to consider an assay valid", required=false) protected double maxHardy = 20.0; - @Argument(fullName="maxNoCall", doc="Maximum no-call rate (as a fraction) to consider an assay valid [default:0.05]", required=false) + + /** + * To disable, set to a value greater than 1. + */ + @Argument(fullName="maxNoCall", doc="Maximum no-call rate (as a fraction) to consider an assay valid", required=false) protected double maxNoCall = 0.05; - @Argument(fullName="maxHomVar", doc="Maximum homozygous variant rate (as a fraction) to consider an assay valid [default:1.1, disabled]", required=false) + + /** + * To disable, set to a value greater than 1. + */ + @Argument(fullName="maxHomVar", doc="Maximum homozygous variant rate (as a fraction) to consider an assay valid", required=false) protected double maxHomNonref = 1.1; //@Argument(fullName="populationFile", shortName="populations", doc="A tab-delimited file relating individuals to populations,"+ @@ -93,7 +127,7 @@ public class VariantValidationAssessor extends RodWalker if ( tracker == null ) return null; - VariantContext vc = tracker.getFirstValue(variants, ref.getLocus()); + VariantContext vc = tracker.getFirstValue(variantCollection.variants, ref.getLocus()); // ignore places where we don't have a variant if ( vc == null ) return null; @@ -101,7 +135,7 @@ public class VariantValidationAssessor extends RodWalker if ( sampleNames == null ) sampleNames = new TreeSet(vc.getSampleNames()); - return addVariantInformationToCall(ref, vc); + return addVariantInformationToCall(vc); } public Integer reduce(VariantContext call, Integer numVariants) { @@ -113,7 +147,7 @@ public class VariantValidationAssessor extends RodWalker } public void onTraversalDone(Integer finalReduce) { - final List inputNames = Arrays.asList(variants.getName()); + final List inputNames = Arrays.asList(variantCollection.variants.getName()); // setup the header fields Set hInfo = new HashSet(); @@ -159,7 +193,7 @@ public class VariantValidationAssessor extends RodWalker } - private VariantContext addVariantInformationToCall(ReferenceContext ref, VariantContext vContext) { + private VariantContext addVariantInformationToCall(VariantContext vContext) { // check possible filters double hwPvalue = hardyWeinbergCalculation(vContext); From 575303ae6b2563b8bb5380926d9fe813846bb07b Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Wed, 17 Aug 2011 13:28:19 -0400 Subject: [PATCH 044/138] Renaming for consistency and bringing up to speed with new rod system --- .../gatk/walkers/qc/CountRODsByRefWalker.java | 49 ++++++++++++++++--- .../gatk/walkers/qc/CountRODsWalker.java | 8 +-- 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountRODsByRefWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountRODsByRefWalker.java index d1545f159..7c7d6417a 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountRODsByRefWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountRODsByRefWalker.java @@ -25,7 +25,10 @@ package org.broadinstitute.sting.gatk.walkers.qc; +import org.broad.tribble.Feature; import org.broadinstitute.sting.commandline.Argument; +import org.broadinstitute.sting.commandline.Input; +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; @@ -33,25 +36,55 @@ import org.broadinstitute.sting.gatk.walkers.RefWalker; import org.broadinstitute.sting.utils.collections.ExpandingArrayList; import org.broadinstitute.sting.utils.collections.Pair; +import java.util.Collections; +import java.util.List; + /** - * Prints out counts of the number of reference ordered data objects are - * each locus for debugging RefWalkers. + * Prints out counts of the number of reference ordered data objects encountered. + * + * + *

    Input

    + *

    + * One or more rod files. + *

    + * + *

    Output

    + *

    + * Number of rods seen. + *

    + * + *

    Examples

    + *
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T CountRODsByRef \
    + *   -o output.txt \
    + *   --rod input.vcf
    + * 
    + * */ -public class CountRodByRefWalker extends RefWalker, Long>> { - @Argument(fullName = "verbose", shortName = "v", doc="If true, Countrod will print out detailed information about the rods it finds and locations", required = false) +public class CountRODsByRefWalker extends RefWalker, Long>> { + + /** + * One or more input rod files + */ + @Input(fullName="rod", shortName = "rod", doc="Input VCF file(s)", required=false) + public List> rods = Collections.emptyList(); + + @Argument(fullName = "verbose", shortName = "v", doc="If true, this tool will print out detailed information about the rods it finds and locations", required = false) public boolean verbose = false; - @Argument(fullName = "showSkipped", shortName = "s", doc="If true, CountRod will print out the skippped locations", required = false) + @Argument(fullName = "showSkipped", shortName = "s", doc="If true, this tool will print out the skipped locations", required = false) public boolean showSkipped = false; - CountRodWalker crw = new CountRodWalker(); + CountRODsWalker crw = new CountRODsWalker(); public void initialize() { crw.verbose = verbose; crw.showSkipped = showSkipped; } - public CountRodWalker.Datum map(RefMetaDataTracker tracker, ReferenceContext ref, AlignmentContext context) { + public CountRODsWalker.Datum map(RefMetaDataTracker tracker, ReferenceContext ref, AlignmentContext context) { return crw.map(tracker, ref, context); } @@ -59,7 +92,7 @@ public class CountRodByRefWalker extends RefWalker, Long> reduce(CountRodWalker.Datum point, Pair, Long> sum) { + public Pair, Long> reduce(CountRODsWalker.Datum point, Pair, Long> sum) { return crw.reduce(point, sum); } } \ No newline at end of file diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountRODsWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountRODsWalker.java index 04d04c2c4..edbd5ff75 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountRODsWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/CountRODsWalker.java @@ -64,13 +64,13 @@ import java.util.*; *
      * java -Xmx2g -jar GenomeAnalysisTK.jar \
      *   -R ref.fasta \
    - *   -T CountRod \
    + *   -T CountRODs \
      *   -o output.txt \
      *   --rod input.vcf
      * 
    * */ -public class CountRodWalker extends RodWalker, Long>> implements TreeReducible, Long>> { +public class CountRODsWalker extends RodWalker, Long>> implements TreeReducible, Long>> { @Output public PrintStream out; @@ -80,10 +80,10 @@ public class CountRodWalker extends RodWalker> rods = Collections.emptyList(); - @Argument(fullName = "verbose", shortName = "v", doc="If true, CountRod will print out detailed information about the rods it finds and locations", required = false) + @Argument(fullName = "verbose", shortName = "v", doc="If true, this tool will print out detailed information about the rods it finds and locations", required = false) public boolean verbose = false; - @Argument(fullName = "showSkipped", shortName = "s", doc="If true, CountRod will print out the skipped locations", required = false) + @Argument(fullName = "showSkipped", shortName = "s", doc="If true, this tool will print out the skipped locations", required = false) public boolean showSkipped = false; @Override From 069554efe5765dfbdb2023141188966abcdfacda Mon Sep 17 00:00:00 2001 From: Andrey Sivachenko Date: Wed, 17 Aug 2011 14:05:19 -0400 Subject: [PATCH 045/138] somatic indel detector does not die on reads that are too long (likely contain a huge deletion) anymore; instead print a warning and ignore the read --- .../walkers/indels/SomaticIndelDetectorWalker.java | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 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 64cff2c0d..7e8985e03 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 @@ -469,8 +469,17 @@ public class SomaticIndelDetectorWalker extends ReadWalker { // let's double check now that the read fits after the shift if ( read.getAlignmentEnd() > normal_context.getStop()) { // ooops, looks like the read does not fit into the window even after the latter was shifted!! - throw new UserException.BadArgumentValue("window_size", "Read "+read.getReadName()+": out of coverage window bounds. Probably window is too small, so increase the value of the window_size argument.\n"+ - "Read length="+read.getReadLength()+"; cigar="+read.getCigarString()+"; start="+ + // we used to die over such reads and require user to run with larger window size. Now we + // just print a warning and discard the read (this means that our counts can be slightly off in + // th epresence of such reads) + //throw new UserException.BadArgumentValue("window_size", "Read "+read.getReadName()+": out of coverage window bounds. Probably window is too small, so increase the value of the window_size argument.\n"+ + // "Read length="+read.getReadLength()+"; cigar="+read.getCigarString()+"; start="+ + // read.getAlignmentStart()+"; end="+read.getAlignmentEnd()+ + // "; window start (after trying to accomodate the read)="+normal_context.getStart()+"; window end="+normal_context.getStop()); + System.out.println("WARNING: Read "+read.getReadName()+ + " is out of coverage window bounds. Probably window is too small and the window_size value must be increased.\n"+ + " The read is ignored in this run (so all the counts/statistics reported will not include it).\n"+ + " Read length="+read.getReadLength()+"; cigar="+read.getCigarString()+"; start="+ read.getAlignmentStart()+"; end="+read.getAlignmentEnd()+ "; window start (after trying to accomodate the read)="+normal_context.getStart()+"; window end="+normal_context.getStop()); } From 2f19046f0cf599ef57f646e4f05bc8b3edeb014f Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Wed, 17 Aug 2011 14:19:14 -0400 Subject: [PATCH 048/138] Adding docs to the 2 beasts. Saved the worst for last. --- .../walkers/variantutils/CombineVariants.java | 77 ++++++-- .../walkers/variantutils/SelectVariants.java | 177 +++++++++++++++--- .../variantcontext/VariantContextUtils.java | 26 ++- 3 files changed, 244 insertions(+), 36 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 fb172e1b7..7062f17e5 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 @@ -43,10 +43,54 @@ import org.broadinstitute.sting.utils.variantcontext.VariantContextUtils; import java.util.*; /** - * Combines VCF records from different sources; supports both full merges and set unions. + * Combines VCF records from different sources. + * + *

    + * CombineVariants combines VCF records from different sources. Any (unique) name can be used to bind your rod data + * and any number of sources can be input. This tool currently supports two different combination types for each of + * variants (the first 8 fields of the VCF) and genotypes (the rest). * Merge: combines multiple records into a single one; if sample names overlap then they are uniquified. * Union: assumes each rod represents the same set of samples (although this is not enforced); using the - * priority list (if provided), emits a single record instance at every position represented in the rods. + * priority list (if provided), it emits a single record instance at every position represented in the rods. + * + * CombineVariants will include a record at every site in all of your input VCF files, and annotate which input ROD + * bindings the record is present, pass, or filtered in in the set attribute in the INFO field. In effect, + * CombineVariants always produces a union of the input VCFs. However, any part of the Venn of the N merged VCFs + * can be exacted using JEXL expressions on the set attribute using SelectVariants. If you want to extract just + * the records in common between two VCFs, you would first run CombineVariants on the two files to generate a single + * VCF and then run SelectVariants to extract the common records with -select 'set == "Intersection"', as worked out + * in the detailed example on the wiki. + * + *

    Input

    + *

    + * One or more variant sets to combine. + *

    + * + *

    Output

    + *

    + * A combined VCF. + *

    + * + *

    Examples

    + *
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T CombineVariants \
    + *   --variant input1.vcf \
    + *   --variant input2.vcf \
    + *   -o output.vcf \
    + *   -genotypeMergeOptions UNIQUIFY
    + *
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T CombineVariants \
    + *   --variant:foo input1.vcf \
    + *   --variant:bar input2.vcf \
    + *   -o output.vcf \
    + *   -genotypeMergeOptions PRIORITIZE
    + *   -priority foo,bar
    + * 
    + * */ @Reference(window=@Window(start=-50,stop=50)) public class CombineVariants extends RodWalker { @@ -69,32 +113,43 @@ public class CombineVariants extends RodWalker { @Output(doc="File to which variants should be written",required=true) protected VCFWriter vcfWriter = null; - // the types of combinations we currently allow - @Argument(shortName="genotypeMergeOptions", doc="How should we merge genotype records for samples shared across the ROD files?", required=false) + @Argument(shortName="genotypeMergeOptions", doc="Determines how we should merge genotype records for samples shared across the ROD files", required=false) public VariantContextUtils.GenotypeMergeType genotypeMergeOption = VariantContextUtils.GenotypeMergeType.PRIORITIZE; - @Argument(shortName="filteredRecordsMergeType", doc="How should we deal with records seen at the same site in the VCF, but with different FILTER fields? KEEP_IF_ANY_UNFILTERED PASSes the record if any record is unfiltered, KEEP_IF_ALL_UNFILTERED requires all records to be unfiltered", required=false) + @Argument(shortName="filteredRecordsMergeType", doc="Determines how we should handle records seen at the same site in the VCF, but with different FILTER fields", required=false) public VariantContextUtils.FilteredRecordMergeType filteredRecordsMergeType = VariantContextUtils.FilteredRecordMergeType.KEEP_IF_ANY_UNFILTERED; - @Argument(fullName="rod_priority_list", shortName="priority", doc="When taking the union of variants containing genotypes: a comma-separated string describing the priority ordering for the genotypes as far as which record gets emitted; a complete priority list MUST be provided", required=false) + /** + * Used when taking the union of variants that contain genotypes. A complete priority list MUST be provided. + */ + @Argument(fullName="rod_priority_list", shortName="priority", doc="A comma-separated string describing the priority ordering for the genotypes as far as which record gets emitted", required=false) public String PRIORITY_STRING = null; @Argument(fullName="printComplexMerges", shortName="printComplexMerges", doc="Print out interesting sites requiring complex compatibility merging", required=false) public boolean printComplexMerges = false; - @Argument(fullName="filteredAreUncalled", shortName="filteredAreUncalled", doc="If true, then filtered VCFs are treated as uncalled, so that filtered set annotation don't appear in the combined VCF", required=false) + @Argument(fullName="filteredAreUncalled", shortName="filteredAreUncalled", doc="If true, then filtered VCFs are treated as uncalled, so that filtered set annotations don't appear in the combined VCF", required=false) public boolean filteredAreUncalled = false; - @Argument(fullName="minimalVCF", shortName="minimalVCF", doc="If true, then the output VCF will contain no INFO or genotype INFO field", required=false) + /** + * Used to generate a sites-only file. + */ + @Argument(fullName="minimalVCF", shortName="minimalVCF", doc="If true, then the output VCF will contain no INFO or genotype FORMAT fields", required=false) public boolean minimalVCF = false; - @Argument(fullName="setKey", shortName="setKey", doc="Key, by default set, in the INFO key=value tag emitted describing which set the combined VCF record came from. Set to null if you don't want the set field emitted.", required=false) + /** + * Set to 'null' if you don't want the set field emitted. + */ + @Argument(fullName="setKey", shortName="setKey", doc="Key used in the INFO key=value tag emitted describing which set the combined VCF record came from", required=false) public String SET_KEY = "set"; - @Argument(fullName="assumeIdenticalSamples", shortName="assumeIdenticalSamples", doc="If true, assume input VCFs have identical sample sets and disjoint calls so that one can simply perform a merge sort to combine the VCFs into one, drastically reducing the runtime.", required=false) + /** + * This option allows the user to perform a simple merge (concatenation) to combine the VCFs, drastically reducing the runtime.. + */ + @Argument(fullName="assumeIdenticalSamples", shortName="assumeIdenticalSamples", doc="If true, assume input VCFs have identical sample sets and disjoint calls", required=false) public boolean ASSUME_IDENTICAL_SAMPLES = false; - @Argument(fullName="minimumN", shortName="minN", doc="Combine variants and output site only if variant is present in at least N input files.", required=false) + @Argument(fullName="minimumN", shortName="minN", doc="Combine variants and output site only if the variant is present in at least N input files.", required=false) public int minimumN = 1; @Hidden 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 16733bc44..b52863c8f 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 @@ -50,54 +50,172 @@ import java.io.PrintStream; import java.util.*; /** - * Takes a VCF file, selects variants based on sample(s) in which it was found and/or on various annotation criteria, - * recompute the value of certain annotations based on the new sample set, and output a new VCF with the results. + * Selects variants from a VCF source. + * + *

    + * Often, a VCF containing many samples and/or variants will need to be subset in order to facilitate certain analyses + * (e.g. comparing and contrasting cases vs. controls; extracting variant or non-variant loci that meet certain + * requirements, displaying just a few samples in a browser like IGV, etc.). SelectVariants can be used for this purpose. + * Given a single VCF file, one or more samples can be extracted from the file (based on a complete sample name or a + * pattern match). Variants can be further selected by specifying criteria for inclusion, i.e. "DP > 1000" (depth of + * coverage greater than 1000x), "AF < 0.25" (sites with allele frequency less than 0.25). These JEXL expressions are + * documented in the Using JEXL expressions section (http://www.broadinstitute.org/gsa/wiki/index.php/Using_JEXL_expressions). + * One can optionally include concordance or discordance tracks for use in selecting overlapping variants. + * + *

    Input

    + *

    + * A variant set to select from. + *

    + * + *

    Output

    + *

    + * A selected VCF. + *

    + * + *

    Examples

    + *
    + * Select two samples out of a VCF with many samples:
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T SelectVariants \
    + *   --variant input.vcf \
    + *   -o output.vcf \
    + *   -sn SAMPLE_A_PARC \
    + *   -sn SAMPLE_B_ACTG
    + *
    + * Select two samples and any sample that matches a regular expression:
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T SelectVariants \
    + *   --variant input.vcf \
    + *   -o output.vcf \
    + *   -sn SAMPLE_1_PARC \
    + *   -sn SAMPLE_1_ACTG \
    + *   -sn 'SAMPLE.+PARC'
    + *
    + * Select any sample that matches a regular expression and sites where the QD annotation is more than 10:
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T SelectVariants \
    + *   --variant input.vcf \
    + *   -o output.vcf \
    + *   -sn 'SAMPLE.+PARC'
    + *   -select "QD > 10.0"
    + *
    + * Select a sample and exclude non-variant loci and filtered loci:
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T SelectVariants \
    + *   --variant input.vcf \
    + *   -o output.vcf \
    + *   -sn SAMPLE_1_ACTG \
    + *   -env \
    + *   -ef
    + *
    + * Select a sample and restrict the output vcf to a set of intervals:
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T SelectVariants \
    + *   --variant input.vcf \
    + *   -o output.vcf \
    + *   -L /path/to/my.interval_list \
    + *   -sn SAMPLE_1_ACTG
    + *
    + * Select all calls missed in my vcf, but present in HapMap (useful to take a look at why these variants weren't called by this dataset):
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T SelectVariants \
    + *   --variant hapmap.vcf \
    + *   --discordance myCalls.vcf
    + *   -o output.vcf \
    + *   -sn mySample
    + *
    + * Select all calls made by both myCalls and hisCalls (useful to take a look at what is consistent between the two callers):
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T SelectVariants \
    + *   --variant myCalls.vcf \
    + *   --concordance hisCalls.vcf
    + *   -o output.vcf \
    + *   -sn mySample
    + *
    + * Generating a VCF of all the variants that are mendelian violations:
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T SelectVariants \
    + *   --variant input.vcf \
    + *   -o output.vcf \
    + *   -SM family.yaml \
    + *   -family NA12891+NA12892=NA12878 \
    + *   -mvq 50
    + *
    + * Creating a sample of exactly 1000 variants randomly chosen with equal probability from the variant VCF:
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T SelectVariants \
    + *   --variant input.vcf \
    + *   -o output.vcf \
    + *   -number 1000
    + *
    + * Creating a set with 50% of the total number of variants in the variant VCF:
    + * java -Xmx2g -jar GenomeAnalysisTK.jar \
    + *   -R ref.fasta \
    + *   -T SelectVariants \
    + *   --variant input.vcf \
    + *   -o output.vcf \
    + *   -fraction 0.5
    + *
    + * 
    + * */ public class SelectVariants extends RodWalker { @ArgumentCollection protected StandardVariantContextInputArgumentCollection variantCollection = new StandardVariantContextInputArgumentCollection(); /** - * A site is considered discordant if there exists some sample in eval that has a non-reference genotype + * A site is considered discordant if there exists some sample in the variant track that has a non-reference genotype * and either the site isn't present in this track, the sample isn't present in this track, * or the sample is called reference in this track. */ - @Input(fullName="discordance", shortName = "disc", doc="Output variants that were not called in this Feature comparison track", required=false) + @Input(fullName="discordance", shortName = "disc", doc="Output variants that were not called in this comparison track", required=false) private RodBinding discordanceTrack; /** * A site is considered concordant if (1) we are not looking for specific samples and there is a variant called - * in both variants and concordance tracks or (2) every sample present in eval is present in the concordance - * track and they have the sample genotype call. + * in both the variant and concordance tracks or (2) every sample present in the variant track is present in the + * concordance track and they have the sample genotype call. */ - @Input(fullName="concordance", shortName = "conc", doc="Output variants that were also called in this Feature comparison track", required=false) + @Input(fullName="concordance", shortName = "conc", doc="Output variants that were also called in this comparison track", required=false) private RodBinding concordanceTrack; @Output(doc="File to which variants should be written",required=true) protected VCFWriter vcfWriter = null; - @Argument(fullName="sample_name", shortName="sn", doc="Sample name to be included in the analysis. Can be specified multiple times.", required=false) + @Argument(fullName="sample_name", shortName="sn", doc="Include genotypes from this sample. Can be specified multiple times", required=false) public Set sampleNames; - @Argument(fullName="sample_expressions", shortName="se", doc="Regular expression to select many samples from the ROD tracks provided. Can be specified multiple times.", required=false) + @Argument(fullName="sample_expressions", shortName="se", doc="Regular expression to select many samples from the ROD tracks provided. Can be specified multiple times", required=false) public Set sampleExpressions; - @Argument(fullName="sample_file", shortName="sf", doc="File containing a list of samples (one per line). Can be specified multiple times", required=false) + @Argument(fullName="sample_file", shortName="sf", doc="File containing a list of samples (one per line) to include. Can be specified multiple times", required=false) public Set sampleFiles; - @Argument(shortName="select", doc="One or more criteria to use when selecting the data. Evaluated *after* the specified samples are extracted and the INFO-field annotations are updated.", required=false) + /** + * Note that thse expressions are evaluated *after* the specified samples are extracted and the INFO field annotations are updated. + */ + @Argument(shortName="select", doc="One or more criteria to use when selecting the data", required=false) public ArrayList SELECT_EXPRESSIONS = new ArrayList(); - @Argument(fullName="excludeNonVariants", shortName="env", doc="Don't include loci found to be non-variant after the subsetting procedure.", required=false) + @Argument(fullName="excludeNonVariants", shortName="env", doc="Don't include loci found to be non-variant after the subsetting procedure", required=false) private boolean EXCLUDE_NON_VARIANTS = false; - @Argument(fullName="excludeFiltered", shortName="ef", doc="Don't include filtered loci in the analysis.", required=false) + @Argument(fullName="excludeFiltered", shortName="ef", doc="Don't include filtered loci in the analysis", required=false) private boolean EXCLUDE_FILTERED = false; - @Argument(fullName="keepOriginalAC", shortName="keepOriginalAC", doc="Don't include filtered loci.", required=false) + @Argument(fullName="keepOriginalAC", shortName="keepOriginalAC", doc="Don't update the AC, AF, or AN values in the INFO field after selecting", required=false) private boolean KEEP_ORIGINAL_CHR_COUNTS = false; @Hidden - @Argument(fullName="keepAFSpectrum", shortName="keepAF", doc="Don't include loci found to be non-variant after the subsetting procedure.", required=false) + @Argument(fullName="keepAFSpectrum", shortName="keepAF", doc="Don't include loci found to be non-variant after the subsetting procedure", required=false) private boolean KEEP_AF_SPECTRUM = false; @Hidden @@ -108,30 +226,43 @@ public class SelectVariants extends RodWalker { @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) private File FAMILY_STRUCTURE_FILE = null; - @Argument(fullName="family_structure", shortName="family", doc="USE YAML FILE INSTEAD (-SM) !!! string formatted as dad+mom=child where these parameters determine which sample names are examined", required=false) + /** + * 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) private String FAMILY_STRUCTURE = ""; - @Argument(fullName="mendelianViolation", shortName="mv", doc="output mendelian violation sites only. Sample metadata information will be taken from YAML file (passed with -SM)", required=false) + /** + * Sample metadata information will be taken from a YAML file (see the -SM argument). + */ + @Argument(fullName="mendelianViolation", shortName="mv", doc="output mendelian violation sites only", required=false) private Boolean MENDELIAN_VIOLATIONS = false; @Argument(fullName="mendelianViolationQualThreshold", shortName="mvq", doc="Minimum genotype QUAL score for each trio member required to accept a site as a violation", required=false) private double MENDELIAN_VIOLATION_QUAL_THRESHOLD = 0; - @Argument(fullName="select_random_number", shortName="number", doc="Selects a number of variants at random from the variant track. Variants are kept in memory to guarantee that n variants will be output, so use it only for a reasonable number of variants. Use select_random_fraction for larger numbers of variants", required=false) + /** + * Variants are kept in memory to guarantee that exactly n variants will be chosen randomly, so use it only for a reasonable + * number of variants. Use --select_random_fraction for larger numbers of variants. + */ + @Argument(fullName="select_random_number", shortName="number", doc="Selects a number of variants at random from the variant track", required=false) private int numRandom = 0; - @Argument(fullName="select_random_fraction", shortName="fraction", doc="Selects a fraction (a number between 0 and 1) of the total variants at random from the variant track. Routine is based on probability, so the final result is not guaranteed to carry the exact fraction. Can be used for large fractions", required=false) + /** + * This routine is based on probability, so the final result is not guaranteed to carry the exact fraction. Can be used for large fractions. + */ + @Argument(fullName="select_random_fraction", shortName="fraction", doc="Selects a fraction (a number between 0 and 1) of the total variants at random from the variant track", required=false) private double fractionRandom = 0; - @Argument(fullName="selectSNPs", shortName="snps", doc="Select only SNPs.", required=false) + @Argument(fullName="selectSNPs", shortName="snps", doc="Select only SNPs from the input file", required=false) private boolean SELECT_SNPS = false; - @Argument(fullName="selectIndels", shortName="indels", doc="Select only Indels.", required=false) + @Argument(fullName="selectIndels", shortName="indels", doc="Select only indels from the input file", required=false) private boolean SELECT_INDELS = false; @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) - private String outMVFile = null; + @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) + private String outMVFile = null; /* Private class used to store the intermediate variants in the integer random selection process */ private class RandomVariantStructure { 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 fa039b42e..834ad0917 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContextUtils.java @@ -345,11 +345,33 @@ public class VariantContextUtils { } public enum GenotypeMergeType { - UNIQUIFY, PRIORITIZE, UNSORTED, REQUIRE_UNIQUE + /** + * Make all sample genotypes unique by file. Each sample shared across RODs gets named sample.ROD. + */ + UNIQUIFY, + /** + * Take genotypes in priority order (see the priority argument). + */ + PRIORITIZE, + /** + * Take the genotypes in any order. + */ + UNSORTED, + /** + * Require that all samples/genotypes be unique between all inputs. + */ + REQUIRE_UNIQUE } public enum FilteredRecordMergeType { - KEEP_IF_ANY_UNFILTERED, KEEP_IF_ALL_UNFILTERED + /** + * Union - leaves the record if any record is unfiltered. + */ + KEEP_IF_ANY_UNFILTERED, + /** + * Requires all records present at site to be unfiltered. VCF files that don't contain the record don't influence this. + */ + KEEP_IF_ALL_UNFILTERED } /** From 710d34633e951ea6261890652401924175af7ba7 Mon Sep 17 00:00:00 2001 From: Andrey Sivachenko Date: Wed, 17 Aug 2011 15:16:23 -0400 Subject: [PATCH 049/138] now the reads that are too long are truly ignored (fix of the fix) --- .../sting/gatk/walkers/indels/SomaticIndelDetectorWalker.java | 1 + 1 file changed, 1 insertion(+) 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 7e8985e03..b2d30235a 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 @@ -482,6 +482,7 @@ public class SomaticIndelDetectorWalker extends ReadWalker { " Read length="+read.getReadLength()+"; cigar="+read.getCigarString()+"; start="+ read.getAlignmentStart()+"; end="+read.getAlignmentEnd()+ "; window start (after trying to accomodate the read)="+normal_context.getStart()+"; window end="+normal_context.getStop()); + return 1; } } From a423546cdd2c759d0d5d0994a9e5ce8d781cff1d Mon Sep 17 00:00:00 2001 From: Andrey Sivachenko Date: Wed, 17 Aug 2011 15:17:31 -0400 Subject: [PATCH 050/138] fix: RefSeq contains records with zero coding length and the refsec codec/feature used to crash on those; now such records are ignored, with warning printed (once) --- .../refdata/features/refseq/RefSeqCodec.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/refseq/RefSeqCodec.java b/public/java/src/org/broadinstitute/sting/gatk/refdata/features/refseq/RefSeqCodec.java index 461aab9a5..89ee65532 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/refseq/RefSeqCodec.java +++ b/public/java/src/org/broadinstitute/sting/gatk/refdata/features/refseq/RefSeqCodec.java @@ -1,5 +1,6 @@ package org.broadinstitute.sting.gatk.refdata.features.refseq; +import org.apache.commons.io.filefilter.FalseFileFilter; import org.broad.tribble.Feature; import org.broad.tribble.TribbleException; import org.broad.tribble.readers.LineReader; @@ -20,7 +21,7 @@ public class RefSeqCodec implements ReferenceDependentFeatureCodec Date: Wed, 17 Aug 2011 15:46:31 -0400 Subject: [PATCH 051/138] Updated Beagle walker for gatkdocs format. Pushed unsupported, undocumented arguments to @Hidden --- .../beagle/BeagleOutputToVCFWalker.java | 37 +++++++++++---- .../beagle/ProduceBeagleInputWalker.java | 45 ++++++++++++++++--- 2 files changed, 67 insertions(+), 15 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 40e6748ed..aca176bc2 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 @@ -48,6 +48,31 @@ import static java.lang.Math.log10; /** * Takes files produced by Beagle imputation engine and creates a vcf with modified annotations. + * + *

    This walker is intended to be run after Beagle has successfully executed. The full calling sequence for using Beagle along with the GATK is:

    + * + *

    1. Run ProduceBeagleInputWalker.

    + *

    2. Run Beagle

    + *

    3. Uncompress output files

    + *

    4. Run BeagleOutputToVCFWalker.

    + * + * + * Note that this walker requires all input files produced by Beagle. + * + * + *

    Example

    + *
    + *     java -Xmx4000m -jar dist/GenomeAnalysisTK.jar \
    + *      -R reffile.fasta -T BeagleOutputToVCF \
    + *      -B:variant,VCF input_vcf.vcf \
    + *      -B:beagleR2,BEAGLE /myrun.beagle_output.r2 \
    + *      -B:beaglePhased,BEAGLE /myrun.beagle_output.phased \
    + *      -B:beagleProbs,BEAGLE /myrun.beagle_output.gprobs \
    + *      --out output_vcf.vcf
    + *      
    + +

    Note that Beagle produces some of these files compressed as .gz, so gunzip must be run on them before walker is run in order to decompress them

    + */ public class BeagleOutputToVCFWalker extends RodWalker { @@ -57,22 +82,18 @@ public class BeagleOutputToVCFWalker extends RodWalker { @Input(fullName="comp", shortName = "comp", doc="Comparison VCF file", required=false) public RodBinding comp; - @Input(fullName="beagleR2", shortName = "beagleR2", doc="VCF file", required=true) + @Input(fullName="beagleR2", shortName = "beagleR2", doc="Beagle-produced .r2 file containing R^2 values for all markers", required=true) public RodBinding beagleR2; - @Input(fullName="beagleProbs", shortName = "beagleProbs", doc="VCF file", required=true) + @Input(fullName="beagleProbs", shortName = "beagleProbs", doc="Beagle-produced .probs file containing posterior genotype probabilities", required=true) public RodBinding beagleProbs; - @Input(fullName="beaglePhased", shortName = "beaglePhased", doc="VCF file", required=true) + @Input(fullName="beaglePhased", shortName = "beaglePhased", doc="Beagle-produced .phased file containing phased genotypes", required=true) public RodBinding beaglePhased; - @Output(doc="File to which variants should be written",required=true) + @Output(doc="VCF File to which variants should be written",required=true) protected VCFWriter vcfWriter = null; - @Argument(fullName="output_file", shortName="output", doc="Please use --out instead" ,required=false) - @Deprecated - protected String oldOutputArg; - @Argument(fullName="dont_mark_monomorphic_sites_as_filtered", shortName="keep_monomorphic", doc="If provided, we won't filter sites that beagle tags as monomorphic. Useful for imputing a sample's genotypes from a reference panel" ,required=false) public boolean DONT_FILTER_MONOMORPHIC_SITES = false; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/beagle/ProduceBeagleInputWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/beagle/ProduceBeagleInputWalker.java index c1508cf83..6ac817555 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/beagle/ProduceBeagleInputWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/beagle/ProduceBeagleInputWalker.java @@ -48,19 +48,45 @@ import java.io.PrintStream; import java.util.*; /** - * Produces an input file to Beagle imputation engine, listing genotype likelihoods for each sample in input variant file + * Converts the input VCF into a format accepted by the Beagle imputation/analysis program. + *

    + * + *

    Input

    + *

    + * A VCF with variants to convert to Beagle format + *

    + * + *

    Outputs

    + *

    + * A single text file which can be fed to Beagle + *

    + *

    + * Optional: A file with a list of markers + *

    + * + *

    Examples

    + *
    + *     java -Xmx2g -jar dist/GenomeAnalysisTK.jar -L 20 \
    + *      -R reffile.fasta -T ProduceBeagleInput \
    + *      -B:variant,VCF path_to_input_vcf/inputvcf.vcf -o path_to_beagle_output/beagle_output
    + * 
    + * */ + public class ProduceBeagleInputWalker extends RodWalker { @ArgumentCollection protected StandardVariantContextInputArgumentCollection variantCollection = new StandardVariantContextInputArgumentCollection(); - @Input(fullName="validation", shortName = "validation", doc="Input VCF file", required=false) + @Hidden + @Input(fullName="validation", shortName = "validation", doc="Validation VCF file", required=false) public RodBinding validation; + @Output(doc="File to which BEAGLE input should be written",required=true) protected PrintStream beagleWriter = null; - @Output(doc="File to which BEAGLE markers should be written", shortName="markers", fullName = "markers", required = false) + @Hidden + @Output(doc="File to which BEAGLE markers should be written", shortName="markers", fullName = "markers", required = false) protected PrintStream markers = null; int markerCounter = 1; @@ -73,14 +99,19 @@ public class ProduceBeagleInputWalker extends RodWalker { @Argument(doc="VQSqual key", shortName = "vqskey", required=false) protected String VQSLOD_KEY = "VQSqual"; - @Argument(fullName = "inserted_nocall_rate", shortName = "nc_rate", doc = "Rate (0-1) at which genotype no-calls will be randomly inserted, for testing", required = false) + @Hidden + @Argument(fullName = "inserted_nocall_rate", shortName = "nc_rate", doc = "Rate (0-1) at which genotype no-calls will be randomly inserted, for testing", required = false) public double insertedNoCallRate = 0; - @Argument(fullName = "validation_genotype_ptrue", shortName = "valp", doc = "Flat probability to assign to validation genotypes. Will override GL field.", required = false) + @Hidden + @Argument(fullName = "validation_genotype_ptrue", shortName = "valp", doc = "Flat probability to assign to validation genotypes. Will override GL field.", required = false) public double validationPrior = -1.0; - @Argument(fullName = "validation_bootstrap", shortName = "bs", doc = "Proportion of records to be used in bootstrap set", required = false) + @Hidden + @Argument(fullName = "validation_bootstrap", shortName = "bs", doc = "Proportion of records to be used in bootstrap set", required = false) public double bootstrap = 0.0; - @Argument(fullName = "bootstrap_vcf",shortName = "bvcf", doc = "Output a VCF with the records used for bootstrapping filtered out", required = false) + @Hidden + @Argument(fullName = "bootstrap_vcf",shortName = "bvcf", doc = "Output a VCF with the records used for bootstrapping filtered out", required = false) VCFWriter bootstrapVCFOutput = null; + @Argument(fullName = "checkIsMaleOnChrX", shortName = "checkIsMaleOnChrX", doc = "Set to true when Beagle-ing chrX and want to ensure male samples don't have heterozygous calls.", required = false) public boolean CHECK_IS_MALE_ON_CHR_X = false; From 53006da9a55a58f1d7f7f66be3b0466848a6f5fc Mon Sep 17 00:00:00 2001 From: David Roazen Date: Wed, 17 Aug 2011 14:55:32 -0400 Subject: [PATCH 052/138] Improved descriptions for the SnpEff annotations in the VCF header (based on Eric's feedback). --- .../sting/gatk/walkers/annotator/SnpEff.java | 26 +++++++++---------- .../VariantAnnotatorIntegrationTest.java | 2 +- 2 files changed, 14 insertions(+), 14 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 fc5014885..350c683c2 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 @@ -161,19 +161,19 @@ public class SnpEff extends InfoFieldAnnotation implements ExperimentalAnnotatio public List getDescriptions() { return Arrays.asList( - new VCFInfoHeaderLine(GENE_ID_KEY, 1, VCFHeaderLineType.String, "Gene ID"), - new VCFInfoHeaderLine(GENE_NAME_KEY, 1, VCFHeaderLineType.String, "Gene name"), - new VCFInfoHeaderLine(TRANSCRIPT_ID_KEY, 1, VCFHeaderLineType.String, "Transcript ID"), - new VCFInfoHeaderLine(EXON_ID_KEY, 1, VCFHeaderLineType.String, "Exon ID"), - new VCFInfoHeaderLine(EXON_RANK_KEY, 1, VCFHeaderLineType.Integer, "Exon rank"), - new VCFInfoHeaderLine(WITHIN_NON_CODING_GENE_KEY, 0, VCFHeaderLineType.Flag, "If present, gene is non-coding"), - new VCFInfoHeaderLine(EFFECT_KEY, 1, VCFHeaderLineType.String, "One of the most high-impact effects across all transcripts at this site"), - new VCFInfoHeaderLine(EFFECT_IMPACT_KEY, 1, VCFHeaderLineType.String, "Impact of the effect " + Arrays.toString(SnpEffConstants.EffectImpact.values())), - new VCFInfoHeaderLine(EFFECT_EXTRA_INFORMATION_KEY, 1, VCFHeaderLineType.String, "Additional information about the effect"), - new VCFInfoHeaderLine(OLD_NEW_AA_KEY, 1, VCFHeaderLineType.String, "Old/New amino acid"), - new VCFInfoHeaderLine(OLD_NEW_CODON_KEY, 1, VCFHeaderLineType.String, "Old/New codon"), - new VCFInfoHeaderLine(CODON_NUM_KEY, 1, VCFHeaderLineType.Integer, "Codon number"), - new VCFInfoHeaderLine(CDS_SIZE_KEY, 1, VCFHeaderLineType.Integer, "CDS size") + 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") ); } } 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 f54bfa40c..832079807 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 @@ -133,7 +133,7 @@ public class VariantAnnotatorIntegrationTest extends WalkerTest { validationDataLocation + "1000G.exomes.vcf --snpEffFile " + validationDataLocation + "snpEff_1.9.6_1000G.exomes.vcf_hg37.61.out -L 1:26,000,000-26,500,000", 1, - Arrays.asList("c08648a078368c80530bff004b3157f1") + Arrays.asList("03eae1dab19a9358250890594bf53607") ); executeTest("Testing SnpEff annotations", spec); } From d170187896bc58200a2e827373046b03c4c260cd Mon Sep 17 00:00:00 2001 From: Matt Hanna Date: Wed, 17 Aug 2011 16:16:05 -0400 Subject: [PATCH 053/138] Disable optimization that increases marginal speed of the GATK slightly but can produce data loss in a narrow corner case where the BGZF block(s) locations and offsets in the last index bucket of contig n overlap exactly with the BGZF block locations and offset in the last index bucket of contig n+1. A proper fix that keeps the optimization has already been introduced into unstable, but disabling the optimization is a low risk way to make sure that users of stable experience no data loss. --- .../gatk/datasources/reads/LowMemoryIntervalSharder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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..198f7d7d3 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,8 +59,8 @@ public class LowMemoryIntervalSharder implements Iterator { */ public FilePointer next() { FilePointer current = wrappedIterator.next(); - while(wrappedIterator.hasNext() && current.minus(wrappedIterator.peek()) == 0) - current = current.combine(parser,wrappedIterator.next()); + //while(wrappedIterator.hasNext() && current.minus(wrappedIterator.peek()) == 0) + // current = current.combine(parser,wrappedIterator.next()); return current; } From d59e6ed274c555fb4e8d8198449ec0a6eb90de41 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Wed, 17 Aug 2011 16:22:07 -0400 Subject: [PATCH 054/138] Fix for RefSeqCodec bug and better error messages -- RefSeqCodec bug: getFeatureClass() returned RefSeqCodec.class, not RefSeqFeature.class. Really should change this in Tribble to require Class to get compile time type checking -- Better error messages that actually list the available tribble types, when there's a type error --- .../commandline/ArgumentTypeDescriptor.java | 14 +- .../refdata/features/refseq/RefSeqCodec.java | 220 +++++++++--------- .../sting/utils/text/ListFileUtils.java | 4 +- 3 files changed, 119 insertions(+), 119 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java b/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java index d1d4ff914..02af884a2 100644 --- a/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java +++ b/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java @@ -373,16 +373,16 @@ class RodBindingArgumentTypeDescriptor extends ArgumentTypeDescriptor { if ( featureDescriptor != null ) { tribbleType = featureDescriptor.getName(); logger.warn("Dynamically determined type of " + file + " to be " + tribbleType); + } else { + throw new UserException.CommandLineException( + String.format("No tribble type was provided on the command line and the type of the file could not be determined dynamically. " + + "Please add an explicit type tag :TYPE listing the correct type from among the supported types: %s", + manager.userFriendlyListOfAvailableFeatures())); } } } } - if ( tribbleType == null ) // error handling - throw new UserException.CommandLineException( - String.format("Could not parse argument %s with value %s", - defaultDefinition.fullName, value)); - Constructor ctor = (makeRawTypeIfNecessary(type)).getConstructor(Class.class, String.class, String.class, String.class, Tags.class); Class parameterType = getParameterizedTypeClass(type); RodBinding result = (RodBinding)ctor.newInstance(parameterType, name, value, tribbleType, tags); @@ -395,8 +395,8 @@ class RodBindingArgumentTypeDescriptor extends ArgumentTypeDescriptor { value, source.field.getName())); } catch (Exception e) { throw new UserException.CommandLineException( - String.format("Failed to parse value %s for argument %s.", - value, source.field.getName())); + String.format("Failed to parse value %s for argument %s. Message: %s", + value, source.field.getName(), e.getMessage())); } } diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/refseq/RefSeqCodec.java b/public/java/src/org/broadinstitute/sting/gatk/refdata/features/refseq/RefSeqCodec.java index 89ee65532..cec40b5bd 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/refseq/RefSeqCodec.java +++ b/public/java/src/org/broadinstitute/sting/gatk/refdata/features/refseq/RefSeqCodec.java @@ -1,110 +1,110 @@ -package org.broadinstitute.sting.gatk.refdata.features.refseq; - -import org.apache.commons.io.filefilter.FalseFileFilter; -import org.broad.tribble.Feature; -import org.broad.tribble.TribbleException; -import org.broad.tribble.readers.LineReader; -import org.broadinstitute.sting.gatk.refdata.ReferenceDependentFeatureCodec; -import org.broadinstitute.sting.utils.GenomeLoc; -import org.broadinstitute.sting.utils.GenomeLocParser; -import org.broadinstitute.sting.utils.Utils; -import org.broadinstitute.sting.utils.exceptions.UserException; - -import java.util.ArrayList; - -/** - * the ref seq codec - */ -public class RefSeqCodec implements ReferenceDependentFeatureCodec { - - /** - * The parser to use when resolving genome-wide locations. - */ - private GenomeLocParser genomeLocParser; - private boolean zero_coding_length_user_warned = false; - /** - * Set the parser to use when resolving genetic data. - * @param genomeLocParser The supplied parser. - */ - @Override - public void setGenomeLocParser(GenomeLocParser genomeLocParser) { - this.genomeLocParser = genomeLocParser; - } - - @Override - public Feature decodeLoc(String line) { - if (line.startsWith("#")) return null; - String fields[] = line.split("\t"); - if (fields.length < 3) throw new TribbleException("RefSeq (decodeLoc) : Unable to parse line -> " + line + ", we expected at least 3 columns, we saw " + fields.length); - String contig_name = fields[2]; - try { - return new RefSeqFeature(genomeLocParser.createGenomeLoc(contig_name, Integer.parseInt(fields[4])+1, Integer.parseInt(fields[5]))); - } catch ( UserException.MalformedGenomeLoc e ) { - Utils.warnUser("RefSeq file is potentially incorrect, as some transcripts or exons have a negative length ("+fields[2]+")"); - return null; - } - } - - /** Fills this object from a text line in RefSeq (UCSC) text dump file */ - @Override - public RefSeqFeature decode(String line) { - if (line.startsWith("#")) return null; - String fields[] = line.split("\t"); - - // we reference postion 15 in the split array below, make sure we have at least that many columns - if (fields.length < 16) throw new TribbleException("RefSeq (decode) : Unable to parse line -> " + line + ", we expected at least 16 columns, we saw " + fields.length); - String contig_name = fields[2]; - RefSeqFeature feature = new RefSeqFeature(genomeLocParser.createGenomeLoc(contig_name, Integer.parseInt(fields[4])+1, Integer.parseInt(fields[5]))); - - feature.setTranscript_id(fields[1]); - if ( fields[3].length()==1 && fields[3].charAt(0)=='+') feature.setStrand(1); - else if ( fields[3].length()==1 && fields[3].charAt(0)=='-') feature.setStrand(-1); - else throw new UserException.MalformedFile("Expected strand symbol (+/-), found: "+fields[3] + " for line=" + line); - - int coding_start = Integer.parseInt(fields[6])+1; - int coding_stop = Integer.parseInt(fields[7]); - - if ( coding_start > coding_stop ) { - if ( ! zero_coding_length_user_warned ) { - Utils.warnUser("RefSeq file contains transcripts with zero coding length. "+ - "Such transcripts will be ignored (this warning is printed only once)"); - zero_coding_length_user_warned = true; - } - return null; - } - - feature.setTranscript_interval(genomeLocParser.createGenomeLoc(contig_name, Integer.parseInt(fields[4])+1, Integer.parseInt(fields[5]))); - feature.setTranscript_coding_interval(genomeLocParser.createGenomeLoc(contig_name, coding_start, coding_stop)); - feature.setGene_name(fields[12]); - String[] exon_starts = fields[9].split(","); - String[] exon_stops = fields[10].split(","); - String[] eframes = fields[15].split(","); - - if ( exon_starts.length != exon_stops.length ) - throw new UserException.MalformedFile("Data format error: numbers of exon start and stop positions differ for line=" + line); - if ( exon_starts.length != eframes.length ) - throw new UserException.MalformedFile("Data format error: numbers of exons and exon frameshifts differ for line=" + line); - - ArrayList exons = new ArrayList(exon_starts.length); - ArrayList exon_frames = new ArrayList(eframes.length); - - for ( int i = 0 ; i < exon_starts.length ; i++ ) { - exons.add(genomeLocParser.createGenomeLoc(contig_name, Integer.parseInt(exon_starts[i])+1, Integer.parseInt(exon_stops[i]) ) ); - exon_frames.add(Integer.decode(eframes[i])); - } - - feature.setExons(exons); - feature.setExon_frames(exon_frames); - return feature; - } - - @Override - public Object readHeader(LineReader reader) { - return null; - } - - @Override - public Class getFeatureType() { - return RefSeqCodec.class; - } -} +package org.broadinstitute.sting.gatk.refdata.features.refseq; + +import org.apache.commons.io.filefilter.FalseFileFilter; +import org.broad.tribble.Feature; +import org.broad.tribble.TribbleException; +import org.broad.tribble.readers.LineReader; +import org.broadinstitute.sting.gatk.refdata.ReferenceDependentFeatureCodec; +import org.broadinstitute.sting.utils.GenomeLoc; +import org.broadinstitute.sting.utils.GenomeLocParser; +import org.broadinstitute.sting.utils.Utils; +import org.broadinstitute.sting.utils.exceptions.UserException; + +import java.util.ArrayList; + +/** + * the ref seq codec + */ +public class RefSeqCodec implements ReferenceDependentFeatureCodec { + + /** + * The parser to use when resolving genome-wide locations. + */ + private GenomeLocParser genomeLocParser; + private boolean zero_coding_length_user_warned = false; + /** + * Set the parser to use when resolving genetic data. + * @param genomeLocParser The supplied parser. + */ + @Override + public void setGenomeLocParser(GenomeLocParser genomeLocParser) { + this.genomeLocParser = genomeLocParser; + } + + @Override + public Feature decodeLoc(String line) { + if (line.startsWith("#")) return null; + String fields[] = line.split("\t"); + if (fields.length < 3) throw new TribbleException("RefSeq (decodeLoc) : Unable to parse line -> " + line + ", we expected at least 3 columns, we saw " + fields.length); + String contig_name = fields[2]; + try { + return new RefSeqFeature(genomeLocParser.createGenomeLoc(contig_name, Integer.parseInt(fields[4])+1, Integer.parseInt(fields[5]))); + } catch ( UserException.MalformedGenomeLoc e ) { + Utils.warnUser("RefSeq file is potentially incorrect, as some transcripts or exons have a negative length ("+fields[2]+")"); + return null; + } + } + + /** Fills this object from a text line in RefSeq (UCSC) text dump file */ + @Override + public RefSeqFeature decode(String line) { + if (line.startsWith("#")) return null; + String fields[] = line.split("\t"); + + // we reference postion 15 in the split array below, make sure we have at least that many columns + if (fields.length < 16) throw new TribbleException("RefSeq (decode) : Unable to parse line -> " + line + ", we expected at least 16 columns, we saw " + fields.length); + String contig_name = fields[2]; + RefSeqFeature feature = new RefSeqFeature(genomeLocParser.createGenomeLoc(contig_name, Integer.parseInt(fields[4])+1, Integer.parseInt(fields[5]))); + + feature.setTranscript_id(fields[1]); + if ( fields[3].length()==1 && fields[3].charAt(0)=='+') feature.setStrand(1); + else if ( fields[3].length()==1 && fields[3].charAt(0)=='-') feature.setStrand(-1); + else throw new UserException.MalformedFile("Expected strand symbol (+/-), found: "+fields[3] + " for line=" + line); + + int coding_start = Integer.parseInt(fields[6])+1; + int coding_stop = Integer.parseInt(fields[7]); + + if ( coding_start > coding_stop ) { + if ( ! zero_coding_length_user_warned ) { + Utils.warnUser("RefSeq file contains transcripts with zero coding length. "+ + "Such transcripts will be ignored (this warning is printed only once)"); + zero_coding_length_user_warned = true; + } + return null; + } + + feature.setTranscript_interval(genomeLocParser.createGenomeLoc(contig_name, Integer.parseInt(fields[4])+1, Integer.parseInt(fields[5]))); + feature.setTranscript_coding_interval(genomeLocParser.createGenomeLoc(contig_name, coding_start, coding_stop)); + feature.setGene_name(fields[12]); + String[] exon_starts = fields[9].split(","); + String[] exon_stops = fields[10].split(","); + String[] eframes = fields[15].split(","); + + if ( exon_starts.length != exon_stops.length ) + throw new UserException.MalformedFile("Data format error: numbers of exon start and stop positions differ for line=" + line); + if ( exon_starts.length != eframes.length ) + throw new UserException.MalformedFile("Data format error: numbers of exons and exon frameshifts differ for line=" + line); + + ArrayList exons = new ArrayList(exon_starts.length); + ArrayList exon_frames = new ArrayList(eframes.length); + + for ( int i = 0 ; i < exon_starts.length ; i++ ) { + exons.add(genomeLocParser.createGenomeLoc(contig_name, Integer.parseInt(exon_starts[i])+1, Integer.parseInt(exon_stops[i]) ) ); + exon_frames.add(Integer.decode(eframes[i])); + } + + feature.setExons(exons); + feature.setExon_frames(exon_frames); + return feature; + } + + @Override + public Object readHeader(LineReader reader) { + return null; + } + + @Override + public Class getFeatureType() { + return RefSeqFeature.class; + } +} diff --git a/public/java/src/org/broadinstitute/sting/utils/text/ListFileUtils.java b/public/java/src/org/broadinstitute/sting/utils/text/ListFileUtils.java index 61d53679a..b0e25e55b 100644 --- a/public/java/src/org/broadinstitute/sting/utils/text/ListFileUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/text/ListFileUtils.java @@ -160,8 +160,8 @@ public class ListFileUtils { rodBinding.getName(), rodBinding.getTribbleType(), builderForValidation.userFriendlyListOfAvailableFeatures())); if ( ! rodBinding.getType().isAssignableFrom(descriptor.getFeatureClass()) ) throw new UserException.BadArgumentValue(rodBinding.getName(), - String.format("Field %s expected type %s, but the type of the input file provided on the command line was %s. Please make sure that you have provided the correct file type and/or that you are not binding your rod to a name matching one of the available types.", - rodBinding.getName(), rodBinding.getType(), descriptor.getName())); + String.format("Field %s expected type %s, but the type of the input file provided on the command line was %s producing %s. Please make sure that you have provided the correct file type and/or that you are not binding your rod to a name matching one of the available types.", + rodBinding.getName(), rodBinding.getType(), descriptor.getName(), descriptor.getFeatureClass())); rodBindings.add(triplet); From c193f52e5de69e8dd837f42aae299a31ed5b016e Mon Sep 17 00:00:00 2001 From: Guillermo del Angel Date: Wed, 17 Aug 2011 16:29:45 -0400 Subject: [PATCH 056/138] Fixed up examples: pasting from wiki still had old rod syntax --- .../gatk/walkers/beagle/BeagleOutputToVCFWalker.java | 10 +++++----- .../gatk/walkers/beagle/ProduceBeagleInputWalker.java | 2 +- 2 files changed, 6 insertions(+), 6 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 aca176bc2..51fe543df 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 @@ -64,11 +64,11 @@ import static java.lang.Math.log10; *
      *     java -Xmx4000m -jar dist/GenomeAnalysisTK.jar \
      *      -R reffile.fasta -T BeagleOutputToVCF \
    - *      -B:variant,VCF input_vcf.vcf \
    - *      -B:beagleR2,BEAGLE /myrun.beagle_output.r2 \
    - *      -B:beaglePhased,BEAGLE /myrun.beagle_output.phased \
    - *      -B:beagleProbs,BEAGLE /myrun.beagle_output.gprobs \
    - *      --out output_vcf.vcf
    + *      -V input_vcf.vcf \
    + *      -beagleR2:BEAGLE /myrun.beagle_output.r2 \
    + *      -beaglePhased:BEAGLE /myrun.beagle_output.phased \
    + *      -beagleProbs:BEAGLE /myrun.beagle_output.gprobs \
    + *      -o output_vcf.vcf
      *      

    Note that Beagle produces some of these files compressed as .gz, so gunzip must be run on them before walker is run in order to decompress them

    diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/beagle/ProduceBeagleInputWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/beagle/ProduceBeagleInputWalker.java index 6ac817555..07793fd7b 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/beagle/ProduceBeagleInputWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/beagle/ProduceBeagleInputWalker.java @@ -68,7 +68,7 @@ import java.util.*; *
      *     java -Xmx2g -jar dist/GenomeAnalysisTK.jar -L 20 \
      *      -R reffile.fasta -T ProduceBeagleInput \
    - *      -B:variant,VCF path_to_input_vcf/inputvcf.vcf -o path_to_beagle_output/beagle_output
    + *      -V path_to_input_vcf/inputvcf.vcf -o path_to_beagle_output/beagle_output
      * 
    * */ From 2e35592295267f9832647a43658e6972a290289c Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Wed, 17 Aug 2011 16:32:01 -0400 Subject: [PATCH 057/138] GATKDocs for CallableLoci --- .../walkers/coverage/CallableLociWalker.java | 185 +++++++++++++++--- 1 file changed, 163 insertions(+), 22 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/CallableLociWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/CallableLociWalker.java index 90e6fcd77..3046ef925 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/CallableLociWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/CallableLociWalker.java @@ -42,50 +42,190 @@ import java.io.PrintStream; /** * Emits a data file containing information about callable, uncallable, poorly mapped, and other parts of the genome * - * @Author depristo - * @Date May 7, 2010 + *

    + * A very common question about a NGS set of reads is what areas of the genome are considered callable. The system + * considers the coverage at each locus and emits either a per base state or a summary interval BED file that + * partitions the genomic intervals into the following callable states: + *

    + *
    REF_N
    + *
    the reference base was an N, which is not considered callable the GATK
    + *
    CALLABLE
    + *
    the base satisfied the min. depth for calling but had less than maxDepth to avoid having EXCESSIVE_COVERAGE
    + *
    NO_COVERAGE
    + *
    absolutely no reads were seen at this locus, regardless of the filtering parameters
    + *
    LOW_COVERAGE
    + *
    there were less than min. depth bases at the locus, after applying filters
    + *
    EXCESSIVE_COVERAGE
    + *
    more than -maxDepth read at the locus, indicating some sort of mapping problem
    + *
    POOR_MAPPING_QUALITY
    + *
    more than --maxFractionOfReadsWithLowMAPQ at the locus, indicating a poor mapping quality of the reads
    + *
    + *

    + * + *

    Input

    + *

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

    + * + *

    Output

    + *

    + *

      + *
    • -o: a OutputFormatted (recommended BED) file with the callable status covering each base
    • + *
    • -summary: a table of callable status x count of all examined bases
    • + *
    + *

    + * + *

    Examples

    + *
    + *     -T CallableLociWalker \
    + *     -I my.bam \
    + *     -summary my.summary \
    + *     -o my.bed
    + * 
    + * + * would produce a BED file (my.bed) that looks like: + * + *
    + *     20 10000000 10000864 CALLABLE
    + *     20 10000865 10000985 POOR_MAPPING_QUALITY
    + *     20 10000986 10001138 CALLABLE
    + *     20 10001139 10001254 POOR_MAPPING_QUALITY
    + *     20 10001255 10012255 CALLABLE
    + *     20 10012256 10012259 POOR_MAPPING_QUALITY
    + *     20 10012260 10012263 CALLABLE
    + *     20 10012264 10012328 POOR_MAPPING_QUALITY
    + *     20 10012329 10012550 CALLABLE
    + *     20 10012551 10012551 LOW_COVERAGE
    + *     20 10012552 10012554 CALLABLE
    + *     20 10012555 10012557 LOW_COVERAGE
    + *     20 10012558 10012558 CALLABLE
    + *     et cetera...
    + * 
    + * as well as a summary table that looks like: + * + *
    + *                        state nBases
    + *                        REF_N 0
    + *                     CALLABLE 996046
    + *                  NO_COVERAGE 121
    + *                 LOW_COVERAGE 928
    + *           EXCESSIVE_COVERAGE 0
    + *         POOR_MAPPING_QUALITY 2906
    + * 
    + * + * @author Mark DePristo + * @since May 7, 2010 */ @By(DataSource.REFERENCE) public class CallableLociWalker extends LocusWalker { @Output PrintStream out; - @Argument(fullName = "maxLowMAPQ", shortName = "mlmq", doc = "Maximum value for MAPQ to be considered a problematic mapped read. The gap between this value and mmq are reads that are not sufficiently well mapped for calling but aren't indicative of mapping problems.", required = false) + /** + * Callable loci summary counts (see outputs) will be written to this file. + */ + @Output(fullName = "summary", shortName = "summary", doc = "Name of file for output summary", required = true) + File summaryFile; + + /** + * The gap between this value and mmq are reads that are not sufficiently well mapped for calling but + * aren't indicative of mapping problems. For example, if maxLowMAPQ = 1 and mmq = 20, then reads with + * MAPQ == 0 are poorly mapped, MAPQ >= 20 are considered as contributing to calling, where + * reads with MAPQ >= 1 and < 20 are not bad in and of themselves but aren't sufficiently good to contribute to + * calling. In effect this reads are invisible, driving the base to the NO_ or LOW_COVERAGE states + */ + @Argument(fullName = "maxLowMAPQ", shortName = "mlmq", doc = "Maximum value for MAPQ to be considered a problematic mapped read.", required = false) byte maxLowMAPQ = 1; - @Argument(fullName = "minMappingQuality", shortName = "mmq", doc = "Minimum mapping quality of reads to count towards depth. Defaults to 50.", required = false) + /** + * Reads with MAPQ > minMappingQuality are treated as usable for variation detection, contributing to the CALLABLE + * state. + */ + @Argument(fullName = "minMappingQuality", shortName = "mmq", doc = "Minimum mapping quality of reads to count towards depth.", required = false) byte minMappingQuality = 10; - @Argument(fullName = "minBaseQuality", shortName = "mbq", doc = "Minimum quality of bases to count towards depth. Defaults to 20.", required = false) + /** + * Bases with less than minBaseQuality are viewed as not sufficiently high quality to contribute to the CALLABLE state + */ + @Argument(fullName = "minBaseQuality", shortName = "mbq", doc = "Minimum quality of bases to count towards depth.", required = false) byte minBaseQuality = 20; + /** + * If the number of QC+ bases (on reads with MAPQ > minMappingQuality and with base quality > minBaseQuality) exceeds this + * value and is less than maxDepth the site is considered CALLABLE. + */ @Argument(fullName = "minDepth", shortName = "minDepth", doc = "Minimum QC+ read depth before a locus is considered callable", required = false) int minDepth = 4; + /** + * If the QC+ depth exceeds this value the site is considered to have EXCESSIVE_DEPTH + */ @Argument(fullName = "maxDepth", shortName = "maxDepth", doc = "Maximum read depth before a locus is considered poorly mapped", required = false) int maxDepth = -1; + /** + * We don't want to consider a site as POOR_MAPPING_QUALITY just because it has two reads, and one is MAPQ. We + * won't assign a site to the POOR_MAPPING_QUALITY state unless there are at least minDepthForLowMAPQ reads + * covering the site. + */ @Argument(fullName = "minDepthForLowMAPQ", shortName = "mdflmq", doc = "Minimum read depth before a locus is considered a potential candidate for poorly mapped", required = false) int minDepthLowMAPQ = 10; - @Argument(fullName = "maxFractionOfReadsWithLowMAPQ", shortName = "frlmq", doc = "Maximum read depth before a locus is considered poorly mapped", required = false) + /** + * If the number of reads at this site is greater than minDepthForLowMAPQ and the fraction of reads with low mapping quality + * exceeds this fraction then the site has POOR_MAPPING_QUALITY. + */ + @Argument(fullName = "maxFractionOfReadsWithLowMAPQ", shortName = "frlmq", doc = "If the fraction of reads at a base with low mapping quality exceeds this value, the site may be poorly mapped", required = false) double maxLowMAPQFraction = 0.1; - @Argument(fullName = "format", shortName = "format", doc = "Output format for the system: either BED or STATE_PER_BASE", required = false) + /** + * The output of this walker will be written in this format. The recommended option is BED. + */ + @Argument(fullName = "format", shortName = "format", doc = "Output format", required = false) OutputFormat outputFormat; - @Argument(fullName = "summary", shortName = "summary", doc = "Name of file for output summary", required = true) - File summaryFile; + public enum OutputFormat { + /** + * The output will be written as a BED file. There's a BED element for each + * continuous run of callable states (i.e., CALLABLE, REF_N, etc). This is the recommended + * format + */ + BED, - public enum OutputFormat { BED, STATE_PER_BASE } + /** + * Emit chr start stop state quads for each base. Produces a potentially disasterously + * large amount of output. + */ + STATE_PER_BASE + } + + public enum CalledState { + /** the reference base was an N, which is not considered callable the GATK */ + REF_N, + /** the base satisfied the min. depth for calling but had less than maxDepth to avoid having EXCESSIVE_COVERAGE */ + CALLABLE, + /** absolutely no reads were seen at this locus, regardless of the filtering parameters */ + NO_COVERAGE, + /** there were less than min. depth bases at the locus, after applying filters */ + LOW_COVERAGE, + /** more than -maxDepth read at the locus, indicating some sort of mapping problem */ + EXCESSIVE_COVERAGE, + /** more than --maxFractionOfReadsWithLowMAPQ at the locus, indicating a poor mapping quality of the reads */ + POOR_MAPPING_QUALITY + } //////////////////////////////////////////////////////////////////////////////////// // STANDARD WALKER METHODS //////////////////////////////////////////////////////////////////////////////////// + @Override public boolean includeReadsWithDeletionAtLoci() { return true; } + @Override public void initialize() { + if ( getToolkit().getSamples().size() > 1 ) + throw new UserException.BadArgumentValue("-I", "CallableLoci only works for a single sample, but multiple samples were found in the provided BAM files: " + getToolkit().getSamples()); + try { PrintStream summaryOut = new PrintStream(summaryFile); summaryOut.close(); @@ -94,15 +234,15 @@ public class CallableLociWalker extends LocusWalker Date: Wed, 17 Aug 2011 16:58:24 -0400 Subject: [PATCH 058/138] Reverting optimization disable in unstable. --- .../gatk/datasources/reads/LowMemoryIntervalSharder.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 198f7d7d3..ba6321121 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,8 +59,8 @@ public class LowMemoryIntervalSharder implements Iterator { */ public FilePointer next() { FilePointer current = wrappedIterator.next(); - //while(wrappedIterator.hasNext() && current.minus(wrappedIterator.peek()) == 0) - // current = current.combine(parser,wrappedIterator.next()); + while(wrappedIterator.hasNext() && current.minus(wrappedIterator.peek()) == 0) + current = current.combine(parser,wrappedIterator.next()); return current; } From 8e83b6646ba9934c8c9475538e984d684b2d37eb Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Wed, 17 Aug 2011 21:49:14 -0400 Subject: [PATCH 059/138] Bug fix for Chris: don't validate ref base for complex events. --- .../sting/utils/variantcontext/VariantContext.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 ca3399c78..dacc2d94a 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java @@ -1080,8 +1080,8 @@ public class VariantContext implements Feature { // to enable tribble intergrati } public void validateReferenceBases(Allele reference, Byte paddedRefBase) { - // don't validate if we're an insertion - if ( !reference.isNull() && !reference.basesMatch(getReference()) ) { + // 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())); } From a9df3653642da21106d8c9d86cc88300a8323d1f Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Wed, 17 Aug 2011 17:48:04 -0400 Subject: [PATCH 060/138] GenotypeAndValidate walker updated Updated the walker to comply with the new RodBinding system and the new GATKDocs. Will move it to public after writing integration tests. --- settings/helpTemplates/style.css | 48 ++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/settings/helpTemplates/style.css b/settings/helpTemplates/style.css index 1d7bcc576..2abcf0397 100644 --- a/settings/helpTemplates/style.css +++ b/settings/helpTemplates/style.css @@ -95,6 +95,12 @@ dd { padding: 0 0 0.5em 0; } +pre { + border: thin solid lightgray; + margin-left: 1em; + margin-right: 4em; + background-color: #e0fdff; +} /* * clean table layouts */ @@ -128,6 +134,48 @@ dd { } th#row-divider +{ + font-weight: bolder; + font-size: larger; +} + + +/* + * Table design for input/ouptut description + */ + +#description-table +{ + font-family: "Lucida Sans Unicode", "Lucida Grande", Sans-Serif; + font-size: 12px; + background: #fff; + margin: 5px; + border-collapse: collapse; + text-align: left; +} +#description-table th +{ + font-size: 16px; + font-weight: bold; + background-color: lightgray; + color: #039; + text-align: center; + padding: 10px 8px; + border-bottom: 2px solid #6678b1; +} +#description-table td +{ + border-bottom: 1px solid #ccc; + color: #669; + padding: 6px 8px; + text-align: right; +} +#description-table tbody tr:hover td +{ + color: #009; +} + +th#row-divider { font-weight: bolder; font-size: larger; From cc3df8f11a454cdabdd139b4e52f70447ce5756d Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Wed, 17 Aug 2011 21:54:40 -0400 Subject: [PATCH 061/138] Moving GAV walker to public Walker is updated to the new RodBinding system and has the new GATKDocs layout. --- .../validation/GenotypeAndValidateWalker.java | 438 ++++++++++++++++++ 1 file changed, 438 insertions(+) create mode 100755 public/java/src/org/broadinstitute/sting/gatk/walkers/validation/GenotypeAndValidateWalker.java diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/validation/GenotypeAndValidateWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/validation/GenotypeAndValidateWalker.java new file mode 100755 index 000000000..fc23200af --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/validation/GenotypeAndValidateWalker.java @@ -0,0 +1,438 @@ +/* + * 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.validation; + +import org.broadinstitute.sting.commandline.Argument; +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.walkers.*; +import org.broadinstitute.sting.gatk.walkers.genotyper.GenotypeLikelihoodsCalculationModel; +import org.broadinstitute.sting.gatk.walkers.genotyper.UnifiedArgumentCollection; +import org.broadinstitute.sting.gatk.walkers.genotyper.UnifiedGenotyperEngine; +import org.broadinstitute.sting.gatk.walkers.genotyper.VariantCallContext; +import org.broadinstitute.sting.utils.SampleUtils; +import org.broadinstitute.sting.utils.codecs.vcf.VCFHeader; +import org.broadinstitute.sting.utils.codecs.vcf.VCFHeaderLine; +import org.broadinstitute.sting.utils.codecs.vcf.VCFUtils; +import org.broadinstitute.sting.utils.codecs.vcf.VCFWriter; +import org.broadinstitute.sting.utils.exceptions.UserException; +import org.broadinstitute.sting.utils.variantcontext.MutableVariantContext; +import org.broadinstitute.sting.utils.variantcontext.VariantContext; +import org.broadinstitute.sting.utils.variantcontext.VariantContextUtils; + +import java.util.Map; +import java.util.Set; + +import static org.broadinstitute.sting.utils.IndelUtils.isInsideExtendedIndel; + +/** + * Genotypes a dataset and validates the calls of another dataset using the Unified Genotyper. + * + *

    + * Genotype and Validate is a tool to evaluate the quality of a dataset for calling SNPs + * and Indels given a secondary (validation) data source. The data sources are BAM or VCF + * files. You can use them interchangeably (i.e. a BAM to validate calls in a VCF or a VCF + * to validate calls on a BAM). + *

    + * + *

    + * The simplest scenario is when you have a VCF of hand annotated SNPs and Indels, and you + * want to know how well a particular technology performs calling these snps. With a + * dataset (BAM file) generated by the technology in test, and the hand annotated VCF, you + * can run GenotypeAndValidate to asses the accuracy of the calls with the new technology's + * dataset. + *

    + * + *

    + * Another option is to validate the calls on a VCF file, using a deep coverage BAM file + * that you trust the calls on. The GenotypeAndValidate walker will make calls using the + * reads in the BAM file and take them as truth, then compare to the calls in the VCF file + * and produce a truth table. + *

    + * + * + *

    Input

    + *

    + * A BAM file to make calls on and a VCF file to use as truth validation dataset. + * + * You also have the option to invert the roles of the files using the command line options listed below. + *

    + * + *

    Output

    + *

    + * GenotypeAndValidate has two outputs. The truth table and the optional VCF file. The truth table is a + * 2x2 table correlating what was called in the dataset with the truth of the call (whether it's a true + * positive or a false positive). The table should look like this: + *

    + *
    + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
    ALTREFPredictive Value
    called altTrue Positive (TP)False Positive (FP)Positive PV
    called refFalse Negative (FN)True Negative (TN)Negative PV
    + *
    + * + *

    + * The positive predictive value (PPV) is the proportion of subjects with positive test results + * who are correctly diagnosed. + *

    + *

    + * The negative predictive value (NPV) is the proportion of subjects with a negative test result + * who are correctly diagnosed. + *

    + *

    + * The VCF file will contain only the variants that were called or not called, excluding the ones that + * were uncovered or didn't pass the filters. This file is useful if you are trying to compare + * the PPV and NPV of two different technologies on the exact same sites (so you can compare apples to + * apples). + *

    + * + *

    + * Here is an example of an annotated VCF file (info field clipped for clarity) + * + *

    + * #CHROM  POS ID  REF ALT QUAL    FILTER  INFO    FORMAT  NA12878
    + * 1   20568807    .   C   T   0    HapMapHet        AC=1;AF=0.50;AN=2;DP=0;GV=T  GT  0/1
    + * 1   22359922    .   T   C   282  WG-CG-HiSeq      AC=2;AF=0.50;GV=T;AN=4;DP=42 GT:AD:DP:GL:GQ  1/0 ./. 0/1:20,22:39:-72.79,-11.75,-67.94:99    ./.
    + * 13  102391461   .   G   A   341  Indel;SnpCluster AC=1;GV=F;AF=0.50;AN=2;DP=45 GT:AD:DP:GL:GQ  ./. ./. 0/1:32,13:45:-50.99,-13.56,-112.17:99   ./.
    + * 1   175516757   .   C   G   655  SnpCluster,WG    AC=1;AF=0.50;AN=2;GV=F;DP=74 GT:AD:DP:GL:GQ  ./. ./. 0/1:52,22:67:-89.02,-20.20,-191.27:99   ./.
    + * 
    + * + *

    + * + *

    Additional Details

    + *
      + *
    • + * You should always use -BTI on your VCF track, so that the GATK only looks at the sites on the VCF file. + * This speeds up the process a lot. + *
    • + *
    • + * The total number of visited bases may be greater than the number of variants in the original + * VCF file because of extended indels, as they trigger one call per new insertion or deletion. + * (i.e. ACTG/- will count as 4 genotyper calls, but it's only one line in the VCF). + *
    • + *
    + * + *

    Examples

    + *
      + *
    1. + * Genotypes BAM file from new technology using the VCF as a truth dataset: + *
    2. + * + *
      + *  java
      + *      -jar /GenomeAnalysisTK.jar
      + *      -T  GenotypeAndValidate
      + *      -R human_g1k_v37.fasta
      + *      -I myNewTechReads.bam
      + *      -alleles handAnnotatedVCF.vcf
      + *      -BTI alleles
      + * 
      + * + *
    3. + * Using a BAM file as the truth dataset: + *
    4. + * + *
      + *  java
      + *      -jar /GenomeAnalysisTK.jar
      + *      -T  GenotypeAndValidate
      + *      -R human_g1k_v37.fasta
      + *      -I myTruthDataset.bam
      + *      -alleles callsToValidate.vcf
      + *      -BTI alleles
      + *      -bt
      + *      -o gav.vcf
      + * 
      + * + * + * @author Mauricio Carneiro + * @since ${DATE} + */ + +@Requires(value={DataSource.READS, DataSource.REFERENCE}) +@Allows(value={DataSource.READS, DataSource.REFERENCE}) + +@By(DataSource.REFERENCE) +@Reference(window=@Window(start=-200,stop=200)) + + +public class GenotypeAndValidateWalker extends RodWalker implements TreeReducible { + + @Output(doc="Generate a VCF file with the variants considered by the walker, with a new annotation \"callStatus\" which will carry the value called in the validation VCF or BAM file", required=false) + protected VCFWriter vcfWriter = null; + + @Input(fullName="alleles", shortName = "alleles", doc="The set of alleles at which to genotype", required=true) + public RodBinding alleles; + + @Argument(fullName ="set_bam_truth", shortName ="bt", doc="Use the calls on the reads (bam file) as the truth dataset and validate the calls on the VCF", required=false) + private boolean bamIsTruth = false; + + @Argument(fullName="minimum_base_quality_score", shortName="mbq", doc="Minimum base quality score for calling a genotype", required=false) + private int mbq = -1; + + @Argument(fullName="maximum_deletion_fraction", shortName="deletions", doc="Maximum deletion fraction for calling a genotype", required=false) + private double deletions = -1; + + @Argument(fullName="standard_min_confidence_threshold_for_calling", shortName="stand_call_conf", doc="the minimum phred-scaled Qscore threshold to separate high confidence from low confidence calls", required=false) + private double callConf = -1; + + @Argument(fullName="standard_min_confidence_threshold_for_emitting", shortName="stand_emit_conf", doc="the minimum phred-scaled Qscore threshold to emit low confidence calls", required=false) + private double emitConf = -1; + + @Argument(fullName="condition_on_depth", shortName="depth", doc="Condition validation on a minimum depth of coverage by the reads", required=false) + private int minDepth = -1; + + @Argument(fullName ="sample", shortName ="sn", doc="Name of the sample to validate (in case your VCF/BAM has more than one sample)", required=false) + private String sample = ""; + + private UnifiedGenotyperEngine snpEngine; + private UnifiedGenotyperEngine indelEngine; + + public static class CountedData { + private long nAltCalledAlt = 0L; + private long nAltCalledRef = 0L; + private long nRefCalledAlt = 0L; + private long nRefCalledRef = 0L; + private long nNotConfidentCalls = 0L; + private long nUncovered = 0L; + + /** + * Adds the values of other to this, returning this + * @param other the other object + */ + public void add(CountedData other) { + nAltCalledAlt += other.nAltCalledAlt; + nAltCalledRef += other.nAltCalledRef; + nRefCalledAlt += other.nRefCalledAlt; + nRefCalledRef += other.nRefCalledRef; + nUncovered += other.nUncovered; + nNotConfidentCalls += other.nNotConfidentCalls; + } + } + + + + //--------------------------------------------------------------------------------------------------------------- + // + // initialize + // + //--------------------------------------------------------------------------------------------------------------- + + public void initialize() { + + // Initialize VCF header + if (vcfWriter != null) { + Map header = VCFUtils.getVCFHeadersFromRodPrefix(getToolkit(), alleles.getName()); + Set samples = SampleUtils.getSampleList(header, VariantContextUtils.GenotypeMergeType.REQUIRE_UNIQUE); + Set headerLines = VCFUtils.smartMergeHeaders(header.values(), logger); + headerLines.add(new VCFHeaderLine("source", "GenotypeAndValidate")); + vcfWriter.writeHeader(new VCFHeader(headerLines, samples)); + } + + // Filling in SNP calling arguments for UG + UnifiedArgumentCollection uac = new UnifiedArgumentCollection(); + uac.OutputMode = UnifiedGenotyperEngine.OUTPUT_MODE.EMIT_ALL_SITES; + uac.alleles = alleles; + if (!bamIsTruth) uac.GenotypingMode = GenotypeLikelihoodsCalculationModel.GENOTYPING_MODE.GENOTYPE_GIVEN_ALLELES; + if (mbq >= 0) uac.MIN_BASE_QUALTY_SCORE = mbq; + if (deletions >= 0) uac.MAX_DELETION_FRACTION = deletions; + if (emitConf >= 0) uac.STANDARD_CONFIDENCE_FOR_EMITTING = emitConf; + if (callConf >= 0) uac.STANDARD_CONFIDENCE_FOR_CALLING = callConf; + + uac.GLmodel = GenotypeLikelihoodsCalculationModel.Model.SNP; + snpEngine = new UnifiedGenotyperEngine(getToolkit(), uac); + + // Adding the INDEL calling arguments for UG + uac.GLmodel = GenotypeLikelihoodsCalculationModel.Model.INDEL; + indelEngine = new UnifiedGenotyperEngine(getToolkit(), uac); + + // make sure we have callConf set to the threshold set by the UAC so we can use it later. + callConf = uac.STANDARD_CONFIDENCE_FOR_CALLING; + } + + //--------------------------------------------------------------------------------------------------------------- + // + // map + // + //--------------------------------------------------------------------------------------------------------------- + + public CountedData map( RefMetaDataTracker tracker, ReferenceContext ref, AlignmentContext context ) { + + final CountedData counter = new CountedData(); + + // For some reason RodWalkers get map calls with null trackers + if( tracker == null ) + return counter; + + VariantContext vcComp = tracker.getFirstValue(alleles); + if( vcComp == null ) + return counter; + + //todo - not sure I want this, may be misleading to filter extended indel events. + if (isInsideExtendedIndel(vcComp, ref)) + return counter; + + // Do not operate on variants that are not covered to the optional minimum depth + if (!context.hasReads() || (minDepth > 0 && context.getBasePileup().getBases().length < minDepth)) { + counter.nUncovered = 1L; + return counter; + } + + VariantCallContext call; + if ( vcComp.isSNP() ) + call = snpEngine.calculateLikelihoodsAndGenotypes(tracker, ref, context); + else if ( vcComp.isIndel() ) { + call = indelEngine.calculateLikelihoodsAndGenotypes(tracker, ref, context); + } + else { + logger.info("Not SNP or INDEL " + vcComp.getChr() + ":" + vcComp.getStart() + " " + vcComp.getAlleles()); + return counter; + } + + + boolean writeVariant = true; + + if (bamIsTruth) { + if (call.confidentlyCalled) { + // If truth is a confident REF call + if (call.isVariant()) { + if (vcComp.isVariant()) + counter.nAltCalledAlt = 1L; // todo -- may wanna check if the alts called are the same? + else + counter.nAltCalledRef = 1L; + } + // If truth is a confident ALT call + else { + if (vcComp.isVariant()) + counter.nRefCalledAlt = 1L; + else + counter.nRefCalledRef = 1L; + } + } + else { + counter.nNotConfidentCalls = 1L; + writeVariant = false; + } + } + else { + if (!vcComp.hasAttribute("GV")) + throw new UserException.BadInput("Variant has no GV annotation in the INFO field. " + vcComp.getChr() + ":" + vcComp.getStart()); + + + + if (call.isCalledAlt(callConf)) { + if (vcComp.getAttribute("GV").equals("T")) + counter.nAltCalledAlt = 1L; + else + counter.nRefCalledAlt = 1L; + } + else if (call.isCalledRef(callConf)) { + if (vcComp.getAttribute("GV").equals("T")) + counter.nAltCalledRef = 1L; + else + counter.nRefCalledRef = 1L; + } + else { + counter.nNotConfidentCalls = 1L; + writeVariant = false; + } + } + + if (vcfWriter != null && writeVariant) { + if (!vcComp.hasAttribute("callStatus")) { + MutableVariantContext mvc = new MutableVariantContext(vcComp); + mvc.putAttribute("callStatus", call.isCalledAlt(callConf) ? "ALT" : "REF" ); + vcfWriter.add(mvc); + } + else + vcfWriter.add(vcComp); + } + return counter; + } + + //--------------------------------------------------------------------------------------------------------------- + // + // reduce + // + //--------------------------------------------------------------------------------------------------------------- + + public CountedData reduceInit() { + return new CountedData(); + } + + public CountedData treeReduce( final CountedData sum1, final CountedData sum2) { + sum2.add(sum1); + return sum2; + } + + public CountedData reduce( final CountedData mapValue, final CountedData reduceSum ) { + reduceSum.add(mapValue); + return reduceSum; + } + + public void onTraversalDone( CountedData reduceSum ) { + double ppv = 100 * ((double) reduceSum.nAltCalledAlt /( reduceSum.nAltCalledAlt + reduceSum.nRefCalledAlt)); + double npv = 100 * ((double) reduceSum.nRefCalledRef /( reduceSum.nRefCalledRef + reduceSum.nAltCalledRef)); + double sensitivity = 100 * ((double) reduceSum.nAltCalledAlt /( reduceSum.nAltCalledAlt + reduceSum.nAltCalledRef)); + double specificity = (reduceSum.nRefCalledRef + reduceSum.nRefCalledAlt > 0) ? 100 * ((double) reduceSum.nRefCalledRef /( reduceSum.nRefCalledRef + reduceSum.nRefCalledAlt)) : 100; + logger.info(String.format("Resulting Truth Table Output\n\n" + + "---------------------------------------------------\n" + + "\t\t|\tALT\t|\tREF\t\n" + + "---------------------------------------------------\n" + + "called alt\t|\t%d\t|\t%d\n" + + "called ref\t|\t%d\t|\t%d\n" + + "---------------------------------------------------\n" + + "positive predictive value: %f%%\n" + + "negative predictive value: %f%%\n" + + "---------------------------------------------------\n" + + "sensitivity: %f%%\n" + + "specificity: %f%%\n" + + "---------------------------------------------------\n" + + "not confident: %d\n" + + "not covered: %d\n" + + "---------------------------------------------------\n", reduceSum.nAltCalledAlt, reduceSum.nRefCalledAlt, reduceSum.nAltCalledRef, reduceSum.nRefCalledRef, ppv, npv, sensitivity, specificity, reduceSum.nNotConfidentCalls, reduceSum.nUncovered)); + } +} From a7b70e6bb46c10c3a00bcedfcf9cf916321e9d3b Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Wed, 17 Aug 2011 22:28:22 -0400 Subject: [PATCH 063/138] Adding feature for Khalid: ability to exclude particular samples. --- .../walkers/variantutils/SelectVariants.java | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 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 b52863c8f..f6b6a8d65 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 @@ -191,16 +191,28 @@ public class SelectVariants extends RodWalker { protected VCFWriter vcfWriter = null; @Argument(fullName="sample_name", shortName="sn", doc="Include genotypes from this sample. Can be specified multiple times", required=false) - public Set sampleNames; + public Set sampleNames = new HashSet(0); @Argument(fullName="sample_expressions", shortName="se", doc="Regular expression to select many samples from the ROD tracks provided. Can be specified multiple times", required=false) - public Set sampleExpressions; + public Set sampleExpressions ; @Argument(fullName="sample_file", shortName="sf", doc="File containing a list of samples (one per line) to include. Can be specified multiple times", required=false) public Set sampleFiles; /** - * Note that thse expressions are evaluated *after* the specified samples are extracted and the INFO field annotations are updated. + * Note that sample exclusion takes precedence over inclusion, so that if a sample is in both lists it will be excluded. + */ + @Argument(fullName="exclude_sample_name", shortName="xl_sn", doc="Exclude genotypes from this sample. Can be specified multiple times", required=false) + public Set XLsampleNames = new HashSet(0); + + /** + * Note that sample exclusion takes precedence over inclusion, so that if a sample is in both lists it will be excluded. + */ + @Argument(fullName="exclude_sample_file", shortName="xl_sf", doc="File containing a list of samples (one per line) to exclude. Can be specified multiple times", required=false) + public Set XLsampleFiles; + + /** + * Note that these expressions are evaluated *after* the specified samples are extracted and the INFO field annotations are updated. */ @Argument(shortName="select", doc="One or more criteria to use when selecting the data", required=false) public ArrayList SELECT_EXPRESSIONS = new ArrayList(); @@ -304,8 +316,7 @@ public class SelectVariants extends RodWalker { private ArrayList afBoosts = null; double bkDelta = 0.0; - - private PrintStream outMVFileStream = null; + private PrintStream outMVFileStream = null; /** @@ -321,19 +332,27 @@ public class SelectVariants extends RodWalker { Collection samplesFromFile = SampleUtils.getSamplesFromFiles(sampleFiles); Collection samplesFromExpressions = SampleUtils.matchSamplesExpressions(vcfSamples, sampleExpressions); + // first, add any requested samples samples.addAll(samplesFromFile); samples.addAll(samplesFromExpressions); - if (sampleNames != null) - samples.addAll(sampleNames); + samples.addAll(sampleNames); - if(samples.isEmpty()) { + // if none were requested, we want all of them + if ( samples.isEmpty() ) { samples.addAll(vcfSamples); NO_SAMPLES_SPECIFIED = true; } - for (String sample : samples) { + // now, exclude any requested samples + Collection XLsamplesFromFile = SampleUtils.getSamplesFromFiles(XLsampleFiles); + samples.removeAll(XLsamplesFromFile); + samples.removeAll(XLsampleNames); + + if ( samples.size() == 0 ) + throw new UserException("All samples requested to be included were also requested to be excluded."); + + for ( String sample : samples ) logger.info("Including sample '" + sample + "'"); - } // Initialize VCF header Set headerLines = VCFUtils.smartMergeHeaders(vcfRods.values(), logger); From b75a1807e3242fd3dda158aba33362f826fb9041 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Wed, 17 Aug 2011 22:40:09 -0400 Subject: [PATCH 064/138] Adding integration test to cover sample exclusion --- .../SelectVariantsIntegrationTest.java | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 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 bec0d5dd4..cec377f5f 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 @@ -7,7 +7,7 @@ import java.util.Arrays; public class SelectVariantsIntegrationTest extends WalkerTest { public static String baseTestString(String args) { - return "-T SelectVariants -R " + b36KGReference + " -L 1 -o %s" + args; + return "-T SelectVariants -R " + b36KGReference + " -L 1 -o %s -NO_HEADER" + args; } @Test @@ -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 + " -NO_HEADER"), + baseTestString(" -sn A -se '[CDH]' -sf " + samplesFile + " -env -ef -select 'DP < 250' --variant:VCF3 " + testfile), 1, Arrays.asList("d18516c1963802e92cb9e425c0b75fd6") ); @@ -24,12 +24,26 @@ public class SelectVariantsIntegrationTest extends WalkerTest { executeTest("testComplexSelection--" + testfile, spec); } + @Test + public void testSampleExclusion() { + String testfile = validationDataLocation + "test.filtered.maf_annotated.vcf"; + 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, + 1, + Arrays.asList("730f021fd6ecf1d195dabbee2e233bfd") + ); + + executeTest("testSampleExclusion--" + testfile, spec); + } + @Test public void testRepeatedLineSelection() { String testfile = validationDataLocation + "test.dup.vcf"; WalkerTestSpec spec = new WalkerTestSpec( - baseTestString(" -sn A -sn B -sn C --variant:VCF3 " + testfile + " -NO_HEADER"), + baseTestString(" -sn A -sn B -sn C --variant:VCF3 " + testfile), 1, Arrays.asList("b74038779fe6485dbb8734ae48178356") ); From c30e1db744052c789de6726c259678509f6a91e8 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 18 Aug 2011 09:38:51 -0400 Subject: [PATCH 066/138] Better location for help utils --- .../sting/gatk/CommandLineGATK.java | 7 +-- .../help/DocumentedGATKFeatureHandler.java | 2 +- .../sting/utils/help/GATKDocUtils.java | 48 ------------------- .../sting/utils/help/HelpUtils.java | 17 +++++++ 4 files changed, 20 insertions(+), 54 deletions(-) delete mode 100644 public/java/src/org/broadinstitute/sting/utils/help/GATKDocUtils.java diff --git a/public/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java b/public/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java index 7e96b609e..7c567f511 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java +++ b/public/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java @@ -34,10 +34,7 @@ import org.broadinstitute.sting.gatk.filters.ReadFilter; import org.broadinstitute.sting.gatk.walkers.Attribution; import org.broadinstitute.sting.gatk.walkers.Walker; import org.broadinstitute.sting.utils.exceptions.UserException; -import org.broadinstitute.sting.utils.help.ApplicationDetails; -import org.broadinstitute.sting.utils.help.DocumentedGATKFeature; -import org.broadinstitute.sting.utils.help.GATKDocUtils; -import org.broadinstitute.sting.utils.help.GATKDoclet; +import org.broadinstitute.sting.utils.help.*; import org.broadinstitute.sting.utils.text.TextFormattingUtils; import java.util.*; @@ -178,7 +175,7 @@ public class CommandLineGATK extends CommandLineExecutable { Formatter formatter = new Formatter(additionalHelp); formatter.format("For a full description of this walker, see its GATKdocs at:%n"); - formatter.format("%s%n", GATKDocUtils.helpLinksToGATKDocs(walkerType)); + formatter.format("%s%n", HelpUtils.helpLinksToGATKDocs(walkerType)); return additionalHelp.toString(); } diff --git a/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureHandler.java b/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureHandler.java index ce03c8093..214a1716a 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureHandler.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureHandler.java @@ -71,7 +71,7 @@ public abstract class DocumentedGATKFeatureHandler { * @return */ public String getDestinationFilename(ClassDoc doc, Class clazz) { - return GATKDocUtils.htmlFilenameForClass(clazz); + return HelpUtils.htmlFilenameForClass(clazz); } /** diff --git a/public/java/src/org/broadinstitute/sting/utils/help/GATKDocUtils.java b/public/java/src/org/broadinstitute/sting/utils/help/GATKDocUtils.java deleted file mode 100644 index 8efeecd7b..000000000 --- a/public/java/src/org/broadinstitute/sting/utils/help/GATKDocUtils.java +++ /dev/null @@ -1,48 +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.help; - -/** - * @author depristo - * @since 8/8/11 - */ -public class GATKDocUtils { - private final static String URL_ROOT_FOR_RELEASE_GATKDOCS = "http://www.broadinstitute.org/gsa/gatkdocs/release/"; - private final static String URL_ROOT_FOR_STABLE_GATKDOCS = "http://iwww.broadinstitute.org/gsa/gatkdocs/stable/"; - private final static String URL_ROOT_FOR_UNSTABLE_GATKDOCS = "http://iwww.broadinstitute.org/gsa/gatkdocs/unstable/"; - - public static String htmlFilenameForClass(Class c) { - return c.getName().replace(".", "_") + ".html"; - } - - public static String helpLinksToGATKDocs(Class c) { - String classPath = htmlFilenameForClass(c); - StringBuilder b = new StringBuilder(); - b.append("release version: ").append(URL_ROOT_FOR_RELEASE_GATKDOCS).append(classPath).append("\n"); - b.append("stable version: ").append(URL_ROOT_FOR_STABLE_GATKDOCS).append(classPath).append("\n"); - b.append("unstable version: ").append(URL_ROOT_FOR_UNSTABLE_GATKDOCS).append(classPath).append("\n"); - return b.toString(); - } -} diff --git a/public/java/src/org/broadinstitute/sting/utils/help/HelpUtils.java b/public/java/src/org/broadinstitute/sting/utils/help/HelpUtils.java index 4527c6afe..da4e7bdaf 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/HelpUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/HelpUtils.java @@ -32,6 +32,10 @@ import org.broadinstitute.sting.utils.classloader.JVMUtils; import java.lang.reflect.Field; public class HelpUtils { + public final static String URL_ROOT_FOR_RELEASE_GATKDOCS = "http://www.broadinstitute.org/gsa/gatkdocs/release/"; + public final static String URL_ROOT_FOR_STABLE_GATKDOCS = "http://iwww.broadinstitute.org/gsa/gatkdocs/stable/"; + public final static String URL_ROOT_FOR_UNSTABLE_GATKDOCS = "http://iwww.broadinstitute.org/gsa/gatkdocs/unstable/"; + protected static boolean implementsInterface(ProgramElementDoc classDoc, Class... interfaceClasses) { for (Class interfaceClass : interfaceClasses) if (assignableToClass(classDoc, interfaceClass, false)) @@ -74,4 +78,17 @@ public class HelpUtils { String.format("%s.%s", containingPackage.name(), doc.name()) : String.format("%s", doc.name()); } + + public static String htmlFilenameForClass(Class c) { + return c.getName().replace(".", "_") + ".html"; + } + + public static String helpLinksToGATKDocs(Class c) { + String classPath = htmlFilenameForClass(c); + StringBuilder b = new StringBuilder(); + b.append("release version: ").append(URL_ROOT_FOR_RELEASE_GATKDOCS).append(classPath).append("\n"); + b.append("stable version: ").append(URL_ROOT_FOR_STABLE_GATKDOCS).append(classPath).append("\n"); + b.append("unstable version: ").append(URL_ROOT_FOR_UNSTABLE_GATKDOCS).append(classPath).append("\n"); + return b.toString(); + } } \ No newline at end of file From 9c17d54cb602bd4af70921fb9ca67163193ad62a Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 18 Aug 2011 09:39:20 -0400 Subject: [PATCH 067/138] getFeatureClass() now returns Class not Class to avoid yesterday's runtime error --- .../sting/gatk/refdata/features/refseq/RefSeqCodec.java | 2 +- .../sting/gatk/refdata/features/table/TableCodec.java | 2 +- .../sting/utils/codecs/completegenomics/CGVarCodec.java | 2 +- .../sting/utils/codecs/hapmap/RawHapMapCodec.java | 2 +- .../broadinstitute/sting/utils/codecs/snpEff/SnpEffCodec.java | 2 +- .../broadinstitute/sting/utils/codecs/soapsnp/SoapSNPCodec.java | 2 +- .../broadinstitute/sting/utils/codecs/vcf/AbstractVCFCodec.java | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/refseq/RefSeqCodec.java b/public/java/src/org/broadinstitute/sting/gatk/refdata/features/refseq/RefSeqCodec.java index cec40b5bd..43063da17 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/refseq/RefSeqCodec.java +++ b/public/java/src/org/broadinstitute/sting/gatk/refdata/features/refseq/RefSeqCodec.java @@ -104,7 +104,7 @@ public class RefSeqCodec implements ReferenceDependentFeatureCodec getFeatureType() { return RefSeqFeature.class; } } diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/table/TableCodec.java b/public/java/src/org/broadinstitute/sting/gatk/refdata/features/table/TableCodec.java index ab1ac59d8..4e734294f 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/table/TableCodec.java +++ b/public/java/src/org/broadinstitute/sting/gatk/refdata/features/table/TableCodec.java @@ -51,7 +51,7 @@ public class TableCodec implements ReferenceDependentFeatureCodec { } @Override - public Class getFeatureType() { + public Class getFeatureType() { return TableFeature.class; } diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/completegenomics/CGVarCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/completegenomics/CGVarCodec.java index fef6c4ea0..c30da828e 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/completegenomics/CGVarCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/completegenomics/CGVarCodec.java @@ -117,7 +117,7 @@ public class CGVarCodec implements FeatureCodec { return new VariantContext("CGI", array[3], start, end, alleles, VariantContext.NO_NEG_LOG_10PERROR, null, attrs); } - public Class getFeatureType() { + public Class getFeatureType() { return VariantContext.class; } diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/hapmap/RawHapMapCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/hapmap/RawHapMapCodec.java index 90878dee7..535f607a1 100644 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/hapmap/RawHapMapCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/hapmap/RawHapMapCodec.java @@ -82,7 +82,7 @@ public class RawHapMapCodec implements FeatureCodec { headerLine); } - public Class getFeatureType() { + public Class getFeatureType() { return RawHapMapFeature.class; } diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/snpEff/SnpEffCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/snpEff/SnpEffCodec.java index eada8521f..b5efb49a7 100644 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/snpEff/SnpEffCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/snpEff/SnpEffCodec.java @@ -222,7 +222,7 @@ public class SnpEffCodec implements FeatureCodec, SelfScopingFeatureCodec { return null; } - public Class getFeatureType() { + public Class getFeatureType() { return SnpEffFeature.class; } diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/soapsnp/SoapSNPCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/soapsnp/SoapSNPCodec.java index e169dbdfc..284c43e90 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/soapsnp/SoapSNPCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/soapsnp/SoapSNPCodec.java @@ -178,7 +178,7 @@ public class SoapSNPCodec implements FeatureCodec, NameAwareCodec { /** * @return VariantContext */ - public Class getFeatureType() { + public Class getFeatureType() { return VariantContext.class; } 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 cb505c717..19f58ddaa 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 @@ -263,7 +263,7 @@ public abstract class AbstractVCFCodec implements FeatureCodec, NameAwareCodec, * * @return the type of record */ - public Class getFeatureType() { + public Class getFeatureType() { return VariantContext.class; } From c2287c93d7fd2bb067065f93c67245b5605bb7db Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 18 Aug 2011 10:02:46 -0400 Subject: [PATCH 068/138] Cleanup of codec locations. No more dbSNPHelper -- refdata/features now in utils/codecs with the other codecs -- Deleted dbsnpHelper. rsID function now in VCFutils. Remaining code either deleted or put into VariantContextAdaptors -- Many associated import updates due to code move --- .../gatk/refdata/VariantContextAdaptors.java | 72 +++++++- .../gatk/refdata/features/DbSNPHelper.java | 169 ------------------ .../sting/gatk/walkers/PileupWalker.java | 4 - .../annotator/VariantAnnotatorEngine.java | 8 +- .../beagle/BeagleOutputToVCFWalker.java | 2 +- .../coverage/DepthOfCoverageWalker.java | 4 +- .../indels/SomaticIndelDetectorWalker.java | 6 +- .../walkers/qc/ValidatingPileupWalker.java | 2 +- .../validation/ValidationAmplicons.java | 3 +- .../walkers/variantutils/VariantsToVCF.java | 3 +- .../codecs}/beagle/BeagleCodec.java | 2 +- .../codecs}/beagle/BeagleFeature.java | 2 +- .../codecs}/refseq/RefSeqCodec.java | 3 +- .../codecs}/refseq/RefSeqFeature.java | 2 +- .../codecs}/refseq/Transcript.java | 106 +++++------ .../codecs}/sampileup/SAMPileupCodec.java | 4 +- .../codecs}/sampileup/SAMPileupFeature.java | 2 +- .../codecs}/samread/SAMReadCodec.java | 2 +- .../codecs}/samread/SAMReadFeature.java | 2 +- .../codecs}/table/BedTableCodec.java | 2 +- .../codecs}/table/TableCodec.java | 2 +- .../codecs}/table/TableFeature.java | 2 +- .../sting/utils/codecs/vcf/VCFUtils.java | 15 ++ .../commandline/ParsingEngineUnitTest.java | 2 - .../ReferenceOrderedViewUnitTest.java | 2 +- .../rmd/ReferenceOrderedDataPoolUnitTest.java | 2 +- .../refdata/RefMetaDataTrackerUnitTest.java | 2 +- .../tracks/FeatureManagerUnitTest.java | 4 +- 28 files changed, 163 insertions(+), 268 deletions(-) delete mode 100644 public/java/src/org/broadinstitute/sting/gatk/refdata/features/DbSNPHelper.java rename public/java/src/org/broadinstitute/sting/{gatk/refdata/features => utils/codecs}/beagle/BeagleCodec.java (99%) rename public/java/src/org/broadinstitute/sting/{gatk/refdata/features => utils/codecs}/beagle/BeagleFeature.java (97%) rename public/java/src/org/broadinstitute/sting/{gatk/refdata/features => utils/codecs}/refseq/RefSeqCodec.java (97%) rename public/java/src/org/broadinstitute/sting/{gatk/refdata/features => utils/codecs}/refseq/RefSeqFeature.java (99%) rename public/java/src/org/broadinstitute/sting/{gatk/refdata/features => utils/codecs}/refseq/Transcript.java (95%) rename public/java/src/org/broadinstitute/sting/{gatk/refdata/features => utils/codecs}/sampileup/SAMPileupCodec.java (98%) rename public/java/src/org/broadinstitute/sting/{gatk/refdata/features => utils/codecs}/sampileup/SAMPileupFeature.java (99%) rename public/java/src/org/broadinstitute/sting/{gatk/refdata/features => utils/codecs}/samread/SAMReadCodec.java (98%) rename public/java/src/org/broadinstitute/sting/{gatk/refdata/features => utils/codecs}/samread/SAMReadFeature.java (98%) rename public/java/src/org/broadinstitute/sting/{gatk/refdata/features => utils/codecs}/table/BedTableCodec.java (94%) rename public/java/src/org/broadinstitute/sting/{gatk/refdata/features => utils/codecs}/table/TableCodec.java (97%) rename public/java/src/org/broadinstitute/sting/{gatk/refdata/features => utils/codecs}/table/TableFeature.java (96%) diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/VariantContextAdaptors.java b/public/java/src/org/broadinstitute/sting/gatk/refdata/VariantContextAdaptors.java index 902f9d308..bf490e28c 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/VariantContextAdaptors.java +++ b/public/java/src/org/broadinstitute/sting/gatk/refdata/VariantContextAdaptors.java @@ -1,10 +1,11 @@ package org.broadinstitute.sting.gatk.refdata; +import net.sf.samtools.util.SequenceUtil; import org.broad.tribble.Feature; +import org.broad.tribble.annotation.Strand; import org.broad.tribble.dbsnp.DbSNPFeature; import org.broad.tribble.gelitext.GeliTextFeature; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; -import org.broadinstitute.sting.gatk.refdata.features.DbSNPHelper; import org.broadinstitute.sting.utils.classloader.PluginManager; import org.broadinstitute.sting.utils.codecs.hapmap.RawHapMapFeature; import org.broadinstitute.sting.utils.codecs.vcf.VCFHeader; @@ -92,6 +93,67 @@ public class VariantContextAdaptors { // -------------------------------------------------------------------------------------------------------------- private static class DBSnpAdaptor implements VCAdaptor { + private static boolean isSNP(DbSNPFeature feature) { + return feature.getVariantType().contains("single") && feature.getLocationType().contains("exact"); + } + + private static boolean isMNP(DbSNPFeature feature) { + return feature.getVariantType().contains("mnp") && feature.getLocationType().contains("range"); + } + + private static boolean isInsertion(DbSNPFeature feature) { + return feature.getVariantType().contains("insertion"); + } + + private static boolean isDeletion(DbSNPFeature feature) { + return feature.getVariantType().contains("deletion"); + } + + private static boolean isIndel(DbSNPFeature feature) { + return isInsertion(feature) || isDeletion(feature) || isComplexIndel(feature); + } + + public static boolean isComplexIndel(DbSNPFeature feature) { + return feature.getVariantType().contains("in-del"); + } + + /** + * gets the alternate alleles. This method should return all the alleles present at the location, + * NOT including the reference base. This is returned as a string list with no guarantee ordering + * of alleles (i.e. the first alternate allele is not always going to be the allele with the greatest + * frequency). + * + * @return an alternate allele list + */ + public static List getAlternateAlleleList(DbSNPFeature feature) { + List ret = new ArrayList(); + for (String allele : getAlleleList(feature)) + if (!allele.equals(String.valueOf(feature.getNCBIRefBase()))) ret.add(allele); + return ret; + } + + /** + * gets the alleles. This method should return all the alleles present at the location, + * including the reference base. The first allele should always be the reference allele, followed + * by an unordered list of alternate alleles. + * + * @return an alternate allele list + */ + public static List getAlleleList(DbSNPFeature feature) { + List alleleList = new ArrayList(); + // add ref first + if ( feature.getStrand() == Strand.POSITIVE ) + alleleList = Arrays.asList(feature.getObserved()); + else + for (String str : feature.getObserved()) + alleleList.add(SequenceUtil.reverseComplement(str)); + if ( alleleList.size() > 0 && alleleList.contains(feature.getNCBIRefBase()) + && !alleleList.get(0).equals(feature.getNCBIRefBase()) ) + Collections.swap(alleleList, alleleList.indexOf(feature.getNCBIRefBase()), 0); + + return alleleList; + } + /** * Converts non-VCF formatted dbSNP records to VariantContext. * @return DbSNPFeature. @@ -102,18 +164,18 @@ public class VariantContextAdaptors { @Override public VariantContext convert(String name, Object input, ReferenceContext ref) { DbSNPFeature dbsnp = (DbSNPFeature)input; - if ( ! Allele.acceptableAlleleBases(DbSNPHelper.getReference(dbsnp)) ) + if ( ! Allele.acceptableAlleleBases(dbsnp.getNCBIRefBase()) ) return null; - Allele refAllele = Allele.create(DbSNPHelper.getReference(dbsnp), true); + Allele refAllele = Allele.create(dbsnp.getNCBIRefBase(), true); - if ( DbSNPHelper.isSNP(dbsnp) || DbSNPHelper.isIndel(dbsnp) || DbSNPHelper.isMNP(dbsnp) || dbsnp.getVariantType().contains("mixed") ) { + if ( isSNP(dbsnp) || isIndel(dbsnp) || isMNP(dbsnp) || dbsnp.getVariantType().contains("mixed") ) { // add the reference allele List alleles = new ArrayList(); alleles.add(refAllele); // add all of the alt alleles boolean sawNullAllele = refAllele.isNull(); - for ( String alt : DbSNPHelper.getAlternateAlleleList(dbsnp) ) { + for ( String alt : getAlternateAlleleList(dbsnp) ) { if ( ! Allele.acceptableAlleleBases(alt) ) { //System.out.printf("Excluding dbsnp record %s%n", dbsnp); return null; diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/DbSNPHelper.java b/public/java/src/org/broadinstitute/sting/gatk/refdata/features/DbSNPHelper.java deleted file mode 100644 index a2132cee5..000000000 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/DbSNPHelper.java +++ /dev/null @@ -1,169 +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.refdata.features; - -import net.sf.samtools.util.SequenceUtil; -import org.broad.tribble.annotation.Strand; -import org.broad.tribble.dbsnp.DbSNPFeature; -import org.broadinstitute.sting.utils.Utils; -import org.broadinstitute.sting.utils.variantcontext.VariantContext; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -/** - * this class contains static helper methods for DbSNP - */ -public class DbSNPHelper { - - private DbSNPHelper() {} // don't make a DbSNPHelper - - public static String rsIDOfFirstRealVariant(List VCs, VariantContext.Type type) { - if ( VCs == null ) - return null; - - String rsID = null; - for ( VariantContext vc : VCs ) { - if ( vc.getType() == type ) { - rsID = vc.getID(); - break; - } - } - - return rsID; - } - - /** - * get the -1 * (log 10 of the error value) - * - * @return the log based error estimate - */ - public static double getNegLog10PError(DbSNPFeature feature) { - return 4; // -log10(0.0001) - } - - // - // What kind of variant are we? - // - // ---------------------------------------------------------------------- - public static boolean isSNP(DbSNPFeature feature) { - return feature.getVariantType().contains("single") && feature.getLocationType().contains("exact"); - } - - public static boolean isMNP(DbSNPFeature feature) { - return feature.getVariantType().contains("mnp") && feature.getLocationType().contains("range"); - } - - public static String toMediumString(DbSNPFeature feature) { - String s = String.format("%s:%d:%s:%s", feature.getChr(), feature.getStart(), feature.getRsID(), Utils.join("",feature.getObserved())); - if (isSNP(feature)) s += ":SNP"; - if (isIndel(feature)) s += ":Indel"; - if (isHapmap(feature)) s += ":Hapmap"; - if (is2Hit2Allele(feature)) s += ":2Hit"; - return s; - } - - public static boolean isInsertion(DbSNPFeature feature) { - return feature.getVariantType().contains("insertion"); - } - - public static boolean isDeletion(DbSNPFeature feature) { - return feature.getVariantType().contains("deletion"); - } - - public static boolean isIndel(DbSNPFeature feature) { - return DbSNPHelper.isInsertion(feature) || DbSNPHelper.isDeletion(feature) || DbSNPHelper.isComplexIndel(feature); - } - - public static boolean isComplexIndel(DbSNPFeature feature) { - return feature.getVariantType().contains("in-del"); - } - - public static boolean isHapmap(DbSNPFeature feature) { - return feature.getValidationStatus().contains("by-hapmap"); - } - - public static boolean is2Hit2Allele(DbSNPFeature feature) { - return feature.getValidationStatus().contains("by-2hit-2allele"); - } - - public static boolean is1000genomes(DbSNPFeature feature) { - return feature.getValidationStatus().contains("by-1000genomes"); - } - - public static boolean isMQ1(DbSNPFeature feature) { - return feature.getWeight() == 1; - } - - /** - * gets the alternate alleles. This method should return all the alleles present at the location, - * NOT including the reference base. This is returned as a string list with no guarantee ordering - * of alleles (i.e. the first alternate allele is not always going to be the allele with the greatest - * frequency). - * - * @return an alternate allele list - */ - public static List getAlternateAlleleList(DbSNPFeature feature) { - List ret = new ArrayList(); - for (String allele : getAlleleList(feature)) - if (!allele.equals(String.valueOf(feature.getNCBIRefBase()))) ret.add(allele); - return ret; - } - - public static boolean onFwdStrand(DbSNPFeature feature) { - return feature.getStrand() == Strand.POSITIVE; - } - - public static String getReference(DbSNPFeature feature) { - return feature.getNCBIRefBase(); - } - - public static String toSimpleString(DbSNPFeature feature) { - return String.format("%s:%s:%s", feature.getRsID(), feature.getObserved(), (feature.getStrand() == Strand.POSITIVE) ? "+" : "-"); - } - - /** - * gets the alleles. This method should return all the alleles present at the location, - * including the reference base. The first allele should always be the reference allele, followed - * by an unordered list of alternate alleles. - * - * @return an alternate allele list - */ - public static List getAlleleList(DbSNPFeature feature) { - List alleleList = new ArrayList(); - // add ref first - if ( onFwdStrand(feature) ) - alleleList = Arrays.asList(feature.getObserved()); - else - for (String str : feature.getObserved()) - alleleList.add(SequenceUtil.reverseComplement(str)); - if ( alleleList.size() > 0 && alleleList.contains(getReference(feature)) && !alleleList.get(0).equals(getReference(feature)) ) - Collections.swap(alleleList, alleleList.indexOf(getReference(feature)), 0); - - return alleleList; - } -} diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/PileupWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/PileupWalker.java index 6243a6cc0..4d8be4800 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/PileupWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/PileupWalker.java @@ -26,7 +26,6 @@ package org.broadinstitute.sting.gatk.walkers; import org.broad.tribble.Feature; -import org.broad.tribble.dbsnp.DbSNPFeature; import org.broadinstitute.sting.commandline.Argument; import org.broadinstitute.sting.commandline.Input; import org.broadinstitute.sting.commandline.Output; @@ -34,9 +33,6 @@ 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.ReferenceOrderedDatum; -import org.broadinstitute.sting.gatk.refdata.features.DbSNPHelper; -import org.broadinstitute.sting.gatk.refdata.utils.GATKFeature; import org.broadinstitute.sting.utils.Utils; import org.broadinstitute.sting.utils.collections.Pair; import org.broadinstitute.sting.utils.pileup.ReadBackedExtendedEventPileup; 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 a7837813a..01926a7f3 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,15 +29,11 @@ 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.features.DbSNPHelper; 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.utils.codecs.vcf.VCFConstants; -import org.broadinstitute.sting.utils.codecs.vcf.VCFHeaderLine; -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.Genotype; import org.broadinstitute.sting.utils.variantcontext.VariantContext; @@ -158,7 +154,7 @@ public class VariantAnnotatorEngine { private void annotateDBs(RefMetaDataTracker tracker, ReferenceContext ref, VariantContext vc, Map infoAnnotations) { for ( Map.Entry, String> dbSet : dbAnnotations.entrySet() ) { if ( dbSet.getValue().equals(VCFConstants.DBSNP_KEY) ) { - String rsID = DbSNPHelper.rsIDOfFirstRealVariant(tracker.getValues(dbSet.getKey(), ref.getLocus()), vc.getType()); + String rsID = VCFUtils.rsIDOfFirstRealVariant(tracker.getValues(dbSet.getKey(), ref.getLocus()), vc.getType()); infoAnnotations.put(VCFConstants.DBSNP_KEY, rsID != null); // annotate dbsnp id if available and not already there if ( rsID != null && (!vc.hasID() || vc.getID().equals(VCFConstants.EMPTY_ID_FIELD)) ) 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 51fe543df..60f0fcb0a 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 @@ -31,7 +31,7 @@ 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.refdata.features.beagle.BeagleFeature; +import org.broadinstitute.sting.utils.codecs.beagle.BeagleFeature; import org.broadinstitute.sting.gatk.walkers.RodWalker; import org.broadinstitute.sting.utils.GenomeLoc; import org.broadinstitute.sting.utils.SampleUtils; 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 708d6bb94..90036407f 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 @@ -32,8 +32,8 @@ 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.SeekableRODIterator; -import org.broadinstitute.sting.gatk.refdata.features.refseq.RefSeqCodec; -import org.broadinstitute.sting.gatk.refdata.features.refseq.RefSeqFeature; +import org.broadinstitute.sting.utils.codecs.refseq.RefSeqCodec; +import org.broadinstitute.sting.utils.codecs.refseq.RefSeqFeature; import org.broadinstitute.sting.gatk.refdata.tracks.RMDTrack; import org.broadinstitute.sting.gatk.refdata.tracks.RMDTrackBuilder; import org.broadinstitute.sting.gatk.refdata.utils.GATKFeature; 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 b2d30235a..9f6ac2a91 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 @@ -39,9 +39,9 @@ import org.broadinstitute.sting.gatk.filters.PlatformUnitFilter; import org.broadinstitute.sting.gatk.filters.PlatformUnitFilterHelper; import org.broadinstitute.sting.gatk.refdata.ReadMetaDataTracker; import org.broadinstitute.sting.gatk.refdata.SeekableRODIterator; -import org.broadinstitute.sting.gatk.refdata.features.refseq.Transcript; -import org.broadinstitute.sting.gatk.refdata.features.refseq.RefSeqCodec; -import org.broadinstitute.sting.gatk.refdata.features.refseq.RefSeqFeature; +import org.broadinstitute.sting.utils.codecs.refseq.Transcript; +import org.broadinstitute.sting.utils.codecs.refseq.RefSeqCodec; +import org.broadinstitute.sting.utils.codecs.refseq.RefSeqFeature; import org.broadinstitute.sting.gatk.refdata.tracks.RMDTrack; import org.broadinstitute.sting.gatk.refdata.tracks.RMDTrackBuilder; import org.broadinstitute.sting.gatk.refdata.utils.LocationAwareSeekableRODIterator; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/ValidatingPileupWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/ValidatingPileupWalker.java index bd25a73e0..ca30d875b 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/ValidatingPileupWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/ValidatingPileupWalker.java @@ -32,7 +32,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.refdata.features.sampileup.SAMPileupFeature; +import org.broadinstitute.sting.utils.codecs.sampileup.SAMPileupFeature; import org.broadinstitute.sting.gatk.walkers.*; import org.broadinstitute.sting.utils.GenomeLoc; import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; 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 7653f511f..61149e5d9 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 @@ -14,9 +14,8 @@ 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.features.table.TableFeature; +import org.broadinstitute.sting.utils.codecs.table.TableFeature; import org.broadinstitute.sting.gatk.walkers.DataSource; -import org.broadinstitute.sting.gatk.walkers.RMD; import org.broadinstitute.sting.gatk.walkers.Requires; import org.broadinstitute.sting.gatk.walkers.RodWalker; import org.broadinstitute.sting.utils.BaseUtils; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantsToVCF.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantsToVCF.java index 61851abe2..12e62a249 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantsToVCF.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantsToVCF.java @@ -33,7 +33,6 @@ 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.features.DbSNPHelper; import org.broadinstitute.sting.gatk.refdata.tracks.RMDTrackBuilder; import org.broadinstitute.sting.gatk.refdata.utils.GATKFeature; import org.broadinstitute.sting.gatk.walkers.*; @@ -120,7 +119,7 @@ public class VariantsToVCF extends RodWalker { if ( tracker == null || !BaseUtils.isRegularBase(ref.getBase()) ) return 0; - String rsID = dbsnp == null ? null : DbSNPHelper.rsIDOfFirstRealVariant(tracker.getValues(dbsnp.dbsnp, context.getLocation()), VariantContext.Type.SNP); + String rsID = dbsnp == null ? null : VCFUtils.rsIDOfFirstRealVariant(tracker.getValues(dbsnp.dbsnp, context.getLocation()), VariantContext.Type.SNP); Collection contexts = getVariantContexts(tracker, ref); diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/beagle/BeagleCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/beagle/BeagleCodec.java similarity index 99% rename from public/java/src/org/broadinstitute/sting/gatk/refdata/features/beagle/BeagleCodec.java rename to public/java/src/org/broadinstitute/sting/utils/codecs/beagle/BeagleCodec.java index 5e536d4c1..e328c9286 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/beagle/BeagleCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/beagle/BeagleCodec.java @@ -1,4 +1,4 @@ -package org.broadinstitute.sting.gatk.refdata.features.beagle; +package org.broadinstitute.sting.utils.codecs.beagle; /* * Copyright (c) 2010 The Broad Institute * diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/beagle/BeagleFeature.java b/public/java/src/org/broadinstitute/sting/utils/codecs/beagle/BeagleFeature.java similarity index 97% rename from public/java/src/org/broadinstitute/sting/gatk/refdata/features/beagle/BeagleFeature.java rename to public/java/src/org/broadinstitute/sting/utils/codecs/beagle/BeagleFeature.java index e6832754d..0aa9ecba2 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/beagle/BeagleFeature.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/beagle/BeagleFeature.java @@ -22,7 +22,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR * THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package org.broadinstitute.sting.gatk.refdata.features.beagle; +package org.broadinstitute.sting.utils.codecs.beagle; import org.broad.tribble.Feature; import org.broadinstitute.sting.utils.variantcontext.Allele; diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/refseq/RefSeqCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/refseq/RefSeqCodec.java similarity index 97% rename from public/java/src/org/broadinstitute/sting/gatk/refdata/features/refseq/RefSeqCodec.java rename to public/java/src/org/broadinstitute/sting/utils/codecs/refseq/RefSeqCodec.java index 43063da17..391715c63 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/refseq/RefSeqCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/refseq/RefSeqCodec.java @@ -1,6 +1,5 @@ -package org.broadinstitute.sting.gatk.refdata.features.refseq; +package org.broadinstitute.sting.utils.codecs.refseq; -import org.apache.commons.io.filefilter.FalseFileFilter; import org.broad.tribble.Feature; import org.broad.tribble.TribbleException; import org.broad.tribble.readers.LineReader; diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/refseq/RefSeqFeature.java b/public/java/src/org/broadinstitute/sting/utils/codecs/refseq/RefSeqFeature.java similarity index 99% rename from public/java/src/org/broadinstitute/sting/gatk/refdata/features/refseq/RefSeqFeature.java rename to public/java/src/org/broadinstitute/sting/utils/codecs/refseq/RefSeqFeature.java index a38d45428..c04ca8592 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/refseq/RefSeqFeature.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/refseq/RefSeqFeature.java @@ -1,4 +1,4 @@ -package org.broadinstitute.sting.gatk.refdata.features.refseq; +package org.broadinstitute.sting.utils.codecs.refseq; import org.broad.tribble.Feature; import org.broadinstitute.sting.gatk.refdata.utils.GATKFeature; diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/refseq/Transcript.java b/public/java/src/org/broadinstitute/sting/utils/codecs/refseq/Transcript.java similarity index 95% rename from public/java/src/org/broadinstitute/sting/gatk/refdata/features/refseq/Transcript.java rename to public/java/src/org/broadinstitute/sting/utils/codecs/refseq/Transcript.java index d8bf12810..3e8a4fb34 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/refseq/Transcript.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/refseq/Transcript.java @@ -1,53 +1,53 @@ -package org.broadinstitute.sting.gatk.refdata.features.refseq; - -import org.broadinstitute.sting.utils.GenomeLoc; -import org.broadinstitute.sting.utils.HasGenomeLocation; - -import java.util.List; - -/** - * Created by IntelliJ IDEA. - * User: asivache - * Date: Sep 22, 2009 - * Time: 5:22:30 PM - * To change this template use File | Settings | File Templates. - */ -public interface Transcript extends HasGenomeLocation { - - /** Returns id of the transcript (RefSeq NM_* id) */ - public String getTranscriptId(); - /** Returns coding strand of the transcript, 1 or -1 for positive or negative strand, respectively */ - public int getStrand(); - /** Returns transcript's full genomic interval (includes all exons with UTRs) */ - public GenomeLoc getLocation(); - /** Returns genomic interval of the coding sequence (does not include - * UTRs, but still includes introns, since it's a single interval on the DNA) - */ - public GenomeLoc getCodingLocation(); - /** Name of the gene this transcript corresponds to (typically NOT gene id such as Entrez etc, - * but the implementation can decide otherwise) - */ - public String getGeneName(); - /** Number of exons in this transcript */ - public int getNumExons(); - /** Genomic location of the n-th exon; expected to throw an exception (runtime) if n is out of bounds */ - public GenomeLoc getExonLocation(int n); - - /** Returns the list of all exons in this transcript, as genomic intervals */ - public List getExons(); - - /** Returns true if the specified interval 'that' overlaps with the full genomic interval of this transcript */ - public boolean overlapsP (GenomeLoc that); - - /** Returns true if the specified interval 'that' overlaps with the coding genomic interval of this transcript. - * NOTE: since "coding interval" is still a single genomic interval, it will not contain UTRs of the outermost exons, - * but it will still contain introns and/or exons internal to this genomic locus that are not spliced into this transcript. - * @see #overlapsExonP - */ - public boolean overlapsCodingP (GenomeLoc that); - - /** Returns true if the specified interval 'that' overlaps with any of the exons actually spliced into this transcript */ - public boolean overlapsExonP (GenomeLoc that); - - -} +package org.broadinstitute.sting.utils.codecs.refseq; + +import org.broadinstitute.sting.utils.GenomeLoc; +import org.broadinstitute.sting.utils.HasGenomeLocation; + +import java.util.List; + +/** + * Created by IntelliJ IDEA. + * User: asivache + * Date: Sep 22, 2009 + * Time: 5:22:30 PM + * To change this template use File | Settings | File Templates. + */ +public interface Transcript extends HasGenomeLocation { + + /** Returns id of the transcript (RefSeq NM_* id) */ + public String getTranscriptId(); + /** Returns coding strand of the transcript, 1 or -1 for positive or negative strand, respectively */ + public int getStrand(); + /** Returns transcript's full genomic interval (includes all exons with UTRs) */ + public GenomeLoc getLocation(); + /** Returns genomic interval of the coding sequence (does not include + * UTRs, but still includes introns, since it's a single interval on the DNA) + */ + public GenomeLoc getCodingLocation(); + /** Name of the gene this transcript corresponds to (typically NOT gene id such as Entrez etc, + * but the implementation can decide otherwise) + */ + public String getGeneName(); + /** Number of exons in this transcript */ + public int getNumExons(); + /** Genomic location of the n-th exon; expected to throw an exception (runtime) if n is out of bounds */ + public GenomeLoc getExonLocation(int n); + + /** Returns the list of all exons in this transcript, as genomic intervals */ + public List getExons(); + + /** Returns true if the specified interval 'that' overlaps with the full genomic interval of this transcript */ + public boolean overlapsP (GenomeLoc that); + + /** Returns true if the specified interval 'that' overlaps with the coding genomic interval of this transcript. + * NOTE: since "coding interval" is still a single genomic interval, it will not contain UTRs of the outermost exons, + * but it will still contain introns and/or exons internal to this genomic locus that are not spliced into this transcript. + * @see #overlapsExonP + */ + public boolean overlapsCodingP (GenomeLoc that); + + /** Returns true if the specified interval 'that' overlaps with any of the exons actually spliced into this transcript */ + public boolean overlapsExonP (GenomeLoc that); + + +} diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/sampileup/SAMPileupCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/sampileup/SAMPileupCodec.java similarity index 98% rename from public/java/src/org/broadinstitute/sting/gatk/refdata/features/sampileup/SAMPileupCodec.java rename to public/java/src/org/broadinstitute/sting/utils/codecs/sampileup/SAMPileupCodec.java index 43e2c3ff5..f4048d37d 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/sampileup/SAMPileupCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/sampileup/SAMPileupCodec.java @@ -23,7 +23,7 @@ * THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package org.broadinstitute.sting.gatk.refdata.features.sampileup; +package org.broadinstitute.sting.utils.codecs.sampileup; import org.broad.tribble.Feature; import org.broad.tribble.FeatureCodec; @@ -35,7 +35,7 @@ import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; -import static org.broadinstitute.sting.gatk.refdata.features.sampileup.SAMPileupFeature.VariantType; +import static org.broadinstitute.sting.utils.codecs.sampileup.SAMPileupFeature.VariantType; /** * A Tribble encoder / decoder for SAM pileup data. diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/sampileup/SAMPileupFeature.java b/public/java/src/org/broadinstitute/sting/utils/codecs/sampileup/SAMPileupFeature.java similarity index 99% rename from public/java/src/org/broadinstitute/sting/gatk/refdata/features/sampileup/SAMPileupFeature.java rename to public/java/src/org/broadinstitute/sting/utils/codecs/sampileup/SAMPileupFeature.java index 378f26934..eb33243e3 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/sampileup/SAMPileupFeature.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/sampileup/SAMPileupFeature.java @@ -23,7 +23,7 @@ * THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ -package org.broadinstitute.sting.gatk.refdata.features.sampileup; +package org.broadinstitute.sting.utils.codecs.sampileup; import net.sf.samtools.util.StringUtil; import org.broad.tribble.Feature; diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/samread/SAMReadCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/samread/SAMReadCodec.java similarity index 98% rename from public/java/src/org/broadinstitute/sting/gatk/refdata/features/samread/SAMReadCodec.java rename to public/java/src/org/broadinstitute/sting/utils/codecs/samread/SAMReadCodec.java index 039b8adde..f6861e585 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/samread/SAMReadCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/samread/SAMReadCodec.java @@ -22,7 +22,7 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -package org.broadinstitute.sting.gatk.refdata.features.samread; +package org.broadinstitute.sting.utils.codecs.samread; import net.sf.samtools.Cigar; import net.sf.samtools.TextCigarCodec; diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/samread/SAMReadFeature.java b/public/java/src/org/broadinstitute/sting/utils/codecs/samread/SAMReadFeature.java similarity index 98% rename from public/java/src/org/broadinstitute/sting/gatk/refdata/features/samread/SAMReadFeature.java rename to public/java/src/org/broadinstitute/sting/utils/codecs/samread/SAMReadFeature.java index 7f12b2b2f..fc1bf89af 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/samread/SAMReadFeature.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/samread/SAMReadFeature.java @@ -22,7 +22,7 @@ * OTHER DEALINGS IN THE SOFTWARE. */ -package org.broadinstitute.sting.gatk.refdata.features.samread; +package org.broadinstitute.sting.utils.codecs.samread; import org.broad.tribble.Feature; diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/table/BedTableCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/table/BedTableCodec.java similarity index 94% rename from public/java/src/org/broadinstitute/sting/gatk/refdata/features/table/BedTableCodec.java rename to public/java/src/org/broadinstitute/sting/utils/codecs/table/BedTableCodec.java index 745ccdd9f..6fe1907e3 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/table/BedTableCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/table/BedTableCodec.java @@ -1,4 +1,4 @@ -package org.broadinstitute.sting.gatk.refdata.features.table; +package org.broadinstitute.sting.utils.codecs.table; import org.broad.tribble.Feature; import org.broadinstitute.sting.gatk.refdata.ReferenceDependentFeatureCodec; diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/table/TableCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/table/TableCodec.java similarity index 97% rename from public/java/src/org/broadinstitute/sting/gatk/refdata/features/table/TableCodec.java rename to public/java/src/org/broadinstitute/sting/utils/codecs/table/TableCodec.java index 4e734294f..2ce7c679e 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/table/TableCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/table/TableCodec.java @@ -1,4 +1,4 @@ -package org.broadinstitute.sting.gatk.refdata.features.table; +package org.broadinstitute.sting.utils.codecs.table; import org.broad.tribble.Feature; import org.broad.tribble.readers.LineReader; diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/table/TableFeature.java b/public/java/src/org/broadinstitute/sting/utils/codecs/table/TableFeature.java similarity index 96% rename from public/java/src/org/broadinstitute/sting/gatk/refdata/features/table/TableFeature.java rename to public/java/src/org/broadinstitute/sting/utils/codecs/table/TableFeature.java index ca73ee960..a85849f0b 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/features/table/TableFeature.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/table/TableFeature.java @@ -1,4 +1,4 @@ -package org.broadinstitute.sting.gatk.refdata.features.table; +package org.broadinstitute.sting.utils.codecs.table; import org.broad.tribble.Feature; import org.broadinstitute.sting.utils.GenomeLoc; diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFUtils.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFUtils.java index f43891e77..c0a04c81f 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCFUtils.java @@ -180,4 +180,19 @@ public class VCFUtils { return new HashSet(map.values()); } + + public static String rsIDOfFirstRealVariant(List VCs, VariantContext.Type type) { + if ( VCs == null ) + return null; + + String rsID = null; + for ( VariantContext vc : VCs ) { + if ( vc.getType() == type ) { + rsID = vc.getID(); + break; + } + } + + return rsID; + } } \ No newline at end of file diff --git a/public/java/test/org/broadinstitute/sting/commandline/ParsingEngineUnitTest.java b/public/java/test/org/broadinstitute/sting/commandline/ParsingEngineUnitTest.java index 79e9172dd..013a37a88 100755 --- a/public/java/test/org/broadinstitute/sting/commandline/ParsingEngineUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/commandline/ParsingEngineUnitTest.java @@ -26,7 +26,6 @@ package org.broadinstitute.sting.commandline; import org.broad.tribble.Feature; -import org.broadinstitute.sting.gatk.refdata.features.beagle.BeagleFeature; import org.broadinstitute.sting.utils.exceptions.UserException; import org.broadinstitute.sting.utils.variantcontext.VariantContext; import org.testng.Assert; @@ -35,7 +34,6 @@ import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; import org.testng.annotations.BeforeMethod; import org.testng.annotations.Test; -import javax.script.Bindings; import java.util.List; import java.util.EnumSet; /** diff --git a/public/java/test/org/broadinstitute/sting/gatk/datasources/providers/ReferenceOrderedViewUnitTest.java b/public/java/test/org/broadinstitute/sting/gatk/datasources/providers/ReferenceOrderedViewUnitTest.java index f782580e2..85ae1e1f7 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/datasources/providers/ReferenceOrderedViewUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/datasources/providers/ReferenceOrderedViewUnitTest.java @@ -10,7 +10,7 @@ import org.testng.Assert; import org.broadinstitute.sting.BaseTest; import org.broadinstitute.sting.gatk.datasources.reads.Shard; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; -import org.broadinstitute.sting.gatk.refdata.features.table.TableFeature; +import org.broadinstitute.sting.utils.codecs.table.TableFeature; import org.broadinstitute.sting.gatk.refdata.utils.RMDTriplet.RMDStorageType; import org.broadinstitute.sting.utils.GenomeLocParser; import org.broadinstitute.sting.utils.fasta.CachingIndexedFastaSequenceFile; diff --git a/public/java/test/org/broadinstitute/sting/gatk/datasources/rmd/ReferenceOrderedDataPoolUnitTest.java b/public/java/test/org/broadinstitute/sting/gatk/datasources/rmd/ReferenceOrderedDataPoolUnitTest.java index bd4f93d24..d45f6e667 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/datasources/rmd/ReferenceOrderedDataPoolUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/datasources/rmd/ReferenceOrderedDataPoolUnitTest.java @@ -4,7 +4,7 @@ import org.broadinstitute.sting.commandline.Tags; import org.broadinstitute.sting.gatk.refdata.tracks.RMDTrackBuilder; import org.testng.Assert; import org.broadinstitute.sting.BaseTest; -import org.broadinstitute.sting.gatk.refdata.features.table.TableFeature; +import org.broadinstitute.sting.utils.codecs.table.TableFeature; import org.broadinstitute.sting.gatk.refdata.utils.LocationAwareSeekableRODIterator; import org.broadinstitute.sting.gatk.refdata.utils.RMDTriplet; import org.broadinstitute.sting.gatk.refdata.utils.RMDTriplet.RMDStorageType; diff --git a/public/java/test/org/broadinstitute/sting/gatk/refdata/RefMetaDataTrackerUnitTest.java b/public/java/test/org/broadinstitute/sting/gatk/refdata/RefMetaDataTrackerUnitTest.java index fbd30bc8a..1e39fd26f 100644 --- a/public/java/test/org/broadinstitute/sting/gatk/refdata/RefMetaDataTrackerUnitTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/refdata/RefMetaDataTrackerUnitTest.java @@ -31,7 +31,7 @@ import org.broadinstitute.sting.BaseTest; import org.broadinstitute.sting.commandline.RodBinding; import org.broadinstitute.sting.commandline.Tags; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; -import org.broadinstitute.sting.gatk.refdata.features.table.TableFeature; +import org.broadinstitute.sting.utils.codecs.table.TableFeature; import org.broadinstitute.sting.gatk.refdata.utils.GATKFeature; import org.broadinstitute.sting.gatk.refdata.utils.RODRecordList; import org.broadinstitute.sting.utils.GenomeLoc; 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 5d662ffed..90262b9c1 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 @@ -29,8 +29,8 @@ import net.sf.picard.reference.IndexedFastaSequenceFile; import org.broad.tribble.Feature; import org.broad.tribble.FeatureCodec; import org.broadinstitute.sting.BaseTest; -import org.broadinstitute.sting.gatk.refdata.features.table.BedTableCodec; -import org.broadinstitute.sting.gatk.refdata.features.table.TableFeature; +import org.broadinstitute.sting.utils.codecs.table.BedTableCodec; +import org.broadinstitute.sting.utils.codecs.table.TableFeature; import org.broadinstitute.sting.utils.GenomeLocParser; import org.broadinstitute.sting.utils.codecs.vcf.VCF3Codec; import org.broadinstitute.sting.utils.codecs.vcf.VCFCodec; From 2d41ba15a4da7943be033ff7e91ea3d53ee56861 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 18 Aug 2011 10:31:32 -0400 Subject: [PATCH 069/138] Vastly better Tribble help message Here's a new example: ##### ERROR ------------------------------------------------------------------------------------------ ##### ERROR A USER ERROR has occurred (version 1.1-520-g76495cd): ##### ERROR The invalid arguments or inputs must be corrected before the GATK can proceed ##### ERROR Please do not post this error to the GATK forum ##### ERROR ##### ERROR See the documentation (rerun with -h) for this tool to view allowable command-line arguments. ##### ERROR Visit our wiki for extensive documentation http://www.broadinstitute.org/gsa/wiki ##### ERROR Visit our forum to view answers to commonly asked questions http://getsatisfaction.com/gsa ##### ERROR ##### ERROR MESSAGE: Invalid command line: Failed to parse value /humgen/gsa-hpprojects/GATK/data/refGene_b37.filtered.sorted.txt for argument refSeqRodBinding. Message: Invalid command line: No tribble type was provided on the command line and the type of the file could not be determined dynamically. Please add an explicit type tag :TYPE listing the correct type from among the supported types: ##### ERROR Name FeatureType Documentation ##### ERROR BEAGLE BeagleFeature http://www.broadinstitute.org/gsa/gatkdocs/release/org_broadinstitute_sting_utils_codecs_beagle_BeagleCodec.html ##### ERROR BED BEDFeature http://www.broadinstitute.org/gsa/gatkdocs/release/org_broad_tribble_bed_BEDCodec.html ##### ERROR BEDTABLE TableFeature http://www.broadinstitute.org/gsa/gatkdocs/release/org_broadinstitute_sting_utils_codecs_table_BedTableCodec.html ##### ERROR CGVAR VariantContext http://www.broadinstitute.org/gsa/gatkdocs/release/org_broadinstitute_sting_utils_codecs_completegenomics_CGVarCodec.html ##### ERROR DBSNP DbSNPFeature http://www.broadinstitute.org/gsa/gatkdocs/release/org_broad_tribble_dbsnp_DbSNPCodec.html ##### ERROR GELITEXT GeliTextFeature http://www.broadinstitute.org/gsa/gatkdocs/release/org_broad_tribble_gelitext_GeliTextCodec.html ##### ERROR MAF MafFeature http://www.broadinstitute.org/gsa/gatkdocs/release/org_broadinstitute_sting_gatk_features_maf_MafCodec.html ##### ERROR MILLSDEVINE VariantContext http://www.broadinstitute.org/gsa/gatkdocs/release/org_broadinstitute_sting_utils_codecs_MillsDevineCodec.html ##### ERROR RAWHAPMAP RawHapMapFeature http://www.broadinstitute.org/gsa/gatkdocs/release/org_broadinstitute_sting_utils_codecs_hapmap_RawHapMapCodec.html ##### ERROR REFSEQ RefSeqFeature http://www.broadinstitute.org/gsa/gatkdocs/release/org_broadinstitute_sting_utils_codecs_refseq_RefSeqCodec.html ##### ERROR SAMPILEUP SAMPileupFeature http://www.broadinstitute.org/gsa/gatkdocs/release/org_broadinstitute_sting_utils_codecs_sampileup_SAMPileupCodec.html ##### ERROR SAMREAD SAMReadFeature http://www.broadinstitute.org/gsa/gatkdocs/release/org_broadinstitute_sting_utils_codecs_samread_SAMReadCodec.html ##### ERROR SNPEFF SnpEffFeature http://www.broadinstitute.org/gsa/gatkdocs/release/org_broadinstitute_sting_utils_codecs_snpEff_SnpEffCodec.html ##### ERROR SOAPSNP VariantContext http://www.broadinstitute.org/gsa/gatkdocs/release/org_broadinstitute_sting_utils_codecs_soapsnp_SoapSNPCodec.html ##### ERROR TABLE TableFeature http://www.broadinstitute.org/gsa/gatkdocs/release/org_broadinstitute_sting_utils_codecs_table_TableCodec.html ##### ERROR VCF VariantContext http://www.broadinstitute.org/gsa/gatkdocs/release/org_broadinstitute_sting_utils_codecs_vcf_VCFCodec.html ##### ERROR VCF3 VariantContext http://www.broadinstitute.org/gsa/gatkdocs/release/org_broadinstitute_sting_utils_codecs_vcf_VCF3Codec.html ##### ERROR ------------------------------------------------------------------------------------------ --- .../commandline/ArgumentTypeDescriptor.java | 2 +- .../sting/gatk/CommandLineGATK.java | 5 +++ .../gatk/refdata/tracks/FeatureManager.java | 40 +++++++++++++++---- .../sting/utils/help/HelpUtils.java | 6 +-- .../sting/utils/text/ListFileUtils.java | 2 +- 5 files changed, 42 insertions(+), 13 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java b/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java index 02af884a2..9f3eae610 100644 --- a/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java +++ b/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java @@ -376,7 +376,7 @@ class RodBindingArgumentTypeDescriptor extends ArgumentTypeDescriptor { } else { throw new UserException.CommandLineException( String.format("No tribble type was provided on the command line and the type of the file could not be determined dynamically. " + - "Please add an explicit type tag :TYPE listing the correct type from among the supported types: %s", + "Please add an explicit type tag :TYPE listing the correct type from among the supported types:%n%s", manager.userFriendlyListOfAvailableFeatures())); } } diff --git a/public/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java b/public/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java index 7c567f511..652dfd2fd 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java +++ b/public/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java @@ -31,6 +31,7 @@ import org.broadinstitute.sting.commandline.ArgumentCollection; import org.broadinstitute.sting.commandline.CommandLineProgram; import org.broadinstitute.sting.gatk.arguments.GATKArgumentCollection; import org.broadinstitute.sting.gatk.filters.ReadFilter; +import org.broadinstitute.sting.gatk.refdata.tracks.FeatureManager; import org.broadinstitute.sting.gatk.walkers.Attribution; import org.broadinstitute.sting.gatk.walkers.Walker; import org.broadinstitute.sting.utils.exceptions.UserException; @@ -174,6 +175,10 @@ public class CommandLineGATK extends CommandLineExecutable { StringBuilder additionalHelp = new StringBuilder(); Formatter formatter = new Formatter(additionalHelp); + formatter.format("Available Reference Ordered Data types:%n"); + formatter.format(new FeatureManager().userFriendlyListOfAvailableFeatures()); + formatter.format("%n"); + formatter.format("For a full description of this walker, see its GATKdocs at:%n"); formatter.format("%s%n", HelpUtils.helpLinksToGATKDocs(walkerType)); diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/tracks/FeatureManager.java b/public/java/src/org/broadinstitute/sting/gatk/refdata/tracks/FeatureManager.java index 26a400071..7237f8bb5 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/tracks/FeatureManager.java +++ b/public/java/src/org/broadinstitute/sting/gatk/refdata/tracks/FeatureManager.java @@ -36,7 +36,9 @@ import org.broadinstitute.sting.utils.GenomeLocParser; import org.broadinstitute.sting.utils.Utils; import org.broadinstitute.sting.utils.classloader.PluginManager; import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; +import org.broadinstitute.sting.utils.help.HelpUtils; +import javax.mail.Header; import java.io.File; import java.util.*; @@ -50,7 +52,7 @@ import java.util.*; * @author depristo */ public class FeatureManager { - public static class FeatureDescriptor { + public static class FeatureDescriptor implements Comparable { final String name; final FeatureCodec codec; @@ -62,6 +64,7 @@ public class FeatureManager { public String getName() { return name; } + public String getSimpleFeatureName() { return getFeatureClass().getSimpleName(); } public FeatureCodec getCodec() { return codec; } @@ -70,13 +73,18 @@ public class FeatureManager { @Override public String toString() { - return String.format("FeatureDescriptor name=%s codec=%s feature=%s", getName(), getCodecClass().getName(), getFeatureClass().getName()); + return String.format("FeatureDescriptor name=%s codec=%s feature=%s", + getName(), getCodecClass().getName(), getFeatureClass().getName()); + } + + @Override + public int compareTo(FeatureDescriptor o) { + return getName().compareTo(o.getName()); } } private final PluginManager pluginManager; - private final Collection featureDescriptors = new HashSet(); - + private final Collection featureDescriptors = new TreeSet(); /** * Construct a FeatureManager @@ -189,10 +197,26 @@ public class FeatureManager { */ @Ensures("result != null") public String userFriendlyListOfAvailableFeatures() { - List names = new ArrayList(); - for ( final FeatureDescriptor descriptor : featureDescriptors ) - names.add(descriptor.getName()); - return Utils.join(",", names); + final String nameHeader="Name", featureHeader = "FeatureType", docHeader="Documentation"; + + int maxNameLen = nameHeader.length(), maxFeatureNameLen = featureHeader.length(); + for ( final FeatureDescriptor descriptor : featureDescriptors ) { + maxNameLen = Math.max(maxNameLen, descriptor.getName().length()); + maxFeatureNameLen = Math.max(maxFeatureNameLen, descriptor.getSimpleFeatureName().length()); + } + + StringBuilder docs = new StringBuilder(); + String format = "%" + maxNameLen + "s %" + maxFeatureNameLen + "s %s%n"; + docs.append(String.format(format, nameHeader, featureHeader, docHeader)); + for ( final FeatureDescriptor descriptor : featureDescriptors ) { + String oneDoc = String.format(format, + descriptor.getName(), + descriptor.getSimpleFeatureName(), + HelpUtils.helpLinksToGATKDocs(descriptor.getCodecClass())); + docs.append(oneDoc); + } + + return docs.toString(); } /** diff --git a/public/java/src/org/broadinstitute/sting/utils/help/HelpUtils.java b/public/java/src/org/broadinstitute/sting/utils/help/HelpUtils.java index da4e7bdaf..ef6e19933 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/HelpUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/HelpUtils.java @@ -86,9 +86,9 @@ public class HelpUtils { public static String helpLinksToGATKDocs(Class c) { String classPath = htmlFilenameForClass(c); StringBuilder b = new StringBuilder(); - b.append("release version: ").append(URL_ROOT_FOR_RELEASE_GATKDOCS).append(classPath).append("\n"); - b.append("stable version: ").append(URL_ROOT_FOR_STABLE_GATKDOCS).append(classPath).append("\n"); - b.append("unstable version: ").append(URL_ROOT_FOR_UNSTABLE_GATKDOCS).append(classPath).append("\n"); + b.append(URL_ROOT_FOR_RELEASE_GATKDOCS).append(classPath); + //b.append("stable version: ").append(URL_ROOT_FOR_STABLE_GATKDOCS).append(classPath).append("\n"); + //b.append("unstable version: ").append(URL_ROOT_FOR_UNSTABLE_GATKDOCS).append(classPath).append("\n"); return b.toString(); } } \ No newline at end of file diff --git a/public/java/src/org/broadinstitute/sting/utils/text/ListFileUtils.java b/public/java/src/org/broadinstitute/sting/utils/text/ListFileUtils.java index b0e25e55b..8584ce3bb 100644 --- a/public/java/src/org/broadinstitute/sting/utils/text/ListFileUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/text/ListFileUtils.java @@ -161,7 +161,7 @@ public class ListFileUtils { if ( ! rodBinding.getType().isAssignableFrom(descriptor.getFeatureClass()) ) throw new UserException.BadArgumentValue(rodBinding.getName(), String.format("Field %s expected type %s, but the type of the input file provided on the command line was %s producing %s. Please make sure that you have provided the correct file type and/or that you are not binding your rod to a name matching one of the available types.", - rodBinding.getName(), rodBinding.getType(), descriptor.getName(), descriptor.getFeatureClass())); + rodBinding.getName(), rodBinding.getType(), descriptor.getName(), descriptor.getSimpleFeatureName())); rodBindings.add(triplet); From 47bbddb7244c71ade9198e3f52126e20a9dff1d1 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 18 Aug 2011 10:47:16 -0400 Subject: [PATCH 070/138] Now provides type-specific user feedback For RodBinding error messages now list only the Tribble types that produce VariantContexts --- .../commandline/ArgumentTypeDescriptor.java | 7 +++-- .../gatk/refdata/tracks/FeatureManager.java | 28 ++++++++++++++----- .../sting/utils/text/ListFileUtils.java | 9 +++--- 3 files changed, 30 insertions(+), 14 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java b/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java index 9f3eae610..dc32fcc16 100644 --- a/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java +++ b/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java @@ -338,6 +338,8 @@ class RodBindingArgumentTypeDescriptor extends ArgumentTypeDescriptor { public Object parse(ParsingEngine parsingEngine, ArgumentSource source, Type type, ArgumentMatches matches) { ArgumentDefinition defaultDefinition = createDefaultArgumentDefinition(source); String value = getArgumentValue( defaultDefinition, matches ); + Class parameterType = getParameterizedTypeClass(type); + try { String name = defaultDefinition.fullName; String tribbleType = null; @@ -376,15 +378,14 @@ class RodBindingArgumentTypeDescriptor extends ArgumentTypeDescriptor { } else { throw new UserException.CommandLineException( String.format("No tribble type was provided on the command line and the type of the file could not be determined dynamically. " + - "Please add an explicit type tag :TYPE listing the correct type from among the supported types:%n%s", - manager.userFriendlyListOfAvailableFeatures())); + "Please add an explicit type tag :NAME listing the correct type from among the supported types:%n%s", + manager.userFriendlyListOfAvailableFeatures(parameterType))); } } } } Constructor ctor = (makeRawTypeIfNecessary(type)).getConstructor(Class.class, String.class, String.class, String.class, Tags.class); - Class parameterType = getParameterizedTypeClass(type); RodBinding result = (RodBinding)ctor.newInstance(parameterType, name, value, tribbleType, tags); parsingEngine.addTags(result,tags); parsingEngine.addRodBinding(result); diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/tracks/FeatureManager.java b/public/java/src/org/broadinstitute/sting/gatk/refdata/tracks/FeatureManager.java index 7237f8bb5..9a565f1cb 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/tracks/FeatureManager.java +++ b/public/java/src/org/broadinstitute/sting/gatk/refdata/tracks/FeatureManager.java @@ -197,23 +197,37 @@ public class FeatureManager { */ @Ensures("result != null") public String userFriendlyListOfAvailableFeatures() { + return userFriendlyListOfAvailableFeatures(Feature.class); + } + + /** + * Returns a list of the available tribble track names (vcf,dbsnp,etc) that we can load + * restricted to only Codecs producting Features consistent with the requiredFeatureType + * @return + */ + @Ensures("result != null") + public String userFriendlyListOfAvailableFeatures(Class requiredFeatureType) { final String nameHeader="Name", featureHeader = "FeatureType", docHeader="Documentation"; int maxNameLen = nameHeader.length(), maxFeatureNameLen = featureHeader.length(); for ( final FeatureDescriptor descriptor : featureDescriptors ) { - maxNameLen = Math.max(maxNameLen, descriptor.getName().length()); - maxFeatureNameLen = Math.max(maxFeatureNameLen, descriptor.getSimpleFeatureName().length()); + if ( requiredFeatureType.isAssignableFrom(descriptor.getFeatureClass()) ) { + maxNameLen = Math.max(maxNameLen, descriptor.getName().length()); + maxFeatureNameLen = Math.max(maxFeatureNameLen, descriptor.getSimpleFeatureName().length()); + } } StringBuilder docs = new StringBuilder(); String format = "%" + maxNameLen + "s %" + maxFeatureNameLen + "s %s%n"; docs.append(String.format(format, nameHeader, featureHeader, docHeader)); for ( final FeatureDescriptor descriptor : featureDescriptors ) { - String oneDoc = String.format(format, - descriptor.getName(), - descriptor.getSimpleFeatureName(), - HelpUtils.helpLinksToGATKDocs(descriptor.getCodecClass())); - docs.append(oneDoc); + if ( requiredFeatureType.isAssignableFrom(descriptor.getFeatureClass()) ) { + String oneDoc = String.format(format, + descriptor.getName(), + descriptor.getSimpleFeatureName(), + HelpUtils.helpLinksToGATKDocs(descriptor.getCodecClass())); + docs.append(oneDoc); + } } return docs.toString(); diff --git a/public/java/src/org/broadinstitute/sting/utils/text/ListFileUtils.java b/public/java/src/org/broadinstitute/sting/utils/text/ListFileUtils.java index 8584ce3bb..9d4b23a8b 100644 --- a/public/java/src/org/broadinstitute/sting/utils/text/ListFileUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/text/ListFileUtils.java @@ -156,12 +156,13 @@ public class ListFileUtils { FeatureManager.FeatureDescriptor descriptor = builderForValidation.getByTriplet(triplet); if ( descriptor == null ) throw new UserException.UnknownTribbleType(rodBinding.getTribbleType(), - String.format("Field %s had provided type %s but there's no such Tribble type. Available types are %s", - rodBinding.getName(), rodBinding.getTribbleType(), builderForValidation.userFriendlyListOfAvailableFeatures())); + String.format("Field %s had provided type %s but there's no such Tribble type. The compatible types are: %n%s", + rodBinding.getName(), rodBinding.getTribbleType(), builderForValidation.userFriendlyListOfAvailableFeatures(rodBinding.getType()))); if ( ! rodBinding.getType().isAssignableFrom(descriptor.getFeatureClass()) ) throw new UserException.BadArgumentValue(rodBinding.getName(), - String.format("Field %s expected type %s, but the type of the input file provided on the command line was %s producing %s. Please make sure that you have provided the correct file type and/or that you are not binding your rod to a name matching one of the available types.", - rodBinding.getName(), rodBinding.getType(), descriptor.getName(), descriptor.getSimpleFeatureName())); + String.format("Field %s expects Features of type %s, but the input file produces Features of type %s. The compatible types are: %n%s", + rodBinding.getName(), rodBinding.getType().getSimpleName(), descriptor.getSimpleFeatureName(), + builderForValidation.userFriendlyListOfAvailableFeatures(rodBinding.getType()))); rodBindings.add(triplet); From bb79d3edae56c8f32fb26425db86cdec94ed7d58 Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Thu, 18 Aug 2011 10:57:48 -0400 Subject: [PATCH 071/138] Added GATKDocs for the BQSR walkers. --- .../analyzecovariates/AnalyzeCovariates.java | 75 ++++++++++++++-- .../sting/analyzecovariates/package-info.java | 4 + .../CountCovariatesGatherer.java | 28 +++++- .../recalibration/CountCovariatesWalker.java | 70 +++++++++++---- .../recalibration/RecalDataManager.java | 7 ++ .../RecalibrationArgumentCollection.java | 15 ++++ .../TableRecalibrationWalker.java | 89 ++++++++++++++----- 7 files changed, 241 insertions(+), 47 deletions(-) create mode 100644 public/java/src/org/broadinstitute/sting/analyzecovariates/package-info.java diff --git a/public/java/src/org/broadinstitute/sting/analyzecovariates/AnalyzeCovariates.java b/public/java/src/org/broadinstitute/sting/analyzecovariates/AnalyzeCovariates.java index b9e380295..9b316f077 100755 --- a/public/java/src/org/broadinstitute/sting/analyzecovariates/AnalyzeCovariates.java +++ b/public/java/src/org/broadinstitute/sting/analyzecovariates/AnalyzeCovariates.java @@ -28,11 +28,13 @@ package org.broadinstitute.sting.analyzecovariates; import org.broadinstitute.sting.commandline.Argument; import org.broadinstitute.sting.commandline.CommandLineProgram; import org.broadinstitute.sting.commandline.Input; +import org.broadinstitute.sting.gatk.CommandLineGATK; import org.broadinstitute.sting.gatk.walkers.recalibration.Covariate; import org.broadinstitute.sting.gatk.walkers.recalibration.RecalDatum; import org.broadinstitute.sting.gatk.walkers.recalibration.RecalibrationArgumentCollection; import org.broadinstitute.sting.utils.classloader.PluginManager; import org.broadinstitute.sting.utils.exceptions.DynamicClassResolutionException; +import org.broadinstitute.sting.utils.help.DocumentedGATKFeature; import org.broadinstitute.sting.utils.text.XReadLines; import java.io.*; @@ -42,19 +44,71 @@ import java.util.Map; import java.util.regex.Pattern; /** - * Created by IntelliJ IDEA. - * User: rpoplin - * Date: Dec 1, 2009 + * Call R scripts to plot residual error versus the various covariates. * - * Create collapsed versions of the recal csv file and call R scripts to plot residual error versus the various covariates. + *

      + * After counting covariates in either the initial BAM File or again in the recalibrated BAM File, an analysis tool is available which + * reads the .csv file and outputs several PDF (and .dat) files for each read group in the given BAM. These PDF files graphically + * show the various metrics and characteristics of the reported quality scores (often in relation to the empirical qualities). + * In order to show that any biases in the reported quality scores have been generally fixed through recalibration one should run + * CountCovariates again on a bam file produced by TableRecalibration. In this way users can compare the analysis plots generated + * by pre-recalibration and post-recalibration .csv files. Our usual chain of commands that we use to generate plots of residual + * error is: CountCovariates, TableRecalibrate, samtools index on the recalibrated bam file, CountCovariates again on the recalibrated + * bam file, and then AnalyzeCovariates on both the before and after recal_data.csv files to see the improvement in recalibration. + * + *

      + * The color coding along with the RMSE is included in the plots to give some indication of the number of observations that went into + * each of the quality score estimates. It is defined as follows for N, the number of observations: + * + *

        + *
      • light blue means N < 1,000
      • + *
      • cornflower blue means 1,000 <= N < 10,000
      • + *
      • dark blue means N >= 10,000
      • + *
      • The pink dots indicate points whose quality scores are special codes used by the aligner and which are mathematically + * meaningless and so aren't included in any of the numerical calculations.
      • + *
      + * + *

      + * NOTE: For those running this tool externally from the Broad, it is crucial to note that both the -Rscript and -resources options + * must be changed from the default. -Rscript needs to point to your installation of Rscript (this is the scripting version of R, + * not the interactive version) while -resources needs to point to the folder holding the R scripts that are used. For those using + * this tool as part of the Binary Distribution the -resources should point to the resources folder that is part of the tarball. + * For those using this tool by building from the git repository the -resources should point to the R/ subdirectory of the Sting checkout. + * + *

      + * See the GATK wiki for a tutorial and example recalibration accuracy plots. + * http://www.broadinstitute.org/gsa/wiki/index.php/Base_quality_score_recalibration + * + *

      Input

      + *

      + * The recalibration table file in CSV format that was generated by the CountCovariates walker. + *

      + * + *

      Examples

      + *
      + * java -Xmx4g -jar AnalyzeCovariates.jar \
      + *   -recalFile /path/to/recal.table.csv  \
      + *   -outputDir /path/to/output_dir/  \
      + *   -resources resources/  \
      + *   -ignoreQ 5
      + * 
      + * */ +@DocumentedGATKFeature( + groupName = "AnalyzeCovariates", + summary = "Package to plot residual accuracy versus error covariates for the base quality score recalibrator") public class AnalyzeCovariates extends CommandLineProgram { ///////////////////////////// // Command Line Arguments ///////////////////////////// - + /** + * 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. + */ @Input(fullName = "recal_file", shortName = "recalFile", doc = "The input recal csv file to analyze", required = false) 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) @@ -67,11 +121,20 @@ public class AnalyzeCovariates extends CommandLineProgram { private int IGNORE_QSCORES_LESS_THAN = 5; @Argument(fullName = "numRG", shortName = "numRG", doc = "Only process N read groups. Default value: -1 (process all read groups)", required = false) private int NUM_READ_GROUPS_TO_PROCESS = -1; // -1 means process all read groups + + /** + * Combinations of covariates in which there are zero mismatches technically have infinite quality. We get around this situation + * by capping at the specified value. We've found that Q40 is too low when using a more completely database of known variation like dbSNP build 132 or later. + */ @Argument(fullName="max_quality_score", shortName="maxQ", required = false, doc="The integer value at which to cap the quality scores, default is 50") private int MAX_QUALITY_SCORE = 50; + + /** + * This argument is useful for comparing before/after plots and you want the axes to match each other. + */ @Argument(fullName="max_histogram_value", shortName="maxHist", required = false, doc="If supplied, this value will be the max value of the histogram plots") private int MAX_HISTOGRAM_VALUE = 0; - @Argument(fullName="do_indel_quality", shortName="indels", required = false, doc="If supplied, this value will be the max value of the histogram plots") + @Argument(fullName="do_indel_quality", shortName="indels", required = false, doc="If supplied, do indel quality plotting") private boolean DO_INDEL_QUALITY = false; diff --git a/public/java/src/org/broadinstitute/sting/analyzecovariates/package-info.java b/public/java/src/org/broadinstitute/sting/analyzecovariates/package-info.java new file mode 100644 index 000000000..9350e4a66 --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/analyzecovariates/package-info.java @@ -0,0 +1,4 @@ +/** + * Package to plot residual accuracy versus error covariates for the base quality score recalibrator. + */ +package org.broadinstitute.sting.analyzecovariates; \ No newline at end of file diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/CountCovariatesGatherer.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/CountCovariatesGatherer.java index fc6b3daee..9b0824ed0 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/CountCovariatesGatherer.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/CountCovariatesGatherer.java @@ -1,3 +1,28 @@ +/* + * 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.recalibration; import org.broadinstitute.sting.commandline.Gatherer; @@ -12,11 +37,8 @@ import java.util.List; import java.util.regex.Pattern; /** - * Created by IntelliJ IDEA. * User: carneiro * Date: 3/29/11 - * Time: 3:54 PM - * To change this template use File | Settings | File Templates. */ 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 b4739f366..5ffc61fe3 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 @@ -50,22 +50,47 @@ import java.util.List; import java.util.Map; /** - * First pass of the recalibration. Generates recalibration table based on various user-specified covariates (such as reported quality score, cycle, and dinucleotide). + * First pass of the base quality score recalibration -- Generates recalibration table based on various user-specified covariates (such as reported quality score, cycle, and dinucleotide). * - * This walker is designed to work as the first pass in a two-pass processing step. - * It does a by-locus traversal operating only at sites that are not in dbSNP. - * We assume that all reference mismatches we see are therefore errors and indicative of poor base quality. - * This walker generates tables based on various user-specified covariates (such as read group, reported quality score, cycle, and dinucleotide) - * Since there is a large amount of data one can then calculate an empirical probability of error - * given the particular covariates seen at this site, where p(error) = num mismatches / num observations - * The output file is a CSV list of (the several covariate values, num observations, num mismatches, empirical quality score) - * The first non-comment line of the output file gives the name of the covariates that were used for this calculation. + *

      + * This walker is designed to work as the first pass in a two-pass processing step. It does a by-locus traversal operating + * only at sites that are not in dbSNP. We assume that all reference mismatches we see are therefore errors and indicative + * of poor base quality. This walker generates tables based on various user-specified covariates (such as read group, + * reported quality score, cycle, and dinucleotide). Since there is a large amount of data one can then calculate an empirical + * probability of error given the particular covariates seen at this site, where p(error) = num mismatches / num observations. + * The output file is a CSV list of (the several covariate values, num observations, num mismatches, empirical quality score). + *

      + * Note: ReadGroupCovariate and QualityScoreCovariate are required covariates and will be added for the user regardless of whether or not they were specified. * - * Note: ReadGroupCovariate and QualityScoreCovariate are required covariates and will be added for the user regardless of whether or not they were specified - * Note: This walker is designed to be used in conjunction with TableRecalibrationWalker. + *

      + * See the GATK wiki for a tutorial and example recalibration accuracy plots. + * http://www.broadinstitute.org/gsa/wiki/index.php/Base_quality_score_recalibration + * + *

      Input

      + *

      + * A database of known polymorphic sites to skip over. + *

      + * + *

      Output

      + *

      + * A recalibration table file in CSV format that is used by the TableRecalibration walker. + *

      + * + *

      Examples

      + *
      + * java -Xmx4g -jar GenomeAnalysisTK.jar \
      + *   -R resources/Homo_sapiens_assembly18.fasta \
      + *   -knownSites bundle/hg18/dbsnp_132.hg18.vcf \
      + *   -knownSites another/optional/setOfSitesToMask.vcf \
      + *   -I my_reads.bam \
      + *   -T CountCovariates \
      + *   -cov ReadGroupCovariate \
      + *   -cov QualityScoreCovariate \
      + *   -cov CycleCovariate \
      + *   -cov DinucCovariate \
      + *   -recalFile my_reads.recal_data.csv
      + * 
      * - * @author rpoplin - * @since Nov 3, 2009 */ @BAQMode(ApplicationTime = BAQ.ApplicationTime.FORBIDDEN) @@ -96,8 +121,13 @@ public class CountCovariatesWalker extends LocusWalker> knownSites = Collections.emptyList(); - @Output - PrintStream out; + + /** + * 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. + */ @Output(fullName="recal_file", shortName="recalFile", required=true, doc="Filename for the output covariates table recalibration file") @Gather(CountCovariatesGatherer.class) public PrintStream RECAL_FILE; @@ -114,6 +144,10 @@ public class CountCovariatesWalker extends LocusWalker covClass : covariateClasses ) { - out.println( covClass.getSimpleName() ); + logger.info( covClass.getSimpleName() ); } - out.println(); + logger.info(""); System.exit( 0 ); // Early exit here because user requested it } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/RecalDataManager.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/RecalDataManager.java index e6d0b306c..ac25d4f13 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/RecalDataManager.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/RecalDataManager.java @@ -66,15 +66,22 @@ public class RecalDataManager { private static boolean warnUserNullPlatform = false; public enum SOLID_RECAL_MODE { + /** Treat reference inserted bases as reference matching bases. Very unsafe! */ DO_NOTHING, + /** Set reference inserted bases and the previous base (because of color space alignment details) to Q0. This is the default option. */ SET_Q_ZERO, + /** In addition to setting the quality scores to zero, also set the base itself to 'N'. This is useful to visualize in IGV. */ SET_Q_ZERO_BASE_N, + /** Look at the color quality scores and probabilistically decide to change the reference inserted base to be the base which is implied by the original color space instead of the reference. */ REMOVE_REF_BIAS } public enum SOLID_NOCALL_STRATEGY { + /** When a no call is detected throw an exception to alert the user that recalibrating this SOLiD data is unsafe. This is the default option. */ THROW_EXCEPTION, + /** Leave the read in the output bam completely untouched. This mode is only okay if the no calls are very rare. */ LEAVE_READ_UNRECALIBRATED, + /** Mark these reads as failing vendor quality checks so they can be filtered out by downstream analyses. */ PURGE_READ } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/RecalibrationArgumentCollection.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/RecalibrationArgumentCollection.java index 0e7f7d111..f31e2fc5b 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/RecalibrationArgumentCollection.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/RecalibrationArgumentCollection.java @@ -51,12 +51,27 @@ public class RecalibrationArgumentCollection { public String FORCE_PLATFORM = null; @Argument(fullName = "window_size_nqs", shortName="nqs", doc="The window size used by MinimumNQSCovariate for its calculation", required=false) public int WINDOW_SIZE = 5; + + /** + * This window size tells the module in how big of a neighborhood around the current base it should look for the minimum base quality score. + */ @Argument(fullName = "homopolymer_nback", shortName="nback", doc="The number of previous bases to look at in HomopolymerCovariate", required=false) public int HOMOPOLYMER_NBACK = 7; @Argument(fullName = "exception_if_no_tile", shortName="throwTileException", doc="If provided, TileCovariate will throw an exception when no tile can be found. The default behavior is to use tile = -1", required=false) public boolean EXCEPTION_IF_NO_TILE = false; + + /** + * CountCovariates and TableRecalibration accept a --solid_recal_mode flag which governs how the recalibrator handles the + * reads which have had the reference inserted because of color space inconsistencies. + */ @Argument(fullName="solid_recal_mode", shortName="sMode", required = false, doc="How should we recalibrate solid bases in which the reference was inserted? Options = DO_NOTHING, SET_Q_ZERO, SET_Q_ZERO_BASE_N, or REMOVE_REF_BIAS") public RecalDataManager.SOLID_RECAL_MODE SOLID_RECAL_MODE = RecalDataManager.SOLID_RECAL_MODE.SET_Q_ZERO; + + /** + * CountCovariates and TableRecalibration accept a --solid_nocall_strategy flag which governs how the recalibrator handles + * no calls in the color space tag. Unfortunately because of the reference inserted bases mentioned above, reads with no calls in + * their color space tag can not be recalibrated. + */ @Argument(fullName = "solid_nocall_strategy", shortName="solid_nocall_strategy", doc="Defines the behavior of the recalibrator when it encounters no calls in the color space. Options = THROW_EXCEPTION, LEAVE_READ_UNRECALIBRATED, or PURGE_READ", required=false) public RecalDataManager.SOLID_NOCALL_STRATEGY SOLID_NOCALL_STRATEGY = RecalDataManager.SOLID_NOCALL_STRATEGY.THROW_EXCEPTION; } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/TableRecalibrationWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/TableRecalibrationWalker.java index a044abecb..af7148803 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/TableRecalibrationWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/TableRecalibrationWalker.java @@ -52,19 +52,38 @@ import java.util.ResourceBundle; import java.util.regex.Pattern; /** - * Second pass of the recalibration. Uses the table generated by CountCovariates to update the base quality scores of the input bam file using a sequential table calculation making the base quality scores more accurately reflect the actual quality of the bases as measured by reference mismatch rate. + * Second pass of the recalibration -- Uses the table generated by CountCovariates to update the base quality scores of the input bam file using a sequential table calculation making the base quality scores more accurately reflect the actual quality of the bases as measured by reference mismatch rate. * - * This walker is designed to work as the second pass in a two-pass processing step, doing a by-read traversal. + *

      + * This walker is designed to work as the second pass in a two-pass processing step, doing a by-read traversal. For each + * base in each read this walker calculates various user-specified covariates (such as read group, reported quality score, + * cycle, and dinuc). Using these values as a key in a large hashmap the walker calculates an empirical base quality score + * and overwrites the quality score currently in the read. This walker then outputs a new bam file with these updated (recalibrated) reads. * - * For each base in each read this walker calculates various user-specified covariates (such as read group, reported quality score, cycle, and dinuc) - * Using these values as a key in a large hashmap the walker calculates an empirical base quality score and overwrites the quality score currently in the read. - * This walker then outputs a new bam file with these updated (recalibrated) reads. + *

      + * See the GATK wiki for a tutorial and example recalibration accuracy plots. + * http://www.broadinstitute.org/gsa/wiki/index.php/Base_quality_score_recalibration * - * Note: This walker expects as input the recalibration table file generated previously by CovariateCounterWalker. - * Note: This walker is designed to be used in conjunction with CovariateCounterWalker. + *

      Input

      + *

      + * The recalibration table file in CSV format that was generated by the CountCovariates walker. + *

      + * + *

      Output

      + *

      + * A bam file in which the quality scores in each read have been recalibrated. + *

      + * + *

      Examples

      + *
      + * java -Xmx4g -jar GenomeAnalysisTK.jar \
      + *   -R resources/Homo_sapiens_assembly18.fasta \
      + *   -I my_reads.bam \
      + *   -T TableRecalibration \
      + *   -o my_reads.recal.bam \
      + *   -recalFile my_reads.recal_data.csv
      + * 
      * - * @author rpoplin - * @since Nov 3, 2009 */ @BAQMode(QualityMode = BAQ.QualityMode.ADD_TAG, ApplicationTime = BAQ.ApplicationTime.ON_OUTPUT) @@ -79,24 +98,54 @@ public class TableRecalibrationWalker extends ReadWalker flag that instructs TableRecalibration to not modify + * quality scores less than but rather just write them out unmodified in the recalibrated BAM file. This is useful + * because Solexa writes Q2 and Q3 bases when the machine has really gone wrong. This would be fine in and of itself, + * but when you select a subset of these reads based on their ability to align to the reference and their dinucleotide effect, + * your Q2 and Q3 bins can be elevated to Q8 or Q10, leading to issues downstream. With the default value of 5, all Q0-Q4 bases + * are unmodified during recalibration, so they don't get inappropriately evaluated. + */ + @Argument(fullName="preserve_qscores_less_than", shortName="pQ", doc="Bases with quality scores less than this threshold won't be recalibrated. In general it's unsafe to change qualities scores below < 5, since base callers use these values to indicate random or bad bases", required=false) private int PRESERVE_QSCORES_LESS_THAN = 5; - @Argument(fullName="smoothing", shortName="sm", required = false, doc="Number of imaginary counts to add to each bin in order to smooth out bins with few data points, default=1") + + /** + * By default TableRecalibration applies a Yates' correction to account for overfitting when it calculates the empirical + * quality score, in particular, ( # mismatches + 1 ) / ( # observations + 1 ). TableRecalibration accepts a --smoothing / -sm + * argument which sets how many unobserved counts to add to every bin. Use --smoothing 0 to turn off all smoothing or, for example, + * --smoothing 15 for a large amount of smoothing. + */ + @Argument(fullName="smoothing", shortName="sm", required = false, doc="Number of imaginary counts to add to each bin in order to smooth out bins with few data points") private int SMOOTHING = 1; - @Argument(fullName="max_quality_score", shortName="maxQ", required = false, doc="The integer value at which to cap the quality scores, default=50") + + /** + * Combinations of covariates in which there are zero mismatches technically have infinite quality. We get around this situation + * by capping at the specified value. We've found that Q40 is too low when using a more completely database of known variation like dbSNP build 132 or later. + */ + @Argument(fullName="max_quality_score", shortName="maxQ", required = false, doc="The integer value at which to cap the quality scores") private int MAX_QUALITY_SCORE = 50; + + /** + * By default TableRecalibration emits the OQ field -- so you can go back and look at the original quality scores, rerun + * the system using the OQ flags, etc, on the output BAM files; to turn off emission of the OQ field use this flag. + */ @Argument(fullName="doNotWriteOriginalQuals", shortName="noOQs", required=false, doc="If true, we will not write the original quality (OQ) tag for each read") private boolean DO_NOT_WRITE_OQ = false; From a45498150a419d1e3a0fd8814e13793cdc5f7412 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Thu, 18 Aug 2011 11:18:29 -0400 Subject: [PATCH 072/138] Remove non-ascii char --- .../sting/gatk/walkers/indels/RealignerTargetCreator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/RealignerTargetCreator.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/RealignerTargetCreator.java index 08ed1af52..145d0327c 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/RealignerTargetCreator.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/RealignerTargetCreator.java @@ -57,7 +57,7 @@ import java.util.List; *

      * The local realignment tool is designed to consume one or more BAM files and to locally realign reads such that the number of mismatching bases * is minimized across all the reads. In general, a large percent of regions requiring local realignment are due to the presence of an insertion - * or deletion (indels) in the individualÕs genome with respect to the reference genome. Such alignment artifacts result in many bases mismatching + * or deletion (indels) in the individual's genome with respect to the reference genome. Such alignment artifacts result in many bases mismatching * the reference near the misalignment, which are easily mistaken as SNPs. Moreover, since read mapping algorithms operate on each read independently, * it is impossible to place reads on the reference genome such at mismatches are minimized across all reads. Consequently, even when some reads are * correctly mapped with indels, reads covering the indel near just the start or end of the read are often incorrectly mapped with respect the true indel, From f5d7cabb209352e57b7bc5f08cb2e1a97432f83b Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 18 Aug 2011 11:20:12 -0400 Subject: [PATCH 073/138] Fix for reintroducing an already solved problem. --- .../sting/gatk/CommandLineGATK.java | 2 +- .../help/DocumentedGATKFeatureHandler.java | 2 +- .../sting/utils/help/GATKDocUtils.java | 44 +++++++++++++++++++ .../sting/utils/help/HelpUtils.java | 15 ------- 4 files changed, 46 insertions(+), 17 deletions(-) create mode 100644 public/java/src/org/broadinstitute/sting/utils/help/GATKDocUtils.java diff --git a/public/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java b/public/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java index 7c567f511..8a13dadbf 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java +++ b/public/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java @@ -175,7 +175,7 @@ public class CommandLineGATK extends CommandLineExecutable { Formatter formatter = new Formatter(additionalHelp); formatter.format("For a full description of this walker, see its GATKdocs at:%n"); - formatter.format("%s%n", HelpUtils.helpLinksToGATKDocs(walkerType)); + formatter.format("%s%n", GATKDocUtils.helpLinksToGATKDocs(walkerType)); return additionalHelp.toString(); } diff --git a/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureHandler.java b/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureHandler.java index 214a1716a..ce03c8093 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureHandler.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureHandler.java @@ -71,7 +71,7 @@ public abstract class DocumentedGATKFeatureHandler { * @return */ public String getDestinationFilename(ClassDoc doc, Class clazz) { - return HelpUtils.htmlFilenameForClass(clazz); + return GATKDocUtils.htmlFilenameForClass(clazz); } /** diff --git a/public/java/src/org/broadinstitute/sting/utils/help/GATKDocUtils.java b/public/java/src/org/broadinstitute/sting/utils/help/GATKDocUtils.java new file mode 100644 index 000000000..e2909cf15 --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/utils/help/GATKDocUtils.java @@ -0,0 +1,44 @@ +/* + * 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.help; + +public class GATKDocUtils { + public final static String URL_ROOT_FOR_RELEASE_GATKDOCS = "http://www.broadinstitute.org/gsa/gatkdocs/release/"; + public final static String URL_ROOT_FOR_STABLE_GATKDOCS = "http://iwww.broadinstitute.org/gsa/gatkdocs/stable/"; + public final static String URL_ROOT_FOR_UNSTABLE_GATKDOCS = "http://iwww.broadinstitute.org/gsa/gatkdocs/unstable/"; + + public static String htmlFilenameForClass(Class c) { + return c.getName().replace(".", "_") + ".html"; + } + + public static String helpLinksToGATKDocs(Class c) { + String classPath = htmlFilenameForClass(c); + StringBuilder b = new StringBuilder(); + b.append("release version: ").append(URL_ROOT_FOR_RELEASE_GATKDOCS).append(classPath).append("\n"); + b.append("stable version: ").append(URL_ROOT_FOR_STABLE_GATKDOCS).append(classPath).append("\n"); + b.append("unstable version: ").append(URL_ROOT_FOR_UNSTABLE_GATKDOCS).append(classPath).append("\n"); + return b.toString(); + } +} \ No newline at end of file diff --git a/public/java/src/org/broadinstitute/sting/utils/help/HelpUtils.java b/public/java/src/org/broadinstitute/sting/utils/help/HelpUtils.java index da4e7bdaf..d72d2e83c 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/HelpUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/HelpUtils.java @@ -32,9 +32,6 @@ import org.broadinstitute.sting.utils.classloader.JVMUtils; import java.lang.reflect.Field; public class HelpUtils { - public final static String URL_ROOT_FOR_RELEASE_GATKDOCS = "http://www.broadinstitute.org/gsa/gatkdocs/release/"; - public final static String URL_ROOT_FOR_STABLE_GATKDOCS = "http://iwww.broadinstitute.org/gsa/gatkdocs/stable/"; - public final static String URL_ROOT_FOR_UNSTABLE_GATKDOCS = "http://iwww.broadinstitute.org/gsa/gatkdocs/unstable/"; protected static boolean implementsInterface(ProgramElementDoc classDoc, Class... interfaceClasses) { for (Class interfaceClass : interfaceClasses) @@ -79,16 +76,4 @@ public class HelpUtils { String.format("%s", doc.name()); } - public static String htmlFilenameForClass(Class c) { - return c.getName().replace(".", "_") + ".html"; - } - - public static String helpLinksToGATKDocs(Class c) { - String classPath = htmlFilenameForClass(c); - StringBuilder b = new StringBuilder(); - b.append("release version: ").append(URL_ROOT_FOR_RELEASE_GATKDOCS).append(classPath).append("\n"); - b.append("stable version: ").append(URL_ROOT_FOR_STABLE_GATKDOCS).append(classPath).append("\n"); - b.append("unstable version: ").append(URL_ROOT_FOR_UNSTABLE_GATKDOCS).append(classPath).append("\n"); - return b.toString(); - } } \ No newline at end of file From c797616c658297563cb95dfd267f5cf9b17b1947 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 18 Aug 2011 11:51:53 -0400 Subject: [PATCH 074/138] If you have one sample in your BAM, getToolkit().getSamples().size() == 2 Also deleted double initializationm, where a line of code was duplicated in creating the GATK engine. --- .../org/broadinstitute/sting/gatk/GenomeAnalysisEngine.java | 2 -- .../sting/gatk/walkers/coverage/CallableLociWalker.java | 4 +++- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/GenomeAnalysisEngine.java b/public/java/src/org/broadinstitute/sting/gatk/GenomeAnalysisEngine.java index b0c4e203b..da7eaf6e4 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/GenomeAnalysisEngine.java +++ b/public/java/src/org/broadinstitute/sting/gatk/GenomeAnalysisEngine.java @@ -689,8 +689,6 @@ public class GenomeAnalysisEngine { validateSuppliedReads(); readsDataSource = createReadsDataSource(argCollection,genomeLocParser,referenceDataSource.getReference()); - sampleDataSource = new SampleDataSource(getSAMFileHeader(), argCollection.sampleFiles); - for (ReadFilter filter : filters) filter.initialize(this); diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/CallableLociWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/CallableLociWalker.java index 3046ef925..98331ec1d 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/CallableLociWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/CallableLociWalker.java @@ -223,8 +223,10 @@ public class CallableLociWalker extends LocusWalker 1 ) + if ( getToolkit().getSamples().size() != 2 ) { + // unbelievably there are actually two samples even when there's just one in the header. God I hate this Samples system throw new UserException.BadArgumentValue("-I", "CallableLoci only works for a single sample, but multiple samples were found in the provided BAM files: " + getToolkit().getSamples()); + } try { PrintStream summaryOut = new PrintStream(summaryFile); From e03db30ca0dc8f96a380aae85f63b2edf811d57c Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 18 Aug 2011 12:31:04 -0400 Subject: [PATCH 076/138] New uses DocumentedGATKFeatureObject instead of annotation directly -- Step 1 on the way to creating a static list of additional classes that we want to document. --- .../utils/help/DocumentedGATKFeature.java | 1 - .../help/DocumentedGATKFeatureObject.java | 48 +++++++++++++++++++ .../sting/utils/help/GATKDocWorkUnit.java | 4 +- .../sting/utils/help/GATKDoclet.java | 37 +++++++------- 4 files changed, 66 insertions(+), 24 deletions(-) create mode 100644 public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureObject.java diff --git a/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeature.java b/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeature.java index 710503ca8..89163dfcb 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeature.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeature.java @@ -39,6 +39,5 @@ public @interface DocumentedGATKFeature { public boolean enable() default true; public String groupName(); public String summary() default ""; - public Class handler() default GenericDocumentationHandler.class; public Class[] extraDocs() default {}; } diff --git a/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureObject.java b/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureObject.java new file mode 100644 index 000000000..9d198ee1a --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureObject.java @@ -0,0 +1,48 @@ +/* + * 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.help; + +/** + * Documentation unit. Effectively a class version of the DocumentedGATKFeature + * + * @author depristo + */ +class DocumentedGATKFeatureObject { + final boolean enable; + final String groupName, summary; + final Class[] extraDocs; + + public DocumentedGATKFeatureObject(final boolean enable, final String groupName, final String summary, final Class[] extraDocs) { + this.enable = enable; + this.groupName = groupName; + this.summary = summary; + this.extraDocs = extraDocs; + } + + public boolean enable() { return enable; } + public String groupName() { return groupName; } + public String summary() { return summary; } + public Class[] extraDocs() { return extraDocs; } +} diff --git a/public/java/src/org/broadinstitute/sting/utils/help/GATKDocWorkUnit.java b/public/java/src/org/broadinstitute/sting/utils/help/GATKDocWorkUnit.java index 1f6db2757..41c855329 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/GATKDocWorkUnit.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/GATKDocWorkUnit.java @@ -51,7 +51,7 @@ class GATKDocWorkUnit implements Comparable { /** The javadoc documentation for clazz */ final ClassDoc classDoc; /** The annotation that lead to this Class being in GATKDoc */ - final DocumentedGATKFeature annotation; + final DocumentedGATKFeatureObject annotation; /** When was this walker built, and what's the absolute version number */ final String buildTimestamp, absoluteVersion; @@ -60,7 +60,7 @@ class GATKDocWorkUnit implements Comparable { Map forTemplate; public GATKDocWorkUnit(String name, String filename, String group, - DocumentedGATKFeature annotation, DocumentedGATKFeatureHandler handler, + DocumentedGATKFeatureObject annotation, DocumentedGATKFeatureHandler handler, ClassDoc classDoc, Class clazz, String buildTimestamp, String absoluteVersion) { this.annotation = annotation; diff --git a/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java b/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java index 8f3ec293a..070663605 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java @@ -99,7 +99,7 @@ public class GATKDoclet { //if ( clazz != null && clazz.getName().equals("org.broadinstitute.sting.gatk.walkers.annotator.AlleleBalance")) // logger.debug("foo"); - DocumentedGATKFeature feature = getFeatureForClassDoc(doc); + DocumentedGATKFeatureObject feature = getFeatureForClassDoc(doc); DocumentedGATKFeatureHandler handler = createHandler(doc, feature); if ( handler != null && handler.includeInDocs(doc) ) { logger.info("Generating documentation for class " + doc); @@ -146,31 +146,26 @@ public class GATKDoclet { } } - private DocumentedGATKFeatureHandler createHandler(ClassDoc doc, DocumentedGATKFeature feature) { - try { - if ( feature != null ) { - if ( feature.enable() ) { - DocumentedGATKFeatureHandler handler = feature.handler().newInstance(); - handler.setDoclet(this); - return handler; - } else { - logger.info("Skipping disabled Documentation for " + doc); - } + private DocumentedGATKFeatureHandler createHandler(ClassDoc doc, DocumentedGATKFeatureObject feature) { + if ( feature != null ) { + if ( feature.enable() ) { + DocumentedGATKFeatureHandler handler = new GenericDocumentationHandler(); + handler.setDoclet(this); + return handler; + } else { + logger.info("Skipping disabled Documentation for " + doc); } - } catch ( IllegalAccessException e) { - throw new RuntimeException(e); // the constructor is now private -- this is an error - } catch ( InstantiationException e) { - throw new RuntimeException(e); // the constructor is now private -- this is an error } return null; } - private DocumentedGATKFeature getFeatureForClassDoc(ClassDoc doc) { - // todo -- what do I need the ? extends Object to pass the compiler? + private DocumentedGATKFeatureObject getFeatureForClassDoc(ClassDoc doc) { Class docClass = getClassForClassDoc(doc); + // todo -- add looked here to static TO DOC collection as well if ( docClass != null && docClass.isAnnotationPresent(DocumentedGATKFeature.class) ) { - return docClass.getAnnotation(DocumentedGATKFeature.class); + DocumentedGATKFeature f = docClass.getAnnotation(DocumentedGATKFeature.class); + return new DocumentedGATKFeatureObject(f.enable(), f.groupName(), f.summary(), f.extraDocs()); } else { return null; // not annotated so it shouldn't be documented } @@ -217,7 +212,7 @@ public class GATKDoclet { Collections.sort(indexData); - Set docFeatures = new HashSet(); + Set docFeatures = new HashSet(); List> data = new ArrayList>(); for ( GATKDocWorkUnit workUnit : indexData ) { data.add(workUnit.indexDataMap()); @@ -225,7 +220,7 @@ public class GATKDoclet { } List> groups = new ArrayList>(); - for ( DocumentedGATKFeature feature : docFeatures ) { + for ( DocumentedGATKFeatureObject feature : docFeatures ) { groups.add(toMap(feature)); } @@ -237,7 +232,7 @@ public class GATKDoclet { return root; } - private static final Map toMap(DocumentedGATKFeature annotation) { + private static final Map toMap(DocumentedGATKFeatureObject annotation) { Map root = new HashMap(); root.put("name", annotation.groupName()); root.put("summary", annotation.summary()); From 5772766dd5605c92371e9b7e7011dc565b68f84f Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 18 Aug 2011 14:00:09 -0400 Subject: [PATCH 078/138] Improvements to GATKDocs -- Now supports a static list of root classes / interfaces that should receive docs. A complementary approach to documenting features to the DocumentedGATKFeature annotation -- Tribble codecs are now documented! -- No longer displayed sub and super classes --- .../help/DocumentedGATKFeatureObject.java | 15 +++++-- .../sting/utils/help/GATKDoclet.java | 40 +++++++++++++------ .../help/GenericDocumentationHandler.java | 28 ++++++------- 3 files changed, 53 insertions(+), 30 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureObject.java b/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureObject.java index 9d198ee1a..66354202f 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureObject.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureObject.java @@ -30,17 +30,24 @@ package org.broadinstitute.sting.utils.help; * @author depristo */ class DocumentedGATKFeatureObject { - final boolean enable; - final String groupName, summary; - final Class[] extraDocs; + private final Class classToDoc; + private final boolean enable; + private final String groupName, summary; + private final Class[] extraDocs; - public DocumentedGATKFeatureObject(final boolean enable, final String groupName, final String summary, final Class[] extraDocs) { + public DocumentedGATKFeatureObject(Class classToDoc, final boolean enable, final String groupName, final String summary, final Class[] extraDocs) { + this.classToDoc = classToDoc; this.enable = enable; this.groupName = groupName; this.summary = summary; this.extraDocs = extraDocs; } + public DocumentedGATKFeatureObject(Class classToDoc, final String groupName, final String summary) { + this(classToDoc, true, groupName, summary, new Class[]{}); + } + + public Class getClassToDoc() { return classToDoc; } public boolean enable() { return enable; } public String groupName() { return groupName; } public String summary() { return summary; } diff --git a/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java b/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java index 070663605..5755d2b37 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java @@ -33,6 +33,7 @@ import freemarker.template.TemplateException; import org.apache.commons.io.FileUtils; import org.apache.log4j.Level; import org.apache.log4j.Logger; +import org.broad.tribble.FeatureCodec; import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; import java.io.*; @@ -50,6 +51,14 @@ public class GATKDoclet { RootDoc rootDoc; + final static Collection STATIC_DOCS = new ArrayList(); + static { + STATIC_DOCS.add(new DocumentedGATKFeatureObject(FeatureCodec.class, + "Reference ordered data (ROD) codecs", + "Tribble codecs for reading reference ordered data such as VCF or BED files")); + } + + /** * Extracts the contents of certain types of javadoc and adds them to an XML file. * @param rootDoc The documentation root. @@ -162,12 +171,20 @@ public class GATKDoclet { private DocumentedGATKFeatureObject getFeatureForClassDoc(ClassDoc doc) { Class docClass = getClassForClassDoc(doc); - // todo -- add looked here to static TO DOC collection as well - if ( docClass != null && docClass.isAnnotationPresent(DocumentedGATKFeature.class) ) { - DocumentedGATKFeature f = docClass.getAnnotation(DocumentedGATKFeature.class); - return new DocumentedGATKFeatureObject(f.enable(), f.groupName(), f.summary(), f.extraDocs()); - } else { + + if ( docClass == null ) return null; // not annotated so it shouldn't be documented + + if ( docClass.isAnnotationPresent(DocumentedGATKFeature.class) ) { + DocumentedGATKFeature f = docClass.getAnnotation(DocumentedGATKFeature.class); + return new DocumentedGATKFeatureObject(docClass, f.enable(), f.groupName(), f.summary(), f.extraDocs()); + } else { + for ( DocumentedGATKFeatureObject staticDocs : STATIC_DOCS ) { + if ( staticDocs.getClassToDoc().isAssignableFrom(docClass) ) { + return new DocumentedGATKFeatureObject(docClass, staticDocs.enable(), staticDocs.groupName(), staticDocs.summary(), staticDocs.extraDocs()); + } + } + return null; } } @@ -212,16 +229,15 @@ public class GATKDoclet { Collections.sort(indexData); - Set docFeatures = new HashSet(); + List> groups = new ArrayList>(); + Set seenDocumentationFeatures = new HashSet(); List> data = new ArrayList>(); for ( GATKDocWorkUnit workUnit : indexData ) { data.add(workUnit.indexDataMap()); - docFeatures.add(workUnit.annotation); - } - - List> groups = new ArrayList>(); - for ( DocumentedGATKFeatureObject feature : docFeatures ) { - groups.add(toMap(feature)); + if ( ! seenDocumentationFeatures.contains(workUnit.annotation.groupName()) ) { + groups.add(toMap(workUnit.annotation)); + seenDocumentationFeatures.add(workUnit.annotation.groupName()); + } } root.put("data", data); 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 6ddf8a157..b4d82f0e8 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/GenericDocumentationHandler.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/GenericDocumentationHandler.java @@ -255,21 +255,21 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { put("name", otherUnit.name);}}); } - - List> hierarchyDocs = new ArrayList>(); - for (final GATKDocWorkUnit other : all ) { - final String relation = classRelationship(toProcess.clazz, other.clazz); - if ( relation != null ) - hierarchyDocs.add( - new HashMap(){{ - put("filename", other.filename); - put("relation", relation); - put("name", other.name);}}); - - } - - root.put("relatedDocs", hierarchyDocs); root.put("extradocs", extraDocsData); + + +// List> hierarchyDocs = new ArrayList>(); +// for (final GATKDocWorkUnit other : all ) { +// final String relation = classRelationship(toProcess.clazz, other.clazz); +// if ( relation != null ) +// hierarchyDocs.add( +// new HashMap(){{ +// put("filename", other.filename); +// put("relation", relation); +// put("name", other.name);}}); +// +// } +// root.put("relatedDocs", hierarchyDocs); } private static final String classRelationship(Class me, Class other) { From 7c4ce6d9696e02c87ac37824d6885d666b19c98e Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Thu, 18 Aug 2011 14:00:39 -0400 Subject: [PATCH 079/138] Added GATKDocs for the VQSR walkers. --- .../analyzecovariates/AnalyzeCovariates.java | 1 - .../TableRecalibrationWalker.java | 2 +- .../ApplyRecalibration.java | 47 +++++++++-- .../VariantRecalibrator.java | 82 +++++++++++++++---- ...VariantRecalibratorArgumentCollection.java | 8 +- 5 files changed, 112 insertions(+), 28 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/analyzecovariates/AnalyzeCovariates.java b/public/java/src/org/broadinstitute/sting/analyzecovariates/AnalyzeCovariates.java index 9b316f077..2ff8aa979 100755 --- a/public/java/src/org/broadinstitute/sting/analyzecovariates/AnalyzeCovariates.java +++ b/public/java/src/org/broadinstitute/sting/analyzecovariates/AnalyzeCovariates.java @@ -28,7 +28,6 @@ package org.broadinstitute.sting.analyzecovariates; import org.broadinstitute.sting.commandline.Argument; import org.broadinstitute.sting.commandline.CommandLineProgram; import org.broadinstitute.sting.commandline.Input; -import org.broadinstitute.sting.gatk.CommandLineGATK; import org.broadinstitute.sting.gatk.walkers.recalibration.Covariate; import org.broadinstitute.sting.gatk.walkers.recalibration.RecalDatum; import org.broadinstitute.sting.gatk.walkers.recalibration.RecalibrationArgumentCollection; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/TableRecalibrationWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/TableRecalibrationWalker.java index af7148803..85166d30d 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/TableRecalibrationWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/recalibration/TableRecalibrationWalker.java @@ -52,7 +52,7 @@ import java.util.ResourceBundle; import java.util.regex.Pattern; /** - * Second pass of the recalibration -- Uses the table generated by CountCovariates to update the base quality scores of the input bam file using a sequential table calculation making the base quality scores more accurately reflect the actual quality of the bases as measured by reference mismatch rate. + * Second pass of the base quality score recalibration -- Uses the table generated by CountCovariates to update the base quality scores of the input bam file using a sequential table calculation making the base quality scores more accurately reflect the actual quality of the bases as measured by reference mismatch rate. * *

      * This walker is designed to work as the second pass in a two-pass processing step, doing a by-read traversal. For each diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/ApplyRecalibration.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/ApplyRecalibration.java index abe27e483..16f1abf1b 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/ApplyRecalibration.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/ApplyRecalibration.java @@ -45,10 +45,43 @@ import java.io.FileNotFoundException; import java.util.*; /** - * Applies cuts to the input vcf file (by adding filter lines) to achieve the desired novel FDR levels which were specified during VariantRecalibration + * Applies cuts to the input vcf file (by adding filter lines) to achieve the desired novel truth sensitivity levels which were specified during VariantRecalibration + * + *

      + * Using the tranche file generated by the previous step the ApplyRecalibration walker looks at each variant's VQSLOD value + * and decides which tranche it falls in. Variants in tranches that fall below the specified truth sensitivity filter level + * have their filter field annotated with its tranche level. This will result in a call set that simultaneously is filtered + * to the desired level but also has the information necessary to pull out more variants for a higher sensitivity but a + * slightly lower quality level. + * + *

      + * See the GATK wiki for a tutorial and example recalibration accuracy plots. + * http://www.broadinstitute.org/gsa/wiki/index.php/Variant_quality_score_recalibration + * + *

      Input

      + *

      + * The input raw variants to be recalibrated. + *

      + * The recalibration table file in CSV format that was generated by the VariantRecalibrator walker. + *

      + * The tranches file that was generated by the VariantRecalibrator walker. + * + *

      Output

      + *

      + * A recalibrated VCF file in which each variant is annotated with its VQSLOD and filtered if the score is below the desired quality level. + * + *

      Examples

      + *
      + * java -Xmx3g -jar GenomeAnalysisTK.jar \
      + *   -T ApplyRecalibration \
      + *   -R reference/human_g1k_v37.fasta \
      + *   -input NA12878.HiSeq.WGS.bwa.cleaned.raw.hg19.subset.vcf \
      + *   --ts_filter_level 99.0 \
      + *   -tranchesFile path/to/output.tranches \
      + *   -recalFile path/to/output.recal \
      + *   -o path/to/output.recalibrated.filtered.vcf
      + * 
      * - * @author rpoplin - * @since Mar 14, 2011 */ public class ApplyRecalibration extends RodWalker { @@ -57,11 +90,11 @@ public class ApplyRecalibration extends RodWalker { // Inputs ///////////////////////////// /** - * The raw input variants to be recalibrated. + * These calls should be unfiltered and annotated with the error covariates that are intended to use for modeling. */ @Input(fullName="input", shortName = "input", doc="The raw input variants to be recalibrated", required=true) public List> input; - @Input(fullName="recal_file", shortName="recalFile", doc="The output recal file used by ApplyRecalibration", required=true) + @Input(fullName="recal_file", shortName="recalFile", doc="The input recal file used by ApplyRecalibration", required=true) private File RECAL_FILE; @Input(fullName="tranches_file", shortName="tranchesFile", doc="The input tranches file describing where to cut the data", required=true) private File TRANCHES_FILE; @@ -69,7 +102,7 @@ public class ApplyRecalibration extends RodWalker { ///////////////////////////// // Outputs ///////////////////////////// - @Output( doc="The output filtered, recalibrated VCF file", required=true) + @Output( doc="The output filtered and recalibrated VCF file in which each variant is annotated with its VQSLOD value", required=true) private VCFWriter vcfWriter = null; ///////////////////////////// @@ -77,7 +110,7 @@ public class ApplyRecalibration extends RodWalker { ///////////////////////////// @Argument(fullName="ts_filter_level", shortName="ts_filter_level", doc="The truth sensitivity level at which to start filtering", required=false) private double TS_FILTER_LEVEL = 99.0; - @Argument(fullName="ignore_filter", shortName="ignoreFilter", doc="If specified the optimizer will use variants even if the specified filter name is marked in the input VCF file", required=false) + @Argument(fullName="ignore_filter", shortName="ignoreFilter", doc="If specified the variant recalibrator will use variants even if the specified filter name is marked in the input VCF file", required=false) private String[] IGNORE_INPUT_FILTERS = null; @Argument(fullName = "mode", shortName = "mode", doc = "Recalibration mode to employ: 1.) SNP for recalibrating only SNPs (emitting indels untouched in the output VCF); 2.) INDEL for indels; and 3.) BOTH for recalibrating both SNPs and indels simultaneously.", required = false) public VariantRecalibratorArgumentCollection.Mode MODE = VariantRecalibratorArgumentCollection.Mode.SNP; 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 da9da936b..d81a57aad 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 @@ -45,10 +45,54 @@ import java.io.PrintStream; import java.util.*; /** - * Takes variant calls as .vcf files, learns a Gaussian mixture model over the variant annotations and evaluates the variant -- assigning an informative lod score + * Create a Gaussian mixture model by looking at the annotations values over a high quality subset of the input call set and then evaluate all input variants. + * + *

      + * This walker is the first pass in a two-stage processing step. This walker is designed to be used in conjunction with ApplyRecalibration walker. + * + *

      + * The purpose of the variant recalibrator is to assign a well-calibrated probability to each variant call in a call set. + * One can then create highly accurate call sets by filtering based on this single estimate for the accuracy of each call. + * The approach taken by variant quality score recalibration is to develop a continuous, covarying estimate of the relationship + * between SNP call annotations (QD, SB, HaplotypeScore, HRun, for example) and the the probability that a SNP is a true genetic + * variant versus a sequencing or data processing artifact. This model is determined adaptively based on "true sites" provided + * as input, typically HapMap 3 sites and those sites found to be polymorphic on the Omni 2.5M SNP chip array. This adaptive + * error model can then be applied to both known and novel variation discovered in the call set of interest to evaluate the + * probability that each call is real. The score that gets added to the INFO field of each variant is called the VQSLOD. It is + * the log odds ratio of being a true variant versus being false under the trained Gaussian mixture model. + * + *

      + * See the GATK wiki for a tutorial and example recalibration accuracy plots. + * http://www.broadinstitute.org/gsa/wiki/index.php/Variant_quality_score_recalibration + * + *

      Input

      + *

      + * The input raw variants to be recalibrated. + *

      + * Known, truth, and training sets to be used by the algorithm. How these various sets are used is described below. + * + *

      Output

      + *

      + * A recalibration table file in CSV format that is used by the ApplyRecalibration walker. + *

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

      Examples

      + *
      + * 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 \
      + *   -an QD -an HaplotypeScore -an MQRankSum -an ReadPosRankSum -an FS -an MQ \
      + *   -recalFile path/to/output.recal \
      + *   -tranchesFile path/to/output.tranches \
      + *   -rscriptFile path/to/output.plots.R
      + * 
      * - * User: rpoplin - * Date: 3/12/11 */ public class VariantRecalibrator extends RodWalker, ExpandingArrayList> implements TreeReducible> { @@ -62,36 +106,32 @@ public class VariantRecalibrator extends RodWalker> input; + /** - * A list of training variants used to train the Gaussian mixture model. - * * 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; + /** - * A list of true variants to be used when deciding the truth sensitivity cut of the final callset. - * * 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; + /** - * A list of known variants to be used for metric comparison purposes. - * * 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(); + /** - * A list of known bad variants used to supplement training the negative model. - * * 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). */ @@ -109,13 +149,25 @@ public class VariantRecalibrator extends RodWalker Date: Thu, 18 Aug 2011 14:04:47 -0400 Subject: [PATCH 080/138] Only concrete classes are now documented --- .../sting/gatk/CommandLineGATK.java | 2 +- .../help/GenericDocumentationHandler.java | 28 +++++-------------- 2 files changed, 8 insertions(+), 22 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java b/public/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java index 60aea9928..b8488dc9a 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java +++ b/public/java/src/org/broadinstitute/sting/gatk/CommandLineGATK.java @@ -50,7 +50,7 @@ import java.util.*; @DocumentedGATKFeature( groupName = "GATK Engine", summary = "Features and arguments for the GATK engine itself, available to all walkers.", - extraDocs = { ReadFilter.class, UserException.class }) + extraDocs = { UserException.class }) public class CommandLineGATK extends CommandLineExecutable { @Argument(fullName = "analysis_type", shortName = "T", doc = "Type of analysis to run") private String analysisName = null; 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 b4d82f0e8..5b358519e 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/GenericDocumentationHandler.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/GenericDocumentationHandler.java @@ -52,13 +52,13 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { @Override public boolean includeInDocs(ClassDoc doc) { - return true; -// try { -// Class type = HelpUtils.getClassForDoc(doc); -// return JVMUtils.isConcrete(type); -// } catch ( ClassNotFoundException e ) { -// return false; -// } +// return true; + try { + Class type = HelpUtils.getClassForDoc(doc); + return JVMUtils.isConcrete(type); + } catch ( ClassNotFoundException e ) { + return false; + } } @@ -256,20 +256,6 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { } root.put("extradocs", extraDocsData); - - -// List> hierarchyDocs = new ArrayList>(); -// for (final GATKDocWorkUnit other : all ) { -// final String relation = classRelationship(toProcess.clazz, other.clazz); -// if ( relation != null ) -// hierarchyDocs.add( -// new HashMap(){{ -// put("filename", other.filename); -// put("relation", relation); -// put("name", other.name);}}); -// -// } -// root.put("relatedDocs", hierarchyDocs); } private static final String classRelationship(Class me, Class other) { From ce009bd4a416457fbe457ff3fcd453fc277c9a97 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 18 Aug 2011 14:05:09 -0400 Subject: [PATCH 081/138] Works without related data --- settings/helpTemplates/generic.template.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings/helpTemplates/generic.template.html b/settings/helpTemplates/generic.template.html index c11200121..0c938c343 100644 --- a/settings/helpTemplates/generic.template.html +++ b/settings/helpTemplates/generic.template.html @@ -98,7 +98,7 @@ <#-- This class is related to other documented classes via sub/super relationships --> - <#if relatedDocs?size != 0> + <#if relatedDocs?? && relatedDocs?size != 0>

      Related capabilities

      <@relatedByType name="Superclasses" type="superclass"/> From a8935c99fc0bedbb7851b1223511dd58a1ad048d Mon Sep 17 00:00:00 2001 From: Chris Hartl Date: Thu, 18 Aug 2011 15:28:35 -0400 Subject: [PATCH 082/138] dding docs for DepthOfCoverage and ValidationAmplicons --- .../coverage/DepthOfCoverageWalker.java | 46 ++++++++++-- .../validation/ValidationAmplicons.java | 72 ++++++++++++++++--- 2 files changed, 104 insertions(+), 14 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 90036407f..7fe16c9df 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 @@ -51,14 +51,48 @@ import java.io.PrintStream; import java.util.*; /** - * A parallelizable walker designed to quickly aggregate relevant coverage statistics across samples in the input - * file. Assesses the mean and median granular coverages of each sample, and generates part of a cumulative - * distribution of % bases and % targets covered for certain depths. The granularity of DOC can be set by command - * line arguments. + * Toolbox for assessing sequence coverage by a wide array of metrics, partitioned by sample, read group, or library * + *

      + * DepthOfCoverage processes a set of bam files to determine coverage at different levels of partitioning and + * aggregation. Coverage can be analyzed per locus, per interval, per gene, or in total; can be partitioned by + * sample, by read group, by technology, by center, or by library; and can be summarized by mean, median, quartiles, + * and/or percentage of bases covered to or beyond a threshold. + * Additionally, reads and bases can be filtered by mapping or base quality score. + * + *

      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 + *

      + * + *

      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 + *

      + * + *

      Examples

      + *
      + * java -Xmx2g -jar GenomeAnalysisTK.jar \
      + *   -R ref.fasta \
      + *   -T VariantEval \
      + *   -o file_name_base \
      + *   -I input_bams.list
      + *   [-geneList refSeq.sorted.txt] \
      + *   [-pt readgroup] \
      + *   [-ct 4 -ct 6 -ct 10] \
      + *   [-L my_capture_genes.interval_list]
      + * 
      * - * @Author chartl - * @Date Feb 22, 2010 */ // todo -- cache the map from sample names to means in the print functions, rather than regenerating each time // todo -- support for granular histograms for total depth; maybe n*[start,stop], bins*sqrt(n) 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 61149e5d9..cd2891874 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 @@ -30,21 +30,77 @@ import java.util.LinkedList; import java.util.List; /** - * Created by IntelliJ IDEA. - * User: chartl - * Date: 6/13/11 - * Time: 2:12 PM - * To change this template use File | Settings | File Templates. + * Creates FASTA sequences for use in Seqenom or PCR utilities for site amplification and subsequent validation + * + *

      + * ValidationAmplicons consumes a VCF and an Interval list and produces FASTA sequences from which PCR primers or probe + * sequences can be designed. In addition, ValidationAmplicons uses BWA to check for specificity of tracts of bases within + * the output amplicon, lower-casing non-specific tracts, allows for users to provide sites to mask out, and specifies + * reasons why the site may fail validation (nearby variation, for example). + *

      + * + *

      Input

      + *

      + * Requires a VCF containing alleles to design amplicons towards, a VCF of variants to mask out of the amplicons, and an + * interval list defining the size of the amplicons around the sites to be validated + *

      + * + *

      Output

      + *

      + * Output is a FASTA-formatted file with some modifications at probe sites. For instance: + *

      + * >20:207414 INSERTION=1,VARIANT_TOO_NEAR_PROBE=1, 20_207414
      + * CCAACGTTAAGAAAGAGACATGCGACTGGGTgcggtggctcatgcctggaaccccagcactttgggaggccaaggtgggc[A/G*]gNNcacttgaggtcaggagtttgagaccagcctggccaacatggtgaaaccccgtctctactgaaaatacaaaagttagC
      + * >20:792122 Valid 20_792122
      + * TTTTTTTTTagatggagtctcgctcttatcgcccaggcNggagtgggtggtgtgatcttggctNactgcaacttctgcct[-/CCC*]cccaggttcaagtgattNtcctgcctcagccacctgagtagctgggattacaggcatccgccaccatgcctggctaatTT
      + * >20:994145 Valid 20_994145
      + * TCCATGGCCTCCCCCTGGCCCACGAAGTCCTCAGCCACCTCCTTCCTGGAGGGCTCAGCCAAAATCAGACTGAGGAAGAAG[AAG/-*]TGGTGGGCACCCACCTTCTGGCCTTCCTCAGCCCCTTATTCCTAGGACCAGTCCCCATCTAGGGGTCCTCACTGCCTCCC
      + * >20:1074230 SITE_IS_FILTERED=1, 20_1074230
      + * ACCTGATTACCATCAATCAGAACTCATTTCTGTTCCTATCTTCCACCCACAATTGTAATGCCTTTTCCATTTTAACCAAG[T/C*]ACTTATTATAtactatggccataacttttgcagtttgaggtatgacagcaaaaTTAGCATACATTTCATTTTCCTTCTTC
      + * >20:1084330 DELETION=1, 20_1084330
      + * 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 + * MULTIPLE_PROBES=1, // multiple variants to be validated found inside the same amplicon + * DELETION=6,INSERTION=5, // 6 deletions and 5 insertions found inside the amplicon region (from the "mask" VCF), will be potentially difficult to validate + * DELETION=1, // deletion found inside the amplicon region, could shift mass-spec peak + * START_TOO_CLOSE, // variant is too close to the start of the amplicon region to give sequenom a good chance to find a suitable primer + * 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
      + *      -R /humgen/1kg/reference/human_g1k_v37.fasta
      + *      -BTI ProbeIntervals
      + *      -ProbeIntervals:table interval_table.table
      + *      -ValidateAlleles:vcf sites_to_validate.vcf
      + *      -MaskAlleles:vcf mask_sites.vcf
      + *      --virtualPrimerSize 30
      + *      -o probes.fasta
      + * 
      + *
      + * @author chartl
      + * @since July 2011
        */
       @Requires(value={DataSource.REFERENCE})
       public class ValidationAmplicons extends RodWalker {
      -    @Input(fullName = "ProbeIntervals", doc="Chris document me", required=true)
      +    @Input(fullName = "ProbeIntervals", doc="A collection of intervals in table format with optional names that represent the "+
      +                                            "intervals surrounding the probe sites amplicons should be designed for", required=true)
           RodBinding probeIntervals;
       
      -    @Input(fullName = "ValidateAlleles", doc="Chris document me", required=true)
      +    @Input(fullName = "ValidateAlleles", doc="A VCF containing the sites and alleles you want to validate. Restricted to *BI-Allelic* sites", required=true)
           RodBinding validateAlleles;
       
      -    @Input(fullName = "MaskAlleles", doc="Chris document me", required=true)
      +    @Input(fullName = "MaskAlleles", doc="A VCF containing the sites you want to MASK from the designed amplicon (e.g. by Ns or lower-cased bases)", required=true)
           RodBinding maskAlleles;
       
       
      
      From 3dfb60a46e2d150cfde3169bfd516df604dbb2f9 Mon Sep 17 00:00:00 2001
      From: Guillermo del Angel 
      Date: Thu, 18 Aug 2011 16:17:38 -0400
      Subject: [PATCH 083/138] Fixing up and refactoring usage of indel categories.
       On a variant context, isInsertion() and isDeletion() are now removed because
       behavior before was wrong in case of multiallelic sites. Now, methods
       isSimpleInsertion() and isSimpleDeletion() will return true only if sites are
       biallelic. For multiallelic sites, isComplex() will return true in all cases.
       VariantEval module CountVariants is corrected and an additional column is
       added so that we log mixed events and complex indels separately (before they
       were being conflated). VariantEval module IndelStatistics is considerably
       simplified as the sample stratification was wrong and redundant, now it
       should work with the VE-generic Sample stratification. Several columns are
       renamed or removed since they're not really useful
      
      ---
       .../gatk/walkers/annotator/AlleleBalance.java |   2 +-
       .../walkers/annotator/HomopolymerRun.java     |   2 +-
       .../gatk/walkers/annotator/IndelType.java     |   4 +-
       .../fasta/FastaAlternateReferenceWalker.java  |   4 +-
       .../gatk/walkers/indels/IndelRealigner.java   |   6 +-
       .../indels/RealignerTargetCreator.java        |   4 +-
       .../validation/ValidationAmplicons.java       |   9 +-
       .../varianteval/evaluators/CountVariants.java |  18 +-
       .../evaluators/IndelLengthHistogram.java      |   4 +-
       .../evaluators/IndelMetricsByAC.java          | 221 ------------------
       .../evaluators/IndelStatistics.java           |  87 ++-----
       .../variantutils/LeftAlignVariants.java       |  12 +-
       .../variantutils/ValidateVariants.java        |   4 +-
       .../walkers/variantutils/VariantsToVCF.java   |   4 +-
       .../sting/utils/IndelUtils.java               |   4 +-
       .../utils/variantcontext/VariantContext.java  |  14 +-
       .../VariantContextUnitTest.java               |  18 +-
       17 files changed, 75 insertions(+), 342 deletions(-)
       delete mode 100755 public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/IndelMetricsByAC.java
      
      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 6a2ffe189..cf68a9121 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
      @@ -90,7 +90,7 @@ public class AlleleBalance extends InfoFieldAnnotation {
                       }
                       // todo -- actually care about indel length from the pileup (agnostic at the moment)
                       int refCount = indelPileup.size();
      -                int altCount = vc.isInsertion() ? indelPileup.getNumberOfInsertions() : indelPileup.getNumberOfDeletions();
      +                int altCount = vc.isSimpleInsertion() ? indelPileup.getNumberOfInsertions() : indelPileup.getNumberOfDeletions();
       
                       if ( refCount + altCount == 0 ) {
                           continue;
      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 4102d811c..463f7a645 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
      @@ -79,7 +79,7 @@ public class HomopolymerRun extends InfoFieldAnnotation implements StandardAnnot
               GenomeLoc locus = ref.getLocus();
               GenomeLoc window = ref.getWindow();
               int refBasePos = (int) (locus.getStart() - window.getStart())+1;
      -        if ( vc.isDeletion() ) {
      +        if ( vc.isSimpleDeletion() ) {
                   // check that deleted bases are the same
                   byte dBase = bases[refBasePos];
                   for ( int i = 0; i < vc.getReference().length(); i ++ ) {
      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 ff7f9a8f6..bfede40d2 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
      @@ -36,9 +36,9 @@ public class IndelType extends InfoFieldAnnotation implements ExperimentalAnnota
                   if (!vc.isBiallelic())
                       type = "MULTIALLELIC_INDEL";
                   else {
      -                if (vc.isInsertion())
      +                if (vc.isSimpleInsertion())
                           type = "INS.";
      -                else if (vc.isDeletion())
      +                else if (vc.isSimpleDeletion())
                           type = "DEL.";
                       else
                           type = "OTHER.";
      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 8f333a2b3..fd912334f 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
      @@ -101,11 +101,11 @@ public class FastaAlternateReferenceWalker extends FastaReferenceWalker {
                   if ( vc.isFiltered() )
                       continue;
       
      -            if ( vc.isDeletion()) {
      +            if ( vc.isSimpleDeletion()) {
                       deletionBasesRemaining = vc.getReference().length();
                       // delete the next n bases, not this one
                       return new Pair(context.getLocation(), refBase);
      -            } else if ( vc.isInsertion()) {
      +            } else if ( vc.isSimpleInsertion()) {
                       return new Pair(context.getLocation(), refBase.concat(vc.getAlternateAllele(0).toString()));
                   } else if (vc.isSNP()) {
                       return new Pair(context.getLocation(), vc.getAlternateAllele(0).toString());
      diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java
      index d766ae8bd..129be7f55 100755
      --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java
      +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java
      @@ -877,7 +877,7 @@ public class IndelRealigner extends ReadWalker {
               for ( VariantContext knownIndel : knownIndelsToTry ) {
                   if ( knownIndel == null || !knownIndel.isIndel() || knownIndel.isComplexIndel() )
                       continue;
      -            byte[] indelStr = knownIndel.isInsertion() ? knownIndel.getAlternateAllele(0).getBases() : Utils.dupBytes((byte)'-', knownIndel.getReference().length());
      +            byte[] indelStr = knownIndel.isSimpleInsertion() ? knownIndel.getAlternateAllele(0).getBases() : Utils.dupBytes((byte)'-', knownIndel.getReference().length());
                   int start = knownIndel.getStart() - leftmostIndex + 1;
                   Consensus c = createAlternateConsensus(start, reference, indelStr, knownIndel);
                   if ( c != null )
      @@ -1079,11 +1079,11 @@ public class IndelRealigner extends ReadWalker {
               if ( indexOnRef > 0 )
                   cigar.add(new CigarElement(indexOnRef, CigarOperator.M));
       
      -        if ( indel.isDeletion() ) {
      +        if ( indel.isSimpleDeletion() ) {
                   refIdx += indelStr.length;
                   cigar.add(new CigarElement(indelStr.length, CigarOperator.D));
               }
      -        else if ( indel.isInsertion() ) {
      +        else if ( indel.isSimpleInsertion() ) {
                   for ( byte b : indelStr )
                       sb.append((char)b);
                   cigar.add(new CigarElement(indelStr.length, CigarOperator.I));
      diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/RealignerTargetCreator.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/RealignerTargetCreator.java
      index 08ed1af52..48911b952 100755
      --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/RealignerTargetCreator.java
      +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/RealignerTargetCreator.java
      @@ -178,7 +178,7 @@ public class RealignerTargetCreator extends RodWalker {
               } else /* (mask != null && validate == null ) */ {
                   if ( ! mask.isSNP() && ! mask.isFiltered() && ( ! filterMonomorphic || ! mask.isMonomorphic() )) {
                       logger.warn("Mask Variant Context on the following warning line is not a SNP. Currently we can only mask out SNPs. This probe will not be designed.");
      -                logger.warn(String.format("%s:%d-%d\t%s\t%s",mask.getChr(),mask.getStart(),mask.getEnd(),mask.isInsertion() ? "INS" : "DEL", Utils.join(",",mask.getAlleles())));
      +                logger.warn(String.format("%s:%d-%d\t%s\t%s",mask.getChr(),mask.getStart(),mask.getEnd(),mask.isSimpleInsertion() ? "INS" : "DEL", Utils.join(",",mask.getAlleles())));
                       sequenceInvalid = true;
      -                invReason.add(mask.isInsertion() ? "INSERTION" : "DELETION");
      +                invReason.add(mask.isSimpleInsertion() ? "INSERTION" : "DELETION");
                       // note: indelCounter could be > 0 (could have small deletion within larger one). This always selects
                       // the larger event.
      -                int indelCounterNew = mask.isInsertion() ? 2 : mask.getEnd()-mask.getStart();
      +                int indelCounterNew = mask.isSimpleInsertion() ? 2 : mask.getEnd()-mask.getStart();
                       if ( indelCounterNew > indelCounter ) {
                           indelCounter = indelCounterNew;
                       }
                       //sequence.append((char) ref.getBase());
      -                //sequence.append(mask.isInsertion() ? 'I' : 'D');
      +                //sequence.append(mask.isSimpleInsertion() ? 'I' : 'D');
                       sequence.append("N");
                       indelCounter--;
                       rawSequence.append(Character.toUpperCase((char) ref.getBase()));
      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 87b8bac1d..b356a68dc 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
      @@ -39,8 +39,10 @@ public class CountVariants extends VariantEvaluator implements StandardEval {
           public long nInsertions = 0;
           @DataPoint(description = "Number of deletions")
           public long nDeletions = 0;
      -    @DataPoint(description = "Number of complex loci")
      +    @DataPoint(description = "Number of complex indels")
           public long nComplex = 0;
      +    @DataPoint(description = "Number of mixed loci (loci that can't be classified as a SNP, Indel or MNP)")
      +    public long nMixed = 0;
       
       
           @DataPoint(description = "Number of no calls loci")
      @@ -113,11 +115,15 @@ public class CountVariants extends VariantEvaluator implements StandardEval {
                           if (vc1.getAttributeAsBoolean("ISSINGLETON")) nSingletons++;
                           break;
                       case INDEL:
      -                    if (vc1.isInsertion()) nInsertions++;
      -                    else nDeletions++;
      +                    if (vc1.isSimpleInsertion())
      +                        nInsertions++;
      +                    else if (vc1.isSimpleDeletion())
      +                        nDeletions++;
      +                    else
      +                        nComplex++;
                           break;
                       case MIXED:
      -                    nComplex++;
      +                    nMixed++;
                           break;
                       default:
                           throw new ReviewedStingException("Unexpected VariantContext type " + vc1.getType());
      @@ -180,8 +186,8 @@ public class CountVariants extends VariantEvaluator implements StandardEval {
               heterozygosity = perLocusRate(nHets);
               heterozygosityPerBp = perLocusRInverseRate(nHets);
               hetHomRatio = ratio(nHets, nHomVar);
      -        indelRate = perLocusRate(nDeletions + nInsertions);
      -        indelRatePerBp = perLocusRInverseRate(nDeletions + nInsertions);
      +        indelRate = perLocusRate(nDeletions + nInsertions + nComplex);
      +        indelRatePerBp = perLocusRInverseRate(nDeletions + nInsertions + nComplex);
               deletionInsertionRatio = ratio(nDeletions, nInsertions);
           }
       }
      \ No newline at end of file
      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 77def0f30..35fffd815 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
      @@ -96,9 +96,9 @@ public class IndelLengthHistogram extends VariantEvaluator {
               }
       
               if ( vc1.isIndel() ) {
      -            if ( vc1.isInsertion() ) {
      +            if ( vc1.isSimpleInsertion() ) {
                       indelHistogram.update(vc1.getAlternateAllele(0).length());
      -            } else if ( vc1.isDeletion() ) {
      +            } 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/IndelMetricsByAC.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/IndelMetricsByAC.java
      deleted file mode 100755
      index 6e1b76acd..000000000
      --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/varianteval/evaluators/IndelMetricsByAC.java
      +++ /dev/null
      @@ -1,221 +0,0 @@
      -package org.broadinstitute.sting.gatk.walkers.varianteval.evaluators;
      -
      -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.varianteval.VariantEvalWalker;
      -import org.broadinstitute.sting.gatk.walkers.varianteval.util.Analysis;
      -import org.broadinstitute.sting.gatk.walkers.varianteval.util.DataPoint;
      -import org.broadinstitute.sting.gatk.walkers.varianteval.util.TableType;
      -import org.broadinstitute.sting.utils.exceptions.ReviewedStingException;
      -import org.broadinstitute.sting.utils.variantcontext.VariantContext;
      -
      -import java.util.ArrayList;
      -
      -/*
      - * 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.
      - */
      -
      -/**
      - * @author delangel
      - * @since Apr 11, 2010
      - */
      -
      -@Analysis(name = "Indel Metrics by allele count", description = "Shows various stats binned by allele count")
      -public class IndelMetricsByAC extends VariantEvaluator {
      -    // a mapping from quality score histogram bin to Ti/Tv ratio
      -    @DataPoint(description = "Indel Metrics by allele count")
      -    IndelMetricsByAc metrics = null;
      -
      -    int numSamples = 0;
      -
      -    public void initialize(VariantEvalWalker walker) {
      -        numSamples = walker.getNumSamples();
      -    }
      -
      -    //@DataPoint(name="Quality by Allele Count", description = "average variant quality for each allele count")
      -    //AlleleCountStats alleleCountStats = null;
      -    private static final int INDEL_SIZE_LIMIT = 100;
      -    private static final int NUM_SCALAR_COLUMNS = 6;
      -    static int len2Index(int ind) {
      -        return ind+INDEL_SIZE_LIMIT;
      -    }
      -
      -    static int index2len(int ind) {
      -        return ind-INDEL_SIZE_LIMIT-NUM_SCALAR_COLUMNS;
      -    }
      -
      -    protected final static String[] METRIC_COLUMNS;
      -    static {
      -        METRIC_COLUMNS= new String[NUM_SCALAR_COLUMNS+2*INDEL_SIZE_LIMIT+1];
      -        METRIC_COLUMNS[0] = "AC";
      -        METRIC_COLUMNS[1] = "nIns";
      -        METRIC_COLUMNS[2] = "nDels";
      -        METRIC_COLUMNS[3] = "n";
      -        METRIC_COLUMNS[4] = "nComplex";
      -        METRIC_COLUMNS[5] = "nLong";
      -
      -        for (int k=NUM_SCALAR_COLUMNS; k < NUM_SCALAR_COLUMNS+ 2*INDEL_SIZE_LIMIT+1; k++)
      -            METRIC_COLUMNS[k] = "indel_size_len"+Integer.valueOf(index2len(k));
      -    }
      -
      -    class IndelMetricsAtAC {
      -        public int ac = -1, nIns =0, nDel = 0, nComplex = 0, nLong;
      -        public int sizeCount[] = new int[2*INDEL_SIZE_LIMIT+1];
      -
      -        public IndelMetricsAtAC(int ac) { this.ac = ac; }
      -
      -        public void update(VariantContext eval) {
      -            int eventLength = 0;
      -            if ( eval.isInsertion() ) {
      -                eventLength = eval.getAlternateAllele(0).length();
      -                nIns++;
      -            } else if ( eval.isDeletion() ) {
      -                eventLength = -eval.getReference().length();
      -                nDel++;
      -            }
      -            else {
      -                nComplex++;
      -            }
      -            if (Math.abs(eventLength) < INDEL_SIZE_LIMIT)
      -                sizeCount[len2Index(eventLength)]++;
      -            else
      -                nLong++;
      -
      -
      -
      -         }
      -
      -        // corresponding to METRIC_COLUMNS
      -        public String getColumn(int i) {
      -            if (i >= NUM_SCALAR_COLUMNS && i <=NUM_SCALAR_COLUMNS+ 2*INDEL_SIZE_LIMIT)
      -                return String.valueOf(sizeCount[i-NUM_SCALAR_COLUMNS]);
      -
      -            switch (i) {
      -                case 0: return String.valueOf(ac);
      -                case 1: return String.valueOf(nIns);
      -                case 2: return String.valueOf(nDel);
      -                case 3: return String.valueOf(nIns + nDel);
      -                case 4: return String.valueOf(nComplex);
      -                case 5: return String.valueOf(nLong);
      -
      -                default:
      -                    throw new ReviewedStingException("Unexpected column " + i);
      -            }
      -        }
      -    }
      -
      -    class IndelMetricsByAc implements TableType {
      -        ArrayList metrics = new ArrayList();
      -        Object[] rows = null;
      -
      -        public IndelMetricsByAc( int nchromosomes ) {
      -            rows = new Object[nchromosomes+1];
      -            metrics = new ArrayList(nchromosomes+1);
      -            for ( int i = 0; i < nchromosomes + 1; i++ ) {
      -                metrics.add(new IndelMetricsAtAC(i));
      -                rows[i] = "ac" + i;
      -            }
      -        }
      -
      -        public Object[] getRowKeys() {
      -            return rows;
      -        }
      -
      -        public Object[] getColumnKeys() {
      -            return METRIC_COLUMNS;
      -        }
      -
      -        public String getName() {
      -            return "IndelMetricsByAc";
      -        }
      -
      -        //
      -        public String getCell(int ac, int y) {
      -            return metrics.get(ac).getColumn(y);
      -        }
      -
      -        public String toString() {
      -            return "";
      -        }
      -
      -        public void incrValue( VariantContext eval ) {
      -            int ac = -1;
      -
      -            if ( eval.hasGenotypes() )
      -                ac = eval.getChromosomeCount(eval.getAlternateAllele(0));
      -            else if ( eval.hasAttribute("AC") ) {
      -                ac = Integer.valueOf(eval.getAttributeAsString("AC"));
      -            }
      -
      -            if ( ac != -1 )
      -                metrics.get(ac).update(eval);
      -        }
      -    }
      -
      -    //public IndelMetricsByAC(VariantEvalWalker parent) {
      -        //super(parent);
      -        // don't do anything
      -    //}
      -
      -    public String getName() {
      -        return "IndelMetricsByAC";
      -    }
      -
      -    public int getComparisonOrder() {
      -        return 1;   // we only need to see each eval track
      -    }
      -
      -    public boolean enabled() {
      -        return true;
      -    }
      -
      -    public String toString() {
      -        return getName();
      -    }
      -
      -    public String update1(VariantContext eval, RefMetaDataTracker tracker, ReferenceContext ref, AlignmentContext context) {
      -        final String interesting = null;
      -
      -        if (eval != null ) {
      -            if ( metrics == null ) {
      -                int nSamples = numSamples;
      -                //int nSamples = 2;
      -                if ( nSamples != -1 )
      -                    metrics = new IndelMetricsByAc(2 * nSamples);
      -            }
      -
      -            if ( eval.isIndel() && eval.isBiallelic() &&
      -                    metrics != null ) {
      -                metrics.incrValue(eval);
      -            }
      -        }
      -
      -        return interesting; // This module doesn't capture any interesting sites, so return null
      -    }
      -
      -    //public void finalizeEvaluation() {
      -    //
      -    //}
      -}
      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 d99196ecf..78683dfcb 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
      @@ -44,7 +44,7 @@ public class IndelStatistics extends VariantEvaluator {
           @DataPoint(description = "Indel Statistics")
           IndelStats indelStats = null;
       
      -    @DataPoint(description = "Indel Classification")
      +   // @DataPoint(description = "Indel Classification")
           IndelClasses indelClasses = null;
       
           int numSamples = 0;
      @@ -79,8 +79,7 @@ public class IndelStatistics extends VariantEvaluator {
           }
       
           static class IndelStats implements TableType {
      -        protected final static String ALL_SAMPLES_KEY = "allSamples";
      -        protected final static String[] COLUMN_KEYS;
      +         protected final static String[] COLUMN_KEYS;
       
                static {
                   COLUMN_KEYS= new String[NUM_SCALAR_COLUMNS+2*INDEL_SIZE_LIMIT+1];
      @@ -104,13 +103,10 @@ public class IndelStatistics extends VariantEvaluator {
               }
       
               // map of sample to statistics
      -        protected final HashMap indelSummary = new HashMap();
      +        protected final  int[] indelSummary;
       
               public IndelStats(final VariantContext vc) {
      -            indelSummary.put(ALL_SAMPLES_KEY, new int[COLUMN_KEYS.length]);
      -            for( final String sample : vc.getGenotypes().keySet() ) {
      -                indelSummary.put(sample, new int[COLUMN_KEYS.length]);
      -            }
      +            indelSummary = new int[COLUMN_KEYS.length];
               }
       
               /**
      @@ -118,18 +114,18 @@ public class IndelStatistics extends VariantEvaluator {
                * @return one row per sample
                */
               public Object[] getRowKeys() {
      -            return indelSummary.keySet().toArray(new String[indelSummary.size()]);
      +            return new String[]{"all"};
               }
               public Object getCell(int x, int y) {
                   final Object[] rowKeys = getRowKeys();
                   if (y == IND_AT_CG_RATIO) {
       
      -                int at = indelSummary.get(rowKeys[x])[IND_AT_EXP];
      -                int cg = indelSummary.get(rowKeys[x])[IND_CG_EXP];
      +                int at = indelSummary[IND_AT_EXP];
      +                int cg = indelSummary[IND_CG_EXP];
                       return String.format("%4.2f",((double)at) / (Math.max(cg, 1)));
                   }
                   else
      -                return String.format("%d",indelSummary.get(rowKeys[x])[y]);
      +                return String.format("%d",indelSummary[y]);
       
               }
       
      @@ -160,78 +156,31 @@ public class IndelStatistics extends VariantEvaluator {
                   int eventLength = 0;
                   boolean isInsertion = false, isDeletion = false;
       
      -            if ( vc.isInsertion() ) {
      +            if ( vc.isSimpleInsertion() ) {
                       eventLength = vc.getAlternateAllele(0).length();
      -                indelSummary.get(ALL_SAMPLES_KEY)[IND_INS]++;
      +                indelSummary[IND_INS]++;
                       isInsertion = true;
      -            } else if ( vc.isDeletion() ) {
      -                indelSummary.get(ALL_SAMPLES_KEY)[IND_DEL]++;
      +            } else if ( vc.isSimpleDeletion() ) {
      +                indelSummary[IND_DEL]++;
                       eventLength = -vc.getReference().length();
                       isDeletion = true;
                   }
                   else {
      -                indelSummary.get(ALL_SAMPLES_KEY)[IND_COMPLEX]++;
      +                indelSummary[IND_COMPLEX]++;
                   }
                   if (IndelUtils.isATExpansion(vc,ref))
      -                indelSummary.get(ALL_SAMPLES_KEY)[IND_AT_EXP]++;
      +                indelSummary[IND_AT_EXP]++;
                   if (IndelUtils.isCGExpansion(vc,ref))
      -                 indelSummary.get(ALL_SAMPLES_KEY)[IND_CG_EXP]++;
      +                 indelSummary[IND_CG_EXP]++;
       
                   // make sure event doesn't overstep array boundaries
                   if (Math.abs(eventLength) < INDEL_SIZE_LIMIT) {
      -                indelSummary.get(ALL_SAMPLES_KEY)[len2Index(eventLength)]++;
      +                indelSummary[len2Index(eventLength)]++;
                       if (eventLength % 3 != 0)
      -                    indelSummary.get(ALL_SAMPLES_KEY)[IND_FRAMESHIFT]++;
      +                    indelSummary[IND_FRAMESHIFT]++;
                   }
                   else
      -                indelSummary.get(ALL_SAMPLES_KEY)[IND_LONG]++;
      -
      -
      -            for( final String sample : vc.getGenotypes().keySet() ) {
      -                if ( indelSummary.containsKey(sample) ) {
      -                    Genotype g = vc.getGenotype(sample);
      -                    boolean isVariant = (g.isCalled() && !g.isHomRef());
      -                    if (isVariant) {
      -                        // update ins/del count
      -                        if (isInsertion) {
      -                            indelSummary.get(sample)[IND_INS]++;
      -                        }
      -                        else if (isDeletion)
      -                            indelSummary.get(sample)[IND_DEL]++;
      -                        else
      -                            indelSummary.get(sample)[IND_COMPLEX]++;
      -
      -                        // update histogram
      -                        if (Math.abs(eventLength) < INDEL_SIZE_LIMIT) {
      -                            indelSummary.get(sample)[len2Index(eventLength)]++;
      -                            if (eventLength % 3 != 0)
      -                                indelSummary.get(sample)[IND_FRAMESHIFT]++;    
      -                        }
      -                        else
      -                            indelSummary.get(sample)[IND_LONG]++;
      -
      -                        if (g.isHet())
      -                            if (isInsertion)
      -                                indelSummary.get(sample)[IND_HET_INS]++;
      -                            else if (isDeletion)
      -                                indelSummary.get(sample)[IND_HET_DEL]++;
      -                        else
      -                            if (isInsertion)
      -                                indelSummary.get(sample)[IND_HOM_INS]++;
      -                            else if (isDeletion)
      -                                indelSummary.get(sample)[IND_HOM_DEL]++;
      -
      -                        if (IndelUtils.isATExpansion(vc,ref))
      -                            indelSummary.get(sample)[IND_AT_EXP]++;
      -                        if (IndelUtils.isCGExpansion(vc,ref))
      -                             indelSummary.get(sample)[IND_CG_EXP]++;
      -
      -
      -                    }
      -                    else
      -                        indelSummary.get(sample)[IND_HOM_REF]++;
      -                }
      -            }
      +                indelSummary[IND_LONG]++;
       
       
               }
      diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/LeftAlignVariants.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/LeftAlignVariants.java
      index 9fae71e4e..c9f330db5 100755
      --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/LeftAlignVariants.java
      +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/LeftAlignVariants.java
      @@ -133,7 +133,7 @@ public class LeftAlignVariants extends RodWalker {
       
               // get the indel length
               int indelLength;
      -        if ( vc.isDeletion() )
      +        if ( vc.isSimpleDeletion() )
                   indelLength = vc.getReference().length();
               else
                   indelLength = vc.getAlternateAllele(0).length();
      @@ -150,7 +150,7 @@ public class LeftAlignVariants extends RodWalker {
               // create a CIGAR string to represent the event
               ArrayList elements = new ArrayList();
               elements.add(new CigarElement(originalIndex, CigarOperator.M));
      -        elements.add(new CigarElement(indelLength, vc.isDeletion() ? CigarOperator.D : CigarOperator.I));
      +        elements.add(new CigarElement(indelLength, vc.isSimpleDeletion() ? CigarOperator.D : CigarOperator.I));
               elements.add(new CigarElement(refSeq.length - originalIndex, CigarOperator.M));
               Cigar originalCigar = new Cigar(elements);
       
      @@ -165,8 +165,8 @@ public class LeftAlignVariants extends RodWalker {
       
                   int indelIndex = originalIndex-difference;
                   byte[] newBases = new byte[indelLength];
      -            System.arraycopy((vc.isDeletion() ? refSeq : originalIndel), indelIndex, newBases, 0, indelLength);
      -            Allele newAllele = Allele.create(newBases, vc.isDeletion());
      +            System.arraycopy((vc.isSimpleDeletion() ? refSeq : originalIndel), indelIndex, newBases, 0, indelLength);
      +            Allele newAllele = Allele.create(newBases, vc.isSimpleDeletion());
                   newVC = updateAllele(newVC, newAllele, refSeq[indelIndex-1]);
       
                   writer.add(newVC);
      @@ -178,14 +178,14 @@ public class LeftAlignVariants extends RodWalker {
           }
       
           private static byte[] makeHaplotype(VariantContext vc, byte[] ref, int indexOfRef, int indelLength) {
      -        byte[] hap = new byte[ref.length + (indelLength * (vc.isDeletion() ? -1 : 1))];
      +        byte[] hap = new byte[ref.length + (indelLength * (vc.isSimpleDeletion() ? -1 : 1))];
       
               // add the bases before the indel
               System.arraycopy(ref, 0, hap, 0, indexOfRef);
               int currentPos = indexOfRef;
       
               // take care of the indel
      -        if ( vc.isDeletion() ) {
      +        if ( vc.isSimpleDeletion() ) {
                   indexOfRef += indelLength;
               } else {
                   System.arraycopy(vc.getAlternateAllele(0).getBases(), 0, hap, currentPos, indelLength);
      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 01a6e2f70..c0f695966 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
      @@ -137,11 +137,11 @@ public class ValidateVariants extends RodWalker {
               Allele reportedRefAllele = vc.getReference();
               Allele observedRefAllele;
               // insertions
      -        if ( vc.isInsertion() ) {
      +        if ( vc.isSimpleInsertion() ) {
                   observedRefAllele = Allele.create(Allele.NULL_ALLELE_STRING);
               }
               // deletions
      -        else if ( vc.isDeletion() || vc.isMixed() || vc.isMNP() ) {
      +        else if ( vc.isSimpleDeletion() || vc.isMixed() || vc.isMNP() ) {
                   // we can't validate arbitrarily long deletions
                   if ( reportedRefAllele.length() > 100 ) {
                       logger.info(String.format("Reference allele is too long (%d) at position %s:%d; skipping that record.", reportedRefAllele.length(), vc.getChr(), vc.getStart()));
      diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantsToVCF.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantsToVCF.java
      index 61851abe2..b41ce394f 100755
      --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantsToVCF.java
      +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantutils/VariantsToVCF.java
      @@ -168,8 +168,8 @@ public class VariantsToVCF extends RodWalker {
                                   continue;
       
                               Map alleleMap = new HashMap(2);
      -                        alleleMap.put(RawHapMapFeature.DELETION, Allele.create(Allele.NULL_ALLELE_STRING, dbsnpVC.isInsertion()));
      -                        alleleMap.put(RawHapMapFeature.INSERTION, Allele.create(((RawHapMapFeature)record).getAlleles()[1], !dbsnpVC.isInsertion()));
      +                        alleleMap.put(RawHapMapFeature.DELETION, Allele.create(Allele.NULL_ALLELE_STRING, dbsnpVC.isSimpleInsertion()));
      +                        alleleMap.put(RawHapMapFeature.INSERTION, Allele.create(((RawHapMapFeature)record).getAlleles()[1], !dbsnpVC.isSimpleInsertion()));
                               hapmap.setActualAlleles(alleleMap);
       
                               // also, use the correct positioning for insertions
      diff --git a/public/java/src/org/broadinstitute/sting/utils/IndelUtils.java b/public/java/src/org/broadinstitute/sting/utils/IndelUtils.java
      index af69ebca6..74f147127 100755
      --- a/public/java/src/org/broadinstitute/sting/utils/IndelUtils.java
      +++ b/public/java/src/org/broadinstitute/sting/utils/IndelUtils.java
      @@ -121,9 +121,9 @@ public class IndelUtils {
               boolean done = false;
       
               ArrayList inds = new ArrayList();
      -        if ( vc.isInsertion() ) {
      +        if ( vc.isSimpleInsertion() ) {
                   indelAlleleString = vc.getAlternateAllele(0).getDisplayString();
      -        } else if ( vc.isDeletion() ) {
      +        } else if ( vc.isSimpleDeletion() ) {
                   indelAlleleString = vc.getReference().getDisplayString();
               }
               else {
      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 ca3399c78..d953085ab 100755
      --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java
      +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java
      @@ -583,24 +583,24 @@ public class VariantContext implements Feature { // to enable tribble intergrati
           /**
            * @return true if the alleles indicate a simple insertion (i.e., the reference allele is Null)
            */
      -    public boolean isInsertion() {
      -        // can't just call !isDeletion() because of complex indels
      -        return getType() == Type.INDEL && getReference().isNull();
      +    public boolean isSimpleInsertion() {
      +        // can't just call !isSimpleDeletion() because of complex indels
      +        return getType() == Type.INDEL && getReference().isNull() && isBiallelic();
           }
       
           /**
            * @return true if the alleles indicate a simple deletion (i.e., a single alt allele that is Null)
            */
      -    public boolean isDeletion() {
      -        // can't just call !isInsertion() because of complex indels
      -        return getType() == Type.INDEL && getAlternateAllele(0).isNull();
      +    public boolean isSimpleDeletion() {
      +        // can't just call !isSimpleInsertion() because of complex indels
      +        return getType() == Type.INDEL && getAlternateAllele(0).isNull() && isBiallelic();
           }
       
           /**
            * @return true if the alleles indicate neither a simple deletion nor a simple insertion
            */
           public boolean isComplexIndel() {
      -        return isIndel() && !isDeletion() && !isInsertion();
      +        return isIndel() && !isSimpleDeletion() && !isSimpleInsertion();
           }
       
           public boolean isSymbolic() {
      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 d8fa0eae4..f8e6da20a 100755
      --- a/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUnitTest.java
      +++ b/public/java/test/org/broadinstitute/sting/utils/variantcontext/VariantContextUnitTest.java
      @@ -146,8 +146,8 @@ public class VariantContextUnitTest {
               Assert.assertEquals(vc.getType(), VariantContext.Type.SNP);
               Assert.assertTrue(vc.isSNP());
               Assert.assertFalse(vc.isIndel());
      -        Assert.assertFalse(vc.isInsertion());
      -        Assert.assertFalse(vc.isDeletion());
      +        Assert.assertFalse(vc.isSimpleInsertion());
      +        Assert.assertFalse(vc.isSimpleDeletion());
               Assert.assertFalse(vc.isMixed());
               Assert.assertTrue(vc.isBiallelic());
               Assert.assertEquals(vc.getNAlleles(), 2);
      @@ -173,8 +173,8 @@ public class VariantContextUnitTest {
               Assert.assertEquals(VariantContext.Type.NO_VARIATION, vc.getType());
               Assert.assertFalse(vc.isSNP());
               Assert.assertFalse(vc.isIndel());
      -        Assert.assertFalse(vc.isInsertion());
      -        Assert.assertFalse(vc.isDeletion());
      +        Assert.assertFalse(vc.isSimpleInsertion());
      +        Assert.assertFalse(vc.isSimpleDeletion());
               Assert.assertFalse(vc.isMixed());
               Assert.assertFalse(vc.isBiallelic());
               Assert.assertEquals(vc.getNAlleles(), 1);
      @@ -199,8 +199,8 @@ public class VariantContextUnitTest {
               Assert.assertEquals(vc.getType(), VariantContext.Type.INDEL);
               Assert.assertFalse(vc.isSNP());
               Assert.assertTrue(vc.isIndel());
      -        Assert.assertFalse(vc.isInsertion());
      -        Assert.assertTrue(vc.isDeletion());
      +        Assert.assertFalse(vc.isSimpleInsertion());
      +        Assert.assertTrue(vc.isSimpleDeletion());
               Assert.assertFalse(vc.isMixed());
               Assert.assertTrue(vc.isBiallelic());
               Assert.assertEquals(vc.getNAlleles(), 2);
      @@ -226,8 +226,8 @@ public class VariantContextUnitTest {
               Assert.assertEquals(vc.getType(), VariantContext.Type.INDEL);
               Assert.assertFalse(vc.isSNP());
               Assert.assertTrue(vc.isIndel());
      -        Assert.assertTrue(vc.isInsertion());
      -        Assert.assertFalse(vc.isDeletion());
      +        Assert.assertTrue(vc.isSimpleInsertion());
      +        Assert.assertFalse(vc.isSimpleDeletion());
               Assert.assertFalse(vc.isMixed());
               Assert.assertTrue(vc.isBiallelic());
               Assert.assertEquals(vc.getNAlleles(), 2);
      @@ -433,7 +433,7 @@ public class VariantContextUnitTest {
               Assert.assertFalse(vc14.isBiallelic());
       
               Assert.assertTrue(vc5.isIndel());
      -        Assert.assertTrue(vc5.isDeletion());
      +        Assert.assertTrue(vc5.isSimpleDeletion());
               Assert.assertTrue(vc5.isVariant());
               Assert.assertTrue(vc5.isBiallelic());
       
      
      From 626cbf94118e2da51649dfc445670d10f4703852 Mon Sep 17 00:00:00 2001
      From: Guillermo del Angel 
      Date: Thu, 18 Aug 2011 16:28:40 -0400
      Subject: [PATCH 084/138] Bug fixes and cleanups for IndelStatistics
      
      ---
       .../evaluators/IndelStatistics.java           | 70 ++++++-------------
       1 file changed, 23 insertions(+), 47 deletions(-)
      
      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 78683dfcb..fc347339d 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
      @@ -57,13 +57,13 @@ public class IndelStatistics extends VariantEvaluator {
           private static final int IND_HET = 0;
           private static final int IND_INS = 1;
           private static final int IND_DEL = 2;
      -    private static final int IND_AT_CG_RATIO = 3;
      +    private static final int IND_COMPLEX = 3;
           private static final int IND_HET_INS = 4;
           private static final int IND_HOM_INS = 5;
           private static final int IND_HET_DEL = 6;
           private static final int IND_HOM_DEL = 7;
           private static final int IND_HOM_REF = 8;
      -    private static final int IND_COMPLEX = 9;
      +    private static final int IND_MIXED = 9;
           private static final int IND_LONG = 10;
           private static final int IND_AT_EXP = 11;
           private static final int IND_CG_EXP = 12;
      @@ -86,7 +86,7 @@ public class IndelStatistics extends VariantEvaluator {
                   COLUMN_KEYS[0] = "heterozygosity";
                   COLUMN_KEYS[1] = "insertions";
                   COLUMN_KEYS[2] = "deletions";
      -            COLUMN_KEYS[3] = "AT_CG_expansion_ratio";
      +            COLUMN_KEYS[3] = "complex";
                   COLUMN_KEYS[4] = "het_insertions";
                   COLUMN_KEYS[5] = "homozygous_insertions";
                   COLUMN_KEYS[6] = "het_deletions";
      @@ -117,16 +117,7 @@ public class IndelStatistics extends VariantEvaluator {
                   return new String[]{"all"};
               }
               public Object getCell(int x, int y) {
      -            final Object[] rowKeys = getRowKeys();
      -            if (y == IND_AT_CG_RATIO) {
      -
      -                int at = indelSummary[IND_AT_EXP];
      -                int cg = indelSummary[IND_CG_EXP];
      -                return String.format("%4.2f",((double)at) / (Math.max(cg, 1)));
      -            }
      -            else
      -                return String.format("%d",indelSummary[y]);
      -
      +            return String.format("%d",indelSummary[y]);
               }
       
               /**
      @@ -165,40 +156,40 @@ public class IndelStatistics extends VariantEvaluator {
                       eventLength = -vc.getReference().length();
                       isDeletion = true;
                   }
      -            else {
      +            else if (vc.isComplexIndel()) {
                       indelSummary[IND_COMPLEX]++;
                   }
      +            else if (vc.isMixed())
      +                indelSummary[IND_MIXED]++;
      +
                   if (IndelUtils.isATExpansion(vc,ref))
                       indelSummary[IND_AT_EXP]++;
                   if (IndelUtils.isCGExpansion(vc,ref))
                        indelSummary[IND_CG_EXP]++;
       
                   // make sure event doesn't overstep array boundaries
      -            if (Math.abs(eventLength) < INDEL_SIZE_LIMIT) {
      -                indelSummary[len2Index(eventLength)]++;
      -                if (eventLength % 3 != 0)
      -                    indelSummary[IND_FRAMESHIFT]++;
      +            if (vc.isSimpleDeletion() || vc.isSimpleInsertion()) {
      +                if (Math.abs(eventLength) < INDEL_SIZE_LIMIT) {
      +                    indelSummary[len2Index(eventLength)]++;
      +                    if (eventLength % 3 != 0)
      +                        indelSummary[IND_FRAMESHIFT]++;
      +                }
      +                else
      +                    indelSummary[IND_LONG]++;
                   }
      -            else
      -                indelSummary[IND_LONG]++;
      -
       
               }
           }
       
           static class IndelClasses implements TableType {
      -        protected final static String ALL_SAMPLES_KEY = "allSamples";
               protected final static String[] columnNames = IndelUtils.getIndelClassificationNames();
       
       
               // map of sample to statistics
      -        protected final HashMap indelClassSummary = new HashMap();
      +        protected final int[] indelClassSummary;
       
               public IndelClasses(final VariantContext vc) {
      -            indelClassSummary.put(ALL_SAMPLES_KEY, new int[columnNames.length]);
      -            for( final String sample : vc.getGenotypes().keySet() ) {
      -                indelClassSummary.put(sample, new int[columnNames.length]);
      -            }
      +            indelClassSummary = new int[columnNames.length];
               }
       
               /**
      @@ -206,11 +197,10 @@ public class IndelStatistics extends VariantEvaluator {
                * @return one row per sample
                */
               public Object[] getRowKeys() {
      -            return indelClassSummary.keySet().toArray(new String[indelClassSummary.size()]);
      +            return new String[]{"all"};
               }
               public Object getCell(int x, int y) {
      -            final Object[] rowKeys = getRowKeys();
      -            return String.format("%d",indelClassSummary.get(rowKeys[x])[y]);
      +            return String.format("%d",indelClassSummary[y]);
               }
       
               /**
      @@ -234,18 +224,7 @@ public class IndelStatistics extends VariantEvaluator {
               }
       
               private void incrementSampleStat(VariantContext vc, int index) {
      -            indelClassSummary.get(ALL_SAMPLES_KEY)[index]++;
      -            for( final String sample : vc.getGenotypes().keySet() ) {
      -                 if ( indelClassSummary.containsKey(sample) ) {
      -                     Genotype g = vc.getGenotype(sample);
      -                     boolean isVariant = (g.isCalled() && !g.isHomRef());
      -                     if (isVariant)
      -                         // update  count
      -                         indelClassSummary.get(sample)[index]++;
      -
      -                 }
      -             }
      -
      +            indelClassSummary[index]++;
               }
               /*
                * increment the specified value
      @@ -293,16 +272,13 @@ public class IndelStatistics extends VariantEvaluator {
       
               if (eval != null ) {
                   if ( indelStats == null ) {
      -                int nSamples = numSamples;
      -
      -                if ( nSamples != -1 )
      -                    indelStats = new IndelStats(eval);
      +                indelStats = new IndelStats(eval);
                   }
                   if ( indelClasses == null ) {
                       indelClasses = new IndelClasses(eval);
                   }
       
      -            if ( eval.isIndel() && eval.isBiallelic() ) {
      +            if ( eval.isIndel() || eval.isMixed() ) {
                       if (indelStats != null )
                           indelStats.incrValue(eval, ref);
       
      
      From 09d099cadaa49b7eff1450ac1dd901e8a1ba6195 Mon Sep 17 00:00:00 2001
      From: Ryan Poplin 
      Date: Thu, 18 Aug 2011 20:57:02 -0400
      Subject: [PATCH 085/138] Added GATKDocs to the UnifiedGenotyper.
      
      ---
       .../AlleleFrequencyCalculationModel.java      |  2 +
       .../GenotypeLikelihoodsCalculationModel.java  |  2 +
       .../genotyper/UnifiedArgumentCollection.java  | 41 +++++++--
       .../walkers/genotyper/UnifiedGenotyper.java   | 83 +++++++++++++++++--
       .../genotyper/UnifiedGenotyperEngine.java     |  3 +
       .../recalibration/CountCovariatesWalker.java  |  6 ++
       .../TableRecalibrationWalker.java             |  2 +
       .../VariantRecalibrator.java                  |  4 +
       8 files changed, 129 insertions(+), 14 deletions(-)
      
      diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/AlleleFrequencyCalculationModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/AlleleFrequencyCalculationModel.java
      index 83a8ce7d7..70f3c6a1a 100755
      --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/AlleleFrequencyCalculationModel.java
      +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/AlleleFrequencyCalculationModel.java
      @@ -44,7 +44,9 @@ import java.util.Set;
       public abstract class AlleleFrequencyCalculationModel implements Cloneable {
       
           public enum Model {
      +        /** The default model with the best performance in all cases */
               EXACT,
      +        /** For posterity we have kept around the older GRID_SEARCH model, but this gives inferior results and shouldn't be used. */
               GRID_SEARCH
           }
       
      diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/GenotypeLikelihoodsCalculationModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/GenotypeLikelihoodsCalculationModel.java
      index 594c1dd28..60dfe4fe7 100755
      --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/GenotypeLikelihoodsCalculationModel.java
      +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/genotyper/GenotypeLikelihoodsCalculationModel.java
      @@ -53,7 +53,9 @@ public abstract class GenotypeLikelihoodsCalculationModel implements Cloneable {
           }
       
           public enum GENOTYPING_MODE {
      +        /** the default; the Unified Genotyper will choose the most likely alternate allele */
               DISCOVERY,
      +        /** only the alleles passed in from a VCF rod bound to the -alleles argument will be used for genotyping */
               GENOTYPE_GIVEN_ALLELES
           }
       
      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 1a76bfd07..e7f89bf08 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
      @@ -36,31 +36,54 @@ import java.io.File;
       
       public class UnifiedArgumentCollection {
       
      -    // control the various models to be used
           @Argument(fullName = "genotype_likelihoods_model", shortName = "glm", doc = "Genotype likelihoods calculation model to employ -- SNP is the default option, while INDEL is also available for calling indels and BOTH is available for calling both together", required = false)
           public GenotypeLikelihoodsCalculationModel.Model GLmodel = GenotypeLikelihoodsCalculationModel.Model.SNP;
       
      +    /**
      +     * Controls the model used to calculate the probability that a site is variant plus the various sample genotypes in the data at a given locus.
      +     */
           @Argument(fullName = "p_nonref_model", shortName = "pnrm", doc = "Non-reference probability calculation model to employ -- EXACT is the default option, while GRID_SEARCH is also available.", required = false)
           public AlleleFrequencyCalculationModel.Model AFmodel = AlleleFrequencyCalculationModel.Model.EXACT;
       
      +    /**
      +     * The expected heterozygosity value used to compute prior likelihoods for any locus. The default priors are:
      +     * het = 1e-3, P(hom-ref genotype) = 1 - 3 * het / 2, P(het genotype) = het, P(hom-var genotype) = het / 2
      +     */
           @Argument(fullName = "heterozygosity", shortName = "hets", doc = "Heterozygosity value used to compute prior likelihoods for any locus", required = false)
           public Double heterozygosity = DiploidSNPGenotypePriors.HUMAN_HETEROZYGOSITY;
       
           @Argument(fullName = "pcr_error_rate", shortName = "pcr_error", doc = "The PCR error rate to be used for computing fragment-based likelihoods", required = false)
           public Double PCR_error = DiploidSNPGenotypeLikelihoods.DEFAULT_PCR_ERROR_RATE;
       
      +    /**
      +     * Specifies how to determine the alternate allele to use for genotyping
      +     */
           @Argument(fullName = "genotyping_mode", shortName = "gt_mode", doc = "Should we output confident genotypes (i.e. including ref calls) or just the variants?", required = false)
           public GenotypeLikelihoodsCalculationModel.GENOTYPING_MODE GenotypingMode = GenotypeLikelihoodsCalculationModel.GENOTYPING_MODE.DISCOVERY;
       
           @Argument(fullName = "output_mode", shortName = "out_mode", doc = "Should we output confident genotypes (i.e. including ref calls) or just the variants?", required = false)
           public UnifiedGenotyperEngine.OUTPUT_MODE OutputMode = UnifiedGenotyperEngine.OUTPUT_MODE.EMIT_VARIANTS_ONLY;
       
      +    /**
      +     * The minimum phred-scaled Qscore threshold to separate high confidence from low confidence calls. Only genotypes with
      +     * confidence >= this threshold are emitted as called sites. A reasonable threshold is 30 for high-pass calling (this
      +     * is the default). Note that the confidence (QUAL) values for multi-sample low-pass (e.g. 4x per sample) calling might
      +     * be significantly smaller with the new EXACT model than with our older GRID_SEARCH model, as the latter tended to
      +     * over-estimate the confidence; for low-pass calling we tend to use much smaller thresholds (e.g. 4).
      +     */
           @Argument(fullName = "standard_min_confidence_threshold_for_calling", shortName = "stand_call_conf", doc = "The minimum phred-scaled confidence threshold at which variants not at 'trigger' track sites should be called", required = false)
           public double STANDARD_CONFIDENCE_FOR_CALLING = 30.0;
       
      +    /**
      +     * the minimum phred-scaled Qscore threshold to emit low confidence calls. Genotypes with confidence >= this but less
      +     * than the calling threshold are emitted but marked as filtered.
      +     */
           @Argument(fullName = "standard_min_confidence_threshold_for_emitting", shortName = "stand_emit_conf", doc = "The minimum phred-scaled confidence threshold at which variants not at 'trigger' track sites should be emitted (and filtered if less than the calling threshold)", required = false)
           public double STANDARD_CONFIDENCE_FOR_EMITTING = 30.0;
       
      +    /**
      +     * This argument is not enabled by default because it increases the runtime by an appreciable amount.
      +     */
           @Argument(fullName = "computeSLOD", shortName = "sl", doc = "If provided, we will calculate the SLOD", required = false)
           public boolean COMPUTE_SLOD = false;
       
      @@ -80,7 +103,6 @@ public class UnifiedArgumentCollection {
           @Argument(fullName = "abort_at_too_much_coverage", doc = "Don't call a site if the downsampled coverage is greater than this value", required = false)
           public int COVERAGE_AT_WHICH_TO_ABORT = -1;
       
      -
           // control the various parameters to be used
           @Argument(fullName = "min_base_quality_score", shortName = "mbq", doc = "Minimum base quality required to consider a base for calling", required = false)
           public int MIN_BASE_QUALTY_SCORE = 17;
      @@ -91,11 +113,17 @@ public class UnifiedArgumentCollection {
           @Argument(fullName = "max_deletion_fraction", shortName = "deletions", doc = "Maximum fraction of reads with deletions spanning this locus for it to be callable [to disable, set to < 0 or > 1; default:0.05]", required = false)
           public Double MAX_DELETION_FRACTION = 0.05;
       
      -
           // indel-related arguments
      +    /**
      +     * A candidate indel is genotyped (and potentially called) if there are this number of reads with a consensus indel at a site.
      +     * Decreasing this value will increase sensitivity but at the cost of larger calling time and a larger number of false positives.
      +     */
           @Argument(fullName = "min_indel_count_for_genotyping", shortName = "minIndelCnt", doc = "Minimum number of consensus indels required to trigger genotyping run", required = false)
           public int MIN_INDEL_COUNT_FOR_GENOTYPING = 5;
       
      +    /**
      +     * This argument informs the prior probability of having an indel at a site.
      +     */
           @Argument(fullName = "indel_heterozygosity", shortName = "indelHeterozygosity", doc = "Heterozygosity for indel calling", required = false)
           public double INDEL_HETEROZYGOSITY = 1.0/8000;
       
      @@ -126,22 +154,23 @@ public class UnifiedArgumentCollection {
           @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 = "Output indel debug info", 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 = "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)
      +    @Argument(fullName = "ignoreSNPAlleles", shortName = "ignoreSNPAlleles", doc = "expt", required = false)
           public boolean IGNORE_SNP_ALLELES = false;
       
      -
           @Deprecated
           @Argument(fullName="output_all_callable_bases", shortName="all_bases", doc="Please use --output_mode EMIT_ALL_SITES instead" ,required=false)
           private Boolean ALL_BASES_DEPRECATED = false;   
      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 d31bb6fb9..8d2101d8f 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
      @@ -45,11 +45,71 @@ import org.broadinstitute.sting.utils.variantcontext.VariantContext;
       import java.io.PrintStream;
       import java.util.*;
       
      -
       /**
      - * A variant caller which unifies the approaches of several disparate callers.  Works for single-sample and
      - * multi-sample data.  The user can choose from several different incorporated calculation models.
      + * A variant caller which unifies the approaches of several disparate callers -- Works for single-sample and multi-sample data.
      + *
      + * 

      + * The GATK Unified Genotyper is a multiple-sample, technology-aware SNP and indel caller. It uses a Bayesian genotype + * likelihood model to estimate simultaneously the most likely genotypes and allele frequency in a population of N samples, + * emitting an accurate posterior probability of there being a segregating variant allele at each locus as well as for the + * genotype of each sample. The system can either emit just the variant sites or complete genotypes (which includes + * homozygous reference calls) satisfying some phred-scaled confidence value. The genotyper can make accurate calls on + * both single sample data and multi-sample data. + * + *

      Input

      + *

      + * The read data from which to make variant calls. + *

      + * + *

      Output

      + *

      + * A raw, unfiltered, highly specific callset in VCF format. + *

      + * + *

      Example generic command for multi-sample SNP calling

      + *
      + * java -jar GenomeAnalysisTK.jar \
      + *   -R resources/Homo_sapiens_assembly18.fasta \
      + *   -T UnifiedGenotyper \
      + *   -I sample1.bam [-I sample2.bam ...] \
      + *   --dbsnp dbSNP.vcf \
      + *   -o snps.raw.vcf \
      + *   -stand_call_conf [50.0] \
      + *   -stand_emit_conf 10.0 \
      + *   -dcov [50] \
      + *   [-L targets.interval_list]
      + * 
      + * + *

      + * The above command will call all of the samples in your provided BAM files [-I arguments] together and produce a VCF file + * with sites and genotypes for all samples. The easiest way to get the dbSNP file is from the GATK resource bundle. Several + * arguments have parameters that should be chosen based on the average coverage per sample in your data. See the detailed + * argument descriptions below. + *

      + * + *

      Example command for generating calls at all sites

      + *
      + * java -jar /path/to/GenomeAnalysisTK.jar \
      + *   -l INFO \
      + *   -R resources/Homo_sapiens_assembly18.fasta \
      + *   -T UnifiedGenotyper \
      + *   -I /DCC/ftp/pilot_data/data/NA12878/alignment/NA12878.SLX.maq.SRP000031.2009_08.bam \
      + *   -o my.vcf \
      + *   --output_mode EMIT_ALL_SITES
      + * 
      + * + *

      Caveats

      + *
        + *
      • The system is under active and continuous development. All outputs, the underlying likelihood model, arguments, and + * file formats are likely to change.
      • + *
      • The system can be very aggressive in calling variants. In the 1000 genomes project for pilot 2 (deep coverage of ~35x) + * we expect the raw Qscore > 50 variants to contain at least ~10% FP calls. We use extensive post-calling filters to eliminate + * most of these FPs. Variant Quality Score Recalibration is a tool to perform this filtering.
      • + *
      • We only handle diploid genotypes
      • + *
      + * */ + @BAQMode(QualityMode = BAQ.QualityMode.ADD_TAG, ApplicationTime = BAQ.ApplicationTime.ON_INPUT) @ReadFilters( {BadMateFilter.class, MappingQualityUnavailableReadFilter.class} ) @Reference(window=@Window(start=-200,stop=200)) @@ -61,10 +121,9 @@ public class UnifiedGenotyper extends LocusWalker getDbsnpRodBinding() { return dbsnp.dbsnp; } @@ -72,7 +131,9 @@ public class UnifiedGenotyper extends LocusWalker> getCompRodBindings() { return Collections.emptyList(); } public List> getResourceRodBindings() { return Collections.emptyList(); } - // control the output + /** + * A raw, unfiltered, highly specific callset in VCF format. + */ @Output(doc="File to which variants should be written",required=true) protected VCFWriter writer = null; @@ -82,9 +143,15 @@ public class UnifiedGenotyper extends LocusWalker annotationsToUse = new ArrayList(); + /** + * Which groups of annotations to add to the output VCF file. See the VariantAnnotator -list argument to view available groups. + */ @Argument(fullName="group", shortName="G", doc="One or more classes/groups of annotations to apply to variant calls", required=false) protected String[] annotationClassesToUse = { "Standard" }; 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 b3f77fc06..06455df6d 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 @@ -51,8 +51,11 @@ public class UnifiedGenotyperEngine { public static final String LOW_QUAL_FILTER_NAME = "LowQual"; public enum OUTPUT_MODE { + /** the default */ EMIT_VARIANTS_ONLY, + /** include confident reference sites */ EMIT_ALL_CONFIDENT_SITES, + /** any callable site regardless of confidence */ EMIT_ALL_SITES } 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 5ffc61fe3..838842869 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 @@ -68,6 +68,8 @@ import java.util.Map; * *

      Input

      *

      + * The input read data whose base quality scores need to be assessed. + *

      * A database of known polymorphic sites to skip over. *

      * @@ -134,6 +136,10 @@ public class CountCovariatesWalker extends LocusWalkerInput *

      + * The input read data whose base quality scores need to be recalibrated. + *

      * The recalibration table file in CSV format that was generated by the CountCovariates walker. *

      * 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 d81a57aad..517c2362a 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 @@ -157,6 +157,10 @@ public class VariantRecalibrator extends RodWalker Date: Thu, 18 Aug 2011 21:20:09 -0400 Subject: [PATCH 086/138] Improvements to GATKDocs -- Allowed values for RodBinding are displayed in the GATKDocs -- Longest name up to 30 characters is chosen for main argument list (suggested by Ryan/Mauricio) -- Features are listed in alphabetical order -- Moved useful getParameterizedType() function to JVMUtils -- Tests of these features in the Documentation Test --- .../commandline/ArgumentTypeDescriptor.java | 14 +--- .../gatk/refdata/tracks/FeatureManager.java | 2 +- .../sting/utils/classloader/JVMUtils.java | 10 +++ .../help/GenericDocumentationHandler.java | 83 +++++++++++++++++-- 4 files changed, 91 insertions(+), 18 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java b/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java index dc32fcc16..7353305f1 100644 --- a/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java +++ b/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java @@ -325,7 +325,7 @@ class RodBindingArgumentTypeDescriptor extends ArgumentTypeDescriptor { @Override public Object createTypeDefault(ParsingEngine parsingEngine, ArgumentSource source, Type type) { - Class parameterType = getParameterizedTypeClass(type); + Class parameterType = JVMUtils.getParameterizedTypeClass(type); return RodBinding.makeUnbound((Class)parameterType); } @@ -338,7 +338,7 @@ class RodBindingArgumentTypeDescriptor extends ArgumentTypeDescriptor { public Object parse(ParsingEngine parsingEngine, ArgumentSource source, Type type, ArgumentMatches matches) { ArgumentDefinition defaultDefinition = createDefaultArgumentDefinition(source); String value = getArgumentValue( defaultDefinition, matches ); - Class parameterType = getParameterizedTypeClass(type); + Class parameterType = JVMUtils.getParameterizedTypeClass(type); try { String name = defaultDefinition.fullName; @@ -400,16 +400,6 @@ class RodBindingArgumentTypeDescriptor extends ArgumentTypeDescriptor { value, source.field.getName(), e.getMessage())); } } - - private Class getParameterizedTypeClass(Type t) { - if ( t instanceof ParameterizedType ) { - ParameterizedType parameterizedType = (ParameterizedType)t; - if ( parameterizedType.getActualTypeArguments().length != 1 ) - throw new ReviewedStingException("BUG: more than 1 generic type found on class" + t); - return (Class)parameterizedType.getActualTypeArguments()[0]; - } else - throw new ReviewedStingException("BUG: could not find generic type on class " + t); - } } /** diff --git a/public/java/src/org/broadinstitute/sting/gatk/refdata/tracks/FeatureManager.java b/public/java/src/org/broadinstitute/sting/gatk/refdata/tracks/FeatureManager.java index c7dd6ef14..c99aea254 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/refdata/tracks/FeatureManager.java +++ b/public/java/src/org/broadinstitute/sting/gatk/refdata/tracks/FeatureManager.java @@ -123,7 +123,7 @@ public class FeatureManager { */ @Requires("featureClass != null") public Collection getByFeature(Class featureClass) { - Set consistentDescriptors = new HashSet(); + Set consistentDescriptors = new TreeSet(); if (featureClass == null) throw new IllegalArgumentException("trackRecordType value is null, please pass in an actual class object"); diff --git a/public/java/src/org/broadinstitute/sting/utils/classloader/JVMUtils.java b/public/java/src/org/broadinstitute/sting/utils/classloader/JVMUtils.java index e65b8f921..fa154fca3 100755 --- a/public/java/src/org/broadinstitute/sting/utils/classloader/JVMUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/classloader/JVMUtils.java @@ -224,4 +224,14 @@ public class JVMUtils { throw new StingException("Unknown type: " + type + " (" + type.getClass().getName() + ")"); } } + + public static Class getParameterizedTypeClass(Type t) { + if ( t instanceof ParameterizedType ) { + ParameterizedType parameterizedType = (ParameterizedType)t; + if ( parameterizedType.getActualTypeArguments().length != 1 ) + throw new ReviewedStingException("BUG: more than 1 generic type found on class" + t); + return (Class)parameterizedType.getActualTypeArguments()[0]; + } else + throw new ReviewedStingException("BUG: could not find generic type on class " + t); + } } 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 5b358519e..86f72428a 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/GenericDocumentationHandler.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/GenericDocumentationHandler.java @@ -30,14 +30,18 @@ import com.sun.javadoc.FieldDoc; import com.sun.javadoc.RootDoc; import com.sun.javadoc.Tag; import org.apache.log4j.Logger; +import org.broad.tribble.Feature; import org.broadinstitute.sting.commandline.*; import org.broadinstitute.sting.gatk.CommandLineGATK; +import org.broadinstitute.sting.gatk.refdata.tracks.FeatureManager; import org.broadinstitute.sting.utils.Utils; import org.broadinstitute.sting.utils.classloader.JVMUtils; +import org.broadinstitute.sting.utils.collections.Pair; import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; +import org.broadinstitute.sting.utils.exceptions.StingException; import java.io.*; -import java.lang.reflect.Field; +import java.lang.reflect.*; import java.util.*; /** @@ -295,6 +299,8 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { return fieldDoc; Field field = HelpUtils.getFieldForFieldDoc(fieldDoc); + if ( field == null ) + throw new RuntimeException("Could not find the field corresponding to " + fieldDoc + ", presumably because the field is inaccessible"); if ( field.isAnnotationPresent(ArgumentCollection.class) ) { ClassDoc typeDoc = getRootDoc().classNamed(fieldDoc.type().qualifiedTypeName()); if ( typeDoc == null ) @@ -319,15 +325,82 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { return null; } + private static final int MAX_DISPLAY_NAME = 30; + Pair displayNames(String s1, String s2) { + if ( s1 == null ) return new Pair(s2, null); + if ( s2 == null ) return new Pair(s1, null); + + String l = s1.length() > s2.length() ? s1 : s2; + String s = s1.length() > s2.length() ? s2 : s1; + + if ( l.length() > MAX_DISPLAY_NAME ) + return new Pair(s, l); + else + return new Pair(l, s); + } + + protected String argumentTypeString(Type type) { + if (type instanceof ParameterizedType) { + ParameterizedType parameterizedType = (ParameterizedType)type; + List subs = new ArrayList(); + for (Type actualType: parameterizedType.getActualTypeArguments()) + subs.add(argumentTypeString(actualType)); + return argumentTypeString(((ParameterizedType)type).getRawType()) + "[" + Utils.join(",", subs) + "]"; + } else if (type instanceof GenericArrayType) { + return argumentTypeString(((GenericArrayType)type).getGenericComponentType()) + "[]"; + } else if (type instanceof WildcardType) { + throw new RuntimeException("We don't support wildcards in arguments: " + type); + } else if (type instanceof Class) { + return ((Class) type).getSimpleName(); + } else { + throw new StingException("Unknown type: " + type); + } + } + + protected Class getFeatureTypeIfPossible(Type type) { + if ( type instanceof ParameterizedType) { + ParameterizedType paramType = (ParameterizedType)type; + if ( RodBinding.class.isAssignableFrom((Class)paramType.getRawType()) ) { + return (Class)JVMUtils.getParameterizedTypeClass(type); + } else { + for ( Type paramtype : paramType.getActualTypeArguments() ) { + Class x = getFeatureTypeIfPossible(paramtype); + if ( x != null ) + return x; + } + } + } + + return null; + } + protected Map docForArgument(FieldDoc fieldDoc, ArgumentSource source, ArgumentDefinition def) { Map root = new HashMap(); - root.put("name", def.shortName != null ? "-" + def.shortName : "--" + def.fullName ); + Pair names = displayNames("-" + def.shortName, "--" + def.fullName); - if ( def.shortName != null && def.fullName != null ) - root.put("synonyms", "--" + def.fullName); + root.put("name", names.getFirst() ); + + if ( names.getSecond() != null ) + root.put("synonyms", names.getSecond()); root.put("required", def.required ? "yes" : "no"); - root.put("type", def.argumentType.getSimpleName()); + + // type of the field + root.put("type", argumentTypeString(source.field.getGenericType())); + + Class featureClass = getFeatureTypeIfPossible(source.field.getGenericType()); + if ( featureClass != null ) { + // deal with the allowable types + FeatureManager manager = new FeatureManager(); + List rodTypes = new ArrayList(); + for (FeatureManager.FeatureDescriptor descriptor : manager.getByFeature(featureClass) ) { + rodTypes.add(String.format("%s", + GATKDocUtils.htmlFilenameForClass(descriptor.getCodecClass()), + descriptor.getName())); + } + + root.put("rodTypes", Utils.join(", ", rodTypes)); + } // summary and fulltext root.put("summary", def.doc != null ? def.doc : ""); From d94da0b1cfcd66955a365c8207a66b92b4bdd451 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 18 Aug 2011 21:20:26 -0400 Subject: [PATCH 087/138] Moved CG and SOAP codecs to private --- .../codecs/completegenomics/CGVarCodec.java | 159 ------------- .../utils/codecs/soapsnp/SoapSNPCodec.java | 209 ------------------ 2 files changed, 368 deletions(-) delete mode 100755 public/java/src/org/broadinstitute/sting/utils/codecs/completegenomics/CGVarCodec.java delete mode 100755 public/java/src/org/broadinstitute/sting/utils/codecs/soapsnp/SoapSNPCodec.java diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/completegenomics/CGVarCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/completegenomics/CGVarCodec.java deleted file mode 100755 index c30da828e..000000000 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/completegenomics/CGVarCodec.java +++ /dev/null @@ -1,159 +0,0 @@ -/* - * 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.utils.codecs.completegenomics; - -import org.broad.tribble.Feature; -import org.broad.tribble.FeatureCodec; -import org.broad.tribble.readers.LineReader; -import org.broadinstitute.sting.utils.variantcontext.Allele; -import org.broadinstitute.sting.utils.variantcontext.VariantContext; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -/** - * a codec for the VAR file types produced by the Complete Genomics Institute - */ -public class CGVarCodec implements FeatureCodec { - - private static final String REF_TYPE = "ref"; - private static final String SNP_TYPE = "snp"; - private static final String DELETION_TYPE = "del"; - private static final String INSERTION_TYPE = "ins"; - private static final String SUBSTITUTION_TYPE = "sub"; - - // the minimum number of features in the CG file line - private static final int minimumFeatureCount = 8; - - /** - * decode the location only - * @param line the input line to decode - * @return a HapMapFeature - */ - public Feature decodeLoc(String line) { - return decode(line); - } - - /** - * decode the CG record - * @param line the input line to decode - * @return a VariantContext - */ - public Feature decode(String line) { - String[] array = line.split("\\s+"); - - // make sure the split was successful - that we got an appropriate number of fields - if ( array.length < minimumFeatureCount ) - return null; - - String type = array[6]; - - long start = Long.valueOf(array[4]); - long end; - Allele ref, alt = null; - - //System.out.println(line); - - if ( type.equals(SNP_TYPE) ) { - ref = Allele.create(array[7], true); - alt = Allele.create(array[8], false); - end = start; - } else if ( type.equals(INSERTION_TYPE) ) { - ref = Allele.create(Allele.NULL_ALLELE_STRING, true); - alt = Allele.create(array[7], false); - end = start; - } else if ( type.equals(DELETION_TYPE) ) { - ref = Allele.create(array[7], true); - alt = Allele.create(Allele.NULL_ALLELE_STRING, false); - end = start + ref.length(); - //} else if ( type.equals(REF_TYPE) ) { - // ref = Allele.create("N", true); // ref bases aren't accurate - // start++; - // end = start; - //} else if ( type.equals(SUBSTITUTION_TYPE) ) { - // ref = Allele.create(array[7], true); - // alt = Allele.create(array[8], false); - // end = start + Math.max(ref.length(), alt.length()); - } else { - return null; // we don't handle other types - } - - HashSet alleles = new HashSet(); - alleles.add(ref); - if ( alt != null ) - alleles.add(alt); - - HashMap attrs = new HashMap(); - String id = array[array.length - 1]; - if ( id.indexOf("dbsnp") != -1 ) { - attrs.put(VariantContext.ID_KEY, parseID(id)); - } - - // create a new feature given the array - return new VariantContext("CGI", array[3], start, end, alleles, VariantContext.NO_NEG_LOG_10PERROR, null, attrs); - } - - public Class getFeatureType() { - return VariantContext.class; - } - - // There's no spec and no character to distinguish header lines... - private final static int NUM_HEADER_LINES = 12; - public Object readHeader(LineReader reader) { - return null; - - //String headerLine = null; - //try { - // for (int i = 0; i < NUM_HEADER_LINES; i++) - // headerLine = reader.readLine(); - //} catch (IOException e) { - // throw new IllegalArgumentException("Unable to read a line from the line reader"); - //} - //return headerLine; - } - - private static final Pattern DBSNP_PATTERN = Pattern.compile("^dbsnp\\.\\d+:(.*)"); - private String parseID(String raw) { - StringBuilder sb = null; - - String[] ids = raw.split(";"); - for ( String id : ids ) { - Matcher matcher = DBSNP_PATTERN.matcher(id); - if ( matcher.matches() ) { - String rsID = matcher.group(1); - if ( sb == null ) { - sb = new StringBuilder(rsID); - } else { - sb.append(";"); - sb.append(rsID); - } - } - } - - return sb == null ? null : sb.toString(); - } -} diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/soapsnp/SoapSNPCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/soapsnp/SoapSNPCodec.java deleted file mode 100755 index 284c43e90..000000000 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/soapsnp/SoapSNPCodec.java +++ /dev/null @@ -1,209 +0,0 @@ -package org.broadinstitute.sting.utils.codecs.soapsnp; - -import org.broad.tribble.Feature; -import org.broad.tribble.FeatureCodec; -import org.broad.tribble.NameAwareCodec; -import org.broad.tribble.TribbleException; -import org.broad.tribble.exception.CodecLineParsingException; -import org.broad.tribble.readers.LineReader; -import org.broadinstitute.sting.utils.variantcontext.Allele; -import org.broadinstitute.sting.utils.variantcontext.Genotype; -import org.broadinstitute.sting.utils.variantcontext.VariantContext; - -import java.util.*; - -/** - * @author depristo - *

      - * a codec for parsing soapsnp files (see http://soap.genomics.org.cn/soapsnp.html#usage2) - *

      - * - * A simple text file format with the following whitespace separated fields: - * -1) Chromosome ID -2) Coordinate on chromosome, start from 1 -3) Reference genotype -4) Consensus genotype -5) Quality score of consensus genotype -6) Best base -7) Average quality score of best base -8) Count of uniquely mapped best base -9) Count of all mapped best base -10) Second best bases -11) Average quality score of second best base -12) Count of uniquely mapped second best base -13) Count of all mapped second best base -14) Sequencing depth of the site -15) Rank sum test p_value -16) Average copy number of nearby region -17) Whether the site is a dbSNP. - */ -public class SoapSNPCodec implements FeatureCodec, NameAwareCodec { - private String[] parts; - - // we store a name to give to each of the variant contexts we emit - private String name = "Unknown"; - - public Feature decodeLoc(String line) { - return decode(line); - } - - /** - * Decode a line as a Feature. - * - * @param line - * - * @return Return the Feature encoded by the line, or null if the line does not represent a feature (e.g. is - * a comment) - */ - public Feature decode(String line) { - try { - // parse into lines - parts = line.trim().split("\\s+"); - - // check that we got the correct number of tokens in the split - if (parts.length != 18) - throw new CodecLineParsingException("Invalid SoapSNP row found -- incorrect element count. Expected 18, got " + parts.length + " line = " + line); - - String contig = parts[0]; - long start = Long.valueOf(parts[1]); - AlleleAndGenotype allelesAndGenotype = parseAlleles(parts[2], parts[3], line); - - double negLog10PError = Integer.valueOf(parts[4]) / 10.0; - - Map attributes = new HashMap(); - attributes.put("BestBaseQ", parts[6]); - attributes.put("SecondBestBaseQ", parts[10]); - attributes.put("RankSumP", parts[15]); - // add info to keys - - //System.out.printf("Alleles = " + allelesAndGenotype.alleles); - //System.out.printf("genotype = " + allelesAndGenotype.genotype); - - VariantContext vc = new VariantContext(name, contig, start, start, allelesAndGenotype.alleles, allelesAndGenotype.genotype, negLog10PError, VariantContext.PASSES_FILTERS, attributes); - - //System.out.printf("line = %s%n", line); - //System.out.printf("vc = %s%n", vc); - - return vc; - } catch (CodecLineParsingException e) { - throw new TribbleException("Unable to parse line " + line,e); - } catch (NumberFormatException e) { - e.printStackTrace(); //To change body of catch statement use File | Settings | File Templates. - throw new TribbleException("Unable to parse line " + line,e); - } - } - - private static class AlleleAndGenotype { - Collection alleles; - Collection genotype; - - public AlleleAndGenotype(Collection alleles, Genotype genotype) { - this.alleles = alleles; - this.genotype = new HashSet(); - this.genotype.add(genotype); - } - } - - private AlleleAndGenotype parseAlleles(String ref, String consensusGenotype, String line) { - /* A Adenine - C Cytosine - G Guanine - T (or U) Thymine (or Uracil) - R A or G - Y C or T - S G or C - W A or T - K G or T - M A or C - B C or G or T - D A or G or T - H A or C or T - V A or C or G - N any base - . or - gap - */ - if ( ref.equals(consensusGenotype) ) - throw new TribbleException.InternalCodecException("Ref base and consensus genotype are the same " + ref); - - Allele refAllele = Allele.create(ref, true); - List genotypeAlleles = null; - - char base = consensusGenotype.charAt(0); - - switch ( base ) { - case 'A': case 'C': case 'G': case 'T': - Allele a = Allele.create(consensusGenotype); - genotypeAlleles = Arrays.asList(a, a); - break; - case 'R': case 'Y': case 'S': case 'W': case 'K': case 'M': - genotypeAlleles = determineAlt(refAllele, ref.charAt(0), base); - break; - default: - throw new TribbleException("Unexpected consensus genotype " + consensusGenotype + " at line = " + line); - } - - - Collection alleles = new HashSet(genotypeAlleles); - alleles.add(refAllele); - Genotype genotype = new Genotype("unknown", genotypeAlleles); // todo -- probably should include genotype quality - - return new AlleleAndGenotype( alleles, genotype ); - } - - private static final Map IUPAC_SNPS = new HashMap(); - static { - IUPAC_SNPS.put('R', "AG"); - IUPAC_SNPS.put('Y', "CT"); - IUPAC_SNPS.put('S', "GC"); - IUPAC_SNPS.put('W', "AT"); - IUPAC_SNPS.put('K', "GT"); - IUPAC_SNPS.put('M', "AC"); - } - - private List determineAlt(Allele ref, char refbase, char alt) { - String alts = IUPAC_SNPS.get(alt); - if ( alts == null ) - throw new IllegalStateException("BUG: unexpected consensus genotype " + alt); - - Allele a1 = alts.charAt(0) == refbase ? ref : Allele.create((byte)alts.charAt(0)); - Allele a2 = alts.charAt(1) == refbase ? ref : Allele.create((byte)alts.charAt(1)); - - //if ( a1 != ref && a2 != ref ) - // throw new IllegalStateException("BUG: unexpected consensus genotype " + alt + " does not contain the reference base " + ref); - - return Arrays.asList(a1, a2); - } - - /** - * @return VariantContext - */ - public Class getFeatureType() { - return VariantContext.class; - } - - public Object readHeader(LineReader reader) { - - return null; // we don't have a meaningful header - } - - /** - * get the name of this codec - * @return our set name - */ - public String getName() { - return name; - } - - /** - * set the name of this codec - * @param name new name - */ - public void setName(String name) { - this.name = name; - } - - public static void main(String[] args) { - System.out.printf("Testing " + args[0]); - } -} \ No newline at end of file From cca093051738eb18b3491ab8bc58e85d3d4f99ce Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 18 Aug 2011 21:24:23 -0400 Subject: [PATCH 088/138] Better formatting for GATKDocs --- settings/helpTemplates/generic.template.html | 13 +++++++++---- settings/helpTemplates/style.css | 14 +++++++++----- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/settings/helpTemplates/generic.template.html b/settings/helpTemplates/generic.template.html index 0c938c343..e62b7c6fb 100644 --- a/settings/helpTemplates/generic.template.html +++ b/settings/helpTemplates/generic.template.html @@ -20,16 +20,21 @@ <#macro argumentDetails arg>

      ${arg.name}<#if arg.synonyms??> / ${arg.synonyms} (<#if arg.attributes??>${arg.attributes} ${arg.type}<#if arg.defaultValue??> with default value ${arg.defaultValue})

      - ${arg.summary}. ${arg.fulltext}
      +

      + ${arg.summary}. ${arg.fulltext} + <#if arg.rodTypes??>${arg.name} binds reference ordered data. This argument supports ROD files of the + following types: ${arg.rodTypes} <#if arg.options??> -

      The ${arg.name} argument is an enumerated type (${arg.type}), which can have one of the following values:

      +
      + The ${arg.name} argument is an enumerated type (${arg.type}), which can have one of the following values:
      <#list arg.options as option> -
      ${option.name} -
      ${option.summary} +
      ${option.name}
      +
      ${option.summary}
      +

      <#macro relatedByType name type> diff --git a/settings/helpTemplates/style.css b/settings/helpTemplates/style.css index 2abcf0397..375df2f51 100644 --- a/settings/helpTemplates/style.css +++ b/settings/helpTemplates/style.css @@ -42,6 +42,10 @@ p.version text-align: center; } +p.args +{ + margin-left: 3em; +} h1, h2, h3, h4 { @@ -81,17 +85,17 @@ hr * enum DT layout */ -dl { - border: 1px solid #ccc; +dl.enum { + margin-left: 3em; + border: 1px dashed #ccc; } -dt { +dt.enum { font-weight: bold; text-decoration: underline; } -dd { - margin: 0; +dd.enum { padding: 0 0 0.5em 0; } From c5efb6f40e7c20ee1cb5fe965719c9089aea8809 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 18 Aug 2011 21:39:11 -0400 Subject: [PATCH 089/138] Usability improvements to GATKDocs -- ArgumentSources are now sorted by case insensitive names, so arguments are shown in alphabetical order (Ryan) -- @Advanced annotation can be used to indicate that an argument is an advanced option and should be visually deemphasized in the GATKs. There's now an advanced section. Mauricio or Ryan -- could you figure out how to make this section less prominent in the style.css? --- .../sting/commandline/Advanced.java | 40 +++++++++++++++++++ .../sting/commandline/ArgumentSource.java | 15 ++++++- .../sting/commandline/ParsingEngine.java | 2 +- .../help/GenericDocumentationHandler.java | 2 + 4 files changed, 57 insertions(+), 2 deletions(-) create mode 100644 public/java/src/org/broadinstitute/sting/commandline/Advanced.java diff --git a/public/java/src/org/broadinstitute/sting/commandline/Advanced.java b/public/java/src/org/broadinstitute/sting/commandline/Advanced.java new file mode 100644 index 000000000..7aeefe261 --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/commandline/Advanced.java @@ -0,0 +1,40 @@ +/* + * 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.commandline; + +import java.lang.annotation.*; + +/** + * Indicates that a walker argument should is considered an advanced option. + * + * @author Mark DePristo + * @version 0.1 + */ +@Documented +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ElementType.TYPE,ElementType.FIELD}) +public @interface Advanced { +} diff --git a/public/java/src/org/broadinstitute/sting/commandline/ArgumentSource.java b/public/java/src/org/broadinstitute/sting/commandline/ArgumentSource.java index e0e2ac378..3e149b0c6 100644 --- a/public/java/src/org/broadinstitute/sting/commandline/ArgumentSource.java +++ b/public/java/src/org/broadinstitute/sting/commandline/ArgumentSource.java @@ -39,7 +39,7 @@ import java.util.List; * @author mhanna * @version 0.1 */ -public class ArgumentSource { +public class ArgumentSource implements Comparable { /** * Field into which to inject command-line arguments. */ @@ -151,6 +151,14 @@ public class ArgumentSource { return field.isAnnotationPresent(Hidden.class) || field.isAnnotationPresent(Deprecated.class); } + /** + * Is the given argument considered an advanced option when displaying on the command-line argument system. + * @return True if so. False otherwise. + */ + public boolean isAdvanced() { + return field.isAnnotationPresent(Advanced.class); + } + /** * Is this command-line argument dependent on some primitive argument types? * @return True if this command-line argument depends on other arguments; false otherwise. @@ -208,4 +216,9 @@ public class ArgumentSource { public String toString() { return field.getDeclaringClass().getSimpleName() + ": " + field.getName(); } + + @Override + public int compareTo(final ArgumentSource argumentSource) { + return field.getName().toLowerCase().compareTo(argumentSource.field.getName().toLowerCase()); + } } diff --git a/public/java/src/org/broadinstitute/sting/commandline/ParsingEngine.java b/public/java/src/org/broadinstitute/sting/commandline/ParsingEngine.java index fbf8c6516..12b454f4f 100755 --- a/public/java/src/org/broadinstitute/sting/commandline/ParsingEngine.java +++ b/public/java/src/org/broadinstitute/sting/commandline/ParsingEngine.java @@ -451,7 +451,7 @@ public class ParsingEngine { * @return A map of sources associated with this object and its aggregated objects and bindings to their bindings values */ private Map extractArgumentBindings(Object obj, Class sourceClass, Field[] parentFields) { - Map bindings = new LinkedHashMap(); + Map bindings = new TreeMap(); while( sourceClass != null ) { Field[] fields = sourceClass.getDeclaredFields(); 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 86f72428a..fe08e5979 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/GenericDocumentationHandler.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/GenericDocumentationHandler.java @@ -116,6 +116,7 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { args.put("all", new ArrayList()); args.put("required", new ArrayList()); args.put("optional", new ArrayList()); + args.put("advanced", new ArrayList()); args.put("hidden", new ArrayList()); args.put("depreciated", new ArrayList()); try { @@ -127,6 +128,7 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { logger.debug(String.format("Processing %s", argumentSource)); String kind = "optional"; if ( argumentSource.isRequired() ) kind = "required"; + else if ( argumentSource.isAdvanced() ) kind = "advanced"; else if ( argumentSource.isHidden() ) kind = "hidden"; else if ( argumentSource.isDeprecated() ) kind = "depreciated"; From d1892cd0d77194af26df39008600a7ba03b68aab Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 18 Aug 2011 21:58:36 -0400 Subject: [PATCH 090/138] Bug fixes -- Sorting of ArgumentSources now done in GATKDoclet, not in the ParsingEngine, as the system depends on the LinkedTreeMap -- Fixed broken exception throwing in the case where a file's type could not be determined --- .../sting/commandline/ArgumentTypeDescriptor.java | 11 ++++++----- .../sting/commandline/ParsingEngine.java | 2 +- .../sting/utils/help/GenericDocumentationHandler.java | 2 +- 3 files changed, 8 insertions(+), 7 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java b/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java index 7353305f1..b12ae8e75 100644 --- a/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java +++ b/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java @@ -375,13 +375,14 @@ class RodBindingArgumentTypeDescriptor extends ArgumentTypeDescriptor { if ( featureDescriptor != null ) { tribbleType = featureDescriptor.getName(); logger.warn("Dynamically determined type of " + file + " to be " + tribbleType); - } else { - throw new UserException.CommandLineException( - String.format("No tribble type was provided on the command line and the type of the file could not be determined dynamically. " + - "Please add an explicit type tag :NAME listing the correct type from among the supported types:%n%s", - manager.userFriendlyListOfAvailableFeatures(parameterType))); } } + + if ( tribbleType == null ) + throw new UserException.CommandLineException( + String.format("No tribble type was provided on the command line and the type of the file could not be determined dynamically. " + + "Please add an explicit type tag :NAME listing the correct type from among the supported types:%n%s", + manager.userFriendlyListOfAvailableFeatures(parameterType))); } } diff --git a/public/java/src/org/broadinstitute/sting/commandline/ParsingEngine.java b/public/java/src/org/broadinstitute/sting/commandline/ParsingEngine.java index 12b454f4f..fbf8c6516 100755 --- a/public/java/src/org/broadinstitute/sting/commandline/ParsingEngine.java +++ b/public/java/src/org/broadinstitute/sting/commandline/ParsingEngine.java @@ -451,7 +451,7 @@ public class ParsingEngine { * @return A map of sources associated with this object and its aggregated objects and bindings to their bindings values */ private Map extractArgumentBindings(Object obj, Class sourceClass, Field[] parentFields) { - Map bindings = new TreeMap(); + Map bindings = new LinkedHashMap(); while( sourceClass != null ) { Field[] fields = sourceClass.getDeclaredFields(); 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 fe08e5979..ff57bb744 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/GenericDocumentationHandler.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/GenericDocumentationHandler.java @@ -120,7 +120,7 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { args.put("hidden", new ArrayList()); args.put("depreciated", new ArrayList()); try { - for ( ArgumentSource argumentSource : parsingEngine.extractArgumentSources(HelpUtils.getClassForDoc(classdoc)) ) { + for ( ArgumentSource argumentSource : new TreeSet(parsingEngine.extractArgumentSources(HelpUtils.getClassForDoc(classdoc))) ) { ArgumentDefinition argDef = argumentSource.createArgumentDefinitions().get(0); FieldDoc fieldDoc = getFieldDoc(classdoc, argumentSource.field.getName()); Map argBindings = docForArgument(fieldDoc, argumentSource, argDef); // todo -- why can you have multiple ones? From f2a05af3561f45bc315a921660d1e70fda8ddfad Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 18 Aug 2011 21:59:44 -0400 Subject: [PATCH 091/138] Fixed layout problem with advanced arguments --- settings/helpTemplates/generic.template.html | 1 + 1 file changed, 1 insertion(+) diff --git a/settings/helpTemplates/generic.template.html b/settings/helpTemplates/generic.template.html index e62b7c6fb..7fc8cd7bd 100644 --- a/settings/helpTemplates/generic.template.html +++ b/settings/helpTemplates/generic.template.html @@ -82,6 +82,7 @@ <@argumentlist name="Required" myargs=arguments.required/> <@argumentlist name="Optional" myargs=arguments.optional/> + <@argumentlist name="Advanced" myargs=arguments.advanced/> <@argumentlist name="Hidden" myargs=arguments.hidden/> <@argumentlist name="Depreciated" myargs=arguments.depreciated/> From 77fa2c15469a9efeabb7245f8afeae8905bf30ca Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Thu, 18 Aug 2011 22:01:33 -0400 Subject: [PATCH 092/138] Renaming read filters with a superfluous 'Read' in their names. Kept the ones that made sense to have it (e.g. MalformedReadFilter). --- ...ckReadFilter.java => FailsVendorQualityCheckFilter.java} | 2 +- ...pingQualityReadFilter.java => MappingQualityFilter.java} | 2 +- ...ReadFilter.java => MappingQualityUnavailableFilter.java} | 2 +- ...ityZeroReadFilter.java => MappingQualityZeroFilter.java} | 2 +- ...gnmentReadFilter.java => NotPrimaryAlignmentFilter.java} | 2 +- .../broadinstitute/sting/gatk/walkers/DuplicateWalker.java | 4 ++-- .../org/broadinstitute/sting/gatk/walkers/LocusWalker.java | 6 +++--- .../sting/gatk/walkers/genotyper/UnifiedGenotyper.java | 4 ++-- .../sting/gatk/walkers/indels/RealignerTargetCreator.java | 4 ++-- .../gatk/walkers/indels/SomaticIndelDetectorWalker.java | 4 ++-- .../sting/gatk/walkers/phasing/ReadBackedPhasingWalker.java | 4 ++-- .../gatk/walkers/recalibration/CountCovariatesWalker.java | 6 +++--- 12 files changed, 21 insertions(+), 21 deletions(-) rename public/java/src/org/broadinstitute/sting/gatk/filters/{FailsVendorQualityCheckReadFilter.java => FailsVendorQualityCheckFilter.java} (95%) rename public/java/src/org/broadinstitute/sting/gatk/filters/{MappingQualityReadFilter.java => MappingQualityFilter.java} (96%) rename public/java/src/org/broadinstitute/sting/gatk/filters/{MappingQualityUnavailableReadFilter.java => MappingQualityUnavailableFilter.java} (95%) rename public/java/src/org/broadinstitute/sting/gatk/filters/{MappingQualityZeroReadFilter.java => MappingQualityZeroFilter.java} (95%) rename public/java/src/org/broadinstitute/sting/gatk/filters/{NotPrimaryAlignmentReadFilter.java => NotPrimaryAlignmentFilter.java} (95%) diff --git a/public/java/src/org/broadinstitute/sting/gatk/filters/FailsVendorQualityCheckReadFilter.java b/public/java/src/org/broadinstitute/sting/gatk/filters/FailsVendorQualityCheckFilter.java similarity index 95% rename from public/java/src/org/broadinstitute/sting/gatk/filters/FailsVendorQualityCheckReadFilter.java rename to public/java/src/org/broadinstitute/sting/gatk/filters/FailsVendorQualityCheckFilter.java index cd77a9e7e..4ec451567 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/filters/FailsVendorQualityCheckReadFilter.java +++ b/public/java/src/org/broadinstitute/sting/gatk/filters/FailsVendorQualityCheckFilter.java @@ -34,7 +34,7 @@ import net.sf.samtools.SAMRecord; * Filter out FailsVendorQualityCheck reads. */ -public class FailsVendorQualityCheckReadFilter extends ReadFilter { +public class FailsVendorQualityCheckFilter extends ReadFilter { public boolean filterOut( final SAMRecord read ) { return read.getReadFailsVendorQualityCheckFlag(); } diff --git a/public/java/src/org/broadinstitute/sting/gatk/filters/MappingQualityReadFilter.java b/public/java/src/org/broadinstitute/sting/gatk/filters/MappingQualityFilter.java similarity index 96% rename from public/java/src/org/broadinstitute/sting/gatk/filters/MappingQualityReadFilter.java rename to public/java/src/org/broadinstitute/sting/gatk/filters/MappingQualityFilter.java index 75369b306..ed9c37dca 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/filters/MappingQualityReadFilter.java +++ b/public/java/src/org/broadinstitute/sting/gatk/filters/MappingQualityFilter.java @@ -35,7 +35,7 @@ import org.broadinstitute.sting.commandline.Argument; * @version 0.1 */ -public class MappingQualityReadFilter extends ReadFilter { +public class MappingQualityFilter extends ReadFilter { @Argument(fullName = "min_mapping_quality_score", shortName = "mmq", doc = "Minimum read mapping quality required to consider a read for calling", required = false) public int MIN_MAPPING_QUALTY_SCORE = 10; diff --git a/public/java/src/org/broadinstitute/sting/gatk/filters/MappingQualityUnavailableReadFilter.java b/public/java/src/org/broadinstitute/sting/gatk/filters/MappingQualityUnavailableFilter.java similarity index 95% rename from public/java/src/org/broadinstitute/sting/gatk/filters/MappingQualityUnavailableReadFilter.java rename to public/java/src/org/broadinstitute/sting/gatk/filters/MappingQualityUnavailableFilter.java index 1afec36d1..ccdb40d31 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/filters/MappingQualityUnavailableReadFilter.java +++ b/public/java/src/org/broadinstitute/sting/gatk/filters/MappingQualityUnavailableFilter.java @@ -34,7 +34,7 @@ import org.broadinstitute.sting.utils.QualityUtils; * @version 0.1 */ -public class MappingQualityUnavailableReadFilter extends ReadFilter { +public class MappingQualityUnavailableFilter extends ReadFilter { public boolean filterOut(SAMRecord rec) { return (rec.getMappingQuality() == QualityUtils.MAPPING_QUALITY_UNAVAILABLE); } diff --git a/public/java/src/org/broadinstitute/sting/gatk/filters/MappingQualityZeroReadFilter.java b/public/java/src/org/broadinstitute/sting/gatk/filters/MappingQualityZeroFilter.java similarity index 95% rename from public/java/src/org/broadinstitute/sting/gatk/filters/MappingQualityZeroReadFilter.java rename to public/java/src/org/broadinstitute/sting/gatk/filters/MappingQualityZeroFilter.java index e49d4117c..57db8419c 100644 --- a/public/java/src/org/broadinstitute/sting/gatk/filters/MappingQualityZeroReadFilter.java +++ b/public/java/src/org/broadinstitute/sting/gatk/filters/MappingQualityZeroFilter.java @@ -33,7 +33,7 @@ import net.sf.samtools.SAMRecord; * @version 0.1 */ -public class MappingQualityZeroReadFilter extends ReadFilter { +public class MappingQualityZeroFilter extends ReadFilter { public boolean filterOut(SAMRecord rec) { return (rec.getMappingQuality() == 0); } diff --git a/public/java/src/org/broadinstitute/sting/gatk/filters/NotPrimaryAlignmentReadFilter.java b/public/java/src/org/broadinstitute/sting/gatk/filters/NotPrimaryAlignmentFilter.java similarity index 95% rename from public/java/src/org/broadinstitute/sting/gatk/filters/NotPrimaryAlignmentReadFilter.java rename to public/java/src/org/broadinstitute/sting/gatk/filters/NotPrimaryAlignmentFilter.java index 31c2144ce..50cd30f71 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/filters/NotPrimaryAlignmentReadFilter.java +++ b/public/java/src/org/broadinstitute/sting/gatk/filters/NotPrimaryAlignmentFilter.java @@ -34,7 +34,7 @@ import net.sf.samtools.SAMRecord; * Filter out duplicate reads. */ -public class NotPrimaryAlignmentReadFilter extends ReadFilter { +public class NotPrimaryAlignmentFilter extends ReadFilter { public boolean filterOut( final SAMRecord read ) { return read.getNotPrimaryAlignmentFlag(); } diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/DuplicateWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/DuplicateWalker.java index 4bfedb672..e2db1dc52 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/DuplicateWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/DuplicateWalker.java @@ -2,7 +2,7 @@ package org.broadinstitute.sting.gatk.walkers; import net.sf.samtools.SAMRecord; import org.broadinstitute.sting.gatk.contexts.AlignmentContext; -import org.broadinstitute.sting.gatk.filters.NotPrimaryAlignmentReadFilter; +import org.broadinstitute.sting.gatk.filters.NotPrimaryAlignmentFilter; import org.broadinstitute.sting.gatk.filters.UnmappedReadFilter; import org.broadinstitute.sting.utils.GenomeLoc; @@ -17,7 +17,7 @@ import java.util.Set; * To change this template use File | Settings | File Templates. */ @Requires({DataSource.READS,DataSource.REFERENCE}) -@ReadFilters({UnmappedReadFilter.class,NotPrimaryAlignmentReadFilter.class}) +@ReadFilters({UnmappedReadFilter.class,NotPrimaryAlignmentFilter.class}) public abstract class DuplicateWalker extends Walker { // Do we actually want to operate on the context? public boolean filter(GenomeLoc loc, AlignmentContext context, Set> readSets ) { diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/LocusWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/LocusWalker.java index b0b2687f4..8152f74c2 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/LocusWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/LocusWalker.java @@ -3,8 +3,8 @@ package org.broadinstitute.sting.gatk.walkers; import org.broadinstitute.sting.gatk.contexts.AlignmentContext; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.filters.DuplicateReadFilter; -import org.broadinstitute.sting.gatk.filters.FailsVendorQualityCheckReadFilter; -import org.broadinstitute.sting.gatk.filters.NotPrimaryAlignmentReadFilter; +import org.broadinstitute.sting.gatk.filters.FailsVendorQualityCheckFilter; +import org.broadinstitute.sting.gatk.filters.NotPrimaryAlignmentFilter; import org.broadinstitute.sting.gatk.filters.UnmappedReadFilter; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; @@ -18,7 +18,7 @@ import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; @By(DataSource.READS) @Requires({DataSource.READS,DataSource.REFERENCE, DataSource.REFERENCE_BASES}) @PartitionBy(PartitionType.INTERVAL) -@ReadFilters({UnmappedReadFilter.class,NotPrimaryAlignmentReadFilter.class,DuplicateReadFilter.class,FailsVendorQualityCheckReadFilter.class}) +@ReadFilters({UnmappedReadFilter.class,NotPrimaryAlignmentFilter.class,DuplicateReadFilter.class,FailsVendorQualityCheckFilter.class}) public abstract class LocusWalker extends Walker { // Do we actually want to operate on the context? public boolean filter(RefMetaDataTracker tracker, ReferenceContext ref, AlignmentContext context) { 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 8d2101d8f..d5dbdedd6 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 @@ -31,7 +31,7 @@ import org.broadinstitute.sting.gatk.arguments.DbsnpArgumentCollection; import org.broadinstitute.sting.gatk.contexts.AlignmentContext; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.filters.BadMateFilter; -import org.broadinstitute.sting.gatk.filters.MappingQualityUnavailableReadFilter; +import org.broadinstitute.sting.gatk.filters.MappingQualityUnavailableFilter; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; import org.broadinstitute.sting.gatk.walkers.*; import org.broadinstitute.sting.gatk.walkers.annotator.VariantAnnotatorEngine; @@ -111,7 +111,7 @@ import java.util.*; */ @BAQMode(QualityMode = BAQ.QualityMode.ADD_TAG, ApplicationTime = BAQ.ApplicationTime.ON_INPUT) -@ReadFilters( {BadMateFilter.class, MappingQualityUnavailableReadFilter.class} ) +@ReadFilters( {BadMateFilter.class, MappingQualityUnavailableFilter.class} ) @Reference(window=@Window(start=-200,stop=200)) @By(DataSource.REFERENCE) @Downsample(by=DownsampleType.BY_SAMPLE, toCoverage=250) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/RealignerTargetCreator.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/RealignerTargetCreator.java index 145d0327c..e66598b44 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/RealignerTargetCreator.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/RealignerTargetCreator.java @@ -33,7 +33,7 @@ import org.broadinstitute.sting.gatk.contexts.AlignmentContext; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.filters.BadCigarFilter; import org.broadinstitute.sting.gatk.filters.BadMateFilter; -import org.broadinstitute.sting.gatk.filters.MappingQualityZeroReadFilter; +import org.broadinstitute.sting.gatk.filters.MappingQualityZeroFilter; import org.broadinstitute.sting.gatk.filters.Platform454Filter; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; import org.broadinstitute.sting.gatk.walkers.*; @@ -98,7 +98,7 @@ import java.util.List; * * @author ebanks */ -@ReadFilters({Platform454Filter.class, MappingQualityZeroReadFilter.class, BadCigarFilter.class}) +@ReadFilters({Platform454Filter.class, MappingQualityZeroFilter.class, BadCigarFilter.class}) @Reference(window=@Window(start=-1,stop=50)) @Allows(value={DataSource.READS, DataSource.REFERENCE}) @By(DataSource.REFERENCE) 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 9f6ac2a91..546bbe1a6 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 @@ -33,7 +33,7 @@ import org.broadinstitute.sting.commandline.Tags; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.datasources.reads.SAMReaderID; import org.broadinstitute.sting.gatk.datasources.reference.ReferenceDataSource; -import org.broadinstitute.sting.gatk.filters.MappingQualityZeroReadFilter; +import org.broadinstitute.sting.gatk.filters.MappingQualityZeroFilter; import org.broadinstitute.sting.gatk.filters.Platform454Filter; import org.broadinstitute.sting.gatk.filters.PlatformUnitFilter; import org.broadinstitute.sting.gatk.filters.PlatformUnitFilterHelper; @@ -78,7 +78,7 @@ import java.util.*; * 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. */ -@ReadFilters({Platform454Filter.class, MappingQualityZeroReadFilter.class, PlatformUnitFilter.class}) +@ReadFilters({Platform454Filter.class, MappingQualityZeroFilter.class, PlatformUnitFilter.class}) public class SomaticIndelDetectorWalker extends ReadWalker { // @Output // PrintStream out; diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/phasing/ReadBackedPhasingWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/phasing/ReadBackedPhasingWalker.java index 34c7912d9..17a6e20f1 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/phasing/ReadBackedPhasingWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/phasing/ReadBackedPhasingWalker.java @@ -31,7 +31,7 @@ import org.broadinstitute.sting.gatk.arguments.StandardVariantContextInputArgume import org.broadinstitute.sting.gatk.contexts.AlignmentContext; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.datasources.sample.Sample; -import org.broadinstitute.sting.gatk.filters.MappingQualityZeroReadFilter; +import org.broadinstitute.sting.gatk.filters.MappingQualityZeroFilter; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; import org.broadinstitute.sting.gatk.walkers.*; import org.broadinstitute.sting.utils.BaseUtils; @@ -91,7 +91,7 @@ import static org.broadinstitute.sting.utils.codecs.vcf.VCFUtils.getVCFHeadersFr @By(DataSource.READS) // Filter out all reads with zero mapping quality -@ReadFilters({MappingQualityZeroReadFilter.class}) +@ReadFilters({MappingQualityZeroFilter.class}) public class ReadBackedPhasingWalker extends RodWalker { private static final boolean DEBUG = false; 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 838842869..98c8950e3 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 @@ -29,8 +29,8 @@ import org.broad.tribble.Feature; import org.broadinstitute.sting.commandline.*; import org.broadinstitute.sting.gatk.contexts.AlignmentContext; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; -import org.broadinstitute.sting.gatk.filters.MappingQualityUnavailableReadFilter; -import org.broadinstitute.sting.gatk.filters.MappingQualityZeroReadFilter; +import org.broadinstitute.sting.gatk.filters.MappingQualityUnavailableFilter; +import org.broadinstitute.sting.gatk.filters.MappingQualityZeroFilter; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; import org.broadinstitute.sting.gatk.walkers.*; import org.broadinstitute.sting.utils.BaseUtils; @@ -97,7 +97,7 @@ import java.util.Map; @BAQMode(ApplicationTime = BAQ.ApplicationTime.FORBIDDEN) @By( DataSource.READS ) // Only look at covered loci, not every loci of the reference file -@ReadFilters( {MappingQualityZeroReadFilter.class, MappingQualityUnavailableReadFilter.class} ) // Filter out all reads with zero or unavailable mapping quality +@ReadFilters( {MappingQualityZeroFilter.class, MappingQualityUnavailableFilter.class} ) // Filter out all reads with zero or unavailable mapping quality @Requires( {DataSource.READS, DataSource.REFERENCE, DataSource.REFERENCE_BASES} ) // This walker requires both -I input.bam and -R reference.fasta @PartitionBy(PartitionType.LOCUS) public class CountCovariatesWalker extends LocusWalker implements TreeReducible { From 5fbdf968f7ebf396e74a5349ec3f66f472d5c070 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Thu, 18 Aug 2011 22:20:14 -0400 Subject: [PATCH 093/138] ArgumentSource no longer comparable. Arguments sorted by GATKDoclet --- .../sting/commandline/ArgumentSource.java | 7 +--- .../help/GenericDocumentationHandler.java | 42 +++++++++++++++---- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/commandline/ArgumentSource.java b/public/java/src/org/broadinstitute/sting/commandline/ArgumentSource.java index 3e149b0c6..8ec0d650a 100644 --- a/public/java/src/org/broadinstitute/sting/commandline/ArgumentSource.java +++ b/public/java/src/org/broadinstitute/sting/commandline/ArgumentSource.java @@ -39,7 +39,7 @@ import java.util.List; * @author mhanna * @version 0.1 */ -public class ArgumentSource implements Comparable { +public class ArgumentSource { /** * Field into which to inject command-line arguments. */ @@ -216,9 +216,4 @@ public class ArgumentSource implements Comparable { public String toString() { return field.getDeclaringClass().getSimpleName() + ": " + field.getName(); } - - @Override - public int compareTo(final ArgumentSource argumentSource) { - return field.getName().toLowerCase().compareTo(argumentSource.field.getName().toLowerCase()); - } } 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 ff57bb744..d7add9af0 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/GenericDocumentationHandler.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/GenericDocumentationHandler.java @@ -111,16 +111,16 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { // attempt to instantiate the class Object instance = makeInstanceIfPossible(toProcess.clazz); - Map> args = new HashMap>(); + Map>> args = new HashMap>>(); root.put("arguments", args); - args.put("all", new ArrayList()); - args.put("required", new ArrayList()); - args.put("optional", new ArrayList()); - args.put("advanced", new ArrayList()); - args.put("hidden", new ArrayList()); - args.put("depreciated", new ArrayList()); + args.put("all", new ArrayList>()); + args.put("required", new ArrayList>()); + args.put("optional", new ArrayList>()); + args.put("advanced", new ArrayList>()); + args.put("hidden", new ArrayList>()); + args.put("depreciated", new ArrayList>()); try { - for ( ArgumentSource argumentSource : new TreeSet(parsingEngine.extractArgumentSources(HelpUtils.getClassForDoc(classdoc))) ) { + for ( ArgumentSource argumentSource : parsingEngine.extractArgumentSources(HelpUtils.getClassForDoc(classdoc)) ) { ArgumentDefinition argDef = argumentSource.createArgumentDefinitions().get(0); FieldDoc fieldDoc = getFieldDoc(classdoc, argumentSource.field.getName()); Map argBindings = docForArgument(fieldDoc, argumentSource, argDef); // todo -- why can you have multiple ones? @@ -155,11 +155,37 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { logger.debug(String.format("Skipping hidden feature %s", argumentSource)); } } + + // sort the arguments + for (Map.Entry>> entry : args.entrySet()) { + entry.setValue(sortArguments(entry.getValue())); + } } catch ( ClassNotFoundException e ) { throw new RuntimeException(e); } } + private List> sortArguments(List> unsorted) { + Collections.sort(unsorted, new CompareArgumentsByName()); + return unsorted; + } + + private class CompareArgumentsByName implements Comparator> { + public int compare(Map x, Map y) { + return elt(x).compareTo(elt(y)); + } + + private String elt(Map m) { + String v = m.get("name").toString().toLowerCase(); + if ( v.startsWith("--") ) + return v.substring(2); + else if ( v.startsWith("-") ) + return v.substring(1); + else + throw new RuntimeException("Expect to see arguments beginning with at least one -, but found " + v); + } + } + private Object getFieldValue(Class c, Object instance, String fieldName) { Field field = JVMUtils.findField(c, fieldName); if ( field != null ) { From 40e67cff1b802e0ca0fc6b2237ff96a630c35466 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Thu, 18 Aug 2011 22:27:34 -0400 Subject: [PATCH 094/138] I like the @Advanced annotation --- .../sting/gatk/walkers/indels/IndelRealigner.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java index d766ae8bd..5c091c641 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/indels/IndelRealigner.java @@ -178,6 +178,7 @@ public class IndelRealigner extends ReadWalker { * will only proceed with the realignment (even above the given threshold) if it minimizes entropy among the reads (and doesn't simply * push the mismatch column to another position). This parameter is just a heuristic and should be adjusted based on your particular data set. */ + @Advanced @Argument(fullName="entropyThreshold", shortName="entropy", doc="percentage of mismatches at a locus to be considered having high entropy", required=false) protected double MISMATCH_THRESHOLD = 0.15; @@ -185,30 +186,35 @@ public class IndelRealigner extends ReadWalker { * For expert users only! To minimize memory consumption you can lower this number (but then the tool may skip realignment on regions with too much coverage; * and if the number is too low, it may generate errors during realignment). Just make sure to give Java enough memory! 4Gb should be enough with the default value. */ + @Advanced @Argument(fullName="maxReadsInMemory", shortName="maxInMemory", doc="max reads allowed to be kept in memory at a time by the SAMFileWriter", required=false) protected int MAX_RECORDS_IN_MEMORY = 150000; /** * For expert users only! */ + @Advanced @Argument(fullName="maxIsizeForMovement", shortName="maxIsize", doc="maximum insert size of read pairs that we attempt to realign", required=false) protected int MAX_ISIZE_FOR_MOVEMENT = 3000; /** * For expert users only! */ + @Advanced @Argument(fullName="maxPositionalMoveAllowed", shortName="maxPosMove", doc="maximum positional move in basepairs that a read can be adjusted during realignment", required=false) protected int MAX_POS_MOVE_ALLOWED = 200; /** * For expert users only! If you need to find the optimal solution regardless of running time, use a higher number. */ + @Advanced @Argument(fullName="maxConsensuses", shortName="maxConsensuses", doc="max alternate consensuses to try (necessary to improve performance in deep coverage)", required=false) protected int MAX_CONSENSUSES = 30; /** * For expert users only! If you need to find the optimal solution regardless of running time, use a higher number. */ + @Advanced @Argument(fullName="maxReadsForConsensuses", shortName="greedy", doc="max reads used for finding the alternate consensuses (necessary to improve performance in deep coverage)", required=false) protected int MAX_READS_FOR_CONSENSUSES = 120; @@ -216,9 +222,11 @@ public class IndelRealigner extends ReadWalker { * For expert users only! If this value is exceeded at a given interval, realignment is not attempted and the reads are passed to the output file(s) as-is. * If you need to allow more reads (e.g. with very deep coverage) regardless of memory, use a higher number. */ + @Advanced @Argument(fullName="maxReadsForRealignment", shortName="maxReads", doc="max reads allowed at an interval for realignment", required=false) protected int MAX_READS = 20000; + @Advanced @Argument(fullName="noOriginalAlignmentTags", shortName="noTags", required=false, doc="Don't output the original cigar or alignment start tags for each realigned read in the output bam") protected boolean NO_ORIGINAL_ALIGNMENT_TAGS = false; @@ -226,6 +234,7 @@ public class IndelRealigner extends ReadWalker { * For expert users only! This tool assumes that the target interval list 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 Realigner to first sort it in memory. */ + @Advanced @Argument(fullName="targetIntervalsAreNotSorted", shortName="targetNotSorted", required=false, doc="The target intervals are not sorted") protected boolean TARGET_NOT_SORTED = false; From a5e279d697e481600d966211f4c97a16cb973f39 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 19 Aug 2011 09:05:11 -0400 Subject: [PATCH 095/138] Dynamic typing of vcf.gz files -- CombineVariantsIntegrationTests now use dynamic typing of vcf.gz files -- FeatureManagerUnitTests tests for correctness. --- .../tracks/FeatureManagerUnitTest.java | 7 ++-- .../CombineVariantsIntegrationTest.java | 34 +++++++++---------- 2 files changed, 22 insertions(+), 19 deletions(-) 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 90262b9c1..bae8e99ed 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 @@ -54,7 +54,8 @@ import java.util.*; public class FeatureManagerUnitTest extends BaseTest { private static final File RANDOM_FILE = new File(validationDataLocation + "exampleGATKReport.eval"); private static final File VCF3_FILE = new File(validationDataLocation + "vcfexample3.vcf"); - private static final File VCF4_FILE = new File(validationDataLocation + "vcf4.1.example.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 FeatureManager manager; private GenomeLocParser genomeLocParser; @@ -98,7 +99,8 @@ public class FeatureManagerUnitTest extends BaseTest { } public String toString() { - return String.format("FMTest name=%s codec=%s feature=%s file=%s", name, codec, feature, associatedFile); + return String.format("FMTest name=%s codec=%s feature=%s file=%s", + name, codec.getSimpleName(), feature.getSimpleName(), associatedFile); } } @@ -106,6 +108,7 @@ public class FeatureManagerUnitTest extends BaseTest { public Object[][] createTests() { 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(TableFeature.class, BedTableCodec.class, "bedtable", null); return FMTest.getTests(FMTest.class); } 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 4abf0a102..3267173a7 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 @@ -38,21 +38,21 @@ public class CombineVariantsIntegrationTest extends WalkerTest { return "-T CombineVariants -NO_HEADER -L 1:1-50,000,000 -o %s -R " + b36KGReference + args; } - public void test1InOut(String file, String md5, boolean vcf3) { - test1InOut(file, md5, "", vcf3); + public void test1InOut(String file, String md5) { + test1InOut(file, md5, ""); } - public void test1InOut(String file, String md5, String args, boolean vcf3) { + public void test1InOut(String file, String md5, String args) { WalkerTestSpec spec = new WalkerTestSpec( - baseTestString(" -priority v1 -V:v1,VCF" + (vcf3 ? "3 " : " ") + validationDataLocation + file + args), + baseTestString(" -priority v1 -V:v1 " + validationDataLocation + file + args), 1, Arrays.asList(md5)); executeTest("testInOut1--" + file, spec); } - public void combine2(String file1, String file2, String args, String md5, boolean vcf3) { + public void combine2(String file1, String file2, String args, String md5) { WalkerTestSpec spec = new WalkerTestSpec( - baseTestString(" -priority v1,v2 -V:v1,VCF" + (vcf3 ? "3 " : " ") + validationDataLocation + file1 + " -V:v2,VCF" + (vcf3 ? "3 " : " ") + validationDataLocation + file2 + args), + baseTestString(" -priority v1,v2 -V:v1 " + validationDataLocation + file1 + " -V:v2 "+ validationDataLocation + file2 + args), 1, Arrays.asList(md5)); executeTest("combine2 1:" + new File(file1).getName() + " 2:" + new File(file2).getName(), spec); @@ -78,23 +78,23 @@ public class CombineVariantsIntegrationTest extends WalkerTest { executeTest("combine PLs 1:" + new File(file1).getName() + " 2:" + new File(file2).getName(), spec); } - @Test public void test1SNP() { test1InOut("pilot2.snps.vcf4.genotypes.vcf", "c608b9fc1e36dba6cebb4f259883f9f0", true); } - @Test public void test2SNP() { test1InOut("pilot2.snps.vcf4.genotypes.vcf", "20caad94411d6ab48153b214de916df8", " -setKey foo", true); } - @Test public void test3SNP() { test1InOut("pilot2.snps.vcf4.genotypes.vcf", "004f3065cb1bc2ce2f9afd695caf0b48", " -setKey null", true); } - @Test public void testOfficialCEUPilotCalls() { test1InOut("CEU.trio.2010_03.genotypes.vcf.gz", "c9c901ff9ef2a982624b203a8086dff0", false); } // official project VCF files in tabix format + @Test public void test1SNP() { test1InOut("pilot2.snps.vcf4.genotypes.vcf", "c608b9fc1e36dba6cebb4f259883f9f0"); } + @Test public void test2SNP() { test1InOut("pilot2.snps.vcf4.genotypes.vcf", "20caad94411d6ab48153b214de916df8", " -setKey foo"); } + @Test public void test3SNP() { test1InOut("pilot2.snps.vcf4.genotypes.vcf", "004f3065cb1bc2ce2f9afd695caf0b48", " -setKey null"); } + @Test public void testOfficialCEUPilotCalls() { test1InOut("CEU.trio.2010_03.genotypes.vcf.gz", "c9c901ff9ef2a982624b203a8086dff0"); } // official project VCF files in tabix format - @Test public void test1Indel1() { test1InOut("CEU.dindel.vcf4.trio.2010_06.indel.genotypes.vcf", "7593be578d4274d672fc22fced38012b", false); } - @Test public void test1Indel2() { test1InOut("CEU.dindel.vcf4.low_coverage.2010_06.indel.genotypes.vcf", "1cd467863c4e948fadd970681552d57e", false); } + @Test public void test1Indel1() { test1InOut("CEU.dindel.vcf4.trio.2010_06.indel.genotypes.vcf", "7593be578d4274d672fc22fced38012b"); } + @Test public void test1Indel2() { test1InOut("CEU.dindel.vcf4.low_coverage.2010_06.indel.genotypes.vcf", "1cd467863c4e948fadd970681552d57e"); } @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", false); } // 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", false); } // 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", false); } + @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 combineSNPsAndIndels() { combine2("CEU.trio.2010_03.genotypes.vcf.gz", "CEU.dindel.vcf4.low_coverage.2010_06.indel.genotypes.vcf", "", "e144b6283765494bfe8189ac59965083", false); } + @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", true); } + @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 omniHM3Intersect() { combineSites(" -filteredRecordsMergeType KEEP_IF_ALL_UNFILTERED", "6a34b5d743efda8b2f3b639f3a2f5de8"); } From bc902e8421ea948d727ad5fc6656c9d00eb96422 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 19 Aug 2011 09:05:39 -0400 Subject: [PATCH 096/138] GZIP VCF for testing --- public/testdata/HiSeq.10000.vcf.gz | Bin 0 -> 484811 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 public/testdata/HiSeq.10000.vcf.gz diff --git a/public/testdata/HiSeq.10000.vcf.gz b/public/testdata/HiSeq.10000.vcf.gz new file mode 100644 index 0000000000000000000000000000000000000000..15e91010c67ae3eafe6d4f3c014c1d28b9382335 GIT binary patch literal 484811 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�Z00j@bFwL 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%0Ag&Ll) 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%0DLdIWXgI3V6N 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 jS; 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 Ofd3D@m Date: Fri, 19 Aug 2011 09:57:21 -0400 Subject: [PATCH 098/138] GATKDoc descriptions for all standard codecs, or TODO for their owners -- Also added vcf.gz support in the VCF codec. This wasn't committed in the last round, because it was missed by the parallel documentation effort. --- build.xml | 2 +- .../utils/codecs/beagle/BeagleCodec.java | 23 +++++++++++ .../utils/codecs/hapmap/RawHapMapCodec.java | 41 ++++++++++++++++--- .../utils/codecs/refseq/RefSeqCodec.java | 18 +++++++- .../codecs/sampileup/SAMPileupCodec.java | 39 ++++++++++++++++-- .../utils/codecs/samread/SAMReadCodec.java | 17 +++++++- .../utils/codecs/snpEff/SnpEffCodec.java | 11 ++++- .../utils/codecs/table/BedTableCodec.java | 21 ++++++---- .../sting/utils/codecs/table/TableCodec.java | 37 ++++++++++++++--- .../utils/codecs/vcf/AbstractVCFCodec.java | 21 +++++++--- .../sting/utils/codecs/vcf/VCF3Codec.java | 16 +++++++- .../sting/utils/codecs/vcf/VCFCodec.java | 40 ++++++++++++++++-- 12 files changed, 249 insertions(+), 37 deletions(-) diff --git a/build.xml b/build.xml index 85955d774..d8c38738a 100644 --- a/build.xml +++ b/build.xml @@ -49,7 +49,7 @@ - + diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/beagle/BeagleCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/beagle/BeagleCodec.java index e328c9286..413848543 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/beagle/BeagleCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/beagle/BeagleCodec.java @@ -40,6 +40,29 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.regex.Pattern; +/** + * TODO GUILLERMO DEL ANGEL + * + *

      + * Codec Description + *

      + * + *

      + * See also: @see VCF specification
      + *

      + + *

      + * + *

      File format example

      + *
      + *     line 1
      + *     line 2
      + *     line 3
      + * 
      + * + * @author Mark DePristo + * @since 2010 + */ public class BeagleCodec implements ReferenceDependentFeatureCodec { private String[] header; public enum BeagleReaderType {PROBLIKELIHOOD, GENOTYPES, R2}; diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/hapmap/RawHapMapCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/hapmap/RawHapMapCodec.java index 535f607a1..a80e05d59 100644 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/hapmap/RawHapMapCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/hapmap/RawHapMapCodec.java @@ -33,12 +33,43 @@ import java.io.IOException; import java.util.Arrays; /** - * a codec for the file types produced by the HapMap consortium, available on their website: - * http://hapmap.ncbi.nlm.nih.gov/downloads/genotypes/ + * A codec for the file types produced by the HapMap consortium * - * The format includes eleven standard fields, plus genotypes for each of the samples included - * in the file - * + *

      + * The format includes eleven standard fields, plus genotypes for each of the samples included + * in the file: + * + *

      + *     Col1: refSNP rs# identifier at the time of release (NB might merge with another rs# in the future)
      + *     Col2: SNP alleles according to dbSNP
      + *     Col3: chromosome that SNP maps to
      + *     Col4: chromosome position of SNP, in basepairs on reference sequence
      + *     Col5: strand of reference sequence that SNP maps to
      + *     Col6: version of reference sequence assembly
      + *     Col7: HapMap genotype center that produced the genotypes
      + *     Col8: LSID for HapMap protocol used for genotyping
      + *     Col9: LSID for HapMap assay used for genotyping
      + *     Col10: LSID for panel of individuals genotyped
      + *     Col11: QC-code, currently 'QC+' for all entries (for future use)
      + *     Col12 and on: observed genotypes of samples, one per column, sample identifiers in column headers (Coriell catalog numbers, example: NA10847). Duplicate samples have .dup suffix.
      + * 
      + *

      + * + *

      + * See also: @See HapMap genotypes download + *

      + * + *

      File format example

      + * From genotypes_chr1_ASW_r27_nr.b36_fwd.txt.gz: + *
      + *     rs# alleles chrom pos strand assembly# center protLSID assayLSID panelLSID QCcode NA19625 NA19700 NA19701 NA19702 NA19703 NA19704 NA19705 NA19708 NA19712 NA19711 NA19818 NA19819 NA19828 NA19835 NA19834 NA19836 NA19902 NA19901 NA19900 NA19904 NA19919 NA19908 NA19909 NA19914 NA19915 NA19916 NA19917 NA19918 NA19921 NA20129 NA19713 NA19982 NA19983 NA19714 NA19985 NA20128 NA20126 NA20127 NA20277 NA20276 NA20279 NA20282 NA20281 NA20284 NA20287 NA20288 NA20290 NA20289 NA20291 NA20292 NA20295 NA20294 NA20297 NA20300 NA20301 NA20302 NA20317 NA20319 NA20322 NA20333 NA20332 NA20335 NA20334 NA20337 NA20336 NA20340 NA20341 NA20343 NA20342 NA20344 NA20345 NA20346 NA20347 NA20348 NA20349 NA20350 NA20357 NA20356 NA20358 NA20359 NA20360 NA20363 NA20364
      + *     rs9629043 C/T chr1 554636 + ncbi_b36 broad urn:LSID:affymetrix.hapmap.org:Protocol:GenomeWideSNP_6.0:3 urn:LSID:broad.hapmap.org:Assay:SNP_A-8575115:3 urn:lsid:dcc.hapmap.org:Panel:US_African-30-trios:3 QC+ CC CC CC CC CC CC CC CC CC CC CC CC NN CC CC CC CT CT CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CT CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
      + *     rs28446478 G/T chr1 576058 + ncbi_b36 sanger urn:LSID:illumina.hapmap.org:Protocol:Human_1M_BeadChip:3 urn:LSID:sanger.hapmap.org:Assay:H1Mrs28446478:3 urn:lsid:dcc.hapmap.org:Panel:US_African-30-trios:3 QC+ GT TT GT TT TT TT TT GT GT TT TT TT TT GT GT GT GT TT GT TT GT GT TT GT GT TT TT TT GT GT TT TT TT GT TT GT TT GT GT GT GT GT TT GT TT TT GT GT TT TT TT TT TT TT GT GT GT GT TT TT TT TT GT TT GT TT TT GT TT TT TT GT TT TT TT GT GT TT GT TT GT TT TT
      + *     rs12565286 C/G chr1 711153 + ncbi_b36 broad urn:LSID:affymetrix.hapmap.org:Protocol:GenomeWideSNP_6.0:3 urn:LSID:broad.hapmap.org:Assay:SNP_A-8709646:3 urn:lsid:dcc.hapmap.org:Panel:US_African-30-trios:3 QC+ GG GG GG GG GG GG GG GG CG GG GG GG GG GG GG GG GG GG GG CG GG GG GG GG GG GG GG GG GG GG GG GG GG GG GG GG GG GG GG GG CG GG GG GG GG GG GG GG CG CG GG GG GG GG GG GG GG GG GG CG CG GG GG GG GG GG GG GG GG GG GG CG NN GG GG GG GG GG GG NN GG NN NN
      + * 
      + * + * @author Mark DePristo + * @since 2010 */ public class RawHapMapCodec implements FeatureCodec { // the minimum number of features in the HapMap file line 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 391715c63..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,7 +12,23 @@ import org.broadinstitute.sting.utils.exceptions.UserException; import java.util.ArrayList; /** - * the ref seq codec + * TODO FOR CHRIS HARTL + * + *

      + * Codec Description + *

      + * + *

      + * See also: link to file specification + *

      + * + *

      File format example

      + *

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

      + * + * @author Mark DePristo + * @since 2010 */ public class RefSeqCodec implements ReferenceDependentFeatureCodec { diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/sampileup/SAMPileupCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/sampileup/SAMPileupCodec.java index f4048d37d..f4633b2ce 100644 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/sampileup/SAMPileupCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/sampileup/SAMPileupCodec.java @@ -38,10 +38,43 @@ import java.util.regex.Pattern; import static org.broadinstitute.sting.utils.codecs.sampileup.SAMPileupFeature.VariantType; /** - * A Tribble encoder / decoder for SAM pileup data. + * Decoder for SAM pileup data. For GATK validation purposes only * - * @author mhanna - * @version 0.1 + *

      + * Pileup format is first used by Tony Cox and Zemin Ning at the Sanger Institute. + * It desribes the base-pair information at each chromosomal position. This format + * facilitates SNP/indel calling and brief alignment viewing by eyes. + *

      + *

      + * Each line consists of chromosome, 1-based coordinate, reference base, the + * number of reads covering the site, read bases and base qualities. At the + * read base column, a dot stands for a match to the reference base on the + * forward strand, a comma for a match on the reverse strand, `ACGTN' for a mismatch + * on the forward strand and `acgtn' for a mismatch on the reverse strand. + * A pattern `\+[0-9]+[ACGTNacgtn]+' indicates there is an insertion between + * this reference position and the next reference position. The length of the + * insertion is given by the integer in the pattern, followed by the inserted sequence. + *

      + * + *

      + *
      See also: @see SAMTools project
      + *
      See also: @see Pileup format
      + *

      + * + *

      File format example

      + *
      + *     seq1 272 T 24  ,.$.....,,.,.,...,,,.,..^+. <<<+;<<<<<<<<<<<=<;<;7<&
      + *     seq1 273 T 23  ,.....,,.,.,...,,,.,..A <<<;<<<<<<<<<3<=<<<;<<+
      + *     seq1 274 T 23  ,.$....,,.,.,...,,,.,...    7<7;<;<<<<<<<<<=<;<;<<6
      + *     seq1 275 A 23  ,$....,,.,.,...,,,.,...^l.  <+;9*<<<<<<<<<=<<:;<<<<
      + *     seq1 276 G 22  ...T,,.,.,...,,,.,....  33;+<<7=7<<7<&<<1;<<6<
      + *     seq1 277 T 22  ....,,.,.,.C.,,,.,..G.  +7<;<<<<<<<&<=<<:;<<&<
      + *     seq1 278 G 23  ....,,.,.,...,,,.,....^k.   %38*<<;<7<<7<=<<<;<<<<<
      + *     seq1 279 C 23  A..T,,.,.,...,,,.,..... ;75&<<<<<<<<<=<<<9<<:<<
      + * 
      + * + * @author Matt Hanna + * @since 2009 */ public class SAMPileupCodec implements FeatureCodec { // the number of tokens we expect to parse from a pileup line diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/samread/SAMReadCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/samread/SAMReadCodec.java index f6861e585..d4bdb5aa9 100644 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/samread/SAMReadCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/samread/SAMReadCodec.java @@ -36,8 +36,21 @@ import org.broad.tribble.util.ParsingUtils; /** * Decodes a simple SAM text string. * - * @author mhanna - * @version 0.1 + *

      + * Reads in the SAM text version of a BAM file as a ROD. For testing only + *

      + * + *

      + * See also: @see SAMTools for format specification + *

      + * + *

      File format example

      + *
      + *     SL-XBC:1:10:628:923#0	16	Escherichia_coli_K12	1	37	76M	=	1	0	AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGCTTCTGA	B@>87<;A@?@957:>>@AA@B>@A9AB@B>@A@@@@@A;=AAB@BBBBBCBBBB@>A>:ABB@BAABCB=CA@CB
      + * 
      + * + * @author Matt Hanna + * @since 2009 */ public class SAMReadCodec implements FeatureCodec { /* SL-XBC:1:10:628:923#0 16 Escherichia_coli_K12 1 37 76M = 1 0 AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGCTTCTGA B@>87<;A@?@957:>>@AA@B>@A9AB@B>@A@@@@@A;=AAB@BBBBBCBBBB@>A>:ABB@BAABCB=CA@CB */ diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/snpEff/SnpEffCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/snpEff/SnpEffCodec.java index b5efb49a7..7f3d9e17d 100644 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/snpEff/SnpEffCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/snpEff/SnpEffCodec.java @@ -41,10 +41,11 @@ import java.io.IOException; /** * Codec for decoding the output format of the SnpEff variant effect predictor tool - * (http://snpeff.sourceforge.net/). * + *

      * This format has 23 tab-delimited fields: * + *

        * Chromosome
        * Position
        * Reference
      @@ -68,10 +69,16 @@ import java.io.IOException;
        * Codons Around
        * Amino Acids Around
        * Custom Interval ID
      + * 
      + * Note that we treat all except the Chromosome, Position, and Effect fields as optional. + *

      * - * 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 { diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/table/BedTableCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/table/BedTableCodec.java index 6fe1907e3..fdcc8ed10 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/table/BedTableCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/table/BedTableCodec.java @@ -6,14 +6,19 @@ import org.broadinstitute.sting.gatk.refdata.ReferenceDependentFeatureCodec; import java.util.Arrays; /** - * Created by IntelliJ IDEA. - * User: chartl - * Date: 3/28/11 - * Time: 2:47 PM - * To change this template use File | Settings | File Templates. - */ -/** - * The standard table codec with a slightly different parsing convention (expects loci as contig start stop, not contig:start-stop) + * The standard table codec that expects loci as contig start stop, not contig:start-stop + * + *

      + * The standard table codec with a slightly different parsing convention + * (expects loci as contig start stop, not contig:start-stop) + *

      + * + *

      + * See also: TableCodec + *

      + * + * @author Chris Hartl + * @since 2010 */ public class BedTableCodec extends TableCodec implements ReferenceDependentFeatureCodec { diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/table/TableCodec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/table/TableCodec.java index 2ce7c679e..1919ccbf0 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/table/TableCodec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/table/TableCodec.java @@ -11,13 +11,40 @@ import java.util.ArrayList; import java.util.Arrays; /** - * implementation of a simple table (tab or comma delimited format) input files + * Reads tab deliminated tabular text files + * + *

      + *

        + *
      • Header: must begin with line HEADER or track (for IGV), followed by any number of column names, + * separated by whitespace.
      • + *
      • Comment lines starting with # are ignored
      • + *
      • Each non-header and non-comment line is split into parts by whitespace, + * and these parts are assigned as a map to their corresponding column name in the header. + * Note that the first element (corresponding to the HEADER column) must be a valid genome loc + * such as 1, 1:1 or 1:1-10, which is the position of the Table element on the genome. TableCodec + * requires that there be one value for each column in the header, and no more, on all lines.
      • + *
      + *

      + * + *

      + * + *

      File format example

      + *
      + *     HEADER a b c
      + *     1:1  1   2   3
      + *     1:2  4   5   6
      + *     1:3  7   8   9
      + * 
      + * + * @author Mark DePristo + * @since 2009 */ public class TableCodec implements ReferenceDependentFeatureCodec { - protected String delimiterRegex = "\\s+"; - protected String headerDelimiter = "HEADER"; - protected String igvHeaderDelimiter = "track"; - protected String commentDelimiter = "#"; + final static protected String delimiterRegex = "\\s+"; + final static protected String headerDelimiter = "HEADER"; + final static protected String igvHeaderDelimiter = "track"; + final static protected String commentDelimiter = "#"; + protected ArrayList header = new ArrayList(); /** 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 19f58ddaa..46242c302 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 @@ -14,10 +14,9 @@ import org.broadinstitute.sting.utils.variantcontext.Allele; import org.broadinstitute.sting.utils.variantcontext.Genotype; import org.broadinstitute.sting.utils.variantcontext.VariantContext; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; +import java.io.*; import java.util.*; +import java.util.zip.GZIPInputStream; public abstract class AbstractVCFCodec implements FeatureCodec, NameAwareCodec, VCFParser, SelfScopingFeatureCodec { @@ -623,9 +622,21 @@ public abstract class AbstractVCFCodec implements FeatureCodec, NameAwareCodec, public final static boolean canDecodeFile(final File potentialInput, final String MAGIC_HEADER_LINE) { try { - char[] buff = new char[MAGIC_HEADER_LINE.length()]; - new FileReader(potentialInput).read(buff, 0, MAGIC_HEADER_LINE.length()); + return isVCFStream(new FileInputStream(potentialInput), MAGIC_HEADER_LINE) || + isVCFStream(new GZIPInputStream(new FileInputStream(potentialInput)), MAGIC_HEADER_LINE); + } catch ( FileNotFoundException e ) { + return false; + } catch ( IOException e ) { + return false; + } + } + + 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); } catch ( IOException e ) { return false; diff --git a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCF3Codec.java b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCF3Codec.java index ea16595bb..e5b1a2de5 100755 --- a/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCF3Codec.java +++ b/public/java/src/org/broadinstitute/sting/utils/codecs/vcf/VCF3Codec.java @@ -14,8 +14,20 @@ import java.util.*; /** - * a feature codec for the VCF 3 specification. Our aim is to read in the records and convert to VariantContext as - * quickly as possible, relying on VariantContext to do the validation of any contradictory (or malformed) record parameters. + * A feature codec for the VCF3 specification, to read older VCF files. VCF3 has been + * depreciated in favor of VCF4 (See VCF codec for the latest information) + * + *

      + * Reads historical VCF3 encoded files (1000 Genomes Pilot results, for example) + *

      + * + *

      + * See also: @see VCF specification
      + * See also: @see VCF spec. publication + *

      + * + * @author Mark DePristo + * @since 2010 */ public class VCF3Codec extends AbstractVCFCodec { public final static String VCF3_MAGIC_HEADER = "##fileformat=VCFv3"; 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 55a0eb3f9..fa030ef5f 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 @@ -12,12 +12,46 @@ import java.io.FileReader; import java.io.IOException; import java.util.*; - /** - * a feature codec for the VCF 4 specification. Our aim is to read in the records and convert to VariantContext as - * quickly as possible, relying on VariantContext to do the validation of any contradictory (or malformed) record parameters. + * A feature codec for the VCF 4 specification + * + *

      + * VCF is a text file format (most likely stored in a compressed manner). It contains meta-information lines, a + * header line, and then data lines each containing information about a position in the genome. + *

      + *

      One of the main uses of next-generation sequencing is to discover variation amongst large populations + * of related samples. Recently the format for storing next-generation read alignments has been + * standardised by the SAM/BAM file format specification. This has significantly improved the + * interoperability of next-generation tools for alignment, visualisation, and variant calling. + * We propose the Variant Call Format (VCF) as a standarised format for storing the most prevalent + * types of sequence variation, including SNPs, indels and larger structural variants, together + * with rich annotations. VCF is usually stored in a compressed manner and can be indexed for + * fast data retrieval of variants from a range of positions on the reference genome. + * The format was developed for the 1000 Genomes Project, and has also been adopted by other projects + * such as UK10K, dbSNP, or the NHLBI Exome Project. VCFtools is a software suite that implements + * various utilities for processing VCF files, including validation, merging and comparing, + * and also provides a general Perl and Python API. + * The VCF specification and VCFtools are available from http://vcftools.sourceforge.net.

      + * + *

      + * See also: @see VCF specification
      + * See also: @see VCF spec. publication + *

      + * + *

      File format example

      + *
      + *     ##fileformat=VCFv4.0
      + *     #CHROM  POS     ID      REF     ALT     QUAL    FILTER  INFO    FORMAT  NA12878
      + *     chr1    109     .       A       T       0       PASS  AC=1    GT:AD:DP:GL:GQ  0/1:610,327:308:-316.30,-95.47,-803.03:99
      + *     chr1    147     .       C       A       0       PASS  AC=1    GT:AD:DP:GL:GQ  0/1:294,49:118:-57.87,-34.96,-338.46:99
      + * 
      + * + * @author Mark DePristo + * @since 2010 */ public class VCFCodec extends AbstractVCFCodec { + // Our aim is to read in the records and convert to VariantContext as quickly as possible, relying on VariantContext to do the validation of any contradictory (or malformed) record parameters. + public final static String VCF4_MAGIC_HEADER = "##fileformat=VCFv4"; /** From 0f25167efd3b765c8a40a8f6b90777e5a2eb4874 Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Fri, 19 Aug 2011 11:01:04 -0400 Subject: [PATCH 099/138] minor fix in VariantEval docs --- .../sting/gatk/walkers/varianteval/VariantEvalWalker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 d1fa3f4df..f6d42afb1 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 @@ -95,7 +95,7 @@ public class VariantEvalWalker extends RodWalker implements Tr protected DbsnpArgumentCollection dbsnp = new DbsnpArgumentCollection(); // Help arguments - @Argument(fullName="list", shortName="ls", doc="List the available eval modules and exit") + @Argument(fullName="list", shortName="ls", doc="List the available eval modules and exit", required=false) protected Boolean LIST = false; // Partitioning the data arguments From 4d1fd17a97aa49e48c6163b84ff3ff15725bea66 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 19 Aug 2011 13:13:41 -0400 Subject: [PATCH 101/138] GATKDoclet cleanup and documentation -- Fixed bug in the way ArgumentCollections were handled that lead to failure in handling the dbsnp argument collection. --- .../sting/utils/help/GATKDoclet.java | 26 +- .../help/GenericDocumentationHandler.java | 263 +++++++++++------- 2 files changed, 189 insertions(+), 100 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java b/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java index 5755d2b37..de6ad359e 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java @@ -34,7 +34,10 @@ import org.apache.commons.io.FileUtils; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.broad.tribble.FeatureCodec; +import org.broadinstitute.sting.gatk.CommandLineGATK; +import org.broadinstitute.sting.gatk.walkers.qc.DocumentationTest; import org.broadinstitute.sting.utils.exceptions.ReviewedStingException; +import org.broadinstitute.sting.utils.exceptions.UserException; import java.io.*; import java.util.*; @@ -48,6 +51,7 @@ public class GATKDoclet { final protected static Logger logger = Logger.getLogger(GATKDoclet.class); protected static String buildTimestamp = null, absoluteVersion = null; protected static boolean showHiddenFeatures = false; + protected static boolean testOnly = false; RootDoc rootDoc; @@ -75,6 +79,8 @@ public class GATKDoclet { absoluteVersion = options[1]; if (options[0].equals("-include-hidden")) showHiddenFeatures = true; + if (options[0].equals("-test")) + testOnly = true; } GATKDoclet doclet = new GATKDoclet(); @@ -88,16 +94,26 @@ public class GATKDoclet { * @return Number of potential parameters; 0 if not supported. */ public static int optionLength(String option) { - if(option.equals("-build-timestamp") || option.equals("-absolute-version") || option.equals("-include-hidden")) { + if(option.equals("-build-timestamp") || + option.equals("-absolute-version") || + option.equals("-include-hidden")) { return 2; - } - return 0; + } else if ( option.equals("-test") ) + return 1; + else + return 0; } public boolean showHiddenFeatures() { return showHiddenFeatures; } + public static boolean testOnly() { + return testOnly; + } + + private static final List> testOnlyKeepers = Arrays.asList( + DocumentationTest.class, CommandLineGATK.class, UserException.class); public Set workUnits() { TreeSet m = new TreeSet(); @@ -105,6 +121,10 @@ public class GATKDoclet { //logger.debug("Considering " + doc); Class clazz = getClassForClassDoc(doc); + // don't add anything that's not DocumentationTest if we are in test mode + if ( clazz != null && testOnly && ! testOnlyKeepers.contains(clazz) ) + continue; + //if ( clazz != null && clazz.getName().equals("org.broadinstitute.sting.gatk.walkers.annotator.AlleleBalance")) // logger.debug("foo"); 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 d7add9af0..08e430c8a 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/GenericDocumentationHandler.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/GenericDocumentationHandler.java @@ -24,6 +24,7 @@ package org.broadinstitute.sting.utils.help; +import com.google.java.contract.Ensures; import com.google.java.contract.Requires; import com.sun.javadoc.ClassDoc; import com.sun.javadoc.FieldDoc; @@ -31,8 +32,10 @@ import com.sun.javadoc.RootDoc; import com.sun.javadoc.Tag; import org.apache.log4j.Logger; import org.broad.tribble.Feature; +import org.broad.tribble.bed.FullBEDFeature; import org.broadinstitute.sting.commandline.*; import org.broadinstitute.sting.gatk.CommandLineGATK; +import org.broadinstitute.sting.gatk.arguments.DbsnpArgumentCollection; import org.broadinstitute.sting.gatk.refdata.tracks.FeatureManager; import org.broadinstitute.sting.utils.Utils; import org.broadinstitute.sting.utils.classloader.JVMUtils; @@ -49,14 +52,18 @@ import java.util.*; */ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { private static Logger logger = Logger.getLogger(GenericDocumentationHandler.class); - GATKDocWorkUnit toProcess; - ClassDoc classdoc; - Set all; - RootDoc rootDoc; + + /** The Class we are documenting */ + private GATKDocWorkUnit toProcess; + + /** The set of all classes we are documenting, for cross-referencing */ + private Set all; + + /** The JavaDoc root */ + private RootDoc rootDoc; @Override public boolean includeInDocs(ClassDoc doc) { -// return true; try { Class type = HelpUtils.getClassForDoc(doc); return JVMUtils.isConcrete(type); @@ -76,7 +83,6 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { this.rootDoc = rootDoc; this.toProcess = toProcessArg; this.all = allArg; - this.classdoc = toProcess.classDoc; //System.out.printf("%s class %s%n", toProcess.group, toProcess.classDoc); Map root = new HashMap(); @@ -88,71 +94,76 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { toProcess.setHandlerContent((String)root.get("summary"), root); } + /** + * Add high-level summary information about toProcess to root, such as its + * name, summary, description, version, etc. + * + * @param root + */ protected void addHighLevelBindings(Map root) { - root.put("name", classdoc.name()); + root.put("name", toProcess.classDoc.name()); // Extract overrides from the doc tags. StringBuilder summaryBuilder = new StringBuilder(); - for(Tag tag: classdoc.firstSentenceTags()) + for(Tag tag: toProcess.classDoc.firstSentenceTags()) summaryBuilder.append(tag.text()); root.put("summary", summaryBuilder.toString()); - root.put("description", classdoc.commentText().substring(summaryBuilder.toString().length())); + root.put("description", toProcess.classDoc.commentText().substring(summaryBuilder.toString().length())); root.put("timestamp", toProcess.buildTimestamp); root.put("version", toProcess.absoluteVersion); - for(Tag tag: classdoc.tags()) { + for(Tag tag: toProcess.classDoc.tags()) { root.put(tag.name(), tag.text()); } } + /** + * Add bindings describing related GATK capabilites to toProcess + * @param root + */ + protected void addRelatedBindings(Map root) { + List> extraDocsData = new ArrayList>(); + + // add in all of the explicitly related items + for ( final Class extraDocClass : toProcess.annotation.extraDocs() ) { + final GATKDocWorkUnit otherUnit = GATKDoclet.findWorkUnitForClass(extraDocClass, all); + if ( otherUnit == null ) + throw new ReviewedStingException("Requested extraDocs for class without any documentation: " + extraDocClass); + extraDocsData.add( + new HashMap(){{ + put("filename", otherUnit.filename); + put("name", otherUnit.name);}}); + + } + root.put("extradocs", extraDocsData); + } + + /** + * Add information about all of the arguments available to toProcess to root + * + * @param root + */ protected void addArgumentBindings(Map root) { ParsingEngine parsingEngine = createStandardGATKParsingEngine(); - // attempt to instantiate the class - Object instance = makeInstanceIfPossible(toProcess.clazz); - - Map>> args = new HashMap>>(); + Map>> args = createArgumentMap(); root.put("arguments", args); - args.put("all", new ArrayList>()); - args.put("required", new ArrayList>()); - args.put("optional", new ArrayList>()); - args.put("advanced", new ArrayList>()); - args.put("hidden", new ArrayList>()); - args.put("depreciated", new ArrayList>()); try { - for ( ArgumentSource argumentSource : parsingEngine.extractArgumentSources(HelpUtils.getClassForDoc(classdoc)) ) { + // loop over all of the arguments according to the parsing engine + for ( final ArgumentSource argumentSource : parsingEngine.extractArgumentSources(HelpUtils.getClassForDoc(toProcess.classDoc)) ) { + // todo -- why can you have multiple ones? ArgumentDefinition argDef = argumentSource.createArgumentDefinitions().get(0); - FieldDoc fieldDoc = getFieldDoc(classdoc, argumentSource.field.getName()); - Map argBindings = docForArgument(fieldDoc, argumentSource, argDef); // todo -- why can you have multiple ones? + FieldDoc fieldDoc = getFieldDoc(toProcess.classDoc, argumentSource.field.getName()); + Map argBindings = docForArgument(fieldDoc, argumentSource, argDef); if ( ! argumentSource.isHidden() || getDoclet().showHiddenFeatures() ) { - logger.debug(String.format("Processing %s", argumentSource)); - String kind = "optional"; - if ( argumentSource.isRequired() ) kind = "required"; - else if ( argumentSource.isAdvanced() ) kind = "advanced"; - else if ( argumentSource.isHidden() ) kind = "hidden"; - else if ( argumentSource.isDeprecated() ) kind = "depreciated"; + final String kind = docKindOfArg(argumentSource); - // get the value of the field - if ( instance != null ) { - Object value = getFieldValue(toProcess.clazz, instance, fieldDoc.name()); - - if ( value == null && argumentSource.createsTypeDefault() ) { - // handle the case where there's an implicit default - try { - value = argumentSource.typeDefaultDocString(); - } catch (ReviewedStingException e) { - ; // failed to create type default, don't worry about it - } - } - - if ( value != null ) - argBindings.put("defaultValue", prettyPrintValueString(value)); - } + final Object value = argumentValue(toProcess.clazz, argumentSource); + if ( value != null ) + argBindings.put("defaultValue", prettyPrintValueString(value)); args.get(kind).add(argBindings); args.get("all").add(argBindings); - } else { - logger.debug(String.format("Skipping hidden feature %s", argumentSource)); } } @@ -165,11 +176,78 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { } } + /** + * Return the argument kind (required, advanced, hidden, etc) of this argumentSource + * @param argumentSource + * @return + */ + @Requires("argumentSource != null") + @Ensures("result != null") + private String docKindOfArg(ArgumentSource argumentSource) { + if ( argumentSource.isRequired() ) return "required"; + else if ( argumentSource.isAdvanced() ) return "advanced"; + else if ( argumentSource.isHidden() ) return "hidden"; + else if ( argumentSource.isDeprecated() ) return "depreciated"; + else return "optional"; + } + + /** + * Attempts to determine the value of argumentSource in an instantiated version of c + * @param c + * @param argumentSource + * @return value of argumentSource, or null if this isn't possible + */ + @Requires({"c != null", "argumentSource != null"}) + private Object argumentValue(Class c, ArgumentSource argumentSource) { + // get the value of the field + // attempt to instantiate the class + final Object instance = makeInstanceIfPossible(toProcess.clazz); + if ( instance != null ) { + final Object value = getFieldValue(instance, argumentSource.field.getName()); + if ( value != null ) + return value; + + if ( argumentSource.createsTypeDefault() ) { + try { // handle the case where there's an implicit default + return argumentSource.typeDefaultDocString(); + } catch (ReviewedStingException e) { + ; // failed to create type default, don't worry about it + } + } + } + + return null; + } + + /** + * Create the argument map for holding class arguments + * @return + */ + private Map>> createArgumentMap() { + Map>> args = new HashMap>>(); + args.put("all", new ArrayList>()); + args.put("required", new ArrayList>()); + args.put("optional", new ArrayList>()); + args.put("advanced", new ArrayList>()); + args.put("hidden", new ArrayList>()); + args.put("depreciated", new ArrayList>()); + return args; + } + + + /** + * Sorts the individual argument list in unsorted according to CompareArgumentsByName + * @param unsorted + * @return + */ private List> sortArguments(List> unsorted) { Collections.sort(unsorted, new CompareArgumentsByName()); return unsorted; } + /** + * Sort arguments by case-insensitive comparison ignoring the -- and - prefixes + */ private class CompareArgumentsByName implements Comparator> { public int compare(Map x, Map y) { return elt(x).compareTo(elt(y)); @@ -186,25 +264,32 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { } } - private Object getFieldValue(Class c, Object instance, String fieldName) { - Field field = JVMUtils.findField(c, fieldName); - if ( field != null ) { - Object value = JVMUtils.getFieldValue(field, instance); - //System.out.printf("Fetched value of field %s in class %s: %s%n", fieldName, c, value); - return value; - } else { - return findFieldValueInArgumentCollections(c, instance, fieldName); - } - } - - private Object findFieldValueInArgumentCollections(Class c, Object instance, String fieldName) { - for ( Field field : JVMUtils.getAllFields(c) ) { + /** + * Utility function that finds the value of fieldName in any fields of ArgumentCollection fields in + * instance of class c. + * + * @param instance the object to query for the field value + * @param fieldName the name of the field we are looking for in instance + * @return The value assigned to field in the ArgumentCollection, otherwise null + */ + private Object getFieldValue(Object instance, String fieldName) { + // + // subtle note. If you have a field named X that is an ArgumentCollection that + // contains a field X as well, you need only consider fields in the argumentCollection, not + // matching the argument itself. + // + // @ArgumentCollection + // protected DbsnpArgumentCollection dbsnp = new DbsnpArgumentCollection(); + // + for ( Field field : JVMUtils.getAllFields(instance.getClass()) ) { if ( field.isAnnotationPresent(ArgumentCollection.class) ) { //System.out.printf("Searching for %s in argument collection field %s%n", fieldName, field); Object fieldValue = JVMUtils.getFieldValue(field, instance); - Object value = getFieldValue(fieldValue.getClass(), fieldValue, fieldName); + Object value = getFieldValue(fieldValue, fieldName); if ( value != null ) return value; + } else if ( field.getName().equals(fieldName) ) { + return JVMUtils.getFieldValue(field, instance); } } @@ -212,6 +297,8 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { } /** + * Pretty prints value + * * Assumes value != null * @param value * @return @@ -246,6 +333,11 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { return value.toString(); } + /** + * Attempt to instantiate class c, if possible. Returns null if this proves impossible. + * @param c + * @return + */ private Object makeInstanceIfPossible(Class c) { Object instance = null; try { @@ -265,47 +357,16 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { // this last one is super dangerous, but some of these methods catch ClassNotFoundExceptions // and rethrow then as RuntimeExceptions catch (RuntimeException e) {} -// finally { -// if ( instance == null ) -// logger.warn(String.format("Unable to create instance of class %s => %s", c, instance)); -// } return instance; } - protected void addRelatedBindings(Map root) { - List> extraDocsData = new ArrayList>(); - // add in all of the explicitly related items - for ( final Class extraDocClass : toProcess.annotation.extraDocs() ) { - final GATKDocWorkUnit otherUnit = GATKDoclet.findWorkUnitForClass(extraDocClass, all); - if ( otherUnit == null ) - throw new ReviewedStingException("Requested extraDocs for class without any documentation: " + extraDocClass); - extraDocsData.add( - new HashMap(){{ - put("filename", otherUnit.filename); - put("name", otherUnit.name);}}); - - } - root.put("extradocs", extraDocsData); - } - - private static final String classRelationship(Class me, Class other) { - if ( other.equals(me) ) - // no circular references - return null; - else if ( other.isAssignableFrom(me) ) - // toProcess is a superclass of other.clazz - return "superclass"; - else if ( me.isAssignableFrom(other) ) - // toProcess inherits from other.clazz - return "subclass"; - else - return null; - - } - - protected ParsingEngine createStandardGATKParsingEngine() { + /** + * Create an instance of the GATK parsing engine, for argument processing with GATKDoclet + * @return + */ + private ParsingEngine createStandardGATKParsingEngine() { CommandLineProgram clp = new CommandLineGATK(); try { CommandLineProgram.start(clp, new String[]{}, true); @@ -315,6 +376,14 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { } } + /** + * Gets the javadocs associated with field name in classDoc. Throws a + * runtime exception if this proves impossible. + * + * @param classDoc + * @param name + * @return + */ private FieldDoc getFieldDoc(ClassDoc classDoc, String name) { return getFieldDoc(classDoc, name, true); } From 7b5fa4486d27cca06cd3e5b07171d6ea99c3ac38 Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Fri, 19 Aug 2011 13:34:21 -0400 Subject: [PATCH 103/138] GenotypeAndValidate - Added docs to the @Arguments --- .../validation/GenotypeAndValidateWalker.java | 33 ++++++++++++++++--- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/validation/GenotypeAndValidateWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/validation/GenotypeAndValidateWalker.java index fc23200af..2b38afaf6 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/validation/GenotypeAndValidateWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/validation/GenotypeAndValidateWalker.java @@ -25,10 +25,7 @@ package org.broadinstitute.sting.gatk.walkers.validation; -import org.broadinstitute.sting.commandline.Argument; -import org.broadinstitute.sting.commandline.Input; -import org.broadinstitute.sting.commandline.Output; -import org.broadinstitute.sting.commandline.RodBinding; +import org.broadinstitute.sting.commandline.*; import org.broadinstitute.sting.gatk.contexts.AlignmentContext; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.refdata.RefMetaDataTracker; @@ -201,30 +198,58 @@ import static org.broadinstitute.sting.utils.IndelUtils.isInsideExtendedIndel; public class GenotypeAndValidateWalker extends RodWalker implements TreeReducible { + /** + * The optional output file that will have all the variants used in the Genotype and Validation essay. + */ @Output(doc="Generate a VCF file with the variants considered by the walker, with a new annotation \"callStatus\" which will carry the value called in the validation VCF or BAM file", required=false) protected VCFWriter vcfWriter = null; + /** + * The callset to be used as truth (default) or validated (if BAM file is set to truth). + */ @Input(fullName="alleles", shortName = "alleles", doc="The set of alleles at which to genotype", required=true) public RodBinding alleles; + /** + * Makes the Unified Genotyper calls to the BAM file the truth dataset and validates the alleles ROD binding callset. + */ @Argument(fullName ="set_bam_truth", shortName ="bt", doc="Use the calls on the reads (bam file) as the truth dataset and validate the calls on the VCF", required=false) private boolean bamIsTruth = false; + /** + * The minimum base quality score necessary for a base to be considered when calling a genotype. This argument is passed to the Unified Genotyper. + */ @Argument(fullName="minimum_base_quality_score", shortName="mbq", doc="Minimum base quality score for calling a genotype", required=false) private int mbq = -1; + /** + * The maximum deletion fraction allowed in a site for calling a genotype. This argument is passed to the Unified Genotyper. + */ @Argument(fullName="maximum_deletion_fraction", shortName="deletions", doc="Maximum deletion fraction for calling a genotype", required=false) private double deletions = -1; + /** + * the minimum phred-scaled Qscore threshold to separate high confidence from low confidence calls. This argument is passed to the Unified Genotyper. + */ @Argument(fullName="standard_min_confidence_threshold_for_calling", shortName="stand_call_conf", doc="the minimum phred-scaled Qscore threshold to separate high confidence from low confidence calls", required=false) private double callConf = -1; + /** + * the minimum phred-scaled Qscore threshold to emit low confidence calls. This argument is passed to the Unified Genotyper. + */ @Argument(fullName="standard_min_confidence_threshold_for_emitting", shortName="stand_emit_conf", doc="the minimum phred-scaled Qscore threshold to emit low confidence calls", required=false) private double emitConf = -1; + /** + * Only validate sites that have at least a given depth + */ @Argument(fullName="condition_on_depth", shortName="depth", doc="Condition validation on a minimum depth of coverage by the reads", required=false) private int minDepth = -1; + /** + * If your VCF or BAM file has more than one sample and you only want to validate one, use this parameter to choose it. + */ + @Hidden @Argument(fullName ="sample", shortName ="sn", doc="Name of the sample to validate (in case your VCF/BAM has more than one sample)", required=false) private String sample = ""; From 49e831a13b51dc37877138dfca4e0cd178f4f4e5 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 19 Aug 2011 14:35:16 -0400 Subject: [PATCH 106/138] Should have checked in --- .../sting/gatk/walkers/ClipReadsWalker.java | 60 ++++++++- .../gatk/walkers/qc/DocumentationTest.java | 115 ++++++++++++++++++ 2 files changed, 174 insertions(+), 1 deletion(-) create mode 100644 public/java/src/org/broadinstitute/sting/gatk/walkers/qc/DocumentationTest.java diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/ClipReadsWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/ClipReadsWalker.java index 76b0276cd..68afed296 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/ClipReadsWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/ClipReadsWalker.java @@ -51,8 +51,66 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * This ReadWalker provides simple, yet powerful read clipping capabilities. It allows the user to clip bases in reads + * This tool provides simple, powerful read clipping capabilities. + * + *

      + * It allows the user to clip bases in reads * with poor quality scores, that match particular sequences, or that were generated by particular machine cycles. + *

      + * + *

      Input

      + *

      + * A BAM file containing. + *

      + * + *

      Output

      + *

      + *

        + *
      • -o: a OutputFormatted (recommended BED) file with the callable status covering each base
      • + *
      • -summary: a table of callable status x count of all examined bases
      • + *
      + *

      + * + *

      Examples

      + *
      + *     -T CallableLociWalker \
      + *     -I my.bam \
      + *     -summary my.summary \
      + *     -o my.bed
      + * 
      + * + * would produce a BED file (my.bed) that looks like: + * + *
      + *     20 10000000 10000864 CALLABLE
      + *     20 10000865 10000985 POOR_MAPPING_QUALITY
      + *     20 10000986 10001138 CALLABLE
      + *     20 10001139 10001254 POOR_MAPPING_QUALITY
      + *     20 10001255 10012255 CALLABLE
      + *     20 10012256 10012259 POOR_MAPPING_QUALITY
      + *     20 10012260 10012263 CALLABLE
      + *     20 10012264 10012328 POOR_MAPPING_QUALITY
      + *     20 10012329 10012550 CALLABLE
      + *     20 10012551 10012551 LOW_COVERAGE
      + *     20 10012552 10012554 CALLABLE
      + *     20 10012555 10012557 LOW_COVERAGE
      + *     20 10012558 10012558 CALLABLE
      + *     et cetera...
      + * 
      + * as well as a summary table that looks like: + * + *
      + *                        state nBases
      + *                        REF_N 0
      + *                     CALLABLE 996046
      + *                  NO_COVERAGE 121
      + *                 LOW_COVERAGE 928
      + *           EXCESSIVE_COVERAGE 0
      + *         POOR_MAPPING_QUALITY 2906
      + * 
      + * + * @author Mark DePristo + * @since May 7, 2010 */ @Requires({DataSource.READS}) public class ClipReadsWalker extends ReadWalker { 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 new file mode 100644 index 000000000..933e24784 --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/qc/DocumentationTest.java @@ -0,0 +1,115 @@ +/* + * 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.qc; + +import org.broad.tribble.Feature; +import org.broadinstitute.sting.commandline.*; +import org.broadinstitute.sting.gatk.arguments.DbsnpArgumentCollection; +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.codecs.vcf.VCFWriter; +import org.broadinstitute.sting.utils.variantcontext.VariantContext; + +import java.util.*; + +/** + * Summary test + * + *

      Body test

      + */ +public class DocumentationTest extends RodWalker { + // the docs for the arguments are in the collection + @ArgumentCollection protected StandardVariantContextInputArgumentCollection variantCollection = new StandardVariantContextInputArgumentCollection(); + + /** + * dbSNP comparison VCF. By default, the dbSNP file is used to specify the set of "known" variants. + * Other sets can be specified with the -knownName (--known_names) argument. + */ + @ArgumentCollection + protected DbsnpArgumentCollection dbsnp = new DbsnpArgumentCollection(); + + /** + * detailed documentation about the argument goes here. + */ + @Input(fullName="listofRodBinding", shortName = "disc", doc="Output variants that were not called in this Feature comparison track", required=false) + private List> listOfRodBinding = Collections.emptyList(); + + @Input(fullName="optionalRodBinding", shortName = "conc", doc="Output variants that were also called in this Feature comparison track", required=false) + private RodBinding concordanceTrack; + + @Input(fullName="optionalRodBindingWithoutDefault", shortName = "optionalRodBindingWithoutDefault", doc="Output variants that were also called in this Feature comparison track", required=false) + private RodBinding noDefaultOptionalRodBinding; + + @Input(fullName="optionalRodBindingWithoutDefaultNull", shortName = "shortTest", doc="Output variants that were also called in this Feature comparison track", required=false) + private RodBinding noDefaultOptionalRodBindingNull = null; + + @Input(fullName="featureArg", shortName = "featureArg", doc="A RodBinding of feature", required=false) + private RodBinding featureArg = null; + + @Output(doc="VCFWriter",required=true) + protected VCFWriter vcfWriter = null; + + @Advanced + @Argument(fullName="setString", shortName="sn", doc="Sample name to be included in the analysis. Can be specified multiple times.", required=false) + public Set sampleNames; + + @Argument(fullName="setStringInitialized", shortName="setStringInitialized", doc="Sample name to be included in the analysis. Can be specified multiple times.", required=false) + public Set setStringInitialized = new HashSet(); + + @Argument(shortName="optionalArgWithMissinglessDefault", doc="One or more criteria to use when selecting the data. Evaluated *after* the specified samples are extracted and the INFO-field annotations are updated.", required=false) + public ArrayList SELECT_EXPRESSIONS = new ArrayList(); + + @Argument(shortName="AAAAA", fullName = "AAAAA", doc="Should be the first argument", required=false) + public boolean FIRST_ARG = false; + + @Advanced + @Argument(fullName="booleanArg", shortName="env", doc="Don't include loci found to be non-variant after the subsetting procedure.", required=false) + private boolean EXCLUDE_NON_VARIANTS = false; + + @Advanced + @Argument(fullName="booleanArray", shortName="booleanArray", doc="x", required=false) + private boolean[] boolArray = null; + + @Argument(fullName="enumTest", shortName="enumTest", doc="Test enum", required=false) + private TestEnum TestEnumArg = TestEnum.ENUM2; + public enum TestEnum { + /** Docs for enum1 */ + ENUM1, + /** Docs for enum2 */ + ENUM2 + } + + @Hidden + @Argument(fullName="hiddenArg", shortName="keepAF", doc="Don't include loci found to be non-variant after the subsetting procedure.", required=false) + private boolean KEEP_AF_SPECTRUM = false; + + public Integer map(RefMetaDataTracker tracker, ReferenceContext ref, AlignmentContext context) { return 0; } + public Integer reduceInit() { return 0; } + public Integer reduce(Integer value, Integer sum) { return value + sum; } + public void onTraversalDone(Integer result) { } +} From b08d63a6b8dc4e7b8f21c528f234d34f81ce654a Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 19 Aug 2011 15:06:37 -0400 Subject: [PATCH 107/138] Documentation and code cleanup for ClipReads, CallableLoci, and VariantsToTable -- Swapped -o [summary] and -ob [bam] for more standard -o [bam] and -os [summary] arguments. -- @Advanced arguments --- .../sting/gatk/walkers/ClipReadsWalker.java | 181 ++++++++++++------ .../walkers/coverage/CallableLociWalker.java | 4 + .../walkers/variantutils/VariantsToTable.java | 4 + .../clipreads/ClippingRepresentation.java | 29 ++- .../ClipReadsWalkersIntegrationTest.java | 6 +- 5 files changed, 154 insertions(+), 70 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/ClipReadsWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/ClipReadsWalker.java index 68afed296..bb65d9b09 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/ClipReadsWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/ClipReadsWalker.java @@ -30,7 +30,9 @@ import net.sf.picard.reference.ReferenceSequenceFile; import net.sf.picard.reference.ReferenceSequenceFileFactory; import net.sf.samtools.SAMRecord; import net.sf.samtools.util.StringUtil; +import org.broadinstitute.sting.commandline.Advanced; import org.broadinstitute.sting.commandline.Argument; +import org.broadinstitute.sting.commandline.Hidden; import org.broadinstitute.sting.commandline.Output; import org.broadinstitute.sting.gatk.contexts.ReferenceContext; import org.broadinstitute.sting.gatk.io.StingSAMFileWriter; @@ -42,7 +44,6 @@ import org.broadinstitute.sting.utils.clipreads.ClippingRepresentation; import org.broadinstitute.sting.utils.clipreads.ReadClipper; import org.broadinstitute.sting.utils.collections.Pair; import org.broadinstitute.sting.utils.sam.ReadUtils; -import org.yaml.snakeyaml.events.SequenceStartEvent; import java.io.File; import java.io.PrintStream; @@ -51,102 +52,158 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; /** - * This tool provides simple, powerful read clipping capabilities. + * This tool provides simple, powerful read clipping capabilities to remove low quality strings of bases, sections of reads, and reads containing user-provided sequences. + * * *

      - * It allows the user to clip bases in reads - * with poor quality scores, that match particular sequences, or that were generated by particular machine cycles. + * It allows the user to clip bases in reads with poor quality scores, that match particular + * sequences, or that were generated by particular machine cycles. + * + *

      + *
      Quality score based clipping
      + *
      + * Clip bases from the read in clipper from + *
      argmax_x{ \sum{i = x + 1}^l (qTrimmingThreshold - qual)
      + * to the end of the read. This is blatantly stolen from BWA. + * + * Walk through the read from the end (in machine cycle order) to the beginning, calculating the + * running sum of qTrimmingThreshold - qual. While we do this, we track the maximum value of this + * sum where the delta > 0. After the loop, clipPoint is either -1 (don't do anything) or the + * clipping index in the read (from the end). + *
      + *
      Cycle based clipping
      + *
      Clips machine cycles from the read. Accepts a string of ranges of the form start1-end1,start2-end2, etc. + * For each start/end pair, removes bases in machine cycles from start to end, inclusive. These are 1-based values (positions). + * For example, 1-5,10-12 clips the first 5 bases, and then three bases at cycles 10, 11, and 12. + *
      + *
      Sequence matching
      + *
      Clips bases from that exactly match one of a number of base sequences. This employs an exact match algorithm, + * filtering only bases whose sequence exactly matches SEQ.
      + *
      + * *

      * *

      Input

      *

      - * A BAM file containing. + * Any number of BAM files. *

      * *

      Output

      *

      - *

        - *
      • -o: a OutputFormatted (recommended BED) file with the callable status covering each base
      • - *
      • -summary: a table of callable status x count of all examined bases
      • - *
      + * A new BAM file containing all of the reads from the input BAMs with the user-specified clipping + * operation applied to each read. + *

      + *

      + *

      Summary output

      + *
      + *     Number of examined reads              13
      + *     Number of clipped reads               13
      + *     Percent of clipped reads              100.00
      + *     Number of examined bases              988
      + *     Number of clipped bases               126
      + *     Percent of clipped bases              12.75
      + *     Number of quality-score clipped bases 126
      + *     Number of range clipped bases         0
      + *     Number of sequence clipped bases      0
      + *     
      + *

      + * + *

      + *

      Example clipping

      + * Suppose we are given this read: + *
      + *     314KGAAXX090507:1:19:1420:1123#0        16      chrM    3116    29      76M     *       *       *
      + *          TAGGACCCGGGCCCCCCTCCCCAATCCTCCAACGCATATAGCGGCCGCGCCTTCCCCCGTAAATGATATCATCTCA
      + *          #################4?6/?2135;;;'1/=/<'B9;12;68?A79@,@==@9?=AAA3;A@B;A?B54;?ABA
      + *     
      + * + * If we are clipping reads with -QT 10 and -CR WRITE_NS, we get: + * + *
      + *     314KGAAXX090507:1:19:1420:1123#0        16      chrM    3116    29      76M     *       *       *
      + *          NNNNNNNNNNNNNNNNNTCCCCAATCCTCCAACGCATATAGCGGCCGCGCCTTCCCCCGTAAATGATATCATCTCA
      + *          #################4?6/?2135;;;'1/=/<'B9;12;68?A79@,@==@9?=AAA3;A@B;A?B54;?ABA
      + *     
      + * + * Whereas with -CR WRITE_Q0S: + *
      + *     314KGAAXX090507:1:19:1420:1123#0        16      chrM    3116    29      76M     *       *       *
      + *          TAGGACCCGGGCCCCCCTCCCCAATCCTCCAACGCATATAGCGGCCGCGCCTTCCCCCGTAAATGATATCATCTCA
      + *          !!!!!!!!!!!!!!!!!4?6/?2135;;;'1/=/<'B9;12;68?A79@,@==@9?=AAA3;A@B;A?B54;?ABA
      + *     
      + * + * Or -CR SOFTCLIP_BASES: + *
      + *     314KGAAXX090507:1:19:1420:1123#0        16      chrM    3133    29      17S59M  *       *       *
      + *          TAGGACCCGGGCCCCCCTCCCCAATCCTCCAACGCATATAGCGGCCGCGCCTTCCCCCGTAAATGATATCATCTCA
      + *          #################4?6/?2135;;;'1/=/<'B9;12;68?A79@,@==@9?=AAA3;A@B;A?B54;?ABA
      + *     
      *

      * *

      Examples

      *
      - *     -T CallableLociWalker \
      - *     -I my.bam \
      - *     -summary my.summary \
      - *     -o my.bed
      + *     -T ClipReads -I my.bam -I your.bam -o my_and_your.clipped.bam -R Homo_sapiens_assembly18.fasta \
      + *     -XF seqsToClip.fasta -X CCCCC -CT "1-5,11-15" -QT 10
        * 
      - * - * would produce a BED file (my.bed) that looks like: - * - *
      - *     20 10000000 10000864 CALLABLE
      - *     20 10000865 10000985 POOR_MAPPING_QUALITY
      - *     20 10000986 10001138 CALLABLE
      - *     20 10001139 10001254 POOR_MAPPING_QUALITY
      - *     20 10001255 10012255 CALLABLE
      - *     20 10012256 10012259 POOR_MAPPING_QUALITY
      - *     20 10012260 10012263 CALLABLE
      - *     20 10012264 10012328 POOR_MAPPING_QUALITY
      - *     20 10012329 10012550 CALLABLE
      - *     20 10012551 10012551 LOW_COVERAGE
      - *     20 10012552 10012554 CALLABLE
      - *     20 10012555 10012557 LOW_COVERAGE
      - *     20 10012558 10012558 CALLABLE
      - *     et cetera...
      - * 
      - * as well as a summary table that looks like: - * - *
      - *                        state nBases
      - *                        REF_N 0
      - *                     CALLABLE 996046
      - *                  NO_COVERAGE 121
      - *                 LOW_COVERAGE 928
      - *           EXCESSIVE_COVERAGE 0
      - *         POOR_MAPPING_QUALITY 2906
      - * 
      - * + * @author Mark DePristo - * @since May 7, 2010 + * @since 2010 */ @Requires({DataSource.READS}) public class ClipReadsWalker extends ReadWalker { - @Output - PrintStream out; + /** + * If provided, ClipReads will write summary statistics about the clipping operations applied + * to the reads to this file. + */ + @Output(fullName = "outputStatistics", shortName = "os", doc = "Write output statistics to this file", required = false) + PrintStream out = null; /** - * an optional argument to dump the reads out to a BAM file + * The output SAM/BAM file will be written here */ - @Argument(fullName = "outputBam", shortName = "ob", doc = "Write output to this BAM filename instead of STDOUT", required = false) - StingSAMFileWriter outputBam = null; + @Output(doc = "Write BAM output here", required = true) + StingSAMFileWriter outputBam; - @Argument(fullName = "qTrimmingThreshold", shortName = "QT", doc = "", required = false) + /** + * If a value > 0 is provided, then the quality score based read clipper will be applied to the reads using this + * quality score threshold. + */ + @Argument(fullName = "qTrimmingThreshold", shortName = "QT", doc = "If provided, the Q-score clipper will be applied", required = false) int qTrimmingThreshold = -1; - @Argument(fullName = "cyclesToTrim", shortName = "CT", doc = "String of the form 1-10,20-30 indicating machine cycles to clip from the reads", required = false) + /** + * Clips machine cycles from the read. Accepts a string of ranges of the form start1-end1,start2-end2, etc. + * For each start/end pair, removes bases in machine cycles from start to end, inclusive. These are 1-based + * values (positions). For example, 1-5,10-12 clips the first 5 bases, and then three bases at cycles 10, 11, + * and 12. + */ + @Argument(fullName = "cyclesToTrim", shortName = "CT", doc = "String indicating machine cycles to clip from the reads", required = false) String cyclesToClipArg = null; - @Argument(fullName = "clipSequencesFile", shortName = "XF", doc = "Remove sequences within reads matching these sequences", required = false) + /** + * Reads the sequences in the provided FASTA file, and clip any bases that exactly match any of the + * sequences in the file. + */ + @Argument(fullName = "clipSequencesFile", shortName = "XF", doc = "Remove sequences within reads matching the sequences in this FASTA file", required = false) String clipSequenceFile = null; + /** + * Clips bases from the reads matching the provided SEQ. Can be provided any number of times on the command line + */ @Argument(fullName = "clipSequence", shortName = "X", doc = "Remove sequences within reads matching this sequence", required = false) String[] clipSequencesArgs = null; - @Argument(fullName="read", doc="", required=false) - String onlyDoRead = null; - - //@Argument(fullName = "keepCompletelyClipped", shortName = "KCC", doc = "Unfortunately, sometimes a read is completely clipped away but with SOFTCLIP_BASES this results in an invalid CIGAR string. ", required = false) - //boolean keepCompletelyClippedReads = false; - -// @Argument(fullName = "onlyClipFirstSeqMatch", shortName = "ESC", doc="Only clip the first occurrence of a clipping sequence, rather than all subsequences within a read that match", required = false) -// boolean onlyClipFirstSeqMatch = false; - + /** + * The different values for this argument determines how ClipReads applies clips to the reads. This can range + * from writing Ns over the clipped bases to hard clipping away the bases from the BAM. + */ @Argument(fullName = "clipRepresentation", shortName = "CR", doc = "How should we actually clip the bases?", required = false) ClippingRepresentation clippingRepresentation = ClippingRepresentation.WRITE_NS; + @Hidden + @Advanced + @Argument(fullName="read", doc="", required=false) + String onlyDoRead = null; /** * List of sequence that should be clipped from the reads diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/CallableLociWalker.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/CallableLociWalker.java index 98331ec1d..32875a098 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/CallableLociWalker.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/coverage/CallableLociWalker.java @@ -22,6 +22,7 @@ package org.broadinstitute.sting.gatk.walkers.coverage; +import org.broadinstitute.sting.commandline.Advanced; import org.broadinstitute.sting.commandline.Argument; import org.broadinstitute.sting.commandline.Output; import org.broadinstitute.sting.gatk.contexts.AlignmentContext; @@ -154,6 +155,7 @@ public class CallableLociWalker extends LocusWalker minMappingQuality and with base quality > minBaseQuality) exceeds this * value and is less than maxDepth the site is considered CALLABLE. */ + @Advanced @Argument(fullName = "minDepth", shortName = "minDepth", doc = "Minimum QC+ read depth before a locus is considered callable", required = false) int minDepth = 4; @@ -168,6 +170,7 @@ public class CallableLociWalker extends LocusWalker { * By default this tool only emits values for fields where the FILTER field is either PASS or . (unfiltered). * Throwing this flag will cause $WalkerName to emit values regardless of the FILTER field value. */ + @Advanced @Argument(fullName="showFiltered", shortName="raw", doc="If provided, field values from filtered records will be included in the output", required=false) public boolean showFiltered = false; /** * If provided, then this tool will exit with success after this number of records have been emitted to the file. */ + @Advanced @Argument(fullName="maxRecords", shortName="M", doc="If provided, we will emit at most maxRecord records to the table", required=false) public int MAX_RECORDS = -1; int nRecords = 0; @@ -121,6 +123,7 @@ public class VariantsToTable extends RodWalker { * can make your resulting file unreadable and malformated according to tools like R, as the representation of * multi-allelic INFO field values can be lists of values. */ + @Advanced @Argument(fullName="keepMultiAllelic", shortName="KMA", doc="If provided, we will not require the site to be biallelic", required=false) public boolean keepMultiAllelic = false; @@ -131,6 +134,7 @@ public class VariantsToTable extends RodWalker { * fields (e.g., AC not being calculated for filtered records, if included). When provided, this argument * will cause VariantsToTable to write out NA values for missing fields instead of throwing an error. */ + @Advanced @Argument(fullName="allowMissingData", shortName="AMD", doc="If provided, we will not require every record to contain every field", required=false) public boolean ALLOW_MISSING_DATA = false; 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 14c04b5c4..0dbe55726 100644 --- a/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingRepresentation.java +++ b/public/java/src/org/broadinstitute/sting/utils/clipreads/ClippingRepresentation.java @@ -4,9 +4,28 @@ package org.broadinstitute.sting.utils.clipreads; * How should we represent a clipped bases in a read? */ public enum ClippingRepresentation { - WRITE_NS, // change the bases to Ns - WRITE_Q0S, // change the quality scores to Q0 - WRITE_NS_Q0S, // change the quality scores to Q0 and write Ns - SOFTCLIP_BASES, // change cigar string to S, but keep bases - HARDCLIP_BASES // remove the bases from the read + /** Clipped bases are changed to Ns */ + WRITE_NS, + + /** Clipped bases are changed to have Q0 quality score */ + WRITE_Q0S, + + /** Clipped bases are change to have both an N base and a Q0 quality score */ + WRITE_NS_Q0S, + + /** + * Change the read's cigar string to soft clip (S, see sam-spec) away the bases. + * Note that this can only be applied to cases where the clipped bases occur + * at the start or end of a read. + */ + SOFTCLIP_BASES, + + /** + * 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., + * lossy) operation. Note that this can only be applied to cases where the clipped + * bases occur at the start or end of a read. + */ + HARDCLIP_BASES } diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/ClipReadsWalkersIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/ClipReadsWalkersIntegrationTest.java index a129f8adf..ca3d1ee25 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/ClipReadsWalkersIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/ClipReadsWalkersIntegrationTest.java @@ -37,8 +37,8 @@ public class ClipReadsWalkersIntegrationTest extends WalkerTest { "-R " + hg18Reference + " -T ClipReads " + "-I " + validationDataLocation + "clippingReadsTest.bam " + - "-o %s " + - "-ob %s " + args, + "-os %s " + + "-o %s " + args, 2, // just one output file Arrays.asList("tmp", "bam"), Arrays.asList(md51, md52)); @@ -72,7 +72,7 @@ public class ClipReadsWalkersIntegrationTest extends WalkerTest { " -I " + validationDataLocation + "originalQuals.chr1.1-1K.bam" + " -L chr1:1-1,000" + " -OQ -QT 4 -CR WRITE_Q0S" + - " -o %s -ob %s", + " -o %s -os %s", 2, Arrays.asList("55c01ccc2e84481b22d3632cdb06c8ba", "22db22749f811d30216215e047461621")); executeTest("clipOriginalQuals", spec); From f39d0008bc5558c06786d2c3b8cbbaa537ca49b2 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 19 Aug 2011 15:07:26 -0400 Subject: [PATCH 108/138] Build.xml -- contracts not built by default. Slightly simpler CSS for dl. --- build.xml | 2 +- settings/helpTemplates/style.css | 12 ++++++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/build.xml b/build.xml index d8c38738a..ef53f6aa4 100644 --- a/build.xml +++ b/build.xml @@ -489,7 +489,7 @@ docletpathref="doclet.classpath" classpathref="external.dependencies" classpath="${java.classes}" - additionalparam="-private -build-timestamp "${build.timestamp}" -absolute-version ${build.version} -quiet -J-Xdebug -J-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005"> + additionalparam="-private -build-timestamp "${build.timestamp}" -absolute-version ${build.version} -quiet -J-Xdebug -J-Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=5005"> diff --git a/settings/helpTemplates/style.css b/settings/helpTemplates/style.css index 375df2f51..297cd49ef 100644 --- a/settings/helpTemplates/style.css +++ b/settings/helpTemplates/style.css @@ -85,25 +85,33 @@ hr * enum DT layout */ +dl { + margin-left: 3em; +} + dl.enum { margin-left: 3em; border: 1px dashed #ccc; } -dt.enum { +dt, dt.enum { font-weight: bold; text-decoration: underline; } -dd.enum { +/* +dt, dd.enum { padding: 0 0 0.5em 0; } +*/ pre { border: thin solid lightgray; margin-left: 1em; margin-right: 4em; +/* background-color: #e0fdff; +*/ } /* * clean table layouts From 8b3cfb2f1c0f21699d4a4a3b1002fe1d612adf44 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 19 Aug 2011 16:52:17 -0400 Subject: [PATCH 109/138] Final documented version of GATKDoclet and associated classes -- Docs on everything. -- Feature complete. At this point only minor improvements and bugfixes are anticipated --- .../utils/help/DocumentedGATKFeature.java | 4 + .../help/DocumentedGATKFeatureHandler.java | 4 +- .../help/DocumentedGATKFeatureObject.java | 5 +- .../sting/utils/help/GATKDocUtils.java | 19 ++ .../sting/utils/help/GATKDoclet.java | 206 +++++++++++++----- .../help/GenericDocumentationHandler.java | 81 +++++-- .../sting/utils/help/HelpUtils.java | 8 - 7 files changed, 244 insertions(+), 83 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeature.java b/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeature.java index 89163dfcb..5bbe3f91e 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeature.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeature.java @@ -36,8 +36,12 @@ import java.lang.annotation.*; @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) public @interface DocumentedGATKFeature { + /** Should we actually document this feature, even through it's annotated? */ public boolean enable() default true; + /** The overall group name (walkers, readfilters) this feature is associated with */ public String groupName(); + /** A human readable summary of the purpose of this group of features */ public String summary() default ""; + /** Are there links to other docs that we should include? CommandLineGATK.class for walkers, for example? */ public Class[] extraDocs() default {}; } diff --git a/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureHandler.java b/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureHandler.java index ce03c8093..87926d2e3 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureHandler.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureHandler.java @@ -92,9 +92,7 @@ public abstract class DocumentedGATKFeatureHandler { * * toProcess.setHandlerContent(summary, rootMap); * - * @param rootDoc * @param toProcess - * @param all */ - public abstract void processOne(RootDoc rootDoc, GATKDocWorkUnit toProcess, Set all); + public abstract void processOne(GATKDocWorkUnit toProcess); } diff --git a/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureObject.java b/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureObject.java index 66354202f..6c8b0a475 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureObject.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/DocumentedGATKFeatureObject.java @@ -25,12 +25,15 @@ package org.broadinstitute.sting.utils.help; /** - * Documentation unit. Effectively a class version of the DocumentedGATKFeature + * Documentation unit. Effectively a class version of the DocumentedGATKFeature. + * Immutable data structure. * * @author depristo */ class DocumentedGATKFeatureObject { + /** Which class are we documenting. Specific to each class being documented */ private final Class classToDoc; + /** Are we enabled? */ private final boolean enable; private final String groupName, summary; private final Class[] extraDocs; diff --git a/public/java/src/org/broadinstitute/sting/utils/help/GATKDocUtils.java b/public/java/src/org/broadinstitute/sting/utils/help/GATKDocUtils.java index 983805c4d..cd645943b 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/GATKDocUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/GATKDocUtils.java @@ -25,14 +25,33 @@ package org.broadinstitute.sting.utils.help; public class GATKDocUtils { + /** The URL root for RELEASED GATKDOC units */ public final static String URL_ROOT_FOR_RELEASE_GATKDOCS = "http://www.broadinstitute.org/gsa/gatkdocs/release/"; + /** The URL root for STABLE GATKDOC units */ public final static String URL_ROOT_FOR_STABLE_GATKDOCS = "http://iwww.broadinstitute.org/gsa/gatkdocs/stable/"; + /** The URL root for UNSTABLE GATKDOC units */ public final static String URL_ROOT_FOR_UNSTABLE_GATKDOCS = "http://iwww.broadinstitute.org/gsa/gatkdocs/unstable/"; + /** + * Return the filename of the GATKDoc HTML that would be generated for Class. This + * does not guarantee that the docs exist, or that docs would actually be generated + * for class (might not be annotated for documentation, for example). But if + * this class is documented, GATKDocs will write the docs to a file named as returned + * by this function. + * + * @param c + * @return + */ public static String htmlFilenameForClass(Class c) { return c.getName().replace(".", "_") + ".html"; } + /** + * Returns a full URL http://etc/ linking to the documentation for class (assuming it + * exists). Currently points to the RELEASE doc path only. + * @param c + * @return + */ public static String helpLinksToGATKDocs(Class c) { String classPath = htmlFilenameForClass(c); StringBuilder b = new StringBuilder(); diff --git a/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java b/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java index de6ad359e..7f26f22f5 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/GATKDoclet.java @@ -43,18 +43,65 @@ import java.io.*; import java.util.*; /** + * Javadoc Doclet that combines javadoc, GATK ParsingEngine annotations, and FreeMarker + * templates to produce html formatted GATKDocs for walkers + * and other classes. * + * This document has the following workflow: + * + * 1 -- walk the javadoc heirarchy, looking for class that have the + * DocumentedGATKFeature annotation or are in the type heirarchy in the + * static list of things to document, and are to be documented + * 2 -- construct for each a GATKDocWorkUnit, resulting in the complete + * set of things to document + * 3 -- for each unit, actually generate an html page documenting it + * as well as links to related features via their units. Writing + * of a specific class HTML is accomplished by a generate DocumentationHandler + * 4 -- write out an index of all units, organized by group + * + * The documented classes are restricted to only those with @DocumentedGATKFeature + * annotation or are in the STATIC_DOCS class. */ public class GATKDoclet { - final protected static File SETTINGS_DIR = new File("settings/helpTemplates"); - final protected static File DESTINATION_DIR = new File("gatkdocs"); final protected static Logger logger = Logger.getLogger(GATKDoclet.class); + + /** Where we find the help FreeMarker templates */ + final protected static File SETTINGS_DIR = new File("settings/helpTemplates"); + + /** Where we write the GATKDoc html directory */ + final protected static File DESTINATION_DIR = new File("gatkdocs"); + + // ---------------------------------------------------------------------- + // + // Global variables that are set on the command line by javadoc + // + // ---------------------------------------------------------------------- protected static String buildTimestamp = null, absoluteVersion = null; protected static boolean showHiddenFeatures = false; + protected static boolean testOnly = false; + /** + * Any class that's in this list will be included in the documentation + * when the -test argument is provided. Useful for debugging. + */ + private static final List> testOnlyKeepers = Arrays.asList( + DocumentationTest.class, CommandLineGATK.class, UserException.class); + + /** The javadoc root doc */ RootDoc rootDoc; + /** The set of all things we are going to document */ + Set myWorkUnits; + + /** + * A static list of DocumentedGATKFeatureObjects. Any class that is as or extends + * one of the DocumentedGATKFeatureObjects.clazz of this collection will also + * be documented, even if it doesn't have the @DocumentedGATKFeature annotation. Useful + * when you want to document things that implement an interface (annotations on java + * interfaces aren't inherited) or whose base class isn't under your control (tribble + * codecs). + */ final static Collection STATIC_DOCS = new ArrayList(); static { STATIC_DOCS.add(new DocumentedGATKFeatureObject(FeatureCodec.class, @@ -70,7 +117,8 @@ public class GATKDoclet { * @throws java.io.IOException if output can't be written. */ public static boolean start(RootDoc rootDoc) throws IOException { - logger.setLevel(Level.DEBUG); + logger.setLevel(Level.INFO); + // load arguments for(String[] options: rootDoc.options()) { if(options[0].equals("-build-timestamp")) @@ -83,8 +131,9 @@ public class GATKDoclet { testOnly = true; } - GATKDoclet doclet = new GATKDoclet(); - doclet.processDocs(rootDoc); + // process the docs + new GATKDoclet().processDocs(rootDoc); + return true; } @@ -104,17 +153,54 @@ public class GATKDoclet { return 0; } + /** + * Are we supposed to include @Hidden annotations in our documented output? + * @return + */ public boolean showHiddenFeatures() { return showHiddenFeatures; } - public static boolean testOnly() { - return testOnly; + /** + * + * @param rootDoc + */ + private void processDocs(RootDoc rootDoc) { + // setup the global access to the root + this.rootDoc = rootDoc; + + try { + // basic setup + DESTINATION_DIR.mkdirs(); + FileUtils.copyFile(new File(SETTINGS_DIR + "/style.css"), new File(DESTINATION_DIR + "/style.css")); + + /* ------------------------------------------------------------------- */ + /* You should do this ONLY ONCE in the whole application life-cycle: */ + + Configuration cfg = new Configuration(); + // Specify the data source where the template files come from. + cfg.setDirectoryForTemplateLoading(SETTINGS_DIR); + // Specify how templates will see the data-model. This is an advanced topic... + cfg.setObjectWrapper(new DefaultObjectWrapper()); + + myWorkUnits = computeWorkUnits(); + for ( GATKDocWorkUnit workUnit : myWorkUnits ) { + processDocWorkUnit(cfg, workUnit); + } + + processIndex(cfg, new ArrayList(myWorkUnits)); + } catch ( FileNotFoundException e ) { + throw new RuntimeException(e); + } catch ( IOException e ) { + throw new RuntimeException(e); + } } - private static final List> testOnlyKeepers = Arrays.asList( - DocumentationTest.class, CommandLineGATK.class, UserException.class); - public Set workUnits() { + /** + * Returns the set of all GATKDocWorkUnits that we are going to generate docs for. + * @return + */ + private Set computeWorkUnits() { TreeSet m = new TreeSet(); for ( ClassDoc doc : rootDoc.classes() ) { @@ -144,37 +230,13 @@ public class GATKDoclet { return m; } - protected void processDocs(RootDoc rootDoc) { - // setup the global access to the root - this.rootDoc = rootDoc; - - try { - // basic setup - DESTINATION_DIR.mkdirs(); - FileUtils.copyFile(new File(SETTINGS_DIR + "/style.css"), new File(DESTINATION_DIR + "/style.css")); - - /* ------------------------------------------------------------------- */ - /* You should do this ONLY ONCE in the whole application life-cycle: */ - - Configuration cfg = new Configuration(); - // Specify the data source where the template files come from. - cfg.setDirectoryForTemplateLoading(SETTINGS_DIR); - // Specify how templates will see the data-model. This is an advanced topic... - cfg.setObjectWrapper(new DefaultObjectWrapper()); - - Set myWorkUnits = workUnits(); - for ( GATKDocWorkUnit workUnit : myWorkUnits ) { - processDocWorkUnit(cfg, workUnit, myWorkUnits); - } - - processIndex(cfg, new ArrayList(myWorkUnits)); - } catch ( FileNotFoundException e ) { - throw new RuntimeException(e); - } catch ( IOException e ) { - throw new RuntimeException(e); - } - } - + /** + * Create a handler capable of documenting the class doc according to feature. Returns + * null if no appropriate handler is found or doc shouldn't be documented at all. + * @param doc + * @param feature + * @return + */ private DocumentedGATKFeatureHandler createHandler(ClassDoc doc, DocumentedGATKFeatureObject feature) { if ( feature != null ) { if ( feature.enable() ) { @@ -189,6 +251,13 @@ public class GATKDoclet { return null; } + /** + * Returns the instantiated DocumentedGATKFeatureObject that describes the GATKDoc + * structure we will apply to Doc. + * + * @param doc + * @return null if this proves inappropriate or doc shouldn't be documented + */ private DocumentedGATKFeatureObject getFeatureForClassDoc(ClassDoc doc) { Class docClass = getClassForClassDoc(doc); @@ -208,6 +277,11 @@ public class GATKDoclet { } } + /** + * Return the Java class described by the ClassDoc doc + * @param doc + * @return + */ private Class getClassForClassDoc(ClassDoc doc) { try { // todo -- what do I need the ? extends Object to pass the compiler? @@ -223,10 +297,12 @@ public class GATKDoclet { } } - public static ClassDoc getClassDocForClass(RootDoc rootDoc, Class clazz) { - return rootDoc.classNamed(clazz.getName()); - } - + /** + * Create the html index listing all of the GATKDocs features + * @param cfg + * @param indexData + * @throws IOException + */ private void processIndex(Configuration cfg, List indexData) throws IOException { /* Get or create a template */ Template temp = cfg.getTemplate("generic.index.template.html"); @@ -241,6 +317,12 @@ public class GATKDoclet { } } + /** + * Helpful function to create the html index. Given all of the already run GATKDocWorkUnits, + * create the high-level grouping data listing individual features by group. + * @param indexData + * @return + */ private Map groupIndexData(List indexData) { // // root -> data -> { summary -> y, filename -> z }, etc @@ -268,6 +350,11 @@ public class GATKDoclet { return root; } + /** + * Trivial helper routine that returns the map of name and summary given the annotation + * @param annotation + * @return + */ private static final Map toMap(DocumentedGATKFeatureObject annotation) { Map root = new HashMap(); root.put("name", annotation.groupName()); @@ -275,18 +362,39 @@ public class GATKDoclet { return root; } - public final static GATKDocWorkUnit findWorkUnitForClass(Class c, Set all) { - for ( final GATKDocWorkUnit unit : all ) + /** + * Helper function that finding the GATKDocWorkUnit associated with class from among all of the work units + * @param c the class we are looking for + * @return the GATKDocWorkUnit whose .clazz.equals(c), or null if none could be found + */ + public final GATKDocWorkUnit findWorkUnitForClass(Class c) { + for ( final GATKDocWorkUnit unit : this.myWorkUnits ) if ( unit.clazz.equals(c) ) return unit; return null; } - private void processDocWorkUnit(Configuration cfg, GATKDocWorkUnit unit, Set all) + /** + * Return the ClassDoc associated with clazz + * @param clazz + * @return + */ + public ClassDoc getClassDocForClass(Class clazz) { + return rootDoc.classNamed(clazz.getName()); + } + + /** + * High-level function that processes a single DocWorkUnit unit using its handler + * + * @param cfg + * @param unit + * @throws IOException + */ + private void processDocWorkUnit(Configuration cfg, GATKDocWorkUnit unit) throws IOException { //System.out.printf("Processing documentation for class %s%n", unit.classDoc); - unit.handler.processOne(rootDoc, unit, all); + unit.handler.processOne(unit); // Get or create a template Template temp = cfg.getTemplate(unit.handler.getTemplateName(unit.classDoc)); 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 08e430c8a..4f1e95499 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/GenericDocumentationHandler.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/GenericDocumentationHandler.java @@ -53,15 +53,15 @@ import java.util.*; public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { private static Logger logger = Logger.getLogger(GenericDocumentationHandler.class); + /** + * The max. length of the longest of --fullName -shortName argument name + * before we prefer the shorter option. + */ + private static final int MAX_DISPLAY_NAME = 30; + /** The Class we are documenting */ private GATKDocWorkUnit toProcess; - /** The set of all classes we are documenting, for cross-referencing */ - private Set all; - - /** The JavaDoc root */ - private RootDoc rootDoc; - @Override public boolean includeInDocs(ClassDoc doc) { try { @@ -79,10 +79,8 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { } @Override - public void processOne(RootDoc rootDoc, GATKDocWorkUnit toProcessArg, Set allArg) { - this.rootDoc = rootDoc; + public void processOne(GATKDocWorkUnit toProcessArg) { this.toProcess = toProcessArg; - this.all = allArg; //System.out.printf("%s class %s%n", toProcess.group, toProcess.classDoc); Map root = new HashMap(); @@ -126,7 +124,7 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { // add in all of the explicitly related items for ( final Class extraDocClass : toProcess.annotation.extraDocs() ) { - final GATKDocWorkUnit otherUnit = GATKDoclet.findWorkUnitForClass(extraDocClass, all); + final GATKDocWorkUnit otherUnit = getDoclet().findWorkUnitForClass(extraDocClass); if ( otherUnit == null ) throw new ReviewedStingException("Requested extraDocs for class without any documentation: " + extraDocClass); extraDocsData.add( @@ -388,6 +386,13 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { return getFieldDoc(classDoc, name, true); } + /** + * Recursive helper routine to getFieldDoc() + * @param classDoc + * @param name + * @param primary + * @return + */ private FieldDoc getFieldDoc(ClassDoc classDoc, String name, boolean primary) { //System.out.printf("Looking for %s in %s%n", name, classDoc.name()); for ( FieldDoc fieldDoc : classDoc.fields(false) ) { @@ -422,7 +427,14 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { return null; } - private static final int MAX_DISPLAY_NAME = 30; + /** + * Returns a Pair of (main, synonym) names for argument with fullName s1 and + * shortName s2. The main is selected to be the longest of the two, provided + * it doesn't exceed MAX_DISPLAY_NAME, in which case the shorter is taken. + * @param s1 + * @param s2 + * @return + */ Pair displayNames(String s1, String s2) { if ( s1 == null ) return new Pair(s2, null); if ( s2 == null ) return new Pair(s1, null); @@ -436,6 +448,15 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { return new Pair(l, s); } + /** + * Returns a human readable string that describes the Type type of a GATK argument. + * + * This will include parameterized types, so that Set{T} shows up as Set(T) and not + * just Set in the docs. + * + * @param type + * @return + */ protected String argumentTypeString(Type type) { if (type instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType)type; @@ -454,6 +475,13 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { } } + /** + * Helper routine that returns the Feature.class required by a RodBinding, + * either T for RodBinding{T} or List{RodBinding{T}}. Returns null if + * the Type doesn't fit either model. + * @param type + * @return + */ protected Class getFeatureTypeIfPossible(Type type) { if ( type instanceof ParameterizedType) { ParameterizedType paramType = (ParameterizedType)type; @@ -471,6 +499,14 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { return null; } + /** + * High-level entry point for creating a FreeMarker map describing the GATK argument + * source with definition def, with associated javadoc fieldDoc. + * @param fieldDoc + * @param source + * @param def + * @return a non-null Map binding argument keys with their values + */ protected Map docForArgument(FieldDoc fieldDoc, ArgumentSource source, ArgumentDefinition def) { Map root = new HashMap(); Pair names = displayNames("-" + def.shortName, "--" + def.fullName); @@ -503,27 +539,29 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { root.put("summary", def.doc != null ? def.doc : ""); root.put("fulltext", fieldDoc.commentText()); + // What are our enum options? + if ( def.validOptions != null ) + root.put("options", docForEnumArgument(source.field.getType())); + + // general attributes List attributes = new ArrayList(); - // this one below is just too much. - //attributes.add(def.ioType.annotationClass.getSimpleName()); if ( def.required ) attributes.add("required"); - // flag is just boolean, not interesting - //if ( def.isFlag ) attributes.add("flag"); - if ( def.isHidden ) attributes.add("hidden"); if ( source.isDeprecated() ) attributes.add("depreciated"); if ( attributes.size() > 0 ) root.put("attributes", Utils.join(", ", attributes)); - if ( def.validOptions != null ) { - root.put("options", docForEnumArgument(source.field.getType())); - } - return root; } + /** + * Helper routine that provides a FreeMarker map for an enumClass, grabbing the + * values of the enum and their associated javadoc documentation. + * @param enumClass + * @return + */ @Requires("enumClass.isEnum()") private List> docForEnumArgument(Class enumClass) { - ClassDoc doc = GATKDoclet.getClassDocForClass(rootDoc, enumClass); + ClassDoc doc = this.getDoclet().getClassDocForClass(enumClass); if ( doc == null ) // || ! doc.isEnum() ) throw new RuntimeException("Tried to get docs for enum " + enumClass + " but got instead: " + doc); @@ -537,5 +575,4 @@ public class GenericDocumentationHandler extends DocumentedGATKFeatureHandler { return bindings; } - } diff --git a/public/java/src/org/broadinstitute/sting/utils/help/HelpUtils.java b/public/java/src/org/broadinstitute/sting/utils/help/HelpUtils.java index d72d2e83c..645ab34c1 100644 --- a/public/java/src/org/broadinstitute/sting/utils/help/HelpUtils.java +++ b/public/java/src/org/broadinstitute/sting/utils/help/HelpUtils.java @@ -32,14 +32,6 @@ import org.broadinstitute.sting.utils.classloader.JVMUtils; import java.lang.reflect.Field; public class HelpUtils { - - protected static boolean implementsInterface(ProgramElementDoc classDoc, Class... interfaceClasses) { - for (Class interfaceClass : interfaceClasses) - if (assignableToClass(classDoc, interfaceClass, false)) - return true; - return false; - } - protected static boolean assignableToClass(ProgramElementDoc classDoc, Class lhsClass, boolean requireConcrete) { try { Class type = getClassForDoc(classDoc); From ff018c796423184d75cdbb11ff3fc90e9b3af67b Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Fri, 19 Aug 2011 16:55:56 -0400 Subject: [PATCH 110/138] Swapped argument order but not MD5 order --- .../sting/gatk/walkers/ClipReadsWalkersIntegrationTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/java/test/org/broadinstitute/sting/gatk/walkers/ClipReadsWalkersIntegrationTest.java b/public/java/test/org/broadinstitute/sting/gatk/walkers/ClipReadsWalkersIntegrationTest.java index ca3d1ee25..1565c419b 100755 --- a/public/java/test/org/broadinstitute/sting/gatk/walkers/ClipReadsWalkersIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/gatk/walkers/ClipReadsWalkersIntegrationTest.java @@ -74,7 +74,7 @@ public class ClipReadsWalkersIntegrationTest extends WalkerTest { " -OQ -QT 4 -CR WRITE_Q0S" + " -o %s -os %s", 2, - Arrays.asList("55c01ccc2e84481b22d3632cdb06c8ba", "22db22749f811d30216215e047461621")); + Arrays.asList("22db22749f811d30216215e047461621", "55c01ccc2e84481b22d3632cdb06c8ba")); executeTest("clipOriginalQuals", spec); } } From ddb5045e1406bd7bd4d0da97b7c0bf5525ca4997 Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Fri, 19 Aug 2011 19:29:51 -0400 Subject: [PATCH 111/138] Updating the methods development calling pipeline for the new rod binding syntax and the new best practices. --- .../VariantDataManager.java | 11 +- .../VariantRecalibrator.java | 9 +- .../MethodsDevelopmentCallingPipeline.scala | 109 +++++++++--------- 3 files changed, 74 insertions(+), 55 deletions(-) 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 cb4d94332..429becfc7 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 @@ -233,13 +233,15 @@ public class VariantDataManager { } 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> training, final List> truth, final List> known, final List> badSites, final List> resource) { 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) ) { if( isValidVariant( evalVC, trainVC, TRUST_ALL_POLYMORPHIC ) ) { @@ -264,6 +266,13 @@ public class VariantDataManager { } } } + 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 ) { 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 517c2362a..d34a13427 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 @@ -138,6 +138,12 @@ public class VariantRecalibrator extends RodWalker> 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. + */ + @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(); + ///////////////////////////// // Outputs ///////////////////////////// @@ -226,6 +232,7 @@ public class VariantRecalibrator extends RodWalker rod : allInputBindings ) { try { rodToPriorMap.put(rod.getName(), (rod.getTags().containsKey("prior") ? Double.parseDouble(rod.getTags().getValue("prior")) : 0.0) ); @@ -263,7 +270,7 @@ public class VariantRecalibrator extends RodWalker new Target("NA12878.HiSeq", 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.07, 99.0, !lowPass), - "HiSeq19" -> new Target("NA12878.HiSeq19", hg19, dbSNP_b37_129, hapmap_b37, indelMask_b37, + "/humgen/1kg/processing/pipeline_test_bams/whole_genome_chunked.hg18.intervals", 2.17, 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.hg19.intervals", 2.3, 99.0, !lowPass), - "GA2hg19" -> new Target("NA12878.GA2.hg19", hg19, dbSNP_b37_129, hapmap_b37, indelMask_b37, + "/humgen/1kg/processing/pipeline_test_bams/whole_genome_chunked.hg19.intervals", 2.17, 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.3, 99.0, !lowPass), + "/humgen/1kg/processing/pipeline_test_bams/whole_genome_chunked.hg19.intervals", 2.17, 99.0, !lowPass, !exome, 1), "WEx" -> new Target("NA12878.WEx", 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", 2.6, 97.0, !lowPass), - "WExTrio" -> new Target("CEUTrio.WEx", hg19, dbSNP_b37_129, hapmap_b37, indelMask_b37, + "/seq/references/HybSelOligos/whole_exome_agilent_1.1_refseq_plus_3_boosters/whole_exome_agilent_1.1_refseq_plus_3_boosters.targets.interval_list", 2.6, 97.0, !lowPass, exome, 1), + "WExTrio" -> new Target("CEUTrio.WEx", 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", 2.6, 97.0, !lowPass), - "WGSTrio" -> new Target("CEUTrio.WGS", hg19, dbSNP_b37_129, hapmap_b37, indelMask_b37, + "/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", 2.6, 97.0, !lowPass, exome, 3), + "WGSTrio" -> new Target("CEUTrio.WGS", 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), + "/humgen/1kg/processing/pipeline_test_bams/whole_genome_chunked.hg19.intervals", 2.3, 99.0, !lowPass, !exome, 3), "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 ** - "/humgen/1kg/processing/pipeline_test_bams/whole_genome_chunked.chr20.hg19.intervals", 2.3, 99.0, lowPass), + "/humgen/1kg/processing/pipeline_test_bams/whole_genome_chunked.chr20.hg19.intervals", 2.3, 99.0, lowPass, !exome, 79), "TGPWExGdA" -> new Target("1000G.WEx.GdA", b37, dbSNP_b37, hapmap_b37, indelMask_b37, new File("/humgen/1kg/processing/pipeline_test_bams/Barcoded_1000G_WEx_Reduced_Plate_1.cleaned.list"), // BUGBUG: reduce from 60 to 20 people new File("/humgen/gsa-scr1/delangel/NewUG/calls/AugustRelease.filtered_Q50_QD5.0_SB0.0.allSamples.SNPs_hg19.WEx_UG_newUG_MQC.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", 2.6, 99.0, !lowPass), + "/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", 2.6, 99.0, !lowPass, exome, 96), "LowPassN60" -> new Target("lowpass.N60", b36, dbSNP_b36, hapmap_b36, indelMask_b36, new File("/humgen/1kg/analysis/bamsForDataProcessingPapers/lowpass_b36/lowpass.chr20.cleaned.matefixed.bam"), // the bam list to call from new File("/home/radon01/depristo/work/oneOffProjects/VQSRCutByNRS/lowpass.N60.chr20.filtered.vcf"), // the gold standard VCF file to run through the VQSR - "/humgen/1kg/processing/pipeline_test_bams/whole_genome_chunked.chr20.b36.intervals", 2.3, 99.0, lowPass), // chunked interval list to use with Queue's scatter/gather functionality + "/humgen/1kg/processing/pipeline_test_bams/whole_genome_chunked.chr20.b36.intervals", 2.3, 99.0, lowPass, !exome, 60), // chunked interval list to use with Queue's scatter/gather functionality "LowPassEUR363Nov" -> new Target("EUR.nov2010", b37, dbSNP_b37, hapmap_b37, indelMask_b37, new File("/humgen/1kg/processing/pipeline_test_bams/EUR.363sample.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 ** - "/humgen/1kg/processing/pipeline_test_bams/whole_genome_chunked.chr20.hg19.intervals", 2.3, 99.0, lowPass) + "/humgen/1kg/processing/pipeline_test_bams/whole_genome_chunked.chr20.hg19.intervals", 2.3, 99.0, lowPass, !exome, 363) ) @@ -187,22 +194,18 @@ class MethodsDevelopmentCallingPipeline extends QScript { } def bai(bam: File) = new File(bam + ".bai") - val FiltersToIgnore = List("DPFilter", "ABFilter", "ESPStandard", "QualByDepth", "StrandBias", "HomopolymerRun") // 1.) Unified Genotyper Base class GenotyperBase (t: Target) extends UnifiedGenotyper with UNIVERSAL_GATK_ARGS { this.memoryLimit = 3 this.reference_sequence = t.reference this.intervalsString ++= List(t.intervals) - this.scatterCount = 63 // the smallest interval list has 63 intervals, one for each Mb on chr20 + this.scatterCount = 120 this.dcov = if ( t.isLowpass ) { 50 } else { 250 } this.stand_call_conf = if ( t.isLowpass ) { 4.0 } else { 30.0 } this.stand_emit_conf = if ( t.isLowpass ) { 4.0 } else { 30.0 } this.input_file :+= t.bamList - if (t.dbsnpFile.endsWith(".rod")) - this.DBSNP = new File(t.dbsnpFile) - else if (t.dbsnpFile.endsWith(".vcf")) - this.rodBind :+= RodBind("dbsnp", "VCF", t.dbsnpFile) + this.D = new File(t.dbsnpFile) } // 1a.) Call SNPs with UG @@ -216,7 +219,6 @@ class MethodsDevelopmentCallingPipeline extends QScript { this.baq = if (noBAQ) {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" - this.A ++= List("FisherStrand") } // 1b.) Call Indels with UG @@ -234,15 +236,14 @@ class MethodsDevelopmentCallingPipeline extends QScript { this.reference_sequence = t.reference this.intervalsString ++= List(t.intervals) this.scatterCount = 10 - this.filterName ++= List("HARD_TO_VALIDATE") - this.filterExpression ++= List("\"MQ0 >= 4 && (MQ0 / (1.0 * DP)) > 0.1\"") - this.variantVCF = t.rawIndelVCF + this.V = t.rawIndelVCF this.out = t.filteredIndelVCF - this.filterName ++= List("LowQual", "StrandBias", "QualByDepth", "HomopolymerRun") - if (t.isLowpass) - this.filterExpression ++= List("\"QUAL<30.0\"", "\"SB>=-1.0\"", "\"QD<1.0\"", "\"HRun>=15\"") - else - this.filterExpression ++= List("\"QUAL<50.0\"", "\"SB>=-1.0\"", "\"QD<5.0\"", "\"HRun>=15\"") + this.filterName ++= List("IndelQD", "IndelReadPosRankSum", "IndelFS") + this.filterExpression ++= List("\"QD < 2.0\"", "\"ReadPosRankSum < -20.0\"", "\"FS > 200.0\"") + if (t.nSamples >= 10) { + this.filterName ++= List("IndelInbreedingCoeff") + this.filterExpression ++= List("\"InbreedingCoeff < -0.8\"") + } this.analysisName = t.name + "_VF" this.jobName = queueLogDir + t.name + ".indelfilter" } @@ -252,17 +253,22 @@ class MethodsDevelopmentCallingPipeline extends QScript { this.memoryLimit = 4 this.reference_sequence = t.reference this.intervalsString ++= List(t.intervals) - this.rodBind :+= RodBind("input", "VCF", if ( goldStandard ) { t.goldStandard_VCF } else { t.rawVCF } ) - this.rodBind :+= RodBind("hapmap", "VCF", t.hapmapFile, "known=false,training=true,truth=true,prior=15.0") - if( t.hapmapFile.contains("b37") ) - this.rodBind :+= RodBind("omni", "VCF", omni_b37, "known=false,training=true,truth=true,prior=12.0") - else if( t.hapmapFile.contains("b36") ) - this.rodBind :+= RodBind("omni", "VCF", omni_b36, "known=false,training=true,truth=true,prior=12.0") - if (t.dbsnpFile.endsWith(".rod")) - this.rodBind :+= RodBind("dbsnp", "DBSNP", t.dbsnpFile, "known=true,training=false,truth=false,prior=10.0") - else if (t.dbsnpFile.endsWith(".vcf")) - this.rodBind :+= RodBind("dbsnp", "VCF", t.dbsnpFile, "known=true,training=false,truth=false,prior=10.0") - this.use_annotation ++= List("QD", "HaplotypeScore", "MQRankSum", "ReadPosRankSum", "HRun", "FS") + this.input :+= ( if ( goldStandard ) { t.goldStandard_VCF } else { t.rawVCF } ) + this.training :+= new TaggedFile( t.hapmapFile, "prior=15.0") + this.truth :+= new TaggedFile( t.hapmapFile, "prior=15.0") + this.training :+= new TaggedFile( omni_b37, "prior=12.0") + this.truth :+= new TaggedFile( omni_b37, "prior=12.0") + this.training :+= new TaggedFile( training_1000G, "prior=10.0" ) + this.badSites :+= new TaggedFile( badSites_1000G, "prior=2.0" ) + this.known :+= new TaggedFile( t.dbsnpFile, "prior=2.0" ) + this.resource :+= new TaggedFile( projectConsensus_1000G, "prior=10.0" ) + this.use_annotation ++= List("QD", "HaplotypeScore", "MQRankSum", "ReadPosRankSum", "MQ", "FS") + if(t.nSamples >= 10) { + this.use_annotation ++= List("InbreedingCoeff") + } + if(!t.isExome) { + this.use_annotation ++= List("DP") + } this.tranches_file = if ( goldStandard ) { t.goldStandardTranchesFile } else { t.tranchesFile } this.recal_file = if ( goldStandard ) { t.goldStandardRecalFile } else { t.recalFile } this.allPoly = true @@ -277,7 +283,7 @@ class MethodsDevelopmentCallingPipeline extends QScript { this.memoryLimit = 4 this.reference_sequence = t.reference this.intervalsString ++= List(t.intervals) - this.rodBind :+= RodBind("input", "VCF", if ( goldStandard ) { t.goldStandard_VCF } else { t.rawVCF } ) + this.input :+= ( if ( goldStandard ) { t.goldStandard_VCF } else { t.rawVCF } ) this.tranches_file = if ( goldStandard ) { t.goldStandardTranchesFile } else { t.tranchesFile} this.recal_file = if ( goldStandard ) { t.goldStandardRecalFile } else { t.recalFile } this.ts_filter_level = t.trancheTarget @@ -290,19 +296,16 @@ class MethodsDevelopmentCallingPipeline extends QScript { class EvalBase(t: Target) extends VariantEval with UNIVERSAL_GATK_ARGS { this.memoryLimit = 3 this.reference_sequence = t.reference - this.rodBind :+= RodBind("comphapmap", "VCF", t.hapmapFile) + this.comp :+= new TaggedFile(t.hapmapFile, "hapmap" ) this.intervalsString ++= List(t.intervals) - if (t.dbsnpFile.endsWith(".rod")) - this.DBSNP = new File(t.dbsnpFile) - else if (t.dbsnpFile.endsWith(".vcf")) - this.rodBind :+= RodBind("dbsnp", "VCF", t.dbsnpFile) + this.D = new File(t.dbsnpFile) this.sample = samples } // 5a.) SNP Evaluation (OPTIONAL) based on the cut vcf class snpEvaluation(t: Target) extends EvalBase(t) { - if (t.reference == b37 || t.reference == hg19) this.rodBind :+= RodBind("compomni", "VCF", omni_b37) - this.rodBind :+= RodBind("eval", "VCF", t.recalibratedVCF ) + if (t.reference == b37 || t.reference == hg19) this.comp :+= new TaggedFile( omni_b37, "omni" ) + this.eval :+= t.recalibratedVCF this.out = t.evalFile this.analysisName = t.name + "_VEs" this.jobName = queueLogDir + t.name + ".snp.eval" @@ -310,7 +313,7 @@ class MethodsDevelopmentCallingPipeline extends QScript { // 5b.) Indel Evaluation (OPTIONAL) class indelEvaluation(t: Target) extends EvalBase(t) { - this.rodBind :+= RodBind("eval", "VCF", t.filteredIndelVCF) + this.eval :+= t.filteredIndelVCF this.evalModule :+= "IndelStatistics" this.out = t.evalIndelFile this.analysisName = t.name + "_VEi" From 539e157ecddee3a5d6a5729e3eb0d55b97404ece Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Sat, 20 Aug 2011 11:28:48 -0400 Subject: [PATCH 113/138] Fixing misc parameters in MDCP. The pipeline now does VariantEval of output by default. Fix for NaN vqslod values in VQSR --- .../GaussianMixtureModel.java | 34 ++++++++++++-- .../MethodsDevelopmentCallingPipeline.scala | 46 ++++++------------- 2 files changed, 44 insertions(+), 36 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/GaussianMixtureModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/GaussianMixtureModel.java index 17461de2f..5e7bf3575 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/GaussianMixtureModel.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/GaussianMixtureModel.java @@ -44,6 +44,7 @@ import java.util.List; public class GaussianMixtureModel { protected final static Logger logger = Logger.getLogger(GaussianMixtureModel.class); + public final static double MIN_ACCEPTABLE_LOD_SCORE = -20000.0; private final ArrayList gaussians; private final double shrinkage; @@ -207,14 +208,23 @@ public class GaussianMixtureModel { for( final boolean isNull : datum.isNull ) { if( isNull ) { return evaluateDatumMarginalized( datum ); } } + // Fill an array with the log10 probability coming from each Gaussian and then use MathUtils to sum them up correctly final double[] pVarInGaussianLog10 = new double[gaussians.size()]; int gaussianIndex = 0; for( final MultivariateGaussian gaussian : gaussians ) { pVarInGaussianLog10[gaussianIndex++] = gaussian.pMixtureLog10 + gaussian.evaluateDatumLog10( datum ); } - return MathUtils.log10sumLog10(pVarInGaussianLog10); // Sum(pi_k * p(v|n,k)) + double lod = MathUtils.log10sumLog10(pVarInGaussianLog10); // Sum(pi_k * p(v|n,k)) + + // Negative infinity lod values are possible when covariates are extremely far away from their tight Gaussians + // Cap the values at an extremely negative value and spread them out randomly + if( lod < MIN_ACCEPTABLE_LOD_SCORE ) { + lod = MIN_ACCEPTABLE_LOD_SCORE - GenomeAnalysisEngine.getRandomGenerator().nextDouble() * MIN_ACCEPTABLE_LOD_SCORE; + } + return lod; } + // Used only to decide which covariate dimension is most divergent in order to report in the culprit info field annotation public Double evaluateDatumInOneDimension( final VariantDatum datum, final int iii ) { if(datum.isNull[iii]) { return null; } @@ -225,11 +235,18 @@ public class GaussianMixtureModel { normal.setState( gaussian.mu[iii], gaussian.sigma.get(iii, iii) ); pVarInGaussianLog10[gaussianIndex++] = gaussian.pMixtureLog10 + Math.log10( normal.pdf( datum.annotations[iii] ) ); } - return MathUtils.log10sumLog10(pVarInGaussianLog10); // Sum(pi_k * p(v|n,k)) + double lod = MathUtils.log10sumLog10(pVarInGaussianLog10); // Sum(pi_k * p(v|n,k)) + + // Negative infinity lod values are possible when covariates are extremely far away from their tight Gaussians + // Cap the values at an extremely negative value and spread them out randomly + if( lod < MIN_ACCEPTABLE_LOD_SCORE ) { + lod = MIN_ACCEPTABLE_LOD_SCORE - GenomeAnalysisEngine.getRandomGenerator().nextDouble() * MIN_ACCEPTABLE_LOD_SCORE; + } + return lod; } public double evaluateDatumMarginalized( final VariantDatum datum ) { - int numSamples = 0; + int numRandomDraws = 0; double sumPVarInGaussian = 0.0; final int numIterPerMissingAnnotation = 10; // Trade off here between speed of computation and accuracy of the marginalization final double[] pVarInGaussianLog10 = new double[gaussians.size()]; @@ -248,10 +265,17 @@ public class GaussianMixtureModel { // add this sample's probability to the pile in order to take an average in the end sumPVarInGaussian += Math.pow(10.0, MathUtils.log10sumLog10(pVarInGaussianLog10)); // p = 10 ^ Sum(pi_k * p(v|n,k)) - numSamples++; + numRandomDraws++; } } } - return Math.log10( sumPVarInGaussian / ((double) numSamples) ); + double lod = Math.log10( sumPVarInGaussian / ((double) numRandomDraws) ); + + // Negative infinity lod values are possible when covariates are extremely far away from their tight Gaussians + // Cap the values at an extremely negative value and spread them out randomly + if( lod < MIN_ACCEPTABLE_LOD_SCORE ) { + lod = MIN_ACCEPTABLE_LOD_SCORE - GenomeAnalysisEngine.getRandomGenerator().nextDouble() * MIN_ACCEPTABLE_LOD_SCORE; + } + return lod; } } \ No newline at end of file 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 e4385a282..a28f6f949 100755 --- a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala +++ b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala @@ -5,17 +5,6 @@ import org.broadinstitute.sting.queue.extensions.gatk._ import org.broadinstitute.sting.queue.QScript import org.broadinstitute.sting.gatk.phonehome.GATKRunReport - - // ToDos: - // reduce the scope of the datasets so the script is more nimble - // create gold standard BAQ'd bam files, no reason to always do it on the fly - - // Analysis to add at the end of the script: - // auto generation of the cluster plots - // spike in NA12878 to the exomes and to the lowpass, analysis of how much of her variants are being recovered compared to single sample exome or HiSeq calls - // produce Kiran's Venn plots based on comparison between new VCF and gold standard produced VCF - - class MethodsDevelopmentCallingPipeline extends QScript { qscript => @@ -28,15 +17,12 @@ class MethodsDevelopmentCallingPipeline extends QScript { @Argument(shortName="dataset", doc="selects the datasets to run. If not provided, all datasets will be used", required=false) var datasets: List[String] = Nil - @Argument(shortName="skipGoldStandard", doc="doesn't run the pipeline with the goldstandard VCF files for comparison", required=false) - var skipGoldStandard: Boolean = false + @Argument(shortName="runGoldStandard", doc="run the pipeline with the goldstandard VCF files for comparison", required=false) + var runGoldStandard: Boolean = false @Argument(shortName="noBAQ", doc="turns off BAQ calculation", required=false) var noBAQ: Boolean = false - @Argument(shortName="eval", doc="adds the VariantEval walker to the pipeline", required=false) - var eval: Boolean = false - @Argument(shortName="indels", doc="calls indels with the Unified Genotyper", required=false) var callIndels: Boolean = false @@ -52,8 +38,6 @@ class MethodsDevelopmentCallingPipeline extends QScript { @Argument(shortName="sample", doc="Samples to include in Variant Eval", required=false) var samples: List[String] = Nil - - class Target( val baseName: String, val reference: File, @@ -118,15 +102,15 @@ class MethodsDevelopmentCallingPipeline extends QScript { "/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.17, 99.0, !lowPass, !exome, 1), + "/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.hg19.intervals", 2.17, 99.0, !lowPass, !exome, 1), + "/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.17, 99.0, !lowPass, !exome, 1), + "/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, "/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"), @@ -177,9 +161,9 @@ class MethodsDevelopmentCallingPipeline extends QScript { add(new snpCall(target)) add(new VQSR(target, !goldStandard)) add(new applyVQSR(target, !goldStandard)) - if (eval) add(new snpEvaluation(target)) + add(new snpEvaluation(target)) } - if ( !skipGoldStandard ) { + if ( runGoldStandard ) { add(new VQSR(target, goldStandard)) add(new applyVQSR(target, goldStandard)) } @@ -200,7 +184,8 @@ class MethodsDevelopmentCallingPipeline extends QScript { this.memoryLimit = 3 this.reference_sequence = t.reference this.intervalsString ++= List(t.intervals) - this.scatterCount = 120 + this.scatterCount = 140 + this.nt = 2 this.dcov = if ( t.isLowpass ) { 50 } else { 250 } this.stand_call_conf = if ( t.isLowpass ) { 4.0 } else { 30.0 } this.stand_emit_conf = if ( t.isLowpass ) { 4.0 } else { 30.0 } @@ -259,9 +244,8 @@ class MethodsDevelopmentCallingPipeline extends QScript { this.training :+= new TaggedFile( omni_b37, "prior=12.0") this.truth :+= new TaggedFile( omni_b37, "prior=12.0") this.training :+= new TaggedFile( training_1000G, "prior=10.0" ) - this.badSites :+= new TaggedFile( badSites_1000G, "prior=2.0" ) this.known :+= new TaggedFile( t.dbsnpFile, "prior=2.0" ) - this.resource :+= new TaggedFile( projectConsensus_1000G, "prior=10.0" ) + this.resource :+= new TaggedFile( projectConsensus_1000G, "prior=8.0" ) this.use_annotation ++= List("QD", "HaplotypeScore", "MQRankSum", "ReadPosRankSum", "MQ", "FS") if(t.nSamples >= 10) { this.use_annotation ++= List("InbreedingCoeff") @@ -275,12 +259,12 @@ class MethodsDevelopmentCallingPipeline extends QScript { this.tranche ++= List("100.0", "99.9", "99.5", "99.3", "99.0", "98.9", "98.8", "98.5", "98.4", "98.3", "98.2", "98.1", "98.0", "97.9", "97.8", "97.5", "97.0", "95.0", "90.0") this.rscript_file = t.vqsrRscript this.analysisName = t.name + "_VQSR" - this.jobName = queueLogDir + t.name + ".VQSR" + this.jobName = queueLogDir + t.name + ".VQSR" } // 4.) Apply the recalibration table to the appropriate tranches class applyVQSR (t: Target, goldStandard: Boolean) extends ApplyRecalibration with UNIVERSAL_GATK_ARGS { - this.memoryLimit = 4 + this.memoryLimit = 6 this.reference_sequence = t.reference this.intervalsString ++= List(t.intervals) this.input :+= ( if ( goldStandard ) { t.goldStandard_VCF } else { t.rawVCF } ) @@ -289,7 +273,7 @@ class MethodsDevelopmentCallingPipeline extends QScript { this.ts_filter_level = t.trancheTarget this.out = t.recalibratedVCF this.analysisName = t.name + "_AVQSR" - this.jobName = queueLogDir + t.name + ".applyVQSR" + this.jobName = queueLogDir + t.name + ".applyVQSR" } // 5.) Variant Evaluation Base(OPTIONAL) @@ -308,7 +292,7 @@ class MethodsDevelopmentCallingPipeline extends QScript { this.eval :+= t.recalibratedVCF this.out = t.evalFile this.analysisName = t.name + "_VEs" - this.jobName = queueLogDir + t.name + ".snp.eval" + this.jobName = queueLogDir + t.name + ".snp.eval" } // 5b.) Indel Evaluation (OPTIONAL) @@ -317,6 +301,6 @@ class MethodsDevelopmentCallingPipeline extends QScript { this.evalModule :+= "IndelStatistics" this.out = t.evalIndelFile this.analysisName = t.name + "_VEi" - this.jobName = queueLogDir + queueLogDir + t.name + ".indel.eval" + this.jobName = queueLogDir + queueLogDir + t.name + ".indel.eval" } } From 782453235a808acf04ea37298a2e6ebd911c6f9a Mon Sep 17 00:00:00 2001 From: Guillermo del Angel Date: Sat, 20 Aug 2011 12:24:22 -0400 Subject: [PATCH 114/138] Updated VariantEvalIntegrationTest since there's a new column separating nMixed and nComplex in CountVariants Misc updates to WholeGenomeIndelCalling.scala Bug fix in VariantEval (may be temporary, need more investigation): if -disc option is used in sites-only vcf's then a null pointer exception is produced, caused by recent introduction of -xl_sf options. --- .../walkers/variantutils/SelectVariants.java | 12 ++++---- .../VariantEvalIntegrationTest.java | 30 +++++++++---------- 2 files changed, 22 insertions(+), 20 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 f6b6a8d65..93bc9e518 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 @@ -344,11 +344,13 @@ public class SelectVariants extends RodWalker { } // now, exclude any requested samples - Collection XLsamplesFromFile = SampleUtils.getSamplesFromFiles(XLsampleFiles); - samples.removeAll(XLsamplesFromFile); - samples.removeAll(XLsampleNames); - - if ( samples.size() == 0 ) + if (XLsampleFiles != null) + if(!XLsampleFiles.isEmpty()) { + Collection XLsamplesFromFile = SampleUtils.getSamplesFromFiles(XLsampleFiles); + samples.removeAll(XLsamplesFromFile); + samples.removeAll(XLsampleNames); + } + if ( samples.size() == 0 && !NO_SAMPLES_SPECIFIED ) throw new UserException("All samples requested to be included were also requested to be excluded."); for ( String sample : samples ) 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 1de9a72d8..3503a2353 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 @@ -29,7 +29,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { "-o %s" ), 1, - Arrays.asList("bced1842c78fbabb089dd12b7087050d") + Arrays.asList("1fefd6cf9c2554d5f886c3998defd4d0") ); executeTest("testFundamentalsCountVariantsSNPsandIndels", spec); } @@ -50,7 +50,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { "-o %s" ), 1, - Arrays.asList("06510bd37ffaa39e817ca0dcaf8f8ac2") + Arrays.asList("d470e00a368b5a0468012818994c6a89") ); executeTest("testFundamentalsCountVariantsSNPsandIndelsWithNovelty", spec); } @@ -72,7 +72,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { "-o %s" ), 1, - Arrays.asList("19c5b1b6396921c5b1059a2849ae4fcc") + Arrays.asList("12856e52c2682328f91594089328596c") ); executeTest("testFundamentalsCountVariantsSNPsandIndelsWithNoveltyAndFilter", spec); } @@ -93,7 +93,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { "-o %s" ), 1, - Arrays.asList("a71f8d81cf166cd97ac628092650964a") + Arrays.asList("91610b9240f64e0eb03cfd2602cf57af") ); executeTest("testFundamentalsCountVariantsSNPsandIndelsWithCpG", spec); } @@ -114,7 +114,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { "-o %s" ), 1, - Arrays.asList("4dabe0658232f6174188515db6dfe112") + Arrays.asList("e40b77e7ed6581328e373a24b93cd170") ); executeTest("testFundamentalsCountVariantsSNPsandIndelsWithFunctionalClass", spec); } @@ -135,7 +135,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { "-o %s" ), 1, - Arrays.asList("3340587f10ceff83e5567ddfd1a9a60e") + Arrays.asList("15beaf3823c131cabc5fb0445239f978") ); executeTest("testFundamentalsCountVariantsSNPsandIndelsWithDegeneracy", spec); } @@ -156,7 +156,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { "-o %s" ), 1, - Arrays.asList("c730c7ee31c8138cef6efd8dd04fbbfc") + Arrays.asList("7ddd4ee74938d229ce5cb7b9b9104abe") ); executeTest("testFundamentalsCountVariantsSNPsandIndelsWithSample", spec); } @@ -179,7 +179,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { "-o %s" ), 1, - Arrays.asList("2559ca8f454b03e81561f6947f79df18") + Arrays.asList("a90f33906a732ef5eb346e559c96ccc1") ); executeTest("testFundamentalsCountVariantsSNPsandIndelsWithJexlExpression", spec); } @@ -204,7 +204,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { "-o %s" ), 1, - Arrays.asList("23aa5f97641d2fd033095f21c51d2f37") + Arrays.asList("2567f90d3d7354850c5a59730ecc6e4f") ); executeTest("testFundamentalsCountVariantsSNPsandIndelsWithMultipleJexlExpressions", spec); } @@ -223,7 +223,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { "-o %s" ), 1, - Arrays.asList("a69dd3f06903b3f374c6d6f010c653e0") + Arrays.asList("fa091aa8967893389c51102fd9f0bebb") ); executeTest("testFundamentalsCountVariantsNoCompRod", spec); } @@ -236,7 +236,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("125fe0a04b5d933cc14016598b2791cd")); + 1, Arrays.asList("2df4f8911ffc3c8d042298723ed465f8")); executeTestParallel("testSelect1", spec); } @@ -253,7 +253,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("d1932be3748fcf6da77dc51aec323710")); + WalkerTestSpec spec = new WalkerTestSpec(extraArgs,1,Arrays.asList("ed54aa127b173d8ad8b6482f2a929a42")); executeTestParallel("testCompVsEvalAC",spec); } @@ -283,7 +283,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("cbea5f9f8c046d4c014d261db352c43b")); + WalkerTestSpec spec = new WalkerTestSpec(extraArgs,1,Arrays.asList("18c44636e36d6657110bf984f8eac181")); executeTestParallel("testEvalTrackWithoutGenotypes",spec); } @@ -295,7 +295,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("d07a246963ae609643620c839b20cd1e")); + WalkerTestSpec spec = new WalkerTestSpec(extraArgs,1,Arrays.asList("1b8ae4fd10de0888bd843f833859d990")); executeTestParallel("testMultipleEvalTracksWithoutGenotypes",spec); } @@ -373,7 +373,7 @@ public class VariantEvalIntegrationTest extends WalkerTest { "-o %s" ), 1, - Arrays.asList("44464fe7c89a56cf128a932ef640f7da") + Arrays.asList("da65fc8f0d0eeaf0a0b06a07f444bb8e") ); executeTest("testAlleleCountStrat", spec); } From b0086768784789e0550bc58b0253d09cb2968d4a Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Sat, 20 Aug 2011 21:21:55 -0400 Subject: [PATCH 115/138] fixing the previous fix --- .../GaussianMixtureModel.java | 28 ++----------------- .../VariantRecalibrator.java | 2 +- .../VariantRecalibratorEngine.java | 9 +++++- .../MethodsDevelopmentCallingPipeline.scala | 1 + 4 files changed, 13 insertions(+), 27 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/GaussianMixtureModel.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/GaussianMixtureModel.java index 5e7bf3575..3fa9c3883 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/GaussianMixtureModel.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/GaussianMixtureModel.java @@ -44,7 +44,6 @@ import java.util.List; public class GaussianMixtureModel { protected final static Logger logger = Logger.getLogger(GaussianMixtureModel.class); - public final static double MIN_ACCEPTABLE_LOD_SCORE = -20000.0; private final ArrayList gaussians; private final double shrinkage; @@ -214,14 +213,7 @@ public class GaussianMixtureModel { for( final MultivariateGaussian gaussian : gaussians ) { pVarInGaussianLog10[gaussianIndex++] = gaussian.pMixtureLog10 + gaussian.evaluateDatumLog10( datum ); } - double lod = MathUtils.log10sumLog10(pVarInGaussianLog10); // Sum(pi_k * p(v|n,k)) - - // Negative infinity lod values are possible when covariates are extremely far away from their tight Gaussians - // Cap the values at an extremely negative value and spread them out randomly - if( lod < MIN_ACCEPTABLE_LOD_SCORE ) { - lod = MIN_ACCEPTABLE_LOD_SCORE - GenomeAnalysisEngine.getRandomGenerator().nextDouble() * MIN_ACCEPTABLE_LOD_SCORE; - } - return lod; + return MathUtils.log10sumLog10(pVarInGaussianLog10); // Sum(pi_k * p(v|n,k)) } // Used only to decide which covariate dimension is most divergent in order to report in the culprit info field annotation @@ -235,14 +227,7 @@ public class GaussianMixtureModel { normal.setState( gaussian.mu[iii], gaussian.sigma.get(iii, iii) ); pVarInGaussianLog10[gaussianIndex++] = gaussian.pMixtureLog10 + Math.log10( normal.pdf( datum.annotations[iii] ) ); } - double lod = MathUtils.log10sumLog10(pVarInGaussianLog10); // Sum(pi_k * p(v|n,k)) - - // Negative infinity lod values are possible when covariates are extremely far away from their tight Gaussians - // Cap the values at an extremely negative value and spread them out randomly - if( lod < MIN_ACCEPTABLE_LOD_SCORE ) { - lod = MIN_ACCEPTABLE_LOD_SCORE - GenomeAnalysisEngine.getRandomGenerator().nextDouble() * MIN_ACCEPTABLE_LOD_SCORE; - } - return lod; + return MathUtils.log10sumLog10(pVarInGaussianLog10); // Sum(pi_k * p(v|n,k)) } public double evaluateDatumMarginalized( final VariantDatum datum ) { @@ -269,13 +254,6 @@ public class GaussianMixtureModel { } } } - double lod = Math.log10( sumPVarInGaussian / ((double) numRandomDraws) ); - - // Negative infinity lod values are possible when covariates are extremely far away from their tight Gaussians - // Cap the values at an extremely negative value and spread them out randomly - if( lod < MIN_ACCEPTABLE_LOD_SCORE ) { - lod = MIN_ACCEPTABLE_LOD_SCORE - GenomeAnalysisEngine.getRandomGenerator().nextDouble() * MIN_ACCEPTABLE_LOD_SCORE; - } - return lod; + return Math.log10( sumPVarInGaussian / ((double) numRandomDraws) ); } } \ No newline at end of file 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 d34a13427..df4faebd1 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 @@ -272,7 +272,7 @@ public class VariantRecalibrator extends RodWalker Date: Sat, 20 Aug 2011 21:30:08 -0400 Subject: [PATCH 116/138] Fixing the recent SelectVariants fix --- .../gatk/walkers/variantutils/SelectVariants.java | 12 +++++------- 1 file changed, 5 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 93bc9e518..bfe7198cf 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 @@ -209,7 +209,7 @@ public class SelectVariants extends RodWalker { * Note that sample exclusion takes precedence over inclusion, so that if a sample is in both lists it will be excluded. */ @Argument(fullName="exclude_sample_file", shortName="xl_sf", doc="File containing a list of samples (one per line) to exclude. Can be specified multiple times", required=false) - public Set XLsampleFiles; + public Set XLsampleFiles = new HashSet(0); /** * Note that these expressions are evaluated *after* the specified samples are extracted and the INFO field annotations are updated. @@ -344,12 +344,10 @@ public class SelectVariants extends RodWalker { } // now, exclude any requested samples - if (XLsampleFiles != null) - if(!XLsampleFiles.isEmpty()) { - Collection XLsamplesFromFile = SampleUtils.getSamplesFromFiles(XLsampleFiles); - samples.removeAll(XLsamplesFromFile); - samples.removeAll(XLsampleNames); - } + Collection XLsamplesFromFile = SampleUtils.getSamplesFromFiles(XLsampleFiles); + samples.removeAll(XLsamplesFromFile); + samples.removeAll(XLsampleNames); + if ( samples.size() == 0 && !NO_SAMPLES_SPECIFIED ) throw new UserException("All samples requested to be included were also requested to be excluded."); From a8cbced71bdf9b0e4e97def7c7b5c26520865ab5 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Sat, 20 Aug 2011 22:49:51 -0400 Subject: [PATCH 117/138] Bug fix for Ryan: check for no context --- .../sting/gatk/walkers/varianteval/VariantEvalWalker.java | 3 ++- .../sting/gatk/walkers/varianteval/util/VariantEvalUtils.java | 4 ++-- 2 files changed, 4 insertions(+), 3 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 f6d42afb1..613a31ed3 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 @@ -311,7 +311,8 @@ public class VariantEvalWalker extends RodWalker implements Tr // for each comp track for ( final RodBinding compRod : comps ) { // no sample stratification for comps - final Set compSet = compVCs.get(compRod) == null ? new HashSet(0) : compVCs.get(compRod).values().iterator().next(); + final HashMap> compSetHash = compVCs.get(compRod); + final Set compSet = (compSetHash == null || compSetHash.size() == 0) ? new HashSet(0) : compVCs.get(compRod).values().iterator().next(); // find the comp final VariantContext comp = findMatchingComp(eval, compSet); 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 ed0e8d7f6..f31dd9f9f 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 @@ -347,9 +347,9 @@ public class VariantEvalUtils { } } } - - bindings.put(track, mapping); } + + bindings.put(track, mapping); } return bindings; From 22ca44c015382c67423824d4a1a12a4bb02aa9ea Mon Sep 17 00:00:00 2001 From: Khalid Shakir Date: Sun, 21 Aug 2011 02:34:20 -0400 Subject: [PATCH 120/138] Fixed Queue's tagging of RodBindings. Fixed argument definition names. --- .../gatk/ArgumentDefinitionField.java | 83 ++++++++++++------- 1 file changed, 55 insertions(+), 28 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/queue/extensions/gatk/ArgumentDefinitionField.java b/public/java/src/org/broadinstitute/sting/queue/extensions/gatk/ArgumentDefinitionField.java index c09c4037e..cb5bad4ae 100644 --- a/public/java/src/org/broadinstitute/sting/queue/extensions/gatk/ArgumentDefinitionField.java +++ b/public/java/src/org/broadinstitute/sting/queue/extensions/gatk/ArgumentDefinitionField.java @@ -144,6 +144,9 @@ public abstract class ArgumentDefinitionField extends ArgumentField { } else if ("input_file".equals(argumentDefinition.fullName) && argumentDefinition.ioType == ArgumentIOType.INPUT) { return Arrays.asList(new InputTaggedFileDefinitionField(argumentDefinition), new InputIndexesArgumentField(argumentDefinition, BAMIndex.BAMIndexSuffix, ".bam")); + } else if ((RodBinding.class.equals(argumentDefinition.argumentType) || RodBinding.class.equals(argumentDefinition.componentType)) && argumentDefinition.ioType == ArgumentIOType.INPUT) { + return Arrays.asList(new InputTaggedFileDefinitionField(argumentDefinition), new InputIndexesArgumentField(argumentDefinition, Tribble.STANDARD_INDEX_EXTENSION)); + } else if (argumentDefinition.ioType == ArgumentIOType.INPUT) { return Collections.singletonList(new InputArgumentField(argumentDefinition)); @@ -196,7 +199,7 @@ public abstract class ArgumentDefinitionField extends ArgumentField { } // if (intervalFields.contains(argumentDefinition.fullName) && argumentDefinition.ioType == ArgumentIOType.INPUT) - // Change intervals exclusize of intervalsString. + // Change intervals exclusive of intervalsString. private static class IntervalFileArgumentField extends InputArgumentField { public IntervalFileArgumentField(ArgumentDefinition argumentDefinition) { super(argumentDefinition); @@ -332,9 +335,7 @@ public abstract class ArgumentDefinitionField extends ArgumentField { } } - /** - * The other extreme of a NamedRodBindingField, allows the user to specify the track name, track type, and the file. - */ + // Allows the user to specify the track name, track type, and the file. public static class RodBindArgumentField extends ArgumentDefinitionField { public RodBindArgumentField(ArgumentDefinition argumentDefinition) { super(argumentDefinition); @@ -347,25 +348,28 @@ public abstract class ArgumentDefinitionField extends ArgumentField { } } - /** - * Named input_files. - */ + // Tagged input_files or other rods. public static class InputTaggedFileDefinitionField extends ArgumentDefinitionField { public InputTaggedFileDefinitionField(ArgumentDefinition argumentDefinition) { super(argumentDefinition); } @Override protected Class getInnerType() { return null; } // TaggedFile does not need to be imported. - @Override protected String getFieldType() { return "List[File]"; } - @Override protected String getDefaultValue() { return "Nil"; } + @Override protected String getFieldType() { return argumentDefinition.isMultiValued ? "List[File]" : "File"; } + @Override protected String getDefaultValue() { return argumentDefinition.isMultiValued ? "Nil" : "_"; } @Override protected String getCommandLineTemplate() { - return " + repeat(\"\", %3$s, format=TaggedFile.formatCommandLine(\"%1$s\"))"; + if (argumentDefinition.isMultiValued) { + return " + repeat(\"\", %3$s, format=TaggedFile.formatCommandLine(\"%1$s\"))"; + } else if (!argumentDefinition.required) { + return " + optional(\"\", %3$s, format=TaggedFile.formatCommandLine(\"%1$s\"))"; + } else { + return " + TaggedFile.formatCommandLine(\"%1$s\")(\"\", %3$s, \"\")"; + } } } - /** - * Adds optional inputs for the indexes of any bams or sams added to this function. - */ + // Adds optional inputs for the indexes of any rods added to this function. private static class InputIndexesArgumentField extends ArgumentField { + private final boolean originalIsMultiValued; private final String indexFieldName; private final String originalFieldName; private final String indexSuffix; @@ -374,14 +378,19 @@ public abstract class ArgumentDefinitionField extends ArgumentField { this(originalArgumentDefinition, indexSuffix, null); } public InputIndexesArgumentField(ArgumentDefinition originalArgumentDefinition, String indexSuffix, String originalSuffix) { - this.indexFieldName = originalArgumentDefinition.fullName + "Indexes"; + this.originalIsMultiValued = originalArgumentDefinition.isMultiValued; + this.indexFieldName = originalArgumentDefinition.fullName + "Index" + (originalIsMultiValued ? "es" : ""); this.originalFieldName = originalArgumentDefinition.fullName; this.indexSuffix = indexSuffix; this.originalSuffix = originalSuffix; } @Override protected Class getAnnotationIOClass() { return Input.class; } @Override public String getCommandLineAddition() { return ""; } - @Override protected String getDoc() { return "Dependencies on any indexes of " + this.originalFieldName; } + @Override protected String getDoc() { + return originalIsMultiValued + ? "Dependencies on any indexes of " + this.originalFieldName + : "Dependencies on the index of " + this.originalFieldName; + } @Override protected String getFullName() { return this.indexFieldName; } @Override protected boolean isRequired() { return false; } @Override protected String getFieldType() { return "List[File]"; } @@ -389,24 +398,41 @@ public abstract class ArgumentDefinitionField extends ArgumentField { @Override protected Class getInnerType() { return File.class; } @Override protected String getRawFieldName() { return this.indexFieldName; } @Override protected String getFreezeFields() { - if (originalSuffix == null) { - return String.format( - ("%1$s ++= %2$s" + - ".filter(orig => orig != null)" + - ".map(orig => new File(orig.getPath + \"%3$s\"))%n"), - indexFieldName, originalFieldName, indexSuffix); + if (originalIsMultiValued) { + if (originalSuffix == null) { + return String.format( + ("%1$s ++= %2$s" + + ".filter(orig => orig != null)" + + ".map(orig => new File(orig.getPath + \"%3$s\"))%n"), + indexFieldName, originalFieldName, indexSuffix); + } else { + return String.format( + ("%1$s ++= %2$s" + + ".filter(orig => orig != null && orig.getName.endsWith(\"%4$s\"))" + + ".flatMap(orig => Array(" + + " new File(orig.getPath + \"%3$s\")," + + " new File(orig.getPath.stripSuffix(\"%4$s\") + \"%3$s\") ))%n"), + indexFieldName, originalFieldName, indexSuffix, originalSuffix); + } } else { - return String.format( - ("%1$s ++= %2$s" + - ".filter(orig => orig != null && orig.getName.endsWith(\"%4$s\"))" + - ".flatMap(orig => Array(" + - " new File(orig.getPath + \"%3$s\")," + - " new File(orig.getPath.stripSuffix(\"%4$s\") + \"%3$s\") ))%n"), - indexFieldName, originalFieldName, indexSuffix, originalSuffix); + if (originalSuffix == null) { + return String.format( + ("if (%2$s != null)%n " + + "%1$s :+= new File(%2$s.getPath + \"%3$s\")%n"), + indexFieldName, originalFieldName, indexSuffix); + } else { + return String.format( + ("if (%2$s != null && %2$s.getName.endsWith(\"%4$s\"))%n " + + "%1$s ++= Array(" + + " new File(%2$s.getPath + \"%3$s\")," + + " new File(%2$s.getPath.stripSuffix(\"%4$s\") + \"%3$s\") )%n"), + indexFieldName, originalFieldName, indexSuffix, originalSuffix); + } } } } + // Tracks an automatically generated index private static abstract class OutputIndexArgumentField extends ArgumentField { protected final String indexFieldName; protected final String originalFieldName; @@ -456,6 +482,7 @@ public abstract class ArgumentDefinitionField extends ArgumentField { } } + // Allows setting the format for floats and doubles private static class FormatterArgumentField extends ArgumentField { private final ArgumentField argumentField; public FormatterArgumentField(ArgumentField argumentField) { From f93a554b012eea93a9e1e310fb2b8c3a5be39cb5 Mon Sep 17 00:00:00 2001 From: Ryan Poplin Date: Sun, 21 Aug 2011 10:25:36 -0400 Subject: [PATCH 121/138] updating exome specific parameters in MDCP --- .../variantrecalibration/VariantRecalibratorEngine.java | 6 +----- .../queue/qscripts/MethodsDevelopmentCallingPipeline.scala | 6 ++++-- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibratorEngine.java b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibratorEngine.java index 20f3a9fe1..adfb38a25 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibratorEngine.java +++ b/public/java/src/org/broadinstitute/sting/gatk/walkers/variantrecalibration/VariantRecalibratorEngine.java @@ -74,11 +74,7 @@ public class VariantRecalibratorEngine { for( final VariantDatum datum : data ) { final double thisLod = evaluateDatum( datum, model ); if( Double.isNaN(thisLod) ) { - if( evaluateContrastively ) { - throw new UserException("NaN LOD value assigned. Clustering with this few variants and these annotations is unsafe. Please consider raising the number of variants used to train the negative model (via --percentBadVariants 0.05, for example) or lowering the maximum number of Gaussians to use in the model (via --maxGaussians 4, for example)"); - } else { - throw new UserException("NaN LOD value assigned. Clustering with this few variants and these annotations is unsafe."); - } + throw new UserException("NaN LOD value assigned. Clustering with this few variants and these annotations is unsafe. Please consider raising the number of variants used to train the negative model (via --percentBadVariants 0.05, for example) or lowering the maximum number of Gaussians to use in the model (via --maxGaussians 4, for example)"); } datum.lod = ( evaluateContrastively ? 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 7c249c2bb..3c9a3fbcb 100755 --- a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala +++ b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/MethodsDevelopmentCallingPipeline.scala @@ -115,11 +115,11 @@ class MethodsDevelopmentCallingPipeline extends QScript { "/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", 2.6, 97.0, !lowPass, exome, 1), + "/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, 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", 2.6, 97.0, !lowPass, exome, 3), + "/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, 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 ** @@ -253,6 +253,8 @@ class MethodsDevelopmentCallingPipeline extends QScript { } if(!t.isExome) { this.use_annotation ++= List("DP") + } else { + this.mG = 6 } this.tranches_file = if ( goldStandard ) { t.goldStandardTranchesFile } else { t.tranchesFile } this.recal_file = if ( goldStandard ) { t.goldStandardRecalFile } else { t.recalFile } From 518b3dd291c4917fd23ee8e8cd9611c60ddd6475 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Mon, 22 Aug 2011 15:10:30 -0400 Subject: [PATCH 122/138] Don't let the genotypes map be null --- .../sting/utils/variantcontext/VariantContext.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 129ac9ca8..155a539c7 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java @@ -822,8 +822,10 @@ public class VariantContext implements Feature { // to enable tribble intergrati // --------------------------------------------------------------------------------------------------------- private void loadGenotypes() { - if ( !hasAttribute(UNPARSED_GENOTYPE_MAP_KEY) ) + if ( !hasAttribute(UNPARSED_GENOTYPE_MAP_KEY) ) { + genotypes = NO_GENOTYPES; return; + } Object parserObj = getAttribute(UNPARSED_GENOTYPE_PARSER_KEY); if ( parserObj == null || !(parserObj instanceof VCFParser) ) From 2c24b68a96560c2a32a0ad3403e21f18acca64c1 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Mon, 22 Aug 2011 15:11:21 -0400 Subject: [PATCH 123/138] Working implementation of DecodeLoc for VCF parsing. Makes indexing 3x faster. --- .../utils/codecs/vcf/AbstractVCFCodec.java | 58 +++++++++++++++---- 1 file changed, 48 insertions(+), 10 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 46242c302..a3100030e 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 @@ -154,9 +154,45 @@ public abstract class AbstractVCFCodec implements FeatureCodec, NameAwareCodec, * @return a feature, (not guaranteed complete) that has the correct start and stop */ public Feature decodeLoc(String line) { - return reallyDecode(line); + String[] locParts = new String[6]; + ParsingUtils.split(line, locParts, VCFConstants.FIELD_SEPARATOR_CHAR, true); + + // get our alleles (because the end position depends on them) + String ref = getCachedString(locParts[3].toUpperCase()); + String alts = getCachedString(locParts[4].toUpperCase()); + List alleles = parseAlleles(ref, alts, lineNo); + + // find out our location + int start = Integer.valueOf(locParts[1]); + int stop = start; + + // ref alleles don't need to be single bases for monomorphic sites + if ( alleles.size() == 1 ) { + stop = start + alleles.get(0).length() - 1; + } else if ( !isSingleNucleotideEvent(alleles) ) { + stop = clipAlleles(start, ref, alleles, null, lineNo); + } + + return new VCFLocFeature(locParts[0], start, stop); } + private final static class VCFLocFeature implements Feature { + + final String chr; + final int start, stop; + + private VCFLocFeature(String chr, int start, int stop) { + this.chr = chr; + this.start = start; + this.stop = stop; + } + + public String getChr() { return chr; } + public int getStart() { return start; } + public int getEnd() { return stop; } + } + + /** * decode the line into a feature (VariantContext) * @param line the line @@ -207,7 +243,7 @@ public abstract class AbstractVCFCodec implements FeatureCodec, NameAwareCodec, // parse out the required fields String contig = getCachedString(parts[0]); - long pos = Long.valueOf(parts[1]); + int pos = Integer.valueOf(parts[1]); String id = null; if ( parts[2].length() == 0 ) generateException("The VCF specification requires a valid ID field"); @@ -227,7 +263,7 @@ public abstract class AbstractVCFCodec implements FeatureCodec, NameAwareCodec, Map attributes = parseInfo(info, id); // find out our current location, and clip the alleles down to their minimum length - long loc = pos; + int loc = pos; // ref alleles don't need to be single bases for monomorphic sites if ( alleles.size() == 1 ) { loc = pos + alleles.get(0).length() - 1; @@ -506,9 +542,9 @@ public abstract class AbstractVCFCodec implements FeatureCodec, NameAwareCodec, * @param ref the reference string * @param unclippedAlleles the list of unclipped alleles * @param clippedAlleles output list of clipped alleles - * @return a list of alleles, clipped to the reference + * @return the new reference end position of this event */ - protected static long clipAlleles(long position, String ref, List unclippedAlleles, List clippedAlleles, int lineNo) { + protected static int clipAlleles(int position, String ref, List unclippedAlleles, List clippedAlleles, int lineNo) { // Note that the computation of forward clipping here is meant only to see whether there is a common // base to all alleles, and to correctly compute reverse clipping, @@ -534,11 +570,13 @@ public abstract class AbstractVCFCodec implements FeatureCodec, NameAwareCodec, if (clipping) reverseClipped++; } - for (Allele a : unclippedAlleles) { - if (a.isSymbolic()) { - clippedAlleles.add(a); - } else { - clippedAlleles.add(Allele.create(Arrays.copyOfRange(a.getBases(),0,a.getBases().length-reverseClipped),a.isReference())); + if ( clippedAlleles != null ) { + for ( Allele a : unclippedAlleles ) { + if ( a.isSymbolic() ) { + clippedAlleles.add(a); + } else { + clippedAlleles.add(Allele.create(Arrays.copyOfRange(a.getBases(),0,a.getBases().length-reverseClipped),a.isReference())); + } } } From c4c90c88264c85dc0297faa1998bd605eef6863a Mon Sep 17 00:00:00 2001 From: Khalid Shakir Date: Mon, 22 Aug 2011 15:12:55 -0400 Subject: [PATCH 125/138] Updates to JobRunners from the Queue developer community and from running the WholeGenomePipeline: - Ability to pass a different resident memory reservation and limits. Useful for large pileups of low pass genome data that sometimes need high -Xmx6g but usually don't exceed 2-3g in actual heap size. - Fixed jobPriority to work for all job runners. Now must be a integer between 0 and 100- even for GridEngine- and will be mapped to the correct values. - Passing parallel environment and job resource requests to LSF and GridEngine. Useful for passing tokens like iodine_io=1 and -pe pe_slots 8 - Refactored GridEngine JobRunner to also provide basic support for other job dispatchers with DRMAA implementations such as Torque/PBS. Should work for basic running but advanced users must pass their own jobNativeArgs from the command line or in customized QScripts until someone maps properties like jobQueue, jobPriority, residentRequest, etc. into a Torque/PBS/etc. dispatcher. --- .../sting/jna/drmaa/v1_0/JnaJobInfo.java | 100 +++ .../sting/jna/drmaa/v1_0/JnaJobTemplate.java | 315 ++++++++ .../sting/jna/drmaa/v1_0/JnaSession.java | 450 +++++++++++ .../jna/drmaa/v1_0/JnaSessionFactory.java | 39 + .../sting/jna/drmaa/v1_0/LibDrmaa.java | 754 ++++++++++++++++++ .../sting/jna/lsf/v7_0_6/LibBat.java | 97 ++- .../sting/jna/lsf/v7_0_6/LibLsf.java | 22 +- .../drmaa/v1_0/JnaSessionIntegrationTest.java | 145 ++++ .../drmaa/v1_0/LibDrmaaIntegrationTest.java | 236 ++++++ .../jna/lsf/v7_0_6/LibBatIntegrationTest.java | 9 +- .../sting/queue/QSettings.scala | 19 +- .../queue/engine/CommandLineJobRunner.scala | 17 +- .../sting/queue/engine/JobManager.scala | 6 +- .../sting/queue/engine/QGraph.scala | 35 +- .../queue/engine/drmaa/DrmaaJobManager.scala | 61 ++ .../queue/engine/drmaa/DrmaaJobRunner.scala | 149 ++++ .../gridengine/GridEngineJobManager.scala | 10 +- .../gridengine/GridEngineJobRunner.scala | 231 +----- .../queue/engine/lsf/Lsf706JobRunner.scala | 125 ++- .../queue/function/CommandLineFunction.scala | 52 +- .../sting/queue/pipeline/PipelineTest.scala | 13 +- .../queue/pipeline/PipelineTestSpec.scala | 5 +- .../examples/HelloWorldPipelineTest.scala | 77 +- 23 files changed, 2678 insertions(+), 289 deletions(-) create mode 100644 public/java/src/org/broadinstitute/sting/jna/drmaa/v1_0/JnaJobInfo.java create mode 100644 public/java/src/org/broadinstitute/sting/jna/drmaa/v1_0/JnaJobTemplate.java create mode 100644 public/java/src/org/broadinstitute/sting/jna/drmaa/v1_0/JnaSession.java create mode 100644 public/java/src/org/broadinstitute/sting/jna/drmaa/v1_0/JnaSessionFactory.java create mode 100644 public/java/src/org/broadinstitute/sting/jna/drmaa/v1_0/LibDrmaa.java create mode 100644 public/java/test/org/broadinstitute/sting/jna/drmaa/v1_0/JnaSessionIntegrationTest.java create mode 100644 public/java/test/org/broadinstitute/sting/jna/drmaa/v1_0/LibDrmaaIntegrationTest.java create mode 100644 public/scala/src/org/broadinstitute/sting/queue/engine/drmaa/DrmaaJobManager.scala create mode 100644 public/scala/src/org/broadinstitute/sting/queue/engine/drmaa/DrmaaJobRunner.scala diff --git a/public/java/src/org/broadinstitute/sting/jna/drmaa/v1_0/JnaJobInfo.java b/public/java/src/org/broadinstitute/sting/jna/drmaa/v1_0/JnaJobInfo.java new file mode 100644 index 000000000..3716d3110 --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/jna/drmaa/v1_0/JnaJobInfo.java @@ -0,0 +1,100 @@ +/* + * 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.jna.drmaa.v1_0; + +import org.ggf.drmaa.DrmaaException; +import org.ggf.drmaa.JobInfo; + +import java.util.Map; + +/** + * JNA mapping from Java to C DRMAA binding. + */ +public class JnaJobInfo implements JobInfo { + + private final String jobId; + private final Map rusage; + private final boolean hasExited; + private final int exitStatus; + private final boolean hasSignaled; + private final String terminatingSignal; + private final boolean hasCoreDump; + private final boolean wasAborted; + + public JnaJobInfo(String jobId, Map rusage, boolean hasExited, int exitStatus, boolean hasSignaled, String terminatingSignal, boolean hasCoreDump, boolean wasAborted) { + this.jobId = jobId; + this.rusage = rusage; + this.hasExited = hasExited; + this.exitStatus = exitStatus; + this.hasSignaled = hasSignaled; + this.terminatingSignal = terminatingSignal; + this.hasCoreDump = hasCoreDump; + this.wasAborted = wasAborted; + } + + @Override + public String getJobId() throws DrmaaException { + return this.jobId; + } + + @Override + public Map getResourceUsage() throws DrmaaException { + return rusage; + } + + @Override + public boolean hasExited() throws DrmaaException { + return hasExited; + } + + @Override + public int getExitStatus() throws DrmaaException { + if (!hasExited) + throw new IllegalStateException("job has not exited"); + return exitStatus; + } + + @Override + public boolean hasSignaled() throws DrmaaException { + return hasSignaled; + } + + @Override + public String getTerminatingSignal() throws DrmaaException { + if (!hasSignaled) + throw new IllegalStateException("job has not signaled"); + return terminatingSignal; + } + + @Override + public boolean hasCoreDump() throws DrmaaException { + return hasCoreDump; + } + + @Override + public boolean wasAborted() throws DrmaaException { + return wasAborted; + } +} diff --git a/public/java/src/org/broadinstitute/sting/jna/drmaa/v1_0/JnaJobTemplate.java b/public/java/src/org/broadinstitute/sting/jna/drmaa/v1_0/JnaJobTemplate.java new file mode 100644 index 000000000..58cd19926 --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/jna/drmaa/v1_0/JnaJobTemplate.java @@ -0,0 +1,315 @@ +/* + * 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.jna.drmaa.v1_0; + +import com.sun.jna.Pointer; +import org.ggf.drmaa.*; + +import java.util.*; + +/** + * JNA mapping from Java to C DRMAA binding. + */ +public class JnaJobTemplate implements JobTemplate { + private final JnaSession session; + private final Pointer jt; + + public JnaJobTemplate(JnaSession session, Pointer jt) { + this.session = session; + this.jt = jt; + } + + public Pointer getPointer() { + return jt; + } + + @Override + public void setRemoteCommand(String s) throws DrmaaException { + JnaSession.setAttribute(jt, LibDrmaa.DRMAA_REMOTE_COMMAND, s); + } + + @Override + public String getRemoteCommand() throws DrmaaException { + return JnaSession.getAttribute(jt, LibDrmaa.DRMAA_REMOTE_COMMAND); + } + + @SuppressWarnings("unchecked") + @Override + public void setArgs(List list) throws DrmaaException { + JnaSession.setVectorAttribute(jt, LibDrmaa.DRMAA_V_ARGV, list); + } + + @Override + public List getArgs() throws DrmaaException { + return JnaSession.getVectorAttribute(jt, LibDrmaa.DRMAA_V_ARGV); + } + + @Override + public void setJobSubmissionState(int state) throws DrmaaException { + String stateString; + if (state == JobTemplate.HOLD_STATE) + stateString = LibDrmaa.DRMAA_SUBMISSION_STATE_HOLD; + else if (state == JobTemplate.ACTIVE_STATE) + stateString = LibDrmaa.DRMAA_SUBMISSION_STATE_ACTIVE; + else + throw new InvalidAttributeValueException("jobSubmissionState attribute is invalid"); + JnaSession.setAttribute(jt, LibDrmaa.DRMAA_JS_STATE, stateString); + } + + @Override + public int getJobSubmissionState() throws DrmaaException { + int state; + String stateString = JnaSession.getAttribute(jt, LibDrmaa.DRMAA_JS_STATE); + if (LibDrmaa.DRMAA_SUBMISSION_STATE_HOLD.equals(stateString)) + state = JobTemplate.HOLD_STATE; + else if (LibDrmaa.DRMAA_SUBMISSION_STATE_ACTIVE.equals(stateString)) + state = JobTemplate.ACTIVE_STATE; + else + throw new InvalidAttributeValueException("jobSubmissionState attribute is invalid"); + return state; + } + + @SuppressWarnings("unchecked") + @Override + public void setJobEnvironment(Map env) throws DrmaaException { + JnaSession.setVectorAttribute(jt, LibDrmaa.DRMAA_V_ENV, JnaSession.mapToCollection(env)); + } + + @SuppressWarnings("unchecked") + @Override + public Map getJobEnvironment() throws DrmaaException { + return JnaSession.collectionToMap(JnaSession.getVectorAttribute(jt, LibDrmaa.DRMAA_V_ENV)); + } + + @Override + public void setWorkingDirectory(String s) throws DrmaaException { + JnaSession.setAttribute(jt, LibDrmaa.DRMAA_WD, s); + } + + @Override + public String getWorkingDirectory() throws DrmaaException { + return JnaSession.getAttribute(jt, LibDrmaa.DRMAA_WD); + } + + @Override + public void setJobCategory(String s) throws DrmaaException { + JnaSession.setAttribute(jt, LibDrmaa.DRMAA_JOB_CATEGORY, s); + } + + @Override + public String getJobCategory() throws DrmaaException { + return JnaSession.getAttribute(jt, LibDrmaa.DRMAA_JOB_CATEGORY); + } + + @Override + public void setNativeSpecification(String s) throws DrmaaException { + JnaSession.setAttribute(jt, LibDrmaa.DRMAA_NATIVE_SPECIFICATION, s); + } + + @Override + public String getNativeSpecification() throws DrmaaException { + return JnaSession.getAttribute(jt, LibDrmaa.DRMAA_NATIVE_SPECIFICATION); + } + + @SuppressWarnings("unchecked") + @Override + public void setEmail(Set set) throws DrmaaException { + JnaSession.setVectorAttribute(jt, LibDrmaa.DRMAA_V_EMAIL, set); + } + + @SuppressWarnings("unchecked") + @Override + public Set getEmail() throws DrmaaException { + return new LinkedHashSet(JnaSession.getVectorAttribute(jt, LibDrmaa.DRMAA_V_EMAIL)); + } + + @Override + public void setBlockEmail(boolean b) throws DrmaaException { + JnaSession.setAttribute(jt, LibDrmaa.DRMAA_BLOCK_EMAIL, b ? "1" : "0"); + } + + @Override + public boolean getBlockEmail() throws DrmaaException { + return "1".equals(JnaSession.getAttribute(jt, LibDrmaa.DRMAA_BLOCK_EMAIL)); + } + + @Override + public void setStartTime(PartialTimestamp partialTimestamp) throws DrmaaException { + JnaSession.setPartialTime(jt, LibDrmaa.DRMAA_START_TIME, partialTimestamp); + } + + @Override + public PartialTimestamp getStartTime() throws DrmaaException { + return JnaSession.getPartialTime(jt, LibDrmaa.DRMAA_START_TIME); + } + + @Override + public void setJobName(String s) throws DrmaaException { + JnaSession.setAttribute(jt, LibDrmaa.DRMAA_JOB_NAME, s); + } + + @Override + public String getJobName() throws DrmaaException { + return JnaSession.getAttribute(jt, LibDrmaa.DRMAA_JOB_NAME); + } + + @Override + public void setInputPath(String s) throws DrmaaException { + JnaSession.setAttribute(jt, LibDrmaa.DRMAA_INPUT_PATH, s); + } + + @Override + public String getInputPath() throws DrmaaException { + return JnaSession.getAttribute(jt, LibDrmaa.DRMAA_INPUT_PATH); + } + + @Override + public void setOutputPath(String s) throws DrmaaException { + JnaSession.setAttribute(jt, LibDrmaa.DRMAA_OUTPUT_PATH, s); + } + + @Override + public String getOutputPath() throws DrmaaException { + return JnaSession.getAttribute(jt, LibDrmaa.DRMAA_OUTPUT_PATH); + } + + @Override + public void setErrorPath(String s) throws DrmaaException { + JnaSession.setAttribute(jt, LibDrmaa.DRMAA_ERROR_PATH, s); + } + + @Override + public String getErrorPath() throws DrmaaException { + return JnaSession.getAttribute(jt, LibDrmaa.DRMAA_ERROR_PATH); + } + + @Override + public void setJoinFiles(boolean b) throws DrmaaException { + JnaSession.setAttribute(jt, LibDrmaa.DRMAA_JOIN_FILES, b ? "y" : "n"); + } + + @Override + public boolean getJoinFiles() throws DrmaaException { + return "y".equals(JnaSession.getAttribute(jt, LibDrmaa.DRMAA_JOIN_FILES)); + } + + @Override + public void setTransferFiles(FileTransferMode fileTransferMode) throws DrmaaException { + StringBuilder buf = new StringBuilder(); + + if (fileTransferMode.getInputStream()) + buf.append('i'); + + if (fileTransferMode.getOutputStream()) + buf.append('o'); + + if (fileTransferMode.getErrorStream()) + buf.append('e'); + + JnaSession.setAttribute(jt, LibDrmaa.DRMAA_TRANSFER_FILES, buf.toString()); + } + + @Override + public FileTransferMode getTransferFiles() throws DrmaaException { + String mode = JnaSession.getAttribute(jt, LibDrmaa.DRMAA_TRANSFER_FILES); + + if (mode == null) + return null; + + FileTransferMode fileTransferMode = new FileTransferMode(); + fileTransferMode.setInputStream(mode.indexOf('i') >= 0); + fileTransferMode.setOutputStream(mode.indexOf('o') >= 0); + fileTransferMode.setErrorStream(mode.indexOf('e') >= 0); + return fileTransferMode; + } + + @Override + public void setDeadlineTime(PartialTimestamp partialTimestamp) throws DrmaaException { + JnaSession.setPartialTime(jt, LibDrmaa.DRMAA_DEADLINE_TIME, partialTimestamp); + } + + @Override + public PartialTimestamp getDeadlineTime() throws DrmaaException { + return JnaSession.getPartialTime(jt, LibDrmaa.DRMAA_DEADLINE_TIME); + } + + @Override + public void setHardWallclockTimeLimit(long l) throws DrmaaException { + JnaSession.setAttribute(jt, LibDrmaa.DRMAA_WCT_HLIMIT, JnaSession.formatLimit(l)); + } + + @Override + public long getHardWallclockTimeLimit() throws DrmaaException { + return JnaSession.parseLimit(JnaSession.getAttribute(jt, LibDrmaa.DRMAA_WCT_HLIMIT)); + } + + @Override + public void setSoftWallclockTimeLimit(long l) throws DrmaaException { + JnaSession.setAttribute(jt, LibDrmaa.DRMAA_WCT_SLIMIT, JnaSession.formatLimit(l)); + } + + @Override + public long getSoftWallclockTimeLimit() throws DrmaaException { + return JnaSession.parseLimit(JnaSession.getAttribute(jt, LibDrmaa.DRMAA_WCT_SLIMIT)); + } + + @Override + public void setHardRunDurationLimit(long l) throws DrmaaException { + JnaSession.setAttribute(jt, LibDrmaa.DRMAA_DURATION_HLIMIT, JnaSession.formatLimit(l)); + } + + @Override + public long getHardRunDurationLimit() throws DrmaaException { + return JnaSession.parseLimit(JnaSession.getAttribute(jt, LibDrmaa.DRMAA_DURATION_HLIMIT)); + } + + @Override + public void setSoftRunDurationLimit(long l) throws DrmaaException { + JnaSession.setAttribute(jt, LibDrmaa.DRMAA_DURATION_SLIMIT, JnaSession.formatLimit(l)); + } + + @Override + public long getSoftRunDurationLimit() throws DrmaaException { + return JnaSession.parseLimit(JnaSession.getAttribute(jt, LibDrmaa.DRMAA_DURATION_SLIMIT)); + } + + @Override + public Set getAttributeNames() throws DrmaaException { + return JnaSession.getAttrNames(); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof JnaJobTemplate)) + return false; + JnaJobTemplate other = (JnaJobTemplate) obj; + return this.jt.equals(other.jt) && this.session.equals(other.session); + } + + @Override + public int hashCode() { + return jt.hashCode(); + } +} diff --git a/public/java/src/org/broadinstitute/sting/jna/drmaa/v1_0/JnaSession.java b/public/java/src/org/broadinstitute/sting/jna/drmaa/v1_0/JnaSession.java new file mode 100644 index 000000000..480113e1e --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/jna/drmaa/v1_0/JnaSession.java @@ -0,0 +1,450 @@ +/* + * 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.jna.drmaa.v1_0; + +import com.sun.jna.Memory; +import com.sun.jna.NativeLong; +import com.sun.jna.Pointer; +import com.sun.jna.StringArray; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.PointerByReference; +import org.ggf.drmaa.*; + +import java.text.ParseException; +import java.util.*; + +/** + * JNA mapping from Java to C DRMAA binding. + * See: Java and C Binding Documents on http://drmaa.org + */ +public class JnaSession implements Session { + private static final PartialTimestampFormat PARTIAL_TIMESTAMP_FORMAT = new PartialTimestampFormat(); + private static final ThreadLocal threadError = new ThreadLocal() { + @Override + protected Memory initialValue() { + return new Memory(LibDrmaa.DRMAA_ERROR_STRING_BUFFER); + } + }; + + @Override + public void init(String contact) throws DrmaaException { + checkError(LibDrmaa.drmaa_init(contact, getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + } + + @Override + public void exit() throws DrmaaException { + checkError(LibDrmaa.drmaa_exit(getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + } + + @Override + public JobTemplate createJobTemplate() throws DrmaaException { + PointerByReference jtRef = new PointerByReference(); + checkError(LibDrmaa.drmaa_allocate_job_template(jtRef, getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + return new JnaJobTemplate(this, jtRef.getValue()); + } + + @Override + public void deleteJobTemplate(JobTemplate jobTemplate) throws DrmaaException { + JnaJobTemplate jnaJobTemplate = (JnaJobTemplate) jobTemplate; + checkError(LibDrmaa.drmaa_delete_job_template(jnaJobTemplate.getPointer(), getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + } + + @Override + public String runJob(JobTemplate jobTemplate) throws DrmaaException { + Memory jobId = new Memory(LibDrmaa.DRMAA_JOBNAME_BUFFER); + JnaJobTemplate jnaJobTemplate = (JnaJobTemplate) jobTemplate; + checkError(LibDrmaa.drmaa_run_job(jobId, LibDrmaa.DRMAA_JOBNAME_BUFFER_LEN, jnaJobTemplate.getPointer(), getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + return jobId.getString(0); + } + + @Override + public List runBulkJobs(JobTemplate jobTemplate, int start, int end, int incr) throws DrmaaException { + PointerByReference jobIds = new PointerByReference(); + JnaJobTemplate jnaJobTemplate = (JnaJobTemplate) jobTemplate; + checkError(LibDrmaa.drmaa_run_bulk_jobs(jobIds, jnaJobTemplate.getPointer(), start, end, incr, getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + try { + return getJobIds(jobIds); + } finally { + releaseJobIds(jobIds); + } + } + + @Override + public void control(String jobId, int action) throws DrmaaException { + checkError(LibDrmaa.drmaa_control(jobId, action, getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + } + + @SuppressWarnings("unchecked") + @Override + public void synchronize(List list, long timeout, boolean dispose) throws DrmaaException { + StringArray jobIds = new StringArray((String[]) list.toArray(new String[list.size()])); + checkError(LibDrmaa.drmaa_synchronize(jobIds, new NativeLong(timeout), dispose ? 1 : 0, getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + } + + @Override + public JobInfo wait(String jobId, long timeout) throws DrmaaException { + Memory jobIdOut = new Memory(LibDrmaa.DRMAA_JOBNAME_BUFFER); + IntByReference stat = new IntByReference(); + PointerByReference rusage = new PointerByReference(); + IntByReference exited = new IntByReference(); + IntByReference exitStatus = new IntByReference(); + IntByReference signaled = new IntByReference(); + Memory signal = new Memory(LibDrmaa.DRMAA_SIGNAL_BUFFER); + IntByReference coreDumped = new IntByReference(); + IntByReference aborted = new IntByReference(); + + int errnum; + + errnum = LibDrmaa.drmaa_wait(jobId, jobIdOut, LibDrmaa.DRMAA_JOBNAME_BUFFER_LEN, stat, new NativeLong(timeout), rusage, getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + Map rusageMap; + if (errnum == LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_NO_RUSAGE) { + rusageMap = null; + } else { + try { + rusageMap = collectionToMap(getAttrValues(rusage)); + } finally { + releaseAttrValues(rusage); + } + } + + checkError(LibDrmaa.drmaa_wifexited(exited, stat.getValue(), getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + + if (exited.getValue() != 0) { + checkError(LibDrmaa.drmaa_wexitstatus(exitStatus, stat.getValue(), getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + } + + checkError(LibDrmaa.drmaa_wifsignaled(signaled, stat.getValue(), getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + + if (signaled.getValue() != 0) { + checkError(LibDrmaa.drmaa_wtermsig(signal, LibDrmaa.DRMAA_SIGNAL_BUFFER_LEN, stat.getValue(), getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + checkError(LibDrmaa.drmaa_wcoredump(coreDumped, stat.getValue(), getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + } + + checkError(LibDrmaa.drmaa_wifaborted(aborted, stat.getValue(), getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + + return new JnaJobInfo(jobIdOut.getString(0), rusageMap, exited.getValue() != 0, exitStatus.getValue(), + signaled.getValue() != 0, signal.getString(0), coreDumped.getValue() != 0, aborted.getValue() != 0); + } + + @Override + public int getJobProgramStatus(String jobId) throws DrmaaException { + IntByReference remotePs = new IntByReference(); + checkError(LibDrmaa.drmaa_job_ps(jobId, remotePs, getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + return remotePs.getValue(); + } + + @Override + public String getContact() { + Memory contact = new Memory(LibDrmaa.DRMAA_CONTACT_BUFFER); + try { + checkError(LibDrmaa.drmaa_get_contact(contact, LibDrmaa.DRMAA_CONTACT_BUFFER_LEN, getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + } catch (DrmaaException e) { + // DRMAA spec says this method should throw DrmaaException. + // Why doesn't interface implement this? + throw new RuntimeException(e); + } + return contact.getString(0); + } + + @Override + public Version getVersion() { + IntByReference major = new IntByReference(); + IntByReference minor = new IntByReference(); + try { + checkError(LibDrmaa.drmaa_version(major, minor, getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + } catch (DrmaaException e) { + // DRMAA spec says this method should throw DrmaaException. + // Why doesn't interface implement this? + throw new RuntimeException(e); + } + return new Version(major.getValue(), minor.getValue()); + } + + @Override + public String getDrmSystem() { + Memory drmSystem = new Memory(LibDrmaa.DRMAA_DRM_SYSTEM_BUFFER); + try { + checkError(LibDrmaa.drmaa_get_DRM_system(drmSystem, LibDrmaa.DRMAA_DRM_SYSTEM_BUFFER_LEN, getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + } catch (DrmaaException e) { + // DRMAA spec says this method should throw DrmaaException. + // Why doesn't interface implement this? + throw new RuntimeException(e); + } + return drmSystem.getString(0); + } + + @Override + public String getDrmaaImplementation() { + Memory drmaaImplementation = new Memory(LibDrmaa.DRMAA_DRMAA_IMPLEMENTATION_BUFFER); + try { + checkError(LibDrmaa.drmaa_get_DRMAA_implementation(drmaaImplementation, LibDrmaa.DRMAA_DRMAA_IMPLEMENTATION_BUFFER_LEN, getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + } catch (DrmaaException e) { + // DRMAA spec says this method should throw DrmaaException. + // Why doesn't interface implement this? + throw new RuntimeException(e); + } + return drmaaImplementation.getString(0); + } + + public static void setAttribute(Pointer jt, String name, String value) throws DrmaaException { + checkError(LibDrmaa.drmaa_set_attribute(jt, name, value, getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + } + + public static String getAttribute(Pointer jt, String name) throws DrmaaException { + Memory attrBuffer = new Memory(LibDrmaa.DRMAA_ATTR_BUFFER); + checkError(LibDrmaa.drmaa_get_attribute(jt, name, attrBuffer, LibDrmaa.DRMAA_ATTR_BUFFER_LEN, getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + return attrBuffer.getString(0); + } + + public static void setVectorAttribute(Pointer jt, String name, Collection values) throws DrmaaException { + StringArray valuesArray = new StringArray(values.toArray(new String[values.size()])); + checkError(LibDrmaa.drmaa_set_vector_attribute(jt, name, valuesArray, getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + } + + public static List getVectorAttribute(Pointer jt, String name) throws DrmaaException { + PointerByReference values = new PointerByReference(); + checkError(LibDrmaa.drmaa_get_vector_attribute(jt, name, values, getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + try { + return getAttrValues(values); + } finally { + releaseAttrValues(values); + } + } + + public static void setPartialTime(Pointer jt, String name, PartialTimestamp partialTimestamp) throws DrmaaException { + setAttribute(jt, name, PARTIAL_TIMESTAMP_FORMAT.format(partialTimestamp)); + } + + public static PartialTimestamp getPartialTime(Pointer jt, String name) throws DrmaaException { + String time = getAttribute(jt, name); + if (time == null) + return null; + try { + return PARTIAL_TIMESTAMP_FORMAT.parse(time); + } catch (ParseException e) { + throw new InternalException(name + " property is unparsable"); + } + } + + public static Set getAttrNames() throws DrmaaException { + PointerByReference values = new PointerByReference(); + checkError(LibDrmaa.drmaa_get_attribute_names(values, getError(), LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN)); + try { + return new LinkedHashSet(getAttrNames(values)); + } finally { + releaseAttrNames(values); + } + } + + public static Collection mapToCollection(Map map) { + Collection collection = new LinkedHashSet(); + for (Map.Entry entry: map.entrySet()) + collection.add(entry.getKey() + "=" + entry.getValue()); + return collection; + } + + public static Map collectionToMap(Collection list) { + Map map = new LinkedHashMap(); + for (String entry: list) { + if (entry == null) + continue; + int equals = entry.indexOf('='); + if (equals < 0) + continue; + map.put(entry.substring(0, equals), entry.substring(equals + 1)); + } + return map; + } + + public static String formatLimit(long secs) { + long seconds = (secs % 60); + long minutes = (secs / 60) % 60; + long hours = (secs / 3600); + return String.format("%d:%02d:%02d", hours, minutes, seconds); + } + + public static long parseLimit(String limit) { + long seconds = 0; + if (limit != null) { + for (String token: limit.split(":")) { + seconds *= 60; + seconds += Long.parseLong(token); + } + } + return seconds; + } + + private static List getAttrNames(PointerByReference names) throws DrmaaException { + List namesList = new ArrayList(); + IntByReference size = new IntByReference(); + int errnum; + + errnum = LibDrmaa.drmaa_get_num_attr_names(names.getValue(), size); + checkError(errnum, "unable to get attribute names"); + int num = size.getValue(); + + Memory value = new Memory(LibDrmaa.DRMAA_ATTR_BUFFER); + for (int i = 1; i <= num; i++) { + errnum = LibDrmaa.drmaa_get_next_attr_name(names.getValue(), value, LibDrmaa.DRMAA_ATTR_BUFFER_LEN); + checkError(errnum, "unable to get attribute name " + i); + if (errnum == LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_NO_MORE_ELEMENTS) + break; + namesList.add(value.getString(0)); + } + + return namesList; + } + + private static List getAttrValues(PointerByReference values) throws DrmaaException { + List valuesList = new ArrayList(); + IntByReference size = new IntByReference(); + int errnum; + + errnum = LibDrmaa.drmaa_get_num_attr_values(values.getValue(), size); + checkError(errnum, "unable to get attribute values"); + int num = size.getValue(); + + Memory value = new Memory(LibDrmaa.DRMAA_ATTR_BUFFER); + for (int i = 1; i <= num; i++) { + errnum = LibDrmaa.drmaa_get_next_attr_value(values.getValue(), value, LibDrmaa.DRMAA_ATTR_BUFFER_LEN); + checkError(errnum, "unable to get attribute value " + i); + if (errnum == LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_NO_MORE_ELEMENTS) + break; + valuesList.add(value.getString(0)); + } + + return valuesList; + } + + private static List getJobIds(PointerByReference jobIds) throws DrmaaException { + List jobIdsList = new ArrayList(); + IntByReference size = new IntByReference(); + int errnum; + + errnum = LibDrmaa.drmaa_get_num_job_ids(jobIds.getValue(), size); + checkError(errnum, "unable to get jobIds"); + int num = size.getValue(); + + Memory value = new Memory(LibDrmaa.DRMAA_JOBNAME_BUFFER); + for (int i = 1; i <= num; i++) { + errnum = LibDrmaa.drmaa_get_next_job_id(jobIds.getValue(), value, LibDrmaa.DRMAA_JOBNAME_BUFFER_LEN); + checkError(errnum, "unable to get jobId " + i); + if (errnum == LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_NO_MORE_ELEMENTS) + break; + jobIdsList.add(value.getString(0)); + } + + return jobIdsList; + } + + private static void releaseAttrNames(PointerByReference names) throws DrmaaException { + LibDrmaa.drmaa_release_attr_names(names.getValue()); + } + + private static void releaseAttrValues(PointerByReference values) throws DrmaaException { + LibDrmaa.drmaa_release_attr_values(values.getValue()); + } + + private static void releaseJobIds(PointerByReference jobIds) throws DrmaaException { + LibDrmaa.drmaa_release_job_ids(jobIds.getValue()); + } + + private static Memory getError() { + return threadError.get(); + } + + private static void checkError(int errnum) throws DrmaaException { + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + checkError(errnum, getError().getString(0)); + } + + private static void checkError(int errnum, String error) throws DrmaaException { + switch (errnum) { + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS: + break; + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_INTERNAL_ERROR: + throw new InternalException(error); + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_DRM_COMMUNICATION_FAILURE: + throw new DrmCommunicationException(error); + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_AUTH_FAILURE: + throw new AuthorizationException(error); + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_INVALID_ARGUMENT: + throw new IllegalArgumentException(error); + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_NO_ACTIVE_SESSION: + throw new NoActiveSessionException(error); + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_NO_MEMORY: + throw new OutOfMemoryError(error); + + /* -------------- init and exit specific --------------- */ + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_INVALID_CONTACT_STRING: + throw new InvalidContactStringException(error); + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_DEFAULT_CONTACT_STRING_ERROR: + throw new DefaultContactStringException(error); + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_NO_DEFAULT_CONTACT_STRING_SELECTED: + throw new NoDefaultContactStringException(error); + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_DRMS_INIT_FAILED: + throw new DrmsInitException(error); + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_ALREADY_ACTIVE_SESSION: + throw new AlreadyActiveSessionException(error); + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_DRMS_EXIT_ERROR: + throw new DrmsExitException(error); + + /* ---------------- job attributes specific -------------- */ + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_INVALID_ATTRIBUTE_FORMAT: + throw new InvalidAttributeFormatException(error); + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_INVALID_ATTRIBUTE_VALUE: + throw new InvalidAttributeValueException(error); + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_CONFLICTING_ATTRIBUTE_VALUES: + throw new ConflictingAttributeValuesException(error); + + /* --------------------- job submission specific -------------- */ + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_TRY_LATER: + throw new TryLaterException(error); + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_DENIED_BY_DRM: + throw new DeniedByDrmException(error); + + /* ------------------------------- job control specific ---------------- */ + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_INVALID_JOB: + throw new InvalidJobException(error); + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_RESUME_INCONSISTENT_STATE: + throw new ResumeInconsistentStateException(error); + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUSPEND_INCONSISTENT_STATE: + throw new SuspendInconsistentStateException(error); + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_HOLD_INCONSISTENT_STATE: + throw new HoldInconsistentStateException(error); + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_RELEASE_INCONSISTENT_STATE: + throw new ReleaseInconsistentStateException(error); + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_EXIT_TIMEOUT: + throw new ExitTimeoutException(error); + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_NO_RUSAGE: + break; + case LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_NO_MORE_ELEMENTS: + break; + default: + throw new IllegalArgumentException(String.format("Unknown error code %d: %s", errnum, error)); + } + } +} diff --git a/public/java/src/org/broadinstitute/sting/jna/drmaa/v1_0/JnaSessionFactory.java b/public/java/src/org/broadinstitute/sting/jna/drmaa/v1_0/JnaSessionFactory.java new file mode 100644 index 000000000..a1460b7f4 --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/jna/drmaa/v1_0/JnaSessionFactory.java @@ -0,0 +1,39 @@ +/* + * 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.jna.drmaa.v1_0; + +import org.ggf.drmaa.Session; +import org.ggf.drmaa.SessionFactory; + +/** + * JNA mapping from Java to C DRMAA binding. + */ +@SuppressWarnings("unused") +public class JnaSessionFactory extends SessionFactory { + @Override + public Session getSession() { + return new JnaSession(); + } +} diff --git a/public/java/src/org/broadinstitute/sting/jna/drmaa/v1_0/LibDrmaa.java b/public/java/src/org/broadinstitute/sting/jna/drmaa/v1_0/LibDrmaa.java new file mode 100644 index 000000000..1244d3023 --- /dev/null +++ b/public/java/src/org/broadinstitute/sting/jna/drmaa/v1_0/LibDrmaa.java @@ -0,0 +1,754 @@ +/* + * 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. + */ + +/*___INFO__MARK_BEGIN__*/ +/************************************************************************* + * + * The Contents of this file are made available subject to the terms of + * the Sun Industry Standards Source License Version 1.2 + * + * Sun Microsystems Inc., March, 2001 + * + * + * Sun Industry Standards Source License Version 1.2 + * ================================================= + * The contents of this file are subject to the Sun Industry Standards + * Source License Version 1.2 (the "License"); You may not use this file + * except in compliance with the License. You may obtain a copy of the + * License at http://gridengine.sunsource.net/Gridengine_SISSL_license.html + * + * Software provided under this License is provided on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, + * WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS, + * MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING. + * See the License for the specific provisions governing your rights and + * obligations concerning the Software. + * + * The Initial Developer of the Original Code is: Sun Microsystems, Inc. + * + * Copyright: 2001 by Sun Microsystems, Inc. + * + * All Rights Reserved. + * + ************************************************************************/ +/*___INFO__MARK_END__*/ + +package org.broadinstitute.sting.jna.drmaa.v1_0; + +import com.sun.jna.*; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.PointerByReference; + +@SuppressWarnings("unused") +public class LibDrmaa { + static { + Native.register("drmaa"); + } + +/* see www.drmaa.org for more details on the DRMAA specification */ +/****** DRMAA/-DRMAA_Interface ************************************************* +* NAME +* DRMAA_Interface -- DRMAA interface +* +* FUNCTION +* The enlisted functions specify the C/C++ binding of the DRMAA interface +* specification. +* +* SEE ALSO +* DRMAA/drmaa_get_next_attr_name() +* DRMAA/drmaa_get_next_attr_value() +* DRMAA/drmaa_get_next_job_id() +* DRMAA/drmaa_release_attr_names() +* DRMAA/drmaa_release_attr_values() +* DRMAA/drmaa_release_job_ids() +* DRMAA/drmaa_init() +* DRMAA/drmaa_exit() +* DRMAA/drmaa_allocate_job_template() +* DRMAA/drmaa_delete_job_template() +* DRMAA/drmaa_set_attribute() +* DRMAA/drmaa_get_attribute() +* DRMAA/drmaa_set_vector_attribute() +* DRMAA/drmaa_get_vector_attribute() +* DRMAA/drmaa_get_attribute_names() +* DRMAA/drmaa_get_vector_attribute_names() +* DRMAA/drmaa_run_job() +* DRMAA/drmaa_run_bulk_jobs() +* DRMAA/drmaa_control() +* DRMAA/drmaa_synchronize() +* DRMAA/drmaa_wait() +* DRMAA/drmaa_wifexited() +* DRMAA/drmaa_wexitstatus() +* DRMAA/drmaa_wifsignaled() +* DRMAA/drmaa_wtermsig() +* DRMAA/drmaa_wcoredump() +* DRMAA/drmaa_wifaborted() +* DRMAA/drmaa_job_ps() +* DRMAA/drmaa_strerror() +* DRMAA/drmaa_get_contact() +* DRMAA/drmaa_version() +* DRMAA/drmaa_get_DRM_system() +*******************************************************************************/ + +/* ------------------- Constants ------------------- */ +/* + * some not yet agreed buffer length constants + * these are recommended minimum values + */ + +/* drmaa_get_attribute() */ +public static final long DRMAA_ATTR_BUFFER = 1024; +public static final NativeLong DRMAA_ATTR_BUFFER_LEN = new NativeLong(DRMAA_ATTR_BUFFER - 1); + +/* drmaa_get_contact() */ +public static final long DRMAA_CONTACT_BUFFER = 1024; +public static final NativeLong DRMAA_CONTACT_BUFFER_LEN = new NativeLong(DRMAA_CONTACT_BUFFER - 1); + +/* drmaa_get_DRM_system() */ +public static final long DRMAA_DRM_SYSTEM_BUFFER = 1024; +public static final NativeLong DRMAA_DRM_SYSTEM_BUFFER_LEN = new NativeLong(DRMAA_DRM_SYSTEM_BUFFER - 1); + +/* drmaa_get_DRM_system() */ +public static final long DRMAA_DRMAA_IMPLEMENTATION_BUFFER = 1024; +public static final NativeLong DRMAA_DRMAA_IMPLEMENTATION_BUFFER_LEN = new NativeLong(DRMAA_DRMAA_IMPLEMENTATION_BUFFER - 1); + +/* + * Agreed buffer length constants + * these are recommended minimum values + */ +public static final long DRMAA_ERROR_STRING_BUFFER = 1024; +public static final long DRMAA_JOBNAME_BUFFER = 1024; +public static final long DRMAA_SIGNAL_BUFFER = 32; + +public static final NativeLong DRMAA_ERROR_STRING_BUFFER_LEN = new NativeLong(DRMAA_ERROR_STRING_BUFFER - 1); +public static final NativeLong DRMAA_JOBNAME_BUFFER_LEN = new NativeLong(DRMAA_JOBNAME_BUFFER - 1); +public static final NativeLong DRMAA_SIGNAL_BUFFER_LEN = new NativeLong(DRMAA_SIGNAL_BUFFER - 1); + +/* + * Agreed constants + */ +public static final NativeLong DRMAA_TIMEOUT_WAIT_FOREVER = new NativeLong(-1); +public static final NativeLong DRMAA_TIMEOUT_NO_WAIT = new NativeLong(0); + +public static final String DRMAA_JOB_IDS_SESSION_ANY = "DRMAA_JOB_IDS_SESSION_ANY"; +public static final String DRMAA_JOB_IDS_SESSION_ALL = "DRMAA_JOB_IDS_SESSION_ALL"; + +public static final String DRMAA_SUBMISSION_STATE_ACTIVE = "drmaa_active"; +public static final String DRMAA_SUBMISSION_STATE_HOLD = "drmaa_hold"; + +/* + * Agreed placeholder names + */ +public static final String DRMAA_PLACEHOLDER_INCR = "$drmaa_incr_ph$"; +public static final String DRMAA_PLACEHOLDER_HD = "$drmaa_hd_ph$"; +public static final String DRMAA_PLACEHOLDER_WD = "$drmaa_wd_ph$"; + +/* + * Agreed names of job template attributes + */ +public static final String DRMAA_REMOTE_COMMAND = "drmaa_remote_command"; +public static final String DRMAA_JS_STATE = "drmaa_js_state"; +public static final String DRMAA_WD = "drmaa_wd"; +public static final String DRMAA_JOB_CATEGORY = "drmaa_job_category"; +public static final String DRMAA_NATIVE_SPECIFICATION = "drmaa_native_specification"; +public static final String DRMAA_BLOCK_EMAIL = "drmaa_block_email"; +public static final String DRMAA_START_TIME = "drmaa_start_time"; +public static final String DRMAA_JOB_NAME = "drmaa_job_name"; +public static final String DRMAA_INPUT_PATH = "drmaa_input_path"; +public static final String DRMAA_OUTPUT_PATH = "drmaa_output_path"; +public static final String DRMAA_ERROR_PATH = "drmaa_error_path"; +public static final String DRMAA_JOIN_FILES = "drmaa_join_files"; +public static final String DRMAA_TRANSFER_FILES = "drmaa_transfer_files"; +public static final String DRMAA_DEADLINE_TIME = "drmaa_deadline_time"; +public static final String DRMAA_WCT_HLIMIT = "drmaa_wct_hlimit"; +public static final String DRMAA_WCT_SLIMIT = "drmaa_wct_slimit"; +public static final String DRMAA_DURATION_HLIMIT = "drmaa_duration_hlimit"; +public static final String DRMAA_DURATION_SLIMIT = "drmaa_duration_slimit"; + +/* names of job template vector attributes */ +public static final String DRMAA_V_ARGV = "drmaa_v_argv"; +public static final String DRMAA_V_ENV = "drmaa_v_env"; +public static final String DRMAA_V_EMAIL = "drmaa_v_email"; + +/* + * DRMAA errno values + * + * do not touch these values are agreed !!! + */ +public static interface DRMAA_ERRNO { + /* -------------- these are relevant to all sections ---------------- */ + public static final int DRMAA_ERRNO_SUCCESS = 0; /* Routine returned normally with success. */ + public static final int DRMAA_ERRNO_INTERNAL_ERROR = 1; /* Unexpected or internal DRMAA error like memory allocation, system call failure, etc. */ + public static final int DRMAA_ERRNO_DRM_COMMUNICATION_FAILURE = 2; /* Could not contact DRM system for this request. */ + public static final int DRMAA_ERRNO_AUTH_FAILURE = 3; /* The specified request is not processed successfully due to authorization failure. */ + public static final int DRMAA_ERRNO_INVALID_ARGUMENT = 4; /* The input value for an argument is invalid. */ + public static final int DRMAA_ERRNO_NO_ACTIVE_SESSION = 5; /* Exit routine failed because there is no active session */ + public static final int DRMAA_ERRNO_NO_MEMORY = 6; /* failed allocating memory */ + + /* -------------- init and exit specific --------------- */ + public static final int DRMAA_ERRNO_INVALID_CONTACT_STRING = 7; /* Initialization failed due to invalid contact string. */ + public static final int DRMAA_ERRNO_DEFAULT_CONTACT_STRING_ERROR = 8; /* DRMAA could not use the default contact string to connect to DRM system. */ + public static final int DRMAA_ERRNO_NO_DEFAULT_CONTACT_STRING_SELECTED = 9; /* No default contact string was provided or selected. DRMAA requires that the default contact string is selected when there is more than one default contact string due to multiple DRMAA implementation contained in the binary module. */ + public static final int DRMAA_ERRNO_DRMS_INIT_FAILED = 10; /* Initialization failed due to failure to init DRM system. */ + public static final int DRMAA_ERRNO_ALREADY_ACTIVE_SESSION = 11; /* Initialization failed due to existing DRMAA session. */ + public static final int DRMAA_ERRNO_DRMS_EXIT_ERROR = 12; /* DRM system disengagement failed. */ + + /* ---------------- job attributes specific -------------- */ + public static final int DRMAA_ERRNO_INVALID_ATTRIBUTE_FORMAT = 13; /* The format for the job attribute value is invalid. */ + public static final int DRMAA_ERRNO_INVALID_ATTRIBUTE_VALUE = 14; /* The value for the job attribute is invalid. */ + public static final int DRMAA_ERRNO_CONFLICTING_ATTRIBUTE_VALUES = 15; /* The value of this attribute is conflicting with a previously set attributes. */ + + /* --------------------- job submission specific -------------- */ + public static final int DRMAA_ERRNO_TRY_LATER = 16; /* Could not pass job now to DRM system. A retry may succeed however (saturation). */ + public static final int DRMAA_ERRNO_DENIED_BY_DRM = 17; /* The DRM system rejected the job. The job will never be accepted due to DRM configuration or job template settings. */ + + /* ------------------------------- job control specific ---------------- */ + public static final int DRMAA_ERRNO_INVALID_JOB = 18; /* The job specified by the 'jobid' does not exist. */ + public static final int DRMAA_ERRNO_RESUME_INCONSISTENT_STATE = 19; /* The job has not been suspended. The RESUME request will not be processed. */ + public static final int DRMAA_ERRNO_SUSPEND_INCONSISTENT_STATE = 20; /* The job has not been running, and it cannot be suspended. */ + public static final int DRMAA_ERRNO_HOLD_INCONSISTENT_STATE = 21; /* The job cannot be moved to a HOLD state. */ + public static final int DRMAA_ERRNO_RELEASE_INCONSISTENT_STATE = 22; /* The job is not in a HOLD state. */ + public static final int DRMAA_ERRNO_EXIT_TIMEOUT = 23; /* We have encountered a time-out condition for drmaa_synchronize or drmaa_wait. */ + public static final int DRMAA_ERRNO_NO_RUSAGE = 24; /* This error code is returned by drmaa_wait() when a job has finished but no rusage and stat data could be provided. */ + public static final int DRMAA_ERRNO_NO_MORE_ELEMENTS = 25; /* There are no more elements in the opaque string vector. */ + + public static final int DRMAA_NO_ERRNO = 26; +} + +/* + * Agreed DRMAA job states as returned by drmaa_job_ps() + */ +public static interface DRMAA_PS { + public static final int DRMAA_PS_UNDETERMINED = 0x00; /* process status cannot be determined */ + public static final int DRMAA_PS_QUEUED_ACTIVE = 0x10; /* job is queued and active */ + public static final int DRMAA_PS_SYSTEM_ON_HOLD = 0x11; /* job is queued and in system hold */ + public static final int DRMAA_PS_USER_ON_HOLD = 0x12; /* job is queued and in user hold */ + public static final int DRMAA_PS_USER_SYSTEM_ON_HOLD = 0x13; /* job is queued and in user and system hold */ + public static final int DRMAA_PS_RUNNING = 0x20; /* job is running */ + public static final int DRMAA_PS_SYSTEM_SUSPENDED = 0x21; /* job is system suspended */ + public static final int DRMAA_PS_USER_SUSPENDED = 0x22; /* job is user suspended */ + public static final int DRMAA_PS_USER_SYSTEM_SUSPENDED = 0x23; /* job is user and system suspended */ + public static final int DRMAA_PS_DONE = 0x30; /* job finished normally */ + public static final int DRMAA_PS_FAILED = 0x40; /* job finished, but failed */ +} + +/* + * Agreed DRMAA actions for drmaa_control() + */ +public static interface DRMAA_CONTROL { + public static final int DRMAA_CONTROL_SUSPEND = 0; + public static final int DRMAA_CONTROL_RESUME = 1; + public static final int DRMAA_CONTROL_HOLD = 2; + public static final int DRMAA_CONTROL_RELEASE = 3; + public static final int DRMAA_CONTROL_TERMINATE = 4; +} + +/* ------------------- Data types ------------------- */ +/* + * Agreed opaque DRMAA job template + * struct drmaa_job_template_s is in japiP.h + */ +//typedef struct drmaa_job_template_s drmaa_job_template_t; + +/* ---------- C/C++ language binding specific interfaces -------- */ + +//typedef struct drmaa_attr_names_s drmaa_attr_names_t; +//typedef struct drmaa_attr_values_s drmaa_attr_values_t; +//typedef struct drmaa_job_ids_s drmaa_job_ids_t; + +/* + * get next string attribute from iterator + * + * returns DRMAA_ERRNO_SUCCESS or DRMAA_ERRNO_INVALID_ATTRIBUTE_VALUE + * if no such exists + */ + +public static native int drmaa_get_next_attr_name(/* drmaa_attr_names_t* */ Pointer values, Pointer value, + NativeLong value_len); +public static native int drmaa_get_next_attr_value(/* drmaa_attr_names_t* */ Pointer values, Pointer value, + NativeLong value_len); +public static native int drmaa_get_next_job_id(/* drmaa_job_ids_t* */ Pointer values, Pointer value, + NativeLong value_len); + +/* + * get element count of opaque string vector + * + * Gives the number of elements in the opaque string vector. Useful for + * copying the contents into an array. + */ +public static native int drmaa_get_num_attr_names(/* drmaa_attr_names_t* */ Pointer values, IntByReference size); +public static native int drmaa_get_num_attr_values(/* drmaa_attr_values_t* */ Pointer values, IntByReference size); +public static native int drmaa_get_num_job_ids(/* drmaa_job_ids_t* */ Pointer values, IntByReference size); + +/* + * release opaque string vector + * + * Opaque string vectors can be used without any constraint + * until the release function has been called. + */ +public static native void drmaa_release_attr_names(/* drmaa_attr_names_t* */ Pointer values); +public static native void drmaa_release_attr_values(/* drmaa_attr_values_t* */ Pointer values); +public static native void drmaa_release_job_ids(/* drmaa_job_ids_t* */ Pointer values); + +/* ------------------- init/exit routines ------------------- */ +/* + * Initialize DRMAA API library and create a new DRMAA Session. 'Contact' + * is an implementation dependent string which MAY be used to specify + * which DRM system to use. This routine MUST be called before any + * other DRMAA calls, except for drmaa_version(). + * If 'contact' is NULL, the default DRM system SHALL be used provided there is + * only one DRMAA implementation in the provided binary module. When these is + * more than one DRMAA implementation in the binary module, drmaa_init() SHALL + * return the DRMAA_ERRNO_NO_DEFAULT_CONTACT_STRING_SELECTED error. drmaa_init() + * SHOULD be called by only one of the threads. The main thread is RECOMMENDED. + * A call by another thread SHALL return DRMAA_ERRNO_ALREADY_ACTIVE_SESSION. + * When 'contact' is a a semi-colon separated list of name=value strings, the + * strings will be parsed and interpreted. The current list of accepted names + * is: + * session -- the id of the session to which to reconnect +#if 0 + * sge_root -- the SGE_ROOT to use + * sge_cell -- the SGE_CELL to use +#endif + * + * drmaa_init() SHALL return DRMAA_ERRNO_SUCCESS on success, otherwise: + * DRMAA_ERRNO_INVALID_CONTACT_STRING, + * DRMAA_ERRNO_NO_MEMORY, + * DRMAA_ERRNO_ALREADY_ACTIVE_SESSION, + * DRMAA_ERRNO_NO_DEFAULT_CONTACT_STRING_SELECTED, or + * DRMAA_ERRNO_DEFAULT_CONTACT_STRING_ERROR. + */ +public static native int drmaa_init(String contact, Pointer error_diagnosis, NativeLong error_diag_len); + + +/* + * Disengage from DRMAA library and allow the DRMAA library to perform + * any necessary internal clean up. + * This routine SHALL end the current DRMAA Session, but SHALL NOT effect any + * jobs (e.g., queued and running jobs SHALL remain queued and running). + * drmaa_exit() SHOULD be called by only one of the threads. Other thread calls + * to drmaa_exit() MAY fail since there is no active session. + * + * drmaa_exit() SHALL return DRMAA_ERRNO_SUCCESS on success, otherwise: + * DRMAA_ERRNO_DRMS_EXIT_ERROR or + * DRMAA_ERRNO_NO_ACTIVE_SESSION. + */ +public static native int drmaa_exit(Pointer error_diagnosis, NativeLong error_diag_len); + +/* ------------------- job template routines ------------------- */ + +/* + * Allocate a new job template. + * + * drmaa_allocate_job_template() SHALL return DRMAA_ERRNO_SUCCESS on success, + * otherwise: + * DRMAA_ERRNO_DRM_COMMUNICATION_FAILURE, + * DRMAA_ERRNO_INTERNAL_ERROR or + * DRMAA_ERRNO_NO_MEMORY. + */ +public static native int drmaa_allocate_job_template(/* drmaa_job_template_t** */ PointerByReference jt, Pointer error_diagnosis, NativeLong error_diag_len); + +/* + * Deallocate a job template. This routine has no effect on jobs. + * + * drmaa_delete_job_template() SHALL return DRMAA_ERRNO_SUCCESS on success, + * otherwise: + * DRMAA_ERRNO_DRM_COMMUNICATION_FAILURE or + * DRMAA_ERRNO_INTERNAL_ERROR. + */ +public static native int drmaa_delete_job_template(/* drmaa_job_template_t* */ Pointer jt, Pointer error_diagnosis, + NativeLong error_diag_len); + + +/* + * Adds ('name', 'value') pair to list of attributes in job template 'jt'. + * Only non-vector attributes SHALL be passed. + * + * drmaa_set_attribute() SHALL return DRMAA_ERRNO_SUCCESS on success, otherwise: + * DRMAA_ERRNO_INVALID_ATTRIBUTE_FORMAT, + * DRMAA_ERRNO_INVALID_ARGUMENT, + * DRMAA_ERRNO_NO_MEMORY, + * DRMAA_ERRNO_INVALID_ATTRIBUTE_VALUE or + * DRMAA_ERRNO_CONFLICTING_ATTRIBUTE_VALUES. + */ +public static native int drmaa_set_attribute(/* drmaa_job_template_t* */ Pointer jt, String name, + String value, Pointer error_diagnosis, + NativeLong error_diag_len); + + +/* + * If 'name' is an existing non-vector attribute name in the job + * template 'jt', then the value of 'name' SHALL be returned; otherwise, + * NULL is returned. + * + * drmaa_get_attribute() SHALL return DRMAA_ERRNO_SUCCESS on success, otherwise: + * DRMAA_ERRNO_INVALID_ATTRIBUTE_VALUE. + */ +public static native int drmaa_get_attribute(/* drmaa_job_template_t* */ Pointer jt, String name, Pointer value, + NativeLong value_len, Pointer error_diagnosis, + NativeLong error_diag_len); + +/* Adds ('name', 'values') pair to list of vector attributes in job template + * 'jt'. Only vector attributes SHALL be passed. + * A 'value' string vector containing n elements must be n+1 elements long, with + * the nth value, i.e. value[n], being set to NULL as a delimitor. + * + * drmaa_set_vector_attribute() SHALL return DRMAA_ERRNO_SUCCESS on success, + * otherwise: + * DRMAA_ERRNO_INVALID_ATTRIBUTE_FORMAT, + * DRMAA_ERRNO_INVALID_ARGUMENT, + * DRMAA_ERRNO_NO_MEMORY, + * DRMAA_ERRNO_CONFLICTING_ATTRIBUTE_VALUES. + */ +public static native int drmaa_set_vector_attribute(/* drmaa_job_template_t* */ Pointer jt, String name, + Pointer value, Pointer error_diagnosis, + NativeLong error_diag_len); + + +/* + * If 'name' is an existing vector attribute name in the job template 'jt', + * then the values of 'name' are returned; otherwise, NULL is returned. + * + * drmaa_get_vector_attribute() SHALL return DRMAA_ERRNO_SUCCESS on success, + * otherwise: + * DRMAA_ERRNO_INVALID_ATTRIBUTE_VALUE. + */ +public static native int drmaa_get_vector_attribute(/* drmaa_job_template_t* */ Pointer jt, String name, + /* drmaa_attr_values_t ** */ PointerByReference values, + Pointer error_diagnosis, NativeLong error_diag_len); + + +/* + * SHALL return the set of supported attribute names whose associated + * value type is String. This set SHALL include supported DRMAA reserved + * attribute names and native attribute names. + * + * drmaa_get_attribute_names() SHALL return DRMAA_ERRNO_SUCCESS on success, + * otherwise: + * DRMAA_ERRNO_NO_MEMORY. + */ +public static native int drmaa_get_attribute_names(/* drmaa_attr_names_t ** */ PointerByReference values, + Pointer error_diagnosis, NativeLong error_diag_len); + +/* + * SHALL return the set of supported attribute names whose associated + * value type is String Vector. This set SHALL include supported DRMAA reserved + * attribute names and native attribute names. + * + * drmaa_get_vector_attribute_names() SHALL return DRMAA_ERRNO_SUCCESS on + * success, otherwise: + * DRMAA_ERRNO_NO_MEMORY. + */ +public static native int drmaa_get_vector_attribute_names(/* drmaa_attr_names_t ** */ PointerByReference values, + Pointer error_diagnosis, + NativeLong error_diag_len); + +/* ------------------- job submission routines ------------------- */ + +/* + * Submit a job with attributes defined in the job template 'jt'. + * The job identifier 'job_id' is a printable, NULL terminated string, + * identical to that returned by the underlying DRM system. + * + * drmaa_run_job() SHALL return DRMAA_ERRNO_SUCCESS on success, otherwise: + * DRMAA_ERRNO_TRY_LATER, + * DRMAA_ERRNO_DENIED_BY_DRM, + * DRMAA_ERRNO_NO_MEMORY, + * DRMAA_ERRNO_DRM_COMMUNICATION_FAILURE or + * DRMAA_ERRNO_AUTH_FAILURE. + */ +public static native int drmaa_run_job(Pointer job_id, NativeLong job_id_len, + /* drmaa_job_template_t * */ Pointer jt, Pointer error_diagnosis, + NativeLong error_diag_len); + +/* + * Submit a set of parametric jobs, dependent on the implied loop index, each + * with attributes defined in the job template 'jt'. + * The job identifiers 'job_ids' SHALL all be printable, + * NULL terminated strings, identical to those returned by the underlying + * DRM system. Nonnegative loop bounds SHALL NOT use file names + * that start with minus sign like command line options. + * DRMAA defines a special index placeholder, drmaa_incr_ph, (which has the + * value "$incr_pl$") that is used to construct parametric job templates. + * For example: + * //C++ string syntax used + * drmaa_set_attribute(pjt, "stderr", drmaa_incr_ph + ".err" ); + * + * drmaa_run_bulk_jobs() SHALL return DRMAA_ERRNO_SUCCESS on success, otherwise: + * DRMAA_ERRNO_TRY_LATER, + * DRMAA_ERRNO_DENIED_BY_DRM, + * DRMAA_ERRNO_NO_MEMORY, + * DRMAA_ERRNO_DRM_COMMUNICATION_FAILURE or + * DRMAA_ERRNO_AUTH_FAILURE. + */ +public static native int drmaa_run_bulk_jobs(/* drmaa_job_ids_t ** */ PointerByReference jobids, + /* drmaa_job_template_t * */ Pointer jt, int start, int end, + int incr, Pointer error_diagnosis, NativeLong error_diag_len); + +/* ------------------- job control routines ------------------- */ + +/* + * Start, stop, restart, or kill the job identified by 'job_id'. + * If 'job_id' is DRMAA_JOB_IDS_SESSION_ALL then this routine + * acts on all jobs *submitted* during this DRMAA session. + * The legal values for 'action' and their meanings SHALL be: + * DRMAA_CONTROL_SUSPEND: stop the job, + * DRMAA_CONTROL_RESUME: (re)start the job, + * DRMAA_CONTROL_HOLD: put the job on-hold, + * DRMAA_CONTROL_RELEASE: release the hold on the job, and + * DRMAA_CONTROL_TERMINATE: kill the job. + * + * This routine SHALL return once the action has been acknowledged by + * the DRM system, but does not necessarily wait until the action + * has been completed. + * + * drmaa_control() SHALL return DRMAA_ERRNO_SUCCESS on success, otherwise: + * DRMAA_ERRNO_DRM_COMMUNICATION_FAILURE, + * DRMAA_ERRNO_AUTH_FAILURE, + * DRMAA_ERRNO_NO_MEMORY, + * DRMAA_ERRNO_RESUME_INCONSISTENT_STATE, + * DRMAA_ERRNO_SUSPEND_INCONSISTENT_STATE, + * DRMAA_ERRNO_HOLD_INCONSISTENT_STATE, + * DRMAA_ERRNO_RELEASE_INCONSISTENT_STATE or + * DRMAA_ERRNO_INVALID_JOB. + */ +public static native int drmaa_control(String jobid, int action, Pointer error_diagnosis, + NativeLong error_diag_len); + + +/* + * Wait until all jobs specified by 'job_ids' have finished + * execution. If 'job_ids' is DRMAA_JOB_IDS_SESSION_ALL then this routine + * waits for all jobs *submitted* during this DRMAA session. The timeout value + * is used to specify the number of seconds to wait for the job to fail finish + * before returning if a result is not immediately available. The value + * DRMAA_TIMEOUT_WAIT_FOREVER can be used to specify that routine should wait + * indefinitely for a result. The value DRMAA_TIMEOUT_NO_WAIT can be used to + * specify that the routine should return immediately if no result is available. + * If the call exits before timeout, all the jobs have + * been waited on or there was an interrupt. + * If the invocation exits on timeout, the return code is + * DRMAA_ERRNO_EXIT_TIMEOUT. The caller SHOULD check system time before and + * after this call in order to check how much time has passed. + * + * The dispose parameter specifies how to treat reaping information: + * True=1 "fake reap", i.e. dispose of the rusage data + * False=0 do not reap + * + * A 'job_ids' string vector containing n elements must be n+1 elements long, + * with the nth value, i.e. job_ids[n], being set to NULL as a delimitor. + * + * drmaa_synchronize() SHALL return DRMAA_ERRNO_SUCCESS on success, otherwise: + * DRMAA_ERRNO_DRM_COMMUNICATION_FAILURE, + * DRMAA_ERRNO_AUTH_FAILURE, + * DRMAA_ERRNO_NO_MEMORY, + * DRMAA_ERRNO_EXIT_TIMEOUT or + * DRMAA_ERRNO_INVALID_JOB. + */ +public static native int drmaa_synchronize(Pointer job_ids, NativeLong timeout, int dispose, + Pointer error_diagnosis, NativeLong error_diag_len); + + +/* + * This routine SHALL wait for a job with job_id to fail or finish execution. If + * the special string, DRMAA_JOB_IDS_SESSION_ANY is provided as the job_id, + * this routine SHALL wait for any job from the session. This routine is modeled + * on the wait3 POSIX routine. The timeout value is used to specify the number + * of seconds to wait for the job to fail finish before returning if a result is + * not immediately available. The value DRMAA_TIMEOUT_WAIT_FOREVER can be + * used to specify that routine should wait indefinitely for a result. The value + * DRMAA_TIMEOUT_NO_WAIT may be specified that the routine should return + * immediately if no result is available. + * If the call exits before timeout ,the job has been waited on + * successfully or there was an interrupt. + * If the invocation exits on timeout, the return code is + * DRMAA_ERRNO_EXIT_TIMEOUT. The caller SHOULD check system time before and + * after this call in order to check how much time has passed. + * The routine reaps jobs on a successful call, so any subsequent calls + * to drmaa_wait SHOULD fail returning an error DRMAA_ERRNO_INVALID_JOB meaning + * that the job has been already reaped. This error is the same as if the job + * was unknown. Failing due to an elapsed timeout has an effect that it is + * possible to issue drmaa_wait multiple times for the same job_id. When + * successful, the rusage information SHALL be provided as an array of strings, + * where each string complies with the format =. The string portion + * contains the amount of resources consumed by the job and is opaque. + * The 'stat' drmaa_wait parameter is used in the drmaa_w* functions for + * providing more detailed information about job termination if available. An + * analogous set of macros is defined in POSIX for analyzing the wait3(2) OUT + * parameter 'stat'. + * + * drmaa_wait() SHALL return DRMAA_ERRNO_SUCCESS on success, otherwise: + * DRMAA_ERRNO_DRM_COMMUNICATION_FAILURE, + * DRMAA_ERRNO_AUTH_FAILURE, + * DRMAA_ERRNO_NO_RUSAGE, + * DRMAA_ERRNO_NO_MEMORY, + * DRMAA_ERRNO_EXIT_TIMEOUT or + * DRMAA_ERRNO_INVALID_JOB. + */ +public static native int drmaa_wait(String job_id, Pointer job_id_out, NativeLong job_id_out_len, + IntByReference stat, NativeLong timeout, /* drmaa_attr_values_t ** */ PointerByReference rusage, + Pointer error_diagnosis, NativeLong error_diag_len); + +/* + * Evaluates into 'exited' a non-zero value if stat was returned for a + * job that terminated normally. A zero value can also indicate that + * altough the job has terminated normally an exit status is not available + * or that it is not known whether the job terminated normally. In both + * cases drmaa_wexitstatus() SHALL NOT provide exit status information. + * A non-zero 'exited' value indicates more detailed diagnosis can be provided + * by means of drmaa_wifsignaled(), drmaa_wtermsig() and drmaa_wcoredump(). + */ +public static native int drmaa_wifexited(IntByReference exited, int stat, Pointer error_diagnosis, + NativeLong error_diag_len); + +/* + * If the OUT parameter 'exited' of drmaa_wifexited() is non-zero, + * this function evaluates into 'exit_code' the exit code that the + * job passed to _exit() (see exit(2)) or exit(3C), or the value that + * the child process returned from main. + */ +public static native int drmaa_wexitstatus(IntByReference exit_status, int stat, Pointer error_diagnosis, + NativeLong error_diag_len); + +/* + * Evaluates into 'signaled' a non-zero value if status was returned + * for a job that terminated due to the receipt of a signal. A zero value + * can also indicate that altough the job has terminated due to the receipt + * of a signal the signal is not available or that it is not known whether + * the job terminated due to the receipt of a signal. In both cases + * drmaa_wtermsig() SHALL NOT provide signal information. + */ +public static native int drmaa_wifsignaled(IntByReference signaled, int stat, Pointer error_diagnosis, + NativeLong error_diag_len); + +/* + * If the OUT parameter 'signaled' of drmaa_wifsignaled(stat) is + * non-zero, this function evaluates into signal a string representation of the + * signal that caused the termination of the job. For signals declared by POSIX, + * the symbolic names SHALL be returned (e.g., SIGABRT, SIGALRM). + * For signals not declared by POSIX, any other string MAY be returned. + */ +public static native int drmaa_wtermsig(Pointer signal, NativeLong signal_len, int stat, + Pointer error_diagnosis, NativeLong error_diag_len); + +/* + * If the OUT parameter 'signaled' of drmaa_wifsignaled(stat) is + * non-zero, this function evaluates into 'core_dumped' a non-zero value + * if a core image of the terminated job was created. + */ +public static native int drmaa_wcoredump(IntByReference core_dumped, int stat, Pointer error_diagnosis, + NativeLong error_diag_len); + +/* + * Evaluates into 'aborted' a non-zero value if 'stat' + * was returned for a job that ended before entering the running state. + */ +public static native int drmaa_wifaborted(IntByReference aborted, int stat, Pointer error_diagnosis, + NativeLong error_diag_len); + + + +/* + * Get the program status of the job identified by 'job_id'. + * The possible values returned in 'remote_ps' and their meanings SHALL be: + * + * DRMAA_PS_UNDETERMINED = 0x00: process status cannot be determined + * DRMAA_PS_QUEUED_ACTIVE = 0x10: job is queued and active + * DRMAA_PS_SYSTEM_ON_HOLD = 0x11: job is queued and in system hold + * DRMAA_PS_USER_ON_HOLD = 0x12: job is queued and in user hold + * DRMAA_PS_USER_SYSTEM_ON_HOLD = 0x13: job is queued and in user and system + * hold + * DRMAA_PS_RUNNING = 0x20: job is running + * DRMAA_PS_SYSTEM_SUSPENDED = 0x21: job is system suspended + * DRMAA_PS_USER_SUSPENDED = 0x22: job is user suspended + * DRMAA_PS_USER_SYSTEM_SUSPENDED = 0x23: job is user and system suspended + * DRMAA_PS_DONE = 0x30: job finished normally + * DRMAA_PS_FAILED = 0x40: job finished, but failed + * + * DRMAA SHOULD always get the status of job_id from DRM system, unless the + * previous status has been DRMAA_PS_FAILED or DRMAA_PS_DONE and the status has + * been successfully cached. Terminated jobs get DRMAA_PS_FAILED status. + * + * drmaa_synchronize() SHALL return DRMAA_ERRNO_SUCCESS on success, otherwise: + * DRMAA_ERRNO_DRM_COMMUNICATION_FAILURE, + * DRMAA_ERRNO_AUTH_FAILURE, + * DRMAA_ERRNO_NO_MEMORY or + * DRMAA_ERRNO_INVALID_JOB. + */ +public static native int drmaa_job_ps(String job_id, IntByReference remote_ps, Pointer error_diagnosis, + NativeLong error_diag_len); + +/* ------------------- auxiliary routines ------------------- */ + +/* + * SHALL return the error message text associated with the errno number. The + * routine SHALL return null string if called with invalid ERRNO number. + */ +public static native String drmaa_strerror(int drmaa_errno); + +/* + * If called before drmaa_init(), it SHALL return a comma delimited default + * DRMAA implementation contacts string, one per each DRM system provided + * implementation. If called after drmaa_init(), it SHALL return the selected + * contact string. The output string is Implementation dependent. + * drmaa_get_contact() SHALL return DRMAA_ERRNO_SUCCESS on success, otherwise: + * DRMAA_ERRNO_INTERNAL_ERROR. + */ +public static native int drmaa_get_contact(Pointer contact, NativeLong contact_len, + Pointer error_diagnosis, NativeLong error_diag_len); + +/* + * OUT major - major version number (non-negative integer) + * OUT minor - minor version number (non-negative integer) + * SHALL return the major and minor version numbers of the DRMAA library; + * for DRMAA 1.0, 'major' is 1 and 'minor' is 0. + */ +public static native int drmaa_version(IntByReference major, IntByReference minor, + Pointer error_diagnosis, NativeLong error_diag_len); + + +/* + * If called before drmaa_init(), it SHALL return a comma delimited DRM systems + * string, one per each DRM system provided implementation. If called after + * drmaa_init(), it SHALL return the selected DRM system. The output string is + * implementation dependent. + * + * drmaa_get_DRM_system() SHALL return DRMAA_ERRNO_SUCCESS on success, + * otherwise: + * DRMAA_ERRNO_INTERNAL_ERROR. + */ +public static native int drmaa_get_DRM_system(Pointer drm_system, NativeLong drm_system_len, + Pointer error_diagnosis, NativeLong error_diag_len); + + +/* + * If called before drmaa_init(), it SHALL return a comma delimited DRMAA + * implementations string, one per each DRM system provided implementation. If + * called after drmaa_init(), it SHALL return the selected DRMAA implementation. + * The output (string) is implementation dependent. drmaa_get_DRM_implementation + * routine SHALL return DRMAA_ERRNO_SUCCESS on success, otherwise: + * DRMAA_ERRNO_INTERNAL_ERROR. + */ +public static native int drmaa_get_DRMAA_implementation(Pointer drmaa_impl, NativeLong drmaa_impl_len, + Pointer error_diagnosis, NativeLong error_diag_len); +} diff --git a/public/java/src/org/broadinstitute/sting/jna/lsf/v7_0_6/LibBat.java b/public/java/src/org/broadinstitute/sting/jna/lsf/v7_0_6/LibBat.java index 2446383ff..d7b34a253 100644 --- a/public/java/src/org/broadinstitute/sting/jna/lsf/v7_0_6/LibBat.java +++ b/public/java/src/org/broadinstitute/sting/jna/lsf/v7_0_6/LibBat.java @@ -91,6 +91,54 @@ public class LibBat { Native.register("bat"); } + // Via support@platform.com: + // For equivalent api of bsub -a "xxx aaa qqq", option -a is not in struct submit, we + // have to use setOption_ to set it. setOption_ can be used in user program by including + // cmd.h or opensource.h of LSF opensource. You can refer to cmd.sub.c in opensource. + // + // Here is a demonstration on the api for bsub -a + // ========================================================================= + // /*define external setOption_ function*/ + // extern int setOption_(int argc, char **argv, char *template, + // struct submit *req, int mask, int mask2, char **errMsg); + // + // int setEsub(char *esub, struct submit *req) { + // int x; + // char *template, *arg[3]; + // /*set esub with the following strings and set array length*/ + // arg[0] = "blah"; + // arg[1] = "-a"; + // arg[2] = test; + // /* -a "test", You can add additional esubs in here. Just make sure they're space delimited. ie. "test mpich lammpi" */ + // x=3; + // /*set template*/ + // template = "a:" + // /*run setOption_()*/ + // if (setOption_(x, arg, template, req, ~0, ~0, ~0, NULL) == -1) { + // return(-1); + // } + // else { + // return(0); + // } + // } + // ========================================================================= + + /** + * Used for setting esub and other options not in struct submit. + * Via support@platform.com + * + * @param argc number of args + * @param argv arguments including a first argument that will not be used + * @param template a colon delimited list of arguments in getopt format + * @param jobSubReq the lsf submit + * @param mask unknown + * @param mask2 unknown + * @param mask3 unknown + * @param errMsg unknown + * @return -1 if the option setting failed + */ + public static native int setOption_(int argc, Pointer argv, String template, submit jobSubReq, int mask, int mask2, int mask3, Pointer errMsg); + /** Max job name length as defined by 'man bsub'. */ public static final int MAX_JOB_NAME_LEN = 4094; @@ -9690,8 +9738,10 @@ public class LibBat { * for a service class. */ - public enum objectives { - GOAL_DEADLINE, GOAL_VELOCITY, GOAL_THROUGHPUT + public static interface objectives { + public static int GOAL_DEADLINE = 0; + public static int GOAL_VELOCITY = 1; + public static int GOAL_THROUGHPUT = 2; } @@ -15109,52 +15159,46 @@ public static class ByValue extends jobArrayElementLog implements Structure.ByVa * \addtogroup _consumertype _consumertype * consumer types */ - public static enum consumerType { + public static interface consumerType { /** * < Queues */ - LIMIT_QUEUES(1), + public static final int LIMIT_QUEUES = 1; /** * < Per-queue */ - LIMIT_PER_QUEUE(2), + public static final int LIMIT_PER_QUEUE = 2; /** * < Users */ - LIMIT_USERS(3), + public static final int LIMIT_USERS = 3; /** * < Per-users */ - LIMIT_PER_USER(4), + public static final int LIMIT_PER_USER = 4; /** * < Hosts */ - LIMIT_HOSTS(5), + public static final int LIMIT_HOSTS = 5; /** * < Per-host */ - LIMIT_PER_HOST(6), + public static final int LIMIT_PER_HOST = 6; /** * < Projects */ - LIMIT_PROJECTS(7), + public static final int LIMIT_PROJECTS = 7; /** * < Per-project */ - LIMIT_PER_PROJECT(8); - - private int value; - - private consumerType(int value) { - this.value = value; - } + public static final int LIMIT_PER_PROJECT = 8; } @@ -19011,20 +19055,27 @@ public static class ByValue extends jobArrayElementLog implements Structure.ByVa /* [] mis-matched in RMS[] */ public static final int RMS_BRACKETS_MISMATCH_ERR = (-22); - public static enum rmsAllocType_t { - RMS_ALLOC_TYPE_UNKNOWN, RMS_ALLOC_TYPE_SLOAD, RMS_ALLOC_TYPE_SNODE, RMS_ALLOC_TYPE_MCONT + public static interface rmsAllocType_t { + public static final int RMS_ALLOC_TYPE_UNKNOWN = 0; + public static final int RMS_ALLOC_TYPE_SLOAD = 1; + public static final int RMS_ALLOC_TYPE_SNODE = 2; + public static final int RMS_ALLOC_TYPE_MCONT = 3; } - public static enum rmsTopology_t { - RMS_TOPOLOGY_UNKNOWN, RMS_TOPOLOGY_PTILE, RMS_TOPOLOGY_NODES + public static interface rmsTopology_t { + public static final int RMS_TOPOLOGY_UNKNOWN = 0; + public static final int RMS_TOPOLOGY_PTILE = 1; + public static final int RMS_TOPOLOGY_NODES = 2; } - public static enum rmsFlags_t { - RMS_FLAGS_UNKNOWN, RMS_FLAGS_RAILS, RMS_FLAGS_RAILMASK + public static interface rmsFlags_t { + public static final int RMS_FLAGS_UNKNOWN = 0; + public static final int RMS_FLAGS_RAILS = 1; + public static final int RMS_FLAGS_RAILMASK = 2; } diff --git a/public/java/src/org/broadinstitute/sting/jna/lsf/v7_0_6/LibLsf.java b/public/java/src/org/broadinstitute/sting/jna/lsf/v7_0_6/LibLsf.java index c7b3de6cf..30b83abc2 100644 --- a/public/java/src/org/broadinstitute/sting/jna/lsf/v7_0_6/LibLsf.java +++ b/public/java/src/org/broadinstitute/sting/jna/lsf/v7_0_6/LibLsf.java @@ -495,14 +495,19 @@ public class LibLsf { - public enum valueType { - LS_BOOLEAN, LS_NUMERIC, LS_STRING, LS_EXTERNAL + public static interface valueType { + public static final int LS_BOOLEAN = 0; + public static final int LS_NUMERIC = 1; + public static final int LS_STRING = 2; + public static final int LS_EXTERNAL = 3; } - public enum orderType { - INCR, DECR, NA + public static interface orderType { + public static final int INCR = 0; + public static final int DECR = 1; + public static final int NA = 2; } @@ -1567,8 +1572,13 @@ public class LibLsf { public static final int NIO_TASK_ALL = 0x03; public static final int NIO_TASK_CONNECTED = 0x04; - public static enum nioType { - NIO_STATUS, NIO_STDOUT, NIO_EOF, NIO_IOERR, NIO_REQUEUE, NIO_STDERR + public static interface nioType { + public static final int NIO_STATUS = 0; + public static final int NIO_STDOUT = 1; + public static final int NIO_EOF = 2; + public static final int NIO_IOERR = 3; + public static final int NIO_REQUEUE = 4; + public static final int NIO_STDERR = 5; } 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 new file mode 100644 index 000000000..3dfd0550d --- /dev/null +++ b/public/java/test/org/broadinstitute/sting/jna/drmaa/v1_0/JnaSessionIntegrationTest.java @@ -0,0 +1,145 @@ +/* + * 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.jna.drmaa.v1_0; + +import org.apache.commons.io.FileUtils; +import org.broadinstitute.sting.BaseTest; +import org.ggf.drmaa.*; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.io.File; +import java.util.*; + +public class JnaSessionIntegrationTest extends BaseTest { + private static final SessionFactory factory = new JnaSessionFactory(); + + @Test + public void testDrmaa() throws Exception { + Session session = factory.getSession(); + Version version = session.getVersion(); + System.out.println(String.format("DRMAA version: %d.%d", version.getMajor(), version.getMinor())); + 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())); + } + + @Test + public void testSubmitEcho() throws Exception { + File outFile = createNetworkTempFile("JnaSessionIntegrationTest-", ".out"); + Session session = factory.getSession(); + session.init(null); + try { + JobTemplate template = session.createJobTemplate(); + template.setRemoteCommand("sh"); + template.setOutputPath(":" + outFile.getAbsolutePath()); + template.setJoinFiles(true); + template.setArgs(Arrays.asList("-c", "echo \"Hello world.\"")); + + String jobId = session.runJob(template); + System.out.println(String.format("Job id %s", jobId)); + session.deleteJobTemplate(template); + + System.out.println("Waiting for job to run: " + jobId); + int remotePs = Session.QUEUED_ACTIVE; + + List runningStatuses = Arrays.asList(Session.QUEUED_ACTIVE, Session.RUNNING); + + while (runningStatuses.contains(remotePs)) { + Thread.sleep(30 * 1000L); + remotePs = session.getJobProgramStatus(jobId); + } + + Assert.assertEquals(remotePs, Session.DONE, "Job status is not DONE."); + + JobInfo jobInfo = session.wait(jobId, Session.TIMEOUT_NO_WAIT); + + Assert.assertTrue(jobInfo.hasExited(), String.format("Job did not exit cleanly: %s", jobId)); + Assert.assertEquals(jobInfo.getExitStatus(), 0, String.format("Exit status for jobId %s is non-zero", jobId)); + if (jobInfo.hasSignaled()) + Assert.fail(String.format("JobId %s exited with signal %s and core dump flag %s", jobId, jobInfo.getTerminatingSignal(), jobInfo.hasCoreDump())); + Assert.assertFalse(jobInfo.wasAborted(), String.format("Job was aborted: %s", jobId)); + } finally { + session.exit(); + } + + Assert.assertTrue(FileUtils.waitFor(outFile, 120), "File not found: " + outFile.getAbsolutePath()); + System.out.println("--- output ---"); + System.out.println(FileUtils.readFileToString(outFile)); + System.out.println("--- output ---"); + Assert.assertTrue(outFile.delete(), "Unable to delete " + outFile.getAbsolutePath()); + System.out.println("Validating that we reached the end of the test without exit."); + } + + @Test + public void testCollectionConversions() { + Collection list = Arrays.asList("a=1", "foo=bar", "empty="); + Map map = new LinkedHashMap(); + map.put("a", "1"); + map.put("foo", "bar"); + map.put("empty", ""); + + Assert.assertEquals(JnaSession.collectionToMap(list), map); + Assert.assertEquals(JnaSession.mapToCollection(map), list); + } + + @Test + public void testLimitConversions() { + Assert.assertEquals(JnaSession.formatLimit(0), "0:00:00"); + Assert.assertEquals(JnaSession.formatLimit(59), "0:00:59"); + Assert.assertEquals(JnaSession.formatLimit(60), "0:01:00"); + Assert.assertEquals(JnaSession.formatLimit(3540), "0:59:00"); + Assert.assertEquals(JnaSession.formatLimit(3599), "0:59:59"); + Assert.assertEquals(JnaSession.formatLimit(7200), "2:00:00"); + Assert.assertEquals(JnaSession.formatLimit(7260), "2:01:00"); + Assert.assertEquals(JnaSession.formatLimit(7261), "2:01:01"); + + Assert.assertEquals(JnaSession.parseLimit("0"), 0); + Assert.assertEquals(JnaSession.parseLimit("00"), 0); + Assert.assertEquals(JnaSession.parseLimit("0:00"), 0); + Assert.assertEquals(JnaSession.parseLimit("00:00"), 0); + Assert.assertEquals(JnaSession.parseLimit("0:00:00"), 0); + + Assert.assertEquals(JnaSession.parseLimit("1"), 1); + Assert.assertEquals(JnaSession.parseLimit("01"), 1); + Assert.assertEquals(JnaSession.parseLimit("0:01"), 1); + Assert.assertEquals(JnaSession.parseLimit("00:01"), 1); + Assert.assertEquals(JnaSession.parseLimit("0:00:01"), 1); + + Assert.assertEquals(JnaSession.parseLimit("10"), 10); + Assert.assertEquals(JnaSession.parseLimit("0:10"), 10); + Assert.assertEquals(JnaSession.parseLimit("00:10"), 10); + Assert.assertEquals(JnaSession.parseLimit("0:00:10"), 10); + + Assert.assertEquals(JnaSession.parseLimit("1:0"), 60); + Assert.assertEquals(JnaSession.parseLimit("1:00"), 60); + Assert.assertEquals(JnaSession.parseLimit("01:00"), 60); + Assert.assertEquals(JnaSession.parseLimit("0:01:00"), 60); + + Assert.assertEquals(JnaSession.parseLimit("1:00:00"), 3600); + + Assert.assertEquals(JnaSession.parseLimit("1:02:03"), 3723); + } +} diff --git a/public/java/test/org/broadinstitute/sting/jna/drmaa/v1_0/LibDrmaaIntegrationTest.java b/public/java/test/org/broadinstitute/sting/jna/drmaa/v1_0/LibDrmaaIntegrationTest.java new file mode 100644 index 000000000..ac2064640 --- /dev/null +++ b/public/java/test/org/broadinstitute/sting/jna/drmaa/v1_0/LibDrmaaIntegrationTest.java @@ -0,0 +1,236 @@ +/* + * 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.jna.drmaa.v1_0; + +import com.sun.jna.Memory; +import com.sun.jna.NativeLong; +import com.sun.jna.Pointer; +import com.sun.jna.StringArray; +import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.PointerByReference; +import org.apache.commons.io.FileUtils; +import org.broadinstitute.sting.BaseTest; +import org.testng.Assert; +import org.testng.annotations.Test; + +import java.io.File; +import java.util.Arrays; +import java.util.List; + +public class LibDrmaaIntegrationTest extends BaseTest { + + @Test + public void testDrmaa() throws Exception { + Memory error = new Memory(LibDrmaa.DRMAA_ERROR_STRING_BUFFER); + int errnum; + + IntByReference major = new IntByReference(); + IntByReference minor = new IntByReference(); + Memory contact = new Memory(LibDrmaa.DRMAA_CONTACT_BUFFER); + Memory drmSystem = new Memory(LibDrmaa.DRMAA_DRM_SYSTEM_BUFFER); + Memory drmaaImplementation = new Memory(LibDrmaa.DRMAA_DRMAA_IMPLEMENTATION_BUFFER); + + errnum = LibDrmaa.drmaa_version(major, minor, error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + Assert.fail(String.format("Could not get version from the DRMAA library: %s", error.getString(0))); + + System.out.println(String.format("DRMAA version: %d.%d", major.getValue(), minor.getValue())); + + errnum = LibDrmaa.drmaa_get_contact(contact, LibDrmaa.DRMAA_CONTACT_BUFFER_LEN, error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + Assert.fail(String.format("Could not get contacts from the DRMAA library: %s", error.getString(0))); + + System.out.println(String.format("DRMAA contact(s): %s", contact.getString(0))); + + errnum = LibDrmaa.drmaa_get_DRM_system(drmSystem, LibDrmaa.DRMAA_DRM_SYSTEM_BUFFER_LEN, error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + Assert.fail(String.format("Could not get DRM system from the DRMAA library: %s", error.getString(0))); + + System.out.println(String.format("DRM system(s): %s", drmSystem.getString(0))); + + errnum = LibDrmaa.drmaa_get_DRMAA_implementation(drmaaImplementation, LibDrmaa.DRMAA_DRMAA_IMPLEMENTATION_BUFFER_LEN, error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + Assert.fail(String.format("Could not get DRMAA implementation from the DRMAA library: %s", error.getString(0))); + + System.out.println(String.format("DRMAA implementation(s): %s", drmaaImplementation.getString(0))); + } + + @Test + public void testSubmitEcho() throws Exception { + Memory error = new Memory(LibDrmaa.DRMAA_ERROR_STRING_BUFFER); + int errnum; + + File outFile = createNetworkTempFile("LibDrmaaIntegrationTest-", ".out"); + + errnum = LibDrmaa.drmaa_init(null, error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + Assert.fail(String.format("Could not initialize the DRMAA library: %s", error.getString(0))); + + try { + PointerByReference jtRef = new PointerByReference(); + Pointer jt; + Memory jobIdMem = new Memory(LibDrmaa.DRMAA_JOBNAME_BUFFER); + String jobId; + IntByReference remotePs = new IntByReference(); + IntByReference stat = new IntByReference(); + PointerByReference rusage = new PointerByReference(); + + errnum = LibDrmaa.drmaa_allocate_job_template(jtRef, error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + Assert.fail(String.format("Could not create job template: %s", error.getString(0))); + + jt = jtRef.getValue(); + + errnum = LibDrmaa.drmaa_set_attribute(jt, LibDrmaa.DRMAA_REMOTE_COMMAND, "sh", error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + Assert.fail(String.format("Could not set attribute \"%s\": %s", LibDrmaa.DRMAA_REMOTE_COMMAND, error.getString(0))); + + errnum = LibDrmaa.drmaa_set_attribute(jt, LibDrmaa.DRMAA_OUTPUT_PATH, ":" + outFile.getAbsolutePath(), error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + Assert.fail(String.format("Could not set attribute \"%s\": %s", LibDrmaa.DRMAA_OUTPUT_PATH, error.getString(0))); + + errnum = LibDrmaa.drmaa_set_attribute(jt, LibDrmaa.DRMAA_JOIN_FILES, "y", error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + Assert.fail(String.format("Could not set attribute \"%s\": %s", LibDrmaa.DRMAA_JOIN_FILES, error.getString(0))); + + StringArray args = new StringArray(new String[] { "-c", "echo \"Hello world.\"" }); + + errnum = LibDrmaa.drmaa_set_vector_attribute(jt, LibDrmaa.DRMAA_V_ARGV, args, error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + Assert.fail(String.format("Could not set attribute \"%s\": %s", LibDrmaa.DRMAA_REMOTE_COMMAND, error.getString(0))); + + errnum = LibDrmaa.drmaa_run_job(jobIdMem, LibDrmaa.DRMAA_JOBNAME_BUFFER_LEN, jt, error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + Assert.fail(String.format("Could not submit job: %s", error.getString(0))); + + jobId = jobIdMem.getString(0); + + System.out.println(String.format("Job id %s", jobId)); + + errnum = LibDrmaa.drmaa_delete_job_template(jt, error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + Assert.fail(String.format("Could not delete job template: %s", error.getString(0))); + + System.out.println("Waiting for job to run: " + jobId); + remotePs.setValue(LibDrmaa.DRMAA_PS.DRMAA_PS_QUEUED_ACTIVE); + + List runningStatuses = Arrays.asList( + LibDrmaa.DRMAA_PS.DRMAA_PS_QUEUED_ACTIVE, LibDrmaa.DRMAA_PS.DRMAA_PS_RUNNING); + + while (runningStatuses.contains(remotePs.getValue())) { + Thread.sleep(30 * 1000L); + + errnum = LibDrmaa.drmaa_job_ps(jobId, remotePs, error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + Assert.fail(String.format("Could not get status for jobId %s: %s", jobId, error.getString(0))); + } + + Assert.assertEquals(remotePs.getValue(), LibDrmaa.DRMAA_PS.DRMAA_PS_DONE, "Job status is not DONE."); + + errnum = LibDrmaa.drmaa_wait(jobId, Pointer.NULL, new NativeLong(0), stat, LibDrmaa.DRMAA_TIMEOUT_NO_WAIT, + rusage, error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + Assert.fail(String.format("Wait failed for jobId %s: %s", jobId, error.getString(0))); + + IntByReference exited = new IntByReference(); + IntByReference exitStatus = new IntByReference(); + IntByReference signaled = new IntByReference(); + Memory signal = new Memory(LibDrmaa.DRMAA_SIGNAL_BUFFER); + IntByReference coreDumped = new IntByReference(); + IntByReference aborted = new IntByReference(); + + errnum = LibDrmaa.drmaa_wifexited(exited, stat.getValue(), error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + Assert.fail(String.format("Exit check failed for jobId %s: %s", jobId, error.getString(0))); + + Assert.assertTrue(exited.getValue() != 0, String.format("Job did not exit cleanly: %s", jobId)); + + errnum = LibDrmaa.drmaa_wexitstatus(exitStatus, stat.getValue(), error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + Assert.fail(String.format("Exit status failed for jobId %s: %s", jobId, error.getString(0))); + + Assert.assertEquals(exitStatus.getValue(), 0, String.format("Exit status for jobId %s is non-zero", jobId)); + + errnum = LibDrmaa.drmaa_wifsignaled(signaled, stat.getValue(), error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + Assert.fail(String.format("Signaled check failed for jobId %s: %s", jobId, error.getString(0))); + + if (signaled.getValue() != 0) { + errnum = LibDrmaa.drmaa_wtermsig(signal, LibDrmaa.DRMAA_SIGNAL_BUFFER_LEN, stat.getValue(), error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + Assert.fail(String.format("Signal lookup failed for jobId %s: %s", jobId, error.getString(0))); + + errnum = LibDrmaa.drmaa_wcoredump(coreDumped, stat.getValue(), error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + Assert.fail(String.format("Core dump check failed for jobId %s: %s", jobId, error.getString(0))); + + Assert.fail(String.format("JobId %s exited with signal %s and core dump flag %d", jobId, signal.getString(0), coreDumped.getValue())); + } + + errnum = LibDrmaa.drmaa_wifaborted(aborted, stat.getValue(), error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + Assert.fail(String.format("Aborted check failed for jobId %s: %s", jobId, error.getString(0))); + + Assert.assertTrue(aborted.getValue() == 0, String.format("Job was aborted: %s", jobId)); + + } finally { + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) { + LibDrmaa.drmaa_exit(error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + } else { + errnum = LibDrmaa.drmaa_exit(error, LibDrmaa.DRMAA_ERROR_STRING_BUFFER_LEN); + + if (errnum != LibDrmaa.DRMAA_ERRNO.DRMAA_ERRNO_SUCCESS) + Assert.fail(String.format("Could not shut down the DRMAA library: %s", error.getString(0))); + } + } + + Assert.assertTrue(FileUtils.waitFor(outFile, 120), "File not found: " + outFile.getAbsolutePath()); + System.out.println("--- output ---"); + System.out.println(FileUtils.readFileToString(outFile)); + System.out.println("--- output ---"); + Assert.assertTrue(outFile.delete(), "Unable to delete " + outFile.getAbsolutePath()); + System.out.println("Validating that we reached the end of the test without exit."); + } +} diff --git a/public/java/test/org/broadinstitute/sting/jna/lsf/v7_0_6/LibBatIntegrationTest.java b/public/java/test/org/broadinstitute/sting/jna/lsf/v7_0_6/LibBatIntegrationTest.java index 77db34cbc..b4fb5cfa3 100644 --- a/public/java/test/org/broadinstitute/sting/jna/lsf/v7_0_6/LibBatIntegrationTest.java +++ b/public/java/test/org/broadinstitute/sting/jna/lsf/v7_0_6/LibBatIntegrationTest.java @@ -91,7 +91,7 @@ public class LibBatIntegrationTest extends BaseTest { } @Test - public void testSubmitEcho() throws InterruptedException { + public void testSubmitEcho() throws Exception { String queue = "hour"; File outFile = createNetworkTempFile("LibBatIntegrationTest-", ".out"); @@ -114,6 +114,10 @@ public class LibBatIntegrationTest extends BaseTest { req.command = "echo \"Hello world.\""; + String[] argv = {"", "-a", "tv"}; + int setOptionResult = LibBat.setOption_(argv.length, new StringArray(argv), "a:", req, ~0, ~0, ~0, null); + Assert.assertTrue(setOptionResult != -1, "setOption_ returned -1"); + submitReply reply = new submitReply(); long jobId = LibBat.lsb_submit(req, reply); @@ -142,6 +146,9 @@ public class LibBatIntegrationTest extends BaseTest { Assert.assertTrue(Utils.isFlagSet(jobStatus, LibBat.JOB_STAT_DONE), String.format("Unexpected job status: 0x%02x", jobStatus)); Assert.assertTrue(FileUtils.waitFor(outFile, 120), "File not found: " + outFile.getAbsolutePath()); + System.out.println("--- output ---"); + System.out.println(FileUtils.readFileToString(outFile)); + System.out.println("--- output ---"); Assert.assertTrue(outFile.delete(), "Unable to delete " + outFile.getAbsolutePath()); Assert.assertEquals(reply.queue, req.queue, "LSF reply queue does not match requested queue."); System.out.println("Validating that we reached the end of the test without exit."); diff --git a/public/scala/src/org/broadinstitute/sting/queue/QSettings.scala b/public/scala/src/org/broadinstitute/sting/queue/QSettings.scala index 05c1a1775..648f9ffef 100644 --- a/public/scala/src/org/broadinstitute/sting/queue/QSettings.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/QSettings.scala @@ -41,12 +41,27 @@ class QSettings { @Argument(fullName="job_queue", shortName="jobQueue", doc="Default queue for compute farm jobs.", required=false) var jobQueue: String = _ - @Argument(fullName="job_priority", shortName="jobPriority", doc="Default priority for jobs.", required=false) + @Argument(fullName="job_priority", shortName="jobPriority", doc="Default priority for jobs. Min = 0, Max = 100", required=false) var jobPriority: Option[Int] = None - @Argument(fullName="default_memory_limit", shortName="memLimit", doc="Default memory limit for jobs, in gigabytes.", required=false) + @Argument(fullName="job_native_arg", shortName="jobNative", doc="Native arguments to pass to the job runner.", required=false) + var jobNativeArgs: List[String] = Nil + + @Argument(fullName="job_resource_request", shortName="jobResReq", doc="Resource requests to pass to the job runner.", required=false) + var jobResourceRequests: List[String] = Nil + + @Argument(fullName="job_environment_name", shortName="jobEnv", doc="Environment names for the job runner.", required=false) + var jobEnvironmentNames: List[String] = Nil + + @Argument(fullName="memory_limit", shortName="memLimit", doc="Default memory limit for jobs, in gigabytes.", required=false) var memoryLimit: Option[Double] = None + @Argument(fullName="resident_memory_limit", shortName="resMemLimit", doc="Default resident memory limit for jobs, in gigabytes.", required=false) + var residentLimit: Option[Double] = None + + @Argument(fullName="resident_memory_request", shortName="resMemReq", doc="Default resident memory request for jobs, in gigabytes.", required=false) + var residentRequest: Option[Double] = None + @Argument(fullName="run_directory", shortName="runDir", doc="Root directory to run functions from.", required=false) var runDirectory = new File(".") diff --git a/public/scala/src/org/broadinstitute/sting/queue/engine/CommandLineJobRunner.scala b/public/scala/src/org/broadinstitute/sting/queue/engine/CommandLineJobRunner.scala index 2e3108136..2c960d8f6 100755 --- a/public/scala/src/org/broadinstitute/sting/queue/engine/CommandLineJobRunner.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/engine/CommandLineJobRunner.scala @@ -51,10 +51,21 @@ trait CommandLineJobRunner extends JobRunner[CommandLineFunction] with Logging { /** The last time the status was updated */ protected var lastStatusUpdate: Long = _ - final override def status = this.lastStatus + /** The runner specific priority for a minimum priority job */ + protected val minRunnerPriority = 0 - def residentRequestMB: Option[Double] = function.memoryLimit.map(_ * 1024) - def residentLimitMB: Option[Double] = residentRequestMB.map( _ * 1.2 ) + /** The runner specific priority for a maximum priority job */ + protected val maxRunnerPriority = 0 + + /** The priority of the function in the range defined by the runner */ + protected def functionPriority = { + function.jobPriority.map { priority => + (((priority / 100D) * (maxRunnerPriority - minRunnerPriority)) + minRunnerPriority). + round.intValue() min maxRunnerPriority max minRunnerPriority + } + } + + final override def status = this.lastStatus override def init() { super.init() diff --git a/public/scala/src/org/broadinstitute/sting/queue/engine/JobManager.scala b/public/scala/src/org/broadinstitute/sting/queue/engine/JobManager.scala index 30187f7e2..9aeb3a8ee 100644 --- a/public/scala/src/org/broadinstitute/sting/queue/engine/JobManager.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/engine/JobManager.scala @@ -30,6 +30,9 @@ import org.broadinstitute.sting.queue.function.QFunction * Creates and stops JobRunners */ trait JobManager[TFunction <: QFunction, TRunner <: JobRunner[TFunction]] { + def init() {} + def exit() {} + /** The class type of the runner. Available at runtime even after erasure. */ def functionType: Class[TFunction] @@ -52,6 +55,5 @@ trait JobManager[TFunction <: QFunction, TRunner <: JobRunner[TFunction]] { * Stops a list of functions. * @param runners Runners to stop. */ - def tryStop(runners: Set[TRunner]) { - } + def tryStop(runners: Set[TRunner]) {} } diff --git a/public/scala/src/org/broadinstitute/sting/queue/engine/QGraph.scala b/public/scala/src/org/broadinstitute/sting/queue/engine/QGraph.scala index a52e9c561..766d9db94 100755 --- a/public/scala/src/org/broadinstitute/sting/queue/engine/QGraph.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/engine/QGraph.scala @@ -361,6 +361,13 @@ class QGraph extends Logging { settings.jobRunner = "Shell" commandLineManager = commandLinePluginManager.createByName(settings.jobRunner) + for (mgr <- managers) { + if (mgr != null) { + val manager = mgr.asInstanceOf[JobManager[QFunction,JobRunner[QFunction]]] + manager.init() + } + } + if (settings.startFromScratch) logger.info("Removing outputs from previous runs.") @@ -1034,18 +1041,26 @@ class QGraph extends Logging { for (mgr <- managers) { if (mgr != null) { val manager = mgr.asInstanceOf[JobManager[QFunction,JobRunner[QFunction]]] - val managerRunners = runners - .filter(runner => manager.runnerType.isAssignableFrom(runner.getClass)) - .asInstanceOf[Set[JobRunner[QFunction]]] - if (managerRunners.size > 0) - try { - manager.tryStop(managerRunners) - } catch { - case e => /* ignore */ + try { + val managerRunners = runners + .filter(runner => manager.runnerType.isAssignableFrom(runner.getClass)) + .asInstanceOf[Set[JobRunner[QFunction]]] + if (managerRunners.size > 0) + try { + manager.tryStop(managerRunners) + } catch { + case e => /* ignore */ + } + for (runner <- managerRunners) { + try { + runner.cleanup() + } catch { + case e => /* ignore */ + } } - for (runner <- managerRunners) { + } finally { try { - runner.cleanup() + manager.exit() } catch { case e => /* ignore */ } diff --git a/public/scala/src/org/broadinstitute/sting/queue/engine/drmaa/DrmaaJobManager.scala b/public/scala/src/org/broadinstitute/sting/queue/engine/drmaa/DrmaaJobManager.scala new file mode 100644 index 000000000..4c9cc1890 --- /dev/null +++ b/public/scala/src/org/broadinstitute/sting/queue/engine/drmaa/DrmaaJobManager.scala @@ -0,0 +1,61 @@ +/* + * 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.engine.drmaa + +import org.broadinstitute.sting.queue.function.CommandLineFunction +import org.broadinstitute.sting.queue.engine.CommandLineJobManager +import org.broadinstitute.sting.jna.drmaa.v1_0.JnaSessionFactory +import org.ggf.drmaa.Session + +/** + * Runs jobs using DRMAA + */ +class DrmaaJobManager extends CommandLineJobManager[DrmaaJobRunner] { + protected var session: Session = _ + + protected def newSession() = new JnaSessionFactory().getSession + protected def contact = null + + override def init() { + session = newSession() + session.init(contact) + } + + override def exit() { + session.exit() + } + + def runnerType = classOf[DrmaaJobRunner] + def create(function: CommandLineFunction) = new DrmaaJobRunner(session, function) + + override def updateStatus(runners: Set[DrmaaJobRunner]) = { + var updatedRunners = Set.empty[DrmaaJobRunner] + runners.foreach(runner => if (runner.updateJobStatus()) {updatedRunners += runner}) + updatedRunners + } + override def tryStop(runners: Set[DrmaaJobRunner]) { + runners.filterNot(_.jobId == null).foreach(_.tryStop()) + } +} diff --git a/public/scala/src/org/broadinstitute/sting/queue/engine/drmaa/DrmaaJobRunner.scala b/public/scala/src/org/broadinstitute/sting/queue/engine/drmaa/DrmaaJobRunner.scala new file mode 100644 index 000000000..b48dcd2a9 --- /dev/null +++ b/public/scala/src/org/broadinstitute/sting/queue/engine/drmaa/DrmaaJobRunner.scala @@ -0,0 +1,149 @@ +/* + * 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.engine.drmaa + +import org.broadinstitute.sting.queue.QException +import org.broadinstitute.sting.queue.util.{Logging,Retry} +import org.broadinstitute.sting.queue.function.CommandLineFunction +import org.broadinstitute.sting.queue.engine.{RunnerStatus, CommandLineJobRunner} +import java.util.Collections +import org.ggf.drmaa._ + +/** + * Runs jobs using DRMAA. + */ +class DrmaaJobRunner(val session: Session, val function: CommandLineFunction) extends CommandLineJobRunner with Logging { + /** Job Id of the currently executing job. */ + var jobId: String = _ + override def jobIdString = jobId + + // Set the display name to < 512 characters of the description + // NOTE: Not sure if this is configuration specific? + protected val jobNameLength = 500 + protected val jobNameFilter = """[^A-Za-z0-9_]""" + protected def functionNativeSpec = function.jobNativeArgs.mkString(" ") + + def start() { + session.synchronized { + val drmaaJob: JobTemplate = session.createJobTemplate + + drmaaJob.setJobName(function.description.take(jobNameLength).replaceAll(jobNameFilter, "_")) + + // Set the current working directory + drmaaJob.setWorkingDirectory(function.commandDirectory.getPath) + + // Set the output file for stdout + drmaaJob.setOutputPath(":" + function.jobOutputFile.getPath) + + // If the error file is set specify the separate output for stderr + // Otherwise join with stdout + if (function.jobErrorFile != null) { + drmaaJob.setErrorPath(":" + function.jobErrorFile.getPath) + } else { + drmaaJob.setJoinFiles(true) + } + + drmaaJob.setNativeSpecification(functionNativeSpec) + + // Instead of running the function.commandLine, run "sh " + drmaaJob.setRemoteCommand("sh") + drmaaJob.setArgs(Collections.singletonList(jobScript.toString)) + + // Allow advanced users to update the request via QFunction.updateJobRun() + updateJobRun(drmaaJob) + + updateStatus(RunnerStatus.RUNNING) + + // Start the job and store the id so it can be killed in tryStop + try { + Retry.attempt(() => { + try { + jobId = session.runJob(drmaaJob) + } catch { + case de: DrmaaException => throw new QException("Unable to submit job: " + de.getLocalizedMessage) + } + }, 1, 5, 10) + } finally { + // Prevent memory leaks + session.deleteJobTemplate(drmaaJob) + } + logger.info("Submitted job id: " + jobId) + } + } + + def updateJobStatus() = { + session.synchronized { + var returnStatus: RunnerStatus.Value = null + + try { + val jobStatus = session.getJobProgramStatus(jobId); + jobStatus match { + case Session.QUEUED_ACTIVE => returnStatus = RunnerStatus.RUNNING + case Session.DONE => + val jobInfo: JobInfo = session.wait(jobId, Session.TIMEOUT_NO_WAIT) + if ((jobInfo.hasExited && jobInfo.getExitStatus != 0) + || jobInfo.hasSignaled + || jobInfo.wasAborted) + returnStatus = RunnerStatus.FAILED + else + returnStatus = RunnerStatus.DONE + case Session.FAILED => returnStatus = RunnerStatus.FAILED + case Session.UNDETERMINED => logger.warn("Unable to determine status of job id " + jobId) + case _ => returnStatus = RunnerStatus.RUNNING + } + } catch { + // getJobProgramStatus will throw an exception once wait has run, as the + // job will be reaped. If the status is currently DONE or FAILED, return + // the status. + case de: DrmaaException => + if (lastStatus == RunnerStatus.DONE || lastStatus == RunnerStatus.FAILED) + returnStatus = lastStatus + else + logger.warn("Unable to determine status of job id " + jobId, de) + } + + if (returnStatus != null) { + updateStatus(returnStatus) + true + } else { + false + } + } + } + + def tryStop() { + session.synchronized { + try { + // Stop runners. SIGTERM(15) is preferred to SIGKILL(9). + // Only way to send SIGTERM is for the Sys Admin set the terminate_method + // resource of the designated queue to SIGTERM + session.control(jobId, Session.TERMINATE) + } catch { + case e => + logger.error("Unable to kill job " + jobId, e) + } + } + } +} diff --git a/public/scala/src/org/broadinstitute/sting/queue/engine/gridengine/GridEngineJobManager.scala b/public/scala/src/org/broadinstitute/sting/queue/engine/gridengine/GridEngineJobManager.scala index 78bd2cc78..7299036ed 100644 --- a/public/scala/src/org/broadinstitute/sting/queue/engine/gridengine/GridEngineJobManager.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/engine/gridengine/GridEngineJobManager.scala @@ -24,13 +24,9 @@ package org.broadinstitute.sting.queue.engine.gridengine -import org.broadinstitute.sting.queue.engine.CommandLineJobManager import org.broadinstitute.sting.queue.function.CommandLineFunction +import org.broadinstitute.sting.queue.engine.drmaa.DrmaaJobManager -class GridEngineJobManager extends CommandLineJobManager[GridEngineJobRunner] { - def runnerType = classOf[GridEngineJobRunner] - def create(function: CommandLineFunction) = new GridEngineJobRunner(function) - - override def updateStatus(runners: Set[GridEngineJobRunner]) = { GridEngineJobRunner.updateStatus(runners) } - override def tryStop(runners: Set[GridEngineJobRunner]) { GridEngineJobRunner.tryStop(runners) } +class GridEngineJobManager extends DrmaaJobManager { + override def create(function: CommandLineFunction) = new GridEngineJobRunner(session, function) } diff --git a/public/scala/src/org/broadinstitute/sting/queue/engine/gridengine/GridEngineJobRunner.scala b/public/scala/src/org/broadinstitute/sting/queue/engine/gridengine/GridEngineJobRunner.scala index 8c639b5bb..96e3ffd95 100644 --- a/public/scala/src/org/broadinstitute/sting/queue/engine/gridengine/GridEngineJobRunner.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/engine/gridengine/GridEngineJobRunner.scala @@ -24,203 +24,52 @@ package org.broadinstitute.sting.queue.engine.gridengine -import org.broadinstitute.sting.queue.QException -import org.broadinstitute.sting.queue.util.{Logging,Retry} +import org.broadinstitute.sting.queue.util.Logging import org.broadinstitute.sting.queue.function.CommandLineFunction -import org.broadinstitute.sting.queue.engine.{RunnerStatus, CommandLineJobRunner} -import org.ggf.drmaa.{DrmaaException,JobInfo,JobTemplate,Session,SessionFactory} -import java.util.Collections +import org.broadinstitute.sting.queue.engine.drmaa.DrmaaJobRunner +import org.ggf.drmaa.Session /** * Runs jobs on a Grid Engine compute cluster. */ -class GridEngineJobRunner(val function: CommandLineFunction) extends CommandLineJobRunner with Logging { - // Run the static initializer for GridEngineJobRunner - GridEngineJobRunner - - /** Job Id of the currently executing job. */ - private var jobId: String = _ - override def jobIdString = jobId - - def start() { - GridEngineJobRunner.gridEngineSession.synchronized { - val gridEngineJob: JobTemplate = GridEngineJobRunner.gridEngineSession.createJobTemplate - - // Force the remote environment to inherit local environment settings - var nativeSpecString: String = "-V" - - // Set the display name to < 512 characters of the description - // NOTE: Not sure if this is configuration specific? - gridEngineJob.setJobName(GridEngineJobRunner.toJobName(function.description.take(500))) - - // Set the output file for stdout - gridEngineJob.setOutputPath(":" + function.jobOutputFile.getPath) - - // Set the current working directory - gridEngineJob.setWorkingDirectory(function.commandDirectory.getPath) - - // If the error file is set specify the separate output for stderr - // Otherwise join with stdout - if (Option(function.jobErrorFile) != None) { - gridEngineJob.setErrorPath(":" + function.jobErrorFile.getPath) - } else { - gridEngineJob.setJoinFiles(true) - } - - // If a project name is set specify the project name - if (Option(function.jobProject) != None) { - nativeSpecString += " -P " + function.jobProject - } - - // If the job queue is set specify the job queue - if (Option(function.jobQueue) != None) { - nativeSpecString += " -q " + function.jobQueue - } - - // If the resident set size is requested pass on the memory request - if (residentRequestMB.isDefined) { - nativeSpecString += " -l mem_free=%dM".format(residentRequestMB.get.ceil.toInt) - } - - // If the resident set size limit is defined specify the memory limit - if (residentLimitMB.isDefined) { - nativeSpecString += " -l h_rss=%dM".format(residentLimitMB.get.ceil.toInt) - } - - // If the priority is set (user specified Int) specify the priority - if (function.jobPriority.isDefined) { - nativeSpecString += " -p " + function.jobPriority.get - } - - gridEngineJob.setNativeSpecification(nativeSpecString) - - // Instead of running the function.commandLine, run "sh " - gridEngineJob.setRemoteCommand("sh") - gridEngineJob.setArgs(Collections.singletonList(jobScript.toString)) - - // Allow advanced users to update the request via QFunction.updateJobRun() - updateJobRun(gridEngineJob) - - updateStatus(RunnerStatus.RUNNING) - - // Start the job and store the id so it can be killed in tryStop - try { - Retry.attempt(() => { - try { - jobId = GridEngineJobRunner.gridEngineSession.runJob(gridEngineJob) - } catch { - case de: DrmaaException => throw new QException("Unable to submit job: " + de.getLocalizedMessage) - } - }, 1, 5, 10) - } finally { - // Prevent memory leaks - GridEngineJobRunner.gridEngineSession.deleteJobTemplate(gridEngineJob) - } - logger.info("Submitted Grid Engine job id: " + jobId) - } - } -} - -object GridEngineJobRunner extends Logging { - private val gridEngineSession = SessionFactory.getFactory.getSession - - initGridEngine() - - /** - * Initialize the Grid Engine library. - */ - private def initGridEngine() { - gridEngineSession.synchronized { - try { - gridEngineSession.init("") - } catch { - case de: DrmaaException => - logger.error("Issue initializing Grid Engine", de) - throw new QException("init() failed", de) - } - } - } - - /** - * Updates the status of a list of jobs. - * @param runners Runners to update. - * @return runners which were updated. - */ - def updateStatus(runners: Set[GridEngineJobRunner]) = { - var updatedRunners = Set.empty[GridEngineJobRunner] - gridEngineSession.synchronized { - runners.foreach(runner => if (updateRunnerStatus(runner)) {updatedRunners += runner}) - } - updatedRunners - } - - /** - * Tries to stop any running jobs. - * @param runners Runners to stop. - */ - def tryStop(runners: Set[GridEngineJobRunner]) { - // Stop runners. SIGTERM(15) is preferred to SIGKILL(9). - // Only way to send SIGTERM is for the Sys Admin set the terminate_method - // resource of the designated queue to SIGTERM - gridEngineSession.synchronized { - for (runner <- runners.filterNot(runner => Option(runner.jobId) == None)) { - try { - gridEngineSession.control(runner.jobId, Session.TERMINATE) - } catch { - case e => - logger.error("Unable to kill job " + runner.jobId, e) - } - } - gridEngineSession.exit() - } - } - - private def updateRunnerStatus(runner: GridEngineJobRunner): Boolean = { - var returnStatus: RunnerStatus.Value = null - - try { - val jobStatus = gridEngineSession.getJobProgramStatus(runner.jobId); - jobStatus match { - case Session.QUEUED_ACTIVE => returnStatus = RunnerStatus.RUNNING - case Session.DONE => - val jobInfo: JobInfo = gridEngineSession.wait(runner.jobId, Session.TIMEOUT_NO_WAIT) - if ((jobInfo.hasExited && jobInfo.getExitStatus > 0) - || jobInfo.hasSignaled - || jobInfo.wasAborted) - returnStatus = RunnerStatus.FAILED - else - returnStatus = RunnerStatus.DONE - case Session.FAILED => returnStatus = RunnerStatus.FAILED - case Session.UNDETERMINED => logger.warn("Unable to determine status of Grid Engine job id " + runner.jobId) - case _ => returnStatus = RunnerStatus.RUNNING - } - } catch { - // getJobProgramStatus will throw an exception once wait has run, as the - // job will be reaped. If the status is currently DONE or FAILED, return - // the status. - case de: DrmaaException => - if (runner.lastStatus == RunnerStatus.DONE || runner.lastStatus == RunnerStatus.FAILED) - returnStatus = runner.lastStatus - else - logger.warn("Unable to determine status of Grid Engine job id " + runner.jobId, de) - } - - if (returnStatus != null) { - runner.updateStatus(returnStatus) - true - } else { - false - } - } - - // Reap what we've sown - override def finalize() { - gridEngineSession.exit() - } - +class GridEngineJobRunner(session: Session, function: CommandLineFunction) extends DrmaaJobRunner(session, function) with Logging { // Grid Engine disallows certain characters from being in job names. // This replaces all illegal characters with underscores - private def toJobName(name: String): String = { - name.replaceAll("""[\n\t\r/:@\\*?]""", "_") + protected override val jobNameFilter = """[\n\t\r/:@\\*?]""" + protected override val minRunnerPriority = -1023 + protected override val maxRunnerPriority = 0 + + override protected def functionNativeSpec = { + // Force the remote environment to inherit local environment settings + var nativeSpec: String = "-V" + + // If a project name is set specify the project name + if (function.jobProject != null) + nativeSpec += " -P " + function.jobProject + + // If the job queue is set specify the job queue + if (function.jobQueue != null) + nativeSpec += " -q " + function.jobQueue + + // If the resident set size is requested pass on the memory request + if (function.residentRequest.isDefined) + nativeSpec += " -l mem_free=%dM".format(function.residentRequest.map(_ * 1024).get.ceil.toInt) + + // If the resident set size limit is defined specify the memory limit + if (function.residentLimit.isDefined) + nativeSpec += " -l h_rss=%dM".format(function.residentLimit.map(_ * 1024).get.ceil.toInt) + + // Pass on any job resource requests + nativeSpec += function.jobResourceRequests.map(" -l " + _).mkString + + // Pass on any job environment names + nativeSpec += function.jobEnvironmentNames.map(" -pe " + _).mkString + + // If the priority is set specify the priority + val priority = functionPriority + if (priority.isDefined) + nativeSpec += " -p " + priority.get + + (nativeSpec + " " + super.functionNativeSpec).trim() } } diff --git a/public/scala/src/org/broadinstitute/sting/queue/engine/lsf/Lsf706JobRunner.scala b/public/scala/src/org/broadinstitute/sting/queue/engine/lsf/Lsf706JobRunner.scala index 46dd08332..bb711344c 100644 --- a/public/scala/src/org/broadinstitute/sting/queue/engine/lsf/Lsf706JobRunner.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/engine/lsf/Lsf706JobRunner.scala @@ -34,6 +34,8 @@ import org.broadinstitute.sting.jna.lsf.v7_0_6.LibBat.{submitReply, submit} import com.sun.jna.ptr.IntByReference import org.broadinstitute.sting.queue.engine.{RunnerStatus, CommandLineJobRunner} import com.sun.jna.{Structure, StringArray, NativeLong} +import java.util.regex.Pattern +import java.lang.StringBuffer /** * Runs jobs on an LSF compute cluster. @@ -47,12 +49,22 @@ class Lsf706JobRunner(val function: CommandLineFunction) extends CommandLineJobR private var jobId = -1L override def jobIdString = jobId.toString + protected override val minRunnerPriority = 1 + protected override val maxRunnerPriority = Lsf706JobRunner.maxUserPriority + + private val selectString = new StringBuffer() + private val usageString = new StringBuffer() + private val requestString = new StringBuffer() + /** * Dispatches the function on the LSF cluster. * @param function Command to run. */ def start() { Lsf706JobRunner.lsfLibLock.synchronized { + + parseResourceRequest() + val request = new submit for (i <- 0 until LibLsf.LSF_RLIM_NLIMITS) request.rLimits(i) = LibLsf.DEFAULT_RLIMIT; @@ -81,28 +93,45 @@ class Lsf706JobRunner(val function: CommandLineFunction) extends CommandLineJobR } // If the resident set size is requested pass on the memory request - if (residentRequestMB.isDefined) { - val memInUnits = Lsf706JobRunner.convertUnits(residentRequestMB.get) - request.resReq = "select[mem>%1$d] rusage[mem=%1$d]".format(memInUnits) + if (function.residentRequest.isDefined) { + val memInUnits = Lsf706JobRunner.convertUnits(function.residentRequest.get) + appendRequest("select", selectString, "&&", "mem>%d".format(memInUnits)) + appendRequest("rusage", usageString, ",", "mem=%d".format(memInUnits)) + } + + val resReq = getResourceRequest + if (resReq.length > 0) { + request.resReq = resReq request.options |= LibBat.SUB_RES_REQ } // If the resident set size limit is defined specify the memory limit - if (residentLimitMB.isDefined) { - val memInUnits = Lsf706JobRunner.convertUnits(residentLimitMB.get) + if (function.residentLimit.isDefined) { + val memInUnits = Lsf706JobRunner.convertUnits(function.residentLimit.get) request.rLimits(LibLsf.LSF_RLIMIT_RSS) = memInUnits } // If the priority is set (user specified Int) specify the priority - if (function.jobPriority.isDefined) { - request.userPriority = function.jobPriority.get + val priority = functionPriority + if (priority.isDefined) { + request.userPriority = priority.get request.options2 |= LibBat.SUB2_JOB_PRIORITY } - // Broad specific requirement, our esub requires there be a project - // else it will spit out a warning to stdout. see $LSF_SERVERDIR/esub - request.projectName = if (function.jobProject != null) function.jobProject else "Queue" - request.options |= LibBat.SUB_PROJECT_NAME + // Set the project to either the function or LSF default + val project = if (function.jobProject != null) function.jobProject else Lsf706JobRunner.defaultProject + if (project != null) { + request.projectName = project + request.options |= LibBat.SUB_PROJECT_NAME + } + + // Set the esub names based on the job envorinment names + if (!function.jobEnvironmentNames.isEmpty) { + val argv = Array("", "-a", function.jobEnvironmentNames.mkString(" ")) + val setOptionResult = LibBat.setOption_(argv.length, new StringArray(argv), "a:", request, ~0, ~0, ~0, null); + if (setOptionResult == -1) + throw new QException("setOption_() returned -1 while setting esub"); + } // LSF specific: get the max runtime for the jobQueue and pass it for this job request.rLimits(LibLsf.LSF_RLIMIT_RUN) = Lsf706JobRunner.getRlimitRun(function.jobQueue) @@ -132,6 +161,41 @@ class Lsf706JobRunner(val function: CommandLineFunction) extends CommandLineJobR logger.debug("Job Id %s status / exitStatus / exitInfo: ??? / ??? / ???".format(jobId)) super.checkUnknownStatus() } + + private def parseResourceRequest() { + requestString.setLength(0) + selectString.setLength(0) + usageString.setLength(0) + + requestString.append(function.jobResourceRequests.mkString(" ")) + extractSection(requestString, "select", selectString) + extractSection(requestString, "rusage", usageString) + } + + private def extractSection(requestString: StringBuffer, section: String, sectionString: StringBuffer) { + val pattern = Pattern.compile(section + "\\s*\\[[^\\]]+\\]\\s*"); + val matcher = pattern.matcher(requestString.toString) + if (matcher.find()) { + sectionString.setLength(0) + sectionString.append(matcher.group().trim()) + + val sb = new StringBuffer + matcher.appendReplacement(sb, "") + matcher.appendTail(sb) + + requestString.setLength(0) + requestString.append(sb) + } + } + + private def appendRequest(section: String, sectionString: StringBuffer, separator: String, request: String) { + if (sectionString.length() == 0) + sectionString.append(section).append("[").append(request).append("]") + else + sectionString.insert(sectionString.length() - 1, separator + request) + } + + private def getResourceRequest = "%s %s %s".format(selectString, usageString, requestString).trim() } object Lsf706JobRunner extends Logging { @@ -141,15 +205,23 @@ object Lsf706JobRunner extends Logging { /** Number of seconds for a non-normal exit status before we give up on expecting LSF to retry the function. */ private val retryExpiredSeconds = 5 * 60 - initLsf() - /** * Initialize the Lsf library. */ - private def initLsf() { + private val (defaultQueue, defaultProject, maxUserPriority) = { lsfLibLock.synchronized { if (LibBat.lsb_init("Queue") < 0) throw new QException(LibBat.lsb_sperror("lsb_init() failed")) + + val parameterInfo = LibBat.lsb_parameterinfo(null, null, 0); + var defaultQueue: String = parameterInfo.defaultQueues + val defaultProject = parameterInfo.defaultProject + val maxUserPriority = parameterInfo.maxUserPriority + + if (defaultQueue != null && defaultQueue.indexOf(' ') > 0) + defaultQueue = defaultQueue.split(" ")(0) + + (defaultQueue, defaultProject, maxUserPriority) } } @@ -249,17 +321,6 @@ object Lsf706JobRunner extends Logging { } } - /** The name of the default queue. */ - private lazy val defaultQueue: String = { - lsfLibLock.synchronized { - val numQueues = new IntByReference(1) - val queueInfo = LibBat.lsb_queueinfo(null, numQueues, null, null, 0) - if (queueInfo == null) - throw new QException(LibBat.lsb_sperror("Unable to get LSF queue info for the default queue")) - queueInfo.queue - } - } - /** The run limits for each queue. */ private var queueRlimitRun = Map.empty[String,Int] @@ -299,15 +360,15 @@ object Lsf706JobRunner extends Logging { Structure.autoRead(unitsParam.asInstanceOf[Array[Structure]]) unitsParam(0).paramValue match { - case "MB" => 1D - case "GB" => 1024D - case "TB" => 1024D * 1024 - case "PB" => 1024D * 1024 * 1024 - case "EB" => 1024D * 1024 * 1024 * 1024 - case null => 1D + case "MB" => 1 / 1024D + case "GB" => 1D + case "TB" => 1024D + case "PB" => 1024D * 1024 + case "EB" => 1024D * 1024 * 1024 + case null => 1 / 1024D } } } - private def convertUnits(mb: Double) = (mb / unitDivisor).ceil.toInt + private def convertUnits(gb: Double) = (gb / unitDivisor).ceil.toInt } diff --git a/public/scala/src/org/broadinstitute/sting/queue/function/CommandLineFunction.scala b/public/scala/src/org/broadinstitute/sting/queue/function/CommandLineFunction.scala index c62fdcd7c..ff77503ac 100644 --- a/public/scala/src/org/broadinstitute/sting/queue/function/CommandLineFunction.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/function/CommandLineFunction.scala @@ -11,12 +11,27 @@ trait CommandLineFunction extends QFunction with Logging { /** Upper memory limit */ var memoryLimit: Option[Double] = None + /** Resident memory limit */ + var residentLimit: Option[Double] = None + + /** Resident memory request */ + var residentRequest: Option[Double] = None + /** Job project to run the command */ var jobProject: String = _ /** Job queue to run the command */ var jobQueue: String = _ + /** Native arguments to pass to the job runner */ + var jobNativeArgs: List[String] = Nil + + /** Native arguments to pass to the job runner */ + var jobResourceRequests: List[String] = Nil + + /** Environment names to pass to the job runner */ + var jobEnvironmentNames: List[String] = Nil + override def copySettingsTo(function: QFunction) { super.copySettingsTo(function) function match { @@ -24,13 +39,27 @@ trait CommandLineFunction extends QFunction with Logging { if (commandLineFunction.memoryLimit.isEmpty) commandLineFunction.memoryLimit = this.memoryLimit + if (commandLineFunction.residentLimit.isEmpty) + commandLineFunction.residentLimit = this.residentLimit + + if (commandLineFunction.residentRequest.isEmpty) + commandLineFunction.residentRequest = this.residentRequest + if (commandLineFunction.jobProject == null) commandLineFunction.jobProject = this.jobProject if (commandLineFunction.jobQueue == null) commandLineFunction.jobQueue = this.jobQueue - commandLineFunction.jobQueue = this.jobQueue + if (commandLineFunction.jobNativeArgs.isEmpty) + commandLineFunction.jobNativeArgs = this.jobNativeArgs + + if (commandLineFunction.jobResourceRequests.isEmpty) + commandLineFunction.jobResourceRequests = this.jobResourceRequests + + if (commandLineFunction.jobEnvironmentNames.isEmpty) + commandLineFunction.jobEnvironmentNames = this.jobEnvironmentNames + case _ => /* ignore */ } } @@ -53,9 +82,30 @@ trait CommandLineFunction extends QFunction with Logging { if (jobProject == null) jobProject = qSettings.jobProject + if (jobNativeArgs.isEmpty) + jobNativeArgs = qSettings.jobNativeArgs + + if (jobResourceRequests.isEmpty) + jobResourceRequests = qSettings.jobResourceRequests + + if (jobEnvironmentNames.isEmpty) + jobEnvironmentNames = qSettings.jobEnvironmentNames + if (memoryLimit.isEmpty) memoryLimit = qSettings.memoryLimit + if (residentLimit.isEmpty) + residentLimit = qSettings.residentLimit + + if (residentRequest.isEmpty) + residentRequest = qSettings.residentRequest + + if (residentRequest.isEmpty) + residentRequest = memoryLimit + + if (residentLimit.isEmpty) + residentLimit = residentRequest.map( _ * 1.2 ) + super.freezeFieldValues() } diff --git a/public/scala/test/org/broadinstitute/sting/queue/pipeline/PipelineTest.scala b/public/scala/test/org/broadinstitute/sting/queue/pipeline/PipelineTest.scala index 27ac559c5..5de474340 100644 --- a/public/scala/test/org/broadinstitute/sting/queue/pipeline/PipelineTest.scala +++ b/public/scala/test/org/broadinstitute/sting/queue/pipeline/PipelineTest.scala @@ -43,13 +43,15 @@ object PipelineTest extends BaseTest with Logging { private val validationReportsDataLocation = "/humgen/gsa-hpprojects/GATK/validationreports/submitted/" - val run = System.getProperty("pipeline.run") == "run" + final val run = System.getProperty("pipeline.run") == "run" - private val jobRunners = { + final val allJobRunners = { val commandLinePluginManager = new CommandLinePluginManager - commandLinePluginManager.getPlugins.map(commandLinePluginManager.getName(_)).filterNot(_ == "Shell") + commandLinePluginManager.getPlugins.map(commandLinePluginManager.getName(_)).toList } + final val defaultJobRunners = List("Lsf706", "GridEngine") + /** * Returns the top level output path to this test. * @param testName The name of the test passed to PipelineTest.executeTest() @@ -79,9 +81,12 @@ object PipelineTest extends BaseTest with Logging { * @param pipelineTest test to run. */ def executeTest(pipelineTest: PipelineTestSpec) { + var jobRunners = pipelineTest.jobRunners + if (jobRunners == null) + jobRunners = defaultJobRunners; jobRunners.foreach(executeTest(pipelineTest, _)) } - + /** * Runs the pipelineTest. * @param pipelineTest test to run. diff --git a/public/scala/test/org/broadinstitute/sting/queue/pipeline/PipelineTestSpec.scala b/public/scala/test/org/broadinstitute/sting/queue/pipeline/PipelineTestSpec.scala index f26689383..a7b3f3a47 100644 --- a/public/scala/test/org/broadinstitute/sting/queue/pipeline/PipelineTestSpec.scala +++ b/public/scala/test/org/broadinstitute/sting/queue/pipeline/PipelineTestSpec.scala @@ -1,7 +1,5 @@ package org.broadinstitute.sting.queue.pipeline -import java.io.File - class PipelineTestSpec(var name: String = null) { /** The arguments to pass to the Queue test, ex: "-S scala/qscript/examples/HelloWorld.scala" */ @@ -10,6 +8,9 @@ class PipelineTestSpec(var name: String = null) { /** Job Queue to run the test. Default is null which means use hour. */ var jobQueue: String = _ + /** Job runners to run the test. Default is null which means use the default. */ + var jobRunners: List[String] = _ + /** Expected MD5 results for each file path. */ var fileMD5s = Map.empty[String, String] diff --git a/public/scala/test/org/broadinstitute/sting/queue/pipeline/examples/HelloWorldPipelineTest.scala b/public/scala/test/org/broadinstitute/sting/queue/pipeline/examples/HelloWorldPipelineTest.scala index 7c76823da..f320cb3a6 100644 --- a/public/scala/test/org/broadinstitute/sting/queue/pipeline/examples/HelloWorldPipelineTest.scala +++ b/public/scala/test/org/broadinstitute/sting/queue/pipeline/examples/HelloWorldPipelineTest.scala @@ -33,6 +33,7 @@ class HelloWorldPipelineTest { val spec = new PipelineTestSpec spec.name = "HelloWorld" spec.args = "-S public/scala/qscript/org/broadinstitute/sting/queue/qscripts/examples/HelloWorld.scala" + spec.jobRunners = PipelineTest.allJobRunners PipelineTest.executeTest(spec) } @@ -40,23 +41,89 @@ class HelloWorldPipelineTest { def testHelloWorldWithPrefix() { val spec = new PipelineTestSpec spec.name = "HelloWorldWithPrefix" - spec.args = "-S public/scala/qscript/org/broadinstitute/sting/queue/qscripts/examples/HelloWorld.scala -jobPrefix HelloWorld" + spec.args = "-S public/scala/qscript/org/broadinstitute/sting/queue/qscripts/examples/HelloWorld.scala" + + " -jobPrefix HelloWorld" + spec.jobRunners = PipelineTest.allJobRunners PipelineTest.executeTest(spec) } @Test def testHelloWorldWithMemoryLimit() { val spec = new PipelineTestSpec - spec.name = "HelloWorldWithPrefix" - spec.args = "-S public/scala/qscript/org/broadinstitute/sting/queue/qscripts/examples/HelloWorld.scala -memLimit 1.25" + spec.name = "HelloWorldMemoryLimit" + spec.args = "-S public/scala/qscript/org/broadinstitute/sting/queue/qscripts/examples/HelloWorld.scala" + + " -memLimit 1.25" + spec.jobRunners = PipelineTest.allJobRunners PipelineTest.executeTest(spec) } - @Test(enabled=false) + @Test def testHelloWorldWithPriority() { val spec = new PipelineTestSpec spec.name = "HelloWorldWithPriority" - spec.args = "-S public/scala/qscript/org/broadinstitute/sting/queue/qscripts/examples/HelloWorld.scala -jobPriority 100" + spec.args = "-S public/scala/qscript/org/broadinstitute/sting/queue/qscripts/examples/HelloWorld.scala" + + " -jobPriority 100" + spec.jobRunners = PipelineTest.allJobRunners + PipelineTest.executeTest(spec) + } + + @Test + def testHelloWorldWithLsfResource() { + val spec = new PipelineTestSpec + spec.name = "HelloWorldWithLsfResource" + spec.args = "-S public/scala/qscript/org/broadinstitute/sting/queue/qscripts/examples/HelloWorld.scala" + + " -jobResReq rusage[iodine_io=1] -jobResReq select[swp>0] -jobResReq order[swp]" + spec.jobRunners = List("Lsf706") + PipelineTest.executeTest(spec) + } + + @Test + def testHelloWorldWithLsfResourceAndMemoryLimit() { + val spec = new PipelineTestSpec + spec.name = "HelloWorldWithLsfResourceAndMemoryLimit" + spec.args = "-S public/scala/qscript/org/broadinstitute/sting/queue/qscripts/examples/HelloWorld.scala" + + " -memLimit 1.25 -jobResReq rusage[iodine_io=1] -jobResReq select[swp>0] -jobResReq order[swp]" + spec.jobRunners = List("Lsf706") + PipelineTest.executeTest(spec) + } + + @Test + def testHelloWorldWithLsfEnvironment() { + val spec = new PipelineTestSpec + spec.name = "HelloWorldWithLsfEnvironment" + spec.args = "-S public/scala/qscript/org/broadinstitute/sting/queue/qscripts/examples/HelloWorld.scala" + + " -jobEnv tv" + spec.jobRunners = List("Lsf706") + PipelineTest.executeTest(spec) + } + + @Test + def testHelloWorldWithGridEngineResource() { + val spec = new PipelineTestSpec + spec.name = "HelloWorldWithGridEngineResource" + spec.args = "-S public/scala/qscript/org/broadinstitute/sting/queue/qscripts/examples/HelloWorld.scala" + + " -jobResReq s_core=1000M" + spec.jobRunners = List("GridEngine") + PipelineTest.executeTest(spec) + } + + @Test + def testHelloWorldWithGridEngineResourceAndMemoryLimit() { + val spec = new PipelineTestSpec + spec.name = "HelloWorldWithGridEngineResourceAndMemoryLimit" + spec.args = "-S public/scala/qscript/org/broadinstitute/sting/queue/qscripts/examples/HelloWorld.scala" + + " -memLimit 1.25 -jobResReq s_core=1000M" + spec.jobRunners = List("GridEngine") + PipelineTest.executeTest(spec) + } + + @Test + def testHelloWorldWithGridEngineEnvironment() { + val spec = new PipelineTestSpec + spec.name = "HelloWorldWithGridEngineEnvironment" + spec.args = "-S public/scala/qscript/org/broadinstitute/sting/queue/qscripts/examples/HelloWorld.scala" + + " -jobEnv \"make 1\"" + spec.jobRunners = List("GridEngine") PipelineTest.executeTest(spec) } } From dc42571dd9bae7e28734fd8d24e6242efb7ede53 Mon Sep 17 00:00:00 2001 From: Eric Banks Date: Mon, 22 Aug 2011 15:40:36 -0400 Subject: [PATCH 126/138] Only create the genotype map when necessary --- .../sting/utils/variantcontext/VariantContext.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 155a539c7..888dc1e98 100755 --- a/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java +++ b/public/java/src/org/broadinstitute/sting/utils/variantcontext/VariantContext.java @@ -823,7 +823,8 @@ public class VariantContext implements Feature { // to enable tribble intergrati private void loadGenotypes() { if ( !hasAttribute(UNPARSED_GENOTYPE_MAP_KEY) ) { - genotypes = NO_GENOTYPES; + if ( genotypes == null ) + genotypes = NO_GENOTYPES; return; } From 3612a3501dc1db3932194145533c4cad1b052e21 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Mon, 22 Aug 2011 17:24:51 -0400 Subject: [PATCH 127/138] info, not warn, about dynamic type determination --- .../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 b12ae8e75..ff992d77d 100644 --- a/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java +++ b/public/java/src/org/broadinstitute/sting/commandline/ArgumentTypeDescriptor.java @@ -374,7 +374,7 @@ class RodBindingArgumentTypeDescriptor extends ArgumentTypeDescriptor { FeatureManager.FeatureDescriptor featureDescriptor = manager.getByFiletype(file); if ( featureDescriptor != null ) { tribbleType = featureDescriptor.getName(); - logger.warn("Dynamically determined type of " + file + " to be " + tribbleType); + logger.info("Dynamically determined type of " + file + " to be " + tribbleType); } } From 1eab9be35d28d50a5e09b98a1069a9f848dc11f9 Mon Sep 17 00:00:00 2001 From: Mark DePristo Date: Mon, 22 Aug 2011 17:25:15 -0400 Subject: [PATCH 128/138] Now with accurate javadoc --- .../src/org/broadinstitute/sting/gatk/GenomeAnalysisEngine.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/java/src/org/broadinstitute/sting/gatk/GenomeAnalysisEngine.java b/public/java/src/org/broadinstitute/sting/gatk/GenomeAnalysisEngine.java index da7eaf6e4..5b9ebd99b 100755 --- a/public/java/src/org/broadinstitute/sting/gatk/GenomeAnalysisEngine.java +++ b/public/java/src/org/broadinstitute/sting/gatk/GenomeAnalysisEngine.java @@ -961,7 +961,7 @@ public class GenomeAnalysisEngine { /** * Get the list of intervals passed to the engine. - * @return List of intervals. + * @return List of intervals, or null if no intervals are in use */ public GenomeLocSortedSet getIntervals() { return this.intervals; From caebc88e9a75f63c1638419e7747f576af6308cc Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Mon, 22 Aug 2011 14:05:13 -0400 Subject: [PATCH 132/138] Consensus mode and new RodBinding framework. The DPP was not using the parameter correctly. It didn't matter for the default option (which is the only one we have been testing) but it would not work for knowns only or smith waterman. It is fixed now. It now complies with the new rod binding framework. --- .../qscripts/DataProcessingPipeline.scala | 32 +++++++++++-------- 1 file changed, 19 insertions(+), 13 deletions(-) diff --git a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/DataProcessingPipeline.scala b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/DataProcessingPipeline.scala index 47ba0220f..03d1d7766 100755 --- a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/DataProcessingPipeline.scala +++ b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/DataProcessingPipeline.scala @@ -84,12 +84,6 @@ class DataProcessingPipeline extends QScript { var nContigs: Int = 0 // Use the number of contigs for scatter gathering jobs var cleanModelEnum: ConsensusDeterminationModel = ConsensusDeterminationModel.USE_READS - if (cleaningModel == "KNOWNS_ONLY") { - cleanModelEnum = ConsensusDeterminationModel.KNOWNS_ONLY - } - else if (cleaningModel == "USE_SW") { - cleanModelEnum = ConsensusDeterminationModel.USE_SW - } @@ -200,6 +194,15 @@ class DataProcessingPipeline extends QScript { return realignedBams } + def getIndelCleaningModel(): ConsensusDeterminationModel = { + if (cleaningModel == "KNOWNS_ONLY") + ConsensusDeterminationModel.KNOWNS_ONLY + else if (cleaningModel == "USE_SW") + ConsensusDeterminationModel.USE_SW + else + ConsensusDeterminationModel.USE_READS + } + /**************************************************************************** * Main script @@ -208,6 +211,8 @@ class DataProcessingPipeline extends QScript { def script = { + cleanModelEnum = getIndelCleaningModel() + // keep a record of the number of contigs in the first bam file in the list val bams = QScriptUtils.createListFromFile(input) nContigs = QScriptUtils.getNumberOfContigs(bams(0)) @@ -300,13 +305,13 @@ class DataProcessingPipeline extends QScript { } case class target (inBams: File, outIntervals: File) extends RealignerTargetCreator with CommandLineGATKArgs { - if (cleaningModel != ConsensusDeterminationModel.KNOWNS_ONLY) + if (cleanModelEnum != ConsensusDeterminationModel.KNOWNS_ONLY) this.input_file :+= inBams this.out = outIntervals this.mismatchFraction = 0.0 - this.rodBind :+= RodBind("dbsnp", "VCF", dbSNP) + this.known :+= qscript.dbSNP if (indels != null) - this.rodBind :+= RodBind("indels", "VCF", indels) + this.known :+= qscript.indels this.scatterCount = nContigs this.analysisName = queueLogDir + outIntervals + ".target" this.jobName = queueLogDir + outIntervals + ".target" @@ -317,10 +322,10 @@ class DataProcessingPipeline extends QScript { this.input_file :+= inBams this.targetIntervals = tIntervals this.out = outBam - this.rodBind :+= RodBind("dbsnp", "VCF", dbSNP) + this.known :+= qscript.dbSNP if (qscript.indels != null) - this.rodBind :+= RodBind("indels", "VCF", qscript.indels) - this.consensusDeterminationModel = consensusDeterminationModel + this.known :+= qscript.indels + this.consensusDeterminationModel = cleanModelEnum this.compress = 0 this.scatterCount = nContigs this.analysisName = queueLogDir + outBam + ".clean" @@ -328,7 +333,7 @@ class DataProcessingPipeline extends QScript { } case class cov (inBam: File, outRecalFile: File) extends CountCovariates with CommandLineGATKArgs { - this.rodBind :+= RodBind("dbsnp", "VCF", dbSNP) + this.knownSites :+= qscript.dbSNP this.covariate ++= List("ReadGroupCovariate", "QualityScoreCovariate", "CycleCovariate", "DinucCovariate") this.input_file :+= inBam this.recal_file = outRecalFile @@ -372,6 +377,7 @@ class DataProcessingPipeline extends QScript { this.input = List(inBam) this.output = outBam this.metrics = metricsFile + this.memoryLimit = 16 this.analysisName = queueLogDir + outBam + ".dedup" this.jobName = queueLogDir + outBam + ".dedup" } From 8aed151a7154ca991771e32dc3c8ad31c0dbbf84 Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Mon, 22 Aug 2011 14:23:39 -0400 Subject: [PATCH 133/138] Created RevertSam queue class Class for the picard tool RevertSam with all the options for queue scripts. --- .../queue/extensions/picard/RevertSam.scala | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 public/scala/src/org/broadinstitute/sting/queue/extensions/picard/RevertSam.scala diff --git a/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/RevertSam.scala b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/RevertSam.scala new file mode 100644 index 000000000..33cd7ccaf --- /dev/null +++ b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/RevertSam.scala @@ -0,0 +1,52 @@ +package org.broadinstitute.sting.queue.extensions.picard + +import org.broadinstitute.sting.commandline._ + +import java.io.File + +/* + * Created by IntelliJ IDEA. + * User: carneiro + * Date: 6/22/11 + * Time: 10:35 AM + */ +class RevertSam extends org.broadinstitute.sting.queue.function.JavaCommandLineFunction with PicardBamFunction { + analysisName = "SortSam" + javaMainClass = "net.sf.picard.sam.SortSam" + + @Input(shortName = "input", fullName = "input_bam_files", required = true, doc = "The input SAM or BAM files to revert.") + var input: List[File] = _ + + @Output(shortName = "output", fullName = "output_bam_file", required = true, doc = "The reverted BAM or SAM output file.") + var output: File = _ + + @Output(shortName = "out_index", fullName = "output_bam_index_file", required = false, doc = "The output bam index") + var outputIndex: File = new File(output + ".bai") + + @Argument(shortName = "roq", fullName = "restore_original_qualities", required = false, doc = "True to restore original qualities from the OQ field to the QUAL field if available.") + var restoreOriginalQualities: Boolean = true + + @Argument(shortName = "rdi", fullName = "remove_duplicate_information", required = false, doc = "Remove duplicate read flags from all reads. Note that if this is true and REMOVE_ALIGNMENT_INFORMATION==false, the output may have the unusual but sometimes desirable trait of having unmapped reads that are marked as duplicates.") + var removeDuplicateInformation: Boolean = true + + @Argument(shortName = "rai", fullName = "remove_alignment_information", required = false, doc = "Remove all alignment information from the file.") + var removeAlignmentInformation: Boolean = true + + @Argument(shortName = "atc", fullName = "attributes_to_clear", required = false, doc = "When removing alignment information, the set of optional tags to remove.") + var attributesToClear: List[String] = _ + + @Argument(shortName = "sa", fullName = "sample_alias", required = false, doc = "The sample alias to use in the reverted output file. This will override the existing sample alias in the file and is used only if all the read groups in the input file have the same sample alias.") + var sampleAlias: String = null + + @Argument(shortName = "ln", fullName = "library_name", required = false, doc = "The library name to use in the reverted output file. This will override the existing sample alias in the file and is used only if all the read groups in the input file have the same sample alias.") + + override def inputBams = input + override def outputBam = output + this.createIndex = Some(true) + override def commandLine = super.commandLine + + conditionalParameter(!restoreOriginalQualities, " RESTORE_ORIGINAL_QUALITIES=false") + + conditionalParameter(!removeDuplicateInformation, " REMOVE_DUPLICATE_INFORMATION=false") + + conditionalParameter(!removeAlignmentInformation, " REMOVE_ALIGNMENT_INFORMATION=false") + + conditionalParameter(!attributesToClear.isEmpty, repeat(" ATTRIBUTE_TO_CLEAR=", attributesToClear)) + + conditionalParameter(sampleAlias != null, " SAMPLE_ALIAS=" + sampleAlias) +} \ No newline at end of file From 04d8bcaf1941226842e6a5beab179acdfaeaf992 Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Mon, 22 Aug 2011 14:42:29 -0400 Subject: [PATCH 134/138] Fixed bai removal on picard tools BAM index files were not being deleted because picard replaces the name of the file with bai instead of appending to it. --- .../qscripts/DataProcessingPipeline.scala | 62 +++++++++++++------ .../picard/AddOrReplaceReadGroups.scala | 8 ++- .../extensions/picard/MarkDuplicates.scala | 9 ++- .../extensions/picard/MergeSamFiles.scala | 10 ++- .../queue/extensions/picard/RevertSam.scala | 21 +++++-- .../queue/extensions/picard/SortSam.scala | 11 +++- 6 files changed, 91 insertions(+), 30 deletions(-) diff --git a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/DataProcessingPipeline.scala b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/DataProcessingPipeline.scala index 03d1d7766..38109dd9b 100755 --- a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/DataProcessingPipeline.scala +++ b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/DataProcessingPipeline.scala @@ -142,9 +142,9 @@ class DataProcessingPipeline extends QScript { println (f) println() - val sampleFileName = new File(qscript.outputDir + qscript.projectName + "." + sample + ".bam") + val sampleFileName = new File(qscript.outputDir + qscript.projectName + "." + sample + ".list") sampleBamFiles(sample) = sampleFileName - add(joinBams(flist, sampleFileName)) + add(writeList(flist, sampleFileName)) } println("*** INPUT FILES ***\n\n") @@ -170,18 +170,20 @@ class DataProcessingPipeline extends QScript { var realignedBams: List[File] = List() var index = 1 for (bam <- bams) { - val readSortedBam = swapExt(bam, ".bam", "." + index + ".sorted.bam" ) + // first revert the BAM file to the original qualities + val revertedBAM = revertBAM(bam) + val readSortedBam = swapExt(revertedBAM, ".bam", "." + index + ".sorted.bam" ) val saiFile1 = swapExt(bam, ".bam", "." + index + ".1.sai") val saiFile2 = swapExt(bam, ".bam", "." + index + ".2.sai") val realignedSamFile = swapExt(bam, ".bam", "." + index + ".realigned.sam") val realignedBamFile = swapExt(bam, ".bam", "." + index + ".realigned.bam") val rgRealignedBamFile = swapExt(bam, ".bam", "." + index + ".realigned.rg.bam") if (useBWAse) { - add(bwa_aln_se(bam, saiFile1), - bwa_sam_se(bam, saiFile1, realignedSamFile)) + add(bwa_aln_se(revertedBAM, saiFile1), + bwa_sam_se(revertedBAM, saiFile1, realignedSamFile)) } else { - add(sortSam(bam, readSortedBam, SortOrder.queryname), + add(sortSam(revertedBAM, readSortedBam, SortOrder.queryname), bwa_aln_pe(readSortedBam, saiFile1, 1), bwa_aln_pe(readSortedBam, saiFile2, 2), bwa_sam_pe(readSortedBam, saiFile1, saiFile2, realignedSamFile)) @@ -203,6 +205,19 @@ class DataProcessingPipeline extends QScript { ConsensusDeterminationModel.USE_READS } + def revertBams(bams: List[File]): List[File] = { + var revertedBAMList: List[File] = List() + for (bam <- bams) + revertedBAMList :+= revertBAM(bam) + return revertedBAMList + } + + def revertBAM(bam: File): File = { + val revertedBAM = swapExt(bam, ".bam", ".reverted.bam") + add(revert(bam, revertedBAM)) + return revertedBAM + } + /**************************************************************************** * Main script @@ -217,17 +232,17 @@ class DataProcessingPipeline extends QScript { val bams = QScriptUtils.createListFromFile(input) nContigs = QScriptUtils.getNumberOfContigs(bams(0)) - val realignedBams = if (useBWApe || useBWAse) {performAlignment(bams)} else {bams} + val realignedBAMs = if (useBWApe || useBWAse) {performAlignment(bams)} else {revertBams(bams)} // Generate a BAM file per sample joining all per lane files if necessary - val sampleBamFiles: Map[String, File] = createSampleFiles(bams, realignedBams) + val sampleBAMFiles: Map[String, File] = createSampleFiles(bams, realignedBAMs) // Final output list of processed bam files var cohortList: List[File] = List() // Simple progress report println("\nFound the following samples: ") - for ((sample, file) <- sampleBamFiles) + for ((sample, file) <- sampleBAMFiles) println("\t" + sample + " -> " + file) println("\n") @@ -237,7 +252,8 @@ class DataProcessingPipeline extends QScript { add(target(null, globalIntervals)) // Put each sample through the pipeline - for ((sample, bam) <- sampleBamFiles) { + for ((sample, sampleFile) <- sampleBAMFiles) { + val bam = if (sampleFile.endsWith(".list")) {swapExt(sampleFile, ".list", ".bam")} else {sampleFile} // BAM files generated by the pipeline val cleanedBam = swapExt(bam, ".bam", ".clean.bam") @@ -254,17 +270,19 @@ class DataProcessingPipeline extends QScript { val preValidateLog = swapExt(bam, ".bam", ".pre.validation") val postValidateLog = swapExt(bam, ".bam", ".post.validation") + + //todo -- update the validation to work with .list files // Validation is an optional step for the BAM file generated after // alignment and the final bam file of the pipeline. - if (!noValidation) { - add(validate(bam, preValidateLog), - validate(recalBam, postValidateLog)) - } +// if (!noValidation) { +// add(validate(bam, preValidateLog), +// validate(recalBam, postValidateLog)) +// } if (cleaningModel != ConsensusDeterminationModel.KNOWNS_ONLY) - add(target(bam, targetIntervals)) + add(target(sampleFile, targetIntervals)) - add(clean(bam, targetIntervals, cleanedBam), + add(clean(sampleFile, targetIntervals, cleanedBam), dedup(cleanedBam, dedupedBam, metricsFile), cov(dedupedBam, preRecalFile), recal(dedupedBam, preRecalFile, recalBam), @@ -318,7 +336,6 @@ class DataProcessingPipeline extends QScript { } case class clean (inBams: File, tIntervals: File, outBam: File) extends IndelRealigner with CommandLineGATKArgs { - @Output(doc="output bai file") var bai = swapExt(outBam, ".bam", ".bai") this.input_file :+= inBams this.targetIntervals = tIntervals this.out = outBam @@ -373,7 +390,6 @@ class DataProcessingPipeline extends QScript { } case class dedup (inBam: File, outBam: File, metricsFile: File) extends MarkDuplicates with ExternalCommonArgs { - @Output(doc="output bai file") var bai = swapExt(outBam, ".bam", ".bai") this.input = List(inBam) this.output = outBam this.metrics = metricsFile @@ -383,7 +399,6 @@ class DataProcessingPipeline extends QScript { } case class joinBams (inBams: List[File], outBam: File) extends MergeSamFiles with ExternalCommonArgs { - @Output(doc="output bai file") var bai = swapExt(outBam, ".bam", ".bai") this.input = inBams this.output = outBam this.analysisName = queueLogDir + outBam + ".joinBams" @@ -391,7 +406,6 @@ class DataProcessingPipeline extends QScript { } case class sortSam (inSam: File, outBam: File, sortOrderP: SortOrder) extends SortSam with ExternalCommonArgs { - @Output(doc="output bai file") var bai = swapExt(outBam, ".bam", ".bai") this.input = List(inSam) this.output = outBam this.sortOrder = sortOrderP @@ -424,6 +438,14 @@ class DataProcessingPipeline extends QScript { this.jobName = queueLogDir + outBam + ".rg" } + case class revert (inBam: File, outBam: File) extends RevertSam with ExternalCommonArgs { + this.output = outBam + this.input :+= inBam + this.analysisName = queueLogDir + outBam + "revert" + this.jobName = queueLogDir + outBam + ".revert" + + } + case class bwa_aln_se (inBam: File, outSai: File) extends CommandLineFunction with ExternalCommonArgs { @Input(doc="bam file to be aligned") var bam = inBam @Output(doc="output sai file") var sai = outSai diff --git a/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/AddOrReplaceReadGroups.scala b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/AddOrReplaceReadGroups.scala index 2508f5776..6413317e6 100644 --- a/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/AddOrReplaceReadGroups.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/AddOrReplaceReadGroups.scala @@ -21,7 +21,7 @@ class AddOrReplaceReadGroups extends org.broadinstitute.sting.queue.function.Jav var output: File = _ @Output(doc="The output bam index", shortName = "out_index", fullName = "output_bam_index_file", required = false) - var outputIndex: File = new File(output + ".bai") + var outputIndex: File = _ @Argument(doc="Read group ID", shortName = "id", fullName = "read_group_id", required = true) var RGID: String = _ @@ -44,6 +44,12 @@ class AddOrReplaceReadGroups extends org.broadinstitute.sting.queue.function.Jav @Argument(doc = "Read group description", shortName = "ds", fullName = "read_group_description", required = false) var RGDS: String = "" + override def freezeFieldValues() { + super.freezeFieldValues() + if (outputIndex == null && output != null) + outputIndex = new File(output.getName.stripSuffix(".bam") + ".bai") + } + override def inputBams = input override def outputBam = output diff --git a/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/MarkDuplicates.scala b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/MarkDuplicates.scala index 6f006ffad..9489e8b07 100644 --- a/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/MarkDuplicates.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/MarkDuplicates.scala @@ -21,7 +21,7 @@ class MarkDuplicates extends org.broadinstitute.sting.queue.function.JavaCommand var output: File = _ @Output(doc="The output bam index", shortName = "out_index", fullName = "output_bam_index_file", required = false) - var outputIndex: File = new File(output + ".bai") + var outputIndex: File = _ @Output(doc="File to write duplication metrics to", shortName = "out_metrics", fullName = "output_metrics_file", required = false) var metrics: File = new File(output + ".metrics") @@ -35,6 +35,13 @@ class MarkDuplicates extends org.broadinstitute.sting.queue.function.JavaCommand @Argument(doc = "This number, plus the maximum RAM available to the JVM, determine the memory footprint used by some of the sorting collections. If you are running out of memory, try reducing this number.", shortName = "sorting_ratio", fullName = "sorting_collection_size_ratio", required = false) var SORTING_COLLECTION_SIZE_RATIO: Double = -1 + override def freezeFieldValues() { + super.freezeFieldValues() + if (outputIndex == null && output != null) + outputIndex = new File(output.getName.stripSuffix(".bam") + ".bai") + } + + override def inputBams = input override def outputBam = output this.sortOrder = null diff --git a/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/MergeSamFiles.scala b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/MergeSamFiles.scala index a7e74e1b5..a16c1eba2 100644 --- a/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/MergeSamFiles.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/MergeSamFiles.scala @@ -3,6 +3,7 @@ package org.broadinstitute.sting.queue.extensions.picard import org.broadinstitute.sting.commandline._ import java.io.File +import org.broadinstitute.sting.queue.QScript._ /* * Created by IntelliJ IDEA. @@ -21,7 +22,7 @@ class MergeSamFiles extends org.broadinstitute.sting.queue.function.JavaCommandL var output: File = _ @Output(doc="The output bam index", shortName = "out_index", fullName = "output_bam_index_file", required = false) - var outputIndex: File = new File(output + ".bai") + var outputIndex: File = _ @Argument(doc="Merge the seqeunce dictionaries Default value: false. This option can be set to 'null' to clear the default value.", shortName = "merge_dict", fullName = "merge_sequence_dictionaries", required = false) var MERGE_SEQUENCE_DICTIONARIES: Boolean = false @@ -32,6 +33,13 @@ class MergeSamFiles extends org.broadinstitute.sting.queue.function.JavaCommandL @Argument(doc = "Comments to include in the merged output file's header.", shortName = "com", fullName = "comments", required = false) var COMMENT: String = "" + override def freezeFieldValues() { + super.freezeFieldValues() + if (outputIndex == null && output != null) + outputIndex = new File(output.getName.stripSuffix(".bam") + ".bai") + } + + override def inputBams = input override def outputBam = output this.createIndex = Some(true) diff --git a/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/RevertSam.scala b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/RevertSam.scala index 33cd7ccaf..746ce609e 100644 --- a/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/RevertSam.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/RevertSam.scala @@ -11,17 +11,17 @@ import java.io.File * Time: 10:35 AM */ class RevertSam extends org.broadinstitute.sting.queue.function.JavaCommandLineFunction with PicardBamFunction { - analysisName = "SortSam" - javaMainClass = "net.sf.picard.sam.SortSam" + analysisName = "RevertSam" + javaMainClass = "net.sf.picard.sam.RevertSam" @Input(shortName = "input", fullName = "input_bam_files", required = true, doc = "The input SAM or BAM files to revert.") - var input: List[File] = _ + var input: List[File] = Nil @Output(shortName = "output", fullName = "output_bam_file", required = true, doc = "The reverted BAM or SAM output file.") var output: File = _ @Output(shortName = "out_index", fullName = "output_bam_index_file", required = false, doc = "The output bam index") - var outputIndex: File = new File(output + ".bai") + var outputIndex: File = _ @Argument(shortName = "roq", fullName = "restore_original_qualities", required = false, doc = "True to restore original qualities from the OQ field to the QUAL field if available.") var restoreOriginalQualities: Boolean = true @@ -33,12 +33,20 @@ class RevertSam extends org.broadinstitute.sting.queue.function.JavaCommandLineF var removeAlignmentInformation: Boolean = true @Argument(shortName = "atc", fullName = "attributes_to_clear", required = false, doc = "When removing alignment information, the set of optional tags to remove.") - var attributesToClear: List[String] = _ + var attributesToClear: List[String] = Nil @Argument(shortName = "sa", fullName = "sample_alias", required = false, doc = "The sample alias to use in the reverted output file. This will override the existing sample alias in the file and is used only if all the read groups in the input file have the same sample alias.") var sampleAlias: String = null @Argument(shortName = "ln", fullName = "library_name", required = false, doc = "The library name to use in the reverted output file. This will override the existing sample alias in the file and is used only if all the read groups in the input file have the same sample alias.") + var libraryName: String = null + + override def freezeFieldValues() { + super.freezeFieldValues() + if (outputIndex == null && output != null) + outputIndex = new File(output.getName.stripSuffix(".bam") + ".bai") + } + override def inputBams = input override def outputBam = output @@ -48,5 +56,6 @@ class RevertSam extends org.broadinstitute.sting.queue.function.JavaCommandLineF conditionalParameter(!removeDuplicateInformation, " REMOVE_DUPLICATE_INFORMATION=false") + conditionalParameter(!removeAlignmentInformation, " REMOVE_ALIGNMENT_INFORMATION=false") + conditionalParameter(!attributesToClear.isEmpty, repeat(" ATTRIBUTE_TO_CLEAR=", attributesToClear)) + - conditionalParameter(sampleAlias != null, " SAMPLE_ALIAS=" + sampleAlias) + conditionalParameter(sampleAlias != null, " SAMPLE_ALIAS=" + sampleAlias) + + conditionalParameter(libraryName != null, " LIBRARY_NAME=" + libraryName) } \ No newline at end of file diff --git a/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/SortSam.scala b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/SortSam.scala index cc26f7471..520d17a6a 100644 --- a/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/SortSam.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/SortSam.scala @@ -3,6 +3,7 @@ package org.broadinstitute.sting.queue.extensions.picard import org.broadinstitute.sting.commandline._ import java.io.File +import org.broadinstitute.sting.queue.QScript._ /* * Created by IntelliJ IDEA. @@ -21,7 +22,15 @@ class SortSam extends org.broadinstitute.sting.queue.function.JavaCommandLineFun var output: File = _ @Output(doc="The output bam index", shortName = "out_index", fullName = "output_bam_index_file", required = false) - var outputIndex: File = new File(output + ".bai") + var outputIndex: File = _ + + override def freezeFieldValues() { + super.freezeFieldValues() + if (outputIndex == null && output != null) + outputIndex = new File(output.getName.stripSuffix(".bam") + ".bai") + } + + override def inputBams = input override def outputBam = output From 136f0eb6856215d632f205126c7ee0532bac67c7 Mon Sep 17 00:00:00 2001 From: Mauricio Carneiro Date: Mon, 22 Aug 2011 18:00:44 -0400 Subject: [PATCH 135/138] Creating sample-bam list instead of joining This should save us at least one day in the trio decoy processing. --- .../qscripts/DataProcessingPipeline.scala | 19 ++++++++----------- .../picard/AddOrReplaceReadGroups.scala | 2 +- .../extensions/picard/MarkDuplicates.scala | 2 +- .../extensions/picard/MergeSamFiles.scala | 2 +- .../queue/extensions/picard/SortSam.scala | 2 +- .../extensions/picard/ValidateSamFile.scala | 4 ++-- 6 files changed, 14 insertions(+), 17 deletions(-) diff --git a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/DataProcessingPipeline.scala b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/DataProcessingPipeline.scala index 38109dd9b..724518142 100755 --- a/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/DataProcessingPipeline.scala +++ b/public/scala/qscript/org/broadinstitute/sting/queue/qscripts/DataProcessingPipeline.scala @@ -218,7 +218,6 @@ class DataProcessingPipeline extends QScript { return revertedBAM } - /**************************************************************************** * Main script ****************************************************************************/ @@ -271,13 +270,12 @@ class DataProcessingPipeline extends QScript { val postValidateLog = swapExt(bam, ".bam", ".post.validation") - //todo -- update the validation to work with .list files // Validation is an optional step for the BAM file generated after // alignment and the final bam file of the pipeline. -// if (!noValidation) { -// add(validate(bam, preValidateLog), -// validate(recalBam, postValidateLog)) -// } + if (!noValidation && sampleFile.endsWith(".bam")) { // todo -- implement validation for .list BAM files + add(validate(sampleFile, preValidateLog), + validate(recalBam, postValidateLog)) + } if (cleaningModel != ConsensusDeterminationModel.KNOWNS_ONLY) add(target(sampleFile, targetIntervals)) @@ -390,7 +388,7 @@ class DataProcessingPipeline extends QScript { } case class dedup (inBam: File, outBam: File, metricsFile: File) extends MarkDuplicates with ExternalCommonArgs { - this.input = List(inBam) + this.input :+= inBam this.output = outBam this.metrics = metricsFile this.memoryLimit = 16 @@ -406,7 +404,7 @@ class DataProcessingPipeline extends QScript { } case class sortSam (inSam: File, outBam: File, sortOrderP: SortOrder) extends SortSam with ExternalCommonArgs { - this.input = List(inSam) + this.input :+= inSam this.output = outBam this.sortOrder = sortOrderP this.analysisName = queueLogDir + outBam + ".sortSam" @@ -414,7 +412,7 @@ class DataProcessingPipeline extends QScript { } case class validate (inBam: File, outLog: File) extends ValidateSamFile with ExternalCommonArgs { - this.input = List(inBam) + this.input :+= inBam this.output = outLog this.REFERENCE_SEQUENCE = qscript.reference this.isIntermediate = false @@ -424,8 +422,7 @@ class DataProcessingPipeline extends QScript { case class addReadGroup (inBam: File, outBam: File, readGroup: ReadGroup) extends AddOrReplaceReadGroups with ExternalCommonArgs { - @Output(doc="output bai file") var bai = swapExt(outBam, ".bam", ".bai") - this.input = List(inBam) + this.input :+= inBam this.output = outBam this.RGID = readGroup.id this.RGCN = readGroup.cn diff --git a/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/AddOrReplaceReadGroups.scala b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/AddOrReplaceReadGroups.scala index 6413317e6..5456ed02c 100644 --- a/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/AddOrReplaceReadGroups.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/AddOrReplaceReadGroups.scala @@ -15,7 +15,7 @@ class AddOrReplaceReadGroups extends org.broadinstitute.sting.queue.function.Jav javaMainClass = "net.sf.picard.sam.AddOrReplaceReadGroups" @Input(doc="The input SAM or BAM files to analyze. Must be coordinate sorted.", shortName = "input", fullName = "input_bam_files", required = true) - var input: List[File] = _ + var input: List[File] = Nil @Output(doc="The output BAM file with the modified/added read groups", shortName = "output", fullName = "output_bam_file", required = true) var output: File = _ diff --git a/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/MarkDuplicates.scala b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/MarkDuplicates.scala index 9489e8b07..d44d5e004 100644 --- a/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/MarkDuplicates.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/MarkDuplicates.scala @@ -15,7 +15,7 @@ class MarkDuplicates extends org.broadinstitute.sting.queue.function.JavaCommand javaMainClass = "net.sf.picard.sam.MarkDuplicates" @Input(doc="The input SAM or BAM files to analyze. Must be coordinate sorted.", shortName = "input", fullName = "input_bam_files", required = true) - var input: List[File] = _ + var input: List[File] = Nil @Output(doc="The output file to write marked records to", shortName = "output", fullName = "output_bam_file", required = true) var output: File = _ diff --git a/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/MergeSamFiles.scala b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/MergeSamFiles.scala index a16c1eba2..fd107890e 100644 --- a/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/MergeSamFiles.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/MergeSamFiles.scala @@ -16,7 +16,7 @@ class MergeSamFiles extends org.broadinstitute.sting.queue.function.JavaCommandL javaMainClass = "net.sf.picard.sam.MergeSamFiles" @Input(doc="The input SAM or BAM files to analyze. Must be coordinate sorted.", shortName = "input", fullName = "input_bam_files", required = true) - var input: List[File] = _ + var input: List[File] = Nil @Output(doc="The output merged BAM file", shortName = "output", fullName = "output_bam_file", required = true) var output: File = _ diff --git a/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/SortSam.scala b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/SortSam.scala index 520d17a6a..a56093be8 100644 --- a/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/SortSam.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/SortSam.scala @@ -16,7 +16,7 @@ class SortSam extends org.broadinstitute.sting.queue.function.JavaCommandLineFun javaMainClass = "net.sf.picard.sam.SortSam" @Input(doc="The input SAM or BAM files to sort.", shortName = "input", fullName = "input_bam_files", required = true) - var input: List[File] = _ + var input: List[File] = Nil @Output(doc="The sorted BAM or SAM output file.", shortName = "output", fullName = "output_bam_file", required = true) var output: File = _ diff --git a/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/ValidateSamFile.scala b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/ValidateSamFile.scala index 726682b89..2c8fbc6d9 100644 --- a/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/ValidateSamFile.scala +++ b/public/scala/src/org/broadinstitute/sting/queue/extensions/picard/ValidateSamFile.scala @@ -17,7 +17,7 @@ class ValidateSamFile extends org.broadinstitute.sting.queue.function.JavaComman javaMainClass = "net.sf.picard.sam.ValidateSamFile" @Input(doc="The input SAM or BAM files to analyze. Must be coordinate sorted.", shortName = "input", fullName = "input_bam_files", required = true) - var input: List[File] = _ + var input: List[File] = Nil @Output(doc="Send output to a file instead of stdout", shortName = "output", fullName = "output_file", required = false) var output: File = _ @@ -26,7 +26,7 @@ class ValidateSamFile extends org.broadinstitute.sting.queue.function.JavaComman var MODE: Mode = Mode.VERBOSE @Argument(doc="List of validation error types to ignore.", shortName = "ignore", fullName = "ignore_error_types", required = false) - var IGNORE: List[String] = _ + var IGNORE: List[String] = Nil @Argument(doc = "The maximum number of lines output in verbose mode.", shortName = "max", fullName = "max_output", required = false) var MAX_OUTPUT: Int = 100 From 8ae24912f480c182524128a9ec35d46fc410d35b Mon Sep 17 00:00:00 2001 From: Guillermo del Angel Date: Mon, 22 Aug 2011 20:39:06 -0400 Subject: [PATCH 136/138] a) Misc fixes in Phase1 indel vqsr script, b) More R-friendly VariantsToTable printing of AC in case of multiple alt alleles c) Rename FixPLOrderingWalker to FixGenotypesWalker and rewrote: no longer need older code, replaced with code to replace genotypes with all-zero PL's with a no-call. --- .../varianteval/evaluators/CountVariants.java | 9 ++++-- .../walkers/variantutils/VariantsToTable.java | 28 ++++--------------- 2 files changed, 12 insertions(+), 25 deletions(-) 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 b356a68dc..711ed9e04 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 @@ -99,22 +99,24 @@ public class CountVariants extends VariantEvaluator implements StandardEval { // This is really not correct. What we really want here is a polymorphic vs. monomorphic count (i.e. on the Genotypes). // 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. - if ( !vc1.isVariant() || (vc1.hasGenotypes() && vc1.getHomRefCount() == vc1.getNSamples()) ) { + if ( !vc1.isVariant() || (vc1.hasGenotypes() && vc1.getHomRefCount() + vc1.getNoCallCount() == vc1.getNSamples()) ) { nRefLoci++; } else { - nVariantLoci++; - switch (vc1.getType()) { + switch (vc1.getType()) { case NO_VARIATION: break; case SNP: + nVariantLoci++; nSNPs++; if (vc1.getAttributeAsBoolean("ISSINGLETON")) nSingletons++; break; case MNP: + nVariantLoci++; nMNPs++; if (vc1.getAttributeAsBoolean("ISSINGLETON")) nSingletons++; break; case INDEL: + nVariantLoci++; if (vc1.isSimpleInsertion()) nInsertions++; else if (vc1.isSimpleDeletion()) @@ -123,6 +125,7 @@ public class CountVariants extends VariantEvaluator implements StandardEval { nComplex++; break; case MIXED: + nVariantLoci++; nMixed++; break; default: 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 7903be507..19db58e0c 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 @@ -206,28 +206,12 @@ public class VariantsToTable extends RodWalker { } if (field.equals("AF") || field.equals("AC")) { - String afo = val; - - double af=0; - if (afo.contains(",")) { - String[] afs = afo.split(","); - afs[0] = afs[0].substring(1,afs[0].length()); - afs[afs.length-1] = afs[afs.length-1].substring(0,afs[afs.length-1].length()-1); - - double[] afd = new double[afs.length]; - - for (int k=0; k < afd.length; k++) - afd[k] = Double.valueOf(afs[k]); - - af = MathUtils.arrayMax(afd); - //af = Double.valueOf(afs[0]); - - } - else - if (!afo.equals("NA")) - af = Double.valueOf(afo); - - val = Double.toString(af); + if (val.contains(",")) { + // strip [,] and spaces + val = val.replace("[",""); + val = val.replace("]",""); + val = val.replace(" ",""); + } } vals.add(val); From ee68713267aad558f1856ea3ae5b1a232ad3a5fb Mon Sep 17 00:00:00 2001 From: Guillermo del Angel Date: Mon, 22 Aug 2011 20:42:47 -0400 Subject: [PATCH 137/138] Further Bug fixes to CountVariants: stratifications were wrong in case genotypes had no-calls, for example if we stratified by sample and a sample had a no-call, this no-call was considered a true variant and counts were incorrectly increased --- .../sting/gatk/walkers/varianteval/evaluators/CountVariants.java | 1 + 1 file changed, 1 insertion(+) 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 711ed9e04..59ef3d992 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 @@ -99,6 +99,7 @@ public class CountVariants extends VariantEvaluator implements StandardEval { // This is really not correct. What we really want here is a polymorphic vs. monomorphic count (i.e. on the Genotypes). // 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()) ) { nRefLoci++; } else {