package abra.cadabra; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import abra.CompareToReference2; import abra.Feature; import abra.Logger; import abra.Pair; import abra.SAMRecordUtils; import htsjdk.samtools.Cigar; import htsjdk.samtools.CigarElement; import htsjdk.samtools.CigarOperator; import htsjdk.samtools.SAMFileHeader; import htsjdk.samtools.SAMRecord; import htsjdk.samtools.SAMSequenceRecord; import htsjdk.samtools.SamReader; import htsjdk.samtools.TextCigarCodec; public class SimpleAlleleCounter { private static final int MIN_MAPQ = 20; private static final int MIN_BASEQ = 20; private static final int MAX_READS = 500000; private String bam; private ReadLocusReader sample; private CompareToReference2 c2r; private String inputVcf; private List<InputVariant> inputVariants; List<SampleCall> sampleRecords = new ArrayList<SampleCall>(); SimpleAlleleCounter(CompareToReference2 c2r, String bam, String vcf) { this.c2r = c2r; this.bam = bam; this.inputVcf = vcf; } void run() throws IOException { loadInputVariants(); outputHeader(); for (InputVariant variant : inputVariants) { process(variant); } } void process(InputVariant variant) throws IOException { Feature region = new Feature(variant.getChrom(), variant.getPos(), variant.getPos()); sample = new ReadLocusReader(bam, region, MAX_READS); processSimple(variant); sample.close(); } private void loadInputVariants() throws IOException { inputVariants = new ArrayList<InputVariant>(); BufferedReader reader = new BufferedReader(new FileReader(inputVcf)); String line = reader.readLine(); while (line != null) { if (!line.startsWith("#") && !line.trim().equals("")) { inputVariants.add(InputVariant.create(line)); } line = reader.readLine(); } reader.close(); } private void processSimple(InputVariant variant) { Iterator<ReadsAtLocus> sampleIter = sample.iterator(); ReadsAtLocus sampleReads = null; SampleCall call = null; while (sampleIter.hasNext() && call == null) { sampleReads = sampleIter.next(); call = processLocus(sampleReads, variant); } if (call == null) { // Create empty call Allele ref = Allele.getAllele(variant.getRef().charAt(0)); Allele alt = variant.getAllele(); call = SampleCall.emptyCall(variant.getChrom(), variant.getPos(), ref, alt, variant.getRef(), variant.getAlt()); } System.out.println(call); } // private boolean sampleCallExceedsThresholds(SampleCall call) { // return call.alt != null && call.alt != Allele.UNK && call.alleleCounts.get(call.alt).getCount() >= MIN_SUPPORTING_READS && // call.getVaf() >= options.getMinVaf() && call.qual >= options.getMinQual(); // } private int getRepeatLength(int period, String unit, Allele.Type alleleType) { if (alleleType == Allele.Type.DEL) { // Subtract 1 from deletions as we are looking for reference context period = Math.max(period-1, 0); } else if (alleleType != Allele.Type.INS) { period = 0; } return period * unit.length(); } // Returns Pair of <base, quality(phred)> private Pair<Character,Character> getBaseAtPosition(SAMRecord read, int refPos) { int readPos = 0; int refPosInRead = read.getAlignmentStart(); int cigarElementIdx = 0; while (refPosInRead <= refPos && cigarElementIdx < read.getCigar().numCigarElements() && readPos < read.getReadLength()) { CigarElement elem = read.getCigar().getCigarElement(cigarElementIdx++); switch(elem.getOperator()) { case H: //NOOP break; case S: case I: readPos += elem.getLength(); break; case D: case N: refPosInRead += elem.getLength(); break; case M: if (refPos < (refPosInRead + elem.getLength())) { readPos += refPos - refPosInRead; if (readPos < read.getReadLength()) { // Found the base. Return it return new Pair<Character, Character>(read.getReadString().charAt(readPos), read.getBaseQualityString().charAt(readPos)); } } else { readPos += elem.getLength(); refPosInRead += elem.getLength(); } break; default: throw new IllegalArgumentException("Invalid Cigar Operator: " + elem.getOperator() + " for read: " + read.getSAMString()); } } return null; } private char getRefBase(String chr, int pos) { return c2r.getSequence(chr, pos, 1).charAt(0); } private SampleCall processLocus(ReadsAtLocus reads, InputVariant variant) { // Always false here boolean isSomatic = false; SampleCall call = null; String chromosome = reads.getChromosome(); int position = reads.getPosition(); // Only process positions if (!variant.getChrom().equals(chromosome) || variant.getPos() != position) { return null; } int tumorMapq0 = 0; int mismatchExceededReads = 0; int totalDepth = 0; Map<Allele, AlleleCounts> alleleCounts = new HashMap<Allele, AlleleCounts>(); // Always include ref allele char refBase = getRefBase(chromosome, position); Allele refAllele = Allele.getAllele(refBase); alleleCounts.put(refAllele, new AlleleCounts()); // Add input variant allele alleleCounts.put(variant.getAllele(), new AlleleCounts()); for (SAMRecord read : reads.getReads()) { if (!read.getDuplicateReadFlag() && !read.getReadUnmappedFlag() && (read.getFlags() & 0x900) == 0) { totalDepth += 1; if (read.getMappingQuality() < MIN_MAPQ) { tumorMapq0 += 1; continue; } // This causes SNPs in HLA regions to drop out, so only run for Indels. if ((variant.getAllele().getType() == Allele.Type.DEL || variant.getAllele().getType() == Allele.Type.INS) && read.getStringAttribute("YA") == null) { // Cap # mismatches in read that can be counted as reference // This is done because realigner caps # of mismatches for remapped indel reads. // This is needed to remove ref bias int editDist = SAMRecordUtils.getEditDistance(read, null, false); int indelBases = SAMRecordUtils.getNumIndelBases(read); int numMismatches = editDist - indelBases; float mismatchRate = (float) .05; if (numMismatches > SAMRecordUtils.getMappedLength(read) * mismatchRate) { // Skip this read mismatchExceededReads += 1; continue; } } IndelInfo readElement = checkForIndelAtLocus(read, position); Allele allele = Allele.UNK; if (readElement != null) { if (readElement.getCigarElement().getOperator() == CigarOperator.D) { allele = new Allele(Allele.Type.DEL, readElement.getCigarElement().getLength()); } else if (readElement.getCigarElement().getOperator() == CigarOperator.I) { allele = new Allele(Allele.Type.INS, readElement.getCigarElement().getLength()); } } else { // Pair in format <base, quality> Pair<Character, Character> base = getBaseAtPosition(read, position); if (variant.getAllele().getType() == Allele.Type.DEL || variant.getAllele().getType() == Allele.Type.INS) { // Indel Pair<Character, Character> nextBase = getBaseAtPosition(read, position+1); IndelInfo readIndel = checkForIndelAtLocus(read.getAlignmentStart(), read.getCigar(), position); if (readIndel == null && base != null && nextBase != null && base.getSecond()-'!' >= MIN_BASEQ) { allele = Allele.getAllele(base.getFirst()); } } else if (base != null && variant.getAllele().getType() == Allele.Type.MNP) { // MNP if (base.getFirst() == variant.getAlt().charAt(0) && base.getSecond()-'!' >= MIN_BASEQ) { // Look ahead to remaining bases for comparison StringBuffer bases = new StringBuffer(); bases.append(base.getFirst()); int i = 1; while (i < variant.getAlt().length()) { Pair<Character, Character> nextBase = getBaseAtPosition(read, position+i); if (nextBase != null && nextBase.getSecond()-'!' >= MIN_BASEQ) { bases.append(nextBase.getFirst()); } else { break; } i += 1; } if (bases.toString().equals(variant.getAlt())) { allele = variant.getAllele(); } else { allele = Allele.getAllele(base.getFirst()); } } else { allele = Allele.getAllele(base.getFirst()); } } else { // SNP if (base != null && base.getSecond()-'!' >= MIN_BASEQ) { allele = Allele.getAllele(base.getFirst()); } } } if (allele != Allele.UNK) { if (!alleleCounts.containsKey(allele)) { alleleCounts.put(allele, new AlleleCounts()); } AlleleCounts ac = alleleCounts.get(allele); ac.incrementCount(read); if (readElement != null) { ac.updateReadIdx(readElement.getReadIndex()); } if (allele.getType() == Allele.Type.INS) { ac.updateInsertBases(readElement.getInsertBases()); } } } } // Allow readId sets to be garbage collected. for (AlleleCounts counts : alleleCounts.values()) { counts.clearReadIds(); } // Allele alt = getAltIndelAllele(Allele.getAllele(refBase), alleleCounts); Allele alt = variant.getAllele(); String refSeq = null; if (!isSomatic) { int chromosomeLength = c2r.getChromosomeLength(chromosome); refSeq = "N"; if (position > 10 && position < chromosomeLength-10) { refSeq = c2r.getSequence(chromosome, position-9, 20); } } // if (alt != null && (alt.getType() == Allele.Type.DEL || alt.getType() == Allele.Type.INS) && refAllele != Allele.UNK) { if (alt != null && refAllele != Allele.UNK) { AlleleCounts altCounts = alleleCounts.get(alt); AlleleCounts refCounts = alleleCounts.get(refAllele); Pair<Integer, String> repeat = getRepeatPeriod(chromosome, position, alt, variant.getAlt()); double qual = 0; int usableDepth = 0; int repeatLength = getRepeatLength(repeat.getFirst(), repeat.getSecond(), alt.getType()); AlleleCounts.setSpanEnd(position+repeatLength, alleleCounts); usableDepth = AlleleCounts.sum(alleleCounts.values()); qual = calcPhredScaledQuality(refCounts.getCount(), altCounts.getCount(), usableDepth); String refField = variant.getRef(); String altField = variant.getAlt(); // TODO: Check preferred insert bases against input variant!!! String altInsert = null; if (variant.getAllele().getType() == Allele.Type.INS) { altInsert = refField + getPreferredInsertBases(alt, altCounts); } // if (alt.getType() == Allele.Type.DEL) { // refField = getDelRefField(chromosome, position, alt.getLength()); // altField = refField.substring(0, 1); // } else if (alt.getType() == Allele.Type.INS) { // refField = getInsRefField(chromosome, position); // altField = refField + getPreferredInsertBases(alt, altCounts); // } call = new SampleCall(chromosome, position, refAllele, alt, alleleCounts, totalDepth, usableDepth, qual, repeat.getFirst(), repeat.getSecond(), tumorMapq0, refField, altField, mismatchExceededReads, refSeq, altInsert); } else { String refField = getInsRefField(chromosome, position); String altField = "."; double qual = 0; int rp = 0; String ru = ""; call = new SampleCall(chromosome, position, refAllele, Allele.UNK, alleleCounts, totalDepth, 0, qual, rp, ru, tumorMapq0, refField, altField, mismatchExceededReads, refSeq, ""); } return call; } private String getPreferredInsertBases(Allele allele, AlleleCounts counts) { String bases = null; if (counts.getPreferredInsertBases().isEmpty()) { StringBuffer buf = new StringBuffer(); for (int i=0; i<allele.getLength(); i++) { buf.append('N'); } bases = buf.toString(); } else { bases = counts.getPreferredInsertBases(); } return bases; } public static class SampleCall { public static final String FORMAT = "GT:DP:DP2:AD:AD2:ROR:LMQ:ISPAN:VAF:MER:FROR"; String chromosome; int position; Allele ref; Allele alt; Map<Allele, AlleleCounts> alleleCounts; int totalReads; int usableDepth; double qual; int repeatPeriod; String repeatUnit; int mapq0; String refField; String altField; int mismatchExceededReads; HomopolymerRun hrun; String context; int ispan; double fs; String altInsert; static SampleCall emptyCall(String chromosome, int position, Allele ref, Allele alt, String refField, String altField) { SampleCall call = new SampleCall(); call.chromosome = chromosome; call.position = position; call.ref = ref; call.alt = alt; call.alleleCounts = new HashMap<Allele, AlleleCounts>(); call.alleleCounts.put(ref, new AlleleCounts()); call.alleleCounts.put(alt, new AlleleCounts()); call.refField = refField; call.altField = altField; return call; } private SampleCall() { } SampleCall(String chromosome, int position, Allele ref, Allele alt, Map<Allele, AlleleCounts> alleleCounts, int totalReads, int usableDepth, double qual, int repeatPeriod, String repeatUnit, int mapq0, String refField, String altField, int mismatchExceededReads, String context, String altInsert) { this.chromosome = chromosome; this.position = position; this.ref = ref; this.alt = alt; this.alleleCounts = alleleCounts; this.totalReads = totalReads; this.usableDepth = usableDepth; this.qual = qual; this.repeatPeriod = repeatPeriod; this.repeatUnit = repeatUnit; this.mapq0 = mapq0; this.refField = refField; this.altField = altField; this.altInsert = altInsert; AlleleCounts altCounts = alleleCounts.get(alt); this.mismatchExceededReads = mismatchExceededReads; if (context != null) { this.hrun = HomopolymerRun.find(context); this.context = context; } ispan = altCounts == null ? 0 : altCounts.getMaxReadIdx()-altCounts.getMinReadIdx(); } public float getVaf() { float vaf = 0; AlleleCounts altCounts = alleleCounts.get(alt); if (altCounts != null && usableDepth > 0) { vaf = (float) altCounts.getCount() / (float) usableDepth; } return vaf; } public String getSampleInfo(Allele ref, Allele alt) { AlleleCounts refCounts = alleleCounts.get(ref); AlleleCounts altCounts = alleleCounts.get(alt); if (refCounts == null) { refCounts = AlleleCounts.EMPTY_COUNTS; } if (altCounts == null) { altCounts = AlleleCounts.EMPTY_COUNTS; } float vaf = getVaf(); // Calculate phred scaled probability of read orientations occurring by chance int refFwd = refCounts.getFwd(); int refRev = refCounts.getRev(); int altFwd = altCounts.getFwd(); int altRev = altCounts.getRev(); FishersExactTest test = new FishersExactTest(); double fsP = test.twoTailedTest(refFwd, refRev, altFwd, altRev); // Use abs to get rid of -0 this.fs = Math.abs(-10 * Math.log10(fsP)); String sampleInfo = String.format("0/1:%d:%d:%d,%d:%d,%d:%d,%d,%d,%d:%d:%d:%.2f:%d:%.2f", usableDepth, totalReads, refCounts.getCount(), altCounts.getCount(), refCounts.getTotalCount(), altCounts.getTotalCount(), refCounts.getFwd(), refCounts.getRev(), altCounts.getFwd(), altCounts.getRev(), mapq0, ispan, vaf, mismatchExceededReads, fs); return sampleInfo; } public String toString() { String pos = String.valueOf(position); String qualStr = String.format("%.2f", qual); int hrunLen = hrun != null ? hrun.getLength() : 0; char hrunBase = hrun != null ? hrun.getBase() : 'N'; int hrunPos = hrun != null ? hrun.getPos() : 0; String info; if (totalReads == 0) { // Skip empty call info = "."; } else if (altInsert != null && altField.length() > 1 && !altInsert.equals(altField) && alleleCounts.get(alt).getCount() > 0) { // Record info plus alternative inserted sequence (same length, but base mismatches with input variant) info = String.format("RP=%d;RU=%s;HRUN=%d,%d;CTX=%s;ALT_INSERT=%s", repeatPeriod, repeatUnit, hrunLen, hrunPos, context, altInsert.substring(1)); } else { info = String.format("RP=%d;RU=%s;HRUN=%d,%d;CTX=%s", repeatPeriod, repeatUnit, hrunLen, hrunPos, context); } String sampleInfo = getSampleInfo(ref, alt); return String.join("\t", chromosome, pos, ".", refField, altField, qualStr, ".", info, SampleCall.FORMAT, sampleInfo); } } static double strandBias(int rf, int rr, int af, int ar) { FishersExactTest test = new FishersExactTest(); double sb = test.twoTailedTest(rf, rf, af, ar); return sb; } static double calcPhredScaledQuality(int refObs, int altObs, int dp) { return -10 * Math.log10(BetaBinomial.betabinCDF(dp, altObs)); } private Pair<Integer, String> getRepeatPeriod(String chromosome, int position, Allele indel, String altString) { int chromosomeEnd = c2r.getReferenceLength(chromosome); int length = Math.min(indel.getLength() * 100, chromosomeEnd-position-2); String sequence = c2r.getSequence(chromosome, position+1, length); String bases; if (indel.getType() == Allele.Type.DEL) { bases = sequence.substring(0, indel.getLength()); } else { bases = altString.substring(1); } String repeatUnit = RepeatUtils.getRepeatUnit(bases); int period = RepeatUtils.getRepeatPeriod(repeatUnit, sequence); return new Pair<Integer, String>(period, repeatUnit); } private String getDelRefField(String chromosome, int position, int length) { return c2r.getSequence(chromosome, position, length+1); } private String getInsRefField(String chromosome, int position) { return c2r.getSequence(chromosome, position, 1); } private IndelInfo checkForIndelAtLocus(SAMRecord read, int refPos) { IndelInfo elem = null; // if (refPos == 105243047 && read.getReadName().equals("D7T4KXP1:400:C5F94ACXX:5:2302:20513:30410")) { // System.out.println("bar"); // } String contigInfo = read.getStringAttribute("YA"); if (contigInfo != null) { // Get assembled contig info. String[] fields = contigInfo.split(":"); int contigPos = Integer.parseInt(fields[1]); Cigar contigCigar = TextCigarCodec.decode(fields[2]); // Check to see if contig contains indel at current locus elem = checkForIndelAtLocus(contigPos, contigCigar, refPos); if (elem != null) { // Now check to see if this read supports the indel IndelInfo readElem = checkForIndelAtLocus(read.getAlignmentStart(), read.getCigar(), refPos); // Allow partially overlapping indels to support contig // (Should only matter for inserts) if (readElem == null || readElem.getCigarElement().getOperator() != elem.getCigarElement().getOperator()) { // Read element doesn't match contig indel elem = null; } else { elem.setReadIndex(readElem.getReadIndex()); // If this read overlaps the entire insert, capture the bases. if (elem.getCigarElement().getOperator() == CigarOperator.I) { if (elem.getCigarElement().getLength() == readElem.getCigarElement().getLength()) { String insertBases = read.getReadString().substring(readElem.getReadIndex(), readElem.getReadIndex()+readElem.getCigarElement().getLength()); elem.setInsertBases(insertBases); } else if (readElem.getCigarElement().getLength() < elem.getCigarElement().getLength()) { int lengthDiff = elem.getCigarElement().getLength() - readElem.getCigarElement().getLength(); if (readElem.getReadIndex() == 0) { elem.setReadIndex(readElem.getReadIndex() - lengthDiff); } else if (readElem.getReadIndex() == read.getReadLength()-1) { elem.setReadIndex(readElem.getReadIndex() + lengthDiff); } } } } } } return elem; } private IndelInfo checkForIndelAtLocus(int alignmentStart, Cigar cigar, int refPos) { IndelInfo ret = null; int readIdx = 0; int currRefPos = alignmentStart; for (CigarElement element : cigar.getCigarElements()) { if (element.getOperator() == CigarOperator.M) { readIdx += element.getLength(); currRefPos += element.getLength(); } else if (element.getOperator() == CigarOperator.I) { if (currRefPos == refPos+1) { ret = new IndelInfo(element, readIdx); break; } readIdx += element.getLength(); } else if (element.getOperator() == CigarOperator.D) { if (currRefPos == refPos+1) { ret = new IndelInfo(element, readIdx); break; } currRefPos += element.getLength(); } else if (element.getOperator() == CigarOperator.S) { readIdx += element.getLength(); } else if (element.getOperator() == CigarOperator.N) { currRefPos += element.getLength(); } if (currRefPos > refPos+1) { break; } } return ret; } private void outputHeader() throws IOException { SAMFileHeader header; String vcfColumns; SamReader reader = SAMRecordUtils.getSamReader(bam); header = reader.getFileHeader(); reader.close(); vcfColumns = "#CHROM POS ID REF ALT QUAL FILTER INFO FORMAT SAMPLE"; System.out.println("##fileformat=VCFv4.2"); System.out.println("##reference=file://" + c2r.getRefFileName()); for (SAMSequenceRecord seq : header.getSequenceDictionary().getSequences()) { System.out.println(String.format("##contig=<ID=%s,length=%d>", seq.getSequenceName(), seq.getSequenceLength())); } System.out.println("##INFO=<ID=RP,Number=1,Type=Integer,Description=\"Number of times smallest repeating alternate sequence appears in the reference\">"); System.out.println("##INFO=<ID=RU,Number=1,Type=String,Description=\"Smallest repeat unit within alternate sequence. Appears RP times in reference\">"); System.out.println("##INFO=<ID=HRUN,Number=2,Type=Integer,Description=\"Length,position of homopolymer run found in CTX\">"); System.out.println("##INFO=<ID=CTX,Number=1,Type=String,Description=\"Reference context sequence\">"); System.out.println("##FORMAT=<ID=GT,Number=1,Type=String,Description=\"Genotype\">"); System.out.println("##FORMAT=<ID=DP,Number=1,Type=Integer,Description=\"Depth (fragment)\">"); System.out.println("##FORMAT=<ID=DP2,Number=1,Type=Integer,Description=\"Depth 2 (read)\">"); System.out.println("##FORMAT=<ID=AD,Number=2,Type=Integer,Description=\"Allele Depth (fragment)\">"); System.out.println("##FORMAT=<ID=AD2,Number=2,Type=Integer,Description=\"Allele Depth (read)\">"); System.out.println("##FORMAT=<ID=ROR,Number=4,Type=Integer,Description=\"Read Orientation (ref_fwd, ref_rev, alt_fwd, alt_rev)\">"); System.out.println("##FORMAT=<ID=LMQ,Number=1,Type=Integer,Description=\"Number of reads filtered due to low mapping quality\">"); System.out.println("##FORMAT=<ID=ISPAN,Number=1,Type=Integer,Description=\"Max variant read pos minus min variant read pos\">"); System.out.println("##FORMAT=<ID=VAF,Number=1,Type=Float,Description=\"Variant allele frequency\">"); System.out.println("##FORMAT=<ID=MER,Number=1,Type=Integer,Description=\"Number of ref reads with num mismatches greater than read length * .05\">"); System.out.println("##FORMAT=<ID=FROR,Number=1,Type=Float,Description=\"Phred scaled Fisher's Exact Test for read orientation\">"); System.out.println(vcfColumns); } static class InputVariant { private String chrom; private int pos; private String ref; private String alt; private Allele allele; static InputVariant create(String str) { String[] fields = str.split("\\s"); String chrom = fields[0]; int pos = Integer.parseInt(fields[1]); String ref = fields[3]; String alt = fields[4]; int length = 1; if (ref.length() != 1 && alt.length() != 1 && ref.length() != alt.length()) { // Only supporting simple indel representations for now. throw new UnsupportedOperationException("At least one of the REF and ALT fields must be of length 1 for indels"); } Allele allele = Allele.UNK; if (ref.length() > alt.length()) { length = ref.length() - alt.length(); allele = new Allele(Allele.Type.DEL, length); } else if (alt.length() > ref.length()) { length = alt.length() - ref.length(); allele = new Allele(Allele.Type.INS, length); } else if (alt.length() > 1 && alt.length() == ref.length()) { length = alt.length(); allele = Allele.getMnpAllele(alt); } else { allele = Allele.getAllele(alt.charAt(0)); } return new InputVariant(chrom, pos, ref, alt, allele); } private InputVariant(String chrom, int pos, String ref, String alt, Allele allele) { this.chrom = chrom; this.pos = pos; this.ref = ref; this.alt = alt; this.allele = allele; } public String getChrom() { return chrom; } public int getPos() { return pos; } public String getRef() { return ref; } public String getAlt() { return alt; } public Allele getAllele() { return allele; } } public static void main(String[] args) throws Exception { String ref = args[0]; String bam = args[1]; String vcf = args[2]; // String bam = "/home/lmose/dev/mc3/allele_counter/TCGA-D1-A163/TCGA-D1-A163.star.abra2.mc3.bam"; // String bam = "/home/lmose/dev/mc3/allele_counter/TCGA-3N-A9WC/TCGA-3N-A9WC.mc3.callable.bam"; // String vcf = "/home/lmose/dev/mc3/allele_counter/TCGA-D1-A163/TCGA-D1-A163.maf.mc3.vcf"; // String vcf = "/home/lmose/dev/mc3/allele_counter/TCGA-3N-A9WC/TCGA-3N-A9WC.mc3.dna.vcf"; // String ref = "/home/lmose/dev/reference/hg19/19.fa"; // String vcf = "/home/lmose/dev/mc3/allele_counter/TCGA-D1-A163/TCGA-D1-A163.maf.mc3.chr1.vcf"; // String vcf = "t6.vcf"; CompareToReference2 c2r = new CompareToReference2(); c2r.init(ref); SimpleAlleleCounter sac = new SimpleAlleleCounter(c2r, bam, vcf); sac.run(); } }