diff --git a/docs/npgraph.gif b/docs/npgraph.gif new file mode 100644 index 0000000..0fecbef Binary files /dev/null and b/docs/npgraph.gif differ diff --git a/docs/npgraph.md b/docs/npgraph.md index aab4783..cade03e 100644 --- a/docs/npgraph.md +++ b/docs/npgraph.md @@ -1,10 +1,8 @@ -# *npGraph* - Resolve assemblgraph in real-time using nanopore data +# *npGraph* - Resolve assembly graph in real-time using nanopore data This is another real-time scaffolder beside [npScarf](https://github.com/mdcao/npScarf). Instead of using contig sequences as pre-assemblies, this tool is able to work on assembly graph (from [SPAdes](http://cab.spbu.ru/software/spades/)). The batch algorithm has been implemented in hybrid assembler module of [Unicycler](https://github.com/rrwick/Unicycler) and others. -

- npGraph -

+![npGraph GUI](npgraph.gif) ## Introduction *npScarf* is the real-time hybrid assembler that use the stream of long reads to bridge the Illumina contigs together, expecting to give more complete genome sequences while the sequencing process is still ongoing. The pipeline has been applied sucessfully for microbial genomics and even bigger data sets. However, due to its greedy approach over the noisy data, it is difficult to eliminate all mis-assemblies without further pre-processing and parameter tuning. To help prevent this issue, the assembly graph - bulding block graph structure for the contigs - should be used as the source for bridging algorithm. @@ -83,7 +81,7 @@ More features would be added later to the GUI but it's not the focus of this pro All settings from the GUI can be set beforehand via commandline interface. Without using GUI, the mandatory inputs are assembly graph file (*-si*) and long-read data (*-li*). The assembly graph must be output from SPAdes in either FASTG or GFA format (normally *assembly_graph.fastg* or *assembly_graph.gfa*). -From new version of SPAdes, the output GFA file is *assembly_graph_with_scaffolds.gfa* which includes SPAdes path finding and scaffolding results. Sometimes, this might give additional mis-assemblies so the original graph of the building-block contigs (fastg file) is preferred. +From new version of SPAdes, the output GFA file is *assembly_graph_with_scaffolds.gfa* which includes SPAdes path finding and scaffolding results. Sometimes, this might give additional mis-assemblies so the original graph of the building-block contigs is preferred. The long-read data will be used for bridging and can be given as DNA sequences (FASTA/FASTQ format, possible .gz) or alignment records (SAM/BAM) as mentioned above. *npGraph* will try to guess the format of the inputs based on the extensions, but sometimes you'll have to specify it yourself (e.g. when "-" is provided to read from *stdin*). If the sequences are given, then it's mandatory to have either minimap2 (recommended) or BWA-MEM installed in your system to do the alignment between long reads and the pre-assemblies. @@ -96,7 +94,7 @@ or if the graph file is GFA v1 we can use ``` awk '/^S/{print ">"$2; print $3;}' assembly_graph.gfa | fold > assembly_graph.fasta ``` -Note that GFA file from SPAdes is preferred over FASTG since the former gives hint about the k-mer parameter and others, also it is becoming the standard for assembly graph that adapted by many other software. +Note that GFA format from SPAdes is preferred over FASTG since the former gives hint about the k-mer parameter and others, also it is becoming the standard for assembly graph that adapted by many other software. And then you can generate SAM/BAM file with our recommended parameters: ``` minimap2 -t16 -k15 -w5 -a assembly_graph.fasta nnp.fastq ... @@ -150,9 +148,7 @@ awk -F'[:;]' -v q="'" '/^>/{if(index($1,q) ==0 ) flag=1; else flag=0;} {if(flag) Below is how it looked like using *npGraph* with a mock community of 11 species from PoreCamp. -

- npGraph -

+![Metagenomics](npgraph_pc.gif) ### Note diff --git a/docs/npgraph_pc.gif b/docs/npgraph_pc.gif new file mode 100644 index 0000000..9aa1a2d Binary files /dev/null and b/docs/npgraph_pc.gif differ diff --git a/docs/npgraph_pc_short.gif b/docs/npgraph_pc_short.gif new file mode 100644 index 0000000..e261d4c Binary files /dev/null and b/docs/npgraph_pc_short.gif differ diff --git a/pom.xml b/pom.xml index 157b534..a8becaf 100644 --- a/pom.xml +++ b/pom.xml @@ -1,10 +1,11 @@ + 4.0.0 np assembly - 0.1.1-SNAPSHOT + 0.2.1-beta jar assembly @@ -14,6 +15,8 @@ UTF-8 11 13 + 2.11.2 + 3.4.2 @@ -45,6 +48,7 @@ commons-io commons-io + com.github.graphstream gs-algo @@ -57,22 +61,35 @@ com.github.graphstream gs-core - + javax.json javax.json-api + + + + org.apache.logging.log4j + log4j-api + + + org.apache.logging.log4j + log4j-core + + + com.lmax + disruptor + + com.google.guava guava @@ -93,24 +110,57 @@ org.openjfx javafx-controls - ${javafx.version} org.openjfx javafx-fxml - ${javafx.version} + + + io.grpc + grpc-netty-shaded + 1.28.0 + + + io.grpc + grpc-protobuf + 1.28.0 + + + io.grpc + grpc-stub + 1.28.0 + + + + javax.annotation + javax.annotation-api + 1.3.2 + + - - - resources/icons - - - resources/css - - + + + resources/icons + + + resources/css + + + resources + + + + + + kr.motd.maven + os-maven-plugin + 1.6.2 + + + org.apache.maven.plugins @@ -124,6 +174,12 @@ + + foo.bar.Generate + + true + + @@ -155,10 +211,32 @@ ${maven.compiler.release} - + + + + @@ -202,30 +280,35 @@ gs-core 2.0-alpha - + javax.json javax.json-api 1.0 - - org.slf4j - slf4j-api - 1.7.25 - - - org.slf4j - slf4j-simple - 1.7.25 - + + + org.apache.logging.log4j + log4j-api + ${log4j.version} + + + org.apache.logging.log4j + log4j-core + ${log4j.version} + + + + + com.lmax + disruptor + ${disruptor.version} + + com.google.guava guava - 18.0 + 29.0-jre com.github.samtools diff --git a/resources/log4j2.xml b/resources/log4j2.xml new file mode 100644 index 0000000..921564f --- /dev/null +++ b/resources/log4j2.xml @@ -0,0 +1,36 @@ + + + + + + + + + + %d %p %c{1.} [%t] %m%n + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/.fastg_to_fasta.sh.un~ b/scripts/.fastg_to_fasta.sh.un~ new file mode 100755 index 0000000..3532908 Binary files /dev/null and b/scripts/.fastg_to_fasta.sh.un~ differ diff --git a/scripts/fastg_to_fasta.sh b/scripts/fastg_to_fasta.sh index 98e410f..1b11355 100755 --- a/scripts/fastg_to_fasta.sh +++ b/scripts/fastg_to_fasta.sh @@ -3,6 +3,6 @@ if [ "$#" -ne 1 ]; then echo "Usage: $0 " else - awk -F '[:;]' -v q=\' 'BEGIN{flag=0;}/^>/{if(index($1,q)!=0) flag=0; else flag=1;}{if(flag==1) print $1;}' assembly_graph.fastg + awk -F '[:;]' -v q=\' 'BEGIN{flag=0;}/^>/{if(index($1,q)!=0) flag=0; else flag=1;}{if(flag==1) print $1;}' $1 fi diff --git a/src/main/java/japsa/bio/np/ErrorCorrection.java b/src/main/java/japsa/bio/np/ErrorCorrection.java index 459e87d..c6aac27 100644 --- a/src/main/java/japsa/bio/np/ErrorCorrection.java +++ b/src/main/java/japsa/bio/np/ErrorCorrection.java @@ -40,12 +40,13 @@ import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; +import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import japsa.seq.Alphabet; import japsa.seq.Alphabet.DNA; @@ -64,7 +65,7 @@ * */ public class ErrorCorrection { - private static final Logger LOG = LoggerFactory.getLogger(ErrorCorrection.class); + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); public static boolean VERBOSE=false; public static String prefix = "tmp"; public static String msa = "kalign"; @@ -77,12 +78,10 @@ public static double needle(Sequence seq1, Sequence seq2, String prefix) throws String needleOut = prefix + "_alignment.needle"; String cmd = "needle -gapopen 10 -gapextend 0.5 -asequence " + seq1File + " -bsequence " + seq2File + " -outfile " + needleOut; - if(VERBOSE) - LOG.info("Running " + cmd); + logger.trace("Running {}", cmd); Process process = Runtime.getRuntime().exec(cmd); process.waitFor(); - if(VERBOSE) - LOG.info("Run'ed " + cmd ); + logger.trace("Run'ed {}", cmd ); BufferedReader scoreBf = new BufferedReader(new FileReader(needleOut)); String scoreLine = null; @@ -111,8 +110,7 @@ public static void writeAlignmentToFaiFile(List readList,String faiFil SequenceOutputStream faiSt = SequenceOutputStream.makeOutputStream(faiFile); int count = 0; for (Sequence seq:readList){ - if(VERBOSE) - LOG.info(seq.getName() + " " + seq.length()); + logger.trace("{} {}", seq.getName(), seq.length()); seq.writeFasta(faiSt); count ++; if (count >= max) @@ -169,6 +167,8 @@ public static void runMultipleAlignment(String faiFile, String faoFile) throws I }else if (msa.startsWith("spoa")){ cmd = new String[]{"spoa", faiFile}; + }else if (msa.startsWith("abpoa")){ + cmd = new String[]{"abpoa", faiFile, "-o", faoFile}; }else if (msa.startsWith("muscle")){ cmd = new String[]{"muscle", "-in", faiFile, "-out", faoFile, "-maxiters", "5", "-quiet"}; }else if (msa.startsWith("clustal")) { @@ -182,20 +182,19 @@ public static void runMultipleAlignment(String faiFile, String faoFile) throws I }else if (msa.startsWith("mafft")){ cmd = new String[]{"mafft_wrapper.sh", faiFile, faoFile}; }else{ - LOG.error("Unknown msa function " + msa); - throw new InterruptedException("Unknown msa function " + msa); + logger.trace("MSA tool {} is not support!", msa); + msa="none"; + throw new InterruptedException("MSA tool: " + msa); } - if(VERBOSE) - LOG.info("Running " + Arrays.toString(cmd)); + logger.trace("Running {}", Arrays.toString(cmd)); ProcessBuilder builder = new ProcessBuilder(cmd).redirectErrorStream(true); if (msa.startsWith("spoa")){ builder.redirectOutput(new File(faoFile)); } Process process = builder.start(); process.waitFor(); - if(VERBOSE) - LOG.info("Done " + Arrays.toString(cmd)); + logger.trace("Done {}", Arrays.toString(cmd)); temp.deleteOnExit(); } } @@ -215,8 +214,7 @@ public static Sequence readPOAOutput(String faoFile, int seql) throws IOExcepti }//if }//while sb.setName("consensus"); - if(VERBOSE) - LOG.info(sb.getName() + " " + sb.length()); + logger.trace("{} {}", sb.getName(), sb.length()); return sb.toSequence(); } public static Sequence readSPOAOutput(String faoFile, int seql) throws IOException{ @@ -225,16 +223,14 @@ public static Sequence readSPOAOutput(String faoFile, int seql) throws IOExcept String line = bf.readLine(); //First line must start with Consensus if (!line.startsWith("Consensus")){ - if(VERBOSE) - LOG.info("spoa failed: {}",line); + logger.trace("spoa failed: {}", line); return null; } while ( (line = bf.readLine()) != null){ sb.append(new Sequence(Alphabet.DNA(), line, "consensus")); }//while sb.setName("consensus"); - if(VERBOSE) - LOG.info(sb.getName() + " " + sb.length()); + logger.trace("{} {}", sb.getName(), sb.length()); return sb.toSequence(); } public static ArrayList readMultipleAlignment(String faoFile) throws IOException{ @@ -284,8 +280,7 @@ public static Sequence getConsensus(ArrayList seqList){ }//if }//for x sb.setName("consensus"); - if(VERBOSE) - LOG.info(sb.getName() + " " + sb.length()); + logger.trace("{} {}", sb.getName(), sb.length()); consensus = sb.toSequence(); return consensus; } @@ -294,7 +289,7 @@ public static Sequence consensusSequence(List readList, int max, Strin //String faiFile = prefix + "_" + this.currentReadCount; Sequence consensus = null; if (readList != null && readList.size() > 0){ - if (readList.size() == 1){ + if (readList.size() == 1||"none".equals(msa)){ readList.get(0).setName("consensus"); return readList.get(0); }else{ @@ -309,13 +304,18 @@ public static Sequence consensusSequence(List readList, int max, Strin try{ runMultipleAlignment(faiFile, faoFile); }catch(InterruptedException exc){ - return null; + logger.trace("Consensus is pick randomly from the list!"); + readList.get(0).setName("consensus"); + return readList.get(0); } if ("poa".equals(msa)) consensus=readPOAOutput(faoFile, readList.get(0).length()); else if("spoa".equals(msa)){ consensus=readSPOAOutput(faoFile, readList.get(0).length()); + }else if("abpoa".equals(msa)) { + SequenceReader reader = SequenceReader.getReader(faoFile); + consensus=reader.nextSequence(DNA.DNA()); }else{ //3.0 Read in multiple alignment ArrayList seqList = readMultipleAlignment(faoFile); @@ -327,12 +327,11 @@ else if("spoa".equals(msa)){ // Files.deleteIfExists(Paths.get(faiFile)); // Files.deleteIfExists(Paths.get(faoFile)); } - } - - if(consensus==null){ - if(VERBOSE) - LOG.info("Consensus is pick randomly from the list!"); - consensus=readList.get(0); + + if(consensus==null){ + logger.trace("Consensus is pick randomly from the list!"); + consensus=readList.get(0); + } } return consensus; diff --git a/src/main/java/japsa/bio/np/RealtimeAnalysis.java b/src/main/java/japsa/bio/np/RealtimeAnalysis.java index 1f9ab05..36dfc5c 100644 --- a/src/main/java/japsa/bio/np/RealtimeAnalysis.java +++ b/src/main/java/japsa/bio/np/RealtimeAnalysis.java @@ -34,20 +34,21 @@ ****************************************************************************/ package japsa.bio.np; +import java.lang.invoke.MethodHandles; import java.util.Date; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** - * Real time analysis in a thread that runs in parallel with the thread conecting + * Real time analysis in a thread that runs in parallel with the thread connecting * data. * * @author minhduc * */ public abstract class RealtimeAnalysis implements Runnable { - private static final Logger LOG = LoggerFactory.getLogger(RealtimeAnalysis.class); + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); private int readPeriod = 0;//Min number of reads before a new analysis @@ -57,7 +58,7 @@ public abstract class RealtimeAnalysis implements Runnable { protected RealtimeAnalysis(){ } - private boolean waiting = true; + protected boolean waiting = true; protected Long lastTime = 0L;//The last time an analysis is done protected Long startTime; @@ -65,7 +66,7 @@ protected RealtimeAnalysis(){ protected String timeNow; public void stopWaiting(){ - LOG.info("All reads received at " + new Date()); + logger.info("All reads received at {}", new Date()); waiting = false; //TODO: implement notifies if I am sleeping } @@ -76,14 +77,14 @@ public void stopWaiting(){ @Override public void run() { startTime = System.currentTimeMillis(); - LOG.info("Start analysing data at " + new Date(startTime)); + logger.info("Start analysing data at {}", new Date(startTime)); while (waiting){ //Need to wait if timing is not right long timeSleep = timePeriod - (System.currentTimeMillis() - lastTime); if (timeSleep > 0){ try { - LOG.info("Not due time, sleep for " + timeSleep/1000.0 + " seconds"); + logger.info("Not due time, sleep for {} seconds", timeSleep/1000.0); Thread.sleep(timeSleep); } catch (InterruptedException e) { e.printStackTrace(); @@ -94,7 +95,7 @@ public void run() { int currentRead = getCurrentRead(); if (currentRead - lastReadNumber < readPeriod){ try { - LOG.info("Not due read (" + currentRead + "), sleep for " + powerNap/1000.0 + " seconds"); + logger.info("Not due read ({}), sleep for {} seconds", currentRead, powerNap/1000.0); Thread.sleep(powerNap); } catch (InterruptedException e) { e.printStackTrace(); @@ -106,7 +107,7 @@ public void run() { //assert: read number satisfied timeNow = new Date(lastTime).toString(); analysis(); - LOG.info("RUNTIME\t" + timeNow + "\t" + (this.lastTime - this.startTime)/1000.0 + "\t" + this.lastReadNumber + "\t" + (System.currentTimeMillis() - lastTime)/1000.0); + logger.info("RUNTIME\t{}\t{}\t{}\t{}", timeNow, (this.lastTime - this.startTime)/1000.0, this.lastReadNumber, (System.currentTimeMillis() - lastTime)/1000.0); }//while //perform the final analysis @@ -114,10 +115,10 @@ public void run() { lastReadNumber = getCurrentRead(); timeNow = new Date(lastTime).toString(); analysis(); - LOG.info("RUNTIME\t" + timeNow + "\t" + (this.lastTime - this.startTime)/1000.0 + "\t" + this.lastReadNumber + "\t" + (System.currentTimeMillis() - lastTime)/1000.0); + logger.info("RUNTIME\t{}\t{}\t{}\t{}", timeNow, (this.lastTime - this.startTime)/1000.0, this.lastReadNumber, (System.currentTimeMillis() - lastTime)/1000.0); //.. and close it close(); - LOG.info("Real time analysis done"); + logger.info("Real time analysis done"); } abstract protected void close(); diff --git a/src/main/java/japsa/seq/AbstractSequence.java b/src/main/java/japsa/seq/AbstractSequence.java index f5b88e4..824897d 100755 --- a/src/main/java/japsa/seq/AbstractSequence.java +++ b/src/main/java/japsa/seq/AbstractSequence.java @@ -37,9 +37,7 @@ -import japsa.seq.Alphabet; import japsa.util.JapsaMath; - import java.io.IOException; diff --git a/src/main/java/japsa/seq/FastaReader.java b/src/main/java/japsa/seq/FastaReader.java index 70b0b51..2906ee0 100644 --- a/src/main/java/japsa/seq/FastaReader.java +++ b/src/main/java/japsa/seq/FastaReader.java @@ -36,9 +36,6 @@ package japsa.seq; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.BufferedInputStream; import java.io.FileInputStream; import java.io.IOException; diff --git a/src/main/java/japsa/seq/FastqReader.java b/src/main/java/japsa/seq/FastqReader.java index 058f6d0..6e10eba 100644 --- a/src/main/java/japsa/seq/FastqReader.java +++ b/src/main/java/japsa/seq/FastqReader.java @@ -35,13 +35,14 @@ package japsa.seq; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.IOException; import java.io.InputStream; +import java.lang.invoke.MethodHandles; import java.util.Arrays; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + /** @@ -51,7 +52,7 @@ * */ public class FastqReader extends SequenceReader{ - private static final Logger LOG = LoggerFactory.getLogger(FastqReader.class); + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); private byte [] seq = new byte[1024];//estimated max read length private byte [] qual = new byte[1024]; @@ -141,7 +142,7 @@ public FastqSequence nextSequence(Alphabet alphabet) throws IOException { if (seqIndex != qualIndex){ //throw new RuntimeException("Lengths of sequence and quality strings do not match at line " + lineNo + " : " + seqIndex + " vs " + qualIndex); - LOG.warn("Lengths of sequence and quality strings do not match at line " + lineNo + " : " + seqIndex + " vs " + qualIndex); + logger.trace("Lengths of sequence and quality strings do not match at line {} : {} vs {}", lineNo, seqIndex, qualIndex); } //Read the next byte from the stream (expecting a @ or eof diff --git a/src/main/java/japsa/seq/JapsaFileFormat.java b/src/main/java/japsa/seq/JapsaFileFormat.java index ea26e14..1a81658 100644 --- a/src/main/java/japsa/seq/JapsaFileFormat.java +++ b/src/main/java/japsa/seq/JapsaFileFormat.java @@ -34,8 +34,6 @@ package japsa.seq; -import japsa.seq.Alphabet; - import java.io.IOException; import java.io.InputStream; import java.util.Arrays; diff --git a/src/main/java/japsa/seq/PAFRecord.java b/src/main/java/japsa/seq/PAFRecord.java new file mode 100644 index 0000000..d316cda --- /dev/null +++ b/src/main/java/japsa/seq/PAFRecord.java @@ -0,0 +1,78 @@ +package japsa.seq; + +import htsjdk.samtools.Cigar; +import htsjdk.samtools.TextCigarCodec; +import japsa.util.JapsaException; + +/* + * Represent a PAF record generated by minimap2: https://lh3.github.io/minimap2/minimap2.html + * Col Type Description + * 1 string Query sequence name + * 2 int Query sequence length + * 3 int Query start coordinate (0-based inclusive) + * 4 int Query end coordinate (0-based exclusive) + * 5 char ‘+’ if query/target on the same strand; ‘-’ if opposite + * 6 string Target sequence name + * 7 int Target sequence length + * 8 int Target start coordinate on the original strand + * 9 int Target end coordinate on the original strand + * 10 int Number of matching bases in the mapping + * 11 int Number bases, including gaps, in the mapping + * 12 int Mapping quality (0-255 with 255 for missing) + */ + +//IMPORTANT: Note that this class must contain 1-based inclusive coordinate, so proper conversion is required!! +public class PAFRecord { + public String qname, tname; + public int qlen, tlen; + public int qstart, qend, tstart, tend; + public boolean strand; + public int qual, score; + Cigar cigar=null; + + public PAFRecord(String qname, int qlen, int qstart, int qend, + boolean strand, + String tname, int tlen, int tstart, int tend, + int score, int quality ) { + this.qname=qname; this.tname=tname; + this.qlen=qlen; this.qstart=qstart; this.qend=qend; + this.tlen=tlen; this.tstart=tstart; this.tend=tend; + this.qual=quality; this.score=score; + this.strand=strand; + } + //construct a record from a line in .paf file + public PAFRecord(String line) throws JapsaException { + String[] toks= line.split("\t"); + if(toks.length < 12) + throw new JapsaException("Error reading PAF line: " + toks.length + " < 12" + "\n" + line); + qname=toks[0]; + tname=toks[5]; + + strand=("+".equals(toks[4])?true:false); + + qlen=Integer.parseInt(toks[1]); + tlen=Integer.parseInt(toks[6]); + //convert coordinates from 0-based to 1-based + qstart=Integer.parseInt(toks[2])+1; //inclusive -> inclusive + qend=Integer.parseInt(toks[3]); //exclusive -> inclusive + + + tstart=Integer.parseInt(toks[7])+1; + tend=Integer.parseInt(toks[8]); + + qual=Integer.parseInt(toks[11]); + score=Integer.parseInt(toks[9]); + + //Looking for cigar tag + for(int i=12; i< toks.length; i++) { + String tags[]=toks[i].split(":"); + if(tags[0].equalsIgnoreCase("cg")) + cigar=TextCigarCodec.decode(tags[1]); + else + continue; + } + } + + public Cigar getCigar() {return cigar;} + +} diff --git a/src/main/java/japsa/seq/Sequence.java b/src/main/java/japsa/seq/Sequence.java index 81ea968..f3853fa 100755 --- a/src/main/java/japsa/seq/Sequence.java +++ b/src/main/java/japsa/seq/Sequence.java @@ -34,7 +34,6 @@ package japsa.seq; -import japsa.seq.Alphabet; import japsa.util.ByteArray; import java.util.Arrays; diff --git a/src/main/java/japsa/seq/SequenceBuilder.java b/src/main/java/japsa/seq/SequenceBuilder.java index d0db1aa..0adf7e8 100644 --- a/src/main/java/japsa/seq/SequenceBuilder.java +++ b/src/main/java/japsa/seq/SequenceBuilder.java @@ -34,7 +34,6 @@ package japsa.seq; -import japsa.seq.Alphabet; import japsa.util.ByteArray; import java.util.Arrays; diff --git a/src/main/java/japsa/seq/SequenceOutputStream.java b/src/main/java/japsa/seq/SequenceOutputStream.java index ca446f4..4112ed9 100644 --- a/src/main/java/japsa/seq/SequenceOutputStream.java +++ b/src/main/java/japsa/seq/SequenceOutputStream.java @@ -112,7 +112,7 @@ public static SequenceOutputStream makeOutputStream(String fileName)throws IOExc (new GZIPOutputStream (new FileOutputStream(fileName))); else if (fileName.equals("-")) - return new SequenceOutputStream(System.out); + return new SequenceOutputStream(System.out); return new SequenceOutputStream (new FileOutputStream(fileName)); diff --git a/src/main/java/japsa/seq/SequenceReader.java b/src/main/java/japsa/seq/SequenceReader.java index 67b3668..d0485b6 100644 --- a/src/main/java/japsa/seq/SequenceReader.java +++ b/src/main/java/japsa/seq/SequenceReader.java @@ -33,9 +33,6 @@ ****************************************************************************/ package japsa.seq; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.BufferedInputStream; import java.io.BufferedReader; import java.io.Closeable; @@ -55,7 +52,6 @@ * */ public abstract class SequenceReader implements Closeable{ - private static final Logger LOG = LoggerFactory.getLogger(SequenceReader.class); /** * Provide a skeleton of a sequence file reader. Assume that the file diff --git a/src/main/java/japsa/seq/XAFReader.java b/src/main/java/japsa/seq/XAFReader.java index d038846..275a7ae 100644 --- a/src/main/java/japsa/seq/XAFReader.java +++ b/src/main/java/japsa/seq/XAFReader.java @@ -34,15 +34,16 @@ package japsa.seq; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.BufferedReader; import java.io.Closeable; import java.io.IOException; +import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.HashMap; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + /** * eXtendable Annotation Format (xaf) is a format to store information at * certain positions along a genome or a group of sequences. It is a text format @@ -54,7 +55,7 @@ */ public class XAFReader implements Closeable { - private static final Logger LOG = LoggerFactory.getLogger(XAFReader.class); + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); String sep = "\t"; public static final String XAF_HEADER = "#XAF"; @@ -167,7 +168,7 @@ public String getField(String fieldName) { public String getField(int fieldNo) { if (fieldNo >= fields.length) { //throw new RuntimeException("Only " + fields.length + " fields at line " + lineNo); - LOG.warn("Only " + fields.length + " fields at line " + lineNo); + logger.trace("Only {} fields at line {}", fields.length, lineNo); return null; } return fields[fieldNo]; @@ -229,7 +230,7 @@ else if (currentRecord.startsWith(ROW_HEADER)) { for (int i = 0; i < toks.length; i++) { FieldHeader header = headerPool.get(toks[i]); if (header == null) { - LOG.warn("Header " + toks[i] + " not defined"); + logger.trace("Header {} not defined!", toks[i]); header = new FieldHeader(); header.headerStr = toks[i]; headerPool.put(toks[i], header); diff --git a/src/main/java/japsa/util/FxDialogs.java b/src/main/java/japsa/util/FxDialogs.java index 5aa7408..ae0bc3b 100644 --- a/src/main/java/japsa/util/FxDialogs.java +++ b/src/main/java/japsa/util/FxDialogs.java @@ -16,7 +16,6 @@ import java.util.List; import java.util.Optional; -@SuppressWarnings("restriction") public class FxDialogs { public static void showInformation(String title, String message) { diff --git a/src/main/java/japsa/util/HTSUtilities.java b/src/main/java/japsa/util/HTSUtilities.java index ea4cac6..d374407 100644 --- a/src/main/java/japsa/util/HTSUtilities.java +++ b/src/main/java/japsa/util/HTSUtilities.java @@ -33,15 +33,17 @@ ****************************************************************************/ package japsa.util; +import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.Arrays; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + import htsjdk.samtools.CigarElement; import htsjdk.samtools.SAMRecord; import japsa.seq.Alphabet; import japsa.seq.Sequence; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; /** * A collection of utilities to analyse HTS, based on HTS library @@ -49,7 +51,7 @@ * */ public class HTSUtilities { - private static final Logger LOG = LoggerFactory.getLogger(HTSUtilities.class); + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); @@ -123,7 +125,7 @@ public static Sequence readSequence(SAMRecord record, Sequence readSequence, int }//for if (readFrom ==0 || readTo ==0){ - LOG.error("Error at HTSUtilities.readSequence " + readFrom + " " + readTo, 1); + logger.error("Error at HTSUtilities.readSequence {}-{}", readFrom, readTo); System.exit(1); } if (record.getReadNegativeStrandFlag()){ @@ -203,7 +205,7 @@ public static Sequence getReadPosition(SAMRecord rec, int startRef, int endRef){ }// for if (startRead < 0 || endRead < 0){ - LOG.warn(" " + refPos + " " + readPos + " " + startRead + " " + endRead); + logger.warn(" {} {} {} {} ", refPos, readPos, startRead, endRead); return null; } @@ -276,7 +278,7 @@ public static Sequence spanningSequence(SAMRecord record, Sequence readSequence, readEnd = readLength;//1-index if (readLength != readSequence.length()){ - LOG.error("Error0 " + record.getReadName() + " " + readSequence.length() + " vs estimated " + readLength + " Flag = " + record.getFlags()); + logger.error("Error0 {} {} vs estimated {} Flag={}", record.getReadName(), readSequence.length(), readLength, record.getFlags()); return null; } @@ -286,7 +288,7 @@ public static Sequence spanningSequence(SAMRecord record, Sequence readSequence, start = 1;//I am still live in 1-index world if (readEnd > readSequence.length()){ - LOG.error("Error1 " + record.getReadName() + " " + record.getReadLength() + " vs " + readEnd); + logger.error("Error1 {} {} vs {}", record.getReadName(), record.getReadLength(), readEnd); return null; } int end = readEnd + right; @@ -295,7 +297,7 @@ public static Sequence spanningSequence(SAMRecord record, Sequence readSequence, end = readSequence.length(); if (start >= end){ - LOG.error("Error2 " + record.getReadName() + " " + record.getReadLength() + " " + start + " " + end); + logger.error("Error2 {} {} {} {}", record.getReadName(), record.getReadLength(), start, end); return null; } @@ -313,9 +315,7 @@ public static Sequence spanningSequence(SAMRecord record, Sequence readSequence, } }catch(Exception e){ - LOG.warn(e.getMessage()); - e.printStackTrace(); - //continue;//while + logger.warn("{}",e); return null; } diff --git a/src/main/java/japsa/util/JapsaTimer.java b/src/main/java/japsa/util/JapsaTimer.java index 15e46cc..591b9bf 100644 --- a/src/main/java/japsa/util/JapsaTimer.java +++ b/src/main/java/japsa/util/JapsaTimer.java @@ -34,12 +34,14 @@ ****************************************************************************/ package japsa.util; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import java.lang.invoke.MethodHandles; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; //Mon 6 April 2009 public class JapsaTimer { // A Timer object can 'mark' an event by printing the given - private static final Logger LOG = LoggerFactory.getLogger(JapsaTimer.class); + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); // message, // the time since the last event, and the total time the Timer has existed. @@ -53,19 +55,18 @@ public JapsaTimer() { public void mark(String msg) { final long now = System.currentTimeMillis(); - LOG.info(msg + ":" + " increment=" + (now - last) / 1000.0 - + " sec" + " (total=" + (now - milliSecs0) / 1000.0 + ")"); + logger.info("{}: increment= {} sec (total={})", msg, (now - last) / 1000.0, (now - milliSecs0) / 1000.0); last = now; }// mark() static public void systemInfo(){ Runtime runtime = Runtime.getRuntime(); - LOG.info(" CPU=" + runtime.availableProcessors() - + ", maxMem=" + runtime.maxMemory() / 1000000.0 + " MB" - + ", freeMem=" + runtime.freeMemory() / 1000000.0 + " MB" - + ", totalMem=" + runtime.totalMemory() / 1000000.0 + " MB" - + ", usedMem=" + (runtime.totalMemory() -runtime.freeMemory()) / 1000000.0 + " MB" - ); + logger.info(" CPU={}, maxMem={} MB, freeMem={} MB, totalMem={} MB, usedMem={} MB", + runtime.availableProcessors(), + runtime.maxMemory() / 1000000.0, + runtime.freeMemory() / 1000000.0, + runtime.totalMemory() / 1000000.0, + (runtime.totalMemory() -runtime.freeMemory()) / 1000000.0); } /************************************************************************ diff --git a/src/main/java/japsa/util/ProcessManagement.java b/src/main/java/japsa/util/ProcessManagement.java index 5da5704..31e7c12 100644 --- a/src/main/java/japsa/util/ProcessManagement.java +++ b/src/main/java/japsa/util/ProcessManagement.java @@ -35,19 +35,20 @@ package japsa.util; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; +import java.lang.invoke.MethodHandles; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; /** * @author minhduc * */ public class ProcessManagement { - private static final Logger LOG = LoggerFactory.getLogger(ProcessManagement.class); + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); /** * To start a process, redirect output and error to LOG.info, print out * a message and return the status @@ -64,7 +65,7 @@ static public int runProcess(ProcessBuilder pb) throws IOException, InterruptedE String outLine = ""; while ((outLine = pbOut.readLine())!=null){ - LOG.info(outLine.trim()); + logger.info(outLine.trim()); } pbOut.close(); int status = process.waitFor(); diff --git a/src/main/java/org/rtassembly/ConcatChopperCmd.java b/src/main/java/org/rtassembly/ConcatChopperCmd.java index 586778f..f3dcf50 100644 --- a/src/main/java/org/rtassembly/ConcatChopperCmd.java +++ b/src/main/java/org/rtassembly/ConcatChopperCmd.java @@ -5,9 +5,11 @@ import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import org.rtassembly.concatemer.NSDFChopper; +import japsa.bio.np.ErrorCorrection; import japsa.seq.Alphabet; import japsa.seq.Sequence; import japsa.seq.SequenceOutputStream; @@ -22,10 +24,12 @@ public ConcatChopperCmd(){ addString("seq", null, "Name of the base-called FASTQ/FASTA input file."); addString("raw", null, "Name of the raw signal FAST5 input file."); addString("output", "chopper", "Name of the output folder."); + addString("msa", "kalign", "Name of the MSA for consensus calling."); addInt("maxK", 100, "Maximum number of monomer copy in a read. Use for cutoff frequency in low-pass filter."); addInt("minL", 2000, "Minimum length of monomer or genome size of interests."); - + addInt("consensus", 10, "Minimum concatemers for consensus calling a read."); + addStdHelp(); } @@ -36,7 +40,9 @@ public static void main(String[] args) throws IOException{ /***********************************************************************/ String seqInput = cmdLine.getStringVal("seq"), rawInput = cmdLine.getStringVal("raw"), - outputDir = cmdLine.getStringVal("output"); + outputDir = cmdLine.getStringVal("output"), + msa = cmdLine.getStringVal("msa"); + int cThres = cmdLine.getIntVal("consensus"); File outDir=new File(outputDir); if(!outDir.isDirectory() && !outDir.mkdirs()){ @@ -53,7 +59,6 @@ public static void main(String[] args) throws IOException{ try { SequenceReader reader = SequenceReader.getReader(seqInput); Sequence seq=null; - SequenceOutputStream outFile = SequenceOutputStream.makeOutputStream(outputDir+File.separator+"all_monomers.fastq"); while((seq=reader.nextSequence(Alphabet.DNA4()))!=null){ tony.setData(seq); tony.concatemersDetection(); @@ -66,23 +71,33 @@ public static void main(String[] args) throws IOException{ histogram.put(key, clist); } clist.add(tony.getName()); - - - //chop and print - int prev=0, next; - for(int i=0;i=cThres) { + String id=tony.getName().split("\\s+")[0]; + int prev=0, next; + List seqList=new ArrayList<>(); + for(int i=0;i hits, String readID) { + if(hits.isEmpty()) + return; + + Builder requestBuilder = RequestAssembly.newBuilder(); + requestBuilder.setReadId(readID); + for(PAFRecord rec:hits){ + AlignmentMsg msg = AlignmentMsg.newBuilder() + .setQueryName(rec.qname) + .setQueryLength(rec.qlen) + .setQueryStart(rec.qstart) + .setQueryEnd(rec.qend) + .setStrand(rec.strand) + .setTargetName(rec.tname) + .setTargetLength(rec.tlen) + .setTargetStart(rec.tstart) + .setTargetEnd(rec.tend) + .setQuality(rec.qual) + .setScore(rec.score) + .build(); + requestBuilder.addHitsList(msg); + } + RequestAssembly request = requestBuilder.build(); + try { + long before=System.currentTimeMillis(); + ResponseAssembly response = stub.getAssemblyContribution(request); + long interval=System.currentTimeMillis()-before; + logger.info("Get response from server in {} ms: read {} is {}", interval, response.getReadId(), (response.getUsefulness()?"useful":"not useful")); + } catch (StatusRuntimeException e) { + logger.warn("RPC failed: {}", e); + } + return; + + } + public static void main(String[] args) throws IOException, InterruptedException{ + CommandLine cmdLine = new NPGraphClientCmd(); + args = cmdLine.stdParseLine(args); + + /***********************************************************************/ + String target = cmdLine.getStringVal("target"), + reference = cmdLine.getStringVal("si"), + query = cmdLine.getStringVal("li"); + String workDir="/tmp", + algOpts = "-t4 -k15 -w5", + indexFileName=workDir+File.separator+"assembly_graph.mmi"; + + ArrayList command = new ArrayList(); + File indexFile= new File(indexFileName); + if(!indexFile.exists()) { + command.add("minimap2"); + command.addAll(Arrays.asList(algOpts.split("\\s"))); + command.add("-d"); + command.add(indexFileName); + command.add(reference); + ProcessBuilder pb = new ProcessBuilder(command); + Process indexProcess = pb.start(); + indexProcess.waitFor(); + } + //from HybridAssembler.assembly2() + logger.info("Starting alignment at {}", new Date()); + ProcessBuilder pb = null; + command.add("minimap2"); + command.addAll(Arrays.asList(algOpts.split("\\s"))); + command.add("-K20000"); + command.add(indexFileName); + command.add(query); + + + + if ("-".equals(query)){ + pb = new ProcessBuilder(command).redirectInput(Redirect.INHERIT); + }else{ + pb = new ProcessBuilder(command); + } + + Process alignmentProcess = pb.redirectError(ProcessBuilder.Redirect.to(new File(workDir+File.separator+"alignment.log"))).start(); + + logger.info("minimap2 started!"); + InputStreamReader inputStream = new InputStreamReader(alignmentProcess.getInputStream()); + try(BufferedReader reader=new BufferedReader(inputStream)){ + String readID = ""; + ArrayList hits = new ArrayList();// alignment record of the same read; + PAFRecord curRecord=null; + String line; + + channel = ManagedChannelBuilder.forTarget(target).usePlaintext().build(); + stub = AssemblyGuideGrpc.newBlockingStub(channel); + + while ((line=reader.readLine()) != null) { + try { + curRecord = new PAFRecord(line); + logger.info("{} {} {} {} {} {} {} {} {}", + curRecord.qname, + curRecord.qlen, + curRecord.qstart, + curRecord.qend, + (curRecord.strand?"+":"-"), + curRecord.tname, + curRecord.tlen, + curRecord.tstart, + curRecord.tend); + /////////////////////////////////////////////////// + if (curRecord.qual < Alignment.MIN_QUAL){ + logger.info("Ignore low-quality map record: q={}", curRecord.qual); + continue; + } + + + ////////////////////////////////////////////////////////////////// + + if (!readID.equals("") && !readID.equals(curRecord.qname)) { + //generate request and get response + //////////////////////////////////// + testServer(hits, readID); + hits = new ArrayList(); + + } + readID = curRecord.qname; + hits.add(curRecord); + + }catch(Exception e) { + logger.error("Error record! \n {}", e); + break; + } + + }// while + + }catch(Exception e) { + logger.error("Error reading alignment results: \n {}", e); + } + finally { + channel.shutdownNow().awaitTermination(5, TimeUnit.SECONDS); + } + + + } + +} diff --git a/src/main/java/org/rtassembly/NPGraphCmd.java b/src/main/java/org/rtassembly/NPGraphCmd.java index 3b343a1..f5a26e6 100644 --- a/src/main/java/org/rtassembly/NPGraphCmd.java +++ b/src/main/java/org/rtassembly/NPGraphCmd.java @@ -1,48 +1,53 @@ package org.rtassembly; import java.io.File; import java.io.IOException; +import java.lang.invoke.MethodHandles; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; import org.rtassembly.gui.NPGraphFX; import org.rtassembly.npgraph.Alignment; import org.rtassembly.npgraph.BDGraph; -import org.rtassembly.npgraph.GraphWatcher; +import org.rtassembly.npgraph.RealtimeGraphWatcher; import org.rtassembly.npgraph.HybridAssembler; import org.rtassembly.npgraph.SimpleBinner; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import japsa.util.CommandLine; import javafx.application.Application; public class NPGraphCmd extends CommandLine{ - private static final Logger LOG = LoggerFactory.getLogger(NPGraphCmd.class); + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); public NPGraphCmd(){ super(); //Input settings addString("si", "", "Name of the short-read assembly file."); addString("sf", "", "Format of the assembly input file. Accepted format are FASTG, GFA"); addString("li", "", "Name of the long-read data input file, - for stdin."); - addString("lf", "", "Format of the long-read data input file. This may be FASTQ/FASTA (MinION reads) or SAM/BAM (aligned with the assembly graph already)"); + addString("lf", "", "Format of the long-read data input file. This may be FASTQ/FASTA read file or SAM/BAM/PAF alignment file"); addString("output", "/tmp/", "Output folder for temporary files and the final assembly npgraph_assembly.fasta"); addString("sb", "", "Name of the metaBAT file for binning information (experimental)."); - addString("aligner","","Aligner tool that will be used, either minimap2 or bwa"); + addString("aligner","","Aligner tool that will be used, either minimap2 (default) or BWA-MEM"); addString("algOpt", "", "Settings used by aligner to align long reads to the contigs"); - addString("msa","","MSA tools for consensus. Options include spoa, kalign3 (fast); kalign2, poa (slow)."); + addString("msa","","MSA tools for consensus. Options include abpoa (recommended), spoa, kalign3 (fast); kalign2, poa (slow)."); addBoolean("overwrite", true, "Whether to overwrite or reuse the intermediate file"); addBoolean("sp", false, "Whether to use SPAdes contigs.paths for bridging."); //Algorithm-wise addInt("qual", 10, "Minimum quality of alignment to considered"); - addInt("mcov", 3, "Minimum number of reads spanning a confident bridge"); - addInt("depth", 300, "Maximum depth for searching path between 2 neighbors"); - addInt("anchor", 1000, "Minimum length for being considered as an anchor contig."); - addInt("unique", 10000, "Minimum length for being assumed a unique contig."); - + addInt("mincov", 3, "Minimum number of reads spanning a confident bridge"); + addInt("maxcov", 20, "Cut-off number of reads spanning a confident bridge"); + addInt("depth", 300, "Limit depth for searching path between 2 neighbors"); + addInt("anchor", 1000, "Lowerbound of an anchor contig's length."); + addInt("unique", 10000, "Lowerbound of a unique contig's length."); + addInt("time", 10, "Time interval (seconds) to considered for real-time reporting."); + addInt("read", 50, "Read interval to considered for real-time reporting."); + addBoolean("gui", false, "Whether using GUI or not."); addBoolean("keep", false, "Whether to keep extremely-low-coveraged contigs."); - addBoolean("verbose", false, "For debugging."); +// addBoolean("verbose", false, "For debugging."); addStdHelp(); } @@ -65,12 +70,15 @@ public static void main(String[] args) throws IOException{ gui = cmdLine.getBooleanVal("gui"); Alignment.MIN_QUAL = cmdLine.getIntVal("qual"); - HybridAssembler.VERBOSE=cmdLine.getBooleanVal("verbose"); SimpleBinner.ANCHOR_CTG_LEN=cmdLine.getIntVal("anchor"); SimpleBinner.UNIQUE_CTG_LEN=cmdLine.getIntVal("unique"); - GraphWatcher.KEEP=cmdLine.getBooleanVal("keep"); - BDGraph.MIN_SUPPORT=cmdLine.getIntVal("mcov"); + RealtimeGraphWatcher.KEEP=cmdLine.getBooleanVal("keep"); + BDGraph.MIN_SUPPORT=cmdLine.getIntVal("min"); + BDGraph.GOOD_SUPPORT=cmdLine.getIntVal("max"); BDGraph.S_LIMIT=cmdLine.getIntVal("depth"); + RealtimeGraphWatcher.R_INTERVAL=cmdLine.getIntVal("read"); + RealtimeGraphWatcher.T_INTERVAL=cmdLine.getIntVal("time"); + //Default output dir if(outputDir == null) { outputDir = new File(shortReadsInput).getAbsoluteFile().getParent(); @@ -82,28 +90,28 @@ public static void main(String[] args) throws IOException{ //1. Create an assembler object with appropriate file loader HybridAssembler hbAss = new HybridAssembler(); if(shortReadsInput!=null && !shortReadsInput.isEmpty()) - hbAss.setShortReadsInput(shortReadsInput); + hbAss.input.setShortReadsInput(shortReadsInput); if(shortReadsInputFormat!=null && !shortReadsInputFormat.isEmpty()) - hbAss.setShortReadsInputFormat(shortReadsInputFormat); + hbAss.input.setShortReadsInputFormat(shortReadsInputFormat); if(longReadsInput!=null && !longReadsInput.isEmpty()) - hbAss.setLongReadsInput(longReadsInput); + hbAss.input.setLongReadsInput(longReadsInput); if(longReadsInputFormat!=null && !longReadsInputFormat.isEmpty()) - hbAss.setLongReadsInputFormat(longReadsInputFormat); + hbAss.input.setLongReadsInputFormat(longReadsInputFormat); hbAss.setPrefix(outputDir); if(shortReadsBinInput!=null && !shortReadsBinInput.isEmpty()) - hbAss.setBinReadsInput(shortReadsBinInput); + hbAss.input.setBinReadsInput(shortReadsBinInput); if(alg!=null && !alg.isEmpty()) - hbAss.setAligner(alg); + hbAss.input.setAligner(alg); if(algOpt!=null && !algOpt.isEmpty()) - hbAss.setAlignerOpts(algOpt); + hbAss.input.setAlignerOpts(algOpt); if(msa!=null && !msa.isEmpty()) - hbAss.setMSA(msa); + hbAss.input.setMSA(msa); hbAss.setOverwrite(overwrite); - hbAss.setUseSPAdesPath(spaths); + hbAss.input.setUseSPAdesPath(spaths); //4. Call the assembly function or invoke GUI to do so if(gui) { @@ -116,16 +124,15 @@ public static void main(String[] args) throws IOException{ try { if(hbAss.prepareShortReadsProcess() && hbAss.prepareLongReadsProcess()) { hbAss.assembly(); - hbAss.postProcessGraph(); +// hbAss.postProcessGraph(); } else{ - LOG.error("Error with pre-processing step: \n" + hbAss.getCheckLog()); + logger.error("Error(s) happened! Check the log above for more info."); System.exit(1); } } catch (InterruptedException|IOException e) { - LOG.error("Issue when assembly: \n" + e.getMessage()); - e.printStackTrace(); + logger.error("Error {}", e); System.exit(1); } } diff --git a/src/main/java/org/rtassembly/NPGraphServerCmd.java b/src/main/java/org/rtassembly/NPGraphServerCmd.java new file mode 100644 index 0000000..865c208 --- /dev/null +++ b/src/main/java/org/rtassembly/NPGraphServerCmd.java @@ -0,0 +1,131 @@ +package org.rtassembly; +import java.io.File; +import java.io.IOException; +import java.lang.invoke.MethodHandles; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.rtassembly.gui.NPGraphServerFX; +import org.rtassembly.npgraph.Alignment; +import org.rtassembly.npgraph.BDGraph; +import org.rtassembly.npgraph.RealtimeGraphWatcher; +import org.rtassembly.npgraph.HybridAssembler; +import org.rtassembly.npgraph.SimpleBinner; +import org.rtassembly.npgraph.grpc.AssemblyGuideServer; + +import japsa.util.CommandLine; +import javafx.application.Application; + + +public class NPGraphServerCmd extends CommandLine{ + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); + public NPGraphServerCmd(){ + super(); + //Server setttings + addInt("port",2105, "Port number for which the server will listen to."); + + //Input settings + addString("si", "", "Name of the short-read assembly file."); + addString("sf", "", "Format of the assembly input file. Accepted format are FASTG, GFA"); + addString("output", "/tmp/", "Output folder for temporary files and the final assembly npgraph_assembly.fasta"); + + addString("sb", "", "Name of the metaBAT file for binning information (experimental)."); + + addBoolean("overwrite", true, "Whether to overwrite or reuse the intermediate file"); + addBoolean("sp", false, "Whether to use SPAdes contigs.paths for bridging."); + + //Algorithm-wise + addInt("qual", 10, "Minimum quality of alignment to considered"); + addInt("mincov", 3, "Minimum number of reads spanning a confident bridge"); + addInt("maxcov", 20, "Cut-off number of reads spanning a confident bridge"); + addInt("depth", 300, "Limit depth for searching path between 2 neighbors"); + addInt("anchor", 1000, "Lowerbound of an anchor contig's length."); + addInt("unique", 10000, "Lowerbound of a unique contig's length."); + addInt("expect", 10000, "The readuntil protocol will expect a read to span this length for an unblock/proceed decision."); + + addInt("time", 10, "Time interval to considered for real-time assembly."); + addInt("read", 50, "Read interval to considered for real-time assembly."); + + addBoolean("gui", false, "Whether using GUI or not."); + addBoolean("keep", false, "Whether to keep extremely-low-coveraged contigs."); +// addBoolean("verbose", false, "For debugging."); + addStdHelp(); + } + + public static void main(String[] args) throws IOException{ + CommandLine cmdLine = new NPGraphServerCmd(); + args = cmdLine.stdParseLine(args); + + /***********************************************************************/ + int port = cmdLine.getIntVal("port"); + String shortReadsInput = cmdLine.getStringVal("si"), + shortReadsInputFormat = cmdLine.getStringVal("sf"), + outputDir = cmdLine.getStringVal("output"), + shortReadsBinInput = cmdLine.getStringVal("sb"); + boolean overwrite = cmdLine.getBooleanVal("overwrite"), + spaths = cmdLine.getBooleanVal("sp"), + gui = cmdLine.getBooleanVal("gui"); + + Alignment.MIN_QUAL = cmdLine.getIntVal("qual"); + SimpleBinner.ANCHOR_CTG_LEN=cmdLine.getIntVal("anchor"); + SimpleBinner.UNIQUE_CTG_LEN=cmdLine.getIntVal("unique"); + RealtimeGraphWatcher.KEEP=cmdLine.getBooleanVal("keep"); + BDGraph.MIN_SUPPORT=cmdLine.getIntVal("min"); + BDGraph.GOOD_SUPPORT=cmdLine.getIntVal("max"); + BDGraph.S_LIMIT=cmdLine.getIntVal("depth"); + RealtimeGraphWatcher.R_INTERVAL=cmdLine.getIntVal("read"); + RealtimeGraphWatcher.T_INTERVAL=cmdLine.getIntVal("time"); + AssemblyGuideServer.ELEN = cmdLine.getIntVal("expect"); + + //Default output dir + if(outputDir == null) { + outputDir = new File(shortReadsInput).getAbsoluteFile().getParent(); + } + File outDir = new File(outputDir); + if(!outDir.exists()) + outDir.mkdirs(); + + //1. Create an assembler object with appropriate file loader + HybridAssembler hbAss = new HybridAssembler(); + if(shortReadsInput!=null && !shortReadsInput.isEmpty()) + hbAss.input.setShortReadsInput(shortReadsInput); + if(shortReadsInputFormat!=null && !shortReadsInputFormat.isEmpty()) + hbAss.input.setShortReadsInputFormat(shortReadsInputFormat); + + hbAss.input.setLongReadsInput("-"); + hbAss.input.setLongReadsInputFormat("paf"); //to pass prepareLongReadProcess() + + hbAss.setPrefix(outputDir); + if(shortReadsBinInput!=null && !shortReadsBinInput.isEmpty()) + hbAss.input.setBinReadsInput(shortReadsBinInput); + + + hbAss.setOverwrite(overwrite); + hbAss.input.setUseSPAdesPath(spaths); + + //4. Call the assembly function or invoke GUI to do so + if(gui) { + NPGraphServerFX.configServer(hbAss, port); + Application.launch(NPGraphServerFX.class,args); + }else if(shortReadsInput.isEmpty()) { + System.out.println(cmdLine.usageString()); + System.exit(-1); + }else { + try { + if(hbAss.prepareShortReadsProcess() && hbAss.prepareLongReadsProcess()) { + AssemblyGuideServer assServer = new AssemblyGuideServer(port, hbAss); + assServer.start(); + } + else{ + logger.error("Error(s) happened! Check the log above for more info."); + System.exit(1); + } + + } catch (Exception e) { + logger.error("Error", e); + System.exit(1); + } + } + + } +} diff --git a/src/main/java/org/rtassembly/NPScarfCmd.java b/src/main/java/org/rtassembly/NPScarfCmd.java index 5ac3f3b..9826d02 100644 --- a/src/main/java/org/rtassembly/NPScarfCmd.java +++ b/src/main/java/org/rtassembly/NPScarfCmd.java @@ -34,21 +34,21 @@ package org.rtassembly; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; import org.rtassembly.npscarf.ContigBridge; import org.rtassembly.npscarf.RealtimeScaffolding; import org.rtassembly.npscarf.ScaffoldGraph; import org.rtassembly.npscarf.ScaffoldGraphDFS; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import japsa.seq.SequenceReader; import japsa.util.CommandLine; -import japsa.util.Logging; import java.io.BufferedReader; import java.io.File; import java.io.FilenameFilter; import java.io.IOException; +import java.lang.invoke.MethodHandles; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -57,7 +57,7 @@ * */ public class NPScarfCmd extends CommandLine{ - private static final Logger LOG = LoggerFactory.getLogger(NPScarfCmd.class); + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); public NPScarfCmd(){ super(); @@ -155,23 +155,23 @@ public static void main(String[] args) throws bf.close(); if (version.length() == 0){ - LOG.error(bwaExe + " is not the right path to bwa. bwa is required"); + logger.error("{} is not the right path to bwa. bwa is required!", bwaExe); System.exit(1); }else{ - LOG.info("bwa version: " + version); + logger.info("bwa version: {}", version); if (version.compareTo("0.7.11") < 0){ - LOG.error(" Require bwa of 0.7.11 or above"); + logger.error(" Require bwa of 0.7.11 or above"); System.exit(1); } } //run indexing if(cmdLine.getBooleanVal("index")){ - LOG.info("bwa index running..."); + logger.info("bwa index running..."); ProcessBuilder pb2 = new ProcessBuilder(bwaExe,"index",sequenceFile); Process indexProcess = pb2.start(); indexProcess.waitFor(); - LOG.info("bwa index finished!"); + logger.info("bwa index finished!"); } }catch (IOException e){ System.err.println(e.getMessage()); @@ -181,13 +181,13 @@ public static void main(String[] args) throws }else if (format.startsWith("sam") || format.startsWith("bam")){ // no problem }else{ - LOG.error("Unrecognized format: " + format); + logger.error("Unrecognized format: {}", format); System.exit(1); } if(spadesFolder !=null){ if(ScaffoldGraph.assembler==0b00 && graphFile.exists() && pathFile.exists()){ - LOG.info("===> Use assembly graph and path from SPAdes!"); + logger.info("===> Use assembly graph and path from SPAdes!"); }else if (ScaffoldGraph.assembler==0b01){ File f = new File(spadesFolder); File[] matchingFiles = f.listFiles(new FilenameFilter() { @@ -196,18 +196,18 @@ public boolean accept(File dir, String name) { } }); if(matchingFiles.length != 1){ - LOG.info("Failed to looking for an unique *-contigs.dot file in " + spadesFolder + " . Proceeding without assembly graph..."); + logger.info("Failed to looking for an unique *-contigs.dot file in {}. Proceeding without assembly graph...", spadesFolder); spadesFolder=null; } else{ graphFile=matchingFiles[0]; - LOG.info("===> Use assembly graph from short-read assembler ABySS: " + graphFile); + logger.info("===> Use assembly graph from short-read assembler ABySS: {}", graphFile); } } } else{ - LOG.info("Not found any legal assembly output folder, assembly graph thus not included!"); + logger.info("Not found any legal assembly output folder, assembly graph thus not included!"); spadesFolder=null; } @@ -220,15 +220,16 @@ public boolean accept(File dir, String name) { //if(marginThres < 0) // LOG.exit("Marginal threshold must not be negative", 1); if(minContig <= 0) { - LOG.error("Minimum contig length has to be positive"); + logger.error("Minimum contig length has to be positive"); System.exit(1); }if(minSupport <= 0) { - LOG.error("Minimum supporting reads has to be positive"); + logger.error("Minimum supporting reads has to be positive"); System.exit(1); } if(maxRepeat <= 0) { - LOG.error("Maximal possible repeat length has to be positive", 1); + logger.error("Maximal possible repeat length has to be positive"); + System.exit(1); } @@ -242,7 +243,7 @@ public boolean accept(File dir, String name) { double cov = cmdLine.getDoubleVal("cov"); int qual = cmdLine.getIntVal("qual"); if(qual < 0) { - LOG.error("Phred score of quality has to be positive"); + logger.error("Phred score of quality has to be positive"); System.exit(1); } @@ -250,11 +251,11 @@ public boolean accept(File dir, String name) { time = cmdLine.getIntVal("time"); if(number <= 0) { - LOG.error("Number of reads has to be positive"); + logger.error("Number of reads has to be positive"); System.exit(1); } if(time < 0) { - LOG.error("Sleeping time must not be negative"); + logger.error("Sleeping time must not be negative"); System.exit(1); } /**********************************************************************/ diff --git a/src/main/java/org/rtassembly/concatemer/NSDFChopper.java b/src/main/java/org/rtassembly/concatemer/NSDFChopper.java index 94ed797..71a96b8 100644 --- a/src/main/java/org/rtassembly/concatemer/NSDFChopper.java +++ b/src/main/java/org/rtassembly/concatemer/NSDFChopper.java @@ -9,6 +9,7 @@ import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; +import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; @@ -16,19 +17,19 @@ import java.util.stream.DoubleStream; import org.apache.commons.math3.stat.descriptive.SummaryStatistics; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; import org.jtransforms.fft.DoubleFFT_1D; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import ch.systemsx.cisd.hdf5.HDF5Factory; import ch.systemsx.cisd.hdf5.IHDF5Reader; import japsa.seq.Alphabet; -import japsa.seq.FastqReader; import japsa.seq.Sequence; +import japsa.seq.SequenceReader; public class NSDFChopper { - private static final Logger LOG = LoggerFactory.getLogger(NSDFChopper.class); + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); String name; short[] signal; @@ -74,7 +75,7 @@ public void setData(Sequence seq) { signal[i]=2; break; default: - LOG.error("Invalid DNA character (only ACGT)!"); + logger.error("Invalid DNA character (only ACGT)!"); System.exit(1); } @@ -283,7 +284,7 @@ public void lowPassFilter(double[] signal) { /******************************************************************/ public void concatemersDetection() throws IOException { - LOG.info("===================================================================="); + logger.info("===================================================================="); /****************************************** * 1. Calculate NSDF by FFT *****************************************/ @@ -297,9 +298,9 @@ public void concatemersDetection() throws IOException { Arrays.parallelSetAll(n, i->2*r[i]/m[i]) ; // Print to file -// printArrayToFile(n, "mpm.signal.xls"); + printArrayToFile(n, name+"_mpm_signal.xls"); lowPassFilter(n); -// printArrayToFile(n, "mpm_lpf.xls"); + printArrayToFile(n, name+"_mpm_lpf.xls"); // LOG.info("Done NSDF calculation in {} secs", (System.currentTimeMillis()-curTime)/1000); // curTime=System.currentTimeMillis(); @@ -310,16 +311,15 @@ public void concatemersDetection() throws IOException { chopper=findPeaks(n); // LOG.info("Done peak picking in {} secs", (System.currentTimeMillis()-curTime)/1000); if(chopper==null||chopper.isEmpty()) - LOG.info("Processing read {} length={}: not a concatemer!", name, signal.length); + logger.info("Processing read {} length={}: not a concatemer!", name, signal.length); else{ - LOG.info("Processing read {} length={}: {}-concatemers => {}",name, signal.length,chopper.size(),getChopCoords()); + logger.info("Processing read {} length={}: {}-concatemers => {}", name, signal.length, chopper.size(), getChopCoords()); } } - @SuppressWarnings("unused") - private void printArrayToFile(double[] input, String filename){ + private void printArrayToFile(double[] input, String filename){ try { PrintWriter writer = new PrintWriter(new FileWriter(filename)); for(double value:input) @@ -463,10 +463,13 @@ public void clustering(){ } public static void main(String[] args){ +// CUTOFF_FREQ=12; +// MIN_MONOMER=30; +// THRES=.1; ERROR=.3; NSDFChopper tony = new NSDFChopper(); HashMap> histogram = new HashMap<>(); try { - FastqReader reader = new FastqReader(args[0]); + SequenceReader reader = SequenceReader.getReader(args[0]); Sequence seq=null; while((seq=reader.nextSequence(Alphabet.DNA4()))!=null){ tony.setData(seq); diff --git a/src/main/java/org/rtassembly/concatemer/ReferenceBasedChopper.java b/src/main/java/org/rtassembly/concatemer/ReferenceBasedChopper.java index 7d2d232..e5949ca 100644 --- a/src/main/java/org/rtassembly/concatemer/ReferenceBasedChopper.java +++ b/src/main/java/org/rtassembly/concatemer/ReferenceBasedChopper.java @@ -2,6 +2,7 @@ import java.io.File; import java.io.IOException; +import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -20,13 +21,13 @@ import japsa.seq.SequenceOutputStream; import japsa.seq.SequenceReader; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; import org.rtassembly.npscarf.AlignmentRecord; import org.rtassembly.npscarf.Contig; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class ReferenceBasedChopper { - private static final Logger LOG = LoggerFactory.getLogger(ReferenceBasedChopper.class); + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); HashMap refMap = new HashMap(); ReferenceBasedChopper(String refFile) throws IOException{ @@ -74,7 +75,7 @@ public void chopLongReadsBasedOnRef(String samFile, String outFolder, double qua continue; refContig=refMap.get(samRecord.getReferenceName()); if(refContig==null){ - LOG.error("Cannot find {} from reference list!", samRecord.getReferenceName()); + logger.error("Cannot find {} from reference list!", samRecord.getReferenceName()); continue; } @@ -110,12 +111,12 @@ public void chopLongReadsBasedOnRef(String samFile, String outFolder, double qua Collections.sort(counts); int cov=0; for(int c:counts) { - LOG.info("\nNumber of {}-concatemer is {}",c,count2Reads.get(c).size()); + logger.info("\nNumber of {}-concatemer is {}", c, count2Reads.get(c).size()); for(String id:count2Reads.get(c)) - LOG.info(id); + logger.info(id); cov+=c*count2Reads.get(c).size(); } - LOG.info("Total={}X\n",cov); + logger.info("Total={}X\n", cov); for(SequenceOutputStream os:outMap.values()){ os.close(); } diff --git a/src/main/java/org/rtassembly/gui/NPGraphFX.java b/src/main/java/org/rtassembly/gui/NPGraphFX.java index 03a6085..b36c52d 100644 --- a/src/main/java/org/rtassembly/gui/NPGraphFX.java +++ b/src/main/java/org/rtassembly/gui/NPGraphFX.java @@ -35,11 +35,14 @@ import java.io.File; import java.io.IOException; +import java.lang.invoke.MethodHandles; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; import org.graphstream.stream.thread.ThreadProxyPipe; import org.graphstream.ui.fx_viewer.FxDefaultView; import org.graphstream.ui.fx_viewer.FxViewer; @@ -49,8 +52,6 @@ import org.rtassembly.npgraph.Alignment; import org.rtassembly.npgraph.BDGraph; import org.rtassembly.npgraph.HybridAssembler; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import japsa.util.FxDialogs; import japsa.util.ImageButton; @@ -90,7 +91,7 @@ public class NPGraphFX extends Application{ - private static final Logger LOG = LoggerFactory.getLogger(NPGraphFX.class); + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); static HybridAssembler myass = new HybridAssembler(); // static Viewer graphViewer; @@ -159,11 +160,11 @@ public void run() { e.printStackTrace(); } } - LOG.info("GO"); + logger.info("NPGRAPH GUI is now ready to GO!"); // updateData(); try{ myass.assembly(); - myass.postProcessGraph(); +// myass.postProcessGraph(); }catch (Exception e){ System.err.println(e.getMessage()); e.printStackTrace(); @@ -245,13 +246,13 @@ private GridPane addGraphInputPane(Stage pStage, Stage gStage) { TextField shortInputTF = new TextField(""); shortInputTF.setPromptText("Enter file name for assembly graph..."); - shortInputTF.textProperty().bindBidirectional(myass.shortReadsInputProperty()); + shortInputTF.textProperty().bindBidirectional(myass.input.shortReadsInputProperty()); GridPane.setConstraints(shortInputTF, 0,1,4,1); inputPane.getChildren().add(shortInputTF); ComboBox shortInputFormatCombo=new ComboBox(); shortInputFormatCombo.getItems().addAll("fastg", "gfa"); - shortInputFormatCombo.valueProperty().bindBidirectional(myass.shortReadsInputFormatProperty()); + shortInputFormatCombo.valueProperty().bindBidirectional(myass.input.shortReadsInputFormatProperty()); GridPane.setConstraints(shortInputFormatCombo, 2, 0, 2, 1); inputPane.getChildren().add(shortInputFormatCombo); @@ -261,7 +262,7 @@ private GridPane addGraphInputPane(Stage pStage, Stage gStage) { shortInputBrowseButton.setOnAction((event) -> { FileChooser chooser = new FileChooser(); chooser.setTitle("Select assembly graph file"); - File defaultFile = new File(myass.getShortReadsInput()); + File defaultFile = new File(myass.input.getShortReadsInput()); if(defaultFile.isFile()) chooser.setInitialFileName(defaultFile.getName()); if(defaultFile.getParentFile() !=null && defaultFile.getParentFile().isDirectory()) @@ -271,8 +272,8 @@ private GridPane addGraphInputPane(Stage pStage, Stage gStage) { File selectedFile = chooser.showOpenDialog(pStage); if(selectedFile != null){ try { - myass.setShortReadsInput(selectedFile.getCanonicalPath()); - shortInputFormatCombo.setValue(myass.getShortReadsInputFormat()); + myass.input.setShortReadsInput(selectedFile.getCanonicalPath()); + shortInputFormatCombo.setValue(myass.input.getShortReadsInputFormat()); } catch (IOException e1) { FxDialogs.showWarning("Warning", "Error loading graph file. Please try again!"); e1.printStackTrace(); @@ -286,14 +287,14 @@ private GridPane addGraphInputPane(Stage pStage, Stage gStage) { //inputPane.setGridLinesVisible(true); CheckBox binCB = new CheckBox("Use external binning information"); - binCB.setSelected(!myass.getBinReadsInput().isEmpty()); - binCB.selectedProperty().addListener((obser,oldV,newV)->{if(!newV) myass.setBinReadsInput("");}); + binCB.setSelected(!myass.input.getBinReadsInput().isEmpty()); + binCB.selectedProperty().addListener((obser,oldV,newV)->{if(!newV) myass.input.setBinReadsInput("");}); GridPane.setConstraints(binCB, 0,2,4,1); inputPane.getChildren().add(binCB); TextField binInputTF = new TextField(""); binInputTF.setPromptText("Enter file name binning information..."); - binInputTF.textProperty().bindBidirectional(myass.binReadsInputProperty()); + binInputTF.textProperty().bindBidirectional(myass.input.binReadsInputProperty()); binInputTF.disableProperty().bind(binCB.selectedProperty().not()); GridPane.setConstraints(binInputTF, 0,3,4,1); inputPane.getChildren().add(binInputTF); @@ -312,7 +313,7 @@ private GridPane addGraphInputPane(Stage pStage, Stage gStage) { File selectedFile = chooser.showOpenDialog(pStage); if(selectedFile != null){ try { - myass.setBinReadsInput(selectedFile.getCanonicalPath()); + myass.input.setBinReadsInput(selectedFile.getCanonicalPath()); } catch (IOException e1) { FxDialogs.showWarning("Warning", "Error loading bin file. Please try again!"); e1.printStackTrace(); @@ -326,8 +327,8 @@ private GridPane addGraphInputPane(Stage pStage, Stage gStage) { inputPane.getChildren().add(binInputBrowseButton); CheckBox spadesCB = new CheckBox("Use SPAdes induced paths"); - spadesCB.setSelected(myass.getUseSPAdesPath()); - spadesCB.selectedProperty().bindBidirectional(myass.useSPAdesPathProperty()); + spadesCB.setSelected(myass.input.getUseSPAdesPath()); + spadesCB.selectedProperty().bindBidirectional(myass.input.useSPAdesPathProperty()); GridPane.setConstraints(spadesCB, 0,4,4,1); inputPane.getChildren().add(spadesCB); @@ -345,7 +346,7 @@ private GridPane addGraphInputPane(Stage pStage, Stage gStage) { buttonGraph.setPrefSize(100, 20); buttonGraph.setOnAction((event) -> { if(!myass.prepareShortReadsProcess()) { //true if using SPAdes path (not necessary) - FxDialogs.showWarning("Warning", myass.getCheckLog()); + FxDialogs.showError("Error", "Please check the log, fix it and try again!"); return; } if(graphCB.isSelected()) @@ -375,13 +376,13 @@ private GridPane addLongReadInputPane(Stage pStage) { TextField longInputTF = new TextField(""); longInputTF.setPromptText("Enter file name of long-reads data..."); - longInputTF.textProperty().bindBidirectional(myass.longReadsInputProperty()); + longInputTF.textProperty().bindBidirectional(myass.input.longReadsInputProperty()); GridPane.setConstraints(longInputTF, 0,1,4,1); inputPane.getChildren().add(longInputTF); longInputFormatCombo = new ComboBox<>(); - longInputFormatCombo.getItems().addAll("fasta/fastq", "sam/bam"); - longInputFormatCombo.valueProperty().bindBidirectional(myass.longReadsInputFormatProperty()); + longInputFormatCombo.getItems().addAll("fasta/fastq", "sam/bam", "paf"); + longInputFormatCombo.valueProperty().bindBidirectional(myass.input.longReadsInputFormatProperty()); GridPane.setConstraints(longInputFormatCombo, 2, 0, 2, 1); inputPane.getChildren().add(longInputFormatCombo); @@ -390,18 +391,18 @@ private GridPane addLongReadInputPane(Stage pStage) { longReadsBrowseButton.setOnAction((event) -> { FileChooser chooser = new FileChooser(); chooser.setTitle("Select long-reads data file"); - File defaultFile = new File(myass.getLongReadsInput()); + File defaultFile = new File(myass.input.getLongReadsInput()); if(defaultFile.isFile()) chooser.setInitialFileName(defaultFile.getName()); if(defaultFile.getParentFile()!=null && defaultFile.getParentFile().isDirectory()) chooser.setInitialDirectory(defaultFile.getParentFile()); chooser.setSelectedExtensionFilter( - new ExtensionFilter("Long-reads data", "*.fastq", "*.fasta", "*.fq", "*.fa", "*.fna", "*.sam", "*.bam" , - "*.FASTQ", "*.FASTA", "*.FQ", "*.FA", "*.FNA", "*.SAM", "*.BAM")); + new ExtensionFilter("Long-reads data", "*.fastq", "*.fasta", "*.fq", "*.fa", "*.fna", "*.sam", "*.bam" , "*paf" , + "*.FASTQ", "*.FASTA", "*.FQ", "*.FA", "*.FNA", "*.SAM", "*.BAM" , "*PAF")); File selectedFile = chooser.showOpenDialog(pStage); if(selectedFile != null){ try { - myass.setLongReadsInput(selectedFile.getCanonicalPath()); + myass.input.setLongReadsInput(selectedFile.getCanonicalPath()); } catch (IOException e1) { FxDialogs.showWarning("File not found!", "Please specify another long-read data file!"); e1.printStackTrace(); @@ -484,14 +485,14 @@ private GridPane addAlignmentOptionPane(Stage stage) { ComboBox algCombo=new ComboBox(); algCombo.getItems().addAll("minimap2", "bwa"); - algCombo.valueProperty().bindBidirectional(myass.alignerProperty()); + algCombo.valueProperty().bindBidirectional(myass.input.alignerProperty()); GridPane.setConstraints(algCombo, 2, 0, 2, 1); optionPane.getChildren().add(algCombo); TextField algOptTF = new TextField(""); algOptTF.setPromptText("Enter options to aligner..."); - algOptTF.textProperty().bindBidirectional(myass.alignerOptProperty()); + algOptTF.textProperty().bindBidirectional(myass.input.alignerOptProperty()); GridPane.setConstraints(algOptTF, 0,1,4,1); optionPane.getChildren().add(algOptTF); @@ -505,6 +506,7 @@ private GridPane addAlignmentOptionPane(Stage stage) { minQualTF.setText(Integer.toString(Alignment.MIN_QUAL)); minQualTF.setOnKeyPressed(e -> { if (e.getCode() == KeyCode.ENTER) { + Alignment.MIN_QUAL=Integer.parseInt(minQualTF.getText()); buttonStart.requestFocus(); } }); @@ -519,8 +521,8 @@ private GridPane addAlignmentOptionPane(Stage stage) { ComboBox consCombo=new ComboBox(); - consCombo.getItems().addAll("spoa","kalign","kalign3","poa","none"); - consCombo.valueProperty().bindBidirectional(myass.msaProperty()); + consCombo.getItems().addAll("abpoa","spoa","kalign","kalign3","poa","none"); + consCombo.valueProperty().bindBidirectional(myass.input.msaProperty()); GridPane.setConstraints(consCombo, 2, 4, 2, 1); optionPane.getChildren().add(consCombo); @@ -528,15 +530,16 @@ private GridPane addAlignmentOptionPane(Stage stage) { GridPane.setConstraints(labelMSA, 0,5,3,1); optionPane.getChildren().add(labelMSA); - TextField msaTF = new TextField(""); - msaTF.setText(Integer.toString(BDGraph.MIN_SUPPORT)); - msaTF.setOnKeyPressed(e -> { + TextField covTF = new TextField(""); + covTF.setText(Integer.toString(BDGraph.MIN_SUPPORT)); + covTF.setOnKeyPressed(e -> { if (e.getCode() == KeyCode.ENTER) { + BDGraph.MIN_SUPPORT=Integer.parseInt(covTF.getText()); buttonStart.requestFocus(); } }); - GridPane.setConstraints(msaTF, 3,5); - optionPane.getChildren().add(msaTF); + GridPane.setConstraints(covTF, 3,5); + optionPane.getChildren().add(covTF); @@ -555,12 +558,10 @@ private GridPane addControlPane() { buttonStart = new Button("Start", viewStart); buttonStart.setPrefSize(100, 20); buttonStart.setOnAction((event) -> { - String message=myass.getCheckLog(); if(!myass.prepareLongReadsProcess()){ - FxDialogs.showError("Error", myass.getCheckLog()); + FxDialogs.showError("Error", "Please check the log, fix it and try again!"); return; - }else if(message!=null && !message.isEmpty()) - FxDialogs.showWarning("Warning", myass.getCheckLog()); + } myass.setReady(true); @@ -753,12 +754,13 @@ private class AddToQueue implements Runnable { public void run() { try { // add a item of data to queue - dataN50.add(myass.observer.getN50()/1000); //because unit is Kbp - dataN75.add(myass.observer.getN75()/1000); - dataMax.add(myass.observer.getLongestContig()/1000); - dataNumCtgs.add(myass.observer.getNumberOfSequences()); - dataNumCircularCtgs.add(myass.observer.getNumberOfCircularSequences()); - + if(myass.observer.getN50()!=0) { + dataN50.add(myass.observer.getN50()/1000); //because unit is Kbp + dataN75.add(myass.observer.getN75()/1000); //because unit is Kbp + dataMax.add(myass.observer.getLongestContig()/1000); //because unit is Kbp + dataNumCtgs.add(myass.observer.getNumberOfSequences()); + dataNumCircularCtgs.add(myass.observer.getNumberOfCircularSequences()); + } Thread.sleep(500); if(executor!=null && !executor.isShutdown()) executor.execute(this); diff --git a/src/main/java/org/rtassembly/gui/NPGraphServerFX.java b/src/main/java/org/rtassembly/gui/NPGraphServerFX.java new file mode 100644 index 0000000..e87be71 --- /dev/null +++ b/src/main/java/org/rtassembly/gui/NPGraphServerFX.java @@ -0,0 +1,756 @@ +/***************************************************************************** + * Copyright (c) Son Hoang Nguyen, IMB - UQ, All rights reserved. * + * * + * Redistribution and use in source and binary forms, with or without * + * modification, are permitted provided that the following conditions * + * are met: * + * * + * 1. Redistributions of source code must retain the above copyright notice, * + * this list of conditions and the following disclaimer. * + * 2. Redistributions in binary form must reproduce the above copyright * + * notice, this list of conditions and the following disclaimer in the * + * documentation and/or other materials provided with the distribution. * + * 3. Neither the names of the institutions nor the names of the contributors* + * may be used to endorse or promote products derived from this software * + * without specific prior written permission. * + * * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * + ****************************************************************************/ + +/************************** REVISION HISTORY ************************** + * 03/25/2018 - Son Hoang Nguyen: Created + * + ****************************************************************************/ +package org.rtassembly.gui; + +import java.io.File; +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutorService; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.graphstream.stream.thread.ThreadProxyPipe; +import org.graphstream.ui.fx_viewer.FxDefaultView; +import org.graphstream.ui.fx_viewer.FxViewer; +import org.graphstream.ui.javafx.FxGraphRenderer; +import org.graphstream.ui.view.Viewer; +import org.graphstream.ui.view.Viewer.CloseFramePolicy; +import org.rtassembly.npgraph.Alignment; +import org.rtassembly.npgraph.BDGraph; +import org.rtassembly.npgraph.HybridAssembler; +import org.rtassembly.npgraph.grpc.AssemblyGuideServer; + +import japsa.util.FxDialogs; +import japsa.util.ImageButton; +import javafx.application.Application; +import javafx.application.Platform; +import javafx.geometry.HPos; +import javafx.geometry.Insets; +import javafx.scene.Scene; +import javafx.scene.control.Button; +import javafx.scene.control.CheckBox; +import javafx.scene.control.ComboBox; +import javafx.scene.control.Label; +import javafx.scene.control.Separator; +import javafx.scene.control.TextField; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.input.KeyCode; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.ColumnConstraints; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.RowConstraints; +import javafx.scene.layout.VBox; +import javafx.scene.text.Font; +import javafx.scene.text.FontWeight; +import javafx.scene.text.Text; + +import javafx.scene.chart.XYChart; +import javafx.animation.AnimationTimer; +import javafx.scene.chart.AreaChart; +import javafx.scene.chart.NumberAxis; +import javafx.geometry.Side; + +import javafx.stage.DirectoryChooser; +import javafx.stage.FileChooser; +import javafx.stage.FileChooser.ExtensionFilter; +import javafx.stage.Stage; + + +public class NPGraphServerFX extends Application{ + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); + + static HybridAssembler myAss=null; + static int myPort; + + public static void configServer(HybridAssembler ass, int port){ + myAss=ass; + myPort=port; + } + + public void start(Stage primaryStage){ + Stage graphStage = new Stage(); + + try { + /* + * Setup graph stage here + */ + BorderPane gBorder = new BorderPane(); + gBorder.setCenter(addGraphResolverPane()); + Scene gscene = new Scene(gBorder); + graphStage.setScene(gscene); + graphStage.setTitle("Assembly graph"); +// graphStage.show(); + + + /* + * Setup primary stage here + */ + //Start with a BorderPane as root + BorderPane pBorder = new BorderPane(); + // Put start/stop button here + // All the parameters setting to the left + graphInputPane = addGraphInputPane(primaryStage, graphStage); + outputPane = addOutputPane(primaryStage); + serverOptPane = addServerOptionPane(primaryStage); + controlPane = addControlPane(); + + pBorder.setLeft(addVBox()); + + // Here the main content + pBorder.setCenter(addAssemblyStatsBox()); + + Scene pscene = new Scene(pBorder); +// pscene.getStylesheets().add("/main.css"); + primaryStage.setScene(pscene); + primaryStage.setTitle("npGraph Dashboard"); + primaryStage.setOnCloseRequest(e -> { + myAss.terminateAlignmentProcess(); + Platform.exit(); + System.exit(0); + }); + primaryStage.show(); + + + /* + * A thread to run the algorithm + */ + new Thread(new Runnable(){ + + @Override + public void run() { + while (!myAss.getReady()){ + //LOG.info("NOT READY"); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } + logger.info("npGraph server is now ready to GO!"); + try{ + AssemblyGuideServer assServer = new AssemblyGuideServer(myPort, myAss); + assServer.start(); + }catch (Exception e){ + System.err.println(e.getMessage()); + e.printStackTrace(); + } + + } + + }).start(); + + } catch (Exception exc) { + exc.printStackTrace(); + } + + } + + /* + * Components from left pane + */ + private Button buttonStart, buttonStop, buttonGraph; + private GridPane graphInputPane, outputPane, serverOptPane, controlPane; + + + private final int LeftPaneWidth=360; + + /* + * Creates a VBox with a list of parameter settings + */ + private VBox addVBox() { + + VBox vbox = new VBox(); + vbox.setPadding(new Insets(10)); // Set all sides to 10 + vbox.setSpacing(8); // Gap between nodes + + final Text title = new Text("Settings"); + title.setFont(Font.font("Arial", FontWeight.BOLD, 15)); + vbox.getChildren().add(title); + final Separator sep1 = new Separator(); + sep1.setMaxWidth(LeftPaneWidth); + vbox.getChildren().add(1, sep1); + + vbox.getChildren().add(graphInputPane); + vbox.setSpacing(5); + final Separator sep2 = new Separator(); + sep2.setMaxWidth(LeftPaneWidth); + vbox.getChildren().add(3, sep2); + + + vbox.getChildren().add(outputPane); + vbox.setSpacing(5); + final Separator sep4 = new Separator(); + sep4.setMaxWidth(LeftPaneWidth); + vbox.getChildren().add(5, sep4); + + vbox.getChildren().add(serverOptPane); + vbox.setSpacing(5); + final Separator sep5 = new Separator(); + sep4.setMaxWidth(LeftPaneWidth); + vbox.getChildren().add(7, sep5); + + vbox.getChildren().add(controlPane); + return vbox; + } + //TODO: put load graph button here to link with gStage.show() + private GridPane addGraphInputPane(Stage pStage, Stage gStage) { + GridPane inputPane = createFixGridPane(LeftPaneWidth, 5); + + final Label inputLabel = new Label("Assembly graph:"); + inputLabel.setFont(Font.font("Roman", FontWeight.BOLD, 12)); + inputLabel.setStyle("-fx-underline:true"); + GridPane.setConstraints(inputLabel, 0,0,2,1); + inputPane.getChildren().add(inputLabel); + + TextField shortInputTF = new TextField(""); + shortInputTF.setPromptText("Enter file name for assembly graph..."); + shortInputTF.textProperty().bindBidirectional(myAss.input.shortReadsInputProperty()); + GridPane.setConstraints(shortInputTF, 0,1,4,1); + inputPane.getChildren().add(shortInputTF); + + ComboBox shortInputFormatCombo=new ComboBox(); + shortInputFormatCombo.getItems().addAll("fastg", "gfa"); + shortInputFormatCombo.valueProperty().bindBidirectional(myAss.input.shortReadsInputFormatProperty()); + + GridPane.setConstraints(shortInputFormatCombo, 2, 0, 2, 1); + inputPane.getChildren().add(shortInputFormatCombo); + + Button shortInputBrowseButton = new ImageButton("/folder.png"); + shortInputBrowseButton.setPrefSize(10, 10); + shortInputBrowseButton.setOnAction((event) -> { + FileChooser chooser = new FileChooser(); + chooser.setTitle("Select assembly graph file"); + File defaultFile = new File(myAss.input.getShortReadsInput()); + if(defaultFile.isFile()) + chooser.setInitialFileName(defaultFile.getName()); + if(defaultFile.getParentFile() !=null && defaultFile.getParentFile().isDirectory()) + chooser.setInitialDirectory(defaultFile.getParentFile()); + chooser.setSelectedExtensionFilter( + new ExtensionFilter("Assembly graph", "*.fastg", "*.FASTG", "*.gfa", "*.GFA")); + File selectedFile = chooser.showOpenDialog(pStage); + if(selectedFile != null){ + try { + myAss.input.setShortReadsInput(selectedFile.getCanonicalPath()); + shortInputFormatCombo.setValue(myAss.input.getShortReadsInputFormat()); + } catch (IOException e1) { + FxDialogs.showWarning("Warning", "Error loading graph file. Please try again!"); + e1.printStackTrace(); + } + + } + }); + GridPane.setConstraints(shortInputBrowseButton, 4,1); + GridPane.setHalignment(shortInputBrowseButton,HPos.LEFT); + inputPane.getChildren().add(shortInputBrowseButton); + //inputPane.setGridLinesVisible(true); + + CheckBox binCB = new CheckBox("Use external binning information"); + binCB.setSelected(!myAss.input.getBinReadsInput().isEmpty()); + binCB.selectedProperty().addListener((obser,oldV,newV)->{if(!newV) myAss.input.setBinReadsInput("");}); + GridPane.setConstraints(binCB, 0,2,4,1); + inputPane.getChildren().add(binCB); + + TextField binInputTF = new TextField(""); + binInputTF.setPromptText("Enter file name binning information..."); + binInputTF.textProperty().bindBidirectional(myAss.input.binReadsInputProperty()); + binInputTF.disableProperty().bind(binCB.selectedProperty().not()); + GridPane.setConstraints(binInputTF, 0,3,4,1); + inputPane.getChildren().add(binInputTF); + + + Button binInputBrowseButton = new ImageButton("/folder.png"); + binInputBrowseButton.setPrefSize(10, 10); + binInputBrowseButton.setOnAction((event) -> { + FileChooser chooser = new FileChooser(); + chooser.setTitle("Select metaBAT bin file"); + File defaultFile = new File(binInputTF.getText()); + if(defaultFile.isFile()) + chooser.setInitialFileName(defaultFile.getName()); + if(defaultFile.getParentFile() !=null && defaultFile.getParentFile().isDirectory()) + chooser.setInitialDirectory(defaultFile.getParentFile()); + File selectedFile = chooser.showOpenDialog(pStage); + if(selectedFile != null){ + try { + myAss.input.setBinReadsInput(selectedFile.getCanonicalPath()); + } catch (IOException e1) { + FxDialogs.showWarning("Warning", "Error loading bin file. Please try again!"); + e1.printStackTrace(); + } + + } + }); + binInputBrowseButton.disableProperty().bind(binCB.selectedProperty().not()); + GridPane.setConstraints(binInputBrowseButton, 4,3); + GridPane.setHalignment(binInputBrowseButton,HPos.LEFT); + inputPane.getChildren().add(binInputBrowseButton); + + CheckBox spadesCB = new CheckBox("Use SPAdes induced paths"); + spadesCB.setSelected(myAss.input.getUseSPAdesPath()); + spadesCB.selectedProperty().bindBidirectional(myAss.input.useSPAdesPathProperty()); + GridPane.setConstraints(spadesCB, 0,4,4,1); + inputPane.getChildren().add(spadesCB); + + + CheckBox graphCB = new CheckBox("Show graph"); + graphCB.setSelected(myAss.simGraph!=null); + GridPane.setConstraints(graphCB, 3,5,2,1); + inputPane.getChildren().add(graphCB); + + Image imageLoad = new Image(getClass().getResourceAsStream("/load.png")); + ImageView viewLoad = new ImageView(imageLoad); + viewLoad.setFitWidth(20); + viewLoad.setFitHeight(20); + buttonGraph = new Button("Load", viewLoad); + buttonGraph.setPrefSize(100, 20); + buttonGraph.setOnAction((event) -> { + if(!myAss.prepareShortReadsProcess()) { //true if using SPAdes path (not necessary) + FxDialogs.showError("Error", "Please check the log, fix it and try again!"); + return; + } + if(graphCB.isSelected()) + gStage.show(); + //TODO: unhide further controls + outputPane.setDisable(false); + controlPane.setDisable(false); + graphInputPane.setDisable(true); + updateData(); + + }); + GridPane.setConstraints(buttonGraph, 0,5,3,1); + inputPane.getChildren().add(buttonGraph); + + return inputPane; + } + + + private GridPane addOutputPane(Stage stage) { + GridPane outputPane = createFixGridPane(LeftPaneWidth, 5); + + final Label outputLabel = new Label("Output:"); + outputLabel.setFont(Font.font("Roman", FontWeight.BOLD, 12)); + outputLabel.setStyle("-fx-underline:true"); + GridPane.setConstraints(outputLabel, 0,0); + outputPane.getChildren().add(outputLabel); + + TextField outputTF = new TextField(""); + outputTF.setPromptText("Enter name for output file..."); + outputTF.textProperty().bindBidirectional(myAss.prefixProperty()); + GridPane.setConstraints(outputTF, 0,1,4,1); + outputPane.getChildren().add(outputTF); + + + Button outputBrowseButton = new ImageButton("/folder.png"); + outputBrowseButton.setPrefSize(10, 10); +// outputBrowseButton.setDisable(assembler.output.equals("-")); + outputBrowseButton.setOnAction((event) -> { + DirectoryChooser chooser = new DirectoryChooser(); + chooser.setTitle("Save output to a destination folder"); + File defaultDirectory=new File(outputTF.getText()); + if(defaultDirectory.isDirectory()) + chooser.setInitialDirectory(defaultDirectory); + File selectedDirectory=chooser.showDialog(stage); + if(selectedDirectory != null) { + myAss.setPrefix(selectedDirectory.getPath()); + outputTF.setText(myAss.getPrefix()); + try{ + System.setProperty("usr.dir", myAss.getPrefix()); + } + catch(NullPointerException | IllegalArgumentException | SecurityException exception ){ + exception.printStackTrace(); + FxDialogs.showWarning("Illegal output folder!", "Please specify another output destination"); + return; + } + } + + }); + GridPane.setConstraints(outputBrowseButton, 4,1); + GridPane.setHalignment(outputBrowseButton, HPos.LEFT); + outputPane.getChildren().add(outputBrowseButton); + + CheckBox overwriteCB = new CheckBox("Overwrite existing index files"); + overwriteCB.selectedProperty().bindBidirectional(myAss.overwriteProperty()); + GridPane.setConstraints(overwriteCB, 0, 2, 4, 1); + outputPane.getChildren().add(overwriteCB); + + outputPane.setDisable(true); + return outputPane; + } + private GridPane addServerOptionPane(Stage stage) { + GridPane optionPane = createFixGridPane(LeftPaneWidth, 5); + + final Label labelPort = new Label("Listening on port "); + GridPane.setConstraints(labelPort, 0,0,3,1); + optionPane.getChildren().add(labelPort); + + TextField portTF = new TextField(""); + portTF.setText(Integer.toString(myPort)); + portTF.setOnKeyPressed(e -> { + if (e.getCode() == KeyCode.ENTER) { + myPort=Integer.parseInt(portTF.getText()); + buttonStart.requestFocus(); + } + }); + GridPane.setConstraints(portTF, 3,0); + optionPane.getChildren().add(portTF); + + final Label labelQual = new Label("Mininum alignment quality score "); + GridPane.setConstraints(labelQual, 0,1,3,1); + optionPane.getChildren().add(labelQual); + + TextField minQualTF = new TextField(""); +// minQualTF.setPromptText("min."); + minQualTF.setText(Integer.toString(Alignment.MIN_QUAL)); + minQualTF.setOnKeyPressed(e -> { + if (e.getCode() == KeyCode.ENTER) { + Alignment.MIN_QUAL=Integer.parseInt(minQualTF.getText()); + buttonStart.requestFocus(); + } + }); + GridPane.setConstraints(minQualTF, 3,1); + optionPane.getChildren().add(minQualTF); + + + final Label labelCov = new Label("Minium coverage for bridging "); + GridPane.setConstraints(labelCov, 0,2,3,1); + optionPane.getChildren().add(labelCov); + + TextField covTF = new TextField(""); + covTF.setText(Integer.toString(BDGraph.MIN_SUPPORT)); + covTF.setOnKeyPressed(e -> { + if (e.getCode() == KeyCode.ENTER) { + BDGraph.MIN_SUPPORT=Integer.parseInt(covTF.getText()); + buttonStart.requestFocus(); + } + }); + GridPane.setConstraints(covTF, 3,2); + optionPane.getChildren().add(covTF); + + return optionPane; + } + + private GridPane addControlPane() { + GridPane controlPane = createFixGridPane(LeftPaneWidth, 5); + + Image imageStart = new Image(getClass().getResourceAsStream("/start.png")); + ImageView viewStart = new ImageView(imageStart); + viewStart.setFitWidth(20); + viewStart.setFitHeight(20); + buttonStart = new Button("Start", viewStart); + buttonStart.setPrefSize(100, 20); + buttonStart.setOnAction((event) -> { + if(!myAss.prepareLongReadsProcess()){ + FxDialogs.showError("Error", "Please check the log for more detail!"); + return; + } + + myAss.setReady(true); + + //Start running + outputPane.setDisable(true); + serverOptPane.setDisable(true); + + buttonStart.setDisable(true); + buttonStop.setDisable(false); + + }); + GridPane.setConstraints(buttonStart, 0,0,2,1); + controlPane.getChildren().add(buttonStart); + + Image imageStop = new Image(getClass().getResourceAsStream("/stop.png")); + ImageView viewStop = new ImageView(imageStop); + viewStop.setFitWidth(20); + viewStop.setFitHeight(20); + buttonStop = new Button("Stop", viewStop); + buttonStop.setPrefSize(100, 20); + buttonStop.setDisable(true); + buttonStop.setOnAction((event) -> { + String confirm = FxDialogs.showConfirm( "STOP ASSEMBLY PROCESS", "npGraph will stop using incoming long reads for further assembly. Do you want to proceed?", "No", "Yes"); + if(confirm.equals("No")){ + return; + } + myAss.setStopSignal(true); + if(!executor.isTerminated()) + executor.shutdown(); + buttonStop.setDisable(true); + + + }); + GridPane.setConstraints(buttonStop, 3,0,2,1); + controlPane.getChildren().add(buttonStop); + + controlPane.setDisable(true); + return controlPane; + } + + private GridPane createFixGridPane(int width, int ncols){ + GridPane gridpane = new GridPane(); + for (int i = 0; i < ncols; i++) { + ColumnConstraints column = new ColumnConstraints(1.0*width/ncols); + gridpane.getColumnConstraints().add(column); + } + gridpane.setPadding(new Insets(10, 10, 10, 10)); + gridpane.setVgap(5); + gridpane.setHgap(5); + return gridpane; + } + private GridPane createAutoresizeGridPane(int ncols, int nrows){ + GridPane gridpane = new GridPane(); + for (int i = 0; i < ncols; i++) { + ColumnConstraints column = new ColumnConstraints(); + column.setPercentWidth(100/ncols); + gridpane.getColumnConstraints().add(column); + } + for (int i = 0; i < nrows; i++) { + RowConstraints row = new RowConstraints(); + row.setPercentHeight(100/nrows); + gridpane.getRowConstraints().add(row); + } + gridpane.setPadding(new Insets(5, 5, 5, 5)); + gridpane.setVgap(5); + gridpane.setHgap(5); + return gridpane; + } + + + /************************************************************************ + ********************** This is for the stats panel ********************* + ************************************************************************/ + private static final int MAX_DATA_POINTS = 500; + + private int xSeriesData = 0; + private XYChart.Series seriesN50 = new XYChart.Series<>(); + private XYChart.Series seriesN75 = new XYChart.Series<>(); + private XYChart.Series seriesMax = new XYChart.Series<>(); + private XYChart.Series seriesNumCtgs = new XYChart.Series<>(); + private XYChart.Series seriesNumCircularCtgs = new XYChart.Series<>(); + private ExecutorService executor; + private ConcurrentLinkedQueue dataN50 = new ConcurrentLinkedQueue<>(); + private ConcurrentLinkedQueue dataN75 = new ConcurrentLinkedQueue<>(); + private ConcurrentLinkedQueue dataMax = new ConcurrentLinkedQueue<>(); + private ConcurrentLinkedQueue dataNumCtgs = new ConcurrentLinkedQueue<>(); + private ConcurrentLinkedQueue dataNumCircularCtgs = new ConcurrentLinkedQueue<>(); + + private NumberAxis xAxis; + + @SuppressWarnings("unchecked") + private VBox addAssemblyStatsBox(){ + VBox vbox = new VBox(); + vbox.setPadding(new Insets(20)); // Set all sides to 10 + vbox.setSpacing(8); // Gap between nodes + + xAxis = new NumberAxis(0, MAX_DATA_POINTS, MAX_DATA_POINTS / 10); + xAxis.setForceZeroInRange(false); + xAxis.setAutoRanging(false); + xAxis.setTickLabelsVisible(false); + xAxis.setTickMarkVisible(false); + xAxis.setMinorTickVisible(false); + + /* + * Length stats chart + */ + NumberAxis yAxis = new NumberAxis(); + yAxis.setTickLabelRotation(270); + yAxis.setLabel("length (Kbp)"); + // Create a LineChart + final AreaChart lengthChart = new AreaChart(xAxis, yAxis) { + // Override to remove symbols on each data point + @Override + protected void dataItemAdded(Series series, int itemIndex, Data item) { + } + }; + + lengthChart.setAnimated(false); + lengthChart.setHorizontalGridLinesVisible(true); + lengthChart.legendSideProperty().set(Side.TOP); + + // Set Name for Series + seriesN50.setName("N50"); + seriesN75.setName("N75"); + seriesMax.setName("Max length"); + + // Add Chart Series + lengthChart.getData().addAll(seriesN50, seriesN75, seriesMax); + + vbox.getChildren().add(lengthChart); + + /* + * Number of contigs chart + */ + yAxis = new NumberAxis(); + yAxis.setTickLabelRotation(270); + yAxis.setLabel("number of contigs"); + final AreaChart numChart = new AreaChart(xAxis, yAxis) { + // Override to remove symbols on each data point + @Override + protected void dataItemAdded(Series series, int itemIndex, Data item) { + } + }; + + numChart.setAnimated(false); + numChart.setHorizontalGridLinesVisible(true); + + seriesNumCtgs.setName("All contigs"); + seriesNumCircularCtgs.setName("Circular contigs"); + + numChart.getData().addAll(seriesNumCtgs, seriesNumCircularCtgs); + + vbox.getChildren().add(numChart); + + lengthChart.getStylesheets().add("/chart1.css"); + numChart.getStylesheets().add("/chart2.css"); + + return vbox; + } + + /****************************************************************************************** + * ************ Create a Grid Pane for assembly graph ************************************* + ******************************************************************************************/ + private GridPane addGraphResolverPane(){ + GridPane mainGrid = createAutoresizeGridPane(1,1); + mainGrid.setStyle("-fx-background-color: #C0C0C0;"); + + + ThreadProxyPipe pipe = new ThreadProxyPipe() ; + pipe.init(myAss.simGraph); + Viewer graphViewer = new FxViewer(pipe); + System.setProperty("org.graphstream.ui", "javafx"); + + FxDefaultView view = new FxDefaultView(graphViewer, "npGraph", new FxGraphRenderer()); + graphViewer.addView(view); + graphViewer.enableAutoLayout(); + graphViewer.setCloseFramePolicy(CloseFramePolicy.CLOSE_VIEWER); + + mainGrid.getChildren().add(view); + + return mainGrid; + } + + /****************************************************************************************** + * ** Here are variables and controls for all plots *************************************** + ******************************************************************************************/ + //private static boolean stillRun = true; + private class AddToQueue implements Runnable { + public void run() { + try { + // add a item of data to queue + if(myAss.observer.getN50()!=0) { + dataN50.add(myAss.observer.getN50()/1000); //because unit is Kbp + dataN75.add(myAss.observer.getN75()/1000); //because unit is Kbp + dataMax.add(myAss.observer.getLongestContig()/1000); //because unit is Kbp + dataNumCtgs.add(myAss.observer.getNumberOfSequences()); + dataNumCircularCtgs.add(myAss.observer.getNumberOfCircularSequences()); + } + Thread.sleep(500); + if(executor!=null && !executor.isShutdown()) + executor.execute(this); + } catch (InterruptedException ex) { + ex.printStackTrace(); + } + } + } + + //-- Timeline gets called in the JavaFX Main thread + private void prepareTimeline() { + // Every frame to take any data from queue and add to chart + new AnimationTimer() { + @Override + public void handle(long now) { + addDataToSeries(); + } + }.start(); + } + + private void addDataToSeries() { + for (int i = 0; i < 20; i++) { //-- add 20 numbers to the plot+ + if (dataN50.isEmpty() || dataN75.isEmpty() || dataMax.isEmpty() + || dataNumCtgs.isEmpty() || dataNumCircularCtgs.isEmpty()) + break; + seriesN50.getData().add(new XYChart.Data<>(xSeriesData, dataN50.remove())); + seriesN75.getData().add(new XYChart.Data<>(xSeriesData, dataN75.remove())); + seriesMax.getData().add(new XYChart.Data<>(xSeriesData, dataMax.remove())); + seriesNumCtgs.getData().add(new XYChart.Data<>(xSeriesData, dataNumCtgs.remove())); + seriesNumCircularCtgs.getData().add(new XYChart.Data<>(xSeriesData++, dataNumCircularCtgs.remove())); + } + // remove points to keep us at no more than MAX_DATA_POINTS + if (seriesN50.getData().size() > MAX_DATA_POINTS) { + seriesN50.getData().remove(0, seriesN50.getData().size() - MAX_DATA_POINTS); + } + if (seriesN75.getData().size() > MAX_DATA_POINTS) { + seriesN75.getData().remove(0, seriesN75.getData().size() - MAX_DATA_POINTS); + } + if (seriesMax.getData().size() > MAX_DATA_POINTS) { + seriesMax.getData().remove(0, seriesMax.getData().size() - MAX_DATA_POINTS); + } + if (seriesNumCtgs.getData().size() > MAX_DATA_POINTS) { + seriesNumCtgs.getData().remove(0, seriesNumCtgs.getData().size() - MAX_DATA_POINTS); + } + if (seriesNumCircularCtgs.getData().size() > MAX_DATA_POINTS) { + seriesNumCircularCtgs.getData().remove(0, seriesNumCircularCtgs.getData().size() - MAX_DATA_POINTS); + } + // update + xAxis.setLowerBound(xSeriesData - MAX_DATA_POINTS); + xAxis.setUpperBound(xSeriesData - 1); + } + + + private void updateData(){ + executor = Executors.newCachedThreadPool(new ThreadFactory() { + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r); + thread.setDaemon(true); + return thread; + } + }); + + AddToQueue addToQueue = new AddToQueue(); + executor.execute(addToQueue); + //-- Prepare Timeline + prepareTimeline(); + } + + + public static void main(String[] args) { + HybridAssembler hbAss = new HybridAssembler(); + hbAss.input.setShortReadsInput("/home/sonnguyen/Projects/npGraph/test/spades/assembly_graph.fastg"); + NPGraphServerFX.configServer(hbAss, 2105); + Application.launch(NPGraphServerFX.class,args); + } + +} + diff --git a/src/main/java/org/rtassembly/npgraph/AlignedRead.java b/src/main/java/org/rtassembly/npgraph/AlignedRead.java index d6abb64..47b0dc4 100644 --- a/src/main/java/org/rtassembly/npgraph/AlignedRead.java +++ b/src/main/java/org/rtassembly/npgraph/AlignedRead.java @@ -38,15 +38,16 @@ import japsa.seq.Alphabet; import japsa.seq.Sequence; import java.io.File; +import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.Collections; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; public class AlignedRead{ - private static final Logger LOG = LoggerFactory.getLogger(AlignedRead.class); + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); public static int PSEUDO_ID=1; public static String tmpFolder=System.getProperty("usr.dir")+File.separator+"npGraph_tmp"; //folder to save spanning reads of the bridge diff --git a/src/main/java/org/rtassembly/npgraph/Alignment.java b/src/main/java/org/rtassembly/npgraph/Alignment.java index 410c5d5..7c7d7e4 100644 --- a/src/main/java/org/rtassembly/npgraph/Alignment.java +++ b/src/main/java/org/rtassembly/npgraph/Alignment.java @@ -34,11 +34,10 @@ ****************************************************************************/ package org.rtassembly.npgraph; -import java.util.ArrayList; - import htsjdk.samtools.Cigar; import htsjdk.samtools.CigarElement; import htsjdk.samtools.SAMRecord; +import japsa.seq.PAFRecord; import japsa.seq.Sequence; /* @@ -51,10 +50,10 @@ public class Alignment implements Comparable { public static int MIN_QUAL=10; - int quality; + public int quality; public String readID; - BDNode node; + public BDNode node; //keep an instance of contig reference here in case it's removed from BDGraph public int refStart, refEnd; //1-based position on ref of the start and end of the alignment @@ -74,11 +73,54 @@ public class Alignment implements Comparable { public boolean goodMargin = false; public boolean useful = false; - ArrayList alignmentCigars = new ArrayList(); + Cigar cigar=null; - public Alignment(){} + public Alignment(BDNode node){ this.node=node;} + public Alignment(PAFRecord paf, BDNode node) { + this(node); + readID=paf.qname; + quality=paf.qual; + score=paf.score; + readLength=paf.qlen; + refStart=paf.tstart; refEnd=paf.tend; + strand=paf.strand; + cigar=paf.getCigar(); + + //these temporary variable to determine usefulness + int readLeft, readRight; + if (strand){ + readStart=paf.qstart; + readEnd=paf.qend; + readLeft = readStart - 1; + readRight = readLength - readEnd; + }else{ + readStart =paf.qend; + readEnd=paf.qstart; + readLeft = readLength - readStart; + readRight = readEnd - 1; + } + int refLeft = refStart - 1, + refRight = ((Sequence) node.getAttribute("seq")).length() - refEnd; + + int overhangTolerance = (int) Math.min(BDGraph.A_TOL, BDGraph.R_TOL*node.getNumber("len")); + if ( + (readLeft < overhangTolerance || refLeft < overhangTolerance) + && (readRight < overhangTolerance || refRight < overhangTolerance) + && Math.min(refLeft,refRight) < overhangTolerance + ) + goodMargin=true; + + if ( goodMargin + && prime //TODO: there are useful secondary alignment!!! +// && alignLength > BDGraph.getKmerSize() //FIXME: + && quality >= MIN_QUAL + ) + useful = true; + } + public Alignment(SAMRecord sam, BDNode node) { + this(node); // readID = Integer.parseInt(sam.getReadName().split("_")[0]); readID = sam.getReadName(); quality = sam.getMappingQuality(); @@ -88,12 +130,11 @@ public Alignment(SAMRecord sam, BDNode node) { refStart = sam.getAlignmentStart(); refEnd = sam.getAlignmentEnd(); - Cigar cigar = sam.getCigar(); + cigar = sam.getCigar(); boolean enterAlignment = false; ////////////////////////////////////////////////////////////////////////////////// - + for (final CigarElement e : cigar.getCigarElements()) { - alignmentCigars.add(e); final int length = e.getLength(); switch (e.getOperator()) { case H : @@ -148,8 +189,9 @@ public Alignment(SAMRecord sam, BDNode node) { int overhangTolerance = (int) Math.min(BDGraph.A_TOL, BDGraph.R_TOL*node.getNumber("len")); if ( - (readLeft < overhangTolerance || refLeft < overhangTolerance) && - (readRight < overhangTolerance || refRight < overhangTolerance) + (readLeft < overhangTolerance || refLeft < overhangTolerance) + && (readRight < overhangTolerance || refRight < overhangTolerance) + && Math.min(refLeft,refRight) < overhangTolerance ) goodMargin=true; @@ -173,7 +215,7 @@ public int readAlignmentEnd(){ } public Alignment reverseRead(){ - Alignment revAlign = new Alignment(); + Alignment revAlign = new Alignment(node); revAlign.readID = readID; revAlign.refStart = refStart; revAlign.refEnd = refEnd; @@ -183,9 +225,8 @@ public Alignment reverseRead(){ revAlign.readEnd = readLength - readEnd + 1;//1-index revAlign.strand = !strand; revAlign.useful = useful; - revAlign.node = node; revAlign.score = score; - revAlign.alignmentCigars = alignmentCigars;//https://www.biostars.org/p/289583/ + revAlign.cigar = cigar;//https://www.biostars.org/p/289583/ revAlign.quality = quality; return revAlign; @@ -207,65 +248,82 @@ public String toString() { } /** - * Re-implement what already in htsjdk (2.10.1) SAMRecord because we exclude SAMRecord from Alignment - * Return the position on the reference that corresponds to a given position - * on read. + * Re-implement what already in htsjdk (2.10.1) SAMRecord because we remove SAMRecord from Alignment + * Return the position on the reference that corresponds to a given position on the read. * * @param posInRead * @param record * @return */ - public int getReferencePositionAtReadPosition(int readLookingPositon){ - if (readLookingPositon < readAlignmentStart() || readLookingPositon > readAlignmentEnd()) - return 1; - - if (!strand) - readLookingPositon = readLength - readLookingPositon + 1; // use direction of ref (forward) - + public int getReferencePositionAtReadPosition(int readLookingPosition){ + int location=-1; + +// if (readLookingPosition < readAlignmentStart() || readLookingPosition > readAlignmentEnd()) +// return 1; - int posOnRead = strand?readStart:(readLength + 1 - readStart); + int posOnRead = readStart, endOfRead=readEnd; int posOnRef = refStart; + if (!strand) { + readLookingPosition = readLength + 1 - readLookingPosition; // use direction of ref (forward) + posOnRead = readLength + 1 - posOnRead; + endOfRead = readLength + 1 - endOfRead; + } - if(alignmentCigars.isEmpty()){ //perfect alignment made by overlapped EDGES (when using assembly graph) - return posOnRef + readLookingPositon - posOnRead; - - }else{ - for (final CigarElement e : alignmentCigars) { - final int length = e.getLength(); - switch (e.getOperator()) { - case H : - case S : - case P : - break; // ignore pads and clips - case I : - //insert - if (posOnRead + length < readLookingPositon){ - posOnRead += length; - }else{ - return posOnRef; - } - break; - case M ://match or mismatch - case EQ://match - case X ://mismatch - if (posOnRead + length < readLookingPositon){ - posOnRead += length; + if( readLookingPosition < posOnRead) { + location = refStart + readLookingPosition - posOnRead; + }else if(readLookingPosition > endOfRead) { + location = refEnd + readLookingPosition - posOnRead; + }else { + if(cigar==null){ //approximate + location = (int) (posOnRef + (readLookingPosition - posOnRead)*Math.abs((refEnd-refStart)*1.0/(readEnd-readStart))); + }else{ + boolean found=false; + for (final CigarElement e : cigar.getCigarElements()) { + if(found) + break; + final int length = e.getLength(); + switch (e.getOperator()) { + case H : + case S : + case P : + break; // ignore pads and clips + case I : + //insert + if (posOnRead + length < readLookingPosition){ + posOnRead += length; + }else{ + location = posOnRef; + found=true; + } + break; + case M ://match or mismatch + case EQ://match + case X ://mismatch + if (posOnRead + length < readLookingPosition){ + posOnRead += length; + posOnRef += length; + }else{ + location = posOnRef + readLookingPosition - posOnRead; + found=true; + } + break; + case D : + posOnRef += length; + break; + case N : posOnRef += length; - }else{ - return posOnRef + readLookingPositon - posOnRead; - } - break; - case D : - posOnRef += length; - break; - case N : - posOnRef += length; - break; - default : throw new IllegalStateException("Case statement didn't deal with cigar op: " + e.getOperator()); - }//casse - }//for + break; + default : throw new IllegalStateException("Case statement didn't deal with cigar op: " + e.getOperator()); + }//casse + }//for + } + } - return 1; + location=location>refStart?location:refStart; + location=location=refStart?location:-1; } /** * Return the position on the read that corresponds to a given position @@ -275,62 +333,70 @@ public int getReferencePositionAtReadPosition(int readLookingPositon){ * @param record * @return */ - public int getReadPositionAtReferencePosition(int posOnRef){ + public int getReadPositionAtReferencePosition(int refLookingPosition){ // read htsjdk.samtools.* API - int location=0; - - if ((posOnRef - refStart)*(posOnRef - refEnd) >= 0){ - if (Math.abs(posOnRef-refStart) > Math.abs(posOnRef-refEnd)) - location = strand?readEnd+posOnRef-refEnd:readEnd-posOnRef+refEnd; - else - location = strand?readStart+posOnRef-refStart:readStart-posOnRef+refStart; + int location=-1; + + if(refLookingPosition < refStart) { + location = strand?readStart+refLookingPosition-refStart:readStart-refLookingPosition+refStart; + }else if(refLookingPosition > refEnd) { + location = strand?readEnd+refLookingPosition-refEnd:readEnd-refLookingPosition+refEnd; } else{ - // current coordinate on read, followed the reference contig's direction + // current coordinate on sense/anti-sense read that has the same direction as the ref contig int posOnRead = strand?readStart:readLength-readStart+1; // current position on ref - int pos = refStart; + int posOnRef = refStart; - for (final CigarElement e : alignmentCigars) { - final int length = e.getLength(); - switch (e.getOperator()) { - case H : - case S : - case P : - break; // ignore pads and clips - case I : - posOnRead += length; - break; - case M ://match or mismatch - case EQ://match - case X ://mismatch - if (pos + length < posOnRef){ - pos += length; + if(cigar==null){ //approximate + location = (int) (posOnRead + (refLookingPosition - posOnRef)*Math.abs(1.0*(readEnd-readStart)/(refEnd-refStart))); + }else{ + boolean found=false; + for (final CigarElement e : cigar.getCigarElements()) { + if(found) + break; + final int length = e.getLength(); + switch (e.getOperator()) { + case H : + case S : + case P : + break; // ignore pads and clips + case I : posOnRead += length; - }else{ - location = posOnRef + posOnRead - pos; - } - break; - case D : - case N : - //delete - if (pos + length < posOnRef){ - pos += length; - }else{ - location = posOnRead; - } - break; - default : throw new IllegalStateException("Case statement didn't deal with cigar op: " + e.getOperator()); - }//casse - }//for + break; + case M ://match or mismatch + case EQ://match + case X ://mismatch + if (posOnRef + length < refLookingPosition){ + posOnRef += length; + posOnRead += length; + }else{ + location = refLookingPosition + posOnRead - posOnRef; + found=true; + } + break; + case D : + case N : + //delete + if (posOnRef + length < refLookingPosition){ + posOnRef += length; + }else{ + location = posOnRead; + found=true; + } + break; + default : throw new IllegalStateException("Case statement didn't deal with cigar op: " + e.getOperator()); + }//casse + }//for + } //convert back to coordinate based on read direction location = strand?location:readLength-location+1; //1-index } location=location>1?location:1; location=location=readAlignmentStart()&&location<=readAlignmentEnd()?location:-1; } /* (non-Javadoc) diff --git a/src/main/java/org/rtassembly/npgraph/BDEdge.java b/src/main/java/org/rtassembly/npgraph/BDEdge.java index 7897f64..b58509d 100644 --- a/src/main/java/org/rtassembly/npgraph/BDEdge.java +++ b/src/main/java/org/rtassembly/npgraph/BDEdge.java @@ -5,18 +5,14 @@ import org.graphstream.graph.implementations.AbstractEdge; import org.graphstream.graph.implementations.AbstractNode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; public class BDEdge extends AbstractEdge{ protected boolean dir0, dir1;//true: outward, false: inward //note that traversing direction (true: template, false: reverse complement) of destination node is opposite its defined direction (true: outward, false:inward) - private static final Logger LOG = LoggerFactory.getLogger(BDEdge.class); - - protected BDEdge(String id, AbstractNode node0, AbstractNode node1, boolean dir0, boolean dir1) { - // id fuck off!!! we'll make one for ourselves + protected BDEdge(String id, AbstractNode node0, AbstractNode node1, boolean dir0, boolean dir1) { + // id is troublesome, we'll make one for ourselves this(node0,node1,dir0,dir1); } protected BDEdge(AbstractNode node0, AbstractNode node1, boolean dir0, boolean dir1) { diff --git a/src/main/java/org/rtassembly/npgraph/BDEdgePrototype.java b/src/main/java/org/rtassembly/npgraph/BDEdgePrototype.java index 3d491c6..0751d36 100644 --- a/src/main/java/org/rtassembly/npgraph/BDEdgePrototype.java +++ b/src/main/java/org/rtassembly/npgraph/BDEdgePrototype.java @@ -1,4 +1,5 @@ package org.rtassembly.npgraph; + /* * A presentation of BDEdge with directed node (BDNodeState) * TODO: should be merged into one class? diff --git a/src/main/java/org/rtassembly/npgraph/BDGraph.java b/src/main/java/org/rtassembly/npgraph/BDGraph.java index 4da8472..35dd674 100644 --- a/src/main/java/org/rtassembly/npgraph/BDGraph.java +++ b/src/main/java/org/rtassembly/npgraph/BDGraph.java @@ -3,8 +3,8 @@ import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; +import java.lang.invoke.MethodHandles; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -16,10 +16,10 @@ import java.util.stream.Collectors; import java.util.stream.Stream; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; import org.graphstream.graph.*; import org.graphstream.graph.implementations.*; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.google.common.util.concurrent.AtomicDouble; @@ -30,31 +30,33 @@ public class BDGraph extends MultiGraph{ - private static final Logger LOG = LoggerFactory.getLogger(BDGraph.class); + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); static int KMER=127; - static boolean circular=true; // circular or linear preference for the output genome - static double RCOV=0.0; + static boolean IS_CIRCULAR=true, // circular or linear preference for the output genome + IS_COMPLEX=false; // how big and complicated this graph is + static double RCOV=0.0; + SimpleBinner binner; //not gonna change these parameters in other thread public static final double R_TOL=.25;// relative tolerate: can be interpreted as long read error rate (10-25%) public static final int A_TOL=300;// absolute tolerate: can be interpreted as long read absolute error bases (100bp) - public static final int GOOD_SUPPORT=20; //number of minimum spanning reads for an affirmative long-reads-based bridge. public static final double ALPHA=.5; //coverage less than alpha*bin_cov will be considered noise public static final int D_LIMIT=5000; //distance bigger than this will be ignored public static int S_LIMIT=300;// maximum number of graph traversing steps - public static int T_LIMIT=21588;// maximum time allowed to run DFS (milliseconds) + public static int T_LIMIT=2188;// maximum time allowed to run DFS (milliseconds) public static int MAX_LISTING=100; //maximum number of whatever paths //these should be changed in another thread, e.g. settings from GUI - public static volatile double ILLUMINA_READ_LENGTH=300; //Illumina MiSeq - public static volatile int MIN_SUPPORT=3; //minimal support reads for bridging + public static double ILLUMINA_READ_LENGTH=300; //Illumina MiSeq + public static int MIN_SUPPORT=3; //minimal support reads for bridging + public static int GOOD_SUPPORT=20; //number of minimum spanning reads for an affirmative long-reads-based bridge. //provide mapping from unique directed node to its corresponding bridge //E.g: 103-: <103-82-> also 82+:<82+103+> - private HashMap bridgesMap; + public final static HashMap bridgesMap=new HashMap(); ConsensusCaller consensus; //mapping long but unknown contigs to unique successors and predecessor @@ -88,7 +90,6 @@ public BDGraph(String id, boolean strictChecking, boolean autoCreate, int initialNodeCapacity, int initialEdgeCapacity) { super(id, strictChecking, autoCreate); - bridgesMap=new HashMap(initialNodeCapacity*2); consensus = new ConsensusCaller(); // adjacencyMap=new HashMap>(); // All we need to do is to change the node & edge factory @@ -105,15 +106,6 @@ public BDEdge newInstance(String id, Node src, Node dst, boolean directed) { //s }); } - public HashSet getUnsolvedBridges(){ - HashSet retval = new HashSet(); - for(GoInBetweenBridge brg:bridgesMap.values()){ - if(brg.getCompletionLevel()<4) - retval.add(brg); - } - - return retval; - } /** * Creates an empty graph with default edge and node capacity. @@ -148,6 +140,7 @@ public BDGraph(){ protected BDEdge addEdge(AbstractNode src, AbstractNode dst, boolean dir0, boolean dir1){ BDEdge tmp = (BDEdge) addEdge(BDEdge.createID(src, dst, dir0, dir1), src, dst); + logger.trace("Graph {} : edge {} added successfully!", getId(), tmp.getId()); return tmp; } @@ -186,7 +179,16 @@ public static void setKmerSize(int kmer){ /************************************************************************************************** ********************** utility functions to serve the assembly algo ****************************** * ***********************************************************************************************/ - + synchronized public HashSet getUnsolvedBridges(){ + HashSet retval = new HashSet(); + for(GoInBetweenBridge brg:bridgesMap.values()){ + if(brg.getCompletionLevel()<4) + retval.add(brg); + } + + return retval; + } + // when this unique node actually contained by a bridge synchronized public void removeNodeFromBridgesMap(Node unqNode){ bridgesMap.remove(unqNode.getId()+"o"); @@ -374,8 +376,7 @@ synchronized public static PopBin getUniqueBinFromLongReads(BDNode node){ unknownBinMap.remove(ko); unknownBinMap.remove(ki); node.setAttribute("unique", retval); - if(HybridAssembler.VERBOSE) - LOG.info("FOUND NEW UNIQUE CONTIG BY LONG READS: ID=" + node.getId() + " degree= " + node.getDegree() + " out=" + co + " in=" + ci + " read count="+c); + logger.debug("FOUND NEW UNIQUE CONTIG BY LONG READS: ID={} degree={} out={} in={} readcount={}", node.getId(), node.getDegree(), co, ci, c); return retval; } @@ -448,8 +449,7 @@ else if(compare < 0) else continue; - if(HybridAssembler.VERBOSE) - LOG.info("Found one weird node {} inCov={}/{}, outCov={}/{}",(String)node.getAttribute("name"), inCov, node.getInDegree(), outCov, node.getOutDegree()); + logger.debug("Found one weird node {} inCov={}/{}, outCov={}/{}", (String)node.getAttribute("name"), inCov, node.getInDegree(), outCov, node.getOutDegree()); } BDNodeState n0,n1; @@ -469,8 +469,7 @@ else if(compare < 0) int overlap=GraphUtil.overlap(seq0,seq1); if(overlap > 55){ BDEdge e=addEdge(n0.getNode(), n1.getNode(), n0.getDir(), n1.getDir()); - if(HybridAssembler.VERBOSE) - LOG.info("...adding potential edge {} length={}", e.getId(), overlap); + logger.debug("...adding potential edge {} length={}", e.getId(), overlap); } } } @@ -480,8 +479,7 @@ else if(compare < 0) //Depth First Search strategy synchronized ArrayList DFSAllPaths(BDNode srcNode, BDNode dstNode, boolean srcDir, boolean dstDir, int distance) { - if(HybridAssembler.VERBOSE) - LOG.info("Looking for DFS path between {}{} to {}{} with distance={}",srcNode.getId(), srcDir?"o":"i", dstNode.getId(), dstDir?"o":"i" ,distance); + logger.debug("Looking for DFS path between {} to {} with distance={}", srcNode.getId()+(srcDir?"o":"i"), dstNode.getId() + (dstDir?"o":"i"), distance); ArrayList possiblePaths = new ArrayList(), retval=new ArrayList(); @@ -501,8 +499,7 @@ synchronized ArrayList DFSAllPaths(BDNode srcNode, BDNode dstNode, boole delta; BDEdge curEdge = null; - if(HybridAssembler.VERBOSE) - LOG.info("Found " + curNodeState.toString() + " with shortest distance=" + shortestMap.get(curNodeState.toString())); + logger.debug("Found {} with shortest distance={}", curNodeState.toString(), shortestMap.get(curNodeState.toString())); AtomicDouble limit = new AtomicDouble(distance+tolerance); @@ -608,8 +605,7 @@ synchronized ArrayList DFSAllPaths(BDNode srcNode, BDNode dstNode, boole } } - if(HybridAssembler.VERBOSE) - LOG.info("select from list of " + possiblePaths.size() + " DFS paths:"); + logger.debug("select from list of {} DFS paths", possiblePaths.size()); if(possiblePaths.isEmpty()) return null; @@ -621,8 +617,7 @@ synchronized ArrayList DFSAllPaths(BDNode srcNode, BDNode dstNode, boole if(Math.abs(p.getDeviation())>closestDist+Math.abs(distance+getKmerSize())*R_TOL || i>=keepMax) break; retval.add(p); - if(HybridAssembler.VERBOSE) - LOG.info("Hit added: {} deviation={}; depth={}; likelihood score={}", p.getId(), p.getDeviation(), p.size(), p.getPathEstats()); + logger.debug("Hit added: {} deviation={}; depth={}; likelihood={}", p.getId(), p.getDeviation(), p.size(), p.getPathEstats()); } @@ -685,21 +680,47 @@ public static HashMap getShortestTreeFromNode(BDNode rootNode, return retval; } + /********************************************************************************************************* + * Get usefulness of read chunk(s) and reduce the bridge if found + * (a readuntil version of uniqueBridgesFinding) + * @param nnpRead + * @param alignments + * @return usefulness (whether to reject or proceed sequencing this read) + */ + public synchronized boolean getRUDecision(Sequence nnpRead, ArrayList alignments) { + if(nnpRead==null || alignments.isEmpty()) + return false; + + logger.debug("================================================="); + for(Alignment alg:alignments) + logger.info("\t{}", alg.toString()); + logger.debug("================================================="); + + //1. get list of ordered alignments from this chunk + //2. Estimate distance from the first unique contigs to the next unresolved one. + // and assign usefulness accordingly... + //3. If readID has been assigned useful=true and not reach next unresolved unique contig, proceeding... + //4. If reach next unresolved one, estimate the distance to reach the next unresolved one... + // find and reduce unique path if possible as in uniqueBridgesFinding + + + return true; + } + /**********************************************************************************************************/ /* * Find bridges based on list of Alignments. * Return list of bridges with endings as markers and alignments of non-markers in-between. */ - synchronized protected List uniqueBridgesFinding(Sequence nnpRead, ArrayList alignments) { + public synchronized List uniqueBridgesFinding(Sequence nnpRead, ArrayList alignments) { if(nnpRead==null || alignments.size()<=1) return null; - if(HybridAssembler.VERBOSE) { - LOG.info("================================================="); - for(Alignment alg:alignments) - LOG.info("\t"+alg.toString()); - LOG.info("================================================="); - } + logger.debug("================================================="); + for(Alignment alg:alignments) + logger.info("\t{}", alg.toString()); + logger.debug("================================================="); + //First bin the alignments into different overlap regions //only considering useful alignments HashMap allAlignments = new HashMap(); @@ -737,26 +758,23 @@ synchronized protected List uniqueBridgesFinding(Sequence nnpRead, Array } - if(HybridAssembler.VERBOSE) { - LOG.info("Step ranges: "); - String log=""; - for(Range range:curRanges) { - log+=(allAlignments.get(range).node.getId() + " "+ binner.getBinsOfNode(allAlignments.get(range).node) + ": " + range + "; "); - } - LOG.info(log); - } + logger.debug("Step ranges: "); + String log=""; + for(Range range:curRanges) { + log+=(allAlignments.get(range).node.getId() + " "+ binner.getBinsOfNode(allAlignments.get(range).node) + ": " + range + "; "); + } + logger.debug(log); + Alignment curAlg=null; for(int i=1; i buildBridge(AlignedRead read, PopBin bin){ List retval=new ArrayList(); - + boolean complete=false; GoInBetweenBridge storedBridge=getBridgeFromMap(read), reversedBridge; - if(HybridAssembler.VERBOSE) - LOG.info("+++{} <=> {}\n", read.getEndingsID(), storedBridge==null?"null":storedBridge.getEndingsID()); + logger.debug("+++{} <==> {}", read.getEndingsID(), (storedBridge==null?"null":storedBridge.getEndingsID())); //long read give info about unique successor and predecessor addReadsToUnknowMap(read); if(storedBridge!=null) { if(storedBridge.getCompletionLevel()==4){//important since it will ignore the wrong transformed unique nodes here!!! - if(HybridAssembler.VERBOSE) - LOG.info(storedBridge.getEndingsID() + ": already solved and reduced: ignore!"); + logger.debug("{}: already solved and reduced: ignore!", storedBridge.getEndingsID()); return retval; }else{ - if(HybridAssembler.VERBOSE) - LOG.info(storedBridge.getEndingsID() + ": already built: fortify!"); + logger.debug("{}: already built: fortify!", storedBridge.getEndingsID()); //update available bridge using alignments byte state=storedBridge.merge(read,true); @@ -826,13 +841,15 @@ private List buildBridge(AlignedRead read, PopBin bin){ boolean extend=false; if(storedBridge.getCompletionLevel()==1){ if(storedBridge.scanForAnEnd(false)){ - if(HybridAssembler.VERBOSE) - LOG.info("FOUND NEW TRANSFORMED END: " + storedBridge.steps.end.getNode().getId()); + logger.debug("FOUND NEW TRANSFORMED END: {}", storedBridge.steps.end.getNode().getId()); extend=storedBridge.steps.connectBridgeSteps(false); } } - if(storedBridge.getCompletionLevel()==4 || (state&0b01)>0 || extend) + + if(storedBridge.getCompletionLevel()==4 || (state&0b01)>0 || extend) { retval.addAll(storedBridge.scanForNewUniquePaths()); + complete=true; + } // //also update the reversed bridge: important e.g. Shigella_dysenteriae_Sd197. WHY??? (already updated and merged 2 homo bridges) if(read.getEFlag()==3) { @@ -843,8 +860,10 @@ private List buildBridge(AlignedRead read, PopBin bin){ if((anotherState&0b10)>0)//number of anchors has changed after merging updateBridgesMap(reversedBridge); - if(reversedBridge.getCompletionLevel()==4 || (anotherState&0b01)>0) + if(reversedBridge.getCompletionLevel()==4 || (anotherState&0b01)>0) { retval.addAll(reversedBridge.scanForNewUniquePaths()); + complete=true; + } } @@ -862,7 +881,11 @@ private List buildBridge(AlignedRead read, PopBin bin){ updateBridgesMap(reversedBridge); } - consensus.saveBridgingReadsFromAlignments(read); + + if(complete) + consensus.bridgingReads.remove(read.getEndingsID()); + else + consensus.saveBridgingReadsFromAlignments(read); return retval; } @@ -873,13 +896,13 @@ private List buildBridge(AlignedRead read, PopBin bin){ * @param path: unique path to simplify the graph (from origGraph) */ //This assuming path is surely unique!!! - public boolean reduceUniquePath(BDPath path){ + public void reduceUniquePath(BDPath path){ //do nothing if the path has only one node if(path==null||path.getEdgeCount()<1) - return false; + return; - else if(HybridAssembler.VERBOSE) - LOG.info("Reducing path: " + path.getId()); + else + logger.debug("Reducing path: {}", path.getId()); Set potentialRemovedEdges = binner.walkAlongUniquePath(path); @@ -889,51 +912,41 @@ else if(HybridAssembler.VERBOSE) //remove appropriate edges for(Edge e:potentialRemovedEdges){ - if(HybridAssembler.VERBOSE) { - LOG.info("REMOVING EDGE " + e.getId() + " from " + e.getNode0().getGraph().getId() + "-" + e.getNode1().getGraph().getId()); - LOG.info("before: \n\t" + printEdgesOfNode((BDNode) e.getNode0()) + "\n\t" + printEdgesOfNode((BDNode) e.getNode1())); - } + logger.debug("REMOVING EDGE {} from {}-{}", e.getId(), e.getNode0().getGraph().getId(), e.getNode1().getGraph().getId()); + logger.debug("before: \n\t{}\n\t{}", printEdgesOfNode((BDNode) e.getNode0()), printEdgesOfNode((BDNode) e.getNode1())); + removeEdge(e.getId()); - if(HybridAssembler.VERBOSE) - LOG.info("after: \n\t" + printEdgesOfNode((BDNode) e.getNode0()) + "\n\t" + printEdgesOfNode((BDNode) e.getNode1())); + logger.debug("after: \n\t{}\n\t{}", printEdgesOfNode((BDNode) e.getNode0()), printEdgesOfNode((BDNode) e.getNode1())); } //add appropriate edges BDEdge reducedEdge = addEdge(path.getFirstNode(), path.getLastNode(), path.getFirstNodeDirection(), path.getLastNodeDirection()); - if(HybridAssembler.VERBOSE) - LOG.info("ADDING EDGE " + reducedEdge.getId()+ " from " + reducedEdge.getNode0().getGraph().getId() + "-" + reducedEdge.getNode1().getGraph().getId()); + logger.debug("ADDING EDGE {} from {}-{}", reducedEdge.getId(), reducedEdge.getNode0().getGraph().getId(), reducedEdge.getNode1().getGraph().getId()); if(reducedEdge!=null){ if(path.getPrimitivePath().getEdgeCount()>1) reducedEdge.setAttribute("path", path); binner.edge2BinMap.put(reducedEdge, oneBin); } - if(HybridAssembler.VERBOSE) - LOG.info("after adding: \n\t" + printEdgesOfNode((BDNode) reducedEdge.getNode0()) + "\n\t" + printEdgesOfNode((BDNode) reducedEdge.getNode1())); - return true; - }else { - if(HybridAssembler.VERBOSE) - LOG.info("Path {} doesn't need to be reduced!", path.getId()); - return false; - } + logger.debug("after adding: \n\t{}\n\t{}", printEdgesOfNode((BDNode) reducedEdge.getNode0()), printEdgesOfNode((BDNode) reducedEdge.getNode1())); + + notifyChanges(); + }else + logger.debug("Path {} doesn't need to be reduced!", path.getId()); + } // old reducing. Now use for SPAdes paths only - public boolean reduceFromSPAdesPath(BDPath path){ - boolean retval=false; + public void reduceFromSPAdesPath(BDPath path){ //do nothing if the path has only one node if(path==null || path.getEdgeCount()<1) - return retval; - else if(HybridAssembler.VERBOSE) - LOG.info("Input SPAdes path: " + path.getId()); + return; + else + logger.debug("Input SPAdes path: {}", path.getId()); //loop over the edges of path (like spelling()) - for(BDPath p:getNewSubPathsToReduce(path)){ - if(reduceUniquePath(p)) - retval=true; - } - return retval; + getNewSubPathsToReduce(path).stream().forEach(p->reduceUniquePath(p)); } @@ -979,46 +992,62 @@ synchronized public ArrayList getNewSubPathsToReduce(BDPath path){ **********************************************************************/ int n50, n75, maxl; //in Kbp int numOfCtgs, numOfCircularCtgs; - public synchronized void updateStats() { - numOfCtgs=getNodeCount(); - int count=0; - numOfCircularCtgs=0; - maxl=0; - int [] lengths = new int[numOfCtgs]; - double sum = 0; - for (Node node : this) { - /* - * Re-calculate stats - */ - int nlen=(int)node.getNumber("len"); - if(maxl < nlen) - maxl=nlen; - if(node.hasAttribute("circular")) - numOfCircularCtgs++; + volatile private boolean changed=true; //init as true for the initial cleaning + public synchronized void notifyChanges() { + changed=true; + } + public synchronized boolean checkForChangesAndReset() { + boolean retval=changed; + changed=false; + return retval; + } + + public void updateStats() { + synchronized(this) { + numOfCtgs=getNodeCount(); + int count=0; + int nc=0, + ml=0; + int [] lengths = new int[numOfCtgs]; + // double sum = 0; + for (Node node : this) { + /* + * Re-calculate stats + */ + int nlen=(int)node.getNumber("len"); + if(ml < nlen) + ml=nlen; + if(node.hasAttribute("circular")) + nc++; + + lengths[count++]=nlen; + // sum+=nlen; + } + maxl=ml; + numOfCircularCtgs=nc; + /* + * Calculate N50, N75 + */ + // Arrays.sort(lengths); + // + // int i50 = lengths.length, + // i75 = lengths.length; + // double contains = 0; + // while (true){ + // if(contains < .5*sum) + // i50 --; + // if(contains < .75*sum) { + // i75--; + // }else + // break; + // contains += lengths[i75]; + // } + // n50=lengths[i50]; + // n75=lengths[i75]; - lengths[count++]=nlen; - sum+=nlen; + n50=GraphUtil.getNStats(.5, lengths); + n75=GraphUtil.getNStats(.75, lengths); } - - /* - * Calculate N50, N75 - */ - Arrays.sort(lengths); - - int i50 = lengths.length, - i75 = lengths.length; - double contains = 0; - while (true){ - if(contains < .5*sum) - i50 --; - if(contains < .75*sum) { - i75--; - }else - break; - contains += lengths[i75]; - } - n50=lengths[i50]; - n75=lengths[i75]; } private void initGraphComponents() { @@ -1069,9 +1098,13 @@ public void outputFASTA(String fileName) throws IOException { // if( (node.getDegree()==0 && (seq.length() < SimpleBinner.ANCHOR_CTG_LEN)) // || node.getNumber("cov") < 10.0 ) //not display <10% abundance pops // continue; - seq.writeFasta(out); + if(seq!=null) + seq.writeFasta(out); + else { + logger.error("Cannot read sequence from node {}", node.getId()); + break; + } } - out.close(); } public void outputJAPSA(String fileName) throws IOException { @@ -1079,7 +1112,13 @@ public void outputJAPSA(String fileName) throws IOException { JapsaAnnotation annotation; for(Node node:this) { annotation=(JapsaAnnotation) node.getAttribute("annotation"); - annotation.write(out); + if(annotation!=null) + annotation.write(out); + else { + logger.error("Cannot read annotation from node {}", node.getId()); + break; + } + } out.close(); } @@ -1099,11 +1138,11 @@ public void outputGFA(String fileName) throws IOException { ); Set addedNodes = new HashSet(); - //Print S (Segment) + //Print S (Segment) with kmer count (KC) and bin (BI) for(Node node:this){ Sequence seq=(Sequence) node.getAttribute("seq"); int kmer_count=(int)(GraphUtil.getRealCoverage(node.getNumber("cov"))*(BDGraph.ILLUMINA_READ_LENGTH-BDGraph.getKmerSize())/BDGraph.ILLUMINA_READ_LENGTH); - printWriter.printf("S\t%s\t%s\tKC:i:%d\n", node.getId(), seq.toString(),kmer_count); + printWriter.printf("S\t%s\t%s\tKC:i:%d\tBI:i:%d\n", node.getId(), seq.toString(),kmer_count, node.getAttribute("bin")==null?0:((PopBin)node.getAttribute("bin")).getId()); addedNodes.add(node.getId()); } for(Edge ce:compositeEdges){ @@ -1131,7 +1170,8 @@ public void outputGFA(String fileName) throws IOException { Sequence seq=(Sequence) nextNode.getAttribute("seq"); int kmer_count=(int)(GraphUtil.getRealCoverage(nextNode.getNumber("cov"))*(BDGraph.ILLUMINA_READ_LENGTH-BDGraph.getKmerSize())/BDGraph.ILLUMINA_READ_LENGTH); - printWriter.printf("S\t%s\t%s\tKC:i:%d\n", nextID, seq.toString(),kmer_count); + printWriter.printf("S\t%s\t%s\tKC:i:%d\tBI:i:%d\n", nextID, seq.toString(),kmer_count, nextNode.getAttribute("bin")==null?0:((PopBin)nextNode.getAttribute("bin")).getId()); + } diff --git a/src/main/java/org/rtassembly/npgraph/BDNodeVecState.java b/src/main/java/org/rtassembly/npgraph/BDNodeVecState.java index 063f796..a0230c7 100644 --- a/src/main/java/org/rtassembly/npgraph/BDNodeVecState.java +++ b/src/main/java/org/rtassembly/npgraph/BDNodeVecState.java @@ -1,11 +1,13 @@ package org.rtassembly.npgraph; +import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.Objects; import java.util.TreeSet; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; + /* * Class represent a node together with its vector in relative to another root node. @@ -13,7 +15,7 @@ * although we can translate into appropriate info */ public class BDNodeVecState implements Comparable{ - private static final Logger LOG = LoggerFactory.getLogger(BDNodeVecState.class); + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); BDNode root, dest; ScaffoldVector vector; @@ -125,18 +127,21 @@ public boolean approximate(final Object obj) { } } - - public boolean qc() { - return nvsScore >= BDGraph.MIN_SUPPORT*Alignment.GOOD_QUAL; //60*2 + //just to estimate the node support + int qc() { + if(nvsScore < BDGraph.MIN_SUPPORT*Alignment.GOOD_QUAL) + return 0; //60*2 + else if(nvsScore < BDGraph.GOOD_SUPPORT*Alignment.GOOD_QUAL) + return 1; + else + return 2; } //Merging 2 equal NodeVectors public boolean merge(BDNodeVecState nv){ - if(HybridAssembler.VERBOSE) - LOG.info("Merging vector {} with {}: ", this.toString(), nv.toString()); + logger.debug("Merging vector {} with {}:", this.toString(), nv.toString()); if(!this.approximate(nv)){ - if(HybridAssembler.VERBOSE) - LOG.info("...merging failed!"); + logger.debug("...merging failed!"); return false; } @@ -152,8 +157,7 @@ public boolean merge(BDNodeVecState nv){ if(nvsScore>Alignment.GOOD_QUAL*BDGraph.MAX_LISTING) nvsScore=Alignment.GOOD_QUAL*BDGraph.MAX_LISTING; - if(HybridAssembler.VERBOSE) - LOG.info(this.toString()); + logger.debug(this.toString()); return true; } @@ -169,6 +173,9 @@ public static void NWAlignment(TreeSet s0, TreeSet m0 = new ArrayList<>(), @@ -250,8 +256,7 @@ else if(scoreTab[i][j]==delete) scale0=(as0[ii].getDistance()-as0[i].getDistance())*1.0/origStep; scale1=(as0[ii].getDistance()-as0[i].getDistance())*1.0/(as1[jj].getDistance()-as1[j].getDistance()); - if(HybridAssembler.VERBOSE) - LOG.info("scale0={} scale1={}\n", scale0,scale1); + logger.debug("scale0={} scale1={}", scale0, scale1); if(Math.max(scale0, scale1) < BDGraph.A_TOL/1.0 && Math.min(scale0, scale1) > 0) { //constrain scalex //calibrate the vectors from first list's in-between @@ -270,8 +275,8 @@ else if(scoreTab[i][j]==delete) //move to next match coordinate prevOrigNVS=curOrigNVS; i=ii; j=jj; - }else if(HybridAssembler.VERBOSE) - LOG.info("...moving to next match point!"); + }else + logger.debug("...moving to next match point!"); } //nodes after the last match...scale=1.0 for(int i0=i+1;i0 0: filled with Ns", e.getId(), overlap); + logger.debug("Edge {} has length={} > 0: filled with Ns...", e.getId(), overlap); } @@ -275,16 +274,13 @@ public BDPath join(BDPath newPath) { return new BDPath(newPath); if(newPath.getRoot() != peekNode()){ - if(HybridAssembler.VERBOSE) - LOG.error("Cannot join path {} to path {} with disagreed first node: {} != {}", newPath.getId(), this.getId(), newPath.getRoot().getId() ,peekNode().getId()); + logger.debug("Cannot join path {} to path {} with disagreed first node: {}!={}", newPath.getId(), this.getId(), newPath.getRoot().getId(), peekNode().getId()); return null; } if(newPath.getNodeCount() > 1 && this.getNodeCount() > 1 && newPath.getFirstNodeDirection()==getLastNodeDirection()){ - if(HybridAssembler.VERBOSE) - LOG.error("Conflict direction from the first node " + newPath.getRoot().getId()); - + logger.debug("Conflict direction from the first node {}", newPath.getRoot().getId()); return null; } BDPath retval=new BDPath(this); @@ -338,8 +334,7 @@ public int getClosestDistance(Node from, Node to, boolean direction, int distanc }else if(from==peekNode()){ ref=this.reverse(); }else{ - if(HybridAssembler.VERBOSE) - LOG.warn("Node {} couldn't be found as one of the end node in path {}!", from.getId(), getId()); + logger.debug("Node {} couldn't be found as one of the end node in path {}", from.getId(), getId()); return retval; } int curDistance=0; @@ -354,16 +349,16 @@ public int getClosestDistance(Node from, Node to, boolean direction, int distanc dirOfTo=!((BDEdge) e).getNodeDirection((BDNode) curNode); if((dirOfFrom == dirOfTo) == direction){ if(Math.abs(curDistance-distance) < BDGraph.A_TOL || GraphUtil.approxCompare(curDistance, distance)==0){ - if(HybridAssembler.VERBOSE) - LOG.info("|-> agree distance between node {}, node {}: {} and given distance {}", - from.getId(), to.getId(), curDistance, distance); + logger.debug("|-> agree distance between node {} and node {}: {} ~= {}", from.getId(), to.getId(), curDistance, distance); if(Math.abs(retval) > Math.abs(curDistance-distance)) retval=curDistance-distance; } - }else if(HybridAssembler.VERBOSE) - LOG.info("!-> inconsistence direction between node {} ({}), node {} ({}) and given orientation: {}", - from.getId(), dirOfFrom?"+":"-", to.getId(), dirOfTo?"+":"-", direction?"same":"opposite" ); + }else + logger.debug("!-> inconsistence direction between node {} and node {}, given their orientation are the {}", + from.getId()+(dirOfFrom?"+":"-"), + to.getId()+(dirOfTo?"+":"-"), + (direction?"same":"opposite")); } curDistance+=curNode.getNumber("len"); } diff --git a/src/main/java/org/rtassembly/npgraph/ConsensusCaller.java b/src/main/java/org/rtassembly/npgraph/ConsensusCaller.java index 37ff022..182c1f4 100644 --- a/src/main/java/org/rtassembly/npgraph/ConsensusCaller.java +++ b/src/main/java/org/rtassembly/npgraph/ConsensusCaller.java @@ -1,14 +1,16 @@ package org.rtassembly.npgraph; import java.io.File; +import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; + import japsa.bio.np.ErrorCorrection; import japsa.seq.Alphabet; import japsa.seq.Sequence; @@ -16,7 +18,7 @@ //Module for consensus bridging of contigs when they're not connected in the assembly graph public class ConsensusCaller { - private static final Logger LOG = LoggerFactory.getLogger(ConsensusCaller.class); + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); volatile HashMap> bridgingReads; volatile HashMap consensusReads; @@ -36,11 +38,13 @@ public void setConsensusMSA(String aligner){ aligner=aligner.toLowerCase().trim(); if(aligner.startsWith("spoa")) msa="spoa"; - else if(aligner.startsWith("poa")) + else if(aligner.startsWith("abpoa")) + msa="abpoa"; + else if(aligner.startsWith("poa")) //slow but default to many Ubuntu distro msa="poa"; else if(aligner.startsWith("kalign3")) msa="kalign3"; - else if(aligner.startsWith("kalign")) + else if(aligner.startsWith("kalign")) //slow but default to many Ubuntu distro msa="kalign"; else msa="none"; @@ -55,8 +59,10 @@ else if(!bridgingReads.containsKey(id)) bridgingReads.put(id, new ArrayList()); getBridgingReadList(id).add(seq); - if(getBridgingReadsNumber(id) >= BDGraph.GOOD_SUPPORT) + if(getBridgingReadsNumber(id) >= BDGraph.GOOD_SUPPORT) { + //TODO: check sequences length setConsensusSequence(id); + } } public List getBridgingReadList(String id){ return bridgingReads.get(id); @@ -66,19 +72,41 @@ public int getBridgingReadsNumber(String id){ } public Sequence getConsensus(String id, boolean force){ if(force && getBridgingReadsNumber(id)>=BDGraph.MIN_SUPPORT) - setConsensusSequence(id); //do it even the lack not enough bridging reads + setConsensusSequence(id); //do it even not enough bridging reads return consensusReads.get(id); } - private synchronized void setConsensusSequence(String id){ + + //Scan the list and check for the length consistency & remove the odd-one out + //If none is removed -> good, return 1 + //If several are removed -> return 0 + //If more than 20% are removed -> bad list, return -1 and not to considered for consensus calling + private int scanElementReads(String id) { + if(!bridgingReads.containsKey(id)) + return -1; + int size=bridgingReads.get(id).size(); + double average=bridgingReads.get(id).stream().mapToInt(Sequence::length).average().getAsDouble(); + bridgingReads.get(id).removeIf(s->GraphUtil.approxCompare(average, s.length())!=0); + if(bridgingReads.size()==0) + return -1; + else if(bridgingReads.size()==size) + return 1; + else if(bridgingReads.size() > (1-BDGraph.R_TOL*size)) + return 0; + else { + bridgingReads.remove(id); + return -1; + } + + } + + private void setConsensusSequence(String id){ Sequence consensus=null; try { ErrorCorrection.msa=msa; - ErrorCorrection.VERBOSE=HybridAssembler.VERBOSE; consensus=ErrorCorrection.consensusSequence(getBridgingReadList(id), AlignedRead.tmpFolder+File.separator+id); } catch (Exception e) { - if(HybridAssembler.VERBOSE) - LOG.warn("Error with consensus calling:\n {} Pick first read for the consensus of bridge {}", e.getMessage(), id); + logger.debug("Invalid consensus calling for {}. Pick the first read for the consensus of bridge.\n{}", id, e); consensus=getBridgingReadList(id).get(0); } consensusReads.put(id, consensus); @@ -251,11 +279,11 @@ private void saveCorrectedSequenceInBetween(AlignedRead read){ String key=read.getEndingsID(); - if(HybridAssembler.VERBOSE) - LOG.info("Save read {} to bridge {}: {} -> {}", read.readSequence.getName(), key, - fromContig.getId() + (start.strand?"+":"-"), - toContig.getId() + (end.strand?"+":"-") - ); + logger.debug("Save read {} to bridge {}: {} -> {}", + read.readSequence.getName(), + key, + fromContig.getId()+(start.strand?"+":"-"), + toContig.getId() + (end.strand?"+":"-")); Sequence seq=seqBuilder.toSequence(); addBridgingRead(key, seq); } diff --git a/src/main/java/org/rtassembly/npgraph/GoInBetweenBridge.java b/src/main/java/org/rtassembly/npgraph/GoInBetweenBridge.java index 9bea4fb..9627129 100644 --- a/src/main/java/org/rtassembly/npgraph/GoInBetweenBridge.java +++ b/src/main/java/org/rtassembly/npgraph/GoInBetweenBridge.java @@ -1,31 +1,32 @@ package org.rtassembly.npgraph; -import java.io.File; +import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.NoSuchElementException; import java.util.PriorityQueue; import java.util.Set; import java.util.TreeSet; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; import org.graphstream.graph.Node; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import japsa.seq.Sequence; //A bridge structure that the first node must be unique public class GoInBetweenBridge { - private static final Logger LOG = LoggerFactory.getLogger(GoInBetweenBridge.class); + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); BDGraph graph; PopBin bin; - BDEdgePrototype pBridge; //note: the ending nodes must be unique, or else omitted + public BDEdgePrototype pBridge; //note: the ending nodes must be unique, or else omitted ArrayList segments; - BridgeSteps steps; + public BridgeSteps steps; public GoInBetweenBridge(BDGraph graph, AlignedRead bb, PopBin bin) { this.graph=graph; @@ -89,8 +90,13 @@ byte merge(GoInBetweenBridge qBridge, boolean toConnect) { qNode0=qBridge.pBridge.n0, qNode1=qBridge.pBridge.n1; - if(HybridAssembler.VERBOSE) - LOG.info("Merging Bridge {} lvl={} \n{}\nwith Bridge {} lvl={}\n{}\n", getEndingsID(), getCompletionLevel(), getAllNodeVector(), qBridge.getEndingsID(), qBridge.getCompletionLevel(), qBridge.getAllNodeVector()); + logger.debug("Merging Bridge {} level={}\n{}\nwith Bridge {} level={}\n{}", + getEndingsID(), + getCompletionLevel(), + getAllNodeVector(), + qBridge.getEndingsID(), + qBridge.getCompletionLevel(), + qBridge.getAllNodeVector()); // System.out.println("sNode0=" + (sNode0==null?"null":sNode0.getId())); // System.out.println("sNode1=" + (sNode1==null?"null":sNode1.getId())); // System.out.println("qNode0=" + (qNode0==null?"null":qNode0.getId())); @@ -173,8 +179,12 @@ byte merge(AlignedRead read, boolean toConnect) { qNode0=new BDNodeState(read.getFirstAlignment().node, read.getFirstAlignment().strand), qNode1=new BDNodeState(read.getLastAlignment().node, !read.getLastAlignment().strand); - if(HybridAssembler.VERBOSE) - LOG.info("Merging Bridge {} lvl={} \n{}\nwith AlignedRead {}\n{}\n", getEndingsID(), getCompletionLevel(), getAllNodeVector(), read.getEndingsID(), read.getCompsString()); + logger.debug("Merging Bridge {} level={}\n{}with AlignedRead {}\n{}", + getEndingsID(), + getCompletionLevel(), + getAllNodeVector(), + read.getEndingsID(), + read.getCompsString()); // System.out.println("sNode0=" + (sNode0==null?"null":sNode0.getId())); // System.out.println("sNode1=" + (sNode1==null?"null":sNode1.getId())); // System.out.println("qNode0=" + (qNode0==null?"null":qNode0.getId())); @@ -238,10 +248,12 @@ else if(!sNode1.equals(qNode0)) steps.connectBridgeSteps(false); - if(HybridAssembler.VERBOSE){ - LOG.info("After merging: {}\nstart={}; end={}; completion level={}", pBridge.toString(), (steps.start==null?"null":steps.start.toString()), (steps.end==null?"null":steps.end.toString()), getCompletionLevel()); - LOG.info(getAllPossiblePaths()); - } + logger.debug("After merging: {}\nstart={}; end={}; level={}", + pBridge.toString(), + (steps.start==null?"null":steps.start.toString()), + (steps.end==null?"null":steps.end.toString()), + getCompletionLevel()); + logger.debug(getAllPossiblePaths()); if(getNumberOfAnchors()>numOfAnchorsBefore) retval=(byte)(retval+0b10); @@ -258,8 +270,11 @@ private void addSegment(BridgeSegment seg) { private void reverse() { assert getNumberOfAnchors()==2:"Could only reverse a determined bridge (2 anchors)"; - if(HybridAssembler.VERBOSE) - LOG.info("Reversing the bridge {} (start={} end={}):\n{}\n", getEndingsID(), steps.start.toString(), steps.end.toString(), getAllNodeVector()); + logger.debug("Reversing the bridge {} (start={} end={}):\n{}", + getEndingsID(), + steps.start.toString(), + steps.end.toString(), + getAllNodeVector()); pBridge=pBridge.reverse(); int direction=pBridge.getDir0()?1:-1; @@ -278,23 +293,26 @@ private void reverse() { ScaffoldVector rev=ScaffoldVector.reverse(steps.end.getVector());//anchors number = 2 so there exist end node BDNodeVecState tmp = null; + //re-assign start and end + tmp=steps.start; + steps.start=steps.end; + steps.end=tmp; + //need to do this to re-sort the changed elements - for(BDNodeVecState nv:steps.subSet(steps.start, steps.end)) { + for(BDNodeVecState nv:steps.nodes) { nv.setVector(ScaffoldVector.composition(nv.getVector(), rev)); - nv.setRoot(steps.end.getNode()); + nv.setRoot(steps.start.getNode()); if(nv.getVector().isIdentity() || (nv.getVector().getMagnitute()-BDGraph.A_TOL)*direction>0) reversedSet.add(nv); } - //re-assign start and end - tmp=steps.start; - steps.start=steps.end; - steps.end=tmp; steps.nodes=reversedSet; - - if(HybridAssembler.VERBOSE) - LOG.info("Reversed bridge = {} (start={} end={}):\n{}\n", getEndingsID(), steps.start.toString(), steps.end.toString(), getAllNodeVector()); + logger.debug("Reversed bridge = {} (start={} end={}):\n{}", + getEndingsID(), + steps.start.toString(), + steps.end.toString(), + getAllNodeVector()); } @@ -317,7 +335,7 @@ public String getAllPossiblePaths() { retval += "()"; else for(BDPath path:seg.connectedPaths) - retval+="( "+path.getId()+ ": deviation=" + path.getDeviation() + " score=" + path.getPathEstats() + " )"; + retval+="( "+path.getId()+ ": deviation=" + path.getDeviation() + " likelihood=" + path.getPathEstats() + " )"; retval+="\n"; } retval+="}"; @@ -332,8 +350,7 @@ public String getAllNodeVector(){ //Get the most probable path out of the candidates based on deviation and likelihood public BDPath getBestPath(Node startFrom, Node endAt) { //the markers must be (transformed) unique - if(HybridAssembler.VERBOSE) - LOG.info("Finding best path from " + startFrom.getId() + " to " + endAt.getId() + " among: \n" + getAllPossiblePaths()); + logger.debug("Finding best path from {} to {} among:\n{}", startFrom.getId(), endAt.getId(), getAllPossiblePaths()); BDPath retval=null; if(segments==null || segments.isEmpty()) @@ -390,8 +407,7 @@ public BDPath getBestPath(Node startFrom, Node endAt) { //the markers must be (t if(retval!=null) { retval.setConsensusUniqueBinOfPath(bin); - if(HybridAssembler.VERBOSE) - LOG.info("...best path found: " + retval.getId()); + logger.debug("...best path found: {}", retval.getId()); } return retval; @@ -427,8 +443,7 @@ public List scanForNewUniquePaths(){ List retval = new ArrayList<>(); if(segments==null || segments.isEmpty()) return retval; - if(HybridAssembler.VERBOSE) - LOG.info("Scanning on bridge with segments:\n" + getAllPossiblePaths()); + logger.debug("Scanning on bridge with segments:\n{}", getAllPossiblePaths()); BDPath curPath=null; PopBin sbin=null; for(BridgeSegment seg:segments){ @@ -470,11 +485,10 @@ public boolean scanForAnEnd(boolean greedy){ PopBin b=SimpleBinner.getBinIfUniqueNow(tmp.dest); if(PopBin.isCloseBins(b,bin)){ - if(steps.end==null || greedy || steps.end.nvsScore < tmp.nvsScore){ + if(steps.end==null || greedy || tmp.qc()>0){ if(steps.end!=tmp){ steps.end=tmp; - if(HybridAssembler.VERBOSE) - LOG.info("FOUND NEW END: " + steps.end.getNode().getId()); + logger.debug("FOUND NEW END: {}", steps.end.getNode().getId()); return true; } } @@ -510,7 +524,7 @@ class BridgeSegment{ pSegment = new BDEdgePrototype(srcNode,dstNode,dir1,dir2); startNV = nv1; endNV = nv2; int d = ScaffoldVector.composition(nv2.getVector(), ScaffoldVector.reverse(nv1.getVector())).distance(srcNode, dstNode); - if(d<=(greedy?10:1)*BDGraph.D_LIMIT) //greedy search will tolerate 10 times longer gap + if(d<=(greedy?3:1)*BDGraph.D_LIMIT) //greedy search will tolerate 3 times longer gap connectedPaths = graph.DFSAllPaths(srcNode, dstNode, dir1, dir2, d); //call consensus when time come! @@ -547,8 +561,8 @@ class BridgeSegment{ connectedPaths.add(path); }catch(Exception e){ - System.err.println("Failed to make consensus sequence for " + id +"!"); - System.err.println("Reason: "+ e.getMessage()); + logger.debug("Failed to make consensus sequence for {}!",id); + logger.debug("Reason: {}", e); } } @@ -570,8 +584,7 @@ class BridgeSegment{ // startNV=new BDNodeVecState(pSegment.getNode0(),pSegment.getNode0(),new ScaffoldVector()); // endNV=new BDNodeVecState(pSegment.getNode1(), new ScaffoldVector(pSegment.getDir0()?dist:-dist, pSegment.getDir0()!=pSegment.getDir1()?1:-1)); // } catch (Exception e) { -// System.err.println("Cannot make bridge from this path!"); -// e.printStackTrace(); +// logger.debug("Cannot make bridge from this path!\n{}",e); // } // // } @@ -624,13 +637,36 @@ int locateAndVote(BDNodeVecState nv) { return retval; } + //remove low-scored paths //return true iff there is only one candidate left <=> result found! public boolean removeUnlikelyPaths(){ if(connectedPaths==null || connectedPaths.isEmpty()) return false; - connectedPaths.sort((a,b)->Integer.compare(Math.abs(a.getDeviation()), Math.abs(b.getDeviation()))); - int bestDiff = connectedPaths.get(0).getDeviation(); - connectedPaths.removeIf(p->(Math.abs(p.getDeviation()) > Math.abs(bestDiff)+BDGraph.A_TOL)); +// connectedPaths.sort((a,b)->Integer.compare(Math.abs(a.getDeviation()), Math.abs(b.getDeviation()))); +// int bestDiff = connectedPaths.get(0).getDeviation(); + try { + Integer bestDiff = connectedPaths + .stream() + .mapToInt(p->Math.abs(p.getDeviation())) + .min().orElseThrow(NoSuchElementException::new); + + //if sufficient coverage, keep the best & remove the rest + if(steps.start.qc() + steps.end.qc()>2) { + connectedPaths.removeIf(p->(Math.abs(p.getDeviation()) > Math.abs(bestDiff))); + Double bestLikelihood = connectedPaths + .stream() + .mapToDouble(p->p.getPathEstats()) + .max().orElseThrow(NoSuchElementException::new); + connectedPaths.removeIf(p->(p.getPathEstats() < bestLikelihood)); + + } else + connectedPaths.removeIf(p->(Math.abs(p.getDeviation()) > Math.abs(bestDiff)+BDGraph.A_TOL)); + }catch(NoSuchElementException e) { + logger.debug("Path ranking error!"); + return false; + } + + if(connectedPaths.size()==1) return true; else @@ -665,7 +701,7 @@ String getId() { * in-between two ends of a bridge. * Used for determine segments */ - class BridgeSteps{ + public class BridgeSteps{ TreeSet nodes; //save the two unique nodes at 2 endings of the bridge. When end is covered more than threshold, pBridge is set BDNodeVecState start, end; @@ -701,7 +737,7 @@ class BridgeSteps{ } //update the pBridge accordingly - if(end!=null && end.qc()) { + if(end!=null && end.qc()>0) { pBridge=new BDEdgePrototype(firstAlg.node, end.getNode(), firstAlg.strand, end.getDirection(firstAlg.strand));//getDirection doesn't work with self-vector }else if(pBridge==null) pBridge=new BDEdgePrototype(firstAlg.node, firstAlg.strand); @@ -736,8 +772,8 @@ private PriorityQueue getInBetweenSteps(BDNodeVecState left, BDN private ArrayList greedyConnect(HashMap> memory, BDNodeVecState left, BDNodeVecState right, boolean greedy){ - if(HybridAssembler.VERBOSE) - LOG.info("\nConnecting " + left + " to " + right + "..."); +// if(HybridAssembler.VERBOSE) +// LOG.info("\nConnecting " + left + " to " + right + "..."); if(left==right) return null; String pairKey=left.toString()+right.toString(); @@ -749,8 +785,8 @@ private ArrayList greedyConnect(HashMap(memory.get(pairKey)); - if(HybridAssembler.VERBOSE) - LOG.info("\nConnecting " + left + " to " + right + ": " + (retval==null?"unreachable!":retval.size()+"-reachable!")); +// if(HybridAssembler.VERBOSE) +// LOG.info("\nConnecting " + left + " to " + right + ": " + (retval==null?"unreachable!":retval.size()+"-reachable!")); return retval; } @@ -759,8 +795,8 @@ private ArrayList greedyConnect(HashMap lBridge, rBridge; while(!inBetween.isEmpty()){ BDNodeVecState mid=inBetween.poll(); - if(HybridAssembler.VERBOSE) - LOG.info("..cut at mid="+mid.toString()); +// if(HybridAssembler.VERBOSE) +// LOG.info("..cut at mid="+mid.toString()); lBridge = greedyConnect(memory, left, mid, greedy); if(lBridge==null||lBridge.isEmpty()){ @@ -805,13 +841,14 @@ private ArrayList greedyConnect(HashMap> memory = new HashMap<>(); ArrayList segs=greedyConnect(memory, startFrom, endAt, force); if(segs==null||segs.isEmpty()){ segments=null; - if(HybridAssembler.VERBOSE) - LOG.info("Failed to connect " + pBridge.toString()); + logger.debug("Failed to connect {}", pBridge.toString()); return false; }else{ segs.stream().forEach(segment->addSegment(segment)); //set pBridge end iff endAt node is original unique node if(SimpleBinner.getBinIfUnique(endAt.dest)!=null){ pBridge.n1=new BDNodeState(endAt.dest, endAt.getDirection(pBridge.getDir0())); - if(HybridAssembler.VERBOSE) - LOG.info("Success to finish " + pBridge.toString()); - } else - if(HybridAssembler.VERBOSE) - LOG.info("Success to extend " + pBridge.toString() + " to " + endAt.dest.getId()); + logger.debug("Success to finish {}", pBridge.toString()); + }else + logger.debug("Success to extend {} to {}", pBridge.toString(), endAt.dest.getId()); return true; } @@ -849,10 +883,20 @@ boolean isIdentifiable(){ } boolean connectable(){ - if(!isIdentifiable()) - return false; + boolean retval=false; + if(isIdentifiable() && start.qc()>0) { + if(end.qc()>0) + retval=true; + else if(start.getScore() >= BDGraph.GOOD_SUPPORT*Alignment.GOOD_QUAL) { + GoInBetweenBridge brg=BDGraph.bridgesMap.get(end.dest.getId()+(end.getDirection(pBridge.getDir0())?"o":"i")); + + if(brg!=null && brg.steps!=null && brg.steps.isIdentifiable() && brg.steps.end.dest.getId().equals(start.dest.getId())) + retval=true; + } + + } - return start.qc() && end.qc(); + return retval; } public void setNodes(TreeSet nodes){ @@ -864,7 +908,12 @@ public void setStart(BDNodeVecState start){ public void setEnd(BDNodeVecState end){ this.end=end; } - + public ScaffoldVector getSpanVector() {//from start->end + if(start==null||end==null) + return null; + else + return ScaffoldVector.composition(end.getVector(), ScaffoldVector.reverse(start.getVector())); + } public String toString(){ String retval=""; for(BDNodeVecState nv:nodes) diff --git a/src/main/java/org/rtassembly/npgraph/GraphUtil.java b/src/main/java/org/rtassembly/npgraph/GraphUtil.java index 06b4678..fe54071 100644 --- a/src/main/java/org/rtassembly/npgraph/GraphUtil.java +++ b/src/main/java/org/rtassembly/npgraph/GraphUtil.java @@ -4,19 +4,22 @@ import java.io.File; import java.io.FileReader; import java.io.IOException; +import java.lang.invoke.MethodHandles; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.Iterator; +import java.util.Scanner; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; import org.apache.commons.io.FilenameUtils; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.graphstream.graph.Edge; import org.graphstream.graph.Node; import org.graphstream.graph.implementations.AbstractNode; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.joptimizer.functions.PDQuadraticMultivariateRealFunction; import com.joptimizer.optimizers.NewtonUnconstrained; @@ -30,7 +33,7 @@ public class GraphUtil { - private static final Logger LOG = LoggerFactory.getLogger(GraphUtil.class); + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); /********************************************************************************** * ****************************Algorithms go from here***************************** */ @@ -167,19 +170,21 @@ public static void loadFromFASTG(String graphFileName, String binFileName, BDGra astats*=Math.log10(Math.E); node.setAttribute("astats", astats); normalizedCoverage(node); - if(HybridAssembler.VERBOSE) - LOG.info("{} Normalized coverage = {} Length = {} \nA-stats = {}", node.getAttribute("name"), node.getNumber("cov"), node.getNumber("len"), astats); + logger.debug("{} Normalized coverage={} Length={} A-stats={}", + node.getAttribute("name"), node.getNumber("cov"), node.getNumber("len"),astats); } - LOG.info("No of nodes= {} No of edges = {} Estimated avg. read coverage = {} (normalized to 100.0) Total contigs length = {}", graph.getNodeCount(), graph.getEdgeCount(), BDGraph.RCOV, totContigsLen ); - + logger.info("Node count = {} Edge count = {} Estimated avg. norm. read coverage = {} Total contigs length = {}", + graph.getNodeCount(), graph.getEdgeCount(), BDGraph.RCOV, totContigsLen ); + if(graph.getNodeCount()>10000 || graph.getEdgeCount()>10000) + BDGraph.IS_COMPLEX=true; /* * 2. Use a binner to estimate graph multiplicity */ // graph.nodes().filter(n->n.getNumber("cov") < .2*BDGraph.RCOV).forEach(n->{n.edges().forEach(e->graph.removeEdge(e));}); - - graph.fixDeadEnds(); + if(!BDGraph.IS_COMPLEX) + graph.fixDeadEnds(); graph.binning(binFileName); /* @@ -189,7 +194,7 @@ public static void loadFromFASTG(String graphFileName, String binFileName, BDGra File graphFile = new File(graphFileName); String pathsFile = FilenameUtils.getFullPathNoEndSeparator(graphFile.getAbsolutePath()) + File.separator + "contigs.paths"; if(! new File(pathsFile).exists()){ - LOG.warn("Path file {} couldn't be found in SPAdes output! Skipped!", pathsFile); + logger.warn("Path file {} not found in SPAdes output!", pathsFile); return; } BufferedReader pathReader = new BufferedReader(new FileReader(pathsFile)); @@ -300,7 +305,7 @@ public static void loadFromGFA(String graphFile, String binFileName, BDGraph gra break; default: - LOG.warn("Unrecognized GFA field: " + type.toUpperCase().trim()); + logger.warn("Unrecognized GFA field: {}", type.toUpperCase().trim()); } @@ -352,17 +357,20 @@ public static void loadFromGFA(String graphFile, String binFileName, BDGraph gra astats*=Math.log10(Math.E); node.setAttribute("astats", astats); normalizedCoverage(node); - if(HybridAssembler.VERBOSE) - LOG.info("{} Normalized coverage = {} Length = {} A-stats = {}", node.getAttribute("name"), node.getNumber("cov"), node.getNumber("len"), astats ); + logger.debug("{} Normalized coverage={} Length={} A-stats={}", + node.getAttribute("name"), node.getNumber("cov"), node.getNumber("len"),astats); } + + logger.info("Node count = {} Edge count = {} Estimated avg. norm. read coverage = {} Total contigs length = {}", + graph.getNodeCount(), graph.getEdgeCount(), BDGraph.RCOV, totContigsLen ); - LOG.info("No of nodes= {} No of edges = {} Estimated avg. read coverage = {} (normalized to 100.0) Total contigs length = {}", graph.getNodeCount(), graph.getEdgeCount(), BDGraph.RCOV, totContigsLen ); - + if(graph.getNodeCount()>10000 || graph.getEdgeCount()>10000) + BDGraph.IS_COMPLEX=true; /* * 2. Binning the graph */ - - graph.fixDeadEnds(); + if(!BDGraph.IS_COMPLEX) + graph.fixDeadEnds(); graph.binning(binFileName); /* @@ -414,7 +422,7 @@ public static boolean isLikelyStillPresented(Node node){ public static void gradientDescent(BDGraph graph) { - int maxIterations=21, + int maxIterations=(BDGraph.IS_COMPLEX?5:21), eIteCount=0, nIteCount=0; double epsilon=.01; while(true) { @@ -462,8 +470,9 @@ public static void gradientDescent(BDGraph graph) { isConverged=false; } - if(curCov<=delta && HybridAssembler.VERBOSE) - LOG.warn("Edge " + e.getId() + " coverage is not positive : curCov=" + curCov + ", delta=" + delta); + if(curCov<=delta) + logger.debug("Edge {} coverage is not positive: curCov={}, delta={}", + e.getId(), curCov, delta); else e.setAttribute("cov", curCov-delta); } @@ -627,8 +636,7 @@ public static void coverageOptimizer(BDGraph graph) { n.setAttribute("cov", newCovEst); } if(isConverged || nIteCount >= 10) { - if(HybridAssembler.VERBOSE) - LOG.info("STOP at iteration " + nIteCount + "th"); + logger.debug("STOP at iteration {}th ", nIteCount); break; } @@ -702,4 +710,183 @@ public static Sequence getQueryReadFromSAMRecord(SAMRecord sam){ return retval; } + /* + * Return sequence of Ns with predefined length + */ + public static Sequence getNSequence(String id, int length) { + Alphabet dna = Alphabet.DNA(); + int nIndex=dna.char2int('N'); +// System.out.println("Convert " + nIndex + " == " +(byte)nIndex); + if(nIndex < 0) + return null; + else{ + byte[] byteSeq = new byte[length]; + Arrays.fill(byteSeq, (byte)nIndex); + return new Sequence(dna, byteSeq, id); + } + } + + // Get Nxx from an array + public static int getNStats(double nxx, int[] list) { + Arrays.sort(list); + double contains = 0; + int i = list.length, sum=Arrays.stream(list).sum(); + while (true){ + if(contains < nxx*sum) + i --; + else + break; + contains += list[i]; + } + return list[i]; + } + + @SuppressWarnings("resource") + public static void promptEnterKey(){ + System.out.println("Press \"ENTER\" to continue..."); + Scanner scanner = new Scanner(System.in); + scanner.nextLine(); + } + + + public static boolean checkMinimap2(String pathToBinary) { + ProcessBuilder pb = new ProcessBuilder(pathToBinary,"-V").redirectErrorStream(true); + Process process; + try { + process = pb.start(); + BufferedReader bf = SequenceReader.openInputStream(process.getInputStream()); + + + String line; + String version = ""; + Pattern versionPattern = Pattern.compile("^(\\d+\\.\\d+).*"); + Matcher matcher=versionPattern.matcher(""); + + while ((line = bf.readLine())!=null){ + matcher.reset(line); + if (matcher.find()){ + version = matcher.group(1); + break;//while + } + + + } + bf.close(); + + if (version.length() == 0){ + logger.error("Command {} -V failed!", pathToBinary); + return false; + }else{ + logger.info("minimap version: {}", version); + if (version.compareTo("2.0") < 0){ + logger.error("Require minimap version 2 or above!"); + return false; + } + } + } catch (IOException e) { + logger.error("Error running: {}\n{}", pathToBinary, e.getMessage()); + return false; + } + + return true; + + } + + public static boolean checkBWA(String pathToBinary) { + try{ + ProcessBuilder pb = new ProcessBuilder(pathToBinary).redirectErrorStream(true); + Process process = pb.start(); + BufferedReader bf = SequenceReader.openInputStream(process.getInputStream()); + + + String line; + String version = ""; + Pattern versionPattern = Pattern.compile("^Version:\\s(\\d+\\.\\d+\\.\\d+).*"); + Matcher matcher=versionPattern.matcher(""); + + while ((line = bf.readLine())!=null){ + matcher.reset(line); + if (matcher.find()){ + version = matcher.group(1); + break;//while + } + + + } + bf.close(); + + if (version.length() == 0){ + logger.error("Command {} doesn't give version info. Check version failed!", pathToBinary); + return false; + }else{ + logger.info("bwa version: {}", version); + if (version.compareTo("0.7.11") < 0){ + logger.error(" Require bwa of 0.7.11 or above"); + return false; + } + } + + }catch (IOException e){ + logger.error("Error running: {}\n{}", pathToBinary, e.getMessage()); + return false; + } + + return true; + + } + + public static boolean checkMSA(String pathToBinary) { + try{ + String[] cmd; + if(pathToBinary.startsWith("kalign")) //maybe kalign3 + cmd=new String[]{"kalign","-h"}; //important, as kalign acted weird without this + else + cmd=new String[]{pathToBinary}; + + ProcessBuilder pb = new ProcessBuilder(cmd).redirectErrorStream(true); + Process process = pb.start(); + BufferedReader bf = SequenceReader.openInputStream(process.getInputStream()); + String line; + boolean found=false; + while ((line = bf.readLine())!=null){ + if (line.toLowerCase().contains("usage:")){ // kalign, kalign3, poa, spoa all print out "Usage:" if run without parameter + found=true; + break; + } + } + bf.close(); + return found; + + }catch (IOException e){ + logger.debug("MSA {} not found: {}", pathToBinary, e.getMessage()); + return false; + } + + } + + public static boolean checkFile(String _path) { + if (_path==null||_path.isEmpty()){ + logger.error("Empty file name!"); + return false; + } + File _file = new File(_path); + if (!_file.isFile()){ + logger.error("File \"{}\" is not valid!", _path); + return false; + } + return true; + } + + public static boolean checkFolder(String _path) { + if (_path.equals("")){ + logger.error("Empty directory \"{}\"", _path); + return false; + } + File _file = new File(_path); + if (!_file.isDirectory()){ + logger.error("Directory \"{}\" is not valid!", _path); + return false; + } + return true; + } } diff --git a/src/main/java/org/rtassembly/npgraph/GraphWatcher.java b/src/main/java/org/rtassembly/npgraph/GraphWatcher.java deleted file mode 100644 index b872925..0000000 --- a/src/main/java/org/rtassembly/npgraph/GraphWatcher.java +++ /dev/null @@ -1,312 +0,0 @@ -package org.rtassembly.npgraph; - -import java.io.IOException; -import java.time.LocalTime; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Set; -import java.util.stream.Collectors; - -import org.graphstream.algorithm.ConnectedComponents; -import org.graphstream.algorithm.ConnectedComponents.ConnectedComponent; -import org.graphstream.graph.Edge; -import org.graphstream.graph.Node; - -import japsa.seq.JapsaAnnotation; -import japsa.seq.Sequence; - -public class GraphWatcher { - public static boolean KEEP=false; //whether or not keeping the low-coverage nodes - BDGraph inputGraph, outputGraph; - ConnectedComponents rtComponents; - HashSet cutEdges; - int numberOfComponents=0; - - public GraphWatcher(BDGraph graph) { - inputGraph=graph; - rtComponents = new ConnectedComponents(); - rtComponents.init(graph); - rtComponents.setCutAttribute("cut"); - numberOfComponents=rtComponents.getConnectedComponentsCount(); - //initial cleaning - removeDeadEdges(); - - } - - //Should be applied for a Collection of Edges, or subgraph instead of the whole graph??? - synchronized void removeDeadEdges(){ - Set cleanedEdges = new HashSet<>(); - while(true){ - cleanedEdges.stream().forEach(e->inputGraph.removeEdge(e)); - cleanedEdges.clear(); - inputGraph.edges().filter(e->checkDeadEdges(e)).forEach(e->cleanedEdges.add(e)); - if(cleanedEdges.isEmpty()) - break; - } - } - - //check and remove edge if it lead to an insignificant component - synchronized private boolean checkDeadEdges(Edge e){ - BDNode src=(BDNode) e.getNode0(), - dst=(BDNode) e.getNode1(); - if(src==dst) - return false; - boolean srcDir=((BDEdge)e).getDir0(), - dstDir=((BDEdge)e).getDir1(); - e.setAttribute("cut"); - if( numberOfComponents1 || (dstDir?dst.getOutDegree():dst.getInDegree())>1) - && - (!isSignificantComponent(rtComponents.getConnectedComponentOf(dst))) || !isSignificantComponent(rtComponents.getConnectedComponentOf(src)) - ){ - return true; - } - e.removeAttribute("cut"); - return false; - } - - synchronized private void removeBadComponents() { - List removeNodes=new ArrayList(); - for (Iterator compIter = rtComponents.iterator(); compIter.hasNext(); ) { - ConnectedComponent comp = compIter.next(); - if(!isSignificantComponent(comp)) - comp.nodes().forEach(n->removeNodes.add(n)); - } - //Remove abundant components here - removeNodes.stream().forEach(n->inputGraph.removeNode(n)); - } - //FIXME: find most significant path and check if it cover >90%? - synchronized private boolean isSignificantComponent(ConnectedComponent comp){ - int clen = 1, tlen=0; - double ccov = 0; - double threshold=KEEP?0:inputGraph.binner.leastBin.estCov; //lower-bound for coverage?! - for(Node n:comp.getNodeSet()){ - if(SimpleBinner.getBinIfUniqueNow(n)!=null) - return true; - - int len = (int) (n.getNumber("len")-BDGraph.getKmerSize()); - double cov = n.getNumber("cov"); - tlen+=len; - if(GraphUtil.isLikelyStillPresented(n)){ - ccov+=cov*len; - clen+=len; - } - - } - double aveCov=ccov/tlen; - if(GraphUtil.approxCompare(aveCov, threshold) < 0 || clen < Math.max(SimpleBinner.ANCHOR_CTG_LEN,.5*tlen) || (!KEEP&&comp.getEdgeCount() < 1)) - return false; - else{ -// System.out.printf("Keep non-unique components with cov=%.2f, clen=%d, tlen=%d\n", aveCov, clen, tlen); - return true; - } - } - /* - * FIXME: output graph updating is significantly slower as the graph getting more complex - */ - synchronized void update(boolean lastTime) { - //cleaning... - if(lastTime) - removeDeadEdges(); - removeBadComponents(); - - cutEdges = new HashSet(); - //then set the cut edges: just for outputGraph stats (will reset after). FIXME: need a Eulerian paths finding iff lastTime - inputGraph.nodes() - .forEach(n->{ - if(n.getInDegree()>=2) - n.enteringEdges().forEach(e->{e.setAttribute("cut");cutEdges.add((BDEdge) e);}); - if(n.getOutDegree()>=2) - n.leavingEdges().forEach(e->{e.setAttribute("cut");cutEdges.add((BDEdge) e);}); - - }); - - - outputGraph=new BDGraph(); - JapsaAnnotation annotation=null; - BDPath repPath=null; //representative path of a component - System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); - System.out.println("Current time: " + LocalTime.now()); - - for (Iterator compIter = rtComponents.iterator(); compIter.hasNext(); ) { - ConnectedComponent comp = compIter.next(); -// System.out.printf("... id=%s edges=%d nodes=%d \n", comp.id, comp.getEdgeCount(), comp.getNodeCount()); - if(comp.getNodeCount()==0) - continue; - //Start analyzing significant components from here - //check comp: should be linear paths, should start with node+ - Node node = comp.nodes().toArray(Node[]::new)[0]; - repPath = new BDPath(node); - boolean isCircular=false; - - if(comp.getEdgeCount()>=1){ - //extend to - Node curNode=node; - boolean curDir=true; - List ways = (curDir?curNode.leavingEdges():curNode.enteringEdges()).filter(e->!e.hasAttribute("cut")).collect(Collectors.toList()); - while(ways.size()==1){ - Edge edge = ways.get(0); - repPath.add(edge); - curNode=edge.getOpposite(curNode); - - if(curNode==node){//circular - isCircular=true; - break; - } - if(((BDEdge) edge).getNodeDirection((BDNode)curNode)!=null) - curDir=!((BDEdge) edge).getNodeDirection((BDNode)curNode); - ways = (curDir?curNode.leavingEdges():curNode.enteringEdges()).filter(e->!e.hasAttribute("cut")).collect(Collectors.toList()); - } - - //if linear: reverse - if(!isCircular){ - repPath=repPath.reverse(); - //extend in opposite direction - curNode=node; - curDir=false; - ways = (curDir?curNode.leavingEdges():curNode.enteringEdges()).filter(e->!e.hasAttribute("cut")).collect(Collectors.toList()); - - while(ways.size()==1){ - Edge edge = ways.get(0); - repPath.add(edge); - curNode=edge.getOpposite(curNode); - if(((BDEdge) edge).getNodeDirection((BDNode)curNode)!=null) - curDir=!((BDEdge) edge).getNodeDirection((BDNode)curNode); - ways = (curDir?curNode.leavingEdges():curNode.enteringEdges()).filter(e->!e.hasAttribute("cut")).collect(Collectors.toList()); - } - } - - } - //now we have repPath - if(lastTime){ - annotation = new JapsaAnnotation(); - } - Sequence seq=repPath.spelling(annotation); - double cov=GraphUtil.getRealCoverage(repPath.averageCov()); - Node n=outputGraph.addNode(Integer.toString(comp.id)); - seq.setName("Contig_"+comp.id+"_"+(isCircular?"circular":"linear")+"_length_"+seq.length()+"_cov_"+cov); - n.setAttribute("seq", seq); - n.setAttribute("len", seq.length()); - n.setAttribute("cov",cov); - n.setAttribute("path", repPath); - - if(isCircular){ - n.setAttribute("circular"); - } - if(lastTime){ - annotation.setSequence(seq); - n.setAttribute("annotation", annotation); - } -// System.out.println("\n" + seq.getName() + ":" + repPath.getId() + "\n=> "+ repPath.getPrimitivePath().getId()); - - } - - - //now set the edge of outputGraph based on the cut edges - for(Edge e:cutEdges) { - Node n0=e.getNode0(), n1=e.getNode1(); - //get corresponding grouped nodes in outputGraph - ConnectedComponent comp0=rtComponents.getConnectedComponentOf(n0), - comp1=rtComponents.getConnectedComponentOf(n1); - if(comp0==null || comp1==null) - continue; - Node nn0=outputGraph.getNode(Integer.toString(comp0.id)), - nn1=outputGraph.getNode(Integer.toString(comp1.id)); - if(nn0!=null && nn1!=null) { - boolean d0=((BDEdge)e).getDir0(), - d1=((BDEdge)e).getDir1(); - //If it consists of a path, should be the direction of the whole path, not a particular node anymore! - if(((BDPath)nn0.getAttribute("path")).getNodeCount()>1) - d0=(n0==((BDPath)nn0.getAttribute("path")).peekNode())?true:false; - - if(((BDPath)nn1.getAttribute("path")).getNodeCount()>1) - d1=(n1==((BDPath)nn1.getAttribute("path")).getRoot())?false:true; - - if(e.hasAttribute("path")){ - BDPath path = (BDPath)e.getAttribute("path"); - BDPath trimedPath=path.trimEndingNodes(); - if(trimedPath!=null){ - //Add the "middle" node - if(lastTime) - annotation = new JapsaAnnotation(); - Sequence seq=trimedPath.spelling(annotation); - double cov=GraphUtil.getRealCoverage(trimedPath.averageCov()); - int id=0; - while(outputGraph.getNode(Integer.toString(++id))!=null); - Node n=outputGraph.addNode(Integer.toString(id)); - seq.setName("Contig_"+id+"_linear_length_"+seq.length()+"_cov_"+cov); - n.setAttribute("seq", seq); - n.setAttribute("len", seq.length()); - n.setAttribute("cov",cov); - n.setAttribute("path", trimedPath); - if(lastTime){ - annotation.setSequence(seq); - n.setAttribute("annotation", annotation); - } - //Add 2 edges - Boolean dd0=((BDEdge)path.getEdgePath().get(0)).getNodeDirection(trimedPath.getFirstNode()), - dd1=((BDEdge)path.peekEdge()).getNodeDirection(trimedPath.getLastNode()); - if(dd0==null) - dd0=!path.getFirstNodeDirection(); - if(dd1==null) - dd1=!path.getLastNodeDirection(); - - //if trimedPath has more than 1 nodes and has been merged into one - if(trimedPath.getNodeCount()>1){ - if(trimedPath.getRoot()==path.getNodePath().get(1)){ - dd0=false; - dd1=true; - }else{ - dd0=true; - dd1=false; - } - } - outputGraph.addEdge((BDNode)nn0, (BDNode)n , d0, dd0); - outputGraph.addEdge((BDNode)n, (BDNode)nn1 , dd1, d1); - } - - }else{ - String id=BDEdge.createID((BDNode)nn0, (BDNode)nn1 , d0, d1); - if(outputGraph.getEdge(id)==null) - outputGraph.addEdge((BDNode)nn0, (BDNode)nn1 , d0, d1); - } - - } - } - outputGraph.updateStats(); - System.out.printf("Output stats: %d sequences (%d circular) N50=%d N75=%d Max=%d\n", getNumberOfSequences(), getNumberOfCircularSequences(), getN50(), getN75(), getLongestContig()); - if(lastTime) - System.out.println("FINISH!"); - //reset the cutting attributes - inputGraph.edges().filter(e->e.hasAttribute("cut")).forEach(e->{e.removeAttribute("cut");}); - - } - - synchronized public int getN50() { - return outputGraph==null?inputGraph.n50:outputGraph.n50; - } - synchronized public int getN75() { - return outputGraph==null?inputGraph.n75:outputGraph.n75; - } - synchronized public int getLongestContig() { - return outputGraph==null?inputGraph.maxl:outputGraph.maxl; - } - synchronized public int getNumberOfSequences() { - return outputGraph==null?inputGraph.numOfCtgs:outputGraph.numOfCtgs; - } - synchronized public int getNumberOfCircularSequences() { - return outputGraph==null?inputGraph.numOfCircularCtgs:outputGraph.numOfCircularCtgs; - } - synchronized public void outputGFA(String fileName) throws IOException{ - inputGraph.outputGFA(fileName); - } - synchronized public void outputFASTA(String fileName) throws IOException { - outputGraph.outputFASTA(fileName); - } - synchronized public void outputJAPSA(String fileName) throws IOException { - outputGraph.outputJAPSA(fileName); - } -} diff --git a/src/main/java/org/rtassembly/npgraph/HybridAssembler.java b/src/main/java/org/rtassembly/npgraph/HybridAssembler.java index 917735e..711a760 100644 --- a/src/main/java/org/rtassembly/npgraph/HybridAssembler.java +++ b/src/main/java/org/rtassembly/npgraph/HybridAssembler.java @@ -2,8 +2,11 @@ import java.io.BufferedReader; import java.io.File; +import java.io.FileReader; import java.io.IOException; +import java.io.InputStreamReader; import java.lang.ProcessBuilder.Redirect; +import java.lang.invoke.MethodHandles; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -12,12 +15,8 @@ import java.util.Date; import java.util.HashSet; import java.util.List; -import java.util.Scanner; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; import htsjdk.samtools.SAMRecord; import htsjdk.samtools.SAMRecordIterator; @@ -25,32 +24,23 @@ import htsjdk.samtools.SamReader; import htsjdk.samtools.SamReaderFactory; import htsjdk.samtools.ValidationStringency; +import japsa.seq.PAFRecord; import japsa.seq.Sequence; -import japsa.seq.SequenceReader; import javafx.beans.property.StringProperty; import javafx.beans.property.SimpleStringProperty; import javafx.beans.property.BooleanProperty; import javafx.beans.property.SimpleBooleanProperty; public class HybridAssembler { - private static final Logger LOG = LoggerFactory.getLogger(HybridAssembler.class); + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); //setting parameter for the GUI private boolean ready=false; - private BooleanProperty overwrite, useSPAdesPath; - private StringProperty prefix, - aligner, - msa, - alignerOpt, - shortReadsInput, - binReadsInput, - longReadsInput, - shortReadsInputFormat, - longReadsInputFormat; - + private BooleanProperty overwrite; + private StringProperty prefix; + public InputData input; Process alignmentProcess = null; private boolean stop=false; - private String checkLog=""; - int currentReadCount = 0; - long currentBaseCount = 0; + public int currentReadCount = 0; + public long currentBaseCount = 0; //Getters and Setters //==============================================================================================// public void setReady(boolean isReady) {ready=isReady;} @@ -61,203 +51,42 @@ public class HybridAssembler { public final boolean getOverwrite() {return overwrite.get();} public BooleanProperty overwriteProperty() {return overwrite;} - public final void setUseSPAdesPath(boolean owr) {useSPAdesPath.set(owr);} - public final boolean getUseSPAdesPath() {return useSPAdesPath.get();} - public BooleanProperty useSPAdesPathProperty() {return useSPAdesPath;} public final void setPrefix(String output) {prefix.set(output);} public final String getPrefix() {return prefix.get();} public StringProperty prefixProperty() {return prefix;} - public final void setAligner(String tool) { aligner.set(tool);} - public final String getAligner() {return aligner.get();} - public StringProperty alignerProperty(){return aligner;} - - public final void setAlignerOpts(String setting) {alignerOpt.set(setting);} - public final String getAlignerOpts() {return alignerOpt.get();} - public StringProperty alignerOptProperty(){return alignerOpt;} - - public final void setMSA(String tool) { msa.set(tool);} - public final String getMSA() {return msa.get();} - public StringProperty msaProperty(){return msa;} - - public final void setCheckLog(String log) {checkLog+="\n"+log;} - public final String getCheckLog() {return checkLog;} - - public final void setBinReadsInput(String brInput) {binReadsInput.set(brInput);} - public final String getBinReadsInput() {return binReadsInput.get();} - public StringProperty binReadsInputProperty() {return binReadsInput;} - - public final void setShortReadsInput(String srInput) { - shortReadsInput.set(srInput); - } - public final String getShortReadsInput() {return shortReadsInput.get();} - public StringProperty shortReadsInputProperty() {return shortReadsInput;} - - public final void setLongReadsInput(String lrInput) { - longReadsInput.set(lrInput); - } - public final String getLongReadsInput() {return longReadsInput.get();} - public StringProperty longReadsInputProperty() {return longReadsInput;} - - public final void setShortReadsInputFormat(String srInputFormat) {shortReadsInputFormat.set(srInputFormat);} - public final String getShortReadsInputFormat() {return shortReadsInputFormat.get();} - public StringProperty shortReadsInputFormatProperty() {return shortReadsInputFormat;} - - public final void setLongReadsInputFormat(String lrInputFormat) { - lrInputFormat=lrInputFormat.toLowerCase(); - if( lrInputFormat.contains("fasta") || lrInputFormat.contains("fa") || lrInputFormat.contains("fna") - || lrInputFormat.contains("fastq") || lrInputFormat.contains("fq")) - longReadsInputFormat.set("fasta/fastq"); - else if(lrInputFormat.contains("sam") || lrInputFormat.contains("bam")) - longReadsInputFormat.set("sam/bam"); - } - public final String getLongReadsInputFormat() {return longReadsInputFormat.get();} - public StringProperty longReadsInputFormatProperty() {return longReadsInputFormat;} - public synchronized void setStopSignal(boolean stop) {this.stop=stop;} public synchronized boolean getStopSignal() {return stop;} //===============================================================================================// //Operational variables -// final BDGraph origGraph; - public BDGraph simGraph; //original and simplified graph should be separated, no??? - public GraphWatcher observer; - public static boolean VERBOSE=false; + public volatile BDGraph simGraph; //original and simplified graph should be separated, no??? + public RealtimeGraphWatcher observer; + public HybridAssembler(){ -// origGraph=new BDGraph("batch"); simGraph=new BDGraph("real"); -// rtComponents = new ConnectedComponents(); simGraph.setAttribute("ui.quality"); simGraph.setAttribute("ui.antialias"); overwrite = new SimpleBooleanProperty(true); - useSPAdesPath = new SimpleBooleanProperty(false); prefix = new SimpleStringProperty(System.getProperty("java.io.tmpdir")); - aligner = new SimpleStringProperty(""); - alignerOpt = new SimpleStringProperty(""); - msa = new SimpleStringProperty("kalign"); - - shortReadsInput = new SimpleStringProperty(""); - binReadsInput = new SimpleStringProperty(""); - longReadsInput = new SimpleStringProperty(""); - shortReadsInputFormat = new SimpleStringProperty(""); - longReadsInputFormat = new SimpleStringProperty(""); - - //set all binding options here... - shortReadsInput.addListener((observable, oldValue, newValue) -> - { - String fn = ((String)observable.getValue()).toLowerCase(); - if( fn.endsWith(".fastg")) - setShortReadsInputFormat("fastg"); - else if(fn.endsWith(".gfa")) - setShortReadsInputFormat("gfa"); - } - - ); - - shortReadsInputFormat.addListener((observable, oldValue, newValue) -> - { - if(!getShortReadsInput().toLowerCase().endsWith(newValue)) - setShortReadsInput(""); - } - - ); - - longReadsInput.addListener( (observable, oldValue, newValue) -> - { - String fn = ((String)observable.getValue()).toLowerCase(); - if( fn.endsWith(".fasta") || fn.endsWith(".fa") || fn.endsWith("fna") - || fn.endsWith(".fastq") || fn.endsWith(".fq") - || fn.endsWith(".fasta.gz") || fn.endsWith(".fa.gz") || fn.endsWith("fna.gz") - || fn.endsWith(".fastq.gz") || fn.endsWith(".fq.gz") - ) - setLongReadsInputFormat("fasta/fastq"); - else if(fn.endsWith(".sam") || fn.endsWith(".bam")) - setLongReadsInputFormat("sam/bam"); - } - ); - - longReadsInputFormat.addListener((observable, oldValue, newValue) -> - { - String oldFile=getLongReadsInput().toLowerCase(); - if(oldFile.equals("-")) - return; - if( newValue.equals("fasta/fastq") - && !oldFile.endsWith(".fasta") && !oldFile.endsWith(".fa") && !oldFile.endsWith("fna") - && !oldFile.endsWith(".fastq") && !oldFile.endsWith(".fq") - && !oldFile.endsWith(".fasta.gz") && !oldFile.endsWith(".fa.gz") && !oldFile.endsWith("fna.gz") - && !oldFile.endsWith(".fastq.gz") && !oldFile.endsWith(".fq.gz") - ) - setLongReadsInput(""); - - if(newValue.equals("sam/bam") && !oldFile.endsWith(".sam") && !oldFile.endsWith(".bam")) - setLongReadsInput(""); - } - - ); - - aligner.addListener( (observable, oldValue, newValue) -> - { - String aligner=(String)observable.getValue(); - if(aligner.toLowerCase().equals("minimap2")) - setAlignerOpts("-t4 -k15 -w5"); - else if (aligner.toLowerCase().equals("bwa")) - setAlignerOpts("-t4 -k11 -W20 -r10 -A1 -B1 -O1 -E1 -L0 -a -Y"); - } - - ); - - - } - - private boolean checkFile(String _path) { - if (_path.equals("")){ - setCheckLog("Empty file \"" + _path + "\""); - return false; - } - File _file = new File(_path); - if (!_file.isFile()){ - setCheckLog("File \"" + _path + "\" is not valid!"); - return false; - } - return true; - } - private boolean checkFolder(String _path) { - if (_path.equals("")){ - setCheckLog("Empty directory \"" + _path + "\""); - return false; - } - File _file = new File(_path); - if (!_file.isDirectory()){ - setCheckLog("Directory \"" + _path + "\" is not valid!"); - return false; - } - return true; - } + + input = new InputData(); + } - //Indexing reference, prepare for alignment...r + //Indexing reference, prepare for alignment... public boolean prepareLongReadsProcess(){ - //accept the case when no long read data is provided. Just output simplified assembly graph then. - if(getLongReadsInput().isEmpty()) { - LOG.info("No long read data is provided. Only output the simplified assembly graph and stop!"); - return true; - } - - if(!getLongReadsInputFormat().equals("fasta/fastq") && !getLongReadsInputFormat().equals("sam/bam")){ - setCheckLog("Please specify a correct format of long read data (FASTA/FASTQ or BAM/SAM)!"); - return false; - } - if(!getLongReadsInput().equals("-") && !checkFile(getLongReadsInput())) + if(!input.validateLongReadInput()) return false; - if(!checkFolder(getPrefix())) + if(!GraphUtil.checkFolder(getPrefix())) return false; try{ System.setProperty("usr.dir", getPrefix()); } catch(NullPointerException | IllegalArgumentException | SecurityException exception ){ - setCheckLog("Fail to set working directory usr.dir to " + getPrefix()); + logger.error("Fail to set working directory usr.dir to " + getPrefix()); return false; } @@ -270,39 +99,38 @@ public boolean prepareLongReadsProcess(){ .map(Path::toFile) .forEach(File::delete); } catch (IOException e) { - LOG.info("Cannot remove existed temporary folder {}!", tmpFolder.getPath()); - e.printStackTrace(); + logger.error("Cannot remove existed temporary folder: {}", e); } } if(!tmpFolder.mkdir()){ - setCheckLog("Cannot set temporary folder " + tmpFolder.getAbsolutePath()); + logger.error("Cannot set temporary folder " + tmpFolder.getAbsolutePath()); return false; }else{ AlignedRead.tmpFolder=tmpFolder.getAbsolutePath(); } //if long reads data not given in SAM/BAM, need to invoke minimap2 - if(getLongReadsInputFormat().toLowerCase().startsWith("fast")) { + if(input.getLongReadsInputFormat().contains("fast")) { File indexFile=null; ArrayList idxCmd = new ArrayList<>(); - idxCmd.add(getAligner()); - if(getAligner().equals("minimap2")) { + idxCmd.add(input.getAligner()); + if(input.getAligner().endsWith("minimap2")) { indexFile=new File(getPrefix()+File.separator+"assembly_graph.mmi"); - if(!checkMinimap2()) + if(!GraphUtil.checkMinimap2(input.getAligner())) return false; - idxCmd.addAll(Arrays.asList(getAlignerOpts().split("\\s"))); + idxCmd.addAll(Arrays.asList(input.getAlignerOpts().split("\\s"))); idxCmd.add("-d"); idxCmd.add(getPrefix()+File.separator+"assembly_graph.mmi"); - }else if(getAligner().equals("bwa")) { + }else if(input.getAligner().endsWith("bwa")) { indexFile=new File(getPrefix()+File.separator+"assembly_graph.fasta.bwt"); - if(!checkBWA()) + if(!GraphUtil.checkBWA(input.getAligner())) return false; idxCmd.add("index"); }else { - setCheckLog("Invalid aligner! Set to BWA or minimap2 please!"); + logger.error("Invalid aligner! Set to BWA or minimap2 please!"); return false; } idxCmd.add(getPrefix()+File.separator+"assembly_graph.fasta"); @@ -316,7 +144,7 @@ public boolean prepareLongReadsProcess(){ indexProcess.waitFor(); }catch (IOException | InterruptedException e){ - setCheckLog("Issue when indexing the pre-assemblies: \n" + e.getMessage()); + logger.error("Issue when indexing the pre-assemblies:\n{}", e); return false; } } @@ -324,90 +152,276 @@ public boolean prepareLongReadsProcess(){ } //check consensus tool - if(!checkMSA()){ - setCheckLog("WARNING: MSA tool (" + getMSA() + ") not found!"); - setMSA("none"); - if(HybridAssembler.VERBOSE) - LOG.info("WARNING: MSA tools not found!"); - }else if(HybridAssembler.VERBOSE) - LOG.info("MSA for consensus calling is set to {}", getMSA()); + if(!GraphUtil.checkMSA(input.getMSA())){ + logger.warn("WARNING: MSA tool \"{}\" not found! Set to none", input.getMSA()); + input.setMSA("none"); + }else + logger.info("MSA for consensus calling is set to {}", input.getMSA()); - simGraph.consensus.setConsensusMSA(getMSA()); + simGraph.consensus.setConsensusMSA(input.getMSA()); return true; } //Loading the graph, doing preprocessing //binning, ... public boolean prepareShortReadsProcess() { - if(!getShortReadsInputFormat().equals("fastg") && !getShortReadsInputFormat().equals("gfa")){ - setCheckLog("Please specify a correct format of graph file!"); - return false; - } - - if(!checkFile(getShortReadsInput())) - return false; - + //try to read input file try { - if(getShortReadsInputFormat().toLowerCase().equals("gfa")) - GraphUtil.loadFromGFA(getShortReadsInput(), getBinReadsInput(), simGraph, getUseSPAdesPath()); - else if(getShortReadsInputFormat().toLowerCase().equals("fastg")) - GraphUtil.loadFromFASTG(getShortReadsInput(), getBinReadsInput(), simGraph, getUseSPAdesPath()); + if(input.getShortReadsInputFormat().toLowerCase().equals("gfa")) + GraphUtil.loadFromGFA(input.getShortReadsInput(), input.getBinReadsInput(), simGraph, input.getUseSPAdesPath()); + else if(input.getShortReadsInputFormat().toLowerCase().equals("fastg")) + GraphUtil.loadFromFASTG(input.getShortReadsInput(), input.getBinReadsInput(), simGraph, input.getUseSPAdesPath()); else throw new IOException("Assembly graph file must have .gfa or .fastg extension!"); }catch(IOException e) { - setCheckLog("Issue when loading pre-assembly: \n" + e.getMessage()); + logger.error("Issue when loading pre-assembly:\n{}", e); return false; } simGraph.updateStats(); - observer = new GraphWatcher(simGraph); + observer = new RealtimeGraphWatcher(this); + + observer.setReadPeriod(RealtimeGraphWatcher.R_INTERVAL); + observer.setTimePeriod(RealtimeGraphWatcher.T_INTERVAL * 1000); + //re-estimate timely report based on graph complexity +// int timeInterval=(int) (Math.round(Math.log10(simGraph.getNodeCount()))-1); //estimated interval time based on graph complexity +// timeInterval=(timeInterval>1?timeInterval:1)*10; + logger.info("The results will be reported every {} seconds and {} reads pass", RealtimeGraphWatcher.T_INTERVAL, RealtimeGraphWatcher.R_INTERVAL); return true; } /** - * SHN modified the default aligner to minimap2 + * Alignment (if needed) running to update the graph in parallel with an observer thread * @throws IOException * @throws InterruptedException */ public void assembly() throws IOException, InterruptedException{ - if(getLongReadsInput().isEmpty()) { - LOG.info("Scaffolding is ignored due to lack of long-read input!"); + + if(input.getLongReadsInput().isEmpty()) { +// LOG.info("Scaffolding is ignored due to lack of long-read input!"); + return; + }else + logger.info("Scaffolding ready at ", new Date()); + + Thread thread = new Thread(observer); + thread.start(); + /*PAF*/ + if (input.getLongReadsInputFormat().endsWith("paf")){//bam or sam + InputStreamReader inputStream=null; + if ("-".equals(input.getLongReadsInput())) + inputStream = new InputStreamReader(System.in); + else + inputStream = new FileReader(input.getLongReadsInput()); + + try(BufferedReader reader=new BufferedReader(inputStream)){ + String readID = ""; + Sequence read = null; + ArrayList hits = new ArrayList();// alignment record of the same read; + PAFRecord curRecord=null; + + String line; + while ((line=reader.readLine()) != null) { + if(getStopSignal()) + break; + + try { + curRecord = new PAFRecord(line); + }catch(Exception e) { + logger.error("Error reading PAF record: \n {}", e); + // continue; + break; + } + + if (curRecord.qual < Alignment.MIN_QUAL){ + logger.debug("Ignore low-quality map record!"); + if (!readID.equals(curRecord.qname)){ + update(read, hits); + hits = new ArrayList(); + readID = curRecord.qname; + read = GraphUtil.getNSequence(curRecord.qname, curRecord.qlen);//there is no read data from PAF, so just fake one! + + } + continue; + } + + String refName = curRecord.tname; + String refID = refName.split("_").length > 1 ? refName.split("_")[1]:refName; + + //check if this node still in. FIXME: do not remove nodes for metagenomics' graph? + if (simGraph.getNode(refID)==null) { + logger.debug("Ignore record with reference {} not found (removed) from the graph!", refID); + if (!readID.equals(curRecord.qname)){ + update(read, hits); + hits = new ArrayList(); + readID = curRecord.qname; + read = GraphUtil.getNSequence(curRecord.qname, curRecord.qlen);//there is no read data from PAF, so just fake one! + + } + continue; + } + Alignment curAlignment = new Alignment(curRecord, (BDNode) simGraph.getNode(refID)); + + ////////////////////////////////////////////////////////////////// + + if (!readID.equals("") && !readID.equals(curRecord.qname)) { + update(read, hits); + hits = new ArrayList(); + readID = curRecord.qname; + read = GraphUtil.getNSequence(curRecord.qname, curRecord.qlen);//there is no read data from PAF, so just fake one! + + } + hits.add(curAlignment); + }// while + + } + /*SAM/BAM/FASTA/FASTQ*/ + }else { + SamReaderFactory.setDefaultValidationStringency(ValidationStringency.SILENT); + SamReader reader = null; + + if (input.getLongReadsInputFormat().endsWith("am")){//bam or sam + if ("-".equals(input.getLongReadsInput())) + reader = SamReaderFactory.makeDefault().open(SamInputResource.of(System.in)); + else + reader = SamReaderFactory.makeDefault().open(new File(input.getLongReadsInput())); + }else{ + logger.info("Starting alignment by {} at {}", input.getAligner(), new Date()); + ProcessBuilder pb = null; + List command = new ArrayList<>(); + command.add(input.getAligner()); + if(input.getAligner().endsWith("minimap2")) { + command.add("-a"); + command.addAll(Arrays.asList(input.getAlignerOpts().split("\\s"))); + command.add("-K20000"); + command.add(getPrefix()+File.separator+"assembly_graph.mmi"); + command.add(input.getLongReadsInput()); + } + else if(input.getAligner().endsWith("bwa")) { + command.add("mem"); + command.addAll(Arrays.asList(input.getAlignerOpts().split("\\s"))); + command.add("-K20000"); + command.add(getPrefix()+File.separator+"assembly_graph.fasta"); + command.add(input.getLongReadsInput()); + } + + if ("-".equals(input.getLongReadsInput())){ + pb = new ProcessBuilder(command).redirectInput(Redirect.INHERIT); + }else{ + pb = new ProcessBuilder(command); + } + + alignmentProcess = pb.redirectError(ProcessBuilder.Redirect.to(new File(getPrefix()+File.separator+"alignment.log"))).start(); + + logger.info("{} started!", input.getAligner()); + + reader = SamReaderFactory.makeDefault().open(SamInputResource.of(alignmentProcess.getInputStream())); + + } + SAMRecordIterator iter = reader.iterator(); + + String readID = ""; + Sequence read = null; + ArrayList hits = new ArrayList();// alignment record of the same read; + SAMRecord curRecord=null; + + while (iter.hasNext()) { + if(getStopSignal()) + break; + + try { + curRecord = iter.next(); + }catch(Exception e) { + logger.error("Error SAM record: \n {}", e); + break; + } + + if (curRecord.getReadUnmappedFlag() || curRecord.getMappingQuality() < Alignment.MIN_QUAL){ + logger.debug("Ignore one unmapped or low-quality map record!"); + if (!readID.equals(curRecord.getReadName())){ + update(read, hits); + hits = new ArrayList(); + read = GraphUtil.getQueryReadFromSAMRecord(curRecord); + readID = curRecord.getReadName(); + } + continue; + } + + String refName = curRecord.getReferenceName(); + String refID = refName.split("_").length > 1 ? refName.split("_")[1]:refName; + + //check if this node still in. FIXME: do not remove nodes for metagenomics' graph? + if (simGraph.getNode(refID)==null) { + logger.debug("Ignore record with reference {} not found (removed) from the graph!", refID); + if (!readID.equals(curRecord.getReadName())){ + update(read, hits); + hits = new ArrayList(); + read = GraphUtil.getQueryReadFromSAMRecord(curRecord); + readID = curRecord.getReadName(); + } + continue; + } + Alignment curAlignment = new Alignment(curRecord, (BDNode) simGraph.getNode(refID)); + + ////////////////////////////////////////////////////////////////// + + if (!readID.equals("") && !readID.equals(curRecord.getReadName())) { + update(read, hits); + hits = new ArrayList(); + read = GraphUtil.getQueryReadFromSAMRecord(curRecord); + readID = curRecord.getReadName(); + + } + hits.add(curAlignment); + }// while + iter.close(); + reader.close(); + } + + + observer.stopWaiting(); + thread.join(); + terminateAlignmentProcess(); + } + + /** + * Version 2 using PAF format instead of SAM/BAM + * @throws IOException + * @throws InterruptedException + */ + public void assemblyPAF() + throws IOException, InterruptedException{ + + if(input.getLongReadsInput().isEmpty()) { + logger.info("Scaffolding is ignored due to lack of long-read input!"); return; }else - LOG.info("Scaffolding ready at {}", new Date()); + logger.info("Scaffolding ready at {}", new Date()); + - SamReaderFactory.setDefaultValidationStringency(ValidationStringency.SILENT); - SamReader reader = null; + InputStreamReader inputStream = null; - if (getLongReadsInputFormat().endsWith("am")){//bam or sam - if ("-".equals(getLongReadsInput())) - reader = SamReaderFactory.makeDefault().open(SamInputResource.of(System.in)); + if (input.getLongReadsInputFormat().endsWith("paf")){//bam or sam + if ("-".equals(input.getLongReadsInput())) + inputStream = new InputStreamReader(System.in); else - reader = SamReaderFactory.makeDefault().open(new File(getLongReadsInput())); - }else{ - LOG.info("Starting alignment by {} at {}", getAligner(), new Date()); + inputStream = new FileReader(input.getLongReadsInput()); + }else if(input.getLongReadsInputFormat().contains("fast")){ + logger.info("Alignment command: {} {} start at {}", input.getAligner(), input.getAlignerOpts(), new Date()); ProcessBuilder pb = null; List command = new ArrayList<>(); - command.add(getAligner()); - if(getAligner().equals("minimap2")) { - command.add("-a"); - command.addAll(Arrays.asList(getAlignerOpts().split("\\s"))); - command.add("-K20000"); - command.add(getPrefix()+File.separator+"assembly_graph.mmi"); - command.add(getLongReadsInput()); - } - else if(getAligner().equals("bwa")) { - command.add("mem"); - command.addAll(Arrays.asList(getAlignerOpts().split("\\s"))); - command.add("-K20000"); - command.add(getPrefix()+File.separator+"assembly_graph.fasta"); - command.add(getLongReadsInput()); - } + command.add(input.getAligner()); +// command.add("-a"); + command.addAll(Arrays.asList(input.getAlignerOpts().split("\\s"))); + command.add("-K20000"); + command.add(getPrefix()+File.separator+"assembly_graph.mmi"); + command.add(input.getLongReadsInput()); + + - if ("-".equals(getLongReadsInput())){ + if ("-".equals(input.getLongReadsInput())){ pb = new ProcessBuilder(command).redirectInput(Redirect.INHERIT); }else{ pb = new ProcessBuilder(command); @@ -415,94 +429,92 @@ else if(getAligner().equals("bwa")) { alignmentProcess = pb.redirectError(ProcessBuilder.Redirect.to(new File(getPrefix()+File.separator+"alignment.log"))).start(); - LOG.info("{} started!", getAligner()); + logger.info("{} started!", input.getAligner()); - reader = SamReaderFactory.makeDefault().open(SamInputResource.of(alignmentProcess.getInputStream())); + inputStream = new InputStreamReader(alignmentProcess.getInputStream()); } - SAMRecordIterator iter = reader.iterator(); - - String readID = ""; - Sequence read = null; - ArrayList samList = new ArrayList();// alignment record of the same read; - SAMRecord curRecord=null; - while (iter.hasNext()) { - if(getStopSignal()) - break; - - try { - curRecord = iter.next(); - }catch(Exception e) { - if(HybridAssembler.VERBOSE) { - LOG.info("Ignore one faulty SAM record: \n {}", e.getMessage()); - e.printStackTrace(); - } - continue; - } - - if (curRecord.getReadUnmappedFlag() || curRecord.getMappingQuality() < Alignment.MIN_QUAL){ - if(HybridAssembler.VERBOSE) - LOG.info("Ignore one unmapped or low-quality map record!"); - if (!readID.equals(curRecord.getReadName())){ - update(read, samList, curRecord); - samList = new ArrayList(); - read = GraphUtil.getQueryReadFromSAMRecord(curRecord); - readID = curRecord.getReadName(); - } - continue; - } - - String refName = curRecord.getReferenceName(); - String refID = refName.split("_").length > 1 ? refName.split("_")[1]:refName; + + try(BufferedReader reader=new BufferedReader(inputStream)){ + String readID = ""; + Sequence read = null; + ArrayList hits = new ArrayList();// alignment record of the same read; + PAFRecord curRecord=null; - //check if this node still in. FIXME: do not remove nodes for metagenomics' graph? - if (simGraph.getNode(refID)==null) { - if(HybridAssembler.VERBOSE) - LOG.info("Ignore record with reference {} not found (removed) from the graph!", refID); - if (!readID.equals(curRecord.getReadName())){ - update(read, samList, curRecord); - samList = new ArrayList(); - read = GraphUtil.getQueryReadFromSAMRecord(curRecord); - readID = curRecord.getReadName(); + Thread thread = new Thread(observer); + thread.start(); + String line; + while ((line=reader.readLine()) != null) { + if(getStopSignal()) + break; + + try { + curRecord = new PAFRecord(line); + }catch(Exception e) { + logger.error("Error reading PAF record: \n {}", e); + // continue; + break; } - continue; - } - Alignment curAlignment = new Alignment(curRecord, (BDNode) simGraph.getNode(refID)); + + if (curRecord.qual < Alignment.MIN_QUAL){ + logger.debug("Ignore low-quality map record!"); + if (!readID.equals(curRecord.qname)){ + update(read, hits); + hits = new ArrayList(); + readID = curRecord.qname; + read = GraphUtil.getNSequence(curRecord.qname, curRecord.qlen);//there is no read data from PAF, so just fake one! - ////////////////////////////////////////////////////////////////// - - if (!readID.equals("") && !readID.equals(curRecord.getReadName())) { - update(read, samList, curRecord); - samList = new ArrayList(); - read = GraphUtil.getQueryReadFromSAMRecord(curRecord); - readID = curRecord.getReadName(); + } + continue; + } + + String refName = curRecord.tname; + String refID = refName.split("_").length > 1 ? refName.split("_")[1]:refName; + + //check if this node still in. FIXME: do not remove nodes for metagenomics' graph? + if (simGraph.getNode(refID)==null) { + logger.debug("Ignore record with reference {} not found (removed) from the graph!", refID); + if (!readID.equals(curRecord.qname)){ + update(read, hits); + hits = new ArrayList(); + readID = curRecord.qname; + read = GraphUtil.getNSequence(curRecord.qname, curRecord.qlen);//there is no read data from PAF, so just fake one! - } - samList.add(curAlignment); - }// while - iter.close(); - reader.close(); + } + continue; + } + Alignment curAlignment = new Alignment(curRecord, (BDNode) simGraph.getNode(refID)); + + ////////////////////////////////////////////////////////////////// + + if (!readID.equals("") && !readID.equals(curRecord.qname)) { + update(read, hits); + hits = new ArrayList(); + readID = curRecord.qname; + read = GraphUtil.getNSequence(curRecord.qname, curRecord.qlen);//there is no read data from PAF, so just fake one! - terminateAlignmentProcess(); + } + hits.add(curAlignment); + }// while + + observer.stopWaiting(); + thread.join(); + terminateAlignmentProcess(); + } } - //update when SAMRecord of another read coming in - synchronized void update(Sequence nnpRead, ArrayList alignments, SAMRecord curRecord){ - currentReadCount ++; - currentBaseCount += curRecord.getReadLength(); - if(alignments.isEmpty()) + + //update when more read alignments coming in + synchronized void update(Sequence nnpRead, ArrayList alignments){ + if(alignments.isEmpty() || nnpRead==null) return; + + currentReadCount ++; + currentBaseCount += nnpRead.length(); + List paths=simGraph.uniqueBridgesFinding(nnpRead, alignments); - if(paths!=null){ - for(BDPath path:paths) - { - //path here is already unique! (2 unique ending nodes) - if(simGraph.reduceUniquePath(path)) { - observer.update(false); - System.out.printf("Input stats: read count=%d base count=%d\n", currentReadCount, currentBaseCount); - } - } - } + if(paths!=null) + paths.stream().forEach(p->simGraph.reduceUniquePath(p)); } public void terminateAlignmentProcess() { @@ -512,7 +524,8 @@ public void terminateAlignmentProcess() { } - //last attempt to connect bridges, being greedy now + @Deprecated + //last attempt to connect bridges greedily. Now move to RealtimeGraphWatcher public void postProcessGraph() throws IOException{ System.out.printf("Post-processing the graph by greedy path-finding algorithm. Please wait...\n"); HashSet unsolved=simGraph.getUnsolvedBridges(), @@ -520,8 +533,8 @@ public void postProcessGraph() throws IOException{ while(true){ boolean changed=false; for(GoInBetweenBridge brg:unsolved){ - if(HybridAssembler.VERBOSE) - LOG.info("Last attempt on incomplete bridge %s : anchors=%d \n %s", brg.getEndingsID(), brg.getNumberOfAnchors(), brg.getAllPossiblePaths()); + logger.debug("Last attempt on incomplete bridge {} : anchors={}\n{}", + brg.getEndingsID(), brg.getNumberOfAnchors(), brg.getAllPossiblePaths()); //Take the current best path among the candidate of a bridge and connect the bridge(greedy) if(brg.getCompletionLevel()>=3){ simGraph.getNewSubPathsToReduce(brg.getBestPath(brg.pBridge.getNode0(),brg.pBridge.getNode1())).stream().forEach(p->simGraph.reduceUniquePath(p)); @@ -536,8 +549,8 @@ public void postProcessGraph() throws IOException{ simGraph.getNewSubPathsToReduce(brg.getBestPath(brg.steps.start.getNode(),brg.steps.end.getNode())).stream().forEach(p->simGraph.reduceUniquePath(p)); solved.add(brg); } - else if(HybridAssembler.VERBOSE) - LOG.info("Last attempt failed \n"); + else + logger.debug("Last attempt failed!"); } } @@ -555,7 +568,8 @@ else if(HybridAssembler.VERBOSE) observer.outputFASTA(getPrefix()+File.separator+"npgraph_assembly.fasta"); observer.outputJAPSA(getPrefix()+File.separator+"npgraph_assembly.japsa"); - observer.outputGFA(getPrefix()+File.separator+"npgraph_assembly.gfa"); + observer.outputAssGFA(getPrefix()+File.separator+"npgraph_assembly.gfa"); + observer.outputOrigGFA(getPrefix()+File.separator+"npgraph_components.gfa"); //delete temporary files @@ -573,144 +587,19 @@ else if(HybridAssembler.VERBOSE) // } } - - - @SuppressWarnings("resource") - public static void promptEnterKey(){ - LOG.info("Press \"ENTER\" to continue..."); - Scanner scanner = new Scanner(System.in); - scanner.nextLine(); - } - - - public boolean checkMinimap2() { - ProcessBuilder pb = new ProcessBuilder(getAligner(),"-V").redirectErrorStream(true); - Process process; - try { - process = pb.start(); - BufferedReader bf = SequenceReader.openInputStream(process.getInputStream()); - - - String line; - String version = ""; - Pattern versionPattern = Pattern.compile("^(\\d+\\.\\d+).*"); - Matcher matcher=versionPattern.matcher(""); - - while ((line = bf.readLine())!=null){ - matcher.reset(line); - if (matcher.find()){ - version = matcher.group(1); - break;//while - } - - - } - bf.close(); - - if (version.length() == 0){ - setCheckLog("Command " + getAligner() + " -V failed!"); - return false; - }else{ - LOG.info("minimap version: " + version); - if (version.compareTo("2.0") < 0){ - setCheckLog("Require minimap version 2 or above!"); - return false; - } - } - } catch (IOException e) { - setCheckLog("Error running: " + getAligner() + "\n" + e.getMessage()); - return false; - } - - return true; - - } - - public boolean checkBWA() { - try{ - ProcessBuilder pb = new ProcessBuilder(getAligner()).redirectErrorStream(true); - Process process = pb.start(); - BufferedReader bf = SequenceReader.openInputStream(process.getInputStream()); - - - String line; - String version = ""; - Pattern versionPattern = Pattern.compile("^Version:\\s(\\d+\\.\\d+\\.\\d+).*"); - Matcher matcher=versionPattern.matcher(""); - - while ((line = bf.readLine())!=null){ - matcher.reset(line); - if (matcher.find()){ - version = matcher.group(1); - break;//while - } - - - } - bf.close(); - - if (version.length() == 0){ - setCheckLog("Command " + getAligner() + " doesn't give version info. Check version failed!"); - return false; - }else{ - LOG.info("bwa version: " + version); - if (version.compareTo("0.7.11") < 0){ - setCheckLog(" Require bwa of 0.7.11 or above"); - return false; - } - } - - }catch (IOException e){ - setCheckLog("Error running: " + getAligner() + "\n" + e.getMessage()); - return false; - } - - return true; - - } - - public boolean checkMSA() { - try{ - String[] cmd; - if(getMSA().startsWith("kalign")) //maybe kalign3 - cmd=new String[]{"kalign","-h"}; //important, as kalign acted weird without this - else - cmd=new String[]{getMSA()}; - - ProcessBuilder pb = new ProcessBuilder(cmd).redirectErrorStream(true); - Process process = pb.start(); - BufferedReader bf = SequenceReader.openInputStream(process.getInputStream()); - String line; - boolean found=false; - while ((line = bf.readLine())!=null){ - if (line.toLowerCase().contains("usage:")){ // kalign, kalign3, poa, spoa all print out "Usage:" if run without parameter - found=true; - break; - } - } - bf.close(); - return found; - - }catch (IOException e){ - setCheckLog("Error running: " + getMSA() + "\n" + e.getMessage()); - return false; - } - - } - - public static void main(String[] argv) throws IOException, InterruptedException{ - HybridAssembler hbAss = new HybridAssembler(); - - hbAss.setShortReadsInput("/home/sonhoanghguyen/Projects/scaffolding/data/spades_3.7/EcK12S-careful/assembly_graph.fastg"); - hbAss.setShortReadsInputFormat("fastg"); - hbAss.prepareShortReadsProcess(); - hbAss.setLongReadsInput("/home/sonhoanghguyen/Projects/scaffolding/data/spades_3.7/EcK12S-careful/assembly_graph.sam"); - hbAss.setLongReadsInputFormat("sam/bam"); - hbAss.prepareLongReadsProcess(); - hbAss.assembly(); - - } +// public static void main(String[] argv) throws IOException, InterruptedException{ +// HybridAssembler hbAss = new HybridAssembler(); +// +// hbAss.input.setShortReadsInput("/home/sonhoanghguyen/Projects/scaffolding/data/spades_3.7/EcK12S-careful/assembly_graph.fastg"); +// hbAss.input.setShortReadsInputFormat("fastg"); +// hbAss.prepareShortReadsProcess(); +// hbAss.input.setLongReadsInput("/home/sonhoanghguyen/Projects/scaffolding/data/spades_3.7/EcK12S-careful/assembly_graph.sam"); +// hbAss.input.setLongReadsInputFormat("sam/bam"); +// hbAss.prepareLongReadsProcess(); +// hbAss.assembly(); +// +// } } diff --git a/src/main/java/org/rtassembly/npgraph/InputData.java b/src/main/java/org/rtassembly/npgraph/InputData.java new file mode 100644 index 0000000..a86ac8a --- /dev/null +++ b/src/main/java/org/rtassembly/npgraph/InputData.java @@ -0,0 +1,215 @@ +package org.rtassembly.npgraph; + +import java.lang.invoke.MethodHandles; + +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javafx.beans.property.BooleanProperty; +import javafx.beans.property.SimpleBooleanProperty; +import javafx.beans.property.SimpleStringProperty; +import javafx.beans.property.StringProperty; + +public class InputData { + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); + + public ShortReadInputs srIn; + public LongReadInputs lrIn; + + public InputData() { + srIn=new ShortReadInputs(); + lrIn=new LongReadInputs(); + } + public boolean validateShortReadInput() {return srIn.validate();} + public boolean validateLongReadInput() {return lrIn.validate();} + + public final void setUseSPAdesPath(boolean owr) {srIn.useSPAdesPath.set(owr);} + public final boolean getUseSPAdesPath() {return srIn.useSPAdesPath.get();} + public BooleanProperty useSPAdesPathProperty() {return srIn.useSPAdesPath;} + + public final void setAligner(String tool) { lrIn.aligner.set(tool);} + public final String getAligner() {return lrIn.aligner.get();} + public StringProperty alignerProperty(){return lrIn.aligner;} + + public final void setAlignerOpts(String setting) {lrIn.alignerOpt.set(setting);} + public final String getAlignerOpts() {return lrIn.alignerOpt.get();} + public StringProperty alignerOptProperty(){return lrIn.alignerOpt;} + + public final void setMSA(String tool) { lrIn.msa.set(tool);} + public final String getMSA() {return lrIn.msa.get();} + public StringProperty msaProperty(){return lrIn.msa;} + + public final void setBinReadsInput(String brInput) {srIn.binReadsInput.set(brInput);} + public final String getBinReadsInput() {return srIn.binReadsInput.get();} + public StringProperty binReadsInputProperty() {return srIn.binReadsInput;} + + public final void setShortReadsInput(String srInput) {srIn.shortReadsInput.set(srInput);} + public final String getShortReadsInput() {return srIn.shortReadsInput.get();} + public StringProperty shortReadsInputProperty() {return srIn.shortReadsInput;} + + public final void setLongReadsInput(String lrInput) {lrIn.longReadsInput.set(lrInput); + } + public final String getLongReadsInput() {return lrIn.longReadsInput.get();} + public StringProperty longReadsInputProperty() {return lrIn.longReadsInput;} + + public final void setShortReadsInputFormat(String srInputFormat) {srIn.shortReadsInputFormat.set(srInputFormat);} + public final String getShortReadsInputFormat() {return srIn.shortReadsInputFormat.get();} + public StringProperty shortReadsInputFormatProperty() {return srIn.shortReadsInputFormat;} + + public final void setLongReadsInputFormat(String lrInputFormat) { + lrInputFormat=lrInputFormat.toLowerCase(); + if( lrInputFormat.contains("fasta") || lrInputFormat.contains("fa") || lrInputFormat.contains("fna") + || lrInputFormat.contains("fastq") || lrInputFormat.contains("fq")) + lrIn.longReadsInputFormat.set("fasta/fastq"); + else if(lrInputFormat.contains("sam") || lrInputFormat.contains("bam")) + lrIn.longReadsInputFormat.set("sam/bam"); + else if(lrInputFormat.contains("paf")) + lrIn.longReadsInputFormat.set("paf"); + } + public final String getLongReadsInputFormat() {return lrIn.longReadsInputFormat.get();} + public StringProperty longReadsInputFormatProperty() {return lrIn.longReadsInputFormat;} + + + /* + * Class represent input data regarding Illumina short-read + */ + class ShortReadInputs{ + private BooleanProperty useSPAdesPath; + private StringProperty shortReadsInput, + binReadsInput, + shortReadsInputFormat; + ShortReadInputs(){ + useSPAdesPath=new SimpleBooleanProperty(false); + shortReadsInput=new SimpleStringProperty(""); + binReadsInput=new SimpleStringProperty(""); + shortReadsInputFormat=new SimpleStringProperty(""); + + shortReadsInput.addListener((observable, oldValue, newValue) -> + { + String fn = ((String)observable.getValue()).toLowerCase(); + if( fn.endsWith(".fastg")) + setShortReadsInputFormat("fastg"); + else if(fn.endsWith(".gfa")) + setShortReadsInputFormat("gfa"); + } + + ); + + shortReadsInputFormat.addListener((observable, oldValue, newValue) -> + { + if(!getShortReadsInput().toLowerCase().endsWith(newValue)) + setShortReadsInput(""); + } + + ); + } + + boolean validate() { + if(!getShortReadsInputFormat().equals("fastg") && !getShortReadsInputFormat().equals("gfa")){ + logger.error("Please specify a correct format of graph file!"); + return false; + } + + if(!GraphUtil.checkFile(getShortReadsInput())) + return false; + + return true; + } + } + /* + * Class represent input data involving ONT long-read + */ + class LongReadInputs{ + private StringProperty aligner, + alignerOpt, + msa, + longReadsInput, + longReadsInputFormat; + LongReadInputs(){ + aligner=new SimpleStringProperty(""); + alignerOpt=new SimpleStringProperty(""); + msa=new SimpleStringProperty(""); + longReadsInput=new SimpleStringProperty(""); + longReadsInputFormat=new SimpleStringProperty(""); + + longReadsInput.addListener( (observable, oldValue, newValue) -> + { + String fn = ((String)observable.getValue()).toLowerCase(); + if( fn.endsWith(".fasta") || fn.endsWith(".fa") || fn.endsWith("fna") + || fn.endsWith(".fastq") || fn.endsWith(".fq") + || fn.endsWith(".fasta.gz") || fn.endsWith(".fa.gz") || fn.endsWith("fna.gz") + || fn.endsWith(".fastq.gz") || fn.endsWith(".fq.gz") + ) + setLongReadsInputFormat("fasta/fastq"); + else if(fn.endsWith(".sam") || fn.endsWith(".bam")) + setLongReadsInputFormat("sam/bam"); + else if(fn.endsWith(".paf")) + setLongReadsInputFormat("paf"); + } + ); + + longReadsInputFormat.addListener((observable, oldValue, newValue) -> + { + String oldFile=getLongReadsInput().toLowerCase(); + if(oldFile.equals("-")) + return; + if( newValue.equals("fasta/fastq") + && !oldFile.endsWith(".fasta") && !oldFile.endsWith(".fa") && !oldFile.endsWith("fna") + && !oldFile.endsWith(".fastq") && !oldFile.endsWith(".fq") + && !oldFile.endsWith(".fasta.gz") && !oldFile.endsWith(".fa.gz") && !oldFile.endsWith("fna.gz") + && !oldFile.endsWith(".fastq.gz") && !oldFile.endsWith(".fq.gz") + ) + setLongReadsInput(""); + + if(newValue.equals("sam/bam") && !oldFile.endsWith(".sam") && !oldFile.endsWith(".bam")) + setLongReadsInput(""); + + if(newValue.equals("paf") && !oldFile.endsWith(".paf")) + setLongReadsInput(""); + } + + ); + + aligner.addListener( (observable, oldValue, newValue) -> + { + String aligner=(String)observable.getValue(); + if(aligner.toLowerCase().endsWith("minimap2")) + setAlignerOpts("-t4 -k15 -w5"); + else if (aligner.toLowerCase().endsWith("bwa")) + setAlignerOpts("-t4 -k11 -W20 -r10 -A1 -B1 -O1 -E1 -L0 -a -Y"); + } + + ); + } + + boolean validate() { + //accept the case when no long read data is provided. Just output simplified assembly graph then. + if(getLongReadsInput().isEmpty()) { + logger.warn("No long read data is provided. Only output the simplified assembly graph and stop!"); + return true; + } + + //long-read file format must be known if provided + if (getLongReadsInput().isEmpty()) { + logger.warn("Please specify the long-read input format!"); + return false; + }else if(getLongReadsInputFormat().equals("fasta/fastq")){ + if(getAligner().isEmpty() || getAlignerOpts().isEmpty()) { + logger.warn("Please specify aligner and/or alignment arguments!"); + return false; + } + }else if(!getLongReadsInputFormat().equals("sam/bam") + && !getLongReadsInputFormat().equals("paf") ){ + logger.error("Please specify a correct alignment format (BAM/SAM/PAF)!"); + return false; + } + + //check file valid if not input from stdin + if(!getLongReadsInput().equals("-") && !GraphUtil.checkFile(getLongReadsInput())) + return false; + + + return true; + } + } +} \ No newline at end of file diff --git a/src/main/java/org/rtassembly/npgraph/RealtimeGraphWatcher.java b/src/main/java/org/rtassembly/npgraph/RealtimeGraphWatcher.java new file mode 100644 index 0000000..2fcd51d --- /dev/null +++ b/src/main/java/org/rtassembly/npgraph/RealtimeGraphWatcher.java @@ -0,0 +1,471 @@ +package org.rtassembly.npgraph; + +import java.io.File; +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.graphstream.algorithm.ConnectedComponents; +import org.graphstream.algorithm.ConnectedComponents.ConnectedComponent; +import org.graphstream.graph.Edge; +import org.graphstream.graph.Node; + +import japsa.bio.np.RealtimeAnalysis; +import japsa.seq.JapsaAnnotation; +import japsa.seq.Sequence; + +public class RealtimeGraphWatcher extends RealtimeAnalysis{ + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); + + public static boolean KEEP=false; //whether or not keeping the low-coverage nodes + public static int R_INTERVAL, T_INTERVAL; + BDGraph outputGraph; + final ConnectedComponents rtComponents; + HashSet cutEdges; + int numberOfComponents=0; + final HybridAssembler hAss; + HashMap> binn50; + + public RealtimeGraphWatcher(HybridAssembler hAss) { + this.hAss=hAss; + outputGraph=hAss.simGraph; + rtComponents = new ConnectedComponents(); + rtComponents.init(hAss.simGraph); + rtComponents.setCutAttribute("cut"); + numberOfComponents=rtComponents.getConnectedComponentsCount(); + //initial cleaning + removeDeadEdges(); + + } + + //Should be applied for a Collection of Edges, or subgraph instead of the whole graph??? + private void removeDeadEdges(){ + synchronized(hAss.simGraph) { + Set cleanedEdges = new HashSet<>(); + while(true){ + cleanedEdges.stream().forEach(e->hAss.simGraph.removeEdge(e)); + cleanedEdges.clear(); + hAss.simGraph.edges().filter(e->checkDeadEdges(e)).forEach(e->cleanedEdges.add(e)); + if(cleanedEdges.isEmpty()) + break; + else + hAss.simGraph.notifyChanges(); + } + } + } + + //check and remove edge if it lead to an insignificant component + private boolean checkDeadEdges(Edge e){ + BDNode src=(BDNode) e.getNode0(), + dst=(BDNode) e.getNode1(); + if(src==dst) + return false; + boolean srcDir=((BDEdge)e).getDir0(), + dstDir=((BDEdge)e).getDir1(); + e.setAttribute("cut"); + if( numberOfComponents1 || (dstDir?dst.getOutDegree():dst.getInDegree())>1) + && + (!isSignificantComponent(rtComponents.getConnectedComponentOf(dst))) || !isSignificantComponent(rtComponents.getConnectedComponentOf(src)) + ){ + return true; + } + e.removeAttribute("cut"); + return false; + } + + private void removeBadComponents() { +// synchronized(hAss.simGraph) { + synchronized(rtComponents) { + List removeNodes=new ArrayList(); + for (Iterator compIter = rtComponents.iterator(); compIter.hasNext(); ) { + ConnectedComponent comp = compIter.next(); + if(!isSignificantComponent(comp)) + comp.nodes().forEach(n->removeNodes.add(n)); + } + //Remove abundant components here + removeNodes.stream().forEach(n->hAss.simGraph.removeNode(n)); +// } + } + } + //FIXME: find most significant path and check if it cover >90%? + private boolean isSignificantComponent(ConnectedComponent comp){ + synchronized(comp) { + int clen = 1, tlen=0; + double ccov = 0; + double threshold=KEEP?0:hAss.simGraph.binner.leastBin.estCov; //lower-bound for coverage?! + for(Node n:comp.getNodeSet()){ + if(SimpleBinner.getBinIfUniqueNow(n)!=null) + return true; + + int len = (int) (n.getNumber("len")-BDGraph.getKmerSize()); + double cov = n.getNumber("cov"); + tlen+=len; + if(GraphUtil.isLikelyStillPresented(n)){ + ccov+=cov*len; + clen+=len; + } + + } + double aveCov=ccov/tlen; + if(GraphUtil.approxCompare(aveCov, threshold) < 0 || clen < Math.max(SimpleBinner.ANCHOR_CTG_LEN,.5*tlen) || (!KEEP&&comp.getEdgeCount() < 1)) + return false; + else{ + // System.out.printf("Keep non-unique components with cov=%.2f, clen=%d, tlen=%d\n", aveCov, clen, tlen); + return true; + } + } + } + /* + * FIXME: output graph updating is significantly slower as the graph getting more complex + */ + void update(boolean lastTime) { + synchronized(hAss.simGraph) { + //cleaning... + if(lastTime) + removeDeadEdges(); + if(hAss.simGraph.checkForChangesAndReset()){ + removeBadComponents(); +// hAss.simGraph.updateStats(); + + cutEdges = new HashSet(); + //then set the cut edges: just for outputGraph stats (will reset after). FIXME: need a Eulerian paths finding iff lastTime + hAss.simGraph.nodes() + .forEach(n->{ + if(n.getInDegree()>=2) + n.enteringEdges().forEach(e->{e.setAttribute("cut");cutEdges.add((BDEdge) e);}); + if(n.getOutDegree()>=2) + n.leavingEdges().forEach(e->{e.setAttribute("cut");cutEdges.add((BDEdge) e);}); + + }); + + + outputGraph=new BDGraph(); + JapsaAnnotation annotation=null; + BDPath repPath=null; //representative path of a component + System.out.println("+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); + System.out.println("Current time: " + LocalTime.now()); + + binn50 = new HashMap<>(); + PopBin b=null; + synchronized(rtComponents) { + for (Iterator compIter = rtComponents.iterator(); compIter.hasNext(); ) { + ConnectedComponent comp = compIter.next(); + // System.out.printf("... id=%s edges=%d nodes=%d \n", comp.id, comp.getEdgeCount(), comp.getNodeCount()); + if(comp.getNodeCount()==0) + continue; + //Start analyzing significant components from here + //check comp: should be linear paths, should start with node+ + Node node = comp.nodes().toArray(Node[]::new)[0]; + repPath = new BDPath(node); + boolean isCircular=false; + + if(comp.getEdgeCount()>=1){ + //extend to + Node curNode=node; + boolean curDir=true; + List ways = (curDir?curNode.leavingEdges():curNode.enteringEdges()).filter(e->!e.hasAttribute("cut")).collect(Collectors.toList()); + while(ways.size()==1){ + Edge edge = ways.get(0); + repPath.add(edge); + curNode=edge.getOpposite(curNode); + + if(curNode==node){//circular + isCircular=true; + break; + } + if(((BDEdge) edge).getNodeDirection((BDNode)curNode)!=null) + curDir=!((BDEdge) edge).getNodeDirection((BDNode)curNode); + ways = (curDir?curNode.leavingEdges():curNode.enteringEdges()).filter(e->!e.hasAttribute("cut")).collect(Collectors.toList()); + } + + //if linear: reverse + if(!isCircular){ + repPath=repPath.reverse(); + //extend in opposite direction + curNode=node; + curDir=false; + ways = (curDir?curNode.leavingEdges():curNode.enteringEdges()).filter(e->!e.hasAttribute("cut")).collect(Collectors.toList()); + + while(ways.size()==1){ + Edge edge = ways.get(0); + repPath.add(edge); + curNode=edge.getOpposite(curNode); + if(((BDEdge) edge).getNodeDirection((BDNode)curNode)!=null) + curDir=!((BDEdge) edge).getNodeDirection((BDNode)curNode); + ways = (curDir?curNode.leavingEdges():curNode.enteringEdges()).filter(e->!e.hasAttribute("cut")).collect(Collectors.toList()); + } + } + + } + //now we have repPath + if(lastTime){ + annotation = new JapsaAnnotation(); + } + Sequence seq=repPath.spelling(annotation); + + double cov=GraphUtil.getRealCoverage(repPath.averageCov()); + Node n=outputGraph.addNode(Integer.toString(comp.id)); + seq.setName("Contig_"+comp.id+"_"+(isCircular?"circular":"linear")+"_length_"+seq.length()+"_cov_"+cov); + n.setAttribute("seq", seq); + n.setAttribute("len", seq.length()); + n.setAttribute("cov",cov); + n.setAttribute("path", repPath); + + b=SimpleBinner.getBinOfPath(repPath); + if(b!=null) { + n.setAttribute("bin", b); + if(!binn50.containsKey(b)) + binn50.put(b, new ArrayList()); + else + binn50.get(b).add(seq.length()); + } + + + if(isCircular){ + n.setAttribute("circular"); + } + if(lastTime){ + annotation.setSequence(seq); + n.setAttribute("annotation", annotation); + } + // System.out.println("\n" + seq.getName() + ":" + repPath.getId() + "\n=> "+ repPath.getPrimitivePath().getId()); + + } + } + + //now set the edge of outputGraph based on the cut edges + for(Edge e:cutEdges) { + Node n0=e.getNode0(), n1=e.getNode1(); + //get corresponding grouped nodes in outputGraph + ConnectedComponent comp0=rtComponents.getConnectedComponentOf(n0), + comp1=rtComponents.getConnectedComponentOf(n1); + if(comp0==null || comp1==null) + continue; + Node nn0=outputGraph.getNode(Integer.toString(comp0.id)), + nn1=outputGraph.getNode(Integer.toString(comp1.id)); + if(nn0!=null && nn1!=null) { + boolean d0=((BDEdge)e).getDir0(), + d1=((BDEdge)e).getDir1(); + //If it consists of a path, should be the direction of the whole path, not a particular node anymore! + if(((BDPath)nn0.getAttribute("path")).getNodeCount()>1) + d0=(n0==((BDPath)nn0.getAttribute("path")).peekNode())?true:false; + + if(((BDPath)nn1.getAttribute("path")).getNodeCount()>1) + d1=(n1==((BDPath)nn1.getAttribute("path")).getRoot())?false:true; + + if(e.hasAttribute("path")){ + BDPath path = (BDPath)e.getAttribute("path"); + BDPath trimedPath=path.trimEndingNodes(); + if(trimedPath!=null){ + //Add the "middle" node + if(lastTime) + annotation = new JapsaAnnotation(); + Sequence seq=trimedPath.spelling(annotation); + double cov=GraphUtil.getRealCoverage(trimedPath.averageCov()); + int id=0; + while(outputGraph.getNode(Integer.toString(++id))!=null); + Node n=outputGraph.addNode(Integer.toString(id)); + seq.setName("Contig_"+id+"_linear_length_"+seq.length()+"_cov_"+cov); + n.setAttribute("seq", seq); + n.setAttribute("len", seq.length()); + n.setAttribute("cov",cov); + n.setAttribute("path", trimedPath); + b=SimpleBinner.getBinOfPath(repPath); + if(b!=null) { + n.setAttribute("bin", b); + if(!binn50.containsKey(b)) + binn50.put(b, new ArrayList()); + else + binn50.get(b).add(seq.length()); + } + + if(lastTime){ + annotation.setSequence(seq); + n.setAttribute("annotation", annotation); + } + //Add 2 edges + Boolean dd0=((BDEdge)path.getEdgePath().get(0)).getNodeDirection(trimedPath.getFirstNode()), + dd1=((BDEdge)path.peekEdge()).getNodeDirection(trimedPath.getLastNode()); + if(dd0==null) + dd0=!path.getFirstNodeDirection(); + if(dd1==null) + dd1=!path.getLastNodeDirection(); + + //if trimedPath has more than 1 nodes and has been merged into one + if(trimedPath.getNodeCount()>1){ + if(trimedPath.getRoot()==path.getNodePath().get(1)){ + dd0=false; + dd1=true; + }else{ + dd0=true; + dd1=false; + } + } + outputGraph.addEdge((BDNode)nn0, (BDNode)n , d0, dd0); + outputGraph.addEdge((BDNode)n, (BDNode)nn1 , dd1, d1); + } + + }else{ + String id=BDEdge.createID((BDNode)nn0, (BDNode)nn1 , d0, d1); + if(outputGraph.getEdge(id)==null) + outputGraph.addEdge((BDNode)nn0, (BDNode)nn1 , d0, d1); + } + + } + } + outputGraph.updateStats(); + } + + printCurrentStats(); + + if(lastTime) + System.out.println("FINISH!"); + //reset the cutting attributes + hAss.simGraph.edges().filter(Objects::nonNull) + .forEach(e->{if(e.hasAttribute("cut")) e.removeAttribute("cut");}); + + } + } + + private void printCurrentStats() { + System.out.printf("Input: read count=%d base count=%d\n", hAss.currentReadCount, hAss.currentBaseCount); + System.out.printf("Output: %d sequences (%d circular) N50=%d N75=%d Max=%d ", getNumberOfSequences(), getNumberOfCircularSequences(), getN50(), getN75(), getLongestContig()); + for(PopBin bb:binn50.keySet()) { + List lengths=binn50.get(bb); + + if(lengths!=null && lengths.size()>0) + System.out.printf("%s.N50=%d ", bb.getId(), GraphUtil.getNStats(.5, lengths.stream().mapToInt(i -> i).toArray())); + + } + System.out.println(); + // for(PopBin bb:binn50.keySet()) { + // List lengths=binn50.get(bb); + // System.out.printf("%d (%d): ", bb.getId(), lengths.size()); + // for(int i:lengths) + // System.out.printf("%d, ",i); + // System.out.println(); + // } + } + + public int getN50() { + synchronized(outputGraph) { + return outputGraph.n50; + } + } + public int getN75() { + synchronized(outputGraph) { + return outputGraph.n75; + } + } + public int getLongestContig() { + synchronized(outputGraph) { + return outputGraph.maxl; + } + } + public int getNumberOfSequences() { + synchronized(outputGraph) { + return outputGraph.numOfCtgs; + } + } + public int getNumberOfCircularSequences() { + synchronized(outputGraph) { + return outputGraph.numOfCircularCtgs; + } + } + public void outputAssGFA(String fileName) throws IOException{ + synchronized(outputGraph) { + outputGraph.outputGFA(fileName); + } + } + public void outputOrigGFA(String fileName) throws IOException{ + synchronized(hAss.simGraph) { + hAss.simGraph.outputGFA(fileName); + } + } + public void outputFASTA(String fileName) throws IOException { + synchronized(outputGraph) { + outputGraph.outputFASTA(fileName); + } + } + public void outputJAPSA(String fileName) throws IOException { + synchronized(outputGraph) { + outputGraph.outputJAPSA(fileName); + } + } + + @Override + protected void close() { + try { + outputFASTA(hAss.getPrefix()+File.separator+"npgraph_assembly.fasta"); + outputJAPSA(hAss.getPrefix()+File.separator+"npgraph_assembly.japsa"); + outputAssGFA(hAss.getPrefix()+File.separator+"npgraph_assembly.gfa"); + outputOrigGFA(hAss.getPrefix()+File.separator+"npgraph_components.gfa"); + }catch(IOException e) { + logger.error("Could not output assembly results: {}", e); + } + } + + @Override + protected void analysis() { + // TODO Auto-generated method stub + if(waiting) + update(false); + else { + System.out.printf("Post-processing the graph by greedy path-finding algorithm. Please wait...\n"); + HashSet unsolved=hAss.simGraph.getUnsolvedBridges(), + solved=new HashSet<>(); + while(true){ + boolean changed=false; + for(GoInBetweenBridge brg:unsolved){ + logger.debug("Last attempt on incomplete bridge {} : anchors={}\n{}", + brg.getEndingsID(), brg.getNumberOfAnchors(), brg.getAllPossiblePaths()); + //Take the current best path among the candidate of a bridge and connect the bridge(greedy) + if(brg.getCompletionLevel()==3){ + hAss.simGraph.getNewSubPathsToReduce(brg.getBestPath(brg.pBridge.getNode0(),brg.pBridge.getNode1())).stream().forEach(p->hAss.simGraph.reduceUniquePath(p)); + solved.add(brg); + changed=true; + }else{ + brg.scanForAnEnd(true); + changed=brg.steps.connectBridgeSteps(true); + + //return appropriate path + if(changed){ + hAss.simGraph.getNewSubPathsToReduce(brg.getBestPath(brg.steps.start.getNode(),brg.steps.end.getNode())).stream().forEach(p->hAss.simGraph.reduceUniquePath(p)); + solved.add(brg); + } + else + logger.debug("Last attempt failed"); + } + + } + if(solved.isEmpty()&&!changed) + break; + else{ + unsolved.removeAll(solved); + solved.clear(); + } + + } + //update for the last time + update(true); + } + } + + @Override + protected int getCurrentRead() { + // TODO Auto-generated method stub + return hAss.currentReadCount; + } +} diff --git a/src/main/java/org/rtassembly/npgraph/SimpleBinner.java b/src/main/java/org/rtassembly/npgraph/SimpleBinner.java index d4dd303..b87eb8f 100644 --- a/src/main/java/org/rtassembly/npgraph/SimpleBinner.java +++ b/src/main/java/org/rtassembly/npgraph/SimpleBinner.java @@ -3,6 +3,7 @@ import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; +import java.lang.invoke.MethodHandles; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; @@ -15,15 +16,15 @@ import org.apache.commons.math3.ml.clustering.Cluster; import org.apache.commons.math3.ml.clustering.DBSCANClusterer; import org.apache.commons.math3.ml.clustering.DoublePoint; +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; import org.graphstream.graph.Edge; import org.graphstream.graph.Node; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import com.google.common.collect.Iterables; public class SimpleBinner { - private static final Logger LOG = LoggerFactory.getLogger(SimpleBinner.class); + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); public static volatile int UNIQUE_CTG_LEN=10000, ANCHOR_CTG_LEN=1000, //surely length for the anchors; shorter anchors wouldn't be used until double-checked TRANSFORMED_ANCHOR_CTG_LEN=2000; @@ -83,8 +84,7 @@ private void exploringFromNode(Node node){ induceEdgeBin=nbins.substract(leavingEdgeBinCount); if(induceEdgeBin!=null && induceEdgeBin.getSum() > 0){ edge2BinMap.put(e, induceEdgeBin); - if(HybridAssembler.VERBOSE) - LOG.info("From node {}{} firing edge {}{}",node.getId(),getBinsOfNode(node), e.getId(), getBinsOfEdge(e)); + logger.debug("From node {} {} firing edge {} {}", node.getId(), getBinsOfNode(node), e.getId(), getBinsOfEdge(e)); exploringFromEdge(e); } } @@ -104,8 +104,7 @@ private void exploringFromNode(Node node){ induceEdgeBin=nbins.substract(enteringEdgeBinCount); if(induceEdgeBin!=null && induceEdgeBin.getSum() > 0){ edge2BinMap.put(e, induceEdgeBin); - if(HybridAssembler.VERBOSE) - LOG.info("From node {}{} firing edge {}{}\n",node.getId(),getBinsOfNode(node), e.getId(), getBinsOfEdge(e)); + logger.debug("From node {} {} firing edge {} {}", node.getId(), getBinsOfNode(node), e.getId(), getBinsOfEdge(e)); exploringFromEdge(e); } } @@ -120,8 +119,7 @@ private void exploringFromEdge(Edge edge){ unresolvedEdges.remove(edge); Node n0 = edge.getNode0(), n1 = edge.getNode1(); - if(HybridAssembler.VERBOSE) - LOG.info("From edge {}{}: ", edge.getId(), getBinsOfEdge(edge)); + logger.debug("From edge {} {}:", edge.getId(), getBinsOfEdge(edge)); if(!node2BinMap.containsKey(n0)){ boolean dir0 = ((BDEdge)edge).getDir0(); Stream edgeSet0 = dir0?n0.leavingEdges():n0.enteringEdges(); @@ -142,14 +140,12 @@ private void exploringFromEdge(Edge edge){ if(fully && GraphUtil.approxCompare(covSum, n0.getNumber("cov"))==0){ node2BinMap.put(n0, binCounts0); - if(HybridAssembler.VERBOSE) - LOG.info("completing node {}{}", n0.getId(),getBinsOfNode(n0)); + logger.debug("completing node {}", n0.getId()+getBinsOfNode(n0)); exploringFromNode(n0); - }else if(HybridAssembler.VERBOSE) - LOG.info("skip node {}{}", n0.getId(),getBinsOfNode(n0)); + }else + logger.debug("skip node {}", n0.getId()+getBinsOfNode(n0)); }else{ - if(HybridAssembler.VERBOSE) - LOG.info("firing node {}{}", n0.getId(),getBinsOfNode(n0)); + logger.debug("firing node {}", n0.getId()+getBinsOfNode(n0)); exploringFromNode(n0); } @@ -174,16 +170,14 @@ private void exploringFromEdge(Edge edge){ if(fully && GraphUtil.approxCompare(covSum, n1.getNumber("cov"))==0){ node2BinMap.put(n1, binCounts1); - if(HybridAssembler.VERBOSE) - LOG.info("completing node {}{}", n1.getId(),getBinsOfNode(n1)); + logger.debug("completing node {}", n1.getId()+getBinsOfNode(n1)); exploringFromNode(n1); - }else if(HybridAssembler.VERBOSE) - LOG.info("skip node {}{}", n1.getId(),getBinsOfNode(n1)); + }else + logger.debug("skip node {}", n1.getId()+getBinsOfNode(n1)); }else{ //TODO: check consistent here also - if(HybridAssembler.VERBOSE) - LOG.info("firing node {}{}", n1.getId(),getBinsOfNode(n1)); + logger.debug("firing node {}", n1.getId()+getBinsOfNode(n1)); exploringFromNode(n1); } } @@ -291,14 +285,13 @@ public void estimatePathsByCoverage() { GraphUtil.gradientDescent(graph); - if(HybridAssembler.VERBOSE) { - for(PopBin bin:binList) { - LOG.info("Bin " + bin.binID + ": " + bin.estCov); - for(Node n:bin.getCoreNodes()) - LOG.info("...core node " + n.getAttribute("name")); - } - graph.edges().forEach(e->System.out.println("Edge " + e.getId() + " cov=" + e.getNumber("cov"))); + for(PopBin bin:binList) { + logger.debug("Bin {}: {}", bin.binID, bin.estCov); + for(Node n:bin.getCoreNodes()) + logger.debug("...core node {}", n.getAttribute("name")); } + graph.edges().forEach(e->logger.debug("Edge {} cov={}", e.getId(), e.getNumber("cov"))); + //3.1.First round of assigning unit cov: from binned significant nodes for(PopBin b:binList) { @@ -314,8 +307,7 @@ public void estimatePathsByCoverage() { unresolvedEdges.sort((a,b)->Double.compare(a.getNumber("cov"),b.getNumber("cov"))); for(Edge e:unresolvedEdges){ - if(HybridAssembler.VERBOSE) - LOG.info("...scanning edge " + e.getId() + "cov=" + e.getNumber("cov") + ":"); + logger.debug("...scanning edge {} cov={}:", e.getId(),e.getNumber("cov")); PopBin tmp = scanAndGuess(e.getNumber("cov")); if(tmp!=null){ @@ -329,16 +321,14 @@ public void estimatePathsByCoverage() { if(n0.getNumber("len") > UNIQUE_CTG_LEN && getBinIfUnique(n0)==null){ n0.setAttribute("unique", leastBin); node2BinMap.put(n0, unitBinMap); - if(HybridAssembler.VERBOSE) - LOG.info("node {} is unique but have degree={}, length={}", n0.getId(), n0.getDegree(), (int)n0.getNumber("len")); + logger.debug("node {} is unique but have degree={}, length={}", n0.getId(), n0.getDegree(), (int)n0.getNumber("len")); continue; } if(n1.getNumber("len") > UNIQUE_CTG_LEN && getBinIfUnique(n1)==null){ n1.setAttribute("unique", leastBin); node2BinMap.put(n1, unitBinMap); - if(HybridAssembler.VERBOSE) - LOG.info("node {} is unique but have degree={}, length={}", n1.getId(), n1.getDegree(), (int)n1.getNumber("len")); + logger.debug("node {} is unique but have degree={}, length={}", n1.getId(), n1.getDegree(), (int)n1.getNumber("len")); continue; } @@ -348,31 +338,27 @@ public void estimatePathsByCoverage() { highlyPossibleEdges.put(tmp, new ArrayList()); highlyPossibleEdges.get(tmp).add(e); - if(HybridAssembler.VERBOSE) - LOG.info("=> highly possible in bin {}!", tmp.getId()); - }else if(HybridAssembler.VERBOSE) - LOG.info(": none!"); + logger.debug("=> highly possible in bin {}", tmp.getId()); + }else + logger.debug(": none!"); } while(!unresolvedEdges.isEmpty()) { - if(HybridAssembler.VERBOSE) - LOG.info("Starting assigning " + unresolvedEdges.size() + " unresolved edges"); + logger.debug("Starting assigning {} unresolved edges", unresolvedEdges.size()); //sort the unresolved edges based on abundance and guess until all gone... if(!highlyPossibleEdges.keySet().isEmpty()){ for(PopBin b:binList){ if(highlyPossibleEdges.containsKey(b)){ while(!highlyPossibleEdges.get(b).isEmpty()) { Edge guess = highlyPossibleEdges.get(b).remove(0); - if(HybridAssembler.VERBOSE) - LOG.info("...assigning " + guess.getId()); + logger.debug("...assigning {}", guess.getId()); if(unresolvedEdges.contains(guess)){ Multiplicity bc = new Multiplicity(b,1); edge2BinMap.put(guess, bc); - if(HybridAssembler.VERBOSE) - LOG.info(": start explore"); + logger.debug(": start explore"); exploringFromEdge(guess); - }else if(HybridAssembler.VERBOSE) - LOG.info(": already in bin " + getBinsOfEdge(guess)); + }else + logger.debug(": already in bin {}", getBinsOfEdge(guess)); } highlyPossibleEdges.remove(b); @@ -382,8 +368,7 @@ public void estimatePathsByCoverage() { } else{ //we can go further with random guessing but let's stop here for now - if(HybridAssembler.VERBOSE) - LOG.info("GUESS NO MORE!!!"); + logger.debug("GUESS NO MORE!!!"); break; } } @@ -439,8 +424,27 @@ static public PopBin getBinIfUniqueNow(Node node){ return retval; } + //return representative bin from walking a path + static public PopBin getBinOfPath(BDPath path) { + PopBin retval=null; + HashMap b2l = new HashMap<>(); + BDNode curNode = (BDNode) path.getRoot(); + for(Edge e:path.getEdgePath()) { + PopBin curBin=getBinIfUniqueNow(curNode); + if(curBin!=null) { + if(!b2l.containsKey(curBin)) + b2l.put(curBin, (int) curNode.getNumber("length")); + else + b2l.put(curBin, b2l.get(curBin) + (int) curNode.getNumber("length")); + } + curNode=(BDNode) e.getOpposite(curNode); + } + if(b2l.size()>0) + retval=b2l.entrySet().stream().max((entry1, entry2) -> entry1.getValue() > entry2.getValue() ? 1 : -1).get().getKey(); + + return retval; + } - static public boolean isPotentialAnchorNode(BDNode node){ return getBinIfUnique(node)!=null||(BDGraph.isSuspectedNode(node)&&node.getInDegree()<=1&&node.getOutDegree()<=1); } @@ -466,8 +470,7 @@ static public boolean isPotentialAnchorNode(BDNode node){ //Traversal along a unique path (unique ends) and return list of unique edges //Must only be called from BDGraph.reduce() synchronized public Set walkAlongUniquePath(BDPath path) { - if(HybridAssembler.VERBOSE) - LOG.info("Path {} being processed based on binning info...", path.getId()); + logger.debug("Path {} being processed based on binning info...", path.getId()); Node curNode = path.getRoot(), nextNode; PopBin consensusBin=path.getConsensusUniqueBinOfPath(), startBin=getBinIfUniqueNow(path.getRoot()), @@ -475,19 +478,16 @@ synchronized public Set walkAlongUniquePath(BDPath path) { if(consensusBin==null||startBin==null||endBin==null){ - if(HybridAssembler.VERBOSE) - LOG.info("Ignored: population bin of the path (either at ending node or global) is not known!"); + logger.debug("Ignored: population bin of the path (either at ending node or global) is not known!"); return null; } else if(!PopBin.isCloseBins(consensusBin,startBin)){ - if(HybridAssembler.VERBOSE) - LOG.info("Ignored: consensus bin {} doesn't agree with one of the endings bin {} at node {}", consensusBin, startBin, path.getRoot()); + logger.debug("Ignored: consensus bin {} doesn't agree with one of the endings bin {} at node {}", consensusBin, startBin, path.getRoot()); node2BinMap.remove(path.getRoot()); //clean from bin map here... return null; }else if(!PopBin.isCloseBins(consensusBin,endBin)){ - if(HybridAssembler.VERBOSE) - LOG.info("Ignored: consensus bin {} doesn't agree with one of the endings bin {} at node {}", consensusBin, endBin, path.peekNode()); + logger.debug("Ignored: consensus bin {} doesn't agree with one of the endings bin {} at node {}", consensusBin, endBin, path.peekNode()); node2BinMap.remove(path.peekNode()); //clean from bin map here... return null; @@ -531,8 +531,8 @@ else if(!PopBin.isCloseBins(consensusBin,startBin)){ //E.g. b2 vs b1 => b2==b1 //... bcMinusOne=edgeBinsCount.substract(otherBin); edge2BinMap.replace(ep, bcMinusOne); - }else if(HybridAssembler.VERBOSE) - LOG.info("...not found appropriate binning information on path {}, at edge {}: {}", path.getId(), ep.getId(), getBinsOfEdge(ep)); + }else + logger.debug("...not found appropriate binning information on path {}, at edge {} {}", path.getId(), ep.getId(), getBinsOfEdge(ep)); } ep.setAttribute("cov", ep.getNumber("cov")>aveCov?ep.getNumber("cov")-aveCov:0); @@ -585,36 +585,37 @@ public String getBinsOfEdge(Edge edge) { retval+="]"; return retval; } - public static void main(String[] args) throws IOException { - HybridAssembler hbAss = new HybridAssembler(); - hbAss.setShortReadsInput("/home/sonhoanghguyen/Projects/scaffolding/data/porecamp/metaSPAdes/assembly_graph.fastg"); - hbAss.setBinReadsInput("/home/sonhoanghguyen/Projects/scaffolding/data/porecamp/metabat/bin"); - hbAss.setShortReadsInputFormat("fastg"); - hbAss.prepareShortReadsProcess(); - - SimpleBinner binner = hbAss.simGraph.binner; - //binner.estimatePathsByCoverage(); - System.out.println("=> number of bin = " + binner.binList.size()); - for(PopBin b:binner.binList) { - System.out.println("Bin " + b.binID + " estCov=" + b.estCov + " totLen=" + b.estLen); - for(Node n:b.getCoreNodes()) - System.out.println(n.getAttribute("name")); - } - - - for(Node n:hbAss.simGraph) { - Iterator ite = n.edges().iterator(); - while(ite.hasNext()) { - Edge e = ite.next(); - System.out.println("Edge "+e.getId() + " cov=" + e.getNumber("cov") ); - Multiplicity tmp = binner.edge2BinMap.get(e); - if(tmp==null) { - System.out.println("...has not yet assigned!"); - continue; - } - - System.out.println(tmp); - } - } - } + +// public static void main(String[] args) throws IOException { +// HybridAssembler hbAss = new HybridAssembler(); +// hbAss.input.setShortReadsInput("/home/sonhoanghguyen/Projects/scaffolding/data/porecamp/metaSPAdes/assembly_graph.fastg"); +// hbAss.input.setBinReadsInput("/home/sonhoanghguyen/Projects/scaffolding/data/porecamp/metabat/bin"); +// hbAss.input.setShortReadsInputFormat("fastg"); +// hbAss.prepareShortReadsProcess(); +// +// SimpleBinner binner = hbAss.simGraph.binner; +// //binner.estimatePathsByCoverage(); +// System.out.println("=> number of bin = " + binner.binList.size()); +// for(PopBin b:binner.binList) { +// System.out.println("Bin " + b.binID + " estCov=" + b.estCov + " totLen=" + b.estLen); +// for(Node n:b.getCoreNodes()) +// System.out.println(n.getAttribute("name")); +// } +// +// +// for(Node n:hbAss.simGraph) { +// Iterator ite = n.edges().iterator(); +// while(ite.hasNext()) { +// Edge e = ite.next(); +// System.out.println("Edge "+e.getId() + " cov=" + e.getNumber("cov") ); +// Multiplicity tmp = binner.edge2BinMap.get(e); +// if(tmp==null) { +// System.out.println("...has not yet assigned!"); +// continue; +// } +// +// System.out.println(tmp); +// } +// } +// } } diff --git a/src/main/java/org/rtassembly/npgraph/grpc/AlignmentMsg.java b/src/main/java/org/rtassembly/npgraph/grpc/AlignmentMsg.java new file mode 100644 index 0000000..765b5de --- /dev/null +++ b/src/main/java/org/rtassembly/npgraph/grpc/AlignmentMsg.java @@ -0,0 +1,1258 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: npgraph_service.proto + +package org.rtassembly.npgraph.grpc; + +/** + *
+ * An alignment from minimap2 sent to the server 
+ * 
+ * + * Protobuf type {@code assembly.AlignmentMsg} + */ +public final class AlignmentMsg extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:assembly.AlignmentMsg) + AlignmentMsgOrBuilder { +private static final long serialVersionUID = 0L; + // Use AlignmentMsg.newBuilder() to construct. + private AlignmentMsg(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private AlignmentMsg() { + queryName_ = ""; + targetName_ = ""; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new AlignmentMsg(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private AlignmentMsg( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + java.lang.String s = input.readStringRequireUtf8(); + + queryName_ = s; + break; + } + case 16: { + + queryLength_ = input.readInt32(); + break; + } + case 24: { + + queryStart_ = input.readInt32(); + break; + } + case 32: { + + queryEnd_ = input.readInt32(); + break; + } + case 40: { + + strand_ = input.readBool(); + break; + } + case 50: { + java.lang.String s = input.readStringRequireUtf8(); + + targetName_ = s; + break; + } + case 56: { + + targetLength_ = input.readInt32(); + break; + } + case 64: { + + targetStart_ = input.readInt32(); + break; + } + case 72: { + + targetEnd_ = input.readInt32(); + break; + } + case 80: { + + quality_ = input.readInt32(); + break; + } + case 88: { + + score_ = input.readInt32(); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.rtassembly.npgraph.grpc.AssemblyGuideProto.internal_static_assembly_AlignmentMsg_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.rtassembly.npgraph.grpc.AssemblyGuideProto.internal_static_assembly_AlignmentMsg_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.rtassembly.npgraph.grpc.AlignmentMsg.class, org.rtassembly.npgraph.grpc.AlignmentMsg.Builder.class); + } + + public static final int QUERY_NAME_FIELD_NUMBER = 1; + private volatile java.lang.Object queryName_; + /** + * string query_name = 1; + * @return The queryName. + */ + public java.lang.String getQueryName() { + java.lang.Object ref = queryName_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + queryName_ = s; + return s; + } + } + /** + * string query_name = 1; + * @return The bytes for queryName. + */ + public com.google.protobuf.ByteString + getQueryNameBytes() { + java.lang.Object ref = queryName_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + queryName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int QUERY_LENGTH_FIELD_NUMBER = 2; + private int queryLength_; + /** + * int32 query_length = 2; + * @return The queryLength. + */ + public int getQueryLength() { + return queryLength_; + } + + public static final int QUERY_START_FIELD_NUMBER = 3; + private int queryStart_; + /** + * int32 query_start = 3; + * @return The queryStart. + */ + public int getQueryStart() { + return queryStart_; + } + + public static final int QUERY_END_FIELD_NUMBER = 4; + private int queryEnd_; + /** + * int32 query_end = 4; + * @return The queryEnd. + */ + public int getQueryEnd() { + return queryEnd_; + } + + public static final int STRAND_FIELD_NUMBER = 5; + private boolean strand_; + /** + * bool strand = 5; + * @return The strand. + */ + public boolean getStrand() { + return strand_; + } + + public static final int TARGET_NAME_FIELD_NUMBER = 6; + private volatile java.lang.Object targetName_; + /** + * string target_name = 6; + * @return The targetName. + */ + public java.lang.String getTargetName() { + java.lang.Object ref = targetName_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + targetName_ = s; + return s; + } + } + /** + * string target_name = 6; + * @return The bytes for targetName. + */ + public com.google.protobuf.ByteString + getTargetNameBytes() { + java.lang.Object ref = targetName_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + targetName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int TARGET_LENGTH_FIELD_NUMBER = 7; + private int targetLength_; + /** + * int32 target_length = 7; + * @return The targetLength. + */ + public int getTargetLength() { + return targetLength_; + } + + public static final int TARGET_START_FIELD_NUMBER = 8; + private int targetStart_; + /** + * int32 target_start = 8; + * @return The targetStart. + */ + public int getTargetStart() { + return targetStart_; + } + + public static final int TARGET_END_FIELD_NUMBER = 9; + private int targetEnd_; + /** + * int32 target_end = 9; + * @return The targetEnd. + */ + public int getTargetEnd() { + return targetEnd_; + } + + public static final int QUALITY_FIELD_NUMBER = 10; + private int quality_; + /** + * int32 quality = 10; + * @return The quality. + */ + public int getQuality() { + return quality_; + } + + public static final int SCORE_FIELD_NUMBER = 11; + private int score_; + /** + * int32 score = 11; + * @return The score. + */ + public int getScore() { + return score_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!getQueryNameBytes().isEmpty()) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, queryName_); + } + if (queryLength_ != 0) { + output.writeInt32(2, queryLength_); + } + if (queryStart_ != 0) { + output.writeInt32(3, queryStart_); + } + if (queryEnd_ != 0) { + output.writeInt32(4, queryEnd_); + } + if (strand_ != false) { + output.writeBool(5, strand_); + } + if (!getTargetNameBytes().isEmpty()) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 6, targetName_); + } + if (targetLength_ != 0) { + output.writeInt32(7, targetLength_); + } + if (targetStart_ != 0) { + output.writeInt32(8, targetStart_); + } + if (targetEnd_ != 0) { + output.writeInt32(9, targetEnd_); + } + if (quality_ != 0) { + output.writeInt32(10, quality_); + } + if (score_ != 0) { + output.writeInt32(11, score_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!getQueryNameBytes().isEmpty()) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, queryName_); + } + if (queryLength_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(2, queryLength_); + } + if (queryStart_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(3, queryStart_); + } + if (queryEnd_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(4, queryEnd_); + } + if (strand_ != false) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(5, strand_); + } + if (!getTargetNameBytes().isEmpty()) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(6, targetName_); + } + if (targetLength_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(7, targetLength_); + } + if (targetStart_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(8, targetStart_); + } + if (targetEnd_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(9, targetEnd_); + } + if (quality_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(10, quality_); + } + if (score_ != 0) { + size += com.google.protobuf.CodedOutputStream + .computeInt32Size(11, score_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof org.rtassembly.npgraph.grpc.AlignmentMsg)) { + return super.equals(obj); + } + org.rtassembly.npgraph.grpc.AlignmentMsg other = (org.rtassembly.npgraph.grpc.AlignmentMsg) obj; + + if (!getQueryName() + .equals(other.getQueryName())) return false; + if (getQueryLength() + != other.getQueryLength()) return false; + if (getQueryStart() + != other.getQueryStart()) return false; + if (getQueryEnd() + != other.getQueryEnd()) return false; + if (getStrand() + != other.getStrand()) return false; + if (!getTargetName() + .equals(other.getTargetName())) return false; + if (getTargetLength() + != other.getTargetLength()) return false; + if (getTargetStart() + != other.getTargetStart()) return false; + if (getTargetEnd() + != other.getTargetEnd()) return false; + if (getQuality() + != other.getQuality()) return false; + if (getScore() + != other.getScore()) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + QUERY_NAME_FIELD_NUMBER; + hash = (53 * hash) + getQueryName().hashCode(); + hash = (37 * hash) + QUERY_LENGTH_FIELD_NUMBER; + hash = (53 * hash) + getQueryLength(); + hash = (37 * hash) + QUERY_START_FIELD_NUMBER; + hash = (53 * hash) + getQueryStart(); + hash = (37 * hash) + QUERY_END_FIELD_NUMBER; + hash = (53 * hash) + getQueryEnd(); + hash = (37 * hash) + STRAND_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getStrand()); + hash = (37 * hash) + TARGET_NAME_FIELD_NUMBER; + hash = (53 * hash) + getTargetName().hashCode(); + hash = (37 * hash) + TARGET_LENGTH_FIELD_NUMBER; + hash = (53 * hash) + getTargetLength(); + hash = (37 * hash) + TARGET_START_FIELD_NUMBER; + hash = (53 * hash) + getTargetStart(); + hash = (37 * hash) + TARGET_END_FIELD_NUMBER; + hash = (53 * hash) + getTargetEnd(); + hash = (37 * hash) + QUALITY_FIELD_NUMBER; + hash = (53 * hash) + getQuality(); + hash = (37 * hash) + SCORE_FIELD_NUMBER; + hash = (53 * hash) + getScore(); + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static org.rtassembly.npgraph.grpc.AlignmentMsg parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.rtassembly.npgraph.grpc.AlignmentMsg parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.rtassembly.npgraph.grpc.AlignmentMsg parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.rtassembly.npgraph.grpc.AlignmentMsg parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.rtassembly.npgraph.grpc.AlignmentMsg parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.rtassembly.npgraph.grpc.AlignmentMsg parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.rtassembly.npgraph.grpc.AlignmentMsg parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static org.rtassembly.npgraph.grpc.AlignmentMsg parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static org.rtassembly.npgraph.grpc.AlignmentMsg parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static org.rtassembly.npgraph.grpc.AlignmentMsg parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static org.rtassembly.npgraph.grpc.AlignmentMsg parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static org.rtassembly.npgraph.grpc.AlignmentMsg parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(org.rtassembly.npgraph.grpc.AlignmentMsg prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * An alignment from minimap2 sent to the server 
+   * 
+ * + * Protobuf type {@code assembly.AlignmentMsg} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:assembly.AlignmentMsg) + org.rtassembly.npgraph.grpc.AlignmentMsgOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.rtassembly.npgraph.grpc.AssemblyGuideProto.internal_static_assembly_AlignmentMsg_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.rtassembly.npgraph.grpc.AssemblyGuideProto.internal_static_assembly_AlignmentMsg_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.rtassembly.npgraph.grpc.AlignmentMsg.class, org.rtassembly.npgraph.grpc.AlignmentMsg.Builder.class); + } + + // Construct using org.rtassembly.npgraph.grpc.AlignmentMsg.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + queryName_ = ""; + + queryLength_ = 0; + + queryStart_ = 0; + + queryEnd_ = 0; + + strand_ = false; + + targetName_ = ""; + + targetLength_ = 0; + + targetStart_ = 0; + + targetEnd_ = 0; + + quality_ = 0; + + score_ = 0; + + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.rtassembly.npgraph.grpc.AssemblyGuideProto.internal_static_assembly_AlignmentMsg_descriptor; + } + + @java.lang.Override + public org.rtassembly.npgraph.grpc.AlignmentMsg getDefaultInstanceForType() { + return org.rtassembly.npgraph.grpc.AlignmentMsg.getDefaultInstance(); + } + + @java.lang.Override + public org.rtassembly.npgraph.grpc.AlignmentMsg build() { + org.rtassembly.npgraph.grpc.AlignmentMsg result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public org.rtassembly.npgraph.grpc.AlignmentMsg buildPartial() { + org.rtassembly.npgraph.grpc.AlignmentMsg result = new org.rtassembly.npgraph.grpc.AlignmentMsg(this); + result.queryName_ = queryName_; + result.queryLength_ = queryLength_; + result.queryStart_ = queryStart_; + result.queryEnd_ = queryEnd_; + result.strand_ = strand_; + result.targetName_ = targetName_; + result.targetLength_ = targetLength_; + result.targetStart_ = targetStart_; + result.targetEnd_ = targetEnd_; + result.quality_ = quality_; + result.score_ = score_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.rtassembly.npgraph.grpc.AlignmentMsg) { + return mergeFrom((org.rtassembly.npgraph.grpc.AlignmentMsg)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.rtassembly.npgraph.grpc.AlignmentMsg other) { + if (other == org.rtassembly.npgraph.grpc.AlignmentMsg.getDefaultInstance()) return this; + if (!other.getQueryName().isEmpty()) { + queryName_ = other.queryName_; + onChanged(); + } + if (other.getQueryLength() != 0) { + setQueryLength(other.getQueryLength()); + } + if (other.getQueryStart() != 0) { + setQueryStart(other.getQueryStart()); + } + if (other.getQueryEnd() != 0) { + setQueryEnd(other.getQueryEnd()); + } + if (other.getStrand() != false) { + setStrand(other.getStrand()); + } + if (!other.getTargetName().isEmpty()) { + targetName_ = other.targetName_; + onChanged(); + } + if (other.getTargetLength() != 0) { + setTargetLength(other.getTargetLength()); + } + if (other.getTargetStart() != 0) { + setTargetStart(other.getTargetStart()); + } + if (other.getTargetEnd() != 0) { + setTargetEnd(other.getTargetEnd()); + } + if (other.getQuality() != 0) { + setQuality(other.getQuality()); + } + if (other.getScore() != 0) { + setScore(other.getScore()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.rtassembly.npgraph.grpc.AlignmentMsg parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.rtassembly.npgraph.grpc.AlignmentMsg) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + private java.lang.Object queryName_ = ""; + /** + * string query_name = 1; + * @return The queryName. + */ + public java.lang.String getQueryName() { + java.lang.Object ref = queryName_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + queryName_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string query_name = 1; + * @return The bytes for queryName. + */ + public com.google.protobuf.ByteString + getQueryNameBytes() { + java.lang.Object ref = queryName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + queryName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string query_name = 1; + * @param value The queryName to set. + * @return This builder for chaining. + */ + public Builder setQueryName( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + + queryName_ = value; + onChanged(); + return this; + } + /** + * string query_name = 1; + * @return This builder for chaining. + */ + public Builder clearQueryName() { + + queryName_ = getDefaultInstance().getQueryName(); + onChanged(); + return this; + } + /** + * string query_name = 1; + * @param value The bytes for queryName to set. + * @return This builder for chaining. + */ + public Builder setQueryNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + + queryName_ = value; + onChanged(); + return this; + } + + private int queryLength_ ; + /** + * int32 query_length = 2; + * @return The queryLength. + */ + public int getQueryLength() { + return queryLength_; + } + /** + * int32 query_length = 2; + * @param value The queryLength to set. + * @return This builder for chaining. + */ + public Builder setQueryLength(int value) { + + queryLength_ = value; + onChanged(); + return this; + } + /** + * int32 query_length = 2; + * @return This builder for chaining. + */ + public Builder clearQueryLength() { + + queryLength_ = 0; + onChanged(); + return this; + } + + private int queryStart_ ; + /** + * int32 query_start = 3; + * @return The queryStart. + */ + public int getQueryStart() { + return queryStart_; + } + /** + * int32 query_start = 3; + * @param value The queryStart to set. + * @return This builder for chaining. + */ + public Builder setQueryStart(int value) { + + queryStart_ = value; + onChanged(); + return this; + } + /** + * int32 query_start = 3; + * @return This builder for chaining. + */ + public Builder clearQueryStart() { + + queryStart_ = 0; + onChanged(); + return this; + } + + private int queryEnd_ ; + /** + * int32 query_end = 4; + * @return The queryEnd. + */ + public int getQueryEnd() { + return queryEnd_; + } + /** + * int32 query_end = 4; + * @param value The queryEnd to set. + * @return This builder for chaining. + */ + public Builder setQueryEnd(int value) { + + queryEnd_ = value; + onChanged(); + return this; + } + /** + * int32 query_end = 4; + * @return This builder for chaining. + */ + public Builder clearQueryEnd() { + + queryEnd_ = 0; + onChanged(); + return this; + } + + private boolean strand_ ; + /** + * bool strand = 5; + * @return The strand. + */ + public boolean getStrand() { + return strand_; + } + /** + * bool strand = 5; + * @param value The strand to set. + * @return This builder for chaining. + */ + public Builder setStrand(boolean value) { + + strand_ = value; + onChanged(); + return this; + } + /** + * bool strand = 5; + * @return This builder for chaining. + */ + public Builder clearStrand() { + + strand_ = false; + onChanged(); + return this; + } + + private java.lang.Object targetName_ = ""; + /** + * string target_name = 6; + * @return The targetName. + */ + public java.lang.String getTargetName() { + java.lang.Object ref = targetName_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + targetName_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string target_name = 6; + * @return The bytes for targetName. + */ + public com.google.protobuf.ByteString + getTargetNameBytes() { + java.lang.Object ref = targetName_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + targetName_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string target_name = 6; + * @param value The targetName to set. + * @return This builder for chaining. + */ + public Builder setTargetName( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + + targetName_ = value; + onChanged(); + return this; + } + /** + * string target_name = 6; + * @return This builder for chaining. + */ + public Builder clearTargetName() { + + targetName_ = getDefaultInstance().getTargetName(); + onChanged(); + return this; + } + /** + * string target_name = 6; + * @param value The bytes for targetName to set. + * @return This builder for chaining. + */ + public Builder setTargetNameBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + + targetName_ = value; + onChanged(); + return this; + } + + private int targetLength_ ; + /** + * int32 target_length = 7; + * @return The targetLength. + */ + public int getTargetLength() { + return targetLength_; + } + /** + * int32 target_length = 7; + * @param value The targetLength to set. + * @return This builder for chaining. + */ + public Builder setTargetLength(int value) { + + targetLength_ = value; + onChanged(); + return this; + } + /** + * int32 target_length = 7; + * @return This builder for chaining. + */ + public Builder clearTargetLength() { + + targetLength_ = 0; + onChanged(); + return this; + } + + private int targetStart_ ; + /** + * int32 target_start = 8; + * @return The targetStart. + */ + public int getTargetStart() { + return targetStart_; + } + /** + * int32 target_start = 8; + * @param value The targetStart to set. + * @return This builder for chaining. + */ + public Builder setTargetStart(int value) { + + targetStart_ = value; + onChanged(); + return this; + } + /** + * int32 target_start = 8; + * @return This builder for chaining. + */ + public Builder clearTargetStart() { + + targetStart_ = 0; + onChanged(); + return this; + } + + private int targetEnd_ ; + /** + * int32 target_end = 9; + * @return The targetEnd. + */ + public int getTargetEnd() { + return targetEnd_; + } + /** + * int32 target_end = 9; + * @param value The targetEnd to set. + * @return This builder for chaining. + */ + public Builder setTargetEnd(int value) { + + targetEnd_ = value; + onChanged(); + return this; + } + /** + * int32 target_end = 9; + * @return This builder for chaining. + */ + public Builder clearTargetEnd() { + + targetEnd_ = 0; + onChanged(); + return this; + } + + private int quality_ ; + /** + * int32 quality = 10; + * @return The quality. + */ + public int getQuality() { + return quality_; + } + /** + * int32 quality = 10; + * @param value The quality to set. + * @return This builder for chaining. + */ + public Builder setQuality(int value) { + + quality_ = value; + onChanged(); + return this; + } + /** + * int32 quality = 10; + * @return This builder for chaining. + */ + public Builder clearQuality() { + + quality_ = 0; + onChanged(); + return this; + } + + private int score_ ; + /** + * int32 score = 11; + * @return The score. + */ + public int getScore() { + return score_; + } + /** + * int32 score = 11; + * @param value The score to set. + * @return This builder for chaining. + */ + public Builder setScore(int value) { + + score_ = value; + onChanged(); + return this; + } + /** + * int32 score = 11; + * @return This builder for chaining. + */ + public Builder clearScore() { + + score_ = 0; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:assembly.AlignmentMsg) + } + + // @@protoc_insertion_point(class_scope:assembly.AlignmentMsg) + private static final org.rtassembly.npgraph.grpc.AlignmentMsg DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new org.rtassembly.npgraph.grpc.AlignmentMsg(); + } + + public static org.rtassembly.npgraph.grpc.AlignmentMsg getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public AlignmentMsg parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new AlignmentMsg(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public org.rtassembly.npgraph.grpc.AlignmentMsg getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/src/main/java/org/rtassembly/npgraph/grpc/AlignmentMsgOrBuilder.java b/src/main/java/org/rtassembly/npgraph/grpc/AlignmentMsgOrBuilder.java new file mode 100644 index 0000000..fcf4227 --- /dev/null +++ b/src/main/java/org/rtassembly/npgraph/grpc/AlignmentMsgOrBuilder.java @@ -0,0 +1,87 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: npgraph_service.proto + +package org.rtassembly.npgraph.grpc; + +public interface AlignmentMsgOrBuilder extends + // @@protoc_insertion_point(interface_extends:assembly.AlignmentMsg) + com.google.protobuf.MessageOrBuilder { + + /** + * string query_name = 1; + * @return The queryName. + */ + java.lang.String getQueryName(); + /** + * string query_name = 1; + * @return The bytes for queryName. + */ + com.google.protobuf.ByteString + getQueryNameBytes(); + + /** + * int32 query_length = 2; + * @return The queryLength. + */ + int getQueryLength(); + + /** + * int32 query_start = 3; + * @return The queryStart. + */ + int getQueryStart(); + + /** + * int32 query_end = 4; + * @return The queryEnd. + */ + int getQueryEnd(); + + /** + * bool strand = 5; + * @return The strand. + */ + boolean getStrand(); + + /** + * string target_name = 6; + * @return The targetName. + */ + java.lang.String getTargetName(); + /** + * string target_name = 6; + * @return The bytes for targetName. + */ + com.google.protobuf.ByteString + getTargetNameBytes(); + + /** + * int32 target_length = 7; + * @return The targetLength. + */ + int getTargetLength(); + + /** + * int32 target_start = 8; + * @return The targetStart. + */ + int getTargetStart(); + + /** + * int32 target_end = 9; + * @return The targetEnd. + */ + int getTargetEnd(); + + /** + * int32 quality = 10; + * @return The quality. + */ + int getQuality(); + + /** + * int32 score = 11; + * @return The score. + */ + int getScore(); +} diff --git a/src/main/java/org/rtassembly/npgraph/grpc/AssemblyGuideGrpc.java b/src/main/java/org/rtassembly/npgraph/grpc/AssemblyGuideGrpc.java new file mode 100644 index 0000000..62d8eb8 --- /dev/null +++ b/src/main/java/org/rtassembly/npgraph/grpc/AssemblyGuideGrpc.java @@ -0,0 +1,303 @@ +package org.rtassembly.npgraph.grpc; + +import static io.grpc.MethodDescriptor.generateFullMethodName; +import static io.grpc.stub.ClientCalls.asyncBidiStreamingCall; +import static io.grpc.stub.ClientCalls.asyncClientStreamingCall; +import static io.grpc.stub.ClientCalls.asyncServerStreamingCall; +import static io.grpc.stub.ClientCalls.asyncUnaryCall; +import static io.grpc.stub.ClientCalls.blockingServerStreamingCall; +import static io.grpc.stub.ClientCalls.blockingUnaryCall; +import static io.grpc.stub.ClientCalls.futureUnaryCall; +import static io.grpc.stub.ServerCalls.asyncBidiStreamingCall; +import static io.grpc.stub.ServerCalls.asyncClientStreamingCall; +import static io.grpc.stub.ServerCalls.asyncServerStreamingCall; +import static io.grpc.stub.ServerCalls.asyncUnaryCall; +import static io.grpc.stub.ServerCalls.asyncUnimplementedStreamingCall; +import static io.grpc.stub.ServerCalls.asyncUnimplementedUnaryCall; + +/** + *
+ * Interface exported by the server.
+ * 
+ */ +@javax.annotation.Generated( + value = "by gRPC proto compiler (version 1.28.0)", + comments = "Source: npgraph_service.proto") +public final class AssemblyGuideGrpc { + + private AssemblyGuideGrpc() {} + + public static final String SERVICE_NAME = "assembly.AssemblyGuide"; + + // Static method descriptors that strictly reflect the proto. + private static volatile io.grpc.MethodDescriptor getGetAssemblyContributionMethod; + + @io.grpc.stub.annotations.RpcMethod( + fullMethodName = SERVICE_NAME + '/' + "GetAssemblyContribution", + requestType = org.rtassembly.npgraph.grpc.RequestAssembly.class, + responseType = org.rtassembly.npgraph.grpc.ResponseAssembly.class, + methodType = io.grpc.MethodDescriptor.MethodType.UNARY) + public static io.grpc.MethodDescriptor getGetAssemblyContributionMethod() { + io.grpc.MethodDescriptor getGetAssemblyContributionMethod; + if ((getGetAssemblyContributionMethod = AssemblyGuideGrpc.getGetAssemblyContributionMethod) == null) { + synchronized (AssemblyGuideGrpc.class) { + if ((getGetAssemblyContributionMethod = AssemblyGuideGrpc.getGetAssemblyContributionMethod) == null) { + AssemblyGuideGrpc.getGetAssemblyContributionMethod = getGetAssemblyContributionMethod = + io.grpc.MethodDescriptor.newBuilder() + .setType(io.grpc.MethodDescriptor.MethodType.UNARY) + .setFullMethodName(generateFullMethodName(SERVICE_NAME, "GetAssemblyContribution")) + .setSampledToLocalTracing(true) + .setRequestMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.rtassembly.npgraph.grpc.RequestAssembly.getDefaultInstance())) + .setResponseMarshaller(io.grpc.protobuf.ProtoUtils.marshaller( + org.rtassembly.npgraph.grpc.ResponseAssembly.getDefaultInstance())) + .setSchemaDescriptor(new AssemblyGuideMethodDescriptorSupplier("GetAssemblyContribution")) + .build(); + } + } + } + return getGetAssemblyContributionMethod; + } + + /** + * Creates a new async stub that supports all call types for the service + */ + public static AssemblyGuideStub newStub(io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public AssemblyGuideStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new AssemblyGuideStub(channel, callOptions); + } + }; + return AssemblyGuideStub.newStub(factory, channel); + } + + /** + * Creates a new blocking-style stub that supports unary and streaming output calls on the service + */ + public static AssemblyGuideBlockingStub newBlockingStub( + io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public AssemblyGuideBlockingStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new AssemblyGuideBlockingStub(channel, callOptions); + } + }; + return AssemblyGuideBlockingStub.newStub(factory, channel); + } + + /** + * Creates a new ListenableFuture-style stub that supports unary calls on the service + */ + public static AssemblyGuideFutureStub newFutureStub( + io.grpc.Channel channel) { + io.grpc.stub.AbstractStub.StubFactory factory = + new io.grpc.stub.AbstractStub.StubFactory() { + @java.lang.Override + public AssemblyGuideFutureStub newStub(io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new AssemblyGuideFutureStub(channel, callOptions); + } + }; + return AssemblyGuideFutureStub.newStub(factory, channel); + } + + /** + *
+   * Interface exported by the server.
+   * 
+ */ + public static abstract class AssemblyGuideImplBase implements io.grpc.BindableService { + + /** + */ + public void getAssemblyContribution(org.rtassembly.npgraph.grpc.RequestAssembly request, + io.grpc.stub.StreamObserver responseObserver) { + asyncUnimplementedUnaryCall(getGetAssemblyContributionMethod(), responseObserver); + } + + @java.lang.Override public final io.grpc.ServerServiceDefinition bindService() { + return io.grpc.ServerServiceDefinition.builder(getServiceDescriptor()) + .addMethod( + getGetAssemblyContributionMethod(), + asyncUnaryCall( + new MethodHandlers< + org.rtassembly.npgraph.grpc.RequestAssembly, + org.rtassembly.npgraph.grpc.ResponseAssembly>( + this, METHODID_GET_ASSEMBLY_CONTRIBUTION))) + .build(); + } + } + + /** + *
+   * Interface exported by the server.
+   * 
+ */ + public static final class AssemblyGuideStub extends io.grpc.stub.AbstractAsyncStub { + private AssemblyGuideStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected AssemblyGuideStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new AssemblyGuideStub(channel, callOptions); + } + + /** + */ + public void getAssemblyContribution(org.rtassembly.npgraph.grpc.RequestAssembly request, + io.grpc.stub.StreamObserver responseObserver) { + asyncUnaryCall( + getChannel().newCall(getGetAssemblyContributionMethod(), getCallOptions()), request, responseObserver); + } + } + + /** + *
+   * Interface exported by the server.
+   * 
+ */ + public static final class AssemblyGuideBlockingStub extends io.grpc.stub.AbstractBlockingStub { + private AssemblyGuideBlockingStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected AssemblyGuideBlockingStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new AssemblyGuideBlockingStub(channel, callOptions); + } + + /** + */ + public org.rtassembly.npgraph.grpc.ResponseAssembly getAssemblyContribution(org.rtassembly.npgraph.grpc.RequestAssembly request) { + return blockingUnaryCall( + getChannel(), getGetAssemblyContributionMethod(), getCallOptions(), request); + } + } + + /** + *
+   * Interface exported by the server.
+   * 
+ */ + public static final class AssemblyGuideFutureStub extends io.grpc.stub.AbstractFutureStub { + private AssemblyGuideFutureStub( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + super(channel, callOptions); + } + + @java.lang.Override + protected AssemblyGuideFutureStub build( + io.grpc.Channel channel, io.grpc.CallOptions callOptions) { + return new AssemblyGuideFutureStub(channel, callOptions); + } + + /** + */ + public com.google.common.util.concurrent.ListenableFuture getAssemblyContribution( + org.rtassembly.npgraph.grpc.RequestAssembly request) { + return futureUnaryCall( + getChannel().newCall(getGetAssemblyContributionMethod(), getCallOptions()), request); + } + } + + private static final int METHODID_GET_ASSEMBLY_CONTRIBUTION = 0; + + private static final class MethodHandlers implements + io.grpc.stub.ServerCalls.UnaryMethod, + io.grpc.stub.ServerCalls.ServerStreamingMethod, + io.grpc.stub.ServerCalls.ClientStreamingMethod, + io.grpc.stub.ServerCalls.BidiStreamingMethod { + private final AssemblyGuideImplBase serviceImpl; + private final int methodId; + + MethodHandlers(AssemblyGuideImplBase serviceImpl, int methodId) { + this.serviceImpl = serviceImpl; + this.methodId = methodId; + } + + @java.lang.Override + @java.lang.SuppressWarnings("unchecked") + public void invoke(Req request, io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + case METHODID_GET_ASSEMBLY_CONTRIBUTION: + serviceImpl.getAssemblyContribution((org.rtassembly.npgraph.grpc.RequestAssembly) request, + (io.grpc.stub.StreamObserver) responseObserver); + break; + default: + throw new AssertionError(); + } + } + + @java.lang.Override + @java.lang.SuppressWarnings("unchecked") + public io.grpc.stub.StreamObserver invoke( + io.grpc.stub.StreamObserver responseObserver) { + switch (methodId) { + default: + throw new AssertionError(); + } + } + } + + private static abstract class AssemblyGuideBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoFileDescriptorSupplier, io.grpc.protobuf.ProtoServiceDescriptorSupplier { + AssemblyGuideBaseDescriptorSupplier() {} + + @java.lang.Override + public com.google.protobuf.Descriptors.FileDescriptor getFileDescriptor() { + return org.rtassembly.npgraph.grpc.AssemblyGuideProto.getDescriptor(); + } + + @java.lang.Override + public com.google.protobuf.Descriptors.ServiceDescriptor getServiceDescriptor() { + return getFileDescriptor().findServiceByName("AssemblyGuide"); + } + } + + private static final class AssemblyGuideFileDescriptorSupplier + extends AssemblyGuideBaseDescriptorSupplier { + AssemblyGuideFileDescriptorSupplier() {} + } + + private static final class AssemblyGuideMethodDescriptorSupplier + extends AssemblyGuideBaseDescriptorSupplier + implements io.grpc.protobuf.ProtoMethodDescriptorSupplier { + private final String methodName; + + AssemblyGuideMethodDescriptorSupplier(String methodName) { + this.methodName = methodName; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.MethodDescriptor getMethodDescriptor() { + return getServiceDescriptor().findMethodByName(methodName); + } + } + + private static volatile io.grpc.ServiceDescriptor serviceDescriptor; + + public static io.grpc.ServiceDescriptor getServiceDescriptor() { + io.grpc.ServiceDescriptor result = serviceDescriptor; + if (result == null) { + synchronized (AssemblyGuideGrpc.class) { + result = serviceDescriptor; + if (result == null) { + serviceDescriptor = result = io.grpc.ServiceDescriptor.newBuilder(SERVICE_NAME) + .setSchemaDescriptor(new AssemblyGuideFileDescriptorSupplier()) + .addMethod(getGetAssemblyContributionMethod()) + .build(); + } + } + } + return result; + } +} diff --git a/src/main/java/org/rtassembly/npgraph/grpc/AssemblyGuideProto.java b/src/main/java/org/rtassembly/npgraph/grpc/AssemblyGuideProto.java new file mode 100644 index 0000000..6886c43 --- /dev/null +++ b/src/main/java/org/rtassembly/npgraph/grpc/AssemblyGuideProto.java @@ -0,0 +1,82 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: npgraph_service.proto + +package org.rtassembly.npgraph.grpc; + +public final class AssemblyGuideProto { + private AssemblyGuideProto() {} + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistryLite registry) { + } + + public static void registerAllExtensions( + com.google.protobuf.ExtensionRegistry registry) { + registerAllExtensions( + (com.google.protobuf.ExtensionRegistryLite) registry); + } + static final com.google.protobuf.Descriptors.Descriptor + internal_static_assembly_AlignmentMsg_descriptor; + static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_assembly_AlignmentMsg_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_assembly_RequestAssembly_descriptor; + static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_assembly_RequestAssembly_fieldAccessorTable; + static final com.google.protobuf.Descriptors.Descriptor + internal_static_assembly_ResponseAssembly_descriptor; + static final + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internal_static_assembly_ResponseAssembly_fieldAccessorTable; + + public static com.google.protobuf.Descriptors.FileDescriptor + getDescriptor() { + return descriptor; + } + private static com.google.protobuf.Descriptors.FileDescriptor + descriptor; + static { + java.lang.String[] descriptorData = { + "\n\025npgraph_service.proto\022\010assembly\"\346\001\n\014Al" + + "ignmentMsg\022\022\n\nquery_name\030\001 \001(\t\022\024\n\014query_" + + "length\030\002 \001(\005\022\023\n\013query_start\030\003 \001(\005\022\021\n\tque" + + "ry_end\030\004 \001(\005\022\016\n\006strand\030\005 \001(\010\022\023\n\013target_n" + + "ame\030\006 \001(\t\022\025\n\rtarget_length\030\007 \001(\005\022\024\n\014targ" + + "et_start\030\010 \001(\005\022\022\n\ntarget_end\030\t \001(\005\022\017\n\007qu" + + "ality\030\n \001(\005\022\r\n\005score\030\013 \001(\005\"M\n\017RequestAss" + + "embly\022\017\n\007read_id\030\001 \001(\t\022)\n\thits_list\030\002 \003(" + + "\0132\026.assembly.AlignmentMsg\"7\n\020ResponseAss" + + "embly\022\017\n\007read_id\030\001 \001(\t\022\022\n\nusefulness\030\002 \001" + + "(\0102c\n\rAssemblyGuide\022R\n\027GetAssemblyContri" + + "bution\022\031.assembly.RequestAssembly\032\032.asse" + + "mbly.ResponseAssembly\"\000B9\n\033org.rtassembl" + + "y.npgraph.grpcB\022AssemblyGuideProtoP\001\242\002\003A" + + "GPb\006proto3" + }; + descriptor = com.google.protobuf.Descriptors.FileDescriptor + .internalBuildGeneratedFileFrom(descriptorData, + new com.google.protobuf.Descriptors.FileDescriptor[] { + }); + internal_static_assembly_AlignmentMsg_descriptor = + getDescriptor().getMessageTypes().get(0); + internal_static_assembly_AlignmentMsg_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_assembly_AlignmentMsg_descriptor, + new java.lang.String[] { "QueryName", "QueryLength", "QueryStart", "QueryEnd", "Strand", "TargetName", "TargetLength", "TargetStart", "TargetEnd", "Quality", "Score", }); + internal_static_assembly_RequestAssembly_descriptor = + getDescriptor().getMessageTypes().get(1); + internal_static_assembly_RequestAssembly_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_assembly_RequestAssembly_descriptor, + new java.lang.String[] { "ReadId", "HitsList", }); + internal_static_assembly_ResponseAssembly_descriptor = + getDescriptor().getMessageTypes().get(2); + internal_static_assembly_ResponseAssembly_fieldAccessorTable = new + com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( + internal_static_assembly_ResponseAssembly_descriptor, + new java.lang.String[] { "ReadId", "Usefulness", }); + } + + // @@protoc_insertion_point(outer_class_scope) +} diff --git a/src/main/java/org/rtassembly/npgraph/grpc/AssemblyGuideServer.java b/src/main/java/org/rtassembly/npgraph/grpc/AssemblyGuideServer.java new file mode 100644 index 0000000..725e757 --- /dev/null +++ b/src/main/java/org/rtassembly/npgraph/grpc/AssemblyGuideServer.java @@ -0,0 +1,260 @@ +package org.rtassembly.npgraph.grpc; + +import io.grpc.Server; +import io.grpc.ServerBuilder; +import io.grpc.stub.StreamObserver; +import japsa.seq.PAFRecord; +import japsa.seq.Sequence; + +import java.io.IOException; +import java.lang.invoke.MethodHandles; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; + +import org.apache.logging.log4j.Logger; +import org.apache.logging.log4j.LogManager; +import org.rtassembly.npgraph.Alignment; +import org.rtassembly.npgraph.BDGraph; +import org.rtassembly.npgraph.BDNode; +import org.rtassembly.npgraph.BDPath; +import org.rtassembly.npgraph.GoInBetweenBridge; +import org.rtassembly.npgraph.GraphUtil; +import org.rtassembly.npgraph.HybridAssembler; +import org.rtassembly.npgraph.SimpleBinner; + +public class AssemblyGuideServer { + private static final Logger logger = LogManager.getLogger(MethodHandles.lookup().lookupClass()); + + public int port; + public final Server server; + public static HybridAssembler myAss; + public static int ELEN = 10000; +// public final Thread myThread; + public final static ExecutorService executor = Executors.newFixedThreadPool(2); //one for GUI, one for real-time graph reduction + + public AssemblyGuideServer(int port, HybridAssembler ass) { + this(ServerBuilder.forPort(port), port, ass); + } + /** Create a RouteGuide server using serverBuilder as a base */ + public AssemblyGuideServer(ServerBuilder serverBuilder, int port, HybridAssembler badAss) { + this.port = port; + server = serverBuilder.addService(new AssemblyGuideService()) + .build(); + myAss=badAss; +// myThread = new Thread(badAss.observer); + } + + /** Start serving requests. */ + public void start() throws IOException { +// myThread.start(); + executor.execute(myAss.observer); + server.start(); + + logger.info("Server started, listening on {}", port); + Runtime.getRuntime().addShutdownHook(new Thread() { + @Override + public void run() { + // Use stderr here since the logger may have been reset by its JVM shutdown hook. + System.err.println("*** shutting down gRPC server since JVM is shutting down"); + try { + AssemblyGuideServer.this.stop(); + } catch (InterruptedException e) { + e.printStackTrace(System.err); + } + System.err.println("*** server shut down"); + } + }); + } + + /** Stop serving requests and shutdown resources. */ + public void stop() throws InterruptedException { + if (server != null) { + server.shutdown().awaitTermination(30, TimeUnit.SECONDS); + } + + executor.shutdown(); +// if(myThread.isAlive()) { +// myAss.observer.stopWaiting(); +// myThread.join(); +// } + } + + /** + * Our implementation of assembly service. + * + *

See src/main/proto/npgraph_service.proto for details of the methods. + */ + private static class AssemblyGuideService extends AssemblyGuideGrpc.AssemblyGuideImplBase { + private static final HashMap lastMap = new HashMap<>(); //readID to the last unique contig it mapped to + private static final HashMap reduceRead = new HashMap<>(); //readID to the length of chunk used to reduce + + @Override + public void getAssemblyContribution(RequestAssembly request, + StreamObserver responseObserver) { + ArrayList hits = getAlignmentsFromRequest(request); + boolean continueing=true; + Future f=null; + + if(hits.size() > 0) {//this should be checked from client side + Collections.sort(hits); + Alignment a = hits.get(hits.size()-1), //get the last alignment of current hit list + b = lastMap.get(a.readID); //previous last unique alignment + //1. compare the last mapped contig, proceed if the same + //TODO: index repeats of long bridges that already resolved? + + //with read chunk shorter than 1000bp, mapping to unique contig cannot be missed + //when sequentially considering only last Alignment + if(SimpleBinner.getBinIfUniqueNow(a.node)!=null) { + //if the same as prevAlg, proceed + boolean investigating=true; + if(b==null || a.node != b.node) { + lastMap.put(a.readID, a); + }else{ + int alignedReadLen = Math.abs(a.readEnd - a.readStart) + Math.abs(b.readEnd - b.readStart), + alignedRefLen = Math.abs(a.refEnd - a.refStart) + Math.abs(b.refEnd - b.refStart); + double rate = 1.0 * alignedRefLen/alignedReadLen; + + int alignP = (int) ((b.readStart - a.readStart) * rate); + //(rough) relative position from ref_b (contig of b) to ref_a (contig of a) in the assembled genome + int gP = Math.abs((alignP + (a.strand ? a.refStart:-a.refStart) - (b.strand?b.refStart:-b.refStart))); + if(GraphUtil.approxCompare(gP, a.node.getNumber("len")) ==0 ) { //circular + lastMap.put(a.readID, a); + investigating=continueing=false; //terminate sequencing this one + }else { + //just another fragmented alignment of a same contig + logger.debug("...read {} with same contig alignment: continue", a.readID); + responseObserver.onNext(ResponseAssembly.newBuilder().setUsefulness(continueing).setReadId(request.getReadId()).build()); + responseObserver.onCompleted(); + return; + } + } + + //3. estimate distance to the end of this unique contig to calculate usefulness + if(investigating) { + int eLen = a.readAlignmentEnd(); + eLen+=(a.strand?a.node.getNumber("len")-a.refEnd:a.refStart); + + BDNode prevNode, unqNode; + prevNode=unqNode=a.node; + boolean dir=a.strand; + GoInBetweenBridge brg=BDGraph.bridgesMap.get(unqNode.getId()+(dir?"o":"i")); + while(brg!=null&&brg.getCompletionLevel()==4) { + + if(unqNode==brg.pBridge.getNode0() && dir==brg.pBridge.getDir0()) { + unqNode=brg.pBridge.getNode1(); + dir=!brg.pBridge.getDir1(); + }else { + unqNode=brg.pBridge.getNode0(); + dir=!brg.pBridge.getDir0(); + } + eLen+=brg.steps.getSpanVector().distance(prevNode, unqNode); + eLen+=unqNode.getNumber("len"); + + prevNode=unqNode; + brg=BDGraph.bridgesMap.get(unqNode.getId()+(dir?"o":"i")); + } + + continueing=(eLen < ELEN);//??too simple! + if(!continueing) + logger.debug("...read {}, eLen={} is difficult to span next unresolved bridge: stop!", a.readID, eLen); + else + logger.debug("...read {}, eLen={} is expected to span next unresoved bridge: continue!", a.readID, eLen); + } + + //4. reduce + Sequence read = GraphUtil.getNSequence(a.readID, a.readLength); + + + Callable callableObj = () -> { + return new ReducePathInfo(myAss.simGraph.uniqueBridgesFinding(read, hits), a.readID, a.readLength); + }; + f=executor.submit(callableObj); + +// List paths=myAss.simGraph.uniqueBridgesFinding(read, hits); +// if(paths!=null) { +// paths.stream().forEach(p->myAss.simGraph.reduceUniquePath(p)); +// if(reduceRead.containsKey(a.readID)) {//already used for reduction +// myAss.currentBaseCount += a.readLength-reduceRead.get(a.readID); +// reduceRead.replace(a.readID, a.readLength); +// }else {//new +// reduceRead.put(a.readID, a.readLength); +// myAss.currentReadCount++; +// myAss.currentBaseCount+=a.readLength; +// } +// +// } + +// return; + } + + + } + //5. send control signal back to client + responseObserver.onNext(ResponseAssembly.newBuilder().setUsefulness(continueing).setReadId(request.getReadId()).build()); + + if(f!=null) { + try { + ReducePathInfo result = f.get(); + if(!result.isEmpty()) { + result.paths.stream().forEach(p->myAss.simGraph.reduceUniquePath(p)); + if(reduceRead.containsKey(result.readID)) {//already used for reduction + myAss.currentBaseCount += result.readLength-reduceRead.get(result.readID); + reduceRead.replace(result.readID, result.readLength); + }else {//new + reduceRead.put(result.readID, result.readLength); + myAss.currentReadCount++; + myAss.currentBaseCount+=result.readLength; + } + } + }catch(Exception e) { + logger.debug(e); + } + } + + responseObserver.onCompleted(); + } + + private ArrayList getAlignmentsFromRequest(RequestAssembly request){ + ArrayList retval = new ArrayList<>(); + String refID; + for(AlignmentMsg msg:request.getHitsListList()) { + String refName=msg.getTargetName(); + refID = refName.split("_").length > 1 ? refName.split("_")[1]:refName; + BDNode node = (BDNode) myAss.simGraph.getNode(refID); + if(node==null) + return retval; + //Convert the hit message to a PAFRecord: [0-based inclusive; 0-based exlusive] -> [1-based inclusive; 1-based inclusive] + PAFRecord record = new PAFRecord(msg.getQueryName(), msg.getQueryLength(), msg.getQueryStart()+1, msg.getQueryEnd(), + msg.getStrand(), + msg.getTargetName(), msg.getTargetLength(), msg.getTargetStart()+1, msg.getTargetEnd(), + msg.getScore(), msg.getQuality()); + retval.add(new Alignment(record, node)); + + } + + return retval; + } + + class ReducePathInfo{ + List paths = null; + String readID = ""; + int readLength = 0; + ReducePathInfo(List paths, String id, int length){ + this.paths = paths; + readID = id; + readLength = length; + } + public boolean isEmpty() { + return paths==null; + } + + } + } +} diff --git a/src/main/java/org/rtassembly/npgraph/grpc/AssemblyGuideUtil.java b/src/main/java/org/rtassembly/npgraph/grpc/AssemblyGuideUtil.java new file mode 100644 index 0000000..6b38ac7 --- /dev/null +++ b/src/main/java/org/rtassembly/npgraph/grpc/AssemblyGuideUtil.java @@ -0,0 +1,29 @@ +package org.rtassembly.npgraph.grpc; + +import java.util.ArrayList; +import org.rtassembly.npgraph.*; + +import japsa.seq.PAFRecord; + +public class AssemblyGuideUtil { +// public static ArrayList getAlignmentsFromRequest(RequestAssembly request, HybridAssembler myAss){ +// ArrayList retval = new ArrayList<>(); +// String refID; +// for(AlignmentMsg msg:request.getHitsListList()) { +// String refName=msg.getTargetName(); +// refID = refName.split("_").length > 1 ? refName.split("_")[1]:refName; +// BDNode node = (BDNode) myAss.simGraph.getNode(refID); +// if(node==null) +// return retval; +// PAFRecord record = new PAFRecord(msg.getQueryName(), msg.getQueryLength(), msg.getQueryStart(), msg.getQueryEnd(), +// msg.getStrand(), +// msg.getTargetName(), msg.getTargetLength(), msg.getTargetStart(), msg.getTargetEnd(), +// msg.getScore(), msg.getQuality()); +// retval.add(new Alignment(record, node)); +// +// } +// +// return retval; +// } + +} diff --git a/src/main/java/org/rtassembly/npgraph/grpc/RequestAssembly.java b/src/main/java/org/rtassembly/npgraph/grpc/RequestAssembly.java new file mode 100644 index 0000000..1900e9e --- /dev/null +++ b/src/main/java/org/rtassembly/npgraph/grpc/RequestAssembly.java @@ -0,0 +1,909 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: npgraph_service.proto + +package org.rtassembly.npgraph.grpc; + +/** + *

+ * Client sent all hits from a chunk simultaneously
+ * 
+ * + * Protobuf type {@code assembly.RequestAssembly} + */ +public final class RequestAssembly extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:assembly.RequestAssembly) + RequestAssemblyOrBuilder { +private static final long serialVersionUID = 0L; + // Use RequestAssembly.newBuilder() to construct. + private RequestAssembly(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private RequestAssembly() { + readId_ = ""; + hitsList_ = java.util.Collections.emptyList(); + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new RequestAssembly(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private RequestAssembly( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + int mutable_bitField0_ = 0; + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + java.lang.String s = input.readStringRequireUtf8(); + + readId_ = s; + break; + } + case 18: { + if (!((mutable_bitField0_ & 0x00000001) != 0)) { + hitsList_ = new java.util.ArrayList(); + mutable_bitField0_ |= 0x00000001; + } + hitsList_.add( + input.readMessage(org.rtassembly.npgraph.grpc.AlignmentMsg.parser(), extensionRegistry)); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + if (((mutable_bitField0_ & 0x00000001) != 0)) { + hitsList_ = java.util.Collections.unmodifiableList(hitsList_); + } + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.rtassembly.npgraph.grpc.AssemblyGuideProto.internal_static_assembly_RequestAssembly_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.rtassembly.npgraph.grpc.AssemblyGuideProto.internal_static_assembly_RequestAssembly_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.rtassembly.npgraph.grpc.RequestAssembly.class, org.rtassembly.npgraph.grpc.RequestAssembly.Builder.class); + } + + public static final int READ_ID_FIELD_NUMBER = 1; + private volatile java.lang.Object readId_; + /** + * string read_id = 1; + * @return The readId. + */ + public java.lang.String getReadId() { + java.lang.Object ref = readId_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + readId_ = s; + return s; + } + } + /** + * string read_id = 1; + * @return The bytes for readId. + */ + public com.google.protobuf.ByteString + getReadIdBytes() { + java.lang.Object ref = readId_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + readId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int HITS_LIST_FIELD_NUMBER = 2; + private java.util.List hitsList_; + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public java.util.List getHitsListList() { + return hitsList_; + } + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public java.util.List + getHitsListOrBuilderList() { + return hitsList_; + } + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public int getHitsListCount() { + return hitsList_.size(); + } + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public org.rtassembly.npgraph.grpc.AlignmentMsg getHitsList(int index) { + return hitsList_.get(index); + } + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public org.rtassembly.npgraph.grpc.AlignmentMsgOrBuilder getHitsListOrBuilder( + int index) { + return hitsList_.get(index); + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!getReadIdBytes().isEmpty()) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, readId_); + } + for (int i = 0; i < hitsList_.size(); i++) { + output.writeMessage(2, hitsList_.get(i)); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!getReadIdBytes().isEmpty()) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, readId_); + } + for (int i = 0; i < hitsList_.size(); i++) { + size += com.google.protobuf.CodedOutputStream + .computeMessageSize(2, hitsList_.get(i)); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof org.rtassembly.npgraph.grpc.RequestAssembly)) { + return super.equals(obj); + } + org.rtassembly.npgraph.grpc.RequestAssembly other = (org.rtassembly.npgraph.grpc.RequestAssembly) obj; + + if (!getReadId() + .equals(other.getReadId())) return false; + if (!getHitsListList() + .equals(other.getHitsListList())) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + READ_ID_FIELD_NUMBER; + hash = (53 * hash) + getReadId().hashCode(); + if (getHitsListCount() > 0) { + hash = (37 * hash) + HITS_LIST_FIELD_NUMBER; + hash = (53 * hash) + getHitsListList().hashCode(); + } + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static org.rtassembly.npgraph.grpc.RequestAssembly parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.rtassembly.npgraph.grpc.RequestAssembly parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.rtassembly.npgraph.grpc.RequestAssembly parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.rtassembly.npgraph.grpc.RequestAssembly parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.rtassembly.npgraph.grpc.RequestAssembly parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.rtassembly.npgraph.grpc.RequestAssembly parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.rtassembly.npgraph.grpc.RequestAssembly parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static org.rtassembly.npgraph.grpc.RequestAssembly parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static org.rtassembly.npgraph.grpc.RequestAssembly parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static org.rtassembly.npgraph.grpc.RequestAssembly parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static org.rtassembly.npgraph.grpc.RequestAssembly parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static org.rtassembly.npgraph.grpc.RequestAssembly parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(org.rtassembly.npgraph.grpc.RequestAssembly prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * Client sent all hits from a chunk simultaneously
+   * 
+ * + * Protobuf type {@code assembly.RequestAssembly} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:assembly.RequestAssembly) + org.rtassembly.npgraph.grpc.RequestAssemblyOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.rtassembly.npgraph.grpc.AssemblyGuideProto.internal_static_assembly_RequestAssembly_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.rtassembly.npgraph.grpc.AssemblyGuideProto.internal_static_assembly_RequestAssembly_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.rtassembly.npgraph.grpc.RequestAssembly.class, org.rtassembly.npgraph.grpc.RequestAssembly.Builder.class); + } + + // Construct using org.rtassembly.npgraph.grpc.RequestAssembly.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + getHitsListFieldBuilder(); + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + readId_ = ""; + + if (hitsListBuilder_ == null) { + hitsList_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + } else { + hitsListBuilder_.clear(); + } + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.rtassembly.npgraph.grpc.AssemblyGuideProto.internal_static_assembly_RequestAssembly_descriptor; + } + + @java.lang.Override + public org.rtassembly.npgraph.grpc.RequestAssembly getDefaultInstanceForType() { + return org.rtassembly.npgraph.grpc.RequestAssembly.getDefaultInstance(); + } + + @java.lang.Override + public org.rtassembly.npgraph.grpc.RequestAssembly build() { + org.rtassembly.npgraph.grpc.RequestAssembly result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public org.rtassembly.npgraph.grpc.RequestAssembly buildPartial() { + org.rtassembly.npgraph.grpc.RequestAssembly result = new org.rtassembly.npgraph.grpc.RequestAssembly(this); + int from_bitField0_ = bitField0_; + result.readId_ = readId_; + if (hitsListBuilder_ == null) { + if (((bitField0_ & 0x00000001) != 0)) { + hitsList_ = java.util.Collections.unmodifiableList(hitsList_); + bitField0_ = (bitField0_ & ~0x00000001); + } + result.hitsList_ = hitsList_; + } else { + result.hitsList_ = hitsListBuilder_.build(); + } + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.rtassembly.npgraph.grpc.RequestAssembly) { + return mergeFrom((org.rtassembly.npgraph.grpc.RequestAssembly)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.rtassembly.npgraph.grpc.RequestAssembly other) { + if (other == org.rtassembly.npgraph.grpc.RequestAssembly.getDefaultInstance()) return this; + if (!other.getReadId().isEmpty()) { + readId_ = other.readId_; + onChanged(); + } + if (hitsListBuilder_ == null) { + if (!other.hitsList_.isEmpty()) { + if (hitsList_.isEmpty()) { + hitsList_ = other.hitsList_; + bitField0_ = (bitField0_ & ~0x00000001); + } else { + ensureHitsListIsMutable(); + hitsList_.addAll(other.hitsList_); + } + onChanged(); + } + } else { + if (!other.hitsList_.isEmpty()) { + if (hitsListBuilder_.isEmpty()) { + hitsListBuilder_.dispose(); + hitsListBuilder_ = null; + hitsList_ = other.hitsList_; + bitField0_ = (bitField0_ & ~0x00000001); + hitsListBuilder_ = + com.google.protobuf.GeneratedMessageV3.alwaysUseFieldBuilders ? + getHitsListFieldBuilder() : null; + } else { + hitsListBuilder_.addAllMessages(other.hitsList_); + } + } + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.rtassembly.npgraph.grpc.RequestAssembly parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.rtassembly.npgraph.grpc.RequestAssembly) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + private int bitField0_; + + private java.lang.Object readId_ = ""; + /** + * string read_id = 1; + * @return The readId. + */ + public java.lang.String getReadId() { + java.lang.Object ref = readId_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + readId_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string read_id = 1; + * @return The bytes for readId. + */ + public com.google.protobuf.ByteString + getReadIdBytes() { + java.lang.Object ref = readId_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + readId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string read_id = 1; + * @param value The readId to set. + * @return This builder for chaining. + */ + public Builder setReadId( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + + readId_ = value; + onChanged(); + return this; + } + /** + * string read_id = 1; + * @return This builder for chaining. + */ + public Builder clearReadId() { + + readId_ = getDefaultInstance().getReadId(); + onChanged(); + return this; + } + /** + * string read_id = 1; + * @param value The bytes for readId to set. + * @return This builder for chaining. + */ + public Builder setReadIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + + readId_ = value; + onChanged(); + return this; + } + + private java.util.List hitsList_ = + java.util.Collections.emptyList(); + private void ensureHitsListIsMutable() { + if (!((bitField0_ & 0x00000001) != 0)) { + hitsList_ = new java.util.ArrayList(hitsList_); + bitField0_ |= 0x00000001; + } + } + + private com.google.protobuf.RepeatedFieldBuilderV3< + org.rtassembly.npgraph.grpc.AlignmentMsg, org.rtassembly.npgraph.grpc.AlignmentMsg.Builder, org.rtassembly.npgraph.grpc.AlignmentMsgOrBuilder> hitsListBuilder_; + + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public java.util.List getHitsListList() { + if (hitsListBuilder_ == null) { + return java.util.Collections.unmodifiableList(hitsList_); + } else { + return hitsListBuilder_.getMessageList(); + } + } + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public int getHitsListCount() { + if (hitsListBuilder_ == null) { + return hitsList_.size(); + } else { + return hitsListBuilder_.getCount(); + } + } + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public org.rtassembly.npgraph.grpc.AlignmentMsg getHitsList(int index) { + if (hitsListBuilder_ == null) { + return hitsList_.get(index); + } else { + return hitsListBuilder_.getMessage(index); + } + } + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public Builder setHitsList( + int index, org.rtassembly.npgraph.grpc.AlignmentMsg value) { + if (hitsListBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureHitsListIsMutable(); + hitsList_.set(index, value); + onChanged(); + } else { + hitsListBuilder_.setMessage(index, value); + } + return this; + } + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public Builder setHitsList( + int index, org.rtassembly.npgraph.grpc.AlignmentMsg.Builder builderForValue) { + if (hitsListBuilder_ == null) { + ensureHitsListIsMutable(); + hitsList_.set(index, builderForValue.build()); + onChanged(); + } else { + hitsListBuilder_.setMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public Builder addHitsList(org.rtassembly.npgraph.grpc.AlignmentMsg value) { + if (hitsListBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureHitsListIsMutable(); + hitsList_.add(value); + onChanged(); + } else { + hitsListBuilder_.addMessage(value); + } + return this; + } + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public Builder addHitsList( + int index, org.rtassembly.npgraph.grpc.AlignmentMsg value) { + if (hitsListBuilder_ == null) { + if (value == null) { + throw new NullPointerException(); + } + ensureHitsListIsMutable(); + hitsList_.add(index, value); + onChanged(); + } else { + hitsListBuilder_.addMessage(index, value); + } + return this; + } + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public Builder addHitsList( + org.rtassembly.npgraph.grpc.AlignmentMsg.Builder builderForValue) { + if (hitsListBuilder_ == null) { + ensureHitsListIsMutable(); + hitsList_.add(builderForValue.build()); + onChanged(); + } else { + hitsListBuilder_.addMessage(builderForValue.build()); + } + return this; + } + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public Builder addHitsList( + int index, org.rtassembly.npgraph.grpc.AlignmentMsg.Builder builderForValue) { + if (hitsListBuilder_ == null) { + ensureHitsListIsMutable(); + hitsList_.add(index, builderForValue.build()); + onChanged(); + } else { + hitsListBuilder_.addMessage(index, builderForValue.build()); + } + return this; + } + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public Builder addAllHitsList( + java.lang.Iterable values) { + if (hitsListBuilder_ == null) { + ensureHitsListIsMutable(); + com.google.protobuf.AbstractMessageLite.Builder.addAll( + values, hitsList_); + onChanged(); + } else { + hitsListBuilder_.addAllMessages(values); + } + return this; + } + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public Builder clearHitsList() { + if (hitsListBuilder_ == null) { + hitsList_ = java.util.Collections.emptyList(); + bitField0_ = (bitField0_ & ~0x00000001); + onChanged(); + } else { + hitsListBuilder_.clear(); + } + return this; + } + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public Builder removeHitsList(int index) { + if (hitsListBuilder_ == null) { + ensureHitsListIsMutable(); + hitsList_.remove(index); + onChanged(); + } else { + hitsListBuilder_.remove(index); + } + return this; + } + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public org.rtassembly.npgraph.grpc.AlignmentMsg.Builder getHitsListBuilder( + int index) { + return getHitsListFieldBuilder().getBuilder(index); + } + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public org.rtassembly.npgraph.grpc.AlignmentMsgOrBuilder getHitsListOrBuilder( + int index) { + if (hitsListBuilder_ == null) { + return hitsList_.get(index); } else { + return hitsListBuilder_.getMessageOrBuilder(index); + } + } + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public java.util.List + getHitsListOrBuilderList() { + if (hitsListBuilder_ != null) { + return hitsListBuilder_.getMessageOrBuilderList(); + } else { + return java.util.Collections.unmodifiableList(hitsList_); + } + } + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public org.rtassembly.npgraph.grpc.AlignmentMsg.Builder addHitsListBuilder() { + return getHitsListFieldBuilder().addBuilder( + org.rtassembly.npgraph.grpc.AlignmentMsg.getDefaultInstance()); + } + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public org.rtassembly.npgraph.grpc.AlignmentMsg.Builder addHitsListBuilder( + int index) { + return getHitsListFieldBuilder().addBuilder( + index, org.rtassembly.npgraph.grpc.AlignmentMsg.getDefaultInstance()); + } + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + public java.util.List + getHitsListBuilderList() { + return getHitsListFieldBuilder().getBuilderList(); + } + private com.google.protobuf.RepeatedFieldBuilderV3< + org.rtassembly.npgraph.grpc.AlignmentMsg, org.rtassembly.npgraph.grpc.AlignmentMsg.Builder, org.rtassembly.npgraph.grpc.AlignmentMsgOrBuilder> + getHitsListFieldBuilder() { + if (hitsListBuilder_ == null) { + hitsListBuilder_ = new com.google.protobuf.RepeatedFieldBuilderV3< + org.rtassembly.npgraph.grpc.AlignmentMsg, org.rtassembly.npgraph.grpc.AlignmentMsg.Builder, org.rtassembly.npgraph.grpc.AlignmentMsgOrBuilder>( + hitsList_, + ((bitField0_ & 0x00000001) != 0), + getParentForChildren(), + isClean()); + hitsList_ = null; + } + return hitsListBuilder_; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:assembly.RequestAssembly) + } + + // @@protoc_insertion_point(class_scope:assembly.RequestAssembly) + private static final org.rtassembly.npgraph.grpc.RequestAssembly DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new org.rtassembly.npgraph.grpc.RequestAssembly(); + } + + public static org.rtassembly.npgraph.grpc.RequestAssembly getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public RequestAssembly parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new RequestAssembly(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public org.rtassembly.npgraph.grpc.RequestAssembly getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/src/main/java/org/rtassembly/npgraph/grpc/RequestAssemblyOrBuilder.java b/src/main/java/org/rtassembly/npgraph/grpc/RequestAssemblyOrBuilder.java new file mode 100644 index 0000000..67e7e4f --- /dev/null +++ b/src/main/java/org/rtassembly/npgraph/grpc/RequestAssemblyOrBuilder.java @@ -0,0 +1,45 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: npgraph_service.proto + +package org.rtassembly.npgraph.grpc; + +public interface RequestAssemblyOrBuilder extends + // @@protoc_insertion_point(interface_extends:assembly.RequestAssembly) + com.google.protobuf.MessageOrBuilder { + + /** + * string read_id = 1; + * @return The readId. + */ + java.lang.String getReadId(); + /** + * string read_id = 1; + * @return The bytes for readId. + */ + com.google.protobuf.ByteString + getReadIdBytes(); + + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + java.util.List + getHitsListList(); + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + org.rtassembly.npgraph.grpc.AlignmentMsg getHitsList(int index); + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + int getHitsListCount(); + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + java.util.List + getHitsListOrBuilderList(); + /** + * repeated .assembly.AlignmentMsg hits_list = 2; + */ + org.rtassembly.npgraph.grpc.AlignmentMsgOrBuilder getHitsListOrBuilder( + int index); +} diff --git a/src/main/java/org/rtassembly/npgraph/grpc/ResponseAssembly.java b/src/main/java/org/rtassembly/npgraph/grpc/ResponseAssembly.java new file mode 100644 index 0000000..c1566c7 --- /dev/null +++ b/src/main/java/org/rtassembly/npgraph/grpc/ResponseAssembly.java @@ -0,0 +1,626 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: npgraph_service.proto + +package org.rtassembly.npgraph.grpc; + +/** + *
+ * Response from the assembly guide service: useful or not
+ * 
+ * + * Protobuf type {@code assembly.ResponseAssembly} + */ +public final class ResponseAssembly extends + com.google.protobuf.GeneratedMessageV3 implements + // @@protoc_insertion_point(message_implements:assembly.ResponseAssembly) + ResponseAssemblyOrBuilder { +private static final long serialVersionUID = 0L; + // Use ResponseAssembly.newBuilder() to construct. + private ResponseAssembly(com.google.protobuf.GeneratedMessageV3.Builder builder) { + super(builder); + } + private ResponseAssembly() { + readId_ = ""; + } + + @java.lang.Override + @SuppressWarnings({"unused"}) + protected java.lang.Object newInstance( + UnusedPrivateParameter unused) { + return new ResponseAssembly(); + } + + @java.lang.Override + public final com.google.protobuf.UnknownFieldSet + getUnknownFields() { + return this.unknownFields; + } + private ResponseAssembly( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + this(); + if (extensionRegistry == null) { + throw new java.lang.NullPointerException(); + } + com.google.protobuf.UnknownFieldSet.Builder unknownFields = + com.google.protobuf.UnknownFieldSet.newBuilder(); + try { + boolean done = false; + while (!done) { + int tag = input.readTag(); + switch (tag) { + case 0: + done = true; + break; + case 10: { + java.lang.String s = input.readStringRequireUtf8(); + + readId_ = s; + break; + } + case 16: { + + usefulness_ = input.readBool(); + break; + } + default: { + if (!parseUnknownField( + input, unknownFields, extensionRegistry, tag)) { + done = true; + } + break; + } + } + } + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + throw e.setUnfinishedMessage(this); + } catch (java.io.IOException e) { + throw new com.google.protobuf.InvalidProtocolBufferException( + e).setUnfinishedMessage(this); + } finally { + this.unknownFields = unknownFields.build(); + makeExtensionsImmutable(); + } + } + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.rtassembly.npgraph.grpc.AssemblyGuideProto.internal_static_assembly_ResponseAssembly_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.rtassembly.npgraph.grpc.AssemblyGuideProto.internal_static_assembly_ResponseAssembly_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.rtassembly.npgraph.grpc.ResponseAssembly.class, org.rtassembly.npgraph.grpc.ResponseAssembly.Builder.class); + } + + public static final int READ_ID_FIELD_NUMBER = 1; + private volatile java.lang.Object readId_; + /** + * string read_id = 1; + * @return The readId. + */ + public java.lang.String getReadId() { + java.lang.Object ref = readId_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + readId_ = s; + return s; + } + } + /** + * string read_id = 1; + * @return The bytes for readId. + */ + public com.google.protobuf.ByteString + getReadIdBytes() { + java.lang.Object ref = readId_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + readId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + + public static final int USEFULNESS_FIELD_NUMBER = 2; + private boolean usefulness_; + /** + * bool usefulness = 2; + * @return The usefulness. + */ + public boolean getUsefulness() { + return usefulness_; + } + + private byte memoizedIsInitialized = -1; + @java.lang.Override + public final boolean isInitialized() { + byte isInitialized = memoizedIsInitialized; + if (isInitialized == 1) return true; + if (isInitialized == 0) return false; + + memoizedIsInitialized = 1; + return true; + } + + @java.lang.Override + public void writeTo(com.google.protobuf.CodedOutputStream output) + throws java.io.IOException { + if (!getReadIdBytes().isEmpty()) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 1, readId_); + } + if (usefulness_ != false) { + output.writeBool(2, usefulness_); + } + unknownFields.writeTo(output); + } + + @java.lang.Override + public int getSerializedSize() { + int size = memoizedSize; + if (size != -1) return size; + + size = 0; + if (!getReadIdBytes().isEmpty()) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, readId_); + } + if (usefulness_ != false) { + size += com.google.protobuf.CodedOutputStream + .computeBoolSize(2, usefulness_); + } + size += unknownFields.getSerializedSize(); + memoizedSize = size; + return size; + } + + @java.lang.Override + public boolean equals(final java.lang.Object obj) { + if (obj == this) { + return true; + } + if (!(obj instanceof org.rtassembly.npgraph.grpc.ResponseAssembly)) { + return super.equals(obj); + } + org.rtassembly.npgraph.grpc.ResponseAssembly other = (org.rtassembly.npgraph.grpc.ResponseAssembly) obj; + + if (!getReadId() + .equals(other.getReadId())) return false; + if (getUsefulness() + != other.getUsefulness()) return false; + if (!unknownFields.equals(other.unknownFields)) return false; + return true; + } + + @java.lang.Override + public int hashCode() { + if (memoizedHashCode != 0) { + return memoizedHashCode; + } + int hash = 41; + hash = (19 * hash) + getDescriptor().hashCode(); + hash = (37 * hash) + READ_ID_FIELD_NUMBER; + hash = (53 * hash) + getReadId().hashCode(); + hash = (37 * hash) + USEFULNESS_FIELD_NUMBER; + hash = (53 * hash) + com.google.protobuf.Internal.hashBoolean( + getUsefulness()); + hash = (29 * hash) + unknownFields.hashCode(); + memoizedHashCode = hash; + return hash; + } + + public static org.rtassembly.npgraph.grpc.ResponseAssembly parseFrom( + java.nio.ByteBuffer data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.rtassembly.npgraph.grpc.ResponseAssembly parseFrom( + java.nio.ByteBuffer data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.rtassembly.npgraph.grpc.ResponseAssembly parseFrom( + com.google.protobuf.ByteString data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.rtassembly.npgraph.grpc.ResponseAssembly parseFrom( + com.google.protobuf.ByteString data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.rtassembly.npgraph.grpc.ResponseAssembly parseFrom(byte[] data) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data); + } + public static org.rtassembly.npgraph.grpc.ResponseAssembly parseFrom( + byte[] data, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return PARSER.parseFrom(data, extensionRegistry); + } + public static org.rtassembly.npgraph.grpc.ResponseAssembly parseFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static org.rtassembly.npgraph.grpc.ResponseAssembly parseFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + public static org.rtassembly.npgraph.grpc.ResponseAssembly parseDelimitedFrom(java.io.InputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input); + } + public static org.rtassembly.npgraph.grpc.ResponseAssembly parseDelimitedFrom( + java.io.InputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseDelimitedWithIOException(PARSER, input, extensionRegistry); + } + public static org.rtassembly.npgraph.grpc.ResponseAssembly parseFrom( + com.google.protobuf.CodedInputStream input) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input); + } + public static org.rtassembly.npgraph.grpc.ResponseAssembly parseFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + return com.google.protobuf.GeneratedMessageV3 + .parseWithIOException(PARSER, input, extensionRegistry); + } + + @java.lang.Override + public Builder newBuilderForType() { return newBuilder(); } + public static Builder newBuilder() { + return DEFAULT_INSTANCE.toBuilder(); + } + public static Builder newBuilder(org.rtassembly.npgraph.grpc.ResponseAssembly prototype) { + return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype); + } + @java.lang.Override + public Builder toBuilder() { + return this == DEFAULT_INSTANCE + ? new Builder() : new Builder().mergeFrom(this); + } + + @java.lang.Override + protected Builder newBuilderForType( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + Builder builder = new Builder(parent); + return builder; + } + /** + *
+   * Response from the assembly guide service: useful or not
+   * 
+ * + * Protobuf type {@code assembly.ResponseAssembly} + */ + public static final class Builder extends + com.google.protobuf.GeneratedMessageV3.Builder implements + // @@protoc_insertion_point(builder_implements:assembly.ResponseAssembly) + org.rtassembly.npgraph.grpc.ResponseAssemblyOrBuilder { + public static final com.google.protobuf.Descriptors.Descriptor + getDescriptor() { + return org.rtassembly.npgraph.grpc.AssemblyGuideProto.internal_static_assembly_ResponseAssembly_descriptor; + } + + @java.lang.Override + protected com.google.protobuf.GeneratedMessageV3.FieldAccessorTable + internalGetFieldAccessorTable() { + return org.rtassembly.npgraph.grpc.AssemblyGuideProto.internal_static_assembly_ResponseAssembly_fieldAccessorTable + .ensureFieldAccessorsInitialized( + org.rtassembly.npgraph.grpc.ResponseAssembly.class, org.rtassembly.npgraph.grpc.ResponseAssembly.Builder.class); + } + + // Construct using org.rtassembly.npgraph.grpc.ResponseAssembly.newBuilder() + private Builder() { + maybeForceBuilderInitialization(); + } + + private Builder( + com.google.protobuf.GeneratedMessageV3.BuilderParent parent) { + super(parent); + maybeForceBuilderInitialization(); + } + private void maybeForceBuilderInitialization() { + if (com.google.protobuf.GeneratedMessageV3 + .alwaysUseFieldBuilders) { + } + } + @java.lang.Override + public Builder clear() { + super.clear(); + readId_ = ""; + + usefulness_ = false; + + return this; + } + + @java.lang.Override + public com.google.protobuf.Descriptors.Descriptor + getDescriptorForType() { + return org.rtassembly.npgraph.grpc.AssemblyGuideProto.internal_static_assembly_ResponseAssembly_descriptor; + } + + @java.lang.Override + public org.rtassembly.npgraph.grpc.ResponseAssembly getDefaultInstanceForType() { + return org.rtassembly.npgraph.grpc.ResponseAssembly.getDefaultInstance(); + } + + @java.lang.Override + public org.rtassembly.npgraph.grpc.ResponseAssembly build() { + org.rtassembly.npgraph.grpc.ResponseAssembly result = buildPartial(); + if (!result.isInitialized()) { + throw newUninitializedMessageException(result); + } + return result; + } + + @java.lang.Override + public org.rtassembly.npgraph.grpc.ResponseAssembly buildPartial() { + org.rtassembly.npgraph.grpc.ResponseAssembly result = new org.rtassembly.npgraph.grpc.ResponseAssembly(this); + result.readId_ = readId_; + result.usefulness_ = usefulness_; + onBuilt(); + return result; + } + + @java.lang.Override + public Builder clone() { + return super.clone(); + } + @java.lang.Override + public Builder setField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.setField(field, value); + } + @java.lang.Override + public Builder clearField( + com.google.protobuf.Descriptors.FieldDescriptor field) { + return super.clearField(field); + } + @java.lang.Override + public Builder clearOneof( + com.google.protobuf.Descriptors.OneofDescriptor oneof) { + return super.clearOneof(oneof); + } + @java.lang.Override + public Builder setRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + int index, java.lang.Object value) { + return super.setRepeatedField(field, index, value); + } + @java.lang.Override + public Builder addRepeatedField( + com.google.protobuf.Descriptors.FieldDescriptor field, + java.lang.Object value) { + return super.addRepeatedField(field, value); + } + @java.lang.Override + public Builder mergeFrom(com.google.protobuf.Message other) { + if (other instanceof org.rtassembly.npgraph.grpc.ResponseAssembly) { + return mergeFrom((org.rtassembly.npgraph.grpc.ResponseAssembly)other); + } else { + super.mergeFrom(other); + return this; + } + } + + public Builder mergeFrom(org.rtassembly.npgraph.grpc.ResponseAssembly other) { + if (other == org.rtassembly.npgraph.grpc.ResponseAssembly.getDefaultInstance()) return this; + if (!other.getReadId().isEmpty()) { + readId_ = other.readId_; + onChanged(); + } + if (other.getUsefulness() != false) { + setUsefulness(other.getUsefulness()); + } + this.mergeUnknownFields(other.unknownFields); + onChanged(); + return this; + } + + @java.lang.Override + public final boolean isInitialized() { + return true; + } + + @java.lang.Override + public Builder mergeFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws java.io.IOException { + org.rtassembly.npgraph.grpc.ResponseAssembly parsedMessage = null; + try { + parsedMessage = PARSER.parsePartialFrom(input, extensionRegistry); + } catch (com.google.protobuf.InvalidProtocolBufferException e) { + parsedMessage = (org.rtassembly.npgraph.grpc.ResponseAssembly) e.getUnfinishedMessage(); + throw e.unwrapIOException(); + } finally { + if (parsedMessage != null) { + mergeFrom(parsedMessage); + } + } + return this; + } + + private java.lang.Object readId_ = ""; + /** + * string read_id = 1; + * @return The readId. + */ + public java.lang.String getReadId() { + java.lang.Object ref = readId_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + readId_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string read_id = 1; + * @return The bytes for readId. + */ + public com.google.protobuf.ByteString + getReadIdBytes() { + java.lang.Object ref = readId_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + readId_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string read_id = 1; + * @param value The readId to set. + * @return This builder for chaining. + */ + public Builder setReadId( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + + readId_ = value; + onChanged(); + return this; + } + /** + * string read_id = 1; + * @return This builder for chaining. + */ + public Builder clearReadId() { + + readId_ = getDefaultInstance().getReadId(); + onChanged(); + return this; + } + /** + * string read_id = 1; + * @param value The bytes for readId to set. + * @return This builder for chaining. + */ + public Builder setReadIdBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + + readId_ = value; + onChanged(); + return this; + } + + private boolean usefulness_ ; + /** + * bool usefulness = 2; + * @return The usefulness. + */ + public boolean getUsefulness() { + return usefulness_; + } + /** + * bool usefulness = 2; + * @param value The usefulness to set. + * @return This builder for chaining. + */ + public Builder setUsefulness(boolean value) { + + usefulness_ = value; + onChanged(); + return this; + } + /** + * bool usefulness = 2; + * @return This builder for chaining. + */ + public Builder clearUsefulness() { + + usefulness_ = false; + onChanged(); + return this; + } + @java.lang.Override + public final Builder setUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.setUnknownFields(unknownFields); + } + + @java.lang.Override + public final Builder mergeUnknownFields( + final com.google.protobuf.UnknownFieldSet unknownFields) { + return super.mergeUnknownFields(unknownFields); + } + + + // @@protoc_insertion_point(builder_scope:assembly.ResponseAssembly) + } + + // @@protoc_insertion_point(class_scope:assembly.ResponseAssembly) + private static final org.rtassembly.npgraph.grpc.ResponseAssembly DEFAULT_INSTANCE; + static { + DEFAULT_INSTANCE = new org.rtassembly.npgraph.grpc.ResponseAssembly(); + } + + public static org.rtassembly.npgraph.grpc.ResponseAssembly getDefaultInstance() { + return DEFAULT_INSTANCE; + } + + private static final com.google.protobuf.Parser + PARSER = new com.google.protobuf.AbstractParser() { + @java.lang.Override + public ResponseAssembly parsePartialFrom( + com.google.protobuf.CodedInputStream input, + com.google.protobuf.ExtensionRegistryLite extensionRegistry) + throws com.google.protobuf.InvalidProtocolBufferException { + return new ResponseAssembly(input, extensionRegistry); + } + }; + + public static com.google.protobuf.Parser parser() { + return PARSER; + } + + @java.lang.Override + public com.google.protobuf.Parser getParserForType() { + return PARSER; + } + + @java.lang.Override + public org.rtassembly.npgraph.grpc.ResponseAssembly getDefaultInstanceForType() { + return DEFAULT_INSTANCE; + } + +} + diff --git a/src/main/java/org/rtassembly/npgraph/grpc/ResponseAssemblyOrBuilder.java b/src/main/java/org/rtassembly/npgraph/grpc/ResponseAssemblyOrBuilder.java new file mode 100644 index 0000000..ddcfd73 --- /dev/null +++ b/src/main/java/org/rtassembly/npgraph/grpc/ResponseAssemblyOrBuilder.java @@ -0,0 +1,27 @@ +// Generated by the protocol buffer compiler. DO NOT EDIT! +// source: npgraph_service.proto + +package org.rtassembly.npgraph.grpc; + +public interface ResponseAssemblyOrBuilder extends + // @@protoc_insertion_point(interface_extends:assembly.ResponseAssembly) + com.google.protobuf.MessageOrBuilder { + + /** + * string read_id = 1; + * @return The readId. + */ + java.lang.String getReadId(); + /** + * string read_id = 1; + * @return The bytes for readId. + */ + com.google.protobuf.ByteString + getReadIdBytes(); + + /** + * bool usefulness = 2; + * @return The usefulness. + */ + boolean getUsefulness(); +} diff --git a/src/main/java/org/rtassembly/npgraph/grpc/package-info.java b/src/main/java/org/rtassembly/npgraph/grpc/package-info.java new file mode 100644 index 0000000..a8306a2 --- /dev/null +++ b/src/main/java/org/rtassembly/npgraph/grpc/package-info.java @@ -0,0 +1,8 @@ +/** + * + */ +/** + * @author sonnguyen + * + */ +package org.rtassembly.npgraph.grpc; \ No newline at end of file diff --git a/src/main/java/org/rtassembly/npscarf/RealtimeScaffolding.java b/src/main/java/org/rtassembly/npscarf/RealtimeScaffolding.java index d1b48e4..9bee231 100644 --- a/src/main/java/org/rtassembly/npscarf/RealtimeScaffolding.java +++ b/src/main/java/org/rtassembly/npscarf/RealtimeScaffolding.java @@ -209,7 +209,7 @@ protected void analysis() { sg.connectBridges(); try { - // This function is for the sake of real-time annotation experiments being more readable + // This function make real-time annotation experiments more readable scaffolding.graph.printRT(scaffolding.currentBaseCount); sg.printSequences(ScaffoldGraph.updateGenome,false); outOS.print("Time |\tStep |\tRead count |\tBase count|\tNumber of scaffolds|\tCircular scaffolds |\tN50 | \tBreaks (maxlen)\n"); diff --git a/src/main/proto/npgraph_service.proto b/src/main/proto/npgraph_service.proto new file mode 100644 index 0000000..51006c4 --- /dev/null +++ b/src/main/proto/npgraph_service.proto @@ -0,0 +1,38 @@ +syntax = "proto3"; + +option java_multiple_files = true; +option java_package = "org.rtassembly.npgraph.grpc"; +option java_outer_classname = "AssemblyGuideProto"; +option objc_class_prefix = "AGP"; + +package assembly; + +// Interface exported by the server. +service AssemblyGuide { + rpc GetAssemblyContribution(RequestAssembly) returns (ResponseAssembly) {} +} + +// An alignment from minimap2 sent to the server +message AlignmentMsg { + string query_name = 1; + int32 query_length = 2; + int32 query_start = 3; + int32 query_end = 4; + bool strand = 5; + string target_name = 6; + int32 target_length = 7; + int32 target_start = 8; + int32 target_end = 9; + int32 quality = 10; + int32 score = 11; +} +// Client sent all hits from a chunk simultaneously +message RequestAssembly { + string read_id = 1; + repeated AlignmentMsg hits_list = 2; +} +// Response from the assembly guide service: useful or not +message ResponseAssembly { + string read_id = 1; + bool usefulness = 2; +} \ No newline at end of file