diff --git a/src/main/scala/com/fulcrumgenomics/umi/DuplexConsensusCaller.scala b/src/main/scala/com/fulcrumgenomics/umi/DuplexConsensusCaller.scala index 4c3d9c4cf..d006a6499 100644 --- a/src/main/scala/com/fulcrumgenomics/umi/DuplexConsensusCaller.scala +++ b/src/main/scala/com/fulcrumgenomics/umi/DuplexConsensusCaller.scala @@ -25,13 +25,13 @@ package com.fulcrumgenomics.umi import java.lang.Math.min - import com.fulcrumgenomics.FgBioDef._ import com.fulcrumgenomics.bam.api.SamRecord import com.fulcrumgenomics.commons.util.LazyLogging import com.fulcrumgenomics.umi.DuplexConsensusCaller._ import com.fulcrumgenomics.umi.UmiConsensusCaller.ReadType.{ReadType, _} import com.fulcrumgenomics.umi.UmiConsensusCaller.{SimpleRead, SourceRead} +import com.fulcrumgenomics.umi.Umis.UmiSeparatorPattern import com.fulcrumgenomics.util.NumericTypes.PhredScore /** @@ -324,7 +324,7 @@ class DuplexConsensusCaller(override val readNamePrefix: String, // UMI bases are present, `None` otherwise. reads.flatMap(_.sam).flatMap { rec => rec.get[String](ConsensusTags.UmiBases).map { umi => - if (rec.firstOfPair == firstOfPair) umi else umi.split('-').reverse.mkString("-") + if (rec.firstOfPair == firstOfPair) umi else UmiSeparatorPattern.split(umi, -1).reverse.mkString("-") } } } diff --git a/src/test/scala/com/fulcrumgenomics/umi/DuplexConsensusCallerTest.scala b/src/test/scala/com/fulcrumgenomics/umi/DuplexConsensusCallerTest.scala index e51fad2bd..a22ca6d24 100644 --- a/src/test/scala/com/fulcrumgenomics/umi/DuplexConsensusCallerTest.scala +++ b/src/test/scala/com/fulcrumgenomics/umi/DuplexConsensusCallerTest.scala @@ -88,6 +88,29 @@ class DuplexConsensusCallerTest extends UnitSpec with OptionValues { r2.quals(0) > 30 shouldBe true } + Seq( + Seq("right", "ACT-", "-ACT"), + Seq("left", "-ACT", "ACT-"), + ).foreach { case Seq(orientation, leftUmi, rightUmi) => + it should s"create a simple double stranded consensus for a pair of A and a pair of B reads in which the $orientation UMI is absent" in { + val builder = new SamBuilder(readLength = 10, baseQuality = 20) + builder.addPair(name = "q1", start1 = 100, start2 = 200, strand1 = Plus, strand2 = Minus, bases1 = "AAAAAAAAAA", bases2 = "CCCCCCCCCC", attrs = Map(MI -> "foo/A", RX -> leftUmi)) + builder.addPair(name = "q2", start1 = 200, start2 = 100, strand1 = Minus, strand2 = Plus, bases1 = "CCCCCCCCCC", bases2 = "AAAAAAAAAA", attrs = Map(MI -> "foo/B", RX -> rightUmi)) + + val recs = c.consensusReadsFromSamRecords(builder.toSeq) + recs should have size 2 + val r1 = recs.find(_.firstOfPair).getOrElse(fail("No first of pair.")) + val r2 = recs.find(_.secondOfPair).getOrElse(fail("No second of pair.")) + r1.basesString shouldBe "AAAAAAAAAA" + r2.basesString shouldBe "GGGGGGGGGG" // after un-revcomping + r1.quals(0) > 30 shouldBe true + r2.quals(0) > 30 shouldBe true + // Both R1 and R2 inherit the RX pattern of the "A" family which is the left-oriented read pair in this test. + r1.get[String](RX) shouldBe Some(leftUmi) + r2.get[String](RX) shouldBe Some(leftUmi) + } + } + Seq(true).foreach { allowSingleStrand => val extraName: String = if (allowSingleStrand) "" else "not " Seq("A", "B").foreach { duplexStrand =>