This is a test version of Biostars. For the public version, visit https://www.biostars.org.
assemble_polyg_meta_v1.sh is removing most of my reads

Greetings BioStars,

I'm excited to be using this assemble_polyg_meta_v1.sh pipeline from BBTools to benchmark shotgun metagenome assemblies from the CAMI-challenge datasets. I am using the Simulated Illumina HiSeq 2500 metagenome dataset from a plant rhizosphere environment (Illumina only; no long reads). The outputs below concern a single sample (sample 0) from this dataset.

Here are the characteristics of this simulated dataset:
Number of samples: 21.
Total size: 105 Gb.
Read length: 2x150 bp.
Insert size mean: 270 bp.
Insert size s.d.: 20 bp.
Files were created in 2021, so I assume a HiSeq 2500 error profile from that era was used for the simulation.

My question is, why am I losing so much of my data?
I performed all of the steps in the pipeline except for optical dedupe and filtering by low-quality flowcell areas, since these aspects of Illumina data are not simulated in these synthetic read samples.

As you can see from the assemble_polyg_meta_v1.sh link above, this pipeline has two parts:
Part A) Poly-G removal and other generic preprocessing
Part B) Assembly improvement, specific to metagenome-assembled genome pipelines. These two stages are also referenced in the announcement of the new polyfilter.sh tool.

This is a pre-processing pipeline, designed to clean reads before assembly into MAGs via SPAdes or similar. The assembly referenced below is done with tadpole.sh and is only for pre-processing, in order to recalibrate quality scores for more accurate quality filtering and trimming.

Here are my file sizes showing data loss at different steps in the pipeline. It seems I am losing a big chunk of data (800 MB) during the quality score recalibration step ('Step 4') in Part A, and another big chunk (2.3 GB) during the error correction ('Step 1') in Part B, and another 500 MB towards the end of Part B ('Step 3'). I can provide excerpts from the log files from these outputs, though I did not see any run-time errors or script outputs that I could connect to the data removal (though I could have easily missed it).

I would be grateful for any info or advice.

PART A: Poly-G removal and other generic preprocessing

Size         Step         Filename
4.4G    INFILE A    Anonymous-reads.fq.gz
4.3G    Step 1       raw_trimmed.fq.gz
4.3G    Step 2      filtered.fq.gz
4.3G    Step 3       clean.fq.gz
2.3G    (assembly)  qecc.fq.gz
11M     (assemb out)    quick.fa
2.0G    (assembly mapped) clean.sam.gz
3.6G    Step 4       clean_recal.fq.gz
3.6G    Step 5       polyfilter_clean_recal.fq.gz
3.6G    OUTFILE A   hdist2.fq.gz

PART B: Assembly improvement

3.6G    INFILE B    hdist2.fq.gz
1.3G    Step 1 out  ecco.fq.gz
1.2G    Step 2 out  ecct.fq.gz
195K    Step 3        merged_rem3.fq.gz
28    Step 3        merged_rem2.fq.gz
36M   Step 3        merged_rem.fq.gz
676M    Step 3      merged0.fq.gz
361M    Step 3       unmerged_rem3.fq.gz
362M    Step 3       unmerged_rem2.fq.gz
362M    Step 3       unmerged_rem.fq.gz
401M    Step 3       unmerged0.fq.gz
712M    OUTFILE B   merged_both.fq.gz
12M      OUTFILE B  qtrimmed.fq.gz
bbmerge ecco shotgun polyfilter

Edit to add info:

I thought maybe the loss of data at the Part A: quality recalibration ('Step 4'; 800 MB lost) might be due to some filtering of very low depth kmers. (e.g. a long tail of kmer depth=1 for plant rhizosphere data likely exists in the raw data; and this was being filtered out somehow).

To test, I used khist.sh:

khist.sh in="$IN" hist="$OUT"/"$f"_kmer-hist-zeroes.tsv peaks="$OUT"/"$f"_kmer-hist-peaks-zeroes.tsv minprob=0 minqual=0 mindepth=0 printzerocoverage=true minq=6 minprob=0.5

I am not that experienced at these analyses, so I was surprised to learn that the low-depth coverage was identical between raw and qual-recalib kmers for this sample. Instead, the quality recalibration seemed to have shaved off the higher depth kmers, if I'm interpreting the results correctly.

Specifically, the highest kmer depth values recorded in khist (tail) were about twice as deep for the RAW data:

         #Depth    Raw_Count    Unique_Kmers  
RAW:    2426           3703        2  
RECAL: 1228        2372       2

The loss of high-depth kmers reminds me of read normalization, which is commonly done before assembly, but I do not want read normalization to happen at this quality recalib step.

Here again is the code in the polyfilter pipeline that occurs during the quality recalibration step. Note that I did not include the usetiles flag in my pipeline because these simulated reads do not have tile info.

#Make recal matrices
calctruequality.sh "$HIGH" "$ARGS" in=clean.sam.gz usetiles ref=quick.fa callvars

#Recalibrate quality
bbduk.sh "$LOW" "$ARGS" in=clean.fq.gz out=clean_recal_tile.fq.gz recalibrate usetiles

Here's the kmer hist comparison, in case others have additional comments.

enter image description here

Edit to add two more pieces of info:

Reading the bbduk.sh params again, I found this:

Note - if ktrim, kmask, and ksplit are unset, the default behavior is kfilter.
All kmer processing modes are mutually exclusive.

The defaults for these params are here:

qtrim=f             Trim read ends to remove bases with quality below trimq.
ksplit=f            For single-ended reads only.  Reads will be split into
kmask=              Replace bases matching ref kmers with another symbol.
                    Allows any non-whitespace character, and processes short
                    kmers on both ends if mink is set.  'kmask=lc' will
                    convert masked bases to lowercase.
    k=31

So, my assumption that bbduk.sh is running quality score recalibration only is wrong, it seems? It is also running a kfilter by default, for this quality score recalibration step.

To test this theory, I reran the entire polyfilter pipeline as I had done previously, but adding outm=recal-matched.fq.gz to the quality recalib step, as shown here:

bbduk.sh -Xmx146553m -Xms200m in=clean.fq.gz out=clean_recal.fq.gz outm=recal-matched.fq.gz recalibrate;

And re-checked the file sizes:

#SIZE     #STEP       #FILE NAME
4.4G    INFILE A    Anonymous-reads.fq.gz
    …   
3.6G    Step 4  clean_recal.fq.gz
28       Step 4 recal-matched.fq.gz

So, I am not able to explain this change from 4.4GB to 3.6GB with kfiltering, it seems, since the outmatched file is basically empty.

Second piece of info: checking difference in total number of reads, rather than file size. I used lhist for this and though I ran it twice, I am assuming I am making some errors because somehow I end up with slightly more reads after running the quality recalibration:

RAW anonymous_reads.fq.gz_readLen-hist.tsv
Length  Count
135 146
136 267
137 587
138 1262
139 2785
140 5557
141 10487
142 15919
143 16096
144 16049
150 33175769
SUM:    33,244,924
QUAL-RECALIB     clean_recal.fq.gz_readLen-hist.tsv
Length  Count
150 33,247,226

So, the 800 MB smaller file size for clean_recal.fq.gz may just be more efficient file compression? But confused with the change in read length distribution and read number from raw to recalibrated. Also, maybe the difference in kmer depth is just due to a few outlier reads?

Summary of read counts using read length histograms, lhist.

# Name                 File Size        read len range, bp    Sum read counts
anonymous_reads.fq.gz     4.4GB          135-150 bp           33,244,942
clean_recal.fq.gz         3.6GB            150 bp             33,247,226
histd2.fq.gz              3.6GB           135-150 bp         33,244,942
ecco.fq.gz                1.3GB           15 - 150 bp         10,590,400
merged_both.fq.gz         712MB           15- 291 bp         3,660,983 (7,321,966)

So, it seems like the only big chop of 2/3 of my reads is happening in the error correction step, to create ecco.fq.gz in the first step of Part B. I assume the adapters.fa file is the same as the one generated earlier in the pipeline.

The information for prefilter tells me that changing this setting will conserve my long tail of kmers at depth=1. I will check by re-running with prefilter=0 and compare. This adjustment to prefilter=0 seems like it will help us recover more rare taxa when we start doing co-assemblies, rather than individual sample assemblies, as long as we can wrangle enough RAM for the larger kmer tables.

RESULT: ecco.fq.gz is still 1.3GB when prefilter=0.

bbmerge.sh "$MAX" "$ARGS" in=hdist2.fq.gz out=ecco.fq.gz ecco mix adapters=adapters.fa kfilter=1 k=31 prefilter=1
prefilter=0          If set to a positive integer, use a countmin sketch to 
                     ignore kmers with depth of that value or lower, to
                     reduce memory usage.

The last parameter that caught my eye for this step is kfilter=1.

kfilter=0            Ban overlaps that create kmers with count below
                     this value (0 disables).  If this is used minprob should
                     probably be set to 0.  Requires good coverage.

minprob=0.5          Ignore kmers with overall probability of correctness 
                     below this, to reduce memory usage.

I reran with prefilter=0, kfilter=0 and minprob=0.

bbmerge-auto.sh -Xmx207642m -Xms200m t=2 in="$IN"/hdist2.fq.gz out="$OUT"/ecco.fq.gz ecco mix adapters="$IN"/adapters.fa kfilter=0 k=31 prefilter=0 minprob=0

RESULT: ecco.fq.gz is still 1.3GB.

I can see from B Bushnell et al 2017 paper announcing BBmerge, in Fig 1 panel B for example, when reads do not overlap, they are discarded. But, I thought with the ecco flag in place, there is error correction by overlap only. So, esp with filters set to zero, there should be no discarded reads at this step, regardless of whether they overlap or not, correct? The actual outfile of merged reads happens later in the pipeline.

There are a few issues here. Using the link you included above for the sample 0 reads, BBMerge is throwing an error that there are non-ACTG bases included. You need to add the flag itn to the bbmerge.sh command to address that. Unfortunately the clumpify.sh step will not run with this data because it is simulated in such a way that it does not have the "illumina format" coordinate information in the headers. So I end up with zero byte files as output, essentially derailing the remaining pipeline.

You must have changed the script provided by Brian to get it to go past the bbduk.sh step otherwise I am not sure how you are getting the results included in original post.

Thank you GenoMax for your reply and comments!

Thank you for the itn flag info. I don't remember seeing bbmerge throwing this error, but I will go back and look at my logs to see if I can find it. Maybe it's specific to versions of bbmerge?

Yes, I performed all of the steps in the pipeline except for optical dedupe and filtering by low-quality flowcell areas, since these aspects of Illumina data are not simulated in these synthetic read samples.

I simply commented out those steps, since they were not required for these simulated reads, and updated the input/output file names to accommodate:

Start of pipeline Part A:
#find adapter seq
bbmerge.sh in="$IN" outa=adapters.fa;

# Trimming adapter sequence
bbduk.sh -Xmx146553m -Xms200m in="$IN" out=raw_trimmed.fq.gz tbo tpe hdist=2 k=23 mink=9 hdist2=1 ref=adapters.fa minlen=135 ktrim=r;

#Remove optical duplicates: this step was omitted
#clumpify.sh in=raw_trimmed.fq.gz out=deduped.fq.gz passes=4 dedupe optical dist=50;

#filter artifacts
bbduk.sh -Xmx146553m -Xms200m ref=artifacts,phix literal=polya in=raw_trimmed.fq.gz k=31 hdist=1 out=filtered.fq.gz;

etc.

0 answers

No answers yet.

Log in to answer this question.