0
|
1 #!/usr/bin/perl --
|
|
2 use strict;
|
|
3 use warnings;
|
|
4 use IO::Handle;
|
|
5 use Cwd;
|
|
6 $|++;
|
|
7 use Getopt::Long;
|
|
8
|
|
9
|
|
10 ## This program is Copyright (C) 2010-13, Felix Krueger (felix.krueger@babraham.ac.uk)
|
|
11
|
|
12 ## This program is free software: you can redistribute it and/or modify
|
|
13 ## it under the terms of the GNU General Public License as published by
|
|
14 ## the Free Software Foundation, either version 3 of the License, or
|
|
15 ## (at your option) any later version.
|
|
16
|
|
17 ## This program is distributed in the hope that it will be useful,
|
|
18 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
19 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
20 ## GNU General Public License for more details.
|
|
21
|
|
22 ## You should have received a copy of the GNU General Public License
|
|
23 ## along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
24
|
|
25
|
|
26 my $parent_dir = getcwd;
|
|
27 my $bismark_version = 'v0.7.12';
|
|
28 my $command_line = join (" ",@ARGV);
|
|
29
|
|
30 ### before processing the command line we will replace --solexa1.3-quals with --phred64-quals as the '.' in the option name will cause Getopt::Long to fail
|
|
31 foreach my $arg (@ARGV){
|
|
32 if ($arg eq '--solexa1.3-quals'){
|
|
33 $arg = '--phred64-quals';
|
|
34 }
|
|
35 }
|
|
36 my @filenames; # will be populated by processing the command line
|
|
37
|
|
38 my ($genome_folder,$CT_index_basename,$GA_index_basename,$path_to_bowtie,$sequence_file_format,$bowtie_options,$directional,$unmapped,$ambiguous,$phred64,$solexa,$output_dir,$bowtie2,$vanilla,$sam_no_hd,$skip,$upto,$temp_dir,$non_bs_mm,$insertion_open,$insertion_extend,$deletion_open,$deletion_extend,$gzip,$bam,$samtools_path,$pbat) = process_command_line();
|
|
39
|
|
40 my @fhs; # stores alignment process names, bisulfite index location, bowtie filehandles and the number of times sequences produced an alignment
|
|
41 my %chromosomes; # stores the chromosome sequences of the mouse genome
|
|
42 my %counting; # counting various events
|
|
43
|
|
44 my $seqID_contains_tabs;
|
|
45
|
|
46 foreach my $filename (@filenames){
|
|
47
|
|
48 chdir $parent_dir or die "Unable to move to initial working directory $!\n";
|
|
49 ### resetting the counting hash and fhs
|
|
50 reset_counters_and_fhs($filename);
|
|
51 $seqID_contains_tabs = 0;
|
|
52
|
|
53 ### PAIRED-END ALIGNMENTS
|
|
54 if ($filename =~ ','){
|
|
55 my ($C_to_T_infile_1,$G_to_A_infile_1); # to be made from mate1 file
|
|
56
|
|
57 $fhs[0]->{name} = 'CTread1GAread2CTgenome';
|
|
58 $fhs[1]->{name} = 'GAread1CTread2GAgenome';
|
|
59 $fhs[2]->{name} = 'GAread1CTread2CTgenome';
|
|
60 $fhs[3]->{name} = 'CTread1GAread2GAgenome';
|
|
61
|
|
62 warn "\nPaired-end alignments will be performed\n",'='x39,"\n\n";
|
|
63
|
|
64 my ($filename_1,$filename_2) = (split (/,/,$filename));
|
|
65 warn "The provided filenames for paired-end alignments are $filename_1 and $filename_2\n";
|
|
66
|
|
67 ### additional variables only for paired-end alignments
|
|
68 my ($C_to_T_infile_2,$G_to_A_infile_2); # to be made from mate2 file
|
|
69
|
|
70 ### FastA format
|
|
71 if ($sequence_file_format eq 'FASTA'){
|
|
72 warn "Input files are in FastA format\n";
|
|
73
|
|
74 if ($directional){
|
|
75 ($C_to_T_infile_1) = biTransformFastAFiles_paired_end ($filename_1,1); # also passing the read number
|
|
76 ($G_to_A_infile_2) = biTransformFastAFiles_paired_end ($filename_2,2);
|
|
77
|
|
78 $fhs[0]->{inputfile_1} = $C_to_T_infile_1;
|
|
79 $fhs[0]->{inputfile_2} = $G_to_A_infile_2;
|
|
80 $fhs[1]->{inputfile_1} = undef;
|
|
81 $fhs[1]->{inputfile_2} = undef;
|
|
82 $fhs[2]->{inputfile_1} = undef;
|
|
83 $fhs[2]->{inputfile_2} = undef;
|
|
84 $fhs[3]->{inputfile_1} = $C_to_T_infile_1;
|
|
85 $fhs[3]->{inputfile_2} = $G_to_A_infile_2;
|
|
86 }
|
|
87 else{
|
|
88 ($C_to_T_infile_1,$G_to_A_infile_1) = biTransformFastAFiles_paired_end ($filename_1,1); # also passing the read number
|
|
89 ($C_to_T_infile_2,$G_to_A_infile_2) = biTransformFastAFiles_paired_end ($filename_2,2);
|
|
90
|
|
91 $fhs[0]->{inputfile_1} = $C_to_T_infile_1;
|
|
92 $fhs[0]->{inputfile_2} = $G_to_A_infile_2;
|
|
93 $fhs[1]->{inputfile_1} = $G_to_A_infile_1;
|
|
94 $fhs[1]->{inputfile_2} = $C_to_T_infile_2;
|
|
95 $fhs[2]->{inputfile_1} = $G_to_A_infile_1;
|
|
96 $fhs[2]->{inputfile_2} = $C_to_T_infile_2;
|
|
97 $fhs[3]->{inputfile_1} = $C_to_T_infile_1;
|
|
98 $fhs[3]->{inputfile_2} = $G_to_A_infile_2;
|
|
99 }
|
|
100
|
|
101 if ($bowtie2){
|
|
102 paired_end_align_fragments_to_bisulfite_genome_fastA_bowtie2 ($C_to_T_infile_1,$G_to_A_infile_1,$C_to_T_infile_2,$G_to_A_infile_2);
|
|
103 }
|
|
104 else{
|
|
105 paired_end_align_fragments_to_bisulfite_genome_fastA ($C_to_T_infile_1,$G_to_A_infile_1,$C_to_T_infile_2,$G_to_A_infile_2);
|
|
106 }
|
|
107 }
|
|
108
|
|
109 ### FastQ format
|
|
110 else{
|
|
111 warn "Input files are in FastQ format\n";
|
|
112 if ($directional){
|
|
113 if ($bowtie2){
|
|
114 ($C_to_T_infile_1) = biTransformFastQFiles_paired_end ($filename_1,1); # also passing the read number
|
|
115 ($G_to_A_infile_2) = biTransformFastQFiles_paired_end ($filename_2,2);
|
|
116
|
|
117 $fhs[0]->{inputfile_1} = $C_to_T_infile_1;
|
|
118 $fhs[0]->{inputfile_2} = $G_to_A_infile_2;
|
|
119 $fhs[1]->{inputfile_1} = undef;
|
|
120 $fhs[1]->{inputfile_2} = undef;
|
|
121 $fhs[2]->{inputfile_1} = undef;
|
|
122 $fhs[2]->{inputfile_2} = undef;
|
|
123 $fhs[3]->{inputfile_1} = $C_to_T_infile_1;
|
|
124 $fhs[3]->{inputfile_2} = $G_to_A_infile_2;
|
|
125 }
|
|
126 else{ # Bowtie 1 alignments
|
|
127 if ($gzip){
|
|
128 ($C_to_T_infile_1) = biTransformFastQFiles_paired_end_bowtie1_gzip ($filename_1,$filename_2); # passing both reads at the same time
|
|
129
|
|
130 $fhs[0]->{inputfile_1} = $C_to_T_infile_1; # this file contains both read 1 and read 2 in tab delimited format
|
|
131 $fhs[0]->{inputfile_2} = undef; # no longer needed
|
|
132 $fhs[1]->{inputfile_1} = undef;
|
|
133 $fhs[1]->{inputfile_2} = undef;
|
|
134 $fhs[2]->{inputfile_1} = undef;
|
|
135 $fhs[2]->{inputfile_2} = undef;
|
|
136 $fhs[3]->{inputfile_1} = $C_to_T_infile_1; # this file contains both read 1 and read 2 in tab delimited format
|
|
137 $fhs[3]->{inputfile_2} = undef; # no longer needed
|
|
138 }
|
|
139 else{
|
|
140 ($C_to_T_infile_1) = biTransformFastQFiles_paired_end ($filename_1,1); # also passing the read number
|
|
141 ($G_to_A_infile_2) = biTransformFastQFiles_paired_end ($filename_2,2);
|
|
142
|
|
143 $fhs[0]->{inputfile_1} = $C_to_T_infile_1;
|
|
144 $fhs[0]->{inputfile_2} = $G_to_A_infile_2;
|
|
145 $fhs[1]->{inputfile_1} = undef;
|
|
146 $fhs[1]->{inputfile_2} = undef;
|
|
147 $fhs[2]->{inputfile_1} = undef;
|
|
148 $fhs[2]->{inputfile_2} = undef;
|
|
149 $fhs[3]->{inputfile_1} = $C_to_T_infile_1;
|
|
150 $fhs[3]->{inputfile_2} = $G_to_A_infile_2;
|
|
151 }
|
|
152 }
|
|
153 }
|
|
154 elsif($pbat){ # PBAT-Seq
|
|
155 ### At the moment we are only performing uncompressed FastQ alignments with Bowtie1
|
|
156 ($C_to_T_infile_1,$G_to_A_infile_1) = biTransformFastQFiles_paired_end ($filename_1,1); # also passing the read number
|
|
157 ($C_to_T_infile_2,$G_to_A_infile_2) = biTransformFastQFiles_paired_end ($filename_2,2);
|
|
158
|
|
159 $fhs[0]->{inputfile_1} = undef;
|
|
160 $fhs[0]->{inputfile_2} = undef;
|
|
161 $fhs[1]->{inputfile_1} = $G_to_A_infile_1;
|
|
162 $fhs[1]->{inputfile_2} = $C_to_T_infile_2;
|
|
163 $fhs[2]->{inputfile_1} = $G_to_A_infile_1;
|
|
164 $fhs[2]->{inputfile_2} = $C_to_T_infile_2;
|
|
165 $fhs[3]->{inputfile_1} = undef;
|
|
166 $fhs[3]->{inputfile_2} = undef;
|
|
167 }
|
|
168 else{
|
|
169 if ($bowtie2){
|
|
170 ($C_to_T_infile_1,$G_to_A_infile_1) = biTransformFastQFiles_paired_end ($filename_1,1); # also passing the read number
|
|
171 ($C_to_T_infile_2,$G_to_A_infile_2) = biTransformFastQFiles_paired_end ($filename_2,2);
|
|
172
|
|
173 $fhs[0]->{inputfile_1} = $C_to_T_infile_1;
|
|
174 $fhs[0]->{inputfile_2} = $G_to_A_infile_2;
|
|
175 $fhs[1]->{inputfile_1} = $G_to_A_infile_1;
|
|
176 $fhs[1]->{inputfile_2} = $C_to_T_infile_2;
|
|
177 $fhs[2]->{inputfile_1} = $G_to_A_infile_1;
|
|
178 $fhs[2]->{inputfile_2} = $C_to_T_infile_2;
|
|
179 $fhs[3]->{inputfile_1} = $C_to_T_infile_1;
|
|
180 $fhs[3]->{inputfile_2} = $G_to_A_infile_2;
|
|
181 }
|
|
182 else{ # Bowtie 1 alignments
|
|
183 if ($gzip){
|
|
184 ($C_to_T_infile_1,$G_to_A_infile_1) = biTransformFastQFiles_paired_end_bowtie1_gzip ($filename_1,$filename_2); # passing both reads at the same time
|
|
185
|
|
186 $fhs[0]->{inputfile_1} = $C_to_T_infile_1;
|
|
187 $fhs[0]->{inputfile_2} = undef; # not needed for compressed temp files
|
|
188 $fhs[1]->{inputfile_1} = $G_to_A_infile_1;
|
|
189 $fhs[1]->{inputfile_2} = undef;
|
|
190 $fhs[2]->{inputfile_1} = $G_to_A_infile_1;
|
|
191 $fhs[2]->{inputfile_2} = undef;
|
|
192 $fhs[3]->{inputfile_1} = $C_to_T_infile_1;
|
|
193 $fhs[3]->{inputfile_2} = undef; # not needed for compressed temp files
|
|
194 }
|
|
195 else{ #uncompressed temp files
|
|
196 ($C_to_T_infile_1,$G_to_A_infile_1) = biTransformFastQFiles_paired_end ($filename_1,1); # also passing the read number
|
|
197 ($C_to_T_infile_2,$G_to_A_infile_2) = biTransformFastQFiles_paired_end ($filename_2,2);
|
|
198
|
|
199 $fhs[0]->{inputfile_1} = $C_to_T_infile_1;
|
|
200 $fhs[0]->{inputfile_2} = $G_to_A_infile_2;
|
|
201 $fhs[1]->{inputfile_1} = $G_to_A_infile_1;
|
|
202 $fhs[1]->{inputfile_2} = $C_to_T_infile_2;
|
|
203 $fhs[2]->{inputfile_1} = $G_to_A_infile_1;
|
|
204 $fhs[2]->{inputfile_2} = $C_to_T_infile_2;
|
|
205 $fhs[3]->{inputfile_1} = $C_to_T_infile_1;
|
|
206 $fhs[3]->{inputfile_2} = $G_to_A_infile_2;
|
|
207 }
|
|
208 }
|
|
209 }
|
|
210 if ($bowtie2){
|
|
211 paired_end_align_fragments_to_bisulfite_genome_fastQ_bowtie2 ($C_to_T_infile_1,$G_to_A_infile_1,$C_to_T_infile_2,$G_to_A_infile_2);
|
|
212 }
|
|
213 else{
|
|
214 paired_end_align_fragments_to_bisulfite_genome_fastQ ($C_to_T_infile_1,$G_to_A_infile_1,$C_to_T_infile_2,$G_to_A_infile_2);
|
|
215 }
|
|
216 }
|
|
217 start_methylation_call_procedure_paired_ends($filename_1,$filename_2,$C_to_T_infile_1,$G_to_A_infile_1,$C_to_T_infile_2,$G_to_A_infile_2);
|
|
218 }
|
|
219
|
|
220 ### Else we are performing SINGLE-END ALIGNMENTS
|
|
221 else{
|
|
222 warn "\nSingle-end alignments will be performed\n",'='x39,"\n\n";
|
|
223 ### Initialising bisulfite conversion filenames
|
|
224 my ($C_to_T_infile,$G_to_A_infile);
|
|
225
|
|
226
|
|
227 ### FastA format
|
|
228 if ($sequence_file_format eq 'FASTA'){
|
|
229 warn "Inut file is in FastA format\n";
|
|
230 if ($directional){
|
|
231 ($C_to_T_infile) = biTransformFastAFiles ($filename);
|
|
232 $fhs[0]->{inputfile} = $fhs[1]->{inputfile} = $C_to_T_infile;
|
|
233 }
|
|
234 else{
|
|
235 ($C_to_T_infile,$G_to_A_infile) = biTransformFastAFiles ($filename);
|
|
236 $fhs[0]->{inputfile} = $fhs[1]->{inputfile} = $C_to_T_infile;
|
|
237 $fhs[2]->{inputfile} = $fhs[3]->{inputfile} = $G_to_A_infile;
|
|
238 }
|
|
239
|
|
240 ### Creating 4 different bowtie filehandles and storing the first entry
|
|
241 if ($bowtie2){
|
|
242 single_end_align_fragments_to_bisulfite_genome_fastA_bowtie2 ($C_to_T_infile,$G_to_A_infile);
|
|
243 }
|
|
244 else{
|
|
245 single_end_align_fragments_to_bisulfite_genome_fastA ($C_to_T_infile,$G_to_A_infile);
|
|
246 }
|
|
247 }
|
|
248
|
|
249 ## FastQ format
|
|
250 else{
|
|
251 warn "Input file is in FastQ format\n";
|
|
252 if ($directional){
|
|
253 ($C_to_T_infile) = biTransformFastQFiles ($filename);
|
|
254 $fhs[0]->{inputfile} = $fhs[1]->{inputfile} = $C_to_T_infile;
|
|
255 }
|
|
256 elsif($pbat){
|
|
257 ($G_to_A_infile) = biTransformFastQFiles ($filename);
|
|
258 $fhs[0]->{inputfile} = $fhs[1]->{inputfile} = $G_to_A_infile; # PBAT-Seq only uses the G to A converted files
|
|
259 }
|
|
260 else{
|
|
261 ($C_to_T_infile,$G_to_A_infile) = biTransformFastQFiles ($filename);
|
|
262 $fhs[0]->{inputfile} = $fhs[1]->{inputfile} = $C_to_T_infile;
|
|
263 $fhs[2]->{inputfile} = $fhs[3]->{inputfile} = $G_to_A_infile;
|
|
264 }
|
|
265
|
|
266 ### Creating up to 4 different bowtie filehandles and storing the first entry
|
|
267 if ($bowtie2){
|
|
268 single_end_align_fragments_to_bisulfite_genome_fastQ_bowtie2 ($C_to_T_infile,$G_to_A_infile);
|
|
269 }
|
|
270 elsif ($pbat){
|
|
271 single_end_align_fragments_to_bisulfite_genome_fastQ (undef,$G_to_A_infile);
|
|
272 }
|
|
273 else{
|
|
274 single_end_align_fragments_to_bisulfite_genome_fastQ ($C_to_T_infile,$G_to_A_infile);
|
|
275 }
|
|
276 }
|
|
277
|
|
278 start_methylation_call_procedure_single_ends($filename,$C_to_T_infile,$G_to_A_infile);
|
|
279
|
|
280 }
|
|
281 }
|
|
282
|
|
283 sub start_methylation_call_procedure_single_ends {
|
|
284 my ($sequence_file,$C_to_T_infile,$G_to_A_infile) = @_;
|
|
285 my ($dir,$filename);
|
|
286
|
|
287 if ($sequence_file =~ /\//){
|
|
288 ($dir,$filename) = $sequence_file =~ m/(.*\/)(.*)$/;
|
|
289 }
|
|
290 else{
|
|
291 $filename = $sequence_file;
|
|
292 }
|
|
293
|
|
294 ### printing all alignments to a results file
|
|
295 my $outfile = $filename;
|
|
296
|
|
297 if ($bowtie2){ # SAM format is the default for Bowtie 2
|
|
298 $outfile =~ s/$/_bt2_bismark.sam/;
|
|
299 }
|
|
300 elsif ($vanilla){ # vanilla custom Bismark output single-end output (like Bismark versions 0.5.X)
|
|
301 $outfile =~ s/$/_bismark.txt/;
|
|
302 }
|
|
303 else{ # SAM is the default output
|
|
304 $outfile =~ s/$/_bismark.sam/;
|
|
305 }
|
|
306
|
|
307 $bam = 0 unless (defined $bam);
|
|
308
|
|
309 if ($bam == 1){ ### Samtools is installed, writing out BAM directly
|
|
310 $outfile =~ s/sam/bam/;
|
|
311 open (OUT,"| $samtools_path view -bSh 2>/dev/null - > $output_dir$outfile") or die "Failed to write to $outfile: $!\n";
|
|
312 }
|
|
313 elsif($bam == 2){ ### no Samtools found on system. Using GZIP compression instead
|
|
314 $outfile .= '.gz';
|
|
315 open (OUT,"| gzip -c - > $output_dir$outfile") or die "Failed to write to $outfile: $!\n";
|
|
316 }
|
|
317 else{ # uncompressed ouput, default
|
|
318 open (OUT,'>',"$output_dir$outfile") or die "Failed to write to $outfile: $!\n";
|
|
319 }
|
|
320
|
|
321 warn "\n>>> Writing bisulfite mapping results to $output_dir$outfile <<<\n\n";
|
|
322 sleep(1);
|
|
323
|
|
324 if ($vanilla){
|
|
325 print OUT "Bismark version: $bismark_version\n";
|
|
326 }
|
|
327
|
|
328 ### printing alignment and methylation call summary to a report file
|
|
329 my $reportfile = $filename;
|
|
330 if ($bowtie2){
|
|
331 $reportfile =~ s/$/_bt2_bismark_SE_report.txt/;
|
|
332 }
|
|
333 else{
|
|
334 $reportfile =~ s/$/_bismark_SE_report.txt/;
|
|
335 }
|
|
336
|
|
337 open (REPORT,'>',"$output_dir$reportfile") or die "Failed to write to $reportfile: $!\n";
|
|
338 print REPORT "Bismark report for: $sequence_file (version: $bismark_version)\n";
|
|
339
|
|
340 if ($unmapped){
|
|
341 my $unmapped_file = $filename;
|
|
342 $unmapped_file =~ s/$/_unmapped_reads.txt/;
|
|
343 open (UNMAPPED,'>',"$output_dir$unmapped_file") or die "Failed to write to $unmapped_file: $!\n";
|
|
344 print "Unmapped sequences will be written to $output_dir$unmapped_file\n";
|
|
345 }
|
|
346 if ($ambiguous){
|
|
347 my $ambiguous_file = $filename;
|
|
348 $ambiguous_file =~ s/$/_ambiguous_reads.txt/;
|
|
349 open (AMBIG,'>',"$output_dir$ambiguous_file") or die "Failed to write to $ambiguous_file: $!\n";
|
|
350 print "Ambiguously mapping sequences will be written to $output_dir$ambiguous_file\n";
|
|
351 }
|
|
352
|
|
353 if ($directional){
|
|
354 print REPORT "Option '--directional' specified: alignments to complementary strands will be ignored (i.e. not performed!)\n";
|
|
355 }
|
|
356 print REPORT "Bowtie was run against the bisulfite genome of $genome_folder with the specified options: $bowtie_options\n\n";
|
|
357
|
|
358
|
|
359 ### if 2 or more files are provided we can hold the genome in memory and don't need to read it in a second time
|
|
360 unless (%chromosomes){
|
|
361 my $cwd = getcwd; # storing the path of the current working directory
|
|
362 print "Current working directory is: $cwd\n\n";
|
|
363 read_genome_into_memory($cwd);
|
|
364 }
|
|
365
|
|
366 unless ($vanilla or $sam_no_hd){
|
|
367 generate_SAM_header();
|
|
368 }
|
|
369
|
|
370 ### Input file is in FastA format
|
|
371 if ($sequence_file_format eq 'FASTA'){
|
|
372 process_single_end_fastA_file_for_methylation_call($sequence_file,$C_to_T_infile,$G_to_A_infile);
|
|
373 }
|
|
374 ### Input file is in FastQ format
|
|
375 else{
|
|
376 process_single_end_fastQ_file_for_methylation_call($sequence_file,$C_to_T_infile,$G_to_A_infile);
|
|
377 }
|
|
378 }
|
|
379
|
|
380 sub start_methylation_call_procedure_paired_ends {
|
|
381 my ($sequence_file_1,$sequence_file_2,$C_to_T_infile_1,$G_to_A_infile_1,$C_to_T_infile_2,$G_to_A_infile_2) = @_;
|
|
382
|
|
383 my ($dir_1,$filename_1);
|
|
384
|
|
385 if ($sequence_file_1 =~ /\//){
|
|
386 ($dir_1,$filename_1) = $sequence_file_1 =~ m/(.*\/)(.*)$/;
|
|
387 }
|
|
388 else{
|
|
389 $filename_1 = $sequence_file_1;
|
|
390 }
|
|
391
|
|
392 my ($dir_2,$filename_2);
|
|
393
|
|
394 if ($sequence_file_2 =~ /\//){
|
|
395 ($dir_2,$filename_2) = $sequence_file_2 =~ m/(.*\/)(.*)$/;
|
|
396 }
|
|
397 else{
|
|
398 $filename_2 = $sequence_file_2;
|
|
399 }
|
|
400
|
|
401 ### printing all alignments to a results file
|
|
402 my $outfile = $filename_1;
|
|
403 if ($bowtie2){ # SAM format is the default Bowtie 2 output
|
|
404 $outfile =~ s/$/_bismark_bt2_pe.sam/;
|
|
405 }
|
|
406 elsif ($vanilla){ # vanilla custom Bismark paired-end output (like Bismark versions 0.5.X)
|
|
407 $outfile =~ s/$/_bismark_pe.txt/;
|
|
408 }
|
|
409 else{ # SAM format is the default Bowtie 1 output
|
|
410 $outfile =~ s/$/_bismark_pe.sam/;
|
|
411 }
|
|
412
|
|
413 $bam = 0 unless (defined $bam);
|
|
414
|
|
415 if ($bam == 1){ ### Samtools is installed, writing out BAM directly
|
|
416 $outfile =~ s/sam/bam/;
|
|
417 open (OUT,"| $samtools_path view -bSh 2>/dev/null - > $output_dir$outfile") or die "Failed to write to $outfile: $!\n";
|
|
418 }
|
|
419 elsif($bam == 2){ ### no Samtools found on system. Using GZIP compression instead
|
|
420 $outfile .= '.gz';
|
|
421 open (OUT,"| gzip -c - > $output_dir$outfile") or die "Failed to write to $outfile: $!\n";
|
|
422 }
|
|
423 else{ # uncompressed ouput, default
|
|
424 open (OUT,'>',"$output_dir$outfile") or die "Failed to write to $outfile: $!\n";
|
|
425 }
|
|
426
|
|
427 warn "\n>>> Writing bisulfite mapping results to $outfile <<<\n\n";
|
|
428 sleep(1);
|
|
429
|
|
430 if ($vanilla){
|
|
431 print OUT "Bismark version: $bismark_version\n";
|
|
432 }
|
|
433
|
|
434 ### printing alignment and methylation call summary to a report file
|
|
435 my $reportfile = $filename_1;
|
|
436 if ($bowtie2){
|
|
437 $reportfile =~ s/$/_bismark_bt2_PE_report.txt/;
|
|
438 }
|
|
439 else{
|
|
440 $reportfile =~ s/$/_bismark_PE_report.txt/;
|
|
441 }
|
|
442
|
|
443 open (REPORT,'>',"$output_dir$reportfile") or die "Failed to write to $reportfile: $!\n";
|
|
444 print REPORT "Bismark report for: $sequence_file_1 and $sequence_file_2 (version: $bismark_version)\n";
|
|
445 print REPORT "Bowtie was run against the bisulfite genome of $genome_folder with the specified options: $bowtie_options\n\n";
|
|
446
|
|
447
|
|
448 ### Unmapped read output
|
|
449 if ($unmapped){
|
|
450 my $unmapped_1 = $filename_1;
|
|
451 my $unmapped_2 = $filename_2;
|
|
452 $unmapped_1 =~ s/$/_unmapped_reads_1.txt/;
|
|
453 $unmapped_2 =~ s/$/_unmapped_reads_2.txt/;
|
|
454 open (UNMAPPED_1,'>',"$output_dir$unmapped_1") or die "Failed to write to $unmapped_1: $!\n";
|
|
455 open (UNMAPPED_2,'>',"$output_dir$unmapped_2") or die "Failed to write to $unmapped_2: $!\n";
|
|
456 print "Unmapped sequences will be written to $unmapped_1 and $unmapped_2\n";
|
|
457 }
|
|
458
|
|
459 if ($ambiguous){
|
|
460 my $amb_1 = $filename_1;
|
|
461 my $amb_2 = $filename_2;
|
|
462 $amb_1 =~ s/$/_ambiguous_reads_1.txt/;
|
|
463 $amb_2 =~ s/$/_ambiguous_reads_2.txt/;
|
|
464 open (AMBIG_1,'>',"$output_dir$amb_1") or die "Failed to write to $amb_1: $!\n";
|
|
465 open (AMBIG_2,'>',"$output_dir$amb_2") or die "Failed to write to $amb_2: $!\n";
|
|
466 print "Ambiguously mapping sequences will be written to $amb_1 and $amb_2\n";
|
|
467 }
|
|
468
|
|
469 if ($directional){
|
|
470 print REPORT "Option '--directional' specified: alignments to complementary strands will be ignored (i.e. not performed)\n";
|
|
471 }
|
|
472
|
|
473 ### if 2 or more files are provided we might still hold the genome in memory and don't need to read it in a second time
|
|
474 unless (%chromosomes){
|
|
475 my $cwd = getcwd; # storing the path of the current working directory
|
|
476 print "Current working directory is: $cwd\n\n";
|
|
477 read_genome_into_memory($cwd);
|
|
478 }
|
|
479
|
|
480 unless ($vanilla or $sam_no_hd){
|
|
481 generate_SAM_header();
|
|
482 }
|
|
483
|
|
484 ### Input files are in FastA format
|
|
485 if ($sequence_file_format eq 'FASTA'){
|
|
486 process_fastA_files_for_paired_end_methylation_calls($sequence_file_1,$sequence_file_2,$C_to_T_infile_1,$G_to_A_infile_1,$C_to_T_infile_2,$G_to_A_infile_2);
|
|
487 }
|
|
488 ### Input files are in FastQ format
|
|
489 else{
|
|
490 process_fastQ_files_for_paired_end_methylation_calls($sequence_file_1,$sequence_file_2,$C_to_T_infile_1,$G_to_A_infile_1,$C_to_T_infile_2,$G_to_A_infile_2);
|
|
491 }
|
|
492 }
|
|
493
|
|
494 sub print_final_analysis_report_single_end{
|
|
495 my ($C_to_T_infile,$G_to_A_infile) = @_;
|
|
496 ### All sequences from the original sequence file have been analysed now
|
|
497 ### deleting temporary C->T or G->A infiles
|
|
498
|
|
499 if ($directional){
|
|
500 my $deletion_successful = unlink "$temp_dir$C_to_T_infile";
|
|
501 if ($deletion_successful == 1){
|
|
502 warn "\nSuccessfully deleted the temporary file $temp_dir$C_to_T_infile\n\n";
|
|
503 }
|
|
504 else{
|
|
505 warn "Could not delete temporary file $C_to_T_infile properly $!\n";
|
|
506 }
|
|
507 }
|
|
508 elsif ($pbat){
|
|
509 my $deletion_successful = unlink "$temp_dir$G_to_A_infile";
|
|
510 if ($deletion_successful == 1){
|
|
511 warn "\nSuccessfully deleted the temporary file $temp_dir$G_to_A_infile\n\n";
|
|
512 }
|
|
513 else{
|
|
514 warn "Could not delete temporary file $G_to_A_infile properly $!\n";
|
|
515 }
|
|
516 }
|
|
517 else{
|
|
518 my $deletion_successful = unlink "$temp_dir$C_to_T_infile","$temp_dir$G_to_A_infile";
|
|
519 if ($deletion_successful == 2){
|
|
520 warn "\nSuccessfully deleted the temporary files $temp_dir$C_to_T_infile and $temp_dir$G_to_A_infile\n\n";
|
|
521 }
|
|
522 else{
|
|
523 warn "Could not delete temporary files properly $!\n";
|
|
524 }
|
|
525 }
|
|
526
|
|
527 ### printing a final report for the alignment procedure
|
|
528 print REPORT "Final Alignment report\n",'='x22,"\n";
|
|
529 warn "Final Alignment report\n",'='x22,"\n";
|
|
530 # foreach my $index (0..$#fhs){
|
|
531 # print "$fhs[$index]->{name}\n";
|
|
532 # print "$fhs[$index]->{seen}\talignments on the correct strand in total\n";
|
|
533 # print "$fhs[$index]->{wrong_strand}\talignments were discarded (nonsensical alignments)\n\n";
|
|
534 # }
|
|
535
|
|
536 ### printing a final report for the methylation call procedure
|
|
537 warn "Sequences analysed in total:\t$counting{sequences_count}\n";
|
|
538 print REPORT "Sequences analysed in total:\t$counting{sequences_count}\n";
|
|
539 my $percent_alignable_sequences;
|
|
540
|
|
541 if ($counting{sequences_count} == 0){
|
|
542 $percent_alignable_sequences = 0;
|
|
543 }
|
|
544 else{
|
|
545 $percent_alignable_sequences = sprintf ("%.1f",$counting{unique_best_alignment_count}*100/$counting{sequences_count});
|
|
546 }
|
|
547
|
|
548 warn "Number of alignments with a unique best hit from the different alignments:\t$counting{unique_best_alignment_count}\nMapping efficiency:\t${percent_alignable_sequences}%\n\n";
|
|
549 print REPORT "Number of alignments with a unique best hit from the different alignments:\t$counting{unique_best_alignment_count}\nMapping efficiency:\t${percent_alignable_sequences}%\n";
|
|
550
|
|
551 ### percentage of low complexity reads overruled because of low complexity (thereby creating a bias for highly methylated reads),
|
|
552 ### only calculating the percentage if there were any overruled alignments
|
|
553 if ($counting{low_complexity_alignments_overruled_count}){
|
|
554 my $percent_overruled_low_complexity_alignments = sprintf ("%.1f",$counting{low_complexity_alignments_overruled_count}*100/$counting{sequences_count});
|
|
555 # print REPORT "Number of low complexity alignments which were overruled to have a unique best hit rather than discarding them:\t$counting{low_complexity_alignments_overruled_count}\t(${percent_overruled_low_complexity_alignments}%)\n";
|
|
556 }
|
|
557
|
|
558 print "Sequences with no alignments under any condition:\t$counting{no_single_alignment_found}\n";
|
|
559 print "Sequences did not map uniquely:\t$counting{unsuitable_sequence_count}\n";
|
|
560 print "Sequences which were discarded because genomic sequence could not be extracted:\t$counting{genomic_sequence_could_not_be_extracted_count}\n\n";
|
|
561 print "Number of sequences with unique best (first) alignment came from the bowtie output:\n";
|
|
562 print join ("\n","CT/CT:\t$counting{CT_CT_count}\t((converted) top strand)","CT/GA:\t$counting{CT_GA_count}\t((converted) bottom strand)","GA/CT:\t$counting{GA_CT_count}\t(complementary to (converted) top strand)","GA/GA:\t$counting{GA_GA_count}\t(complementary to (converted) bottom strand)"),"\n\n";
|
|
563
|
|
564 print REPORT "Sequences with no alignments under any condition:\t$counting{no_single_alignment_found}\n";
|
|
565 print REPORT "Sequences did not map uniquely:\t$counting{unsuitable_sequence_count}\n";
|
|
566 print REPORT "Sequences which were discarded because genomic sequence could not be extracted:\t$counting{genomic_sequence_could_not_be_extracted_count}\n\n";
|
|
567 print REPORT "Number of sequences with unique best (first) alignment came from the bowtie output:\n";
|
|
568 print REPORT join ("\n","CT/CT:\t$counting{CT_CT_count}\t((converted) top strand)","CT/GA:\t$counting{CT_GA_count}\t((converted) bottom strand)","GA/CT:\t$counting{GA_CT_count}\t(complementary to (converted) top strand)","GA/GA:\t$counting{GA_GA_count}\t(complementary to (converted) bottom strand)"),"\n\n";
|
|
569
|
|
570 if ($directional){
|
|
571 print "Number of alignments to (merely theoretical) complementary strands being rejected in total:\t$counting{alignments_rejected_count}\n\n";
|
|
572 print REPORT "Number of alignments to (merely theoretical) complementary strands being rejected in total:\t$counting{alignments_rejected_count}\n\n";
|
|
573 }
|
|
574
|
|
575 ### detailed information about Cs analysed
|
|
576 warn "Final Cytosine Methylation Report\n",'='x33,"\n";
|
|
577 my $total_number_of_C = $counting{total_meCHH_count}+$counting{total_meCHG_count}+$counting{total_meCpG_count}+$counting{total_unmethylated_CHH_count}+$counting{total_unmethylated_CHG_count}+$counting{total_unmethylated_CpG_count};
|
|
578 warn "Total number of C's analysed:\t$total_number_of_C\n\n";
|
|
579 warn "Total methylated C's in CpG context:\t$counting{total_meCpG_count}\n";
|
|
580 warn "Total methylated C's in CHG context:\t$counting{total_meCHG_count}\n";
|
|
581 warn "Total methylated C's in CHH context:\t$counting{total_meCHH_count}\n\n";
|
|
582 warn "Total C to T conversions in CpG context:\t$counting{total_unmethylated_CpG_count}\n";
|
|
583 warn "Total C to T conversions in CHG context:\t$counting{total_unmethylated_CHG_count}\n";
|
|
584 warn "Total C to T conversions in CHH context:\t$counting{total_unmethylated_CHH_count}\n\n";
|
|
585
|
|
586 print REPORT "Final Cytosine Methylation Report\n",'='x33,"\n";
|
|
587 print REPORT "Total number of C's analysed:\t$total_number_of_C\n\n";
|
|
588 print REPORT "Total methylated C's in CpG context:\t $counting{total_meCpG_count}\n";
|
|
589 print REPORT "Total methylated C's in CHG context:\t$counting{total_meCHG_count}\n";
|
|
590 print REPORT "Total methylated C's in CHH context:\t$counting{total_meCHH_count}\n\n";
|
|
591 print REPORT "Total C to T conversions in CpG context:\t$counting{total_unmethylated_CpG_count}\n";
|
|
592 print REPORT "Total C to T conversions in CHG context:\t$counting{total_unmethylated_CHG_count}\n";
|
|
593 print REPORT "Total C to T conversions in CHH context:\t$counting{total_unmethylated_CHH_count}\n\n";
|
|
594
|
|
595 my $percent_meCHG;
|
|
596 if (($counting{total_meCHG_count}+$counting{total_unmethylated_CHG_count}) > 0){
|
|
597 $percent_meCHG = sprintf("%.1f",100*$counting{total_meCHG_count}/($counting{total_meCHG_count}+$counting{total_unmethylated_CHG_count}));
|
|
598 }
|
|
599
|
|
600 my $percent_meCHH;
|
|
601 if (($counting{total_meCHH_count}+$counting{total_unmethylated_CHH_count}) > 0){
|
|
602 $percent_meCHH = sprintf("%.1f",100*$counting{total_meCHH_count}/($counting{total_meCHH_count}+$counting{total_unmethylated_CHH_count}));
|
|
603 }
|
|
604
|
|
605 my $percent_meCpG;
|
|
606 if (($counting{total_meCpG_count}+$counting{total_unmethylated_CpG_count}) > 0){
|
|
607 $percent_meCpG = sprintf("%.1f",100*$counting{total_meCpG_count}/($counting{total_meCpG_count}+$counting{total_unmethylated_CpG_count}));
|
|
608 }
|
|
609
|
|
610 ### printing methylated CpG percentage if applicable
|
|
611 if ($percent_meCpG){
|
|
612 warn "C methylated in CpG context:\t${percent_meCpG}%\n";
|
|
613 print REPORT "C methylated in CpG context:\t${percent_meCpG}%\n";
|
|
614 }
|
|
615 else{
|
|
616 warn "Can't determine percentage of methylated Cs in CpG context if value was 0\n";
|
|
617 print REPORT "Can't determine percentage of methylated Cs in CpG context if value was 0\n";
|
|
618 }
|
|
619
|
|
620 ### printing methylated C percentage (CHG context) if applicable
|
|
621 if ($percent_meCHG){
|
|
622 warn "C methylated in CHG context:\t${percent_meCHG}%\n";
|
|
623 print REPORT "C methylated in CHG context:\t${percent_meCHG}%\n";
|
|
624 }
|
|
625 else{
|
|
626 warn "Can't determine percentage of methylated Cs in CHG context if value was 0\n";
|
|
627 print REPORT "Can't determine percentage of methylated Cs in CHG context if value was 0\n";
|
|
628 }
|
|
629
|
|
630 ### printing methylated C percentage (CHH context) if applicable
|
|
631 if ($percent_meCHH){
|
|
632 warn "C methylated in CHH context:\t${percent_meCHH}%\n\n\n";
|
|
633 print REPORT "C methylated in CHH context:\t${percent_meCHH}%\n\n\n";
|
|
634 }
|
|
635 else{
|
|
636 warn "Can't determine percentage of methylated Cs in CHH context if value was 0\n\n\n";
|
|
637 print REPORT "Can't determine percentage of methylated Cs in CHH context if value was 0\n\n\n";
|
|
638 }
|
|
639
|
|
640 if ($seqID_contains_tabs){
|
|
641 warn "The sequence IDs in the provided file contain tab-stops which might prevent sequence alignments. If this happened, please replace all tab characters within the seqID field with spaces before running Bismark.\n\n";
|
|
642 print REPORT "The sequence IDs in the provided file contain tab-stops which might prevent sequence alignments. If this happened, please replace all tab characters within the seqID field with spaces before running Bismark.\n\n";
|
|
643 }
|
|
644 }
|
|
645
|
|
646 sub print_final_analysis_report_paired_ends{
|
|
647 my ($C_to_T_infile_1,$G_to_A_infile_1,$C_to_T_infile_2,$G_to_A_infile_2) = @_;
|
|
648 ### All sequences from the original sequence file have been analysed now, therefore deleting temporary C->T or G->A infiles
|
|
649 if ($directional){
|
|
650 if ($G_to_A_infile_2){
|
|
651 my $deletion_successful = unlink "$temp_dir$C_to_T_infile_1","$temp_dir$G_to_A_infile_2";
|
|
652 if ($deletion_successful == 2){
|
|
653 warn "\nSuccessfully deleted the temporary files $temp_dir$C_to_T_infile_1 and $temp_dir$G_to_A_infile_2\n\n";
|
|
654 }
|
|
655 else{
|
|
656 warn "Could not delete temporary files $temp_dir$C_to_T_infile_1 and $temp_dir$G_to_A_infile_2 properly: $!\n";
|
|
657 }
|
|
658 }
|
|
659 else{ # for paired-end FastQ infiles with Bowtie1 there is only one file to delete
|
|
660 my $deletion_successful = unlink "$temp_dir$C_to_T_infile_1";
|
|
661 if ($deletion_successful == 1){
|
|
662 warn "\nSuccessfully deleted the temporary file $temp_dir$C_to_T_infile_1\n\n";
|
|
663 }
|
|
664 else{
|
|
665 warn "Could not delete temporary file $temp_dir$C_to_T_infile_1 properly: $!\n";
|
|
666 }
|
|
667 }
|
|
668 }
|
|
669 else{
|
|
670 if ($G_to_A_infile_2 and $C_to_T_infile_2){
|
|
671 my $deletion_successful = unlink "$temp_dir$C_to_T_infile_1","$temp_dir$G_to_A_infile_1","$temp_dir$C_to_T_infile_2","$temp_dir$G_to_A_infile_2";
|
|
672 if ($deletion_successful == 4){
|
|
673 warn "\nSuccessfully deleted the temporary files $temp_dir$C_to_T_infile_1, $temp_dir$G_to_A_infile_1, $temp_dir$C_to_T_infile_2 and $temp_dir$G_to_A_infile_2\n\n";
|
|
674 }
|
|
675 else{
|
|
676 warn "Could not delete temporary files properly: $!\n";
|
|
677 }
|
|
678 }
|
|
679 else{ # for paired-end FastQ infiles with Bowtie1 there are only two files to delete
|
|
680 my $deletion_successful = unlink "$temp_dir$C_to_T_infile_1","$temp_dir$G_to_A_infile_1";
|
|
681 if ($deletion_successful == 2){
|
|
682 warn "\nSuccessfully deleted the temporary files $temp_dir$C_to_T_infile_1 and $temp_dir$G_to_A_infile_1\n\n";
|
|
683 }
|
|
684 else{
|
|
685 warn "Could not delete temporary files properly: $!\n";
|
|
686 }
|
|
687 }
|
|
688 }
|
|
689
|
|
690 ### printing a final report for the alignment procedure
|
|
691 warn "Final Alignment report\n",'='x22,"\n";
|
|
692 print REPORT "Final Alignment report\n",'='x22,"\n";
|
|
693 # foreach my $index (0..$#fhs){
|
|
694 # print "$fhs[$index]->{name}\n";
|
|
695 # print "$fhs[$index]->{seen}\talignments on the correct strand in total\n";
|
|
696 # print "$fhs[$index]->{wrong_strand}\talignments were discarded (nonsensical alignments)\n\n";
|
|
697 # }
|
|
698
|
|
699 ### printing a final report for the methylation call procedure
|
|
700 warn "Sequence pairs analysed in total:\t$counting{sequences_count}\n";
|
|
701 print REPORT "Sequence pairs analysed in total:\t$counting{sequences_count}\n";
|
|
702
|
|
703 my $percent_alignable_sequence_pairs;
|
|
704 if ($counting{sequences_count} == 0){
|
|
705 $percent_alignable_sequence_pairs = 0;
|
|
706 }
|
|
707 else{
|
|
708 $percent_alignable_sequence_pairs = sprintf ("%.1f",$counting{unique_best_alignment_count}*100/$counting{sequences_count});
|
|
709 }
|
|
710 print "Number of paired-end alignments with a unique best hit:\t$counting{unique_best_alignment_count}\nMapping efficiency:\t${percent_alignable_sequence_pairs}%\n\n";
|
|
711 print REPORT "Number of paired-end alignments with a unique best hit:\t$counting{unique_best_alignment_count}\nMapping efficiency:\t${percent_alignable_sequence_pairs}% \n";
|
|
712
|
|
713 print "Sequence pairs with no alignments under any condition:\t$counting{no_single_alignment_found}\n";
|
|
714 print "Sequence pairs did not map uniquely:\t$counting{unsuitable_sequence_count}\n";
|
|
715 print "Sequence pairs which were discarded because genomic sequence could not be extracted:\t$counting{genomic_sequence_could_not_be_extracted_count}\n\n";
|
|
716 print "Number of sequence pairs with unique best (first) alignment came from the bowtie output:\n";
|
|
717 print join ("\n","CT/GA/CT:\t$counting{CT_GA_CT_count}\t((converted) top strand)","GA/CT/CT:\t$counting{GA_CT_CT_count}\t(complementary to (converted) top strand)","GA/CT/GA:\t$counting{GA_CT_GA_count}\t(complementary to (converted) bottom strand)","CT/GA/GA:\t$counting{CT_GA_GA_count}\t((converted) bottom strand)"),"\n\n";
|
|
718
|
|
719
|
|
720 print REPORT "Sequence pairs with no alignments under any condition:\t$counting{no_single_alignment_found}\n";
|
|
721 print REPORT "Sequence pairs did not map uniquely:\t$counting{unsuitable_sequence_count}\n";
|
|
722 print REPORT "Sequence pairs which were discarded because genomic sequence could not be extracted:\t$counting{genomic_sequence_could_not_be_extracted_count}\n\n";
|
|
723 print REPORT "Number of sequence pairs with unique best (first) alignment came from the bowtie output:\n";
|
|
724 print REPORT join ("\n","CT/GA/CT:\t$counting{CT_GA_CT_count}\t((converted) top strand)","GA/CT/CT:\t$counting{GA_CT_CT_count}\t(complementary to (converted) top strand)","GA/CT/GA:\t$counting{GA_CT_GA_count}\t(complementary to (converted) bottom strand)","CT/GA/GA:\t$counting{CT_GA_GA_count}\t((converted) bottom strand)"),"\n\n";
|
|
725 ### detailed information about Cs analysed
|
|
726
|
|
727 if ($directional){
|
|
728 print "Number of alignments to (merely theoretical) complementary strands being rejected in total:\t$counting{alignments_rejected_count}\n\n";
|
|
729 print REPORT "Number of alignments to (merely theoretical) complementary strands being rejected in total:\t$counting{alignments_rejected_count}\n\n";
|
|
730 }
|
|
731
|
|
732 warn "Final Cytosine Methylation Report\n",'='x33,"\n";
|
|
733 print REPORT "Final Cytosine Methylation Report\n",'='x33,"\n";
|
|
734
|
|
735 my $total_number_of_C = $counting{total_meCHG_count}+ $counting{total_meCHH_count}+$counting{total_meCpG_count}+$counting{total_unmethylated_CHG_count}+$counting{total_unmethylated_CHH_count}+$counting{total_unmethylated_CpG_count};
|
|
736 warn "Total number of C's analysed:\t$total_number_of_C\n\n";
|
|
737 warn "Total methylated C's in CpG context:\t$counting{total_meCpG_count}\n";
|
|
738 warn "Total methylated C's in CHG context:\t$counting{total_meCHG_count}\n";
|
|
739 warn "Total methylated C's in CHH context:\t$counting{total_meCHH_count}\n\n";
|
|
740 warn "Total C to T conversions in CpG context:\t$counting{total_unmethylated_CpG_count}\n";
|
|
741 warn "Total C to T conversions in CHG context:\t$counting{total_unmethylated_CHG_count}\n";
|
|
742 warn "Total C to T conversions in CHH context:\t$counting{total_unmethylated_CHH_count}\n\n";
|
|
743
|
|
744 print REPORT "Total number of C's analysed:\t$total_number_of_C\n\n";
|
|
745 print REPORT "Total methylated C's in CpG context:\t$counting{total_meCpG_count}\n";
|
|
746 print REPORT "Total methylated C's in CHG context:\t$counting{total_meCHG_count}\n";
|
|
747 print REPORT "Total methylated C's in CHH context:\t$counting{total_meCHH_count}\n\n";
|
|
748 print REPORT "Total C to T conversions in CpG context:\t$counting{total_unmethylated_CpG_count}\n";
|
|
749 print REPORT "Total C to T conversions in CHG context:\t$counting{total_unmethylated_CHG_count}\n";
|
|
750 print REPORT "Total C to T conversions in CHH context:\t$counting{total_unmethylated_CHH_count}\n\n";
|
|
751
|
|
752 my $percent_meCHG;
|
|
753 if (($counting{total_meCHG_count}+$counting{total_unmethylated_CHG_count}) > 0){
|
|
754 $percent_meCHG = sprintf("%.1f",100*$counting{total_meCHG_count}/($counting{total_meCHG_count}+$counting{total_unmethylated_CHG_count}));
|
|
755 }
|
|
756
|
|
757 my $percent_meCHH;
|
|
758 if (($counting{total_meCHH_count}+$counting{total_unmethylated_CHH_count}) > 0){
|
|
759 $percent_meCHH = sprintf("%.1f",100*$counting{total_meCHH_count}/($counting{total_meCHH_count}+$counting{total_unmethylated_CHH_count}));
|
|
760 }
|
|
761
|
|
762 my $percent_meCpG;
|
|
763 if (($counting{total_meCpG_count}+$counting{total_unmethylated_CpG_count}) > 0){
|
|
764 $percent_meCpG = sprintf("%.1f",100*$counting{total_meCpG_count}/($counting{total_meCpG_count}+$counting{total_unmethylated_CpG_count}));
|
|
765 }
|
|
766
|
|
767 ### printing methylated CpG percentage if applicable
|
|
768 if ($percent_meCpG){
|
|
769 warn "C methylated in CpG context:\t${percent_meCpG}%\n";
|
|
770 print REPORT "C methylated in CpG context:\t${percent_meCpG}%\n";
|
|
771 }
|
|
772 else{
|
|
773 warn "Can't determine percentage of methylated Cs in CpG context if value was 0\n";
|
|
774 print REPORT "Can't determine percentage of methylated Cs in CpG context if value was 0\n";
|
|
775 }
|
|
776
|
|
777 ### printing methylated C percentage in CHG context if applicable
|
|
778 if ($percent_meCHG){
|
|
779 warn "C methylated in CHG context:\t${percent_meCHG}%\n";
|
|
780 print REPORT "C methylated in CHG context:\t${percent_meCHG}%\n";
|
|
781 }
|
|
782 else{
|
|
783 warn "Can't determine percentage of methylated Cs in CHG context if value was 0\n";
|
|
784 print REPORT "Can't determine percentage of methylated Cs in CHG context if value was 0\n";
|
|
785 }
|
|
786
|
|
787 ### printing methylated C percentage in CHH context if applicable
|
|
788 if ($percent_meCHH){
|
|
789 warn "C methylated in CHH context:\t${percent_meCHH}%\n\n\n";
|
|
790 print REPORT "C methylated in CHH context:\t${percent_meCHH}%\n\n\n";
|
|
791 }
|
|
792 else{
|
|
793 warn "Can't determine percentage of methylated Cs in CHH context if value was 0\n\n\n";
|
|
794 print REPORT "Can't determine percentage of methylated Cs in CHH context if value was 0\n\n\n";
|
|
795 }
|
|
796
|
|
797 }
|
|
798
|
|
799 sub process_single_end_fastA_file_for_methylation_call{
|
|
800 my ($sequence_file,$C_to_T_infile,$G_to_A_infile) = @_;
|
|
801 ### this is a FastA sequence file; we need the actual sequence to compare it against the genomic sequence in order to make a methylation call.
|
|
802 ### Now reading in the sequence file sequence by sequence and see if the current sequence was mapped to one (or both) of the converted genomes in either
|
|
803 ### the C->T or G->A version
|
|
804
|
|
805 ### gzipped version of the infile
|
|
806 if ($sequence_file =~ /\.gz$/){
|
|
807 open (IN,"zcat $sequence_file |") or die $!;
|
|
808 }
|
|
809 else{
|
|
810 open (IN,$sequence_file) or die $!;
|
|
811 }
|
|
812
|
|
813 my $count = 0;
|
|
814
|
|
815 warn "\nReading in the sequence file $sequence_file\n";
|
|
816 while (1) {
|
|
817 # last if ($counting{sequences_count} > 100);
|
|
818 my $identifier = <IN>;
|
|
819 my $sequence = <IN>;
|
|
820 last unless ($identifier and $sequence);
|
|
821
|
|
822 $identifier = fix_IDs($identifier); # this is to avoid problems with truncated read ID when they contain white spaces
|
|
823
|
|
824 ++$count;
|
|
825
|
|
826 if ($skip){
|
|
827 next unless ($count > $skip);
|
|
828 }
|
|
829 if ($upto){
|
|
830 last if ($count > $upto);
|
|
831 }
|
|
832
|
|
833 $counting{sequences_count}++;
|
|
834 if ($counting{sequences_count}%100000==0) {
|
|
835 warn "Processed $counting{sequences_count} sequences so far\n";
|
|
836 }
|
|
837 chomp $sequence;
|
|
838 chomp $identifier;
|
|
839
|
|
840 $identifier =~ s/^>//; # deletes the > at the beginning of FastA headers
|
|
841
|
|
842 my $return;
|
|
843 if ($bowtie2){
|
|
844 $return = check_bowtie_results_single_end_bowtie2 (uc$sequence,$identifier);
|
|
845 }
|
|
846 else{
|
|
847 $return = check_bowtie_results_single_end(uc$sequence,$identifier); # default Bowtie 1
|
|
848 }
|
|
849
|
|
850 unless ($return){
|
|
851 $return = 0;
|
|
852 }
|
|
853
|
|
854 # print the sequence to ambiguous.out if --ambiguous was specified
|
|
855 if ($ambiguous and $return == 2){
|
|
856 print AMBIG ">$identifier\n";
|
|
857 print AMBIG "$sequence\n";
|
|
858 }
|
|
859
|
|
860 # print the sequence to <unmapped.out> file if --un was specified
|
|
861 elsif ($unmapped and $return == 1){
|
|
862 print UNMAPPED ">$identifier\n";
|
|
863 print UNMAPPED "$sequence\n";
|
|
864 }
|
|
865 }
|
|
866 print "Processed $counting{sequences_count} sequences in total\n\n";
|
|
867
|
|
868 print_final_analysis_report_single_end($C_to_T_infile,$G_to_A_infile);
|
|
869
|
|
870 }
|
|
871
|
|
872 sub process_single_end_fastQ_file_for_methylation_call{
|
|
873 my ($sequence_file,$C_to_T_infile,$G_to_A_infile) = @_;
|
|
874 ### this is the Illumina sequence file; we need the actual sequence to compare it against the genomic sequence in order to make a methylation call.
|
|
875 ### Now reading in the sequence file sequence by sequence and see if the current sequence was mapped to one (or both) of the converted genomes in either
|
|
876 ### the C->T or G->A version
|
|
877
|
|
878 ### gzipped version of the infile
|
|
879 if ($sequence_file =~ /\.gz$/){
|
|
880 open (IN,"zcat $sequence_file |") or die $!;
|
|
881 }
|
|
882 else{
|
|
883 open (IN,$sequence_file) or die $!;
|
|
884 }
|
|
885
|
|
886 my $count = 0;
|
|
887
|
|
888 warn "\nReading in the sequence file $sequence_file\n";
|
|
889 while (1) {
|
|
890 my $identifier = <IN>;
|
|
891 my $sequence = <IN>;
|
|
892 my $identifier_2 = <IN>;
|
|
893 my $quality_value = <IN>;
|
|
894 last unless ($identifier and $sequence and $identifier_2 and $quality_value);
|
|
895
|
|
896 $identifier = fix_IDs($identifier); # this is to avoid problems with truncated read ID when they contain white spaces
|
|
897
|
|
898 ++$count;
|
|
899
|
|
900 if ($skip){
|
|
901 next unless ($count > $skip);
|
|
902 }
|
|
903 if ($upto){
|
|
904 last if ($count > $upto);
|
|
905 }
|
|
906
|
|
907 $counting{sequences_count}++;
|
|
908
|
|
909 if ($counting{sequences_count}%1000000==0) {
|
|
910 warn "Processed $counting{sequences_count} sequences so far\n";
|
|
911 }
|
|
912 chomp $sequence;
|
|
913 chomp $identifier;
|
|
914 chomp $quality_value;
|
|
915
|
|
916 $identifier =~ s/^\@//; # deletes the @ at the beginning of Illumin FastQ headers
|
|
917
|
|
918 my $return;
|
|
919 if ($bowtie2){
|
|
920 $return = check_bowtie_results_single_end_bowtie2 (uc$sequence,$identifier,$quality_value);
|
|
921 }
|
|
922 else{
|
|
923 $return = check_bowtie_results_single_end(uc$sequence,$identifier,$quality_value); # default Bowtie 1
|
|
924 }
|
|
925
|
|
926 unless ($return){
|
|
927 $return = 0;
|
|
928 }
|
|
929
|
|
930 # print the sequence to ambiguous.out if --ambiguous was specified
|
|
931 if ($ambiguous and $return == 2){
|
|
932 print AMBIG "\@$identifier\n";
|
|
933 print AMBIG "$sequence\n";
|
|
934 print AMBIG $identifier_2;
|
|
935 print AMBIG "$quality_value\n";
|
|
936 }
|
|
937
|
|
938 # print the sequence to <unmapped.out> file if --un was specified
|
|
939 elsif ($unmapped and $return == 1){
|
|
940 print UNMAPPED "\@$identifier\n";
|
|
941 print UNMAPPED "$sequence\n";
|
|
942 print UNMAPPED $identifier_2;
|
|
943 print UNMAPPED "$quality_value\n";
|
|
944 }
|
|
945 }
|
|
946 print "Processed $counting{sequences_count} sequences in total\n\n";
|
|
947
|
|
948 print_final_analysis_report_single_end($C_to_T_infile,$G_to_A_infile);
|
|
949
|
|
950 }
|
|
951
|
|
952 sub process_fastA_files_for_paired_end_methylation_calls{
|
|
953 my ($sequence_file_1,$sequence_file_2,$C_to_T_infile_1,$G_to_A_infile_1,$C_to_T_infile_2,$G_to_A_infile_2) = @_;
|
|
954 ### Processing the two FastA sequence files; we need the actual sequences of both reads to compare them against the genomic sequence in order to
|
|
955 ### make a methylation call. The sequence idetifier per definition needs to be the same for a sequence pair used for paired-end mapping.
|
|
956 ### Now reading in the sequence files sequence by sequence and see if the current sequences produced an alignment to one (or both) of the
|
|
957 ### converted genomes (either the C->T or G->A version)
|
|
958
|
|
959 ### gzipped version of the infiles
|
|
960 if ($sequence_file_1 =~ /\.gz$/ and $sequence_file_2 =~ /\.gz$/){
|
|
961 open (IN1,"zcat $sequence_file_1 |") or die "Failed to open zcat pipe to $sequence_file_1 $!\n";
|
|
962 open (IN2,"zcat $sequence_file_2 |") or die "Failed to open zcat pipe to $sequence_file_2 $!\n";
|
|
963 }
|
|
964 else{
|
|
965 open (IN1,$sequence_file_1) or die $!;
|
|
966 open (IN2,$sequence_file_2) or die $!;
|
|
967 }
|
|
968
|
|
969 warn "\nReading in the sequence files $sequence_file_1 and $sequence_file_2\n";
|
|
970 ### Both files are required to have the exact same number of sequences, therefore we can process the sequences jointly one by one
|
|
971
|
|
972 my $count = 0;
|
|
973
|
|
974 while (1) {
|
|
975 # reading from the first input file
|
|
976 my $identifier_1 = <IN1>;
|
|
977 my $sequence_1 = <IN1>;
|
|
978 # reading from the second input file
|
|
979 my $identifier_2 = <IN2>;
|
|
980 my $sequence_2 = <IN2>;
|
|
981 last unless ($identifier_1 and $sequence_1 and $identifier_2 and $sequence_2);
|
|
982
|
|
983 $identifier_1 = fix_IDs($identifier_1); # this is to avoid problems with truncated read ID when they contain white spaces
|
|
984 $identifier_2 = fix_IDs($identifier_2);
|
|
985
|
|
986 ++$count;
|
|
987
|
|
988 if ($skip){
|
|
989 next unless ($count > $skip);
|
|
990 }
|
|
991 if ($upto){
|
|
992 last if ($count > $upto);
|
|
993 }
|
|
994
|
|
995 $counting{sequences_count}++;
|
|
996 if ($counting{sequences_count}%100000==0) {
|
|
997 warn "Processed $counting{sequences_count} sequences so far\n";
|
|
998 }
|
|
999 my $orig_identifier_1 = $identifier_1;
|
|
1000 my $orig_identifier_2 = $identifier_2;
|
|
1001
|
|
1002 chomp $sequence_1;
|
|
1003 chomp $identifier_1;
|
|
1004 chomp $sequence_2;
|
|
1005 chomp $identifier_2;
|
|
1006
|
|
1007 $identifier_1 =~ s/^>//; # deletes the > at the beginning of FastA headers
|
|
1008
|
|
1009 my $return;
|
|
1010 if ($bowtie2){
|
|
1011 $return = check_bowtie_results_paired_ends_bowtie2 (uc$sequence_1,uc$sequence_2,$identifier_1);
|
|
1012 }
|
|
1013 else{
|
|
1014 $return = check_bowtie_results_paired_ends (uc$sequence_1,uc$sequence_2,$identifier_1);
|
|
1015 }
|
|
1016
|
|
1017 unless ($return){
|
|
1018 $return = 0;
|
|
1019 }
|
|
1020
|
|
1021 # print the sequences to ambiguous_1 and _2 if --ambiguous was specified
|
|
1022 if ($ambiguous and $return == 2){
|
|
1023 print AMBIG_1 $orig_identifier_1;
|
|
1024 print AMBIG_1 "$sequence_1\n";
|
|
1025 print AMBIG_2 $orig_identifier_2;
|
|
1026 print AMBIG_2 "$sequence_2\n";
|
|
1027 }
|
|
1028
|
|
1029 # print the sequences to unmapped_1.out and unmapped_2.out if --un was specified
|
|
1030 elsif ($unmapped and $return == 1){
|
|
1031 print UNMAPPED_1 $orig_identifier_1;
|
|
1032 print UNMAPPED_1 "$sequence_1\n";
|
|
1033 print UNMAPPED_2 $orig_identifier_2;
|
|
1034 print UNMAPPED_2 "$sequence_2\n";
|
|
1035 }
|
|
1036 }
|
|
1037
|
|
1038 warn "Processed $counting{sequences_count} sequences in total\n\n";
|
|
1039
|
|
1040 close OUT or die $!;
|
|
1041
|
|
1042 print_final_analysis_report_paired_ends($C_to_T_infile_1,$G_to_A_infile_1,$C_to_T_infile_2,$G_to_A_infile_2);
|
|
1043
|
|
1044 }
|
|
1045
|
|
1046 sub process_fastQ_files_for_paired_end_methylation_calls{
|
|
1047 my ($sequence_file_1,$sequence_file_2,$C_to_T_infile_1,$G_to_A_infile_1,$C_to_T_infile_2,$G_to_A_infile_2) = @_;
|
|
1048 ### Processing the two Illumina sequence files; we need the actual sequence of both reads to compare them against the genomic sequence in order to
|
|
1049 ### make a methylation call. The sequence identifier per definition needs to be same for a sequence pair used for paired-end alignments.
|
|
1050 ### Now reading in the sequence files sequence by sequence and see if the current sequences produced a paired-end alignment to one (or both)
|
|
1051 ### of the converted genomes (either C->T or G->A version)
|
|
1052
|
|
1053 ### gzipped version of the infiles
|
|
1054 if ($sequence_file_1 =~ /\.gz$/ and $sequence_file_2 =~ /\.gz$/){
|
|
1055 open (IN1,"zcat $sequence_file_1 |") or die "Failed to open zcat pipe to $sequence_file_1 $!\n";
|
|
1056 open (IN2,"zcat $sequence_file_2 |") or die "Failed to open zcat pipe to $sequence_file_2 $!\n";
|
|
1057 }
|
|
1058 else{
|
|
1059 open (IN1,$sequence_file_1) or die $!;
|
|
1060 open (IN2,$sequence_file_2) or die $!;
|
|
1061 }
|
|
1062
|
|
1063 my $count = 0;
|
|
1064
|
|
1065 warn "\nReading in the sequence files $sequence_file_1 and $sequence_file_2\n";
|
|
1066 ### Both files are required to have the exact same number of sequences, therefore we can process the sequences jointly one by one
|
|
1067 while (1) {
|
|
1068 # reading from the first input file
|
|
1069 my $identifier_1 = <IN1>;
|
|
1070 my $sequence_1 = <IN1>;
|
|
1071 my $ident_1 = <IN1>; # not needed
|
|
1072 my $quality_value_1 = <IN1>; # not needed
|
|
1073 # reading from the second input file
|
|
1074 my $identifier_2 = <IN2>;
|
|
1075 my $sequence_2 = <IN2>;
|
|
1076 my $ident_2 = <IN2>; # not needed
|
|
1077 my $quality_value_2 = <IN2>; # not needed
|
|
1078 last unless ($identifier_1 and $sequence_1 and $quality_value_1 and $identifier_2 and $sequence_2 and $quality_value_2);
|
|
1079
|
|
1080 $identifier_1 = fix_IDs($identifier_1); # this is to avoid problems with truncated read ID when they contain white spaces
|
|
1081 $identifier_2 = fix_IDs($identifier_2);
|
|
1082
|
|
1083 ++$count;
|
|
1084
|
|
1085 if ($skip){
|
|
1086 next unless ($count > $skip);
|
|
1087 }
|
|
1088 if ($upto){
|
|
1089 last if ($count > $upto);
|
|
1090 }
|
|
1091
|
|
1092 $counting{sequences_count}++;
|
|
1093 if ($counting{sequences_count}%100000==0) {
|
|
1094 warn "Processed $counting{sequences_count} sequences so far\n";
|
|
1095 }
|
|
1096
|
|
1097 my $orig_identifier_1 = $identifier_1;
|
|
1098 my $orig_identifier_2 = $identifier_2;
|
|
1099
|
|
1100 chomp $sequence_1;
|
|
1101 chomp $identifier_1;
|
|
1102 chomp $sequence_2;
|
|
1103 chomp $identifier_2;
|
|
1104 chomp $quality_value_1;
|
|
1105 chomp $quality_value_2;
|
|
1106
|
|
1107 $identifier_1 =~ s/^\@//; # deletes the @ at the beginning of the FastQ ID
|
|
1108
|
|
1109 my $return;
|
|
1110 if ($bowtie2){
|
|
1111 $return = check_bowtie_results_paired_ends_bowtie2 (uc$sequence_1,uc$sequence_2,$identifier_1,$quality_value_1,$quality_value_2);
|
|
1112 }
|
|
1113 else{
|
|
1114 $return = check_bowtie_results_paired_ends (uc$sequence_1,uc$sequence_2,$identifier_1,$quality_value_1,$quality_value_2);
|
|
1115 }
|
|
1116
|
|
1117 unless ($return){
|
|
1118 $return = 0;
|
|
1119 }
|
|
1120
|
|
1121 # print the sequences to ambiguous_1 and _2 if --ambiguous was specified
|
|
1122 if ($ambiguous and $return == 2){
|
|
1123 # seq_1
|
|
1124 print AMBIG_1 $orig_identifier_1;
|
|
1125 print AMBIG_1 "$sequence_1\n";
|
|
1126 print AMBIG_1 $ident_1;
|
|
1127 print AMBIG_1 "$quality_value_1\n";
|
|
1128 # seq_2
|
|
1129 print AMBIG_2 $orig_identifier_2;
|
|
1130 print AMBIG_2 "$sequence_2\n";
|
|
1131 print AMBIG_2 $ident_2;
|
|
1132 print AMBIG_2 "$quality_value_2\n";
|
|
1133 }
|
|
1134
|
|
1135 # print the sequences to unmapped_1.out and unmapped_2.out if --un was specified
|
|
1136 elsif ($unmapped and $return == 1){
|
|
1137 # seq_1
|
|
1138 print UNMAPPED_1 $orig_identifier_1;
|
|
1139 print UNMAPPED_1 "$sequence_1\n";
|
|
1140 print UNMAPPED_1 $ident_1;
|
|
1141 print UNMAPPED_1 "$quality_value_1\n";
|
|
1142 # seq_2
|
|
1143 print UNMAPPED_2 $orig_identifier_2;
|
|
1144 print UNMAPPED_2 "$sequence_2\n";
|
|
1145 print UNMAPPED_2 $ident_2;
|
|
1146 print UNMAPPED_2 "$quality_value_2\n";
|
|
1147 }
|
|
1148 }
|
|
1149
|
|
1150 warn "Processed $counting{sequences_count} sequences in total\n\n";
|
|
1151
|
|
1152 close OUT or die $!;
|
|
1153
|
|
1154 print_final_analysis_report_paired_ends($C_to_T_infile_1,$G_to_A_infile_1,$C_to_T_infile_2,$G_to_A_infile_2);
|
|
1155
|
|
1156 }
|
|
1157
|
|
1158 sub check_bowtie_results_single_end{
|
|
1159 my ($sequence,$identifier,$quality_value) = @_;
|
|
1160
|
|
1161 unless ($quality_value){ # FastA sequences get assigned a quality value of Phred 40 throughout
|
|
1162 $quality_value = 'I'x(length$sequence);
|
|
1163 }
|
|
1164
|
|
1165 my %mismatches = ();
|
|
1166 ### reading from the bowtie output files to see if this sequence aligned to a bisulfite converted genome
|
|
1167 foreach my $index (0..$#fhs){
|
|
1168
|
|
1169 ### skipping this index if the last alignment has been set to undefined already (i.e. end of bowtie output)
|
|
1170 next unless ($fhs[$index]->{last_line} and defined $fhs[$index]->{last_seq_id});
|
|
1171 ### if the sequence we are currently looking at produced an alignment we are doing various things with it
|
|
1172 if ($fhs[$index]->{last_seq_id} eq $identifier) {
|
|
1173 ###############################################################
|
|
1174 ### STEP I Now processing the alignment stored in last_line ###
|
|
1175 ###############################################################
|
|
1176 my $valid_alignment_found_1 = decide_whether_single_end_alignment_is_valid($index,$identifier);
|
|
1177 ### sequences can fail at this point if there was only 1 seq in the wrong orientation, or if there were 2 seqs, both in the wrong orientation
|
|
1178 ### we only continue to extract useful information about this alignment if 1 was returned
|
|
1179 if ($valid_alignment_found_1 == 1){
|
|
1180 ### Bowtie outputs which made it this far are in the correct orientation, so we can continue to analyse the alignment itself
|
|
1181 ### need to extract the chromosome number from the bowtie output (which is either XY_cf (complete forward) or XY_cr (complete reverse)
|
|
1182 my ($id,$strand,$mapped_chromosome,$position,$bowtie_sequence,$mismatch_info) = (split (/\t/,$fhs[$index]->{last_line},-1))[0,1,2,3,4,7];
|
|
1183
|
|
1184 unless($mismatch_info){
|
|
1185 $mismatch_info = '';
|
|
1186 }
|
|
1187
|
|
1188 chomp $mismatch_info;
|
|
1189 my $chromosome;
|
|
1190 if ($mapped_chromosome =~ s/_(CT|GA)_converted$//){
|
|
1191 $chromosome = $mapped_chromosome;
|
|
1192 }
|
|
1193 else{
|
|
1194 die "Chromosome number extraction failed for $mapped_chromosome\n";
|
|
1195 }
|
|
1196 ### Now extracting the number of mismatches to the converted genome
|
|
1197 my $number_of_mismatches;
|
|
1198 if ($mismatch_info eq ''){
|
|
1199 $number_of_mismatches = 0;
|
|
1200 }
|
|
1201 elsif ($mismatch_info =~ /^\d/){
|
|
1202 my @mismatches = split (/,/,$mismatch_info);
|
|
1203 $number_of_mismatches = scalar @mismatches;
|
|
1204 }
|
|
1205 else{
|
|
1206 die "Something weird is going on with the mismatch field:\t>>> $mismatch_info <<<\n";
|
|
1207 }
|
|
1208 ### creating a composite location variable from $chromosome and $position and storing the alignment information in a temporary hash table
|
|
1209 my $alignment_location = join (":",$chromosome,$position);
|
|
1210 ### If a sequence aligns to exactly the same location twice the sequence does either not contain any C or G, or all the Cs (or Gs on the reverse
|
|
1211 ### strand) were methylated and therefore protected. It is not needed to overwrite the same positional entry with a second entry for the same
|
|
1212 ### location (the genomic sequence extraction and methylation would not be affected by this, only the thing which would change is the index
|
|
1213 ### number for the found alignment)
|
|
1214 unless (exists $mismatches{$number_of_mismatches}->{$alignment_location}){
|
|
1215 $mismatches{$number_of_mismatches}->{$alignment_location}->{seq_id}=$id;
|
|
1216 $mismatches{$number_of_mismatches}->{$alignment_location}->{bowtie_sequence}=$bowtie_sequence;
|
|
1217 $mismatches{$number_of_mismatches}->{$alignment_location}->{index}=$index;
|
|
1218 $mismatches{$number_of_mismatches}->{$alignment_location}->{chromosome}=$chromosome;
|
|
1219 $mismatches{$number_of_mismatches}->{$alignment_location}->{position}=$position;
|
|
1220 }
|
|
1221 $number_of_mismatches = undef;
|
|
1222 ##################################################################################################################################################
|
|
1223 ### STEP II Now reading in the next line from the bowtie filehandle. The next alignment can either be a second alignment of the same sequence or a
|
|
1224 ### a new sequence. In either case we will store the next line in @fhs ->{last_line}. In case the alignment is already the next entry, a 0 will
|
|
1225 ### be returned as $valid_alignment_found and it will then be processed in the next round only.
|
|
1226 ##################################################################################################################################################
|
|
1227 my $newline = $fhs[$index]->{fh}-> getline();
|
|
1228 if ($newline){
|
|
1229 my ($seq_id) = split (/\t/,$newline);
|
|
1230 $fhs[$index]->{last_seq_id} = $seq_id;
|
|
1231 $fhs[$index]->{last_line} = $newline;
|
|
1232 }
|
|
1233 else {
|
|
1234 # assigning undef to last_seq_id and last_line and jumping to the next index (end of bowtie output)
|
|
1235 $fhs[$index]->{last_seq_id} = undef;
|
|
1236 $fhs[$index]->{last_line} = undef;
|
|
1237 next;
|
|
1238 }
|
|
1239 my $valid_alignment_found_2 = decide_whether_single_end_alignment_is_valid($index,$identifier);
|
|
1240 ### we only continue to extract useful information about this second alignment if 1 was returned
|
|
1241 if ($valid_alignment_found_2 == 1){
|
|
1242 ### If the second Bowtie output made it this far it is in the correct orientation, so we can continue to analyse the alignment itself
|
|
1243 ### need to extract the chromosome number from the bowtie output (which is either XY_cf (complete forward) or XY_cr (complete reverse)
|
|
1244 my ($id,$strand,$mapped_chromosome,$position,$bowtie_sequence,$mismatch_info) = (split (/\t/,$fhs[$index]->{last_line},-1))[0,1,2,3,4,7];
|
|
1245 unless($mismatch_info){
|
|
1246 $mismatch_info = '';
|
|
1247 }
|
|
1248 chomp $mismatch_info;
|
|
1249
|
|
1250 my $chromosome;
|
|
1251 if ($mapped_chromosome =~ s/_(CT|GA)_converted$//){
|
|
1252 $chromosome = $mapped_chromosome;
|
|
1253 }
|
|
1254 else{
|
|
1255 die "Chromosome number extraction failed for $mapped_chromosome\n";
|
|
1256 }
|
|
1257
|
|
1258 ### Now extracting the number of mismatches to the converted genome
|
|
1259 my $number_of_mismatches;
|
|
1260 if ($mismatch_info eq ''){
|
|
1261 $number_of_mismatches = 0;
|
|
1262 }
|
|
1263 elsif ($mismatch_info =~ /^\d/){
|
|
1264 my @mismatches = split (/,/,$mismatch_info);
|
|
1265 $number_of_mismatches = scalar @mismatches;
|
|
1266 }
|
|
1267 else{
|
|
1268 die "Something weird is going on with the mismatch field\n";
|
|
1269 }
|
|
1270 ### creating a composite location variable from $chromosome and $position and storing the alignment information in a temporary hash table
|
|
1271 ### extracting the chromosome number from the bowtie output (see above)
|
|
1272 my $alignment_location = join (":",$chromosome,$position);
|
|
1273 ### In the special case that two differently converted sequences align against differently converted genomes, but to the same position
|
|
1274 ### with the same number of mismatches (or perfect matches), the chromosome, position and number of mismatches are the same. In this
|
|
1275 ### case we are not writing the same entry out a second time.
|
|
1276 unless (exists $mismatches{$number_of_mismatches}->{$alignment_location}){
|
|
1277 $mismatches{$number_of_mismatches}->{$alignment_location}->{seq_id}=$id;
|
|
1278 $mismatches{$number_of_mismatches}->{$alignment_location}->{bowtie_sequence}=$bowtie_sequence;
|
|
1279 $mismatches{$number_of_mismatches}->{$alignment_location}->{index}=$index;
|
|
1280 $mismatches{$number_of_mismatches}->{$alignment_location}->{chromosome}=$chromosome;
|
|
1281 $mismatches{$number_of_mismatches}->{$alignment_location}->{position}=$position;
|
|
1282 }
|
|
1283 ####################################################################################################################################
|
|
1284 #### STEP III Now reading in one more line which has to be the next alignment to be analysed. Adding it to @fhs ->{last_line} ###
|
|
1285 ####################################################################################################################################
|
|
1286 $newline = $fhs[$index]->{fh}-> getline();
|
|
1287 if ($newline){
|
|
1288 my ($seq_id) = split (/\t/,$newline);
|
|
1289 die "The same seq ID occurred more than twice in a row\n" if ($seq_id eq $identifier);
|
|
1290 $fhs[$index]->{last_seq_id} = $seq_id;
|
|
1291 $fhs[$index]->{last_line} = $newline;
|
|
1292 next;
|
|
1293 }
|
|
1294 else {
|
|
1295 # assigning undef to last_seq_id and last_line and jumping to the next index (end of bowtie output)
|
|
1296 $fhs[$index]->{last_seq_id} = undef;
|
|
1297 $fhs[$index]->{last_line} = undef;
|
|
1298 next;
|
|
1299 }
|
|
1300 ### still within the 2nd sequence in correct orientation found
|
|
1301 }
|
|
1302 ### still withing the 1st sequence in correct orientation found
|
|
1303 }
|
|
1304 ### still within the if (last_seq_id eq identifier) condition
|
|
1305 }
|
|
1306 ### still within foreach index loop
|
|
1307 }
|
|
1308 ### if there was not a single alignment found for a certain sequence we will continue with the next sequence in the sequence file
|
|
1309 unless(%mismatches){
|
|
1310 $counting{no_single_alignment_found}++;
|
|
1311 if ($unmapped){
|
|
1312 return 1; ### We will print this sequence out as unmapped sequence if --un unmapped.out has been specified
|
|
1313 }
|
|
1314 else{
|
|
1315 return;
|
|
1316 }
|
|
1317 }
|
|
1318 #######################################################################################################################################################
|
|
1319 #######################################################################################################################################################
|
|
1320 ### We are now looking if there is a unique best alignment for a certain sequence. This means we are sorting in ascending order and look at the ###
|
|
1321 ### sequence with the lowest amount of mismatches. If there is only one single best position we are going to store the alignment information in the ###
|
|
1322 ### meth_call variables, if there are multiple hits with the same amount of (lowest) mismatches we are discarding the sequence altogether ###
|
|
1323 #######################################################################################################################################################
|
|
1324 #######################################################################################################################################################
|
|
1325 ### Going to use the variable $sequence_fails as a 'memory' if a sequence could not be aligned uniquely (set to 1 then)
|
|
1326 my $sequence_fails = 0;
|
|
1327 ### Declaring an empty hash reference which will store all information we need for the methylation call
|
|
1328 my $methylation_call_params; # hash reference!
|
|
1329 ### sorting in ascending order
|
|
1330 foreach my $mismatch_number (sort {$a<=>$b} keys %mismatches){
|
|
1331
|
|
1332 ### if there is only 1 entry in the hash with the lowest number of mismatches we accept it as the best alignment
|
|
1333 if (scalar keys %{$mismatches{$mismatch_number}} == 1){
|
|
1334 for my $unique_best_alignment (keys %{$mismatches{$mismatch_number}}){
|
|
1335 $methylation_call_params->{$identifier}->{bowtie_sequence} = $mismatches{$mismatch_number}->{$unique_best_alignment}->{bowtie_sequence};
|
|
1336 $methylation_call_params->{$identifier}->{chromosome} = $mismatches{$mismatch_number}->{$unique_best_alignment}->{chromosome};
|
|
1337 $methylation_call_params->{$identifier}->{position} = $mismatches{$mismatch_number}->{$unique_best_alignment}->{position};
|
|
1338 $methylation_call_params->{$identifier}->{index} = $mismatches{$mismatch_number}->{$unique_best_alignment}->{index};
|
|
1339 $methylation_call_params->{$identifier}->{number_of_mismatches} = $mismatch_number;
|
|
1340 }
|
|
1341 }
|
|
1342 elsif (scalar keys %{$mismatches{$mismatch_number}} == 3){
|
|
1343 ### If there are 3 sequences with the same number of lowest mismatches we can discriminate 2 cases: (i) all 3 alignments are unique best hits and
|
|
1344 ### come from different alignments processes (== indices) or (ii) one sequence alignment (== index) will give a unique best alignment, whereas a
|
|
1345 ### second one will produce 2 (or potentially many) alignments for the same sequence but in a different conversion state or against a different genome
|
|
1346 ### version (or both). This becomes especially relevant for highly converted sequences in which all Cs have been converted to Ts in the bisulfite
|
|
1347 ### reaction. E.g.
|
|
1348 ### CAGTCACGCGCGCGCG will become
|
|
1349 ### TAGTTATGTGTGTGTG in the CT transformed version, which will ideally still give the correct alignment in the CT->CT alignment condition.
|
|
1350 ### If the same read will then become G->A transformed as well however, the resulting sequence will look differently and potentially behave
|
|
1351 ### differently in a GA->GA alignment and this depends on the methylation state of the original sequence!:
|
|
1352 ### G->A conversion:
|
|
1353 ### highly methylated: CAATCACACACACACA
|
|
1354 ### highly converted : TAATTATATATATATA <== this sequence has a reduced complexity (only 2 bases left and not 3), and it is more likely to produce
|
|
1355 ### an alignment with a low complexity genomic region than the one above. This would normally lead to the entire sequence being kicked out as the
|
|
1356 ### there will be 3 alignments with the same number of lowest mismatches!! This in turn means that highly methylated and thereby not converted
|
|
1357 ### sequences are more likely to pass the alignment step, thereby creating a bias for methylated reads compared to their non-methylated counterparts.
|
|
1358 ### We do not want any bias, whatsover. Therefore if we have 1 sequence producing a unique best alignment and the second and third conditions
|
|
1359 ### producing alignments only after performing an additional (theoretical) conversion we want to keep the best alignment with the lowest number of
|
|
1360 ### additional transliterations performed. Thus we want to have a look at the level of complexity of the sequences producing the alignment.
|
|
1361 ### In the above example the number of transliterations required to transform the actual sequence
|
|
1362 ### to the C->T version would be TAGTTATGTGTGTGTG -> TAGTTATGTGTGTGTG = 0; (assuming this gives the correct alignment)
|
|
1363 ### in the G->A case it would be TAGTTATGTGTGTGTG -> TAATTATATATATATA = 6; (assuming this gives multiple wrong alignments)
|
|
1364 ### if the sequence giving a unique best alignment required a lower number of transliterations than the second best sequence yielding alignments
|
|
1365 ### while requiring a much higher number of transliterations, we are going to accept the unique best alignment with the lowest number of performed
|
|
1366 ### transliterations. As a threshold which does scale we will start with the number of tranliterations of the lowest best match x 2 must still be
|
|
1367 ### smaller than the number of tranliterations of the second best sequence. Everything will be flagged with $sequence_fails = 1 and discarded.
|
|
1368 my @three_candidate_seqs;
|
|
1369 foreach my $composite_location (keys (%{$mismatches{$mismatch_number}}) ){
|
|
1370 my $transliterations_performed;
|
|
1371 if ($mismatches{$mismatch_number}->{$composite_location}->{index} == 0 or $mismatches{$mismatch_number}->{$composite_location}->{index} == 1){
|
|
1372 $transliterations_performed = determine_number_of_transliterations_performed($sequence,'CT');
|
|
1373 }
|
|
1374 elsif ($mismatches{$mismatch_number}->{$composite_location}->{index} == 2 or $mismatches{$mismatch_number}->{$composite_location}->{index} == 3){
|
|
1375 $transliterations_performed = determine_number_of_transliterations_performed($sequence,'GA');
|
|
1376 }
|
|
1377 else{
|
|
1378 die "unexpected index number range $!\n";
|
|
1379 }
|
|
1380 push @three_candidate_seqs,{
|
|
1381 index =>$mismatches{$mismatch_number}->{$composite_location}->{index},
|
|
1382 bowtie_sequence => $mismatches{$mismatch_number}->{$composite_location}->{bowtie_sequence},
|
|
1383 mismatch_number => $mismatch_number,
|
|
1384 chromosome => $mismatches{$mismatch_number}->{$composite_location}->{chromosome},
|
|
1385 position => $mismatches{$mismatch_number}->{$composite_location}->{position},
|
|
1386 seq_id => $mismatches{$mismatch_number}->{$composite_location}->{seq_id},
|
|
1387 transliterations_performed => $transliterations_performed,
|
|
1388 };
|
|
1389 }
|
|
1390 ### sorting in ascending order for the lowest number of transliterations performed
|
|
1391 @three_candidate_seqs = sort {$a->{transliterations_performed} <=> $b->{transliterations_performed}} @three_candidate_seqs;
|
|
1392 my $first_array_element = $three_candidate_seqs[0]->{transliterations_performed};
|
|
1393 my $second_array_element = $three_candidate_seqs[1]->{transliterations_performed};
|
|
1394 my $third_array_element = $three_candidate_seqs[2]->{transliterations_performed};
|
|
1395 # print "$first_array_element\t$second_array_element\t$third_array_element\n";
|
|
1396 if (($first_array_element*2) < $second_array_element){
|
|
1397 $counting{low_complexity_alignments_overruled_count}++;
|
|
1398 ### taking the index with the unique best hit and over ruling low complexity alignments with 2 hits
|
|
1399 $methylation_call_params->{$identifier}->{bowtie_sequence} = $three_candidate_seqs[0]->{bowtie_sequence};
|
|
1400 $methylation_call_params->{$identifier}->{chromosome} = $three_candidate_seqs[0]->{chromosome};
|
|
1401 $methylation_call_params->{$identifier}->{position} = $three_candidate_seqs[0]->{position};
|
|
1402 $methylation_call_params->{$identifier}->{index} = $three_candidate_seqs[0]->{index};
|
|
1403 $methylation_call_params->{$identifier}->{number_of_mismatches} = $mismatch_number;
|
|
1404 # print "Overruled low complexity alignments! Using $first_array_element and disregarding $second_array_element and $third_array_element\n";
|
|
1405 }
|
|
1406 else{
|
|
1407 $sequence_fails = 1;
|
|
1408 }
|
|
1409 }
|
|
1410 else{
|
|
1411 $sequence_fails = 1;
|
|
1412 }
|
|
1413 ### after processing the alignment with the lowest number of mismatches we exit
|
|
1414 last;
|
|
1415 }
|
|
1416 ### skipping the sequence completely if there were multiple alignments with the same amount of lowest mismatches found at different positions
|
|
1417 if ($sequence_fails == 1){
|
|
1418 $counting{unsuitable_sequence_count}++;
|
|
1419 if ($ambiguous){
|
|
1420 return 2; # => exits to next sequence, and prints it out to multiple_alignments.out if --ambiguous has been specified
|
|
1421 }
|
|
1422 if ($unmapped){
|
|
1423 return 1; # => exits to next sequence, and prints it out to unmapped.out if --un has been specified
|
|
1424 }
|
|
1425 else{
|
|
1426 return 0; # => exits to next sequence (default)
|
|
1427 }
|
|
1428 }
|
|
1429
|
|
1430 ### --DIRECTIONAL
|
|
1431 ### If the option --directional has been specified the user wants to consider only alignments to the original top strand or the original bottom strand. We will therefore
|
|
1432 ### discard all alignments to strands complementary to the original strands, as they should not exist in reality due to the library preparation protocol
|
|
1433 if ($directional){
|
|
1434 if ( ($methylation_call_params->{$identifier}->{index} == 2) or ($methylation_call_params->{$identifier}->{index} == 3) ){
|
|
1435 # warn "Alignment rejected! (index was: $methylation_call_params->{$identifier}->{index})\n";
|
|
1436 $counting{alignments_rejected_count}++;
|
|
1437 return 0;
|
|
1438 }
|
|
1439 }
|
|
1440
|
|
1441 ### If the sequence has not been rejected so far it will have a unique best alignment
|
|
1442 $counting{unique_best_alignment_count}++;
|
|
1443 if ($pbat){
|
|
1444 extract_corresponding_genomic_sequence_single_end_pbat($identifier,$methylation_call_params);
|
|
1445 }
|
|
1446 else{
|
|
1447 extract_corresponding_genomic_sequence_single_end($identifier,$methylation_call_params);
|
|
1448 }
|
|
1449
|
|
1450 ### check test to see if the genomic sequence we extracted has the same length as the observed sequence+2, and only then we perform the methylation call
|
|
1451 if (length($methylation_call_params->{$identifier}->{unmodified_genomic_sequence}) != length($sequence)+2){
|
|
1452 warn "Chromosomal sequence could not be extracted for\t$identifier\t$methylation_call_params->{$identifier}->{chromosome}\t$methylation_call_params->{$identifier}->{position}\n";
|
|
1453 $counting{genomic_sequence_could_not_be_extracted_count}++;
|
|
1454 return 0;
|
|
1455 }
|
|
1456
|
|
1457 ### otherwise we are set to perform the actual methylation call
|
|
1458 $methylation_call_params->{$identifier}->{methylation_call} = methylation_call($identifier,$sequence,$methylation_call_params->{$identifier}->{unmodified_genomic_sequence},$methylation_call_params->{$identifier}->{read_conversion});
|
|
1459
|
|
1460 print_bisulfite_mapping_result_single_end($identifier,$sequence,$methylation_call_params,$quality_value);
|
|
1461 return 0; ## otherwise 1 will be returned by default, which would print the sequence to unmapped.out
|
|
1462 }
|
|
1463
|
|
1464 sub check_bowtie_results_single_end_bowtie2{
|
|
1465 my ($sequence,$identifier,$quality_value) = @_;
|
|
1466
|
|
1467
|
|
1468 unless ($quality_value){ # FastA sequences get assigned a quality value of Phred 40 throughout
|
|
1469 $quality_value = 'I'x(length$sequence);
|
|
1470 }
|
|
1471
|
|
1472 # as of version Bowtie 2 2.0.0 beta7, when input reads are unpaired, Bowtie 2 no longer removes the trailing /1 or /2 from the read name.
|
|
1473 # $identifier =~ s/\/[1234567890]+$//; # some sequencers don't just have /1 or /2 at the end of read IDs
|
|
1474 # print "sequence $sequence\nid $identifier\nquality: '$quality_value'\n";
|
|
1475
|
|
1476 my $alignment_ambiguous = 0;
|
|
1477
|
|
1478 my %alignments = ();
|
|
1479
|
|
1480 ### reading from the Bowtie 2 output filehandles
|
|
1481 foreach my $index (0..$#fhs){
|
|
1482 # print "Index: $index\n";
|
|
1483 # print "$fhs[$index]->{last_line}\n";
|
|
1484 # print "$fhs[$index]->{last_seq_id}\n";
|
|
1485 # sleep (1);
|
|
1486 ### skipping this index if the last alignment has been set to undefined already (i.e. end of bowtie output)
|
|
1487 next unless ($fhs[$index]->{last_line} and defined $fhs[$index]->{last_seq_id});
|
|
1488
|
|
1489 ### if the sequence we are currently looking at produced an alignment we are doing various things with it
|
|
1490 # print "last seq id: $fhs[$index]->{last_seq_id} and identifier: $identifier\n";
|
|
1491
|
|
1492 if ($fhs[$index]->{last_seq_id} eq $identifier) {
|
|
1493 # SAM format specifications for Bowtie 2
|
|
1494 # (1) Name of read that aligned
|
|
1495 # (2) Sum of all applicable flags. Flags relevant to Bowtie are:
|
|
1496 # 1 The read is one of a pair
|
|
1497 # 2 The alignment is one end of a proper paired-end alignment
|
|
1498 # 4 The read has no reported alignments
|
|
1499 # 8 The read is one of a pair and has no reported alignments
|
|
1500 # 16 The alignment is to the reverse reference strand
|
|
1501 # 32 The other mate in the paired-end alignment is aligned to the reverse reference strand
|
|
1502 # 64 The read is mate 1 in a pair
|
|
1503 # 128 The read is mate 2 in a pair
|
|
1504 # 256 The read has multiple mapping states
|
|
1505 # (3) Name of reference sequence where alignment occurs (unmapped reads have a *)
|
|
1506 # (4) 1-based offset into the forward reference strand where leftmost character of the alignment occurs (0 for unmapped reads)
|
|
1507 # (5) Mapping quality (255 means MAPQ is not available)
|
|
1508 # (6) CIGAR string representation of alignment (* if unavailable)
|
|
1509 # (7) Name of reference sequence where mate's alignment occurs. Set to = if the mate's reference sequence is the same as this alignment's, or * if there is no mate.
|
|
1510 # (8) 1-based offset into the forward reference strand where leftmost character of the mate's alignment occurs. Offset is 0 if there is no mate.
|
|
1511 # (9) Inferred fragment size. Size is negative if the mate's alignment occurs upstream of this alignment. Size is 0 if there is no mate.
|
|
1512 # (10) Read sequence (reverse-complemented if aligned to the reverse strand)
|
|
1513 # (11) ASCII-encoded read qualities (reverse-complemented if the read aligned to the reverse strand). The encoded quality values are on the Phred quality scale and the encoding is ASCII-offset by 33 (ASCII char !), similarly to a FASTQ file.
|
|
1514 # (12) Optional fields. Fields are tab-separated. bowtie2 outputs zero or more of these optional fields for each alignment, depending on the type of the alignment:
|
|
1515 # AS:i:<N> Alignment score. Can be negative. Can be greater than 0 in --local mode (but not in --end-to-end mode). Only present if SAM record is for an aligned read.
|
|
1516 # XS:i:<N> Alignment score for second-best alignment. Can be negative. Can be greater than 0 in --local mode (but not in --end-to-end mode). Only present if the SAM record is for an aligned read and more than one alignment was found for the read.
|
|
1517 # YS:i:<N> Alignment score for opposite mate in the paired-end alignment. Only present if the SAM record is for a read that aligned as part of a paired-end alignment.
|
|
1518 # XN:i:<N> The number of ambiguous bases in the reference covering this alignment. Only present if SAM record is for an aligned read.
|
|
1519 # XM:i:<N> The number of mismatches in the alignment. Only present if SAM record is for an aligned read.
|
|
1520 # XO:i:<N> The number of gap opens, for both read and reference gaps, in the alignment. Only present if SAM record is for an aligned read.
|
|
1521 # XG:i:<N> The number of gap extensions, for both read and reference gaps, in the alignment. Only present if SAM record is for an aligned read.
|
|
1522 # NM:i:<N> The edit distance; that is, the minimal number of one-nucleotide edits (substitutions, insertions and deletions) needed to transform the read string into the reference string. Only present if SAM record is for an aligned read.
|
|
1523 # YF:Z:<N> String indicating reason why the read was filtered out. See also: Filtering. Only appears for reads that were filtered out.
|
|
1524 # MD:Z:<S> A string representation of the mismatched reference bases in the alignment. See SAM format specification for details. Only present if SAM record is for an aligned read.
|
|
1525
|
|
1526 my ($id,$flag,$mapped_chromosome,$position,$mapping_quality,$cigar,$bowtie_sequence,$qual) = (split (/\t/,$fhs[$index]->{last_line}))[0,1,2,3,4,5,9,10];
|
|
1527
|
|
1528 ### If a sequence has no reported alignments there will be a single output line with a bit-wise flag value of 4. We can store the next alignment and move on to the next Bowtie 2 instance
|
|
1529 if ($flag == 4){
|
|
1530 ## reading in the next alignment, which must be the next sequence
|
|
1531 my $newline = $fhs[$index]->{fh}-> getline();
|
|
1532 if ($newline){
|
|
1533 chomp $newline;
|
|
1534 my ($seq_id) = split (/\t/,$newline);
|
|
1535 $fhs[$index]->{last_seq_id} = $seq_id;
|
|
1536 $fhs[$index]->{last_line} = $newline;
|
|
1537 if ($seq_id eq $identifier){
|
|
1538 die "Sequence with ID $identifier did not produce any alignment, but next seq-ID was also $fhs[$index]->{last_seq_id}!\n";
|
|
1539 }
|
|
1540 next; # next instance
|
|
1541 }
|
|
1542 else{
|
|
1543 # assigning undef to last_seq_id and last_line and jumping to the next index (end of Bowtie 2 output)
|
|
1544 $fhs[$index]->{last_seq_id} = undef;
|
|
1545 $fhs[$index]->{last_line} = undef;
|
|
1546 next;
|
|
1547 }
|
|
1548 }
|
|
1549
|
|
1550 # if there are one or more proper alignments we can extract the chromosome number
|
|
1551 my $chromosome;
|
|
1552 if ($mapped_chromosome =~ s/_(CT|GA)_converted$//){
|
|
1553 $chromosome = $mapped_chromosome;
|
|
1554 }
|
|
1555 else{
|
|
1556 die "Chromosome number extraction failed for $mapped_chromosome\n";
|
|
1557 }
|
|
1558
|
|
1559 ### We will use the optional field to determine the best alignment. Later on we extract the number of mismatches and/or indels from the CIGAR string
|
|
1560 my ($alignment_score,$second_best,$MD_tag);
|
|
1561 my @fields = split (/\t/,$fhs[$index]->{last_line});
|
|
1562
|
|
1563 foreach (11..$#fields){
|
|
1564 if ($fields[$_] =~ /AS:i:(.*)/){
|
|
1565 $alignment_score = $1;
|
|
1566 }
|
|
1567 elsif ($fields[$_] =~ /XS:i:(.*)/){
|
|
1568 $second_best = $1;
|
|
1569 }
|
|
1570 elsif ($fields[$_] =~ /MD:Z:(.*)/){
|
|
1571 $MD_tag = $1;
|
|
1572 }
|
|
1573 }
|
|
1574
|
|
1575 # warn "First best alignment_score is: '$alignment_score'\n";
|
|
1576 # warn "MD tag is: '$MD_tag'\n";
|
|
1577 die "Failed to extract alignment score ($alignment_score) and MD tag ($MD_tag)!\n" unless (defined $alignment_score and defined $MD_tag);
|
|
1578
|
|
1579 if (defined $second_best){
|
|
1580 # warn "second best alignment_score is: '$second_best'\n\n";
|
|
1581
|
|
1582 # If the first alignment score is the same as the alignment score of the second best hit we are going to boot this sequence altogether
|
|
1583 if ($alignment_score == $second_best){
|
|
1584 $alignment_ambiguous = 1;
|
|
1585 ## need to read and discard all additional ambiguous reads until we reach the next sequence
|
|
1586 until ($fhs[$index]->{last_seq_id} ne $identifier){
|
|
1587 my $newline = $fhs[$index]->{fh}-> getline();
|
|
1588 if ($newline){
|
|
1589 chomp $newline;
|
|
1590 my ($seq_id) = split (/\t/,$newline);
|
|
1591 $fhs[$index]->{last_seq_id} = $seq_id;
|
|
1592 $fhs[$index]->{last_line} = $newline;
|
|
1593 }
|
|
1594 else{
|
|
1595 # assigning undef to last_seq_id and last_line and jumping to the next index (end of Bowtie 2 output)
|
|
1596 $fhs[$index]->{last_seq_id} = undef;
|
|
1597 $fhs[$index]->{last_line} = undef;
|
|
1598 last; # break free in case we have reached the end of the alignment output
|
|
1599 }
|
|
1600 }
|
|
1601 # warn "Index: $index\tThe current Seq-ID is $identifier, skipped all ambiguous sequences until the next ID which is: $fhs[$index]->{last_seq_id}\n";
|
|
1602 }
|
|
1603 else{ # the next best alignment has a lower alignment score than the current read, so we can safely store the current alignment
|
|
1604
|
|
1605 my $alignment_location = join (":",$chromosome,$position);
|
|
1606
|
|
1607 ### If a sequence aligns to exactly the same location with a perfect match twice the sequence does either not contain any C or G, or all the Cs (or Gs on the reverse
|
|
1608 ### strand) were methylated and therefore protected. Alternatively it will align better in one condition than in the other. In any case, it is not needed to overwrite
|
|
1609 ### the same positional entry with a second entry for the same location, as the genomic sequence extraction and methylation call would not be affected by this. The only
|
|
1610 ### thing which would change is the index number for the found alignment). We will continue to assign these alignments to the first indexes 0 and 1, i.e. OT and OB
|
|
1611
|
|
1612 unless (exists $alignments{$alignment_location}){
|
|
1613 $alignments{$alignment_location}->{seq_id} = $id;
|
|
1614 $alignments{$alignment_location}->{alignment_score} = $alignment_score;
|
|
1615 $alignments{$alignment_location}->{bowtie_sequence} = $bowtie_sequence;
|
|
1616 $alignments{$alignment_location}->{index} = $index;
|
|
1617 $alignments{$alignment_location}->{chromosome} = $chromosome;
|
|
1618 $alignments{$alignment_location}->{position} = $position;
|
|
1619 $alignments{$alignment_location}->{CIGAR} = $cigar;
|
|
1620 $alignments{$alignment_location}->{MD_tag} = $MD_tag;
|
|
1621 }
|
|
1622
|
|
1623 ### now reading and discarding all (inferior) alignments of this sequencing read until we hit the next sequence
|
|
1624 until ($fhs[$index]->{last_seq_id} ne $identifier){
|
|
1625 my $newline = $fhs[$index]->{fh}-> getline();
|
|
1626 if ($newline){
|
|
1627 chomp $newline;
|
|
1628 my ($seq_id) = split (/\t/,$newline);
|
|
1629 $fhs[$index]->{last_seq_id} = $seq_id;
|
|
1630 $fhs[$index]->{last_line} = $newline;
|
|
1631 }
|
|
1632 else{
|
|
1633 # assigning undef to last_seq_id and last_line and jumping to the next index (end of Bowtie 2 output)
|
|
1634 $fhs[$index]->{last_seq_id} = undef;
|
|
1635 $fhs[$index]->{last_line} = undef;
|
|
1636 last; # break free in case we have reached the end of the alignment output
|
|
1637 }
|
|
1638 }
|
|
1639 # warn "Index: $index\tThe current Seq-ID is $identifier, skipped all ambiguous sequences until the next ID which is: $fhs[$index]->{last_seq_id}\n";
|
|
1640 }
|
|
1641 }
|
|
1642 else{ # there is no second best hit, so we can just store this one and read in the next sequence
|
|
1643
|
|
1644 my $alignment_location = join (":",$chromosome,$position);
|
|
1645
|
|
1646 ### If a sequence aligns to exactly the same location with a perfect match twice the sequence does either not contain any C or G, or all the Cs (or Gs on the reverse
|
|
1647 ### strand) were methylated and therefore protected. Alternatively it will align better in one condition than in the other. In any case, it is not needed to overwrite
|
|
1648 ### the same positional entry with a second entry for the same location, as the genomic sequence extraction and methylation call would not be affected by this. The only
|
|
1649 ### thing which would change is the index number for the found alignment). We will continue to assign these alignments to the first indexes 0 and 1, i.e. OT and OB
|
|
1650
|
|
1651 unless (exists $alignments{$alignment_location}){
|
|
1652 $alignments{$alignment_location}->{seq_id} = $id;
|
|
1653 $alignments{$alignment_location}->{alignment_score} = $alignment_score;
|
|
1654 $alignments{$alignment_location}->{bowtie_sequence} = $bowtie_sequence;
|
|
1655 $alignments{$alignment_location}->{index} = $index;
|
|
1656 $alignments{$alignment_location}->{chromosome} = $chromosome;
|
|
1657 $alignments{$alignment_location}->{position} = $position;
|
|
1658 $alignments{$alignment_location}->{MD_tag} = $MD_tag;
|
|
1659 $alignments{$alignment_location}->{CIGAR} = $cigar;
|
|
1660 }
|
|
1661
|
|
1662 my $newline = $fhs[$index]->{fh}-> getline();
|
|
1663 if ($newline){
|
|
1664 chomp $newline;
|
|
1665 my ($seq_id) = split (/\t/,$newline);
|
|
1666 $fhs[$index]->{last_seq_id} = $seq_id;
|
|
1667 $fhs[$index]->{last_line} = $newline;
|
|
1668 if ($seq_id eq $identifier){
|
|
1669 die "Sequence with ID $identifier did not have a second best alignment, but next seq-ID was also $fhs[$index]->{last_seq_id}!\n";
|
|
1670 }
|
|
1671 }
|
|
1672 else{
|
|
1673 # assigning undef to last_seq_id and last_line and jumping to the next index (end of Bowtie 2 output)
|
|
1674 $fhs[$index]->{last_seq_id} = undef;
|
|
1675 $fhs[$index]->{last_line} = undef;
|
|
1676 }
|
|
1677 }
|
|
1678 }
|
|
1679 }
|
|
1680
|
|
1681 ### if the read produced several ambiguous alignments already now can returning already now. If --ambiguous or --unmapped was specified the read sequence will be printed out.
|
|
1682 if ($alignment_ambiguous == 1){
|
|
1683 $counting{unsuitable_sequence_count}++;
|
|
1684 ### report that the sequence has multiple hits with bitwise flag 256. We can print the sequence to the result file straight away and skip everything else
|
|
1685 # my $ambiguous_read_output = join("\t",$identifier,'256','*','0','0','*','*','0','0',$sequence,$quality_value);
|
|
1686 # print "$ambiguous_read_output\n";
|
|
1687
|
|
1688 if ($ambiguous){
|
|
1689 return 2; # => exits to next sequence, and prints it out to _ambiguous_reads.txt if '--ambiguous' was specified
|
|
1690 }
|
|
1691 elsif ($unmapped){
|
|
1692 return 1; # => exits to next sequence, and prints it out to _unmapped_reads.txt if '--unmapped' but not '--ambiguous' was specified
|
|
1693 }
|
|
1694 else{
|
|
1695 return 0;
|
|
1696 }
|
|
1697 }
|
|
1698
|
|
1699 ### if there was no alignment found for a certain sequence at all we continue with the next sequence in the sequence file
|
|
1700 unless(%alignments){
|
|
1701 $counting{no_single_alignment_found}++;
|
|
1702 # my $unmapped_read_output = join("\t",$identifier,'4','*','0','0','*','*','0','0',$sequence,$quality_value);
|
|
1703 # print "$unmapped_read_output\n";
|
|
1704 if ($unmapped){
|
|
1705 return 1; # => exits to next sequence, and prints it out to _unmapped_reads.txt if '--unmapped' was specified
|
|
1706 }
|
|
1707 else{
|
|
1708 return 0; # default
|
|
1709 }
|
|
1710 }
|
|
1711
|
|
1712 #######################################################################################################################################################
|
|
1713
|
|
1714 ### If the sequence was not rejected so far we are now looking if there is a unique best alignment among all alignment instances. If there is only one
|
|
1715 ### single best position we are going to store the alignment information in the $meth_call variable. If there are multiple hits with the same (highest)
|
|
1716 ### alignment score we are discarding the sequence altogether.
|
|
1717 ### For end-to-end alignments the maximum alignment score can be 0, each mismatch can receive penalties up to 6, and each gap receives penalties for
|
|
1718 ### opening (5) and extending (3 per bp) the gap.
|
|
1719
|
|
1720 #######################################################################################################################################################
|
|
1721
|
|
1722 my $methylation_call_params; # hash reference which will store all information we need for the methylation call
|
|
1723 my $sequence_fails = 0; # Going to use $sequence_fails as a 'memory' if a sequence could not be aligned uniquely (set to 1 then)
|
|
1724
|
|
1725 ### print contents of %alignments for debugging
|
|
1726 # if (scalar keys %alignments > 1){
|
|
1727 # print "\n******\n";
|
|
1728 # foreach my $alignment_location (sort {$a cmp $b} keys %alignments){
|
|
1729 # print "Loc: $alignment_location\n";
|
|
1730 # print "ID: $alignments{$alignment_location}->{seq_id}\n";
|
|
1731 # print "AS: $alignments{$alignment_location}->{alignment_score}\n";
|
|
1732 # print "Seq: $alignments{$alignment_location}->{bowtie_sequence}\n";
|
|
1733 # print "Index $alignments{$alignment_location}->{index}\n";
|
|
1734 # print "Chr: $alignments{$alignment_location}->{chromosome}\n";
|
|
1735 # print "pos: $alignments{$alignment_location}->{position}\n";
|
|
1736 # print "MD: $alignments{$alignment_location}->{MD_tag}\n\n";
|
|
1737 # }
|
|
1738 # print "\n******\n";
|
|
1739 # }
|
|
1740
|
|
1741 ### if there is only 1 entry in the hash with we accept it as the best alignment
|
|
1742 if (scalar keys %alignments == 1){
|
|
1743 for my $unique_best_alignment (keys %alignments){
|
|
1744 $methylation_call_params->{$identifier}->{bowtie_sequence} = $alignments{$unique_best_alignment}->{bowtie_sequence};
|
|
1745 $methylation_call_params->{$identifier}->{chromosome} = $alignments{$unique_best_alignment}->{chromosome};
|
|
1746 $methylation_call_params->{$identifier}->{position} = $alignments{$unique_best_alignment}->{position};
|
|
1747 $methylation_call_params->{$identifier}->{index} = $alignments{$unique_best_alignment}->{index};
|
|
1748 $methylation_call_params->{$identifier}->{alignment_score} = $alignments{$unique_best_alignment}->{alignment_score};
|
|
1749 $methylation_call_params->{$identifier}->{MD_tag} = $alignments{$unique_best_alignment}->{MD_tag};
|
|
1750 $methylation_call_params->{$identifier}->{CIGAR} = $alignments{$unique_best_alignment}->{CIGAR};
|
|
1751 }
|
|
1752 }
|
|
1753
|
|
1754 ### otherwise we are going to find out if there is a best match among the multiple alignments, or whether there are 2 or more equally good alignments (in which case
|
|
1755 ### we boot the sequence altogether
|
|
1756 elsif (scalar keys %alignments >= 2 and scalar keys %alignments <= 4){
|
|
1757 my $best_alignment_score;
|
|
1758 my $best_alignment_location;
|
|
1759 foreach my $alignment_location (sort {$alignments{$b}->{alignment_score} <=> $alignments{$a}->{alignment_score}} keys %alignments){
|
|
1760 # print "$alignments{$alignment_location}->{alignment_score}\n";
|
|
1761 unless (defined $best_alignment_score){
|
|
1762 $best_alignment_score = $alignments{$alignment_location}->{alignment_score};
|
|
1763 $best_alignment_location = $alignment_location;
|
|
1764 # print "setting best alignment score: $best_alignment_score\n";
|
|
1765 }
|
|
1766 else{
|
|
1767 ### if the second best alignment has the same alignment score as the first one, the sequence will get booted
|
|
1768 if ($alignments{$alignment_location}->{alignment_score} == $best_alignment_score){
|
|
1769 # warn "Same alignment score, the sequence will get booted!\n";
|
|
1770 $sequence_fails = 1;
|
|
1771 last; # exiting after the second alignment since we know that the sequence has ambiguous alignments
|
|
1772 }
|
|
1773 ### else we are going to store the best alignment for further processing
|
|
1774 else{
|
|
1775 $methylation_call_params->{$identifier}->{bowtie_sequence} = $alignments{$best_alignment_location}->{bowtie_sequence};
|
|
1776 $methylation_call_params->{$identifier}->{chromosome} = $alignments{$best_alignment_location}->{chromosome};
|
|
1777 $methylation_call_params->{$identifier}->{position} = $alignments{$best_alignment_location}->{position};
|
|
1778 $methylation_call_params->{$identifier}->{index} = $alignments{$best_alignment_location}->{index};
|
|
1779 $methylation_call_params->{$identifier}->{alignment_score} = $alignments{$best_alignment_location}->{alignment_score};
|
|
1780 $methylation_call_params->{$identifier}->{MD_tag} = $alignments{$best_alignment_location}->{MD_tag};
|
|
1781 $methylation_call_params->{$identifier}->{CIGAR} = $alignments{$best_alignment_location}->{CIGAR};
|
|
1782 last; # exiting after processing the second alignment since the sequence produced a unique best alignment
|
|
1783 }
|
|
1784 }
|
|
1785 }
|
|
1786 }
|
|
1787 else{
|
|
1788 die "There are too many potential hits for this sequence (1-4 expected, but found: ",scalar keys %alignments,")\n";;
|
|
1789 }
|
|
1790
|
|
1791 ### skipping the sequence completely if there were multiple alignments with the same best alignment score at different positions
|
|
1792 if ($sequence_fails == 1){
|
|
1793 $counting{unsuitable_sequence_count}++;
|
|
1794
|
|
1795 ### report that the sequence has multiple hits with bitwise flag 256. We can print the sequence to the result file straight away and skip everything else
|
|
1796 # my $ambiguous_read_output = join("\t",$identifier,'256','*','0','0','*','*','0','0',$sequence,$quality_value);
|
|
1797 # print OUT "$ambiguous_read_output\n";
|
|
1798
|
|
1799 if ($ambiguous){
|
|
1800 return 2; # => exits to next sequence, and prints it out (in FastQ format) to _ambiguous_reads.txt if '--ambiguous' was specified
|
|
1801 }
|
|
1802 elsif ($unmapped){
|
|
1803 return 1; # => exits to next sequence, and prints it out (in FastQ format) to _unmapped_reads.txt if '--unmapped' but not '--ambiguous' was specified
|
|
1804 }
|
|
1805 else{
|
|
1806 return 0; # => exits to next sequence (default)
|
|
1807 }
|
|
1808 }
|
|
1809
|
|
1810 ### --DIRECTIONAL
|
|
1811 ### If the option --directional has been specified the user wants to consider only alignments to the original top strand or the original bottom strand. We will therefore
|
|
1812 ### discard all alignments to strands complementary to the original strands, as they should not exist in reality due to the library preparation protocol
|
|
1813 if ($directional){
|
|
1814 if ( ($methylation_call_params->{$identifier}->{index} == 2) or ($methylation_call_params->{$identifier}->{index} == 3) ){
|
|
1815 # warn "Alignment rejected! (index was: $methylation_call_params->{$identifier}->{index})\n";
|
|
1816 $counting{alignments_rejected_count}++;
|
|
1817 return 0;
|
|
1818 }
|
|
1819 }
|
|
1820
|
|
1821 ### If the sequence has not been rejected so far it has a unique best alignment
|
|
1822 $counting{unique_best_alignment_count}++;
|
|
1823
|
|
1824 ### Now we need to extract a genomic sequence that exactly corresponds to the reported alignment. This potentially means that we need to deal with insertions or deletions as well
|
|
1825 extract_corresponding_genomic_sequence_single_end_bowtie2 ($identifier,$methylation_call_params);
|
|
1826
|
|
1827 ### check test to see if the genomic sequence we extracted has the same length as the observed sequence+2, and only then we perform the methylation call
|
|
1828 if (length($methylation_call_params->{$identifier}->{unmodified_genomic_sequence}) != length($sequence)+2){
|
|
1829 warn "Chromosomal sequence could not be extracted for\t$identifier\t$methylation_call_params->{$identifier}->{chromosome}\t$methylation_call_params->{$identifier}->{position}\n";
|
|
1830 $counting{genomic_sequence_could_not_be_extracted_count}++;
|
|
1831 return 0;
|
|
1832 }
|
|
1833
|
|
1834
|
|
1835 ### otherwise we are set to perform the actual methylation call
|
|
1836 $methylation_call_params->{$identifier}->{methylation_call} = methylation_call($identifier,$sequence,$methylation_call_params->{$identifier}->{unmodified_genomic_sequence},$methylation_call_params->{$identifier}->{read_conversion});
|
|
1837 print_bisulfite_mapping_result_single_end_bowtie2 ($identifier,$sequence,$methylation_call_params,$quality_value);
|
|
1838 return 0; ## if a sequence got this far we do not want to print it to unmapped or ambiguous.out
|
|
1839 }
|
|
1840
|
|
1841
|
|
1842 sub determine_number_of_transliterations_performed{
|
|
1843 my ($sequence,$read_conversion) = @_;
|
|
1844 my $number_of_transliterations;
|
|
1845 if ($read_conversion eq 'CT'){
|
|
1846 $number_of_transliterations = $sequence =~ tr/C/T/;
|
|
1847 }
|
|
1848 elsif ($read_conversion eq 'GA'){
|
|
1849 $number_of_transliterations = $sequence =~ tr/G/A/;
|
|
1850 }
|
|
1851 else{
|
|
1852 die "Read conversion mode of the read was not specified $!\n";
|
|
1853 }
|
|
1854 return $number_of_transliterations;
|
|
1855 }
|
|
1856
|
|
1857 sub decide_whether_single_end_alignment_is_valid{
|
|
1858 my ($index,$identifier) = @_;
|
|
1859
|
|
1860 # extracting from Bowtie 1 format
|
|
1861 my ($id,$strand) = (split (/\t/,$fhs[$index]->{last_line}))[0,1];
|
|
1862
|
|
1863 ### ensuring that the entry is the correct sequence
|
|
1864 if (($id eq $fhs[$index]->{last_seq_id}) and ($id eq $identifier)){
|
|
1865 ### checking the orientation of the alignment. We need to discriminate between 8 different conditions, however only 4 of them are theoretically
|
|
1866 ### sensible alignments
|
|
1867 my $orientation = ensure_sensical_alignment_orientation_single_end ($index,$strand);
|
|
1868 ### If the orientation was correct can we move on
|
|
1869 if ($orientation == 1){
|
|
1870 return 1; ### 1st possibility for a sequence to pass
|
|
1871 }
|
|
1872 ### If the alignment was in the wrong orientation we need to read in a new line
|
|
1873 elsif($orientation == 0){
|
|
1874 my $newline = $fhs[$index]->{fh}->getline();
|
|
1875 if ($newline){
|
|
1876 ($id,$strand) = (split (/\t/,$newline))[0,1];
|
|
1877
|
|
1878 ### ensuring that the next entry is still the correct sequence
|
|
1879 if ($id eq $identifier){
|
|
1880 ### checking orientation again
|
|
1881 $orientation = ensure_sensical_alignment_orientation_single_end ($index,$strand);
|
|
1882 ### If the orientation was correct can we move on
|
|
1883 if ($orientation == 1){
|
|
1884 $fhs[$index]->{last_seq_id} = $id;
|
|
1885 $fhs[$index]->{last_line} = $newline;
|
|
1886 return 1; ### 2nd possibility for a sequence to pass
|
|
1887 }
|
|
1888 ### If the alignment was in the wrong orientation again we need to read in yet another new line and store it in @fhs
|
|
1889 elsif ($orientation == 0){
|
|
1890 $newline = $fhs[$index]->{fh}->getline();
|
|
1891 if ($newline){
|
|
1892 my ($seq_id) = split (/\t/,$newline);
|
|
1893 ### check if the next line still has the same seq ID (must not happen), and if not overwrite the current seq-ID and bowtie output with
|
|
1894 ### the same fields of the just read next entry
|
|
1895 die "Same seq ID 3 or more times in a row!(should be 2 max) $!" if ($seq_id eq $identifier);
|
|
1896 $fhs[$index]->{last_seq_id} = $seq_id;
|
|
1897 $fhs[$index]->{last_line} = $newline;
|
|
1898 return 0; # not processing anything this round as the alignment currently stored in last_line was in the wrong orientation
|
|
1899 }
|
|
1900 else{
|
|
1901 # assigning undef to last_seq_id and last_line (end of bowtie output)
|
|
1902 $fhs[$index]->{last_seq_id} = undef;
|
|
1903 $fhs[$index]->{last_line} = undef;
|
|
1904 return 0; # not processing anything as the alignment currently stored in last_line was in the wrong orientation
|
|
1905 }
|
|
1906 }
|
|
1907 else{
|
|
1908 die "The orientation of the alignment must be either correct or incorrect\n";
|
|
1909 }
|
|
1910 }
|
|
1911 ### the sequence we just read in is already the next sequence to be analysed -> store it in @fhs
|
|
1912 else{
|
|
1913 $fhs[$index]->{last_seq_id} = $id;
|
|
1914 $fhs[$index]->{last_line} = $newline;
|
|
1915 return 0; # processing the new alignment result only in the next round
|
|
1916 }
|
|
1917 }
|
|
1918 else {
|
|
1919 # assigning undef to last_seq_id and last_line (end of bowtie output)
|
|
1920 $fhs[$index]->{last_seq_id} = undef;
|
|
1921 $fhs[$index]->{last_line} = undef;
|
|
1922 return 0; # not processing anything as the alignment currently stored in last_line was in the wrong orientation
|
|
1923 }
|
|
1924 }
|
|
1925 else{
|
|
1926 die "The orientation of the alignment must be either correct or incorrect\n";
|
|
1927 }
|
|
1928 }
|
|
1929 ### the sequence stored in @fhs as last_line is already the next sequence to be analysed -> analyse next round
|
|
1930 else{
|
|
1931 return 0;
|
|
1932 }
|
|
1933 }
|
|
1934 #########################
|
|
1935 ### BOWTIE 1 | PAIRED-END
|
|
1936 #########################
|
|
1937
|
|
1938 sub check_bowtie_results_paired_ends{
|
|
1939 my ($sequence_1,$sequence_2,$identifier,$quality_value_1,$quality_value_2) = @_;
|
|
1940
|
|
1941 ### quality values are not given for FastA files, so they are initialised with a Phred quality of 40
|
|
1942 unless ($quality_value_1){
|
|
1943 $quality_value_1 = 'I'x(length$sequence_1);
|
|
1944 }
|
|
1945 unless ($quality_value_2){
|
|
1946 $quality_value_2 = 'I'x(length$sequence_2);
|
|
1947 }
|
|
1948
|
|
1949 # warn "$identifier\n$fhs[0]->{last_seq_id}\n$fhs[1]->{last_seq_id}\n$fhs[2]->{last_seq_id}\n$fhs[3]->{last_seq_id}\n\n";
|
|
1950 # sleep (1);
|
|
1951 my %mismatches = ();
|
|
1952 ### reading from the bowtie output files to see if this sequence pair aligned to a bisulfite converted genome
|
|
1953
|
|
1954
|
|
1955 ### for paired end reads we are reporting alignments to the OT strand first (index 0), then the OB strand (index 3!!), similiar to the single end way.
|
|
1956 ### alignments to the complementary strands are reported afterwards (CTOT got index 1, and CTOB got index 2).
|
|
1957 ### This is needed so that alignments which either contain no single C or G or reads which contain only protected Cs are reported to the original strands (OT and OB)
|
|
1958 ### Before the complementary strands. Remember that it does not make any difference for the methylation calls, but it will matter if alignment to the complementary
|
|
1959 ### strands are not being reported by specifying --directional
|
|
1960
|
|
1961 foreach my $index (0,3,1,2){
|
|
1962 ### skipping this index if the last alignment has been set to undefined already (i.e. end of bowtie output)
|
|
1963 next unless ($fhs[$index]->{last_line_1} and $fhs[$index]->{last_line_2} and defined $fhs[$index]->{last_seq_id});
|
|
1964 ### if the sequence pair we are currently looking at produced an alignment we are doing various things with it
|
|
1965 if ($fhs[$index]->{last_seq_id} eq $identifier) {
|
|
1966 # print "$identifier\n$fhs[$index]->{last_seq_id}\n\n";
|
|
1967
|
|
1968 ##################################################################################
|
|
1969 ### STEP I Processing the entry which is stored in last_line_1 and last_line_2 ###
|
|
1970 ##################################################################################
|
|
1971 my $valid_alignment_found = decide_whether_paired_end_alignment_is_valid($index,$identifier);
|
|
1972 ### sequences can fail at this point if there was only 1 alignment in the wrong orientation, or if there were 2 aligments both in the wrong
|
|
1973 ### orientation. We only continue to extract useful information about this alignment if 1 was returned
|
|
1974 if ($valid_alignment_found == 1){
|
|
1975 ### Bowtie outputs which made it this far are in the correct orientation, so we can continue to analyse the alignment itself.
|
|
1976 ### we store the useful information in %mismatches
|
|
1977 my ($id_1,$strand_1,$mapped_chromosome_1,$position_1,$bowtie_sequence_1,$mismatch_info_1) = (split (/\t/,$fhs[$index]->{last_line_1},-1))[0,1,2,3,4,7];
|
|
1978 my ($id_2,$strand_2,$mapped_chromosome_2,$position_2,$bowtie_sequence_2,$mismatch_info_2) = (split (/\t/,$fhs[$index]->{last_line_2},-1))[0,1,2,3,4,7];
|
|
1979 chomp $mismatch_info_1;
|
|
1980 chomp $mismatch_info_2;
|
|
1981
|
|
1982 ### need to extract the chromosome number from the bowtie output (which is either XY_CT_converted or XY_GA_converted
|
|
1983 my ($chromosome_1,$chromosome_2);
|
|
1984 if ($mapped_chromosome_1 =~ s/_(CT|GA)_converted$//){
|
|
1985 $chromosome_1 = $mapped_chromosome_1;
|
|
1986 }
|
|
1987 else{
|
|
1988 die "Chromosome number extraction failed for $mapped_chromosome_1\n";
|
|
1989 }
|
|
1990 if ($mapped_chromosome_2 =~ s/_(CT|GA)_converted$//){
|
|
1991 $chromosome_2 = $mapped_chromosome_2;
|
|
1992 }
|
|
1993 else{
|
|
1994 die "Chromosome number extraction failed for $mapped_chromosome_2\n";
|
|
1995 }
|
|
1996
|
|
1997 ### Now extracting the number of mismatches to the converted genome
|
|
1998 my $number_of_mismatches_1;
|
|
1999 my $number_of_mismatches_2;
|
|
2000 if ($mismatch_info_1 eq ''){
|
|
2001 $number_of_mismatches_1 = 0;
|
|
2002 }
|
|
2003 elsif ($mismatch_info_1 =~ /^\d/){
|
|
2004 my @mismatches = split (/,/,$mismatch_info_1);
|
|
2005 $number_of_mismatches_1 = scalar @mismatches;
|
|
2006 }
|
|
2007 else{
|
|
2008 die "Something weird is going on with the mismatch field\n";
|
|
2009 }
|
|
2010 if ($mismatch_info_2 eq ''){
|
|
2011 $number_of_mismatches_2 = 0;
|
|
2012 }
|
|
2013 elsif ($mismatch_info_2 =~ /^\d/){
|
|
2014 my @mismatches = split (/,/,$mismatch_info_2);
|
|
2015 $number_of_mismatches_2 = scalar @mismatches;
|
|
2016 }
|
|
2017 else{
|
|
2018 die "Something weird is going on with the mismatch field\n";
|
|
2019 }
|
|
2020 ### To decide whether a sequence pair has a unique best alignment we will look at the lowest sum of mismatches from both alignments
|
|
2021 my $sum_of_mismatches = $number_of_mismatches_1+$number_of_mismatches_2;
|
|
2022 ### creating a composite location variable from $chromosome and $position and storing the alignment information in a temporary hash table
|
|
2023 die "Position 1 is higher than position 2" if ($position_1 > $position_2);
|
|
2024 die "Paired-end alignments need to be on the same chromosome\n" unless ($chromosome_1 eq $chromosome_2);
|
|
2025 my $alignment_location = join(":",$chromosome_1,$position_1,$position_2);
|
|
2026 ### If a sequence aligns to exactly the same location twice the sequence does either not contain any C or G, or all the Cs (or Gs on the reverse
|
|
2027 ### strand) were methylated and therefore protected. It is not needed to overwrite the same positional entry with a second entry for the same
|
|
2028 ### location (the genomic sequence extraction and methylation would not be affected by this, only the thing which would change is the index
|
|
2029 ### number for the found alignment)
|
|
2030 unless (exists $mismatches{$sum_of_mismatches}->{$alignment_location}){
|
|
2031 $mismatches{$sum_of_mismatches}->{$alignment_location}->{seq_id}=$id_1; # either is fine
|
|
2032 $mismatches{$sum_of_mismatches}->{$alignment_location}->{bowtie_sequence_1}=$bowtie_sequence_1;
|
|
2033 $mismatches{$sum_of_mismatches}->{$alignment_location}->{bowtie_sequence_2}=$bowtie_sequence_2;
|
|
2034 $mismatches{$sum_of_mismatches}->{$alignment_location}->{index}=$index;
|
|
2035 $mismatches{$sum_of_mismatches}->{$alignment_location}->{chromosome}=$chromosome_1; # either is fine
|
|
2036 $mismatches{$sum_of_mismatches}->{$alignment_location}->{start_seq_1}=$position_1;
|
|
2037 $mismatches{$sum_of_mismatches}->{$alignment_location}->{start_seq_2}=$position_2;
|
|
2038 $mismatches{$sum_of_mismatches}->{$alignment_location}->{number_of_mismatches_1} = $number_of_mismatches_1;
|
|
2039 $mismatches{$sum_of_mismatches}->{$alignment_location}->{number_of_mismatches_2} = $number_of_mismatches_2;
|
|
2040 }
|
|
2041 ###################################################################################################################################################
|
|
2042 ### STEP II Now reading in the next 2 lines from the bowtie filehandle. If there are 2 next lines in the alignments filehandle it can either ###
|
|
2043 ### be a second alignment of the same sequence pair or a new sequence pair. In any case we will just add it to last_line_1 and last_line _2. ###
|
|
2044 ### If it is the alignment of the next sequence pair, 0 will be returned as $valid_alignment_found, so it will not be processed any further in ###
|
|
2045 ### this round ###
|
|
2046 ###################################################################################################################################################
|
|
2047 my $newline_1 = $fhs[$index]->{fh}-> getline();
|
|
2048 my $newline_2 = $fhs[$index]->{fh}-> getline();
|
|
2049
|
|
2050 if ($newline_1 and $newline_2){
|
|
2051 my ($seq_id_1) = split (/\t/,$newline_1);
|
|
2052 my ($seq_id_2) = split (/\t/,$newline_2);
|
|
2053
|
|
2054 if ($seq_id_1 =~ s/\/1$//){ # removing the read /1 tag
|
|
2055 $fhs[$index]->{last_seq_id} = $seq_id_1;
|
|
2056 }
|
|
2057 elsif ($seq_id_2 =~ s/\/1$//){ # removing the read /1 tag
|
|
2058 $fhs[$index]->{last_seq_id} = $seq_id_2;
|
|
2059 }
|
|
2060 else{
|
|
2061 die "Either read 1 or read 2 needs to end on '/1'\n";
|
|
2062 }
|
|
2063
|
|
2064 $fhs[$index]->{last_line_1} = $newline_1;
|
|
2065 $fhs[$index]->{last_line_2} = $newline_2;
|
|
2066 }
|
|
2067 else {
|
|
2068 # assigning undef to last_seq_id and both last_lines and jumping to the next index (end of bowtie output)
|
|
2069 $fhs[$index]->{last_seq_id} = undef;
|
|
2070 $fhs[$index]->{last_line_1} = undef;
|
|
2071 $fhs[$index]->{last_line_2} = undef;
|
|
2072 next; # jumping to the next index
|
|
2073 }
|
|
2074 ### Now processing the entry we just stored in last_line_1 and last_line_2
|
|
2075 $valid_alignment_found = decide_whether_paired_end_alignment_is_valid($index,$identifier);
|
|
2076 ### only processing the alignment further if 1 was returned. 0 will be returned either if the alignment is already the next sequence pair to
|
|
2077 ### be analysed or if it was a second alignment of the current sequence pair but in the wrong orientation
|
|
2078 if ($valid_alignment_found == 1){
|
|
2079 ### we store the useful information in %mismatches
|
|
2080 ($id_1,$strand_1,$mapped_chromosome_1,$position_1,$bowtie_sequence_1,$mismatch_info_1) = (split (/\t/,$fhs[$index]->{last_line_1}))[0,1,2,3,4,7];
|
|
2081 ($id_2,$strand_2,$mapped_chromosome_2,$position_2,$bowtie_sequence_2,$mismatch_info_2) = (split (/\t/,$fhs[$index]->{last_line_2}))[0,1,2,3,4,7];
|
|
2082 chomp $mismatch_info_1;
|
|
2083 chomp $mismatch_info_2;
|
|
2084 ### need to extract the chromosome number from the bowtie output (which is either _CT_converted or _GA_converted)
|
|
2085 if ($mapped_chromosome_1 =~ s/_(CT|GA)_converted$//){
|
|
2086 $chromosome_1 = $mapped_chromosome_1;
|
|
2087 }
|
|
2088 else{
|
|
2089 die "Chromosome number extraction failed for $mapped_chromosome_1\n";
|
|
2090 }
|
|
2091 if ($mapped_chromosome_2 =~ s/_(CT|GA)_converted$//){
|
|
2092 $chromosome_2 = $mapped_chromosome_2;
|
|
2093 }
|
|
2094 else{
|
|
2095 die "Chromosome number extraction failed for $mapped_chromosome_2\n";
|
|
2096 }
|
|
2097
|
|
2098 $number_of_mismatches_1='';
|
|
2099 $number_of_mismatches_2='';
|
|
2100 ### Now extracting the number of mismatches to the converted genome
|
|
2101 if ($mismatch_info_1 eq ''){
|
|
2102 $number_of_mismatches_1 = 0;
|
|
2103 }
|
|
2104 elsif ($mismatch_info_1 =~ /^\d/){
|
|
2105 my @mismatches = split (/,/,$mismatch_info_1);
|
|
2106 $number_of_mismatches_1 = scalar @mismatches;
|
|
2107 }
|
|
2108 else{
|
|
2109 die "Something weird is going on with the mismatch field\n";
|
|
2110 }
|
|
2111 if ($mismatch_info_2 eq ''){
|
|
2112 $number_of_mismatches_2 = 0;
|
|
2113 }
|
|
2114 elsif ($mismatch_info_2 =~ /^\d/){
|
|
2115 my @mismatches = split (/,/,$mismatch_info_2);
|
|
2116 $number_of_mismatches_2 = scalar @mismatches;
|
|
2117 }
|
|
2118 else{
|
|
2119 die "Something weird is going on with the mismatch field\n";
|
|
2120 }
|
|
2121 ### To decide whether a sequence pair has a unique best alignment we will look at the lowest sum of mismatches from both alignments
|
|
2122 $sum_of_mismatches = $number_of_mismatches_1+$number_of_mismatches_2;
|
|
2123 ### creating a composite location variable from $chromosome and $position and storing the alignment information in a temporary hash table
|
|
2124 die "position 1 is greater than position 2" if ($position_1 > $position_2);
|
|
2125 die "Paired-end alignments need to be on the same chromosome\n" unless ($chromosome_1 eq $chromosome_2);
|
|
2126 $alignment_location = join(":",$chromosome_1,$position_1,$position_2);
|
|
2127 ### If a sequence aligns to exactly the same location twice the sequence does either not contain any C or G, or all the Cs (or Gs on the reverse
|
|
2128 ### strand) were methylated and therefore protected. It is not needed to overwrite the same positional entry with a second entry for the same
|
|
2129 ### location (the genomic sequence extraction and methylation would not be affected by this, only the thing which would change is the index
|
|
2130 ### number for the found alignment)
|
|
2131 unless (exists $mismatches{$sum_of_mismatches}->{$alignment_location}){
|
|
2132 $mismatches{$sum_of_mismatches}->{$alignment_location}->{seq_id}=$id_1; # either is fine
|
|
2133 $mismatches{$sum_of_mismatches}->{$alignment_location}->{bowtie_sequence_1}=$bowtie_sequence_1;
|
|
2134 $mismatches{$sum_of_mismatches}->{$alignment_location}->{bowtie_sequence_2}=$bowtie_sequence_2;
|
|
2135 $mismatches{$sum_of_mismatches}->{$alignment_location}->{index}=$index;
|
|
2136 $mismatches{$sum_of_mismatches}->{$alignment_location}->{chromosome}=$chromosome_1; # either is fine
|
|
2137 $mismatches{$sum_of_mismatches}->{$alignment_location}->{start_seq_1}=$position_1;
|
|
2138 $mismatches{$sum_of_mismatches}->{$alignment_location}->{start_seq_2}=$position_2;
|
|
2139 $mismatches{$sum_of_mismatches}->{$alignment_location}->{number_of_mismatches_1} = $number_of_mismatches_1;
|
|
2140 $mismatches{$sum_of_mismatches}->{$alignment_location}->{number_of_mismatches_2} = $number_of_mismatches_2;
|
|
2141 }
|
|
2142 ###############################################################################################################################################
|
|
2143 ### STEP III Now reading in two more lines. These have to be the next entry and we will just add assign them to last_line_1 and last_line_2 ###
|
|
2144 ###############################################################################################################################################
|
|
2145 $newline_1 = $fhs[$index]->{fh}-> getline();
|
|
2146 $newline_2 = $fhs[$index]->{fh}-> getline();
|
|
2147
|
|
2148 if ($newline_1 and $newline_2){
|
|
2149 my ($seq_id_1) = split (/\t/,$newline_1);
|
|
2150 my ($seq_id_2) = split (/\t/,$newline_2);
|
|
2151
|
|
2152 if ($seq_id_1 =~ s/\/1$//){ # removing the read /1 tag
|
|
2153 $fhs[$index]->{last_seq_id} = $seq_id_1;
|
|
2154 }
|
|
2155 if ($seq_id_2 =~ s/\/1$//){ # removing the read /1 tag
|
|
2156 $fhs[$index]->{last_seq_id} = $seq_id_2;
|
|
2157 }
|
|
2158 $fhs[$index]->{last_line_1} = $newline_1;
|
|
2159 $fhs[$index]->{last_line_2} = $newline_2;
|
|
2160 }
|
|
2161 else {
|
|
2162 # assigning undef to last_seq_id and both last_lines and jumping to the next index (end of bowtie output)
|
|
2163 $fhs[$index]->{last_seq_id} = undef;
|
|
2164 $fhs[$index]->{last_line_1} = undef;
|
|
2165 $fhs[$index]->{last_line_2} = undef;
|
|
2166 next; # jumping to the next index
|
|
2167 }
|
|
2168 ### within the 2nd sequence pair alignment in correct orientation found
|
|
2169 }
|
|
2170 ### within the 1st sequence pair alignment in correct orientation found
|
|
2171 }
|
|
2172 ### still within the (last_seq_id eq identifier) condition
|
|
2173 }
|
|
2174 ### still within foreach index loop
|
|
2175 }
|
|
2176 ### if there was no single alignment found for a certain sequence we will continue with the next sequence in the sequence file
|
|
2177 unless(%mismatches){
|
|
2178 $counting{no_single_alignment_found}++;
|
|
2179 return 1; ### We will print this sequence out as unmapped sequence if --un unmapped.out has been specified
|
|
2180 }
|
|
2181 ### Going to use the variable $sequence_pair_fails as a 'memory' if a sequence could not be aligned uniquely (set to 1 then)
|
|
2182 my $sequence_pair_fails = 0;
|
|
2183 ### Declaring an empty hash reference which will store all information we need for the methylation call
|
|
2184 my $methylation_call_params; # hash reference!
|
|
2185 ### We are now looking if there is a unique best alignment for a certain sequence. This means we are sorting in ascending order and look at the
|
|
2186 ### sequence with the lowest amount of mismatches. If there is only one single best position we are going to store the alignment information in the
|
|
2187 ### meth_call variables, if there are multiple hits with the same amount of (lowest) mismatches we are discarding the sequence altogether
|
|
2188 foreach my $mismatch_number (sort {$a<=>$b} keys %mismatches){
|
|
2189 #dev print "Number of mismatches: $mismatch_number\t$identifier\t$sequence_1\t$sequence_2\n";
|
|
2190 foreach my $entry (keys (%{$mismatches{$mismatch_number}}) ){
|
|
2191 #dev print "$mismatch_number\t$entry\t$mismatches{$mismatch_number}->{$entry}->{index}\n";
|
|
2192 # print join("\t",$mismatch_number,$mismatches{$mismatch_number}->{$entry}->{seq_id},$sequence,$mismatches{$mismatch_number}->{$entry}->{bowtie_sequence},$mismatches{$mismatch_number}->{$entry}->{chromosome},$mismatches{$mismatch_number}->{$entry}->{position},$mismatches{$mismatch_number}->{$entry}->{index}),"\n";
|
|
2193 }
|
|
2194 if (scalar keys %{$mismatches{$mismatch_number}} == 1){
|
|
2195 # print "Unique best alignment for sequence pair $sequence_1\t$sequence_1\n";
|
|
2196 for my $unique_best_alignment (keys %{$mismatches{$mismatch_number}}){
|
|
2197 $methylation_call_params->{$identifier}->{seq_id} = $identifier;
|
|
2198 $methylation_call_params->{$identifier}->{bowtie_sequence_1} = $mismatches{$mismatch_number}->{$unique_best_alignment}->{bowtie_sequence_1};
|
|
2199 $methylation_call_params->{$identifier}->{bowtie_sequence_2} = $mismatches{$mismatch_number}->{$unique_best_alignment}->{bowtie_sequence_2};
|
|
2200 $methylation_call_params->{$identifier}->{chromosome} = $mismatches{$mismatch_number}->{$unique_best_alignment}->{chromosome};
|
|
2201 $methylation_call_params->{$identifier}->{start_seq_1} = $mismatches{$mismatch_number}->{$unique_best_alignment}->{start_seq_1};
|
|
2202 $methylation_call_params->{$identifier}->{start_seq_2} = $mismatches{$mismatch_number}->{$unique_best_alignment}->{start_seq_2};
|
|
2203 $methylation_call_params->{$identifier}->{alignment_end} = ($mismatches{$mismatch_number}->{$unique_best_alignment}->{start_seq_2}+length($mismatches{$mismatch_number}->{$unique_best_alignment}->{bowtie_sequence_2}));
|
|
2204 $methylation_call_params->{$identifier}->{index} = $mismatches{$mismatch_number}->{$unique_best_alignment}->{index};
|
|
2205 $methylation_call_params->{$identifier}->{number_of_mismatches_1} = $mismatches{$mismatch_number}->{$unique_best_alignment}->{number_of_mismatches_1};
|
|
2206 $methylation_call_params->{$identifier}->{number_of_mismatches_2} = $mismatches{$mismatch_number}->{$unique_best_alignment}->{number_of_mismatches_2};
|
|
2207 }
|
|
2208 }
|
|
2209 else{
|
|
2210 $sequence_pair_fails = 1;
|
|
2211 }
|
|
2212 ### after processing the alignment with the lowest number of mismatches we exit
|
|
2213 last;
|
|
2214 }
|
|
2215 ### skipping the sequence completely if there were multiple alignments with the same amount of lowest mismatches found at different positions
|
|
2216 if ($sequence_pair_fails == 1){
|
|
2217 $counting{unsuitable_sequence_count}++;
|
|
2218 if ($ambiguous){
|
|
2219 return 2; # => exits to next sequence pair, and prints both seqs out to multiple_alignments_1 and -2 if --ambiguous has been specified
|
|
2220 }
|
|
2221 if ($unmapped){
|
|
2222 return 1; # => exits to next sequence pair, and prints both seqs out to unmapped_1 and _2 if --un has been specified
|
|
2223 }
|
|
2224 else{
|
|
2225 return 0; # => exits to next sequence (default)
|
|
2226 }
|
|
2227 }
|
|
2228
|
|
2229 ### --DIRECTIONAL
|
|
2230 ### If the option --directional has been specified the user wants to consider only alignments to the original top strand or the original bottom strand. We will therefore
|
|
2231 ### discard all alignments to strands complementary to the original strands, as they should not exist in reality due to the library preparation protocol
|
|
2232 if ($directional){
|
|
2233 if ( ($methylation_call_params->{$identifier}->{index} == 1) or ($methylation_call_params->{$identifier}->{index} == 2) ){
|
|
2234 # warn "Alignment rejected! (index was: $methylation_call_params->{$identifier}->{index})\n";
|
|
2235 $counting{alignments_rejected_count}++;
|
|
2236 return 0;
|
|
2237 }
|
|
2238 }
|
|
2239
|
|
2240 ### If the sequence has not been rejected so far it does have a unique best alignment
|
|
2241 $counting{unique_best_alignment_count}++;
|
|
2242 extract_corresponding_genomic_sequence_paired_ends($identifier,$methylation_call_params);
|
|
2243
|
|
2244 ### check test to see if the genomic sequences we extracted has the same length as the observed sequences +2, and only then we perform the methylation call
|
|
2245 if (length($methylation_call_params->{$identifier}->{unmodified_genomic_sequence_1}) != length($sequence_1)+2){
|
|
2246 warn "Chromosomal sequence could not be extracted for\t$identifier\t$methylation_call_params->{$identifier}->{chromosome}\t$methylation_call_params->{$identifier}->{start_seq_1}\n";
|
|
2247 $counting{genomic_sequence_could_not_be_extracted_count}++;
|
|
2248 return 0;
|
|
2249 }
|
|
2250 if (length($methylation_call_params->{$identifier}->{unmodified_genomic_sequence_2}) != length($sequence_2)+2){
|
|
2251 warn "Chromosomal sequence could not be extracted for\t$identifier\t$methylation_call_params->{$identifier}->{chromosome}\t$methylation_call_params->{$identifier}->{start_seq_2}\n";
|
|
2252 $counting{genomic_sequence_could_not_be_extracted_count}++;
|
|
2253 return 0;
|
|
2254 }
|
|
2255
|
|
2256 ### otherwise we are set to perform the actual methylation call
|
|
2257 $methylation_call_params->{$identifier}->{methylation_call_1} = methylation_call($identifier,$sequence_1,$methylation_call_params->{$identifier}->{unmodified_genomic_sequence_1},$methylation_call_params->{$identifier}->{read_conversion_1});
|
|
2258 $methylation_call_params->{$identifier}->{methylation_call_2} = methylation_call($identifier,$sequence_2,$methylation_call_params->{$identifier}->{unmodified_genomic_sequence_2},$methylation_call_params->{$identifier}->{read_conversion_2});
|
|
2259
|
|
2260 print_bisulfite_mapping_results_paired_ends($identifier,$sequence_1,$sequence_2,$methylation_call_params,$quality_value_1,$quality_value_2);
|
|
2261 return 0; ## otherwise 1 will be returned by default, which would print the sequence pair to unmapped_1 and _2
|
|
2262 }
|
|
2263
|
|
2264 #########################
|
|
2265 ### BOWTIE 2 | PAIRED-END
|
|
2266 #########################
|
|
2267
|
|
2268 sub check_bowtie_results_paired_ends_bowtie2{
|
|
2269 my ($sequence_1,$sequence_2,$identifier,$quality_value_1,$quality_value_2) = @_;
|
|
2270
|
|
2271 ### quality values are not given for FastA files, so they are initialised with a Phred quality of 40
|
|
2272 unless ($quality_value_1){
|
|
2273 $quality_value_1 = 'I'x(length$sequence_1);
|
|
2274 }
|
|
2275
|
|
2276 unless ($quality_value_2){
|
|
2277 $quality_value_2 = 'I'x(length$sequence_2);
|
|
2278 }
|
|
2279
|
|
2280
|
|
2281 # print "$identifier\n$fhs[0]->{last_seq_id}\n$fhs[1]->{last_seq_id}\n$fhs[2]->{last_seq_id}\n$fhs[3]->{last_seq_id}\n\n";
|
|
2282
|
|
2283
|
|
2284 my %alignments;
|
|
2285 my $alignment_ambiguous = 0;
|
|
2286
|
|
2287 ### reading from the Bowtie 2 output filehandles
|
|
2288
|
|
2289 ### for paired end reads we are reporting alignments to the OT strand first (index 0), then the OB strand (index 3!!), similiar to the single end way.
|
|
2290 ### alignments to the complementary strands are reported afterwards (CTOT got index 1, and CTOB got index 2).
|
|
2291 ### This is needed so that alignments which either contain no single C or G or reads which contain only protected Cs are reported to the original strands (OT and OB)
|
|
2292 ### Before the complementary strands. Remember that it does not make any difference for the methylation calls, but it will matter if alignments to the complementary
|
|
2293 ### strands are not being reported when '--directional' is specified
|
|
2294
|
|
2295 foreach my $index (0,3,1,2){
|
|
2296 ### skipping this index if the last alignment has been set to undefined already (i.e. end of bowtie output)
|
|
2297 next unless ($fhs[$index]->{last_line_1} and $fhs[$index]->{last_line_2} and defined $fhs[$index]->{last_seq_id});
|
|
2298
|
|
2299 ### if the sequence pair we are currently looking at produced an alignment we are doing various things with it
|
|
2300 if ($fhs[$index]->{last_seq_id} eq $identifier) {
|
|
2301
|
|
2302 my ($id_1,$flag_1,$mapped_chromosome_1,$position_1,$mapping_quality_1,$cigar_1,$bowtie_sequence_1,$qual_1) = (split (/\t/,$fhs[$index]->{last_line_1}))[0,1,2,3,4,5,9,10];
|
|
2303 my ($id_2,$flag_2,$mapped_chromosome_2,$position_2,$mapping_quality_2,$cigar_2,$bowtie_sequence_2,$qual_2) = (split (/\t/,$fhs[$index]->{last_line_2}))[0,1,2,3,4,5,9,10];
|
|
2304 # print "Index: $index\t$fhs[$index]->{last_line_1}\n";
|
|
2305 # print "Index: $index\t$fhs[$index]->{last_line_2}\n";
|
|
2306 # print join ("\t",$id_1,$flag_1,$mapped_chromosome_1,$position_1,$mapping_quality_1,$cigar_1,$bowtie_sequence_1,$qual_1),"\n";
|
|
2307 # print join ("\t",$id_2,$flag_2,$mapped_chromosome_2,$position_2,$mapping_quality_2,$cigar_2,$bowtie_sequence_2,$qual_2),"\n";
|
|
2308 $id_1 =~ s/\/1$//;
|
|
2309 $id_2 =~ s/\/2$//;
|
|
2310
|
|
2311 # SAM format specifications for Bowtie 2
|
|
2312 # (1) Name of read that aligned
|
|
2313 # (2) Sum of all applicable flags. Flags relevant to Bowtie are:
|
|
2314 # 1 The read is one of a pair
|
|
2315 # 2 The alignment is one end of a proper paired-end alignment
|
|
2316 # 4 The read has no reported alignments
|
|
2317 # 8 The read is one of a pair and has no reported alignments
|
|
2318 # 16 The alignment is to the reverse reference strand
|
|
2319 # 32 The other mate in the paired-end alignment is aligned to the reverse reference strand
|
|
2320 # 64 The read is mate 1 in a pair
|
|
2321 # 128 The read is mate 2 in a pair
|
|
2322 # 256 The read has multiple mapping states
|
|
2323 # (3) Name of reference sequence where alignment occurs (unmapped reads have a *)
|
|
2324 # (4) 1-based offset into the forward reference strand where leftmost character of the alignment occurs (0 for unmapped reads)
|
|
2325 # (5) Mapping quality (255 means MAPQ is not available)
|
|
2326 # (6) CIGAR string representation of alignment (* if unavailable)
|
|
2327 # (7) Name of reference sequence where mate's alignment occurs. Set to = if the mate's reference sequence is the same as this alignment's, or * if there is no mate.
|
|
2328 # (8) 1-based offset into the forward reference strand where leftmost character of the mate's alignment occurs. Offset is 0 if there is no mate.
|
|
2329 # (9) Inferred fragment size. Size is negative if the mate's alignment occurs upstream of this alignment. Size is 0 if there is no mate.
|
|
2330 # (10) Read sequence (reverse-complemented if aligned to the reverse strand)
|
|
2331 # (11) ASCII-encoded read qualities (reverse-complemented if the read aligned to the reverse strand). The encoded quality values are on the Phred quality scale and the encoding is ASCII-offset by 33 (ASCII char !), similarly to a FASTQ file.
|
|
2332 # (12) Optional fields. Fields are tab-separated. bowtie2 outputs zero or more of these optional fields for each alignment, depending on the type of the alignment:
|
|
2333 # AS:i:<N> Alignment score. Can be negative. Can be greater than 0 in --local mode (but not in --end-to-end mode). Only present if SAM record is for an aligned read.
|
|
2334 # XS:i:<N> Alignment score for second-best alignment. Can be negative. Can be greater than 0 in --local mode (but not in --end-to-end mode). Only present if the SAM record is for an aligned read and more than one alignment was found for the read.
|
|
2335 # YS:i:<N> Alignment score for opposite mate in the paired-end alignment. Only present if the SAM record is for a read that aligned as part of a paired-end alignment.
|
|
2336 # XN:i:<N> The number of ambiguous bases in the reference covering this alignment. Only present if SAM record is for an aligned read.
|
|
2337 # XM:i:<N> The number of mismatches in the alignment. Only present if SAM record is for an aligned read.
|
|
2338 # XO:i:<N> The number of gap opens, for both read and reference gaps, in the alignment. Only present if SAM record is for an aligned read.
|
|
2339 # XG:i:<N> The number of gap extensions, for both read and reference gaps, in the alignment. Only present if SAM record is for an aligned read.
|
|
2340 # NM:i:<N> The edit distance; that is, the minimal number of one-nucleotide edits (substitutions, insertions and deletions) needed to transform the read string into the reference string. Only present if SAM record is for an aligned read.
|
|
2341 # YF:Z:<N> String indicating reason why the read was filtered out. See also: Filtering. Only appears for reads that were filtered out.
|
|
2342 # MD:Z:<S> A string representation of the mismatched reference bases in the alignment. See SAM format specification for details. Only present if SAM record is for an aligned read.
|
|
2343
|
|
2344 ### If a sequence has no reported alignments there will be a single output line per sequence with a bit-wise flag value of 77 for read 1 (1+4+8+64), or 141 for read 2 (1+4+8+128).
|
|
2345 ### We can store the next alignment and move on to the next Bowtie 2 instance
|
|
2346 if ($flag_1 == 77 and $flag_2 == 141){
|
|
2347 ## reading in the next alignment, which must be the next sequence
|
|
2348 my $newline_1 = $fhs[$index]->{fh}-> getline();
|
|
2349 my $newline_2 = $fhs[$index]->{fh}-> getline();
|
|
2350
|
|
2351 if ($newline_1 and $newline_2){
|
|
2352 chomp $newline_1;
|
|
2353 chomp $newline_2;
|
|
2354 my ($seq_id_1) = split (/\t/,$newline_1);
|
|
2355 my ($seq_id_2) = split (/\t/,$newline_2);
|
|
2356 $seq_id_1 =~ s/\/1$//;
|
|
2357 $seq_id_2 =~ s/\/2$//;
|
|
2358 $fhs[$index]->{last_seq_id} = $seq_id_1;
|
|
2359 $fhs[$index]->{last_line_1} = $newline_1;
|
|
2360 $fhs[$index]->{last_line_2} = $newline_2;
|
|
2361
|
|
2362 # print "current sequence ($identifier) did not map, reading in next sequence\n";
|
|
2363 # print "$index\t$fhs[$index]->{last_seq_id}\n";
|
|
2364 # print "$index\t$fhs[$index]->{last_line_1}\n";
|
|
2365 # print "$index\t$fhs[$index]->{last_line_2}\n";
|
|
2366 next; # next instance
|
|
2367 }
|
|
2368 else{
|
|
2369 # assigning undef to last_seq_id and last_line and jumping to the next index (end of Bowtie 2 output)
|
|
2370 $fhs[$index]->{last_seq_id} = undef;
|
|
2371 $fhs[$index]->{last_line_1} = undef;
|
|
2372 $fhs[$index]->{last_line_2} = undef;
|
|
2373 next;
|
|
2374 }
|
|
2375 }
|
|
2376
|
|
2377 ### If there are one or more proper alignments we can extract the chromosome number
|
|
2378 my ($chromosome_1,$chromosome_2);
|
|
2379 if ($mapped_chromosome_1 =~ s/_(CT|GA)_converted$//){
|
|
2380 $chromosome_1 = $mapped_chromosome_1;
|
|
2381 }
|
|
2382 else{
|
|
2383 die "Chromosome number extraction failed for $mapped_chromosome_1\n";
|
|
2384 }
|
|
2385 if ($mapped_chromosome_2 =~ s/_(CT|GA)_converted$//){
|
|
2386 $chromosome_2 = $mapped_chromosome_2;
|
|
2387 }
|
|
2388 else{
|
|
2389 die "Chromosome number extraction failed for $mapped_chromosome_2\n";
|
|
2390 }
|
|
2391
|
|
2392 die "Paired-end alignments need to be on the same chromosome\n" unless ($chromosome_1 eq $chromosome_2);
|
|
2393
|
|
2394 ### We will use the optional fields to determine the best alignments. Later on we extract the number of mismatches and/or indels from the CIGAR string
|
|
2395 my ($alignment_score_1,$alignment_score_2,$second_best_1,$second_best_2,$MD_tag_1,$MD_tag_2);
|
|
2396
|
|
2397 my @fields_1 = split (/\t/,$fhs[$index]->{last_line_1});
|
|
2398 my @fields_2 = split (/\t/,$fhs[$index]->{last_line_2});
|
|
2399
|
|
2400 foreach (11..$#fields_1){
|
|
2401 if ($fields_1[$_] =~ /AS:i:(.*)/){
|
|
2402 $alignment_score_1 = $1;
|
|
2403 }
|
|
2404 elsif ($fields_1[$_] =~ /XS:i:(.*)/){
|
|
2405 $second_best_1 = $1;
|
|
2406 }
|
|
2407 elsif ($fields_1[$_] =~ /MD:Z:(.*)/){
|
|
2408 $MD_tag_1 = $1;
|
|
2409 }
|
|
2410 }
|
|
2411
|
|
2412 foreach (11..$#fields_2){
|
|
2413 if ($fields_2[$_] =~ /AS:i:(.*)/){
|
|
2414 $alignment_score_2 = $1;
|
|
2415 }
|
|
2416 elsif ($fields_2[$_] =~ /XS:i:(.*)/){
|
|
2417 $second_best_2 = $1;
|
|
2418 }
|
|
2419 elsif ($fields_2[$_] =~ /MD:Z:(.*)/){
|
|
2420 $MD_tag_2 = $1;
|
|
2421 }
|
|
2422 }
|
|
2423
|
|
2424 die "Failed to extract alignment score 1 ($alignment_score_1) and MD tag ($MD_tag_1)!\nlast alignment 1: $fhs[$index]->{last_line_1}\nlast alignment 2: $fhs[$index]->{last_line_2}\n" unless (defined $alignment_score_1 and defined $MD_tag_1);
|
|
2425 die "Failed to extract alignment score 2 ($alignment_score_2) and MD tag ($MD_tag_2)!\nlast alignment 1: $fhs[$index]->{last_line_1}\nlast alignment 2: $fhs[$index]->{last_line_2}\n" unless (defined $alignment_score_2 and defined $MD_tag_2);
|
|
2426
|
|
2427 # warn "First read 1 alignment score is: '$alignment_score_1'\n";
|
|
2428 # warn "First read 2 alignment score is: '$alignment_score_2'\n";
|
|
2429 # warn "MD tag 1 is: '$MD_tag_1'\n";
|
|
2430 # warn "MD tag 2 is: '$MD_tag_2'\n";
|
|
2431
|
|
2432 ### To decide whether a sequence pair has a unique best alignment we will look at the highest sum of alignment scores from both alignments
|
|
2433 my $sum_of_alignment_scores_1 = $alignment_score_1 + $alignment_score_2 ;
|
|
2434 # print "sum of alignment scores: $sum_of_alignment_scores_1\n\n";
|
|
2435
|
|
2436 if (defined $second_best_1 and defined $second_best_2){
|
|
2437 my $sum_of_alignment_scores_second_best = $second_best_1 + $second_best_2;
|
|
2438 # warn "Second best alignment_score_1 is: '$second_best_1'\n";
|
|
2439 # warn "Second best alignment_score_2 is: '$second_best_2'\n";
|
|
2440 # warn "Second best alignment sum of alignment scores is: '$sum_of_alignment_scores_second_best'\n";
|
|
2441
|
|
2442 # If the first alignment score for the first read pair is the same as the alignment score of the second best hit we are going to boot this sequence pair altogether
|
|
2443 if ($sum_of_alignment_scores_1 == $sum_of_alignment_scores_second_best){
|
|
2444 $alignment_ambiguous = 1;
|
|
2445 # print "This read will be chucked (AS==XS detected)!\n";
|
|
2446
|
|
2447 ## need to read and discard all additional ambiguous reads until we reach the next sequence
|
|
2448 until ($fhs[$index]->{last_seq_id} ne $identifier){
|
|
2449 my $newline_1 = $fhs[$index]->{fh}-> getline();
|
|
2450 my $newline_2 = $fhs[$index]->{fh}-> getline();
|
|
2451 if ($newline_1 and $newline_2){
|
|
2452 chomp $newline_1;
|
|
2453 chomp $newline_2;
|
|
2454 my ($seq_id_1) = split (/\t/,$newline_1);
|
|
2455 my ($seq_id_2) = split (/\t/,$newline_2);
|
|
2456 $seq_id_1 =~ s/\/1$//;
|
|
2457 $seq_id_2 =~ s/\/2$//;
|
|
2458 # print "New Seq IDs:\t$seq_id_1\t$seq_id_2\n";
|
|
2459
|
|
2460 $fhs[$index]->{last_seq_id} = $seq_id_1;
|
|
2461 $fhs[$index]->{last_line_1} = $newline_1;
|
|
2462 $fhs[$index]->{last_line_2} = $newline_2;
|
|
2463 }
|
|
2464 else{
|
|
2465 # assigning undef to last_seq_id and last_line and jumping to the next index (end of Bowtie 2 output)
|
|
2466 $fhs[$index]->{last_seq_id} = undef;
|
|
2467 $fhs[$index]->{last_line_1} = undef;
|
|
2468 $fhs[$index]->{last_line_2} = undef;
|
|
2469 last; # break free if the end of the alignment output was reached
|
|
2470 }
|
|
2471 }
|
|
2472 # if ($fhs[$index]->{last_seq_id}){
|
|
2473 # warn "Index: $index\tThis Seq-ID is $identifier, skipped all ambiguous sequences until the next ID which is: $fhs[$index]->{last_seq_id}\n";
|
|
2474 # }
|
|
2475 }
|
|
2476 else{ # the next best alignment has a lower alignment score than the current read, so we can safely store the current alignment
|
|
2477
|
|
2478 my $alignment_location;
|
|
2479 if ($position_1 <= $position_2){
|
|
2480 $alignment_location = join(":",$chromosome_1,$position_1,$position_2);
|
|
2481 }
|
|
2482 elsif($position_2 < $position_1){
|
|
2483 $alignment_location = join(":",$chromosome_1,$position_2,$position_1);
|
|
2484 }
|
|
2485
|
|
2486 ### If a sequence aligns to exactly the same location twice the sequence does either not contain any C or G, or all the Cs (or Gs on the reverse
|
|
2487 ### strand) were methylated and therefore protected. Alternatively it will align better in one condition than in the other. In any case, it is not needed to overwrite
|
|
2488 ### the same positional entry with a second entry for the same location, as the genomic sequence extraction and methylation call would not be affected by this. The only
|
|
2489 ### thing which would change is the index number for the found alignment). We will continue to assign these alignments to the first indexes 0 and 3, i.e. OT and OB
|
|
2490
|
|
2491 unless (exists $alignments{$alignment_location}){
|
|
2492 $alignments{$alignment_location}->{seq_id} = $id_1;
|
|
2493 $alignments{$alignment_location}->{alignment_score_1} = $alignment_score_1;
|
|
2494 $alignments{$alignment_location}->{alignment_score_2} = $alignment_score_2;
|
|
2495 $alignments{$alignment_location}->{sum_of_alignment_scores} = $sum_of_alignment_scores_1;
|
|
2496 $alignments{$alignment_location}->{bowtie_sequence_1} = $bowtie_sequence_1;
|
|
2497 $alignments{$alignment_location}->{bowtie_sequence_2} = $bowtie_sequence_2;
|
|
2498 $alignments{$alignment_location}->{index} = $index;
|
|
2499 $alignments{$alignment_location}->{chromosome} = $chromosome_1; # either is fine
|
|
2500 $alignments{$alignment_location}->{position_1} = $position_1;
|
|
2501 $alignments{$alignment_location}->{position_2} = $position_2;
|
|
2502 $alignments{$alignment_location}->{mismatch_info_1} = $MD_tag_1;
|
|
2503 $alignments{$alignment_location}->{mismatch_info_2} = $MD_tag_2;
|
|
2504 $alignments{$alignment_location}->{CIGAR_1} = $cigar_1;
|
|
2505 $alignments{$alignment_location}->{CIGAR_2} = $cigar_2;
|
|
2506 $alignments{$alignment_location}->{flag_1} = $flag_1;
|
|
2507 $alignments{$alignment_location}->{flag_2} = $flag_2;
|
|
2508 }
|
|
2509 # warn "added best of several alignments to \%alignments hash\n";
|
|
2510
|
|
2511 ### now reading and discarding all (inferior) alignments of this read pair until we hit the next sequence
|
|
2512 until ($fhs[$index]->{last_seq_id} ne $identifier){
|
|
2513 my $newline_1 = $fhs[$index]->{fh}-> getline();
|
|
2514 my $newline_2 = $fhs[$index]->{fh}-> getline();
|
|
2515 if ($newline_1 and $newline_2){
|
|
2516 chomp $newline_1;
|
|
2517 chomp $newline_2;
|
|
2518 my ($seq_id_1) = split (/\t/,$newline_1);
|
|
2519 my ($seq_id_2) = split (/\t/,$newline_2);
|
|
2520 $seq_id_1 =~ s/\/1$//;
|
|
2521 $seq_id_2 =~ s/\/2$//;
|
|
2522 # print "New Seq IDs:\t$seq_id_1\t$seq_id_2\n";
|
|
2523
|
|
2524 $fhs[$index]->{last_seq_id} = $seq_id_1;
|
|
2525 $fhs[$index]->{last_line_1} = $newline_1;
|
|
2526 $fhs[$index]->{last_line_2} = $newline_2;
|
|
2527 }
|
|
2528 else{
|
|
2529 # assigning undef to last_seq_id and last_line_1 and _2 and jumping to the next index (end of Bowtie 2 output)
|
|
2530 $fhs[$index]->{last_seq_id} = undef;
|
|
2531 $fhs[$index]->{last_line_1} = undef;
|
|
2532 $fhs[$index]->{last_line_2} = undef;
|
|
2533 last; # break free if the end of the alignment output was reached
|
|
2534 }
|
|
2535 }
|
|
2536 # if($fhs[$index]->{last_seq_id}){
|
|
2537 # warn "Index: $index\tThis Seq-ID is $identifier, skipped all other alignments until the next ID was reached which is: $fhs[$index]->{last_seq_id}\n";
|
|
2538 # }
|
|
2539 }
|
|
2540 }
|
|
2541 else{ # there is no second best hit, so we can just store this one and read in the next sequence
|
|
2542
|
|
2543 my $alignment_location = join(":",$chromosome_1,$position_1,$position_2);
|
|
2544 # print "$alignment_location\n";
|
|
2545 ### If a sequence aligns to exactly the same location with a perfect match twice the sequence does either not contain any C or G, or all the Cs (or Gs on the reverse
|
|
2546 ### strand) were methylated and therefore protected. Alternatively it will align better in one condition than in the other. In any case, it is not needed to overwrite
|
|
2547 ### the same positional entry with a second entry for the same location, as the genomic sequence extraction and methylation call would not be affected by this. The only
|
|
2548 ### thing which would change is the index number for the found alignment). We will continue to assign these alignments to the first indexes 0 and 3, i.e. OT and OB
|
|
2549
|
|
2550 unless (exists $alignments{$alignment_location}){
|
|
2551 $alignments{$alignment_location}->{seq_id} = $id_1;
|
|
2552 $alignments{$alignment_location}->{alignment_score_1} = $alignment_score_1;
|
|
2553 $alignments{$alignment_location}->{alignment_score_2} = $alignment_score_2;
|
|
2554 $alignments{$alignment_location}->{sum_of_alignment_scores} = $sum_of_alignment_scores_1;
|
|
2555 $alignments{$alignment_location}->{bowtie_sequence_1} = $bowtie_sequence_1;
|
|
2556 $alignments{$alignment_location}->{bowtie_sequence_2} = $bowtie_sequence_2;
|
|
2557 $alignments{$alignment_location}->{index} = $index;
|
|
2558 $alignments{$alignment_location}->{chromosome} = $chromosome_1; # either is fine
|
|
2559 $alignments{$alignment_location}->{position_1} = $position_1;
|
|
2560 $alignments{$alignment_location}->{position_2} = $position_2;
|
|
2561 $alignments{$alignment_location}->{mismatch_info_1} = $MD_tag_1;
|
|
2562 $alignments{$alignment_location}->{mismatch_info_2} = $MD_tag_2;
|
|
2563 $alignments{$alignment_location}->{CIGAR_1} = $cigar_1;
|
|
2564 $alignments{$alignment_location}->{CIGAR_2} = $cigar_2;
|
|
2565 $alignments{$alignment_location}->{flag_1} = $flag_1;
|
|
2566 $alignments{$alignment_location}->{flag_2} = $flag_2;
|
|
2567 }
|
|
2568
|
|
2569 # warn "added unique alignment to \%alignments hash\n";
|
|
2570
|
|
2571 # Now reading and storing the next read pair
|
|
2572 my $newline_1 = $fhs[$index]->{fh}-> getline();
|
|
2573 my $newline_2 = $fhs[$index]->{fh}-> getline();
|
|
2574 if ($newline_1 and $newline_2){
|
|
2575 chomp $newline_1;
|
|
2576 chomp $newline_2;
|
|
2577 # print "$newline_1\n";
|
|
2578 # print "$newline_2\n";
|
|
2579 my ($seq_id_1) = split (/\t/,$newline_1);
|
|
2580 my ($seq_id_2) = split (/\t/,$newline_2);
|
|
2581 $seq_id_1 =~ s/\/1$//;
|
|
2582 $seq_id_2 =~ s/\/2$//;
|
|
2583 # print "New Seq IDs:\t$seq_id_1\t$seq_id_2\n";
|
|
2584
|
|
2585 $fhs[$index]->{last_seq_id} = $seq_id_1;
|
|
2586 $fhs[$index]->{last_line_1} = $newline_1;
|
|
2587 $fhs[$index]->{last_line_2} = $newline_2;
|
|
2588
|
|
2589 if ($seq_id_1 eq $identifier){
|
|
2590 die "Sequence with ID $identifier did not have a second best alignment, but next seq-ID was also $fhs[$index]->{last_seq_id}!\n";
|
|
2591 }
|
|
2592 }
|
|
2593 else{
|
|
2594 # assigning undef to last_seq_id and last_line_1 and _2 and jumping to the next index (end of Bowtie 2 output)
|
|
2595 $fhs[$index]->{last_seq_id} = undef;
|
|
2596 $fhs[$index]->{last_line_1} = undef;
|
|
2597 $fhs[$index]->{last_line_2} = undef;
|
|
2598 }
|
|
2599 }
|
|
2600 }
|
|
2601 }
|
|
2602
|
|
2603 ### if the read produced several ambiguous alignments for a single instance of Bowtie 2 we can return already now. If --ambiguous was specified the read sequence will be printed out in FastQ format
|
|
2604 if ($alignment_ambiguous == 1){
|
|
2605 $counting{unsuitable_sequence_count}++;
|
|
2606 ### report that the sequence pair has multiple hits with bitwise flag 256. We can print the sequence to the result file straight away and skip everything else
|
|
2607 # my $ambiguous_read_1 = join("\t",$identifier.'/1','256','*','0','0','*','*','0','0',$sequence_1,$quality_value_1);
|
|
2608 # my $ambiguous_read_2 = join("\t",$identifier.'/2','256','*','0','0','*','*','0','0',$sequence_2,$quality_value_2);
|
|
2609 # print "$ambiguous_read_1\n";
|
|
2610 # print "$ambiguous_read_2\n";
|
|
2611
|
|
2612 if ($ambiguous){
|
|
2613 return 2; # => exits to next sequence pair, and prints it out to _ambiguous_reads_1.txt and _ambiguous_reads_2.txt if '--ambiguous' was specified
|
|
2614 }
|
|
2615 elsif ($unmapped){
|
|
2616 return 1; # => exits to next sequence pair, and prints it out to _unmapped_reads_1.txt and _unmapped_reads_2.txt if '--unmapped' but not '--ambiguous' was specified
|
|
2617 }
|
|
2618 else{
|
|
2619 return 0;
|
|
2620 }
|
|
2621 }
|
|
2622
|
|
2623 ### if no alignment was found for a certain sequence at all we continue with the next sequence in the sequence file
|
|
2624 unless (%alignments){
|
|
2625 $counting{no_single_alignment_found}++;
|
|
2626
|
|
2627 # my $unmapped_read_1 = join("\t",$identifier.'/1','77','*','0','0','*','*','0','0',$sequence_1,$quality_value_1);
|
|
2628 # my $unmapped_read_2 = join("\t",$identifier.'/2','141','*','0','0','*','*','0','0',$sequence_2,$quality_value_2);
|
|
2629 # print "$unmapped_read_1\n";
|
|
2630 # print "$unmapped_read_2\n";
|
|
2631 if ($unmapped){
|
|
2632 return 1; # => exits to next sequence pair, and prints it out to _unmapped_reads_1.txt and _unmapped_read_2.txt if '--unmapped' was specified
|
|
2633 }
|
|
2634 else{
|
|
2635 return 0;
|
|
2636 }
|
|
2637 }
|
|
2638
|
|
2639 #######################################################################################################################################################
|
|
2640
|
|
2641 ### If the sequence pair was not rejected so far we are now looking if there is a unique best alignment among all alignment instances. If there is only one
|
|
2642 ### single best position we are going to store the alignment information in the $meth_call variable. If there are multiple hits with the same (highest)
|
|
2643 ### alignment score we are discarding the sequence pair altogether.
|
|
2644 ### For end-to-end alignments the maximum alignment score is 0, each mismatch receives a penalty of 6, and each gap receives penalties for opening (5)
|
|
2645 ### and extending (3 per bp) the gap.
|
|
2646
|
|
2647 #######################################################################################################################################################
|
|
2648
|
|
2649 ### Declaring an empty hash reference which will store all information we need for the methylation call
|
|
2650 my $methylation_call_params; # hash reference
|
|
2651 my $sequence_pair_fails = 0; # using $sequence_pair_fails as a 'memory' if a sequence could not be aligned uniquely (set to 1 then)
|
|
2652
|
|
2653 ### print contents of %alignments for debugging
|
|
2654 ## if (scalar keys %alignments >= 1){
|
|
2655 # print "\n******\n";
|
|
2656 # foreach my $alignment_location (sort {$a cmp $b} keys %alignments){
|
|
2657 # print "Loc: $alignment_location\n";
|
|
2658 # print "ID: $alignments{$alignment_location}->{seq_id}\n";
|
|
2659 # print "AS_1: $alignments{$alignment_location}->{alignment_score_1}\n";
|
|
2660 # print "AS_2: $alignments{$alignment_location}->{alignment_score_2}\n";
|
|
2661 # print "Seq_1: $alignments{$alignment_location}->{bowtie_sequence_1}\n";
|
|
2662 # print "Seq_2: $alignments{$alignment_location}->{bowtie_sequence_2}\n";
|
|
2663 # print "Index $alignments{$alignment_location}->{index}\n";
|
|
2664 # print "Chr: $alignments{$alignment_location}->{chromosome}\n";
|
|
2665 # print "Pos_1: $alignments{$alignment_location}->{position_1}\n";
|
|
2666 # print "Pos_2: $alignments{$alignment_location}->{position_2}\n";
|
|
2667 # print "CIGAR_1: $alignments{$alignment_location}->{CIGAR_1}\n";
|
|
2668 # print "CIGAR_2: $alignments{$alignment_location}->{CIGAR_2}\n";
|
|
2669 # print "MD_1: $alignments{$alignment_location}->{mismatch_info_1}\n";
|
|
2670 # print "MD_2: $alignments{$alignment_location}->{mismatch_info_2}\n";
|
|
2671 # print "Flag 1: $alignments{$alignment_location}->{flag_1}\n";
|
|
2672 # print "Flag 2: $alignments{$alignment_location}->{flag_2}\n";
|
|
2673 # }
|
|
2674 # print "\n******\n";
|
|
2675 # }
|
|
2676
|
|
2677 ### if there is only 1 entry in the %alignments hash we accept it as the best alignment
|
|
2678 if (scalar keys %alignments == 1){
|
|
2679 for my $unique_best_alignment (keys %alignments){
|
|
2680 $methylation_call_params->{$identifier}->{bowtie_sequence_1} = $alignments{$unique_best_alignment}->{bowtie_sequence_1};
|
|
2681 $methylation_call_params->{$identifier}->{bowtie_sequence_2} = $alignments{$unique_best_alignment}->{bowtie_sequence_2};
|
|
2682 $methylation_call_params->{$identifier}->{chromosome} = $alignments{$unique_best_alignment}->{chromosome};
|
|
2683 $methylation_call_params->{$identifier}->{position_1} = $alignments{$unique_best_alignment}->{position_1};
|
|
2684 $methylation_call_params->{$identifier}->{position_2} = $alignments{$unique_best_alignment}->{position_2};
|
|
2685 $methylation_call_params->{$identifier}->{index} = $alignments{$unique_best_alignment}->{index};
|
|
2686 $methylation_call_params->{$identifier}->{alignment_score_1} = $alignments{$unique_best_alignment}->{alignment_score_1};
|
|
2687 $methylation_call_params->{$identifier}->{alignment_score_2} = $alignments{$unique_best_alignment}->{alignment_score_2};
|
|
2688 $methylation_call_params->{$identifier}->{sum_of_alignment_scores} = $alignments{$unique_best_alignment}->{sum_of_alignment_scores};
|
|
2689 $methylation_call_params->{$identifier}->{mismatch_info_1} = $alignments{$unique_best_alignment}->{mismatch_info_1};
|
|
2690 $methylation_call_params->{$identifier}->{mismatch_info_2} = $alignments{$unique_best_alignment}->{mismatch_info_2};
|
|
2691 $methylation_call_params->{$identifier}->{CIGAR_1} = $alignments{$unique_best_alignment}->{CIGAR_1};
|
|
2692 $methylation_call_params->{$identifier}->{CIGAR_2} = $alignments{$unique_best_alignment}->{CIGAR_2};
|
|
2693 $methylation_call_params->{$identifier}->{flag_1} = $alignments{$unique_best_alignment}->{flag_1};
|
|
2694 $methylation_call_params->{$identifier}->{flag_2} = $alignments{$unique_best_alignment}->{flag_2};
|
|
2695 }
|
|
2696 }
|
|
2697
|
|
2698 ### otherwise we are going to find out if there is a best match among the multiple alignments, or whether there are 2 or more equally good alignments (in which case
|
|
2699 ### we boot the sequence pair altogether)
|
|
2700 elsif (scalar keys %alignments >= 2 and scalar keys %alignments <= 4){
|
|
2701 my $best_sum_of_alignment_scores;
|
|
2702 my $best_alignment_location;
|
|
2703 foreach my $alignment_location (sort {$alignments{$b}->{sum_of_alignment_scores} <=> $alignments{$a}->{sum_of_alignment_scores}} keys %alignments){
|
|
2704 # print "$alignments{$alignment_location}->{sum_of_alignment_scores}\n";
|
|
2705 unless (defined $best_sum_of_alignment_scores){
|
|
2706 $best_sum_of_alignment_scores = $alignments{$alignment_location}->{sum_of_alignment_scores};
|
|
2707 $best_alignment_location = $alignment_location;
|
|
2708 # print "setting best alignment score to: $best_sum_of_alignment_scores\n";
|
|
2709 }
|
|
2710 else{
|
|
2711 ### if the second best alignment has the same sum of alignment scores as the first one, the sequence pair will get booted
|
|
2712 if ($alignments{$alignment_location}->{sum_of_alignment_scores} == $best_sum_of_alignment_scores){
|
|
2713 # warn "Same sum of alignment scores for 2 different alignments, the sequence pair will get booted!\n";
|
|
2714 $sequence_pair_fails = 1;
|
|
2715 last; # exiting since we know that the sequence has ambiguous alignments
|
|
2716 }
|
|
2717 ### else we are going to store the best alignment for further processing
|
|
2718 else{
|
|
2719 $methylation_call_params->{$identifier}->{bowtie_sequence_1} = $alignments{$best_alignment_location}->{bowtie_sequence_1};
|
|
2720 $methylation_call_params->{$identifier}->{bowtie_sequence_2} = $alignments{$best_alignment_location}->{bowtie_sequence_2};
|
|
2721 $methylation_call_params->{$identifier}->{chromosome} = $alignments{$best_alignment_location}->{chromosome};
|
|
2722 $methylation_call_params->{$identifier}->{position_1} = $alignments{$best_alignment_location}->{position_1};
|
|
2723 $methylation_call_params->{$identifier}->{position_2} = $alignments{$best_alignment_location}->{position_2};
|
|
2724 $methylation_call_params->{$identifier}->{index} = $alignments{$best_alignment_location}->{index};
|
|
2725 $methylation_call_params->{$identifier}->{alignment_score_1} = $alignments{$best_alignment_location}->{alignment_score_1};
|
|
2726 $methylation_call_params->{$identifier}->{alignment_score_2} = $alignments{$best_alignment_location}->{alignment_score_2};
|
|
2727 $methylation_call_params->{$identifier}->{sum_of_alignment_scores} = $alignments{$best_alignment_location}->{sum_of_alignment_scores};
|
|
2728 $methylation_call_params->{$identifier}->{mismatch_info_1} = $alignments{$best_alignment_location}->{mismatch_info_1};
|
|
2729 $methylation_call_params->{$identifier}->{mismatch_info_2} = $alignments{$best_alignment_location}->{mismatch_info_2};
|
|
2730 $methylation_call_params->{$identifier}->{CIGAR_1} = $alignments{$best_alignment_location}->{CIGAR_1};
|
|
2731 $methylation_call_params->{$identifier}->{CIGAR_2} = $alignments{$best_alignment_location}->{CIGAR_2};
|
|
2732 $methylation_call_params->{$identifier}->{flag_1} = $alignments{$best_alignment_location}->{flag_1};
|
|
2733 $methylation_call_params->{$identifier}->{flag_2} = $alignments{$best_alignment_location}->{flag_2};
|
|
2734 last; # exiting since the sequence produced a unique best alignment
|
|
2735 }
|
|
2736 }
|
|
2737 }
|
|
2738 }
|
|
2739 else{
|
|
2740 die "There are too many potential hits for this sequence pair (1-4 expected, but found: '",scalar keys %alignments,"')\n";;
|
|
2741 }
|
|
2742
|
|
2743 ### skipping the sequence completely if there were multiple alignments with the same best sum of alignment scores at different positions
|
|
2744 if ($sequence_pair_fails == 1){
|
|
2745 $counting{unsuitable_sequence_count}++;
|
|
2746
|
|
2747 ### report that the sequence has multiple hits with bitwise flag 256. We can print the sequence to the result file straight away and skip everything else
|
|
2748 # my $ambiguous_read_1 = join("\t",$identifier.'/1','256','*','0','0','*','*','0','0',$sequence_1,$quality_value_1);
|
|
2749 # my $ambiguous_read_2 = join("\t",$identifier.'/2','256','*','0','0','*','*','0','0',$sequence_2,$quality_value_2);
|
|
2750 # print "$ambiguous_read_1\n";
|
|
2751 # print "$ambiguous_read_2\n";
|
|
2752
|
|
2753 if ($ambiguous){
|
|
2754 return 2; # => exits to next sequence pair, and prints it out (in FastQ format) to _ambiguous_reads_1.txt and _ambiguous_reads_2.txt if '--ambiguous' was specified
|
|
2755 }
|
|
2756 elsif ($unmapped){
|
|
2757 return 1; # => exits to next sequence pair, and prints it out (in FastQ format) to _unmapped_reads_1.txt and _unmapped_reads_2.txt if '--unmapped' but not '--ambiguous' was specified
|
|
2758 }
|
|
2759 else{
|
|
2760 return 0; # => exits to next sequence pair (default)
|
|
2761 }
|
|
2762 }
|
|
2763
|
|
2764 ### --DIRECTIONAL
|
|
2765 ### If the option --directional has been specified the user wants to consider only alignments to the original top strand or the original bottom strand. We will therefore
|
|
2766 ### discard all alignments to strands complementary to the original strands, as they should not exist in reality due to the library preparation protocol
|
|
2767 if ($directional){
|
|
2768 if ( ($methylation_call_params->{$identifier}->{index} == 1) or ($methylation_call_params->{$identifier}->{index} == 2) ){
|
|
2769 # warn "Alignment rejected! (index was: $methylation_call_params->{$identifier}->{index})\n";
|
|
2770 $counting{alignments_rejected_count}++;
|
|
2771 return 0;
|
|
2772 }
|
|
2773 }
|
|
2774
|
|
2775 ### If the sequence pair has not been rejected so far it does have a unique best alignment
|
|
2776 $counting{unique_best_alignment_count}++;
|
|
2777 extract_corresponding_genomic_sequence_paired_ends_bowtie2($identifier,$methylation_call_params);
|
|
2778
|
|
2779 ### check to see if the genomic sequences we extracted has the same length as the observed sequences +2, and only then we perform the methylation call
|
|
2780 if (length($methylation_call_params->{$identifier}->{unmodified_genomic_sequence_1}) != length($sequence_1)+2){
|
|
2781 warn "Chromosomal sequence could not be extracted for\t$identifier\t$methylation_call_params->{$identifier}->{chromosome}\t$methylation_call_params->{$identifier}->{start_seq_1}\n";
|
|
2782 $counting{genomic_sequence_could_not_be_extracted_count}++;
|
|
2783 return 0;
|
|
2784 }
|
|
2785 if (length($methylation_call_params->{$identifier}->{unmodified_genomic_sequence_2}) != length($sequence_2)+2){
|
|
2786 warn "Chromosomal sequence could not be extracted for\t$identifier\t$methylation_call_params->{$identifier}->{chromosome}\t$methylation_call_params->{$identifier}->{start_seq_2}\n";
|
|
2787 $counting{genomic_sequence_could_not_be_extracted_count}++;
|
|
2788 return 0;
|
|
2789 }
|
|
2790
|
|
2791 ### now we are set to perform the actual methylation call
|
|
2792 $methylation_call_params->{$identifier}->{methylation_call_1} = methylation_call($identifier,$sequence_1,$methylation_call_params->{$identifier}->{unmodified_genomic_sequence_1},$methylation_call_params->{$identifier}->{read_conversion_1});
|
|
2793 $methylation_call_params->{$identifier}->{methylation_call_2} = methylation_call($identifier,$sequence_2,$methylation_call_params->{$identifier}->{unmodified_genomic_sequence_2},$methylation_call_params->{$identifier}->{read_conversion_2});
|
|
2794 # print "$methylation_call_params->{$identifier}->{read_conversion_2}\n";
|
|
2795 # print " $sequence_2\n";
|
|
2796 # print "$methylation_call_params->{$identifier}->{unmodified_genomic_sequence_2}\n";
|
|
2797 # print " $methylation_call_params->{$identifier}->{methylation_call_2}\n";
|
|
2798
|
|
2799 print_bisulfite_mapping_results_paired_ends_bowtie2($identifier,$sequence_1,$sequence_2,$methylation_call_params,$quality_value_1,$quality_value_2);
|
|
2800 return 0; ## otherwise 1 will be returned by default, which would print the sequence pair to unmapped_1 and _2
|
|
2801 }
|
|
2802
|
|
2803 ###
|
|
2804
|
|
2805 sub decide_whether_paired_end_alignment_is_valid{
|
|
2806 my ($index,$identifier) = @_;
|
|
2807 my ($id_1,$strand_1,$mapped_chromosome_1,$position_1,$bowtie_sequence_1,$mismatch_info_1) = (split (/\t/,$fhs[$index]->{last_line_1},-1))[0,1,2,3,4,7];
|
|
2808 my ($id_2,$strand_2,$mapped_chromosome_2,$position_2,$bowtie_sequence_2,$mismatch_info_2) = (split (/\t/,$fhs[$index]->{last_line_2},-1))[0,1,2,3,4,7];
|
|
2809 chomp $mismatch_info_1;
|
|
2810 chomp $mismatch_info_2;
|
|
2811 my $seq_id_1 = $id_1;
|
|
2812 my $seq_id_2 = $id_2;
|
|
2813 $seq_id_1 =~ s/\/1$//; # removing the read /1
|
|
2814 $seq_id_2 =~ s/\/1$//; # removing the read /1
|
|
2815
|
|
2816 ### ensuring that the current entry is the correct sequence
|
|
2817 if ($seq_id_1 eq $identifier or $seq_id_2 eq $identifier){
|
|
2818 ### checking the orientation of the alignment. We need to discriminate between 8 different conditions, however only 4 of them are theoretically
|
|
2819 ### sensible alignments
|
|
2820 my $orientation = ensure_sensical_alignment_orientation_paired_ends ($index,$id_1,$strand_1,$id_2,$strand_2);
|
|
2821 ### If the orientation was correct can we move on
|
|
2822 if ($orientation == 1){
|
|
2823 return 1; ### 1st possibility for A SEQUENCE-PAIR TO PASS
|
|
2824 }
|
|
2825 ### If the alignment was in the wrong orientation we need to read in two new lines
|
|
2826 elsif($orientation == 0){
|
|
2827 my $newline_1 = $fhs[$index]->{fh}->getline();
|
|
2828 my $newline_2 = $fhs[$index]->{fh}->getline();
|
|
2829 if ($newline_1 and $newline_2){
|
|
2830 ### extract detailed information about the alignment again (from $newline_1 and $newline_2 this time)
|
|
2831 ($id_1,$strand_1) = (split (/\t/,$newline_1))[0,1];
|
|
2832 ($id_2,$strand_2) = (split (/\t/,$newline_2))[0,1];
|
|
2833
|
|
2834 my $seqid;
|
|
2835 $seq_id_1 = $id_1;
|
|
2836 $seq_id_2 = $id_2;
|
|
2837 # we need to capture the first read (ending on /1)
|
|
2838 if ($seq_id_1 =~ s/\/1$//){ # removing the read /1 tag
|
|
2839 $seqid = $seq_id_1;
|
|
2840 }
|
|
2841 elsif ($seq_id_2 =~ s/\/1$//){ # removing the read /1 tag
|
|
2842 $seqid = $seq_id_2;
|
|
2843 }
|
|
2844 else{
|
|
2845 die "One of the two reads needs to end on /1!!";
|
|
2846 }
|
|
2847
|
|
2848 ### ensuring that the next entry is still the correct sequence
|
|
2849 if ($seq_id_1 eq $identifier or $seq_id_2 eq $identifier){
|
|
2850 ### checking orientation again
|
|
2851 $orientation = ensure_sensical_alignment_orientation_paired_ends ($index,$id_1,$strand_1,$id_2,$strand_2);
|
|
2852 ### If the orientation was correct can we move on
|
|
2853 if ($orientation == 1){
|
|
2854 ### Writing the current sequence to last_line_1 and last_line_2
|
|
2855 $fhs[$index]->{last_seq_id} = $seqid;
|
|
2856 $fhs[$index]->{last_line_1} = $newline_1;
|
|
2857 $fhs[$index]->{last_line_2} = $newline_2;
|
|
2858 return 1; ### 2nd possibility for a SEQUENCE-PAIR TO PASS
|
|
2859 }
|
|
2860 ### If the alignment was in the wrong orientation again we need to read in yet another 2 new lines and store them in @fhs (this must be
|
|
2861 ### the next entry)
|
|
2862 elsif ($orientation == 0){
|
|
2863 $newline_1 = $fhs[$index]->{fh}->getline();
|
|
2864 $newline_2 = $fhs[$index]->{fh}->getline();
|
|
2865 if ($newline_1 and $newline_2){
|
|
2866 ($seq_id_1) = split (/\t/,$newline_1);
|
|
2867 ($seq_id_2) = split (/\t/,$newline_2);
|
|
2868
|
|
2869 $seqid = '';
|
|
2870 if ($seq_id_1 =~ s/\/1$//){ # removing the read /1 tag
|
|
2871 $seqid = $seq_id_1;
|
|
2872 }
|
|
2873 elsif ($seq_id_2 =~ s/\/1$//){ # removing the read /1 tag
|
|
2874 $seqid = $seq_id_2;
|
|
2875 }
|
|
2876 else{
|
|
2877 die "One of the two reads needs to end on /1!!";
|
|
2878 }
|
|
2879
|
|
2880 ### check if the next 2 lines still have the same seq ID (must not happen), and if not overwrite the current seq-ID and bowtie output with
|
|
2881 ### the same fields of the just read next entry
|
|
2882 die "Same seq ID 3 or more times in a row!(should be 2 max)" if ($seqid eq $identifier);
|
|
2883 $fhs[$index]->{last_seq_id} = $seqid;
|
|
2884 $fhs[$index]->{last_line_1} = $newline_1;
|
|
2885 $fhs[$index]->{last_line_2} = $newline_2;
|
|
2886 return 0; # not processing anything this round as the alignment currently stored in last_line_1 and _2 was in the wrong orientation
|
|
2887 }
|
|
2888 else {
|
|
2889 ### assigning undef to last_seq_id and last_line (end of bowtie output)
|
|
2890 $fhs[$index]->{last_seq_id} = undef;
|
|
2891 $fhs[$index]->{last_line_1} = undef;
|
|
2892 $fhs[$index]->{last_line_2} = undef;
|
|
2893 return 0; # not processing anything as the alignment currently stored in last_line_1 and _2 was in the wrong orientation
|
|
2894 }
|
|
2895 }
|
|
2896 else{
|
|
2897 die "The orientation of the alignment must be either correct or incorrect\n";
|
|
2898 }
|
|
2899 }
|
|
2900 ### the sequence pair we just read in is already the next sequence pair to be analysed -> store it in @fhs
|
|
2901 else{
|
|
2902 $fhs[$index]->{last_seq_id} = $seqid;
|
|
2903 $fhs[$index]->{last_line_1} = $newline_1;
|
|
2904 $fhs[$index]->{last_line_2} = $newline_2;
|
|
2905 return 0; # processing the new alignment result only in the next round
|
|
2906 }
|
|
2907 }
|
|
2908 else {
|
|
2909 # assigning undef to last_seq_id and both last_lines (end of bowtie output)
|
|
2910 $fhs[$index]->{last_seq_id} = undef;
|
|
2911 $fhs[$index]->{last_line_1} = undef;
|
|
2912 $fhs[$index]->{last_line_2} = undef;
|
|
2913 return 0; # not processing anything as the alignment currently stored in last_line_1 and _2 was in the wrong orientation
|
|
2914 }
|
|
2915 }
|
|
2916 else{
|
|
2917 die "The orientation of the alignment must be either correct or incorrect\n";
|
|
2918 }
|
|
2919 }
|
|
2920 ### the sequence pair stored in @fhs as last_line_1 and last_line_2 is already the next sequence pair to be analysed -> analyse next round
|
|
2921 else{
|
|
2922 return 0;
|
|
2923 }
|
|
2924 }
|
|
2925
|
|
2926 ### EXTRACT GENOMIC SEQUENCE | BOWTIE 1 | PAIRED-END
|
|
2927
|
|
2928 sub extract_corresponding_genomic_sequence_paired_ends {
|
|
2929 my ($sequence_identifier,$methylation_call_params) = @_;
|
|
2930 ### A bisulfite sequence pair for 1 location in the genome can theoretically be on any of the 4 possible converted strands. We are also giving the
|
|
2931 ### sequence a 'memory' of the conversion we are expecting which we will need later for the methylation call
|
|
2932 my $alignment_read_1;
|
|
2933 my $alignment_read_2;
|
|
2934 my $read_conversion_info_1;
|
|
2935 my $read_conversion_info_2;
|
|
2936 my $genome_conversion;
|
|
2937
|
|
2938 ### Now extracting the same sequence from the mouse genomic sequence, +2 extra bases at oone of the ends so that we can also make a CpG, CHG or CHH methylation call
|
|
2939 ### if the C happens to be at the first or last position of the actually observed sequence
|
|
2940 my $non_bisulfite_sequence_1;
|
|
2941 my $non_bisulfite_sequence_2;
|
|
2942
|
|
2943 ### all alignments reported by bowtie have the + alignment first and the - alignment as the second one irrespective of whether read 1 or read 2 was
|
|
2944 ### the + alignment. We however always read in sequences read 1 then read 2, so if read 2 is the + alignment we need to swap the extracted genomic
|
|
2945 ### sequences around!
|
|
2946 ### results from CT converted read 1 plus GA converted read 2 vs. CT converted genome (+/- orientation alignments are reported only)
|
|
2947 if ($methylation_call_params->{$sequence_identifier}->{index} == 0){
|
|
2948 ### [Index 0, sequence originated from (converted) forward strand]
|
|
2949 $counting{CT_GA_CT_count}++;
|
|
2950 $alignment_read_1 = '+';
|
|
2951 $alignment_read_2 = '-';
|
|
2952 $read_conversion_info_1 = 'CT';
|
|
2953 $read_conversion_info_2 = 'GA';
|
|
2954 $genome_conversion = 'CT';
|
|
2955 ### SEQUENCE 1 (this is always the forward hit, in this case it is read 1)
|
|
2956 ### for hits on the forward strand we need to capture 2 extra bases at the 3' end
|
|
2957
|
|
2958 $non_bisulfite_sequence_1 = substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},$methylation_call_params->{$sequence_identifier}->{start_seq_1},length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence_1})+2); ##CHH change
|
|
2959
|
|
2960 ### SEQUENCE 2 (this will always be on the reverse strand, in this case it is read 2)
|
|
2961 ### As the second conversion is GA we need to capture 1 base 3', so that it is a 5' base after reverse complementation
|
|
2962 if (length($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}}) > $methylation_call_params->{$sequence_identifier}->{start_seq_2}+length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence_2})+1){ ## CHH change to +1
|
|
2963
|
|
2964 $non_bisulfite_sequence_2 = substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},($methylation_call_params->{$sequence_identifier}->{start_seq_2}),length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence_2})+2);
|
|
2965 ### the reverse strand sequence needs to be reverse complemented
|
|
2966 $non_bisulfite_sequence_2 = reverse_complement($non_bisulfite_sequence_2);
|
|
2967 }
|
|
2968 else{
|
|
2969 $non_bisulfite_sequence_2 = '';
|
|
2970 }
|
|
2971 }
|
|
2972
|
|
2973 ### results from GA converted read 1 plus CT converted read 2 vs. GA converted genome (+/- orientation alignments are reported only)
|
|
2974 elsif ($methylation_call_params->{$sequence_identifier}->{index} == 1){
|
|
2975 ### [Index 1, sequence originated from complementary to (converted) reverse strand]
|
|
2976 $counting{GA_CT_GA_count}++;
|
|
2977 $alignment_read_1 = '+';
|
|
2978 $alignment_read_2 = '-';
|
|
2979 $read_conversion_info_1 = 'GA';
|
|
2980 $read_conversion_info_2 = 'CT';
|
|
2981 $genome_conversion = 'GA';
|
|
2982
|
|
2983 ### SEQUENCE 1 (this is always the forward hit, in this case it is read 1)
|
|
2984 ### as we need to make the methylation call for the base 5' of the first base (GA conversion!) we need to capture 2 extra bases at the 5' end
|
|
2985 if ($methylation_call_params->{$sequence_identifier}->{start_seq_1}-1 > 0){ ## CHH change to -1
|
|
2986 $non_bisulfite_sequence_1 = substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},$methylation_call_params->{$sequence_identifier}->{start_seq_1}-2,length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence_1})+2); ### CHH change to -2/+2
|
|
2987 }
|
|
2988 else{
|
|
2989 $non_bisulfite_sequence_1 = '';
|
|
2990 }
|
|
2991
|
|
2992 ### SEQUENCE 2 (this will always be on the reverse strand, in this case it is read 2)
|
|
2993 ### As we are doing a CT comparison for the reverse strand we are taking 2 bases extra at the 5' end, so it is a 3' base after reverse complementation
|
|
2994 $non_bisulfite_sequence_2 = substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},($methylation_call_params->{$sequence_identifier}->{start_seq_2})-2,length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence_2})+2); ### CHH change to -2/+2
|
|
2995 ### the reverse strand sequence needs to be reverse complemented
|
|
2996 $non_bisulfite_sequence_2 = reverse_complement($non_bisulfite_sequence_2);
|
|
2997 }
|
|
2998
|
|
2999 ### results from GA converted read 1 plus CT converted read 2 vs. CT converted genome (-/+ orientation alignments are reported only)
|
|
3000 elsif ($methylation_call_params->{$sequence_identifier}->{index} == 2){
|
|
3001 ### [Index 2, sequence originated from the complementary to (converted) forward strand]
|
|
3002 $counting{GA_CT_CT_count}++;
|
|
3003 $alignment_read_1 = '-';
|
|
3004 $alignment_read_2 = '+';
|
|
3005 $read_conversion_info_1 = 'GA';
|
|
3006 $read_conversion_info_2 = 'CT';
|
|
3007 $genome_conversion = 'CT';
|
|
3008
|
|
3009 ### Here we switch the sequence information round!! non_bisulfite_sequence_1 will later correspond to the read 1!!!!
|
|
3010 ### SEQUENCE 1 (this is always the forward hit, in this case it is READ 2), read 1 is in - orientation on the reverse strand
|
|
3011 ### As read 1 is GA converted we need to capture 2 extra 3' bases which will be 2 extra 5' base after reverse complementation
|
|
3012 $non_bisulfite_sequence_1 = substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},($methylation_call_params->{$sequence_identifier}->{start_seq_2}),length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence_2})+2); ### CHH change to +2
|
|
3013 ### the reverse strand sequence needs to be reverse complemented
|
|
3014 $non_bisulfite_sequence_1 = reverse_complement($non_bisulfite_sequence_1);
|
|
3015
|
|
3016 ### SEQUENCE 2 (this will always be on the reverse strand, in this case it is READ 1)
|
|
3017 ### non_bisulfite_sequence_2 will later correspond to the read 2!!!!
|
|
3018 ### Read 2 is CT converted so we need to capture 2 extra 3' bases
|
|
3019 if (length($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}}) > ($methylation_call_params->{$sequence_identifier}->{start_seq_1})+length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence_1})+1){ ## CHH change to +1
|
|
3020 $non_bisulfite_sequence_2 = substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},($methylation_call_params->{$sequence_identifier}->{start_seq_1}),length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence_1})+2); ## CHH changed from +1 to +2
|
|
3021 }
|
|
3022 else{
|
|
3023 $non_bisulfite_sequence_2 = '';
|
|
3024 }
|
|
3025 }
|
|
3026
|
|
3027 ### results from CT converted read 1 plus GA converted read 2 vs. GA converted genome (-/+ orientation alignments are reported only)
|
|
3028 elsif ($methylation_call_params->{$sequence_identifier}->{index} == 3){
|
|
3029 ### [Index 3, sequence originated from the (converted) reverse strand]
|
|
3030 $counting{CT_GA_GA_count}++;
|
|
3031 $alignment_read_1 = '-';
|
|
3032 $alignment_read_2 = '+';
|
|
3033 $read_conversion_info_1 = 'CT';
|
|
3034 $read_conversion_info_2 = 'GA';
|
|
3035 $genome_conversion = 'GA';
|
|
3036
|
|
3037 ### Here we switch the sequence information round!! non_bisulfite_sequence_1 will later correspond to the read 1!!!!
|
|
3038 ### SEQUENCE 1 (this is always the forward hit, in this case it is READ 2), read 1 is in - orientation on the reverse strand
|
|
3039 ### As read 1 is CT converted we need to capture 2 extra 5' bases which will be 2 extra 3' base after reverse complementation
|
|
3040 if ( ($methylation_call_params->{$sequence_identifier}->{start_seq_2}-1) > 0){ ## CHH changed to -1
|
|
3041 $non_bisulfite_sequence_1 = substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},($methylation_call_params->{$sequence_identifier}->{start_seq_2})-2,length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence_2})+2); ### CHH changed to -2/+2
|
|
3042 ### the reverse strand sequence needs to be reverse complemented
|
|
3043 $non_bisulfite_sequence_1 = reverse_complement($non_bisulfite_sequence_1);
|
|
3044 }
|
|
3045 else{
|
|
3046 $non_bisulfite_sequence_1 = '';
|
|
3047 }
|
|
3048
|
|
3049 ### SEQUENCE 2 (this will always be on the reverse strand, in this case it is READ 1)
|
|
3050 ### non_bisulfite_sequence_2 will later correspond to the read 2!!!!
|
|
3051 ### Read 2 is GA converted so we need to capture 2 extra 5' bases
|
|
3052 $non_bisulfite_sequence_2 = substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},($methylation_call_params->{$sequence_identifier}->{start_seq_1})-2,length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence_1})+2); ### CHH changed to -2/+2
|
|
3053 }
|
|
3054 else{
|
|
3055 die "Too many bowtie result filehandles\n";
|
|
3056 }
|
|
3057 ### the alignment_strand information is needed to determine which strand of the genomic sequence we are comparing the read against,
|
|
3058 ### the read_conversion information is needed to know whether we are looking for C->T or G->A substitutions
|
|
3059
|
|
3060 $methylation_call_params->{$sequence_identifier}->{alignment_read_1} = $alignment_read_1;
|
|
3061 $methylation_call_params->{$sequence_identifier}->{alignment_read_2} = $alignment_read_2;
|
|
3062 $methylation_call_params->{$sequence_identifier}->{genome_conversion} = $genome_conversion;
|
|
3063 $methylation_call_params->{$sequence_identifier}->{read_conversion_1} = $read_conversion_info_1;
|
|
3064 $methylation_call_params->{$sequence_identifier}->{read_conversion_2} = $read_conversion_info_2;
|
|
3065 $methylation_call_params->{$sequence_identifier}->{unmodified_genomic_sequence_1} = $non_bisulfite_sequence_1;
|
|
3066 $methylation_call_params->{$sequence_identifier}->{unmodified_genomic_sequence_2} = $non_bisulfite_sequence_2;
|
|
3067 }
|
|
3068
|
|
3069 ### EXTRACT GENOMIC SEQUENCE BOWTIE 2 | PAIRED-END
|
|
3070
|
|
3071 sub extract_corresponding_genomic_sequence_paired_ends_bowtie2{
|
|
3072 my ($sequence_identifier,$methylation_call_params) = @_;
|
|
3073 ### A bisulfite sequence pair for 1 location in the genome can theoretically be on any of the 4 possible converted strands. We are also giving the
|
|
3074 ### sequence a 'memory' of the conversion we are expecting which we will need later for the methylation call
|
|
3075
|
|
3076 my $cigar_1 = $methylation_call_params->{$sequence_identifier}->{CIGAR_1};
|
|
3077 my $cigar_2 = $methylation_call_params->{$sequence_identifier}->{CIGAR_2};
|
|
3078 my $flag_1 = $methylation_call_params->{$sequence_identifier}->{flag_1};
|
|
3079 my $flag_2 = $methylation_call_params->{$sequence_identifier}->{flag_2};
|
|
3080 # print "$cigar_1\t$cigar_2\t$flag_1\t$flag_2\n";
|
|
3081 ### We are now extracting the corresponding genomic sequence, +2 extra bases at the end (or start) so that we can also make a CpG methylation call and
|
|
3082 ### in addition make differential calls for Cs in CHG or CHH context if the C happens to be at the last (or first) position of the actually observed sequence
|
|
3083
|
|
3084 ### the alignment_strand information is needed to determine which strand of the genomic sequence we are comparing the read against,
|
|
3085 ### the read_conversion information is needed to know whether we are looking for C->T or G->A substitutions
|
|
3086 my $alignment_read_1;
|
|
3087 my $alignment_read_2;
|
|
3088 my $read_conversion_info_1;
|
|
3089 my $read_conversion_info_2;
|
|
3090 my $genome_conversion;
|
|
3091
|
|
3092 ### Now extracting the same sequence from the mouse genomic sequence, +2 extra bases at one of the ends so that we can also make a CpG, CHG or CHH methylation call
|
|
3093 ### if the C happens to be at the last position of the actually observed sequence
|
|
3094 my $non_bisulfite_sequence_1 = '';
|
|
3095 my $non_bisulfite_sequence_2 = '';
|
|
3096
|
|
3097 ### Positions in SAM format are 1 based, so we need to subract 1 when getting substrings
|
|
3098 my $pos_1 = $methylation_call_params->{$sequence_identifier}->{position_1}-1;
|
|
3099 my $pos_2 = $methylation_call_params->{$sequence_identifier}->{position_2}-1;
|
|
3100
|
|
3101 # parsing CIGAR 1 string
|
|
3102 my @len_1 = split (/\D+/,$cigar_1); # storing the length per operation
|
|
3103 my @ops_1 = split (/\d+/,$cigar_1); # storing the operation
|
|
3104 shift @ops_1; # remove the empty first element
|
|
3105 die "CIGAR 1 string contained a non-matching number of lengths and operations\n" unless (scalar @len_1 == scalar @ops_1);
|
|
3106 # parsing CIGAR 2 string
|
|
3107 my @len_2 = split (/\D+/,$cigar_2); # storing the length per operation
|
|
3108 my @ops_2 = split (/\d+/,$cigar_2); # storing the operation
|
|
3109 shift @ops_2; # remove the empty first element
|
|
3110 die "CIGAR 2 string contained a non-matching number of lengths and operations\n" unless (scalar @len_2 == scalar @ops_2);
|
|
3111
|
|
3112 my $indels_1 = 0; # addiong these to the hemming distance value (needed for the NM field in the final SAM output
|
|
3113 my $indels_2 = 0;
|
|
3114
|
|
3115 ### Extracting read 1 genomic sequence ###
|
|
3116
|
|
3117 # extracting 2 additional bp at the 5' end (read 1)
|
|
3118 if ( ($methylation_call_params->{$sequence_identifier}->{index} == 1) or ($methylation_call_params->{$sequence_identifier}->{index} == 3) ){
|
|
3119 # checking if the substring will be valid or if we can't extract the sequence because we are right at the edge of a chromosome
|
|
3120 unless ( ($pos_1-2) > 0){# exiting with en empty genomic sequence otherwise
|
|
3121 $methylation_call_params->{$sequence_identifier}->{unmodified_genomic_sequence_1} = $non_bisulfite_sequence_1;
|
|
3122 return;
|
|
3123 }
|
|
3124 $non_bisulfite_sequence_1 .= substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},$pos_1-2,2);
|
|
3125 }
|
|
3126
|
|
3127 foreach (0..$#len_1){
|
|
3128 if ($ops_1[$_] eq 'M'){
|
|
3129 # extracting genomic sequence
|
|
3130 $non_bisulfite_sequence_1 .= substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},$pos_1,$len_1[$_]);
|
|
3131 # warn "$non_bisulfite_sequence_1\n";
|
|
3132 # adjusting position
|
|
3133 $pos_1 += $len_1[$_];
|
|
3134 }
|
|
3135 elsif ($ops_1[$_] eq 'I'){ # insertion in the read sequence
|
|
3136 # we simply add padding Ns instead of finding genomic sequence. This will not be used to infer methylation calls
|
|
3137 $non_bisulfite_sequence_1 .= 'N' x $len_1[$_];
|
|
3138 # warn "$non_bisulfite_sequence_1\n";
|
|
3139 # position doesn't need adjusting
|
|
3140 $indels_1 += $len_1[$_]; # adding to $indels_1 to determine the hemming distance (= single base mismatches, insertions or deletions) for the SAM output
|
|
3141 }
|
|
3142 elsif ($ops_1[$_] eq 'D'){ # deletion in the read sequence
|
|
3143 # we do not add any genomic sequence but only adjust the position
|
|
3144 # warn "Just adjusting the position by: ",$len_1[$_],"bp\n";
|
|
3145 $pos_1 += $len_1[$_];
|
|
3146 $indels_1 += $len_1[$_]; # adding to $indels_1 to determine the hemming distance (= single base mismatches, insertions or deletions) for the SAM output
|
|
3147 }
|
|
3148 elsif($cigar_1 =~ tr/[NSHPX=]//){ # if these (for standard mapping) illegal characters exist we die
|
|
3149 die "The CIGAR 1 string contained illegal CIGAR operations in addition to 'M', 'I' and 'D': $cigar_1\n";
|
|
3150 }
|
|
3151 else{
|
|
3152 die "The CIGAR 1 string contained undefined CIGAR operations in addition to 'M', 'I' and 'D': $cigar_1\n";
|
|
3153 }
|
|
3154 }
|
|
3155
|
|
3156 ### 3' end of read 1
|
|
3157 if ( ($methylation_call_params->{$sequence_identifier}->{index} == 0) or ($methylation_call_params->{$sequence_identifier}->{index} == 2) ){
|
|
3158 ## checking if the substring will be valid or if we can't extract the sequence because we are right at the edge of a chromosome
|
|
3159 unless (length($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}}) >= $pos_1+2){# exiting with en empty genomic sequence otherwise
|
|
3160 $methylation_call_params->{$sequence_identifier}->{unmodified_genomic_sequence_1} = $non_bisulfite_sequence_1;
|
|
3161 return;
|
|
3162 }
|
|
3163 $non_bisulfite_sequence_1 .= substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},$pos_1,2);
|
|
3164 }
|
|
3165
|
|
3166
|
|
3167 ### Extracting read 2 genomic sequence ###
|
|
3168
|
|
3169 ### 5' end of read 2
|
|
3170 if ( ($methylation_call_params->{$sequence_identifier}->{index} == 1) or ($methylation_call_params->{$sequence_identifier}->{index} == 3) ){
|
|
3171 ## checking if the substring will be valid or if we can't extract the sequence because we are right at the edge of a chromosome
|
|
3172 unless ( ($pos_2-2) >= 0){# exiting with en empty genomic sequence otherwise
|
|
3173 $methylation_call_params->{$sequence_identifier}->{unmodified_genomic_sequence_2} = $non_bisulfite_sequence_2;
|
|
3174 return;
|
|
3175 }
|
|
3176 $non_bisulfite_sequence_2 .= substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},$pos_2-2,2);
|
|
3177 }
|
|
3178
|
|
3179 foreach (0..$#len_2){
|
|
3180 if ($ops_2[$_] eq 'M'){
|
|
3181 # extracting genomic sequence
|
|
3182 $non_bisulfite_sequence_2 .= substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},$pos_2,$len_2[$_]);
|
|
3183 # warn "$non_bisulfite_sequence_2\n";
|
|
3184 # adjusting position
|
|
3185 $pos_2 += $len_2[$_];
|
|
3186 }
|
|
3187 elsif ($ops_2[$_] eq 'I'){ # insertion in the read sequence
|
|
3188 # we simply add padding Ns instead of finding genomic sequence. This will not be used to infer methylation calls
|
|
3189 $non_bisulfite_sequence_2 .= 'N' x $len_2[$_];
|
|
3190 # warn "$non_bisulfite_sequence_2\n";
|
|
3191 # position doesn't need adjusting
|
|
3192 $indels_2 += $len_2[$_]; # adding to $indels_1 to determine the hemming distance (= single base mismatches, insertions or deletions) for the SAM output
|
|
3193 }
|
|
3194 elsif ($ops_2[$_] eq 'D'){ # deletion in the read sequence
|
|
3195 # we do not add any genomic sequence but only adjust the position
|
|
3196 # warn "Just adjusting the position by: ",$len_2[$_],"bp\n";
|
|
3197 $pos_2 += $len_2[$_];
|
|
3198 $indels_2 += $len_2[$_]; # adding to $indels_1 to determine the hemming distance (= single base mismatches, insertions or deletions) for the SAM output
|
|
3199 }
|
|
3200 elsif($cigar_2 =~ tr/[NSHPX=]//){ # if these (for standard mapping) illegal characters exist we die
|
|
3201 die "The CIGAR 2 string contained illegal CIGAR operations in addition to 'M', 'I' and 'D': $cigar_2\n";
|
|
3202 }
|
|
3203 else{
|
|
3204 die "The CIGAR 2 string contained undefined CIGAR operations in addition to 'M', 'I' and 'D': $cigar_2\n";
|
|
3205 }
|
|
3206 }
|
|
3207
|
|
3208 ### 3' end of read 2
|
|
3209 if ( ($methylation_call_params->{$sequence_identifier}->{index} == 0) or ($methylation_call_params->{$sequence_identifier}->{index} == 2) ){
|
|
3210 ## checking if the substring will be valid or if we can't extract the sequence because we are right at the edge of a chromosome
|
|
3211 unless (length($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}}) >= $pos_2+2){# exiting with en empty genomic sequence otherwise
|
|
3212 $methylation_call_params->{$sequence_identifier}->{unmodified_genomic_sequence_2} = $non_bisulfite_sequence_2;
|
|
3213 return;
|
|
3214 }
|
|
3215 $non_bisulfite_sequence_2 .= substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},$pos_2,2);
|
|
3216 }
|
|
3217
|
|
3218 ### all paired-end alignments reported by Bowtie 2 have the Read 1 alignment first and the Read 2 alignment as the second one irrespective of whether read 1 or read 2 was
|
|
3219 ### the + alignment. We also read in sequences read 1 then read 2 so they should correspond perfectly
|
|
3220
|
|
3221 ### results from CT converted read 1 plus GA converted read 2 vs. CT converted genome (+/- orientation alignments are reported only)
|
|
3222 if ($methylation_call_params->{$sequence_identifier}->{index} == 0){
|
|
3223 ### [Index 0, sequence originated from (converted) forward strand]
|
|
3224 $counting{CT_GA_CT_count}++;
|
|
3225 $alignment_read_1 = '+';
|
|
3226 $alignment_read_2 = '-';
|
|
3227 $read_conversion_info_1 = 'CT';
|
|
3228 $read_conversion_info_2 = 'GA';
|
|
3229 $genome_conversion = 'CT';
|
|
3230 ### Read 1 is always the forward hit
|
|
3231 ### Read 2 is will always on the reverse strand, so it needs to be reverse complemented
|
|
3232 $non_bisulfite_sequence_2 = reverse_complement($non_bisulfite_sequence_2);
|
|
3233 }
|
|
3234
|
|
3235 ### results from GA converted read 1 plus CT converted read 2 vs. GA converted genome (+/- orientation alignments are reported only)
|
|
3236 elsif ($methylation_call_params->{$sequence_identifier}->{index} == 1){
|
|
3237 ### [Index 1, sequence originated from complementary to (converted) bottom strand]
|
|
3238 $counting{GA_CT_GA_count}++;
|
|
3239 $alignment_read_1 = '+';
|
|
3240 $alignment_read_2 = '-';
|
|
3241 $read_conversion_info_1 = 'GA';
|
|
3242 $read_conversion_info_2 = 'CT';
|
|
3243 $genome_conversion = 'GA';
|
|
3244 ### Read 1 is always the forward hit
|
|
3245 ### Read 2 is will always on the reverse strand, so it needs to be reverse complemented
|
|
3246 $non_bisulfite_sequence_2 = reverse_complement($non_bisulfite_sequence_2);
|
|
3247 }
|
|
3248
|
|
3249 ### results from GA converted read 1 plus CT converted read 2 vs. CT converted genome (-/+ orientation alignments are reported only)
|
|
3250 elsif ($methylation_call_params->{$sequence_identifier}->{index} == 2){
|
|
3251 ### [Index 2, sequence originated from the complementary to (converted) top strand]
|
|
3252 $counting{GA_CT_CT_count}++;
|
|
3253 $alignment_read_1 = '-';
|
|
3254 $alignment_read_2 = '+';
|
|
3255 $read_conversion_info_1 = 'GA';
|
|
3256 $read_conversion_info_2 = 'CT';
|
|
3257 $genome_conversion = 'CT';
|
|
3258
|
|
3259 ### Read 1 (the reverse strand) genomic sequence needs to be reverse complemented
|
|
3260 $non_bisulfite_sequence_1 = reverse_complement($non_bisulfite_sequence_1);
|
|
3261 }
|
|
3262
|
|
3263 ### results from CT converted read 1 plus GA converted read 2 vs. GA converted genome (-/+ orientation alignments are reported only)
|
|
3264 elsif ($methylation_call_params->{$sequence_identifier}->{index} == 3){
|
|
3265 ### [Index 3, sequence originated from the (converted) reverse strand]
|
|
3266 $counting{CT_GA_GA_count}++;
|
|
3267 $alignment_read_1 = '-';
|
|
3268 $alignment_read_2 = '+';
|
|
3269 $read_conversion_info_1 = 'CT';
|
|
3270 $read_conversion_info_2 = 'GA';
|
|
3271 $genome_conversion = 'GA';
|
|
3272 ### Read 1 (the reverse strand) genomic sequence needs to be reverse complemented
|
|
3273 $non_bisulfite_sequence_1 = reverse_complement($non_bisulfite_sequence_1);
|
|
3274 }
|
|
3275 else{
|
|
3276 die "Too many bowtie result filehandles\n";
|
|
3277 }
|
|
3278 ### the alignment_strand information is needed to determine which strand of the genomic sequence we are comparing the read against,
|
|
3279 ### the read_conversion information is needed to know whether we are looking for C->T or G->A substitutions
|
|
3280
|
|
3281 $methylation_call_params->{$sequence_identifier}->{alignment_read_1} = $alignment_read_1;
|
|
3282 $methylation_call_params->{$sequence_identifier}->{alignment_read_2} = $alignment_read_2;
|
|
3283 $methylation_call_params->{$sequence_identifier}->{genome_conversion} = $genome_conversion;
|
|
3284 $methylation_call_params->{$sequence_identifier}->{read_conversion_1} = $read_conversion_info_1;
|
|
3285 $methylation_call_params->{$sequence_identifier}->{read_conversion_2} = $read_conversion_info_2;
|
|
3286 $methylation_call_params->{$sequence_identifier}->{unmodified_genomic_sequence_1} = $non_bisulfite_sequence_1;
|
|
3287 $methylation_call_params->{$sequence_identifier}->{unmodified_genomic_sequence_2} = $non_bisulfite_sequence_2;
|
|
3288 ## the end position of a read is stored in $pos
|
|
3289 $methylation_call_params->{$sequence_identifier}->{end_position_1} = $pos_1;
|
|
3290 $methylation_call_params->{$sequence_identifier}->{end_position_2} = $pos_2;
|
|
3291 $methylation_call_params->{$sequence_identifier}->{indels_1} = $indels_1;
|
|
3292 $methylation_call_params->{$sequence_identifier}->{indels_2} = $indels_2;
|
|
3293 }
|
|
3294
|
|
3295 ##########################################
|
|
3296 ### PRINT SINGLE END RESULTS: Bowtie 1 ###
|
|
3297 ##########################################
|
|
3298
|
|
3299 sub print_bisulfite_mapping_result_single_end{
|
|
3300 my ($identifier,$sequence,$methylation_call_params,$quality_value)= @_;
|
|
3301
|
|
3302 ### we will output the FastQ quality in Sanger encoding (Phred 33 scale)
|
|
3303 if ($phred64){
|
|
3304 $quality_value = convert_phred64_quals_to_phred33($quality_value);
|
|
3305 }
|
|
3306 elsif ($solexa){
|
|
3307 $quality_value = convert_solexa_quals_to_phred33($quality_value);
|
|
3308 }
|
|
3309
|
|
3310 ### We will add +1 bp to the starting position of single-end reads, as Bowtie 1 reports the index and not the bp position.
|
|
3311 $methylation_call_params->{$identifier}->{position} += 1;
|
|
3312
|
|
3313 ### writing every uniquely mapped read and its methylation call to the output file
|
|
3314 if ($vanilla){
|
|
3315 my $bowtie1_output = join("\t",$identifier,$methylation_call_params->{$identifier}->{alignment_strand},$methylation_call_params->{$identifier}->{chromosome},$methylation_call_params->{$identifier}->{position},$methylation_call_params->{$identifier}->{end_position},$sequence,$methylation_call_params->{$identifier}->{unmodified_genomic_sequence},$methylation_call_params->{$identifier}->{methylation_call},$methylation_call_params->{$identifier}->{read_conversion},$methylation_call_params->{$identifier}->{genome_conversion},$quality_value);
|
|
3316 print OUT "$bowtie1_output\n";
|
|
3317 }
|
|
3318 else{ # SAM output, default since Bismark v1.0.0
|
|
3319 single_end_SAM_output($identifier,$sequence,$methylation_call_params,$quality_value); # at the end of the script
|
|
3320 }
|
|
3321 }
|
|
3322
|
|
3323 ##########################################
|
|
3324 ### PRINT SINGLE END RESULTS: Bowtie 2 ###
|
|
3325 ##########################################
|
|
3326
|
|
3327 sub print_bisulfite_mapping_result_single_end_bowtie2{
|
|
3328 my ($identifier,$sequence,$methylation_call_params,$quality_value)= @_;
|
|
3329
|
|
3330 ### we will output the FastQ quality in Sanger encoding (Phred 33 scale)
|
|
3331 if ($phred64){
|
|
3332 $quality_value = convert_phred64_quals_to_phred33($quality_value);
|
|
3333 }
|
|
3334 elsif ($solexa){
|
|
3335 $quality_value = convert_solexa_quals_to_phred33($quality_value);
|
|
3336 }
|
|
3337
|
|
3338 ### writing every mapped read and its methylation call to the SAM output file (unmapped and ambiguous reads were already printed)
|
|
3339 single_end_SAM_output($identifier,$sequence,$methylation_call_params,$quality_value); # at the end of the script
|
|
3340 }
|
|
3341
|
|
3342 ##########################################
|
|
3343 ### PRINT PAIRED END ESULTS: Bowtie 1 ###
|
|
3344 ##########################################
|
|
3345
|
|
3346 sub print_bisulfite_mapping_results_paired_ends{
|
|
3347 my ($identifier,$sequence_1,$sequence_2,$methylation_call_params,$quality_value_1,$quality_value_2)= @_;
|
|
3348
|
|
3349 ### we will output the FastQ quality in Sanger encoding (Phred 33 scale)
|
|
3350 if ($phred64){
|
|
3351 $quality_value_1 = convert_phred64_quals_to_phred33($quality_value_1);
|
|
3352 $quality_value_2 = convert_phred64_quals_to_phred33($quality_value_2);
|
|
3353 }
|
|
3354 elsif ($solexa){
|
|
3355 $quality_value_1 = convert_solexa_quals_to_phred33($quality_value_1);
|
|
3356 $quality_value_2 = convert_solexa_quals_to_phred33($quality_value_2);
|
|
3357 }
|
|
3358
|
|
3359 ### We will add +1 bp to the start position of paired-end reads, as Bowtie 1 reports the index and not the bp position. (End position is already 1-based)
|
|
3360 $methylation_call_params->{$identifier}->{start_seq_1} += 1;
|
|
3361
|
|
3362 ### writing every single aligned read and its methylation call to the output file
|
|
3363 if ($vanilla){
|
|
3364 my $bowtie1_output_paired_end = join("\t",$identifier,$methylation_call_params->{$identifier}->{alignment_read_1},$methylation_call_params->{$identifier}->{chromosome},$methylation_call_params->{$identifier}->{start_seq_1},$methylation_call_params->{$identifier}->{alignment_end},$sequence_1,$methylation_call_params->{$identifier}->{unmodified_genomic_sequence_1},$methylation_call_params->{$identifier}->{methylation_call_1},$sequence_2,$methylation_call_params->{$identifier}->{unmodified_genomic_sequence_2},$methylation_call_params->{$identifier}->{methylation_call_2},$methylation_call_params->{$identifier}->{read_conversion_1},$methylation_call_params->{$identifier}->{genome_conversion},$quality_value_1,$quality_value_2);
|
|
3365 print OUT "$bowtie1_output_paired_end\n";
|
|
3366 }
|
|
3367 else{ # SAM output, default since Bismark v1.0.0
|
|
3368 paired_end_SAM_output($identifier,$sequence_1,$sequence_2,$methylation_call_params,$quality_value_1,$quality_value_2); # at the end of the script
|
|
3369 }
|
|
3370
|
|
3371 }
|
|
3372
|
|
3373 ##########################################
|
|
3374 ### PRINT PAIRED END ESULTS: Bowtie 2 ###
|
|
3375 ##########################################
|
|
3376
|
|
3377 sub print_bisulfite_mapping_results_paired_ends_bowtie2{
|
|
3378 my ($identifier,$sequence_1,$sequence_2,$methylation_call_params,$quality_value_1,$quality_value_2)= @_;
|
|
3379
|
|
3380 ### we will output the FastQ quality in Sanger encoding (Phred 33 scale)
|
|
3381 if ($phred64){
|
|
3382 $quality_value_1 = convert_phred64_quals_to_phred33($quality_value_1);
|
|
3383 $quality_value_2 = convert_phred64_quals_to_phred33($quality_value_2);
|
|
3384 }
|
|
3385 elsif ($solexa){
|
|
3386 $quality_value_1 = convert_solexa_quals_to_phred33($quality_value_1);
|
|
3387 $quality_value_2 = convert_solexa_quals_to_phred33($quality_value_2);
|
|
3388 }
|
|
3389
|
|
3390 ### writing every single aligned read and its methylation call to the output file (unmapped and ambiguous reads were already printed)
|
|
3391 paired_end_SAM_output($identifier,$sequence_1,$sequence_2,$methylation_call_params,$quality_value_1,$quality_value_2); # at the end of the script
|
|
3392
|
|
3393 }
|
|
3394
|
|
3395
|
|
3396 sub convert_phred64_quals_to_phred33{
|
|
3397
|
|
3398 my $qual = shift;
|
|
3399 my @quals = split (//,$qual);
|
|
3400 my @new_quals;
|
|
3401
|
|
3402 foreach my $index (0..$#quals){
|
|
3403 my $phred_score = convert_phred64_quality_string_into_phred_score ($quals[$index]);
|
|
3404 my $phred33_quality_string = convert_phred_score_into_phred33_quality_string ($phred_score);
|
|
3405 $new_quals[$index] = $phred33_quality_string;
|
|
3406 }
|
|
3407
|
|
3408 my $phred33_quality = join ("",@new_quals);
|
|
3409 return $phred33_quality;
|
|
3410 }
|
|
3411
|
|
3412 sub convert_solexa_quals_to_phred33{
|
|
3413
|
|
3414 my $qual = shift;
|
|
3415 my @quals = split (//,$qual);
|
|
3416 my @new_quals;
|
|
3417
|
|
3418 foreach my $index (0..$#quals){
|
|
3419 my $phred_score = convert_solexa_pre1_3_quality_string_into_phred_score ($quals[$index]);
|
|
3420 my $phred33_quality_string = convert_phred_score_into_phred33_quality_string ($phred_score);
|
|
3421 $new_quals[$index] = $phred33_quality_string;
|
|
3422 }
|
|
3423
|
|
3424 my $phred33_quality = join ("",@new_quals);
|
|
3425 return $phred33_quality;
|
|
3426 }
|
|
3427
|
|
3428 sub convert_phred_score_into_phred33_quality_string{
|
|
3429 my $qual = shift;
|
|
3430 $qual = chr($qual+33);
|
|
3431 return $qual;
|
|
3432 }
|
|
3433
|
|
3434 sub convert_phred64_quality_string_into_phred_score{
|
|
3435 my $string = shift;
|
|
3436 my $qual = ord($string)-64;
|
|
3437 return $qual;
|
|
3438 }
|
|
3439
|
|
3440 sub convert_solexa_pre1_3_quality_string_into_phred_score{
|
|
3441 ### We will just use 59 as the offset here as all Phred Scores between 10 and 40 look exactly the same, there is only a minute difference for values between 0 and 10
|
|
3442 my $string = shift;
|
|
3443 my $qual = ord($string)-59;
|
|
3444 return $qual;
|
|
3445 }
|
|
3446
|
|
3447
|
|
3448 sub extract_corresponding_genomic_sequence_single_end {
|
|
3449 my ($sequence_identifier,$methylation_call_params) = @_;
|
|
3450 ### A bisulfite sequence for 1 location in the genome can theoretically be any of the 4 possible converted strands. We are also giving the
|
|
3451 ### sequence a 'memory' of the conversion we are expecting which we will need later for the methylation call
|
|
3452
|
|
3453 ### the alignment_strand information is needed to determine which strand of the genomic sequence we are comparing the read against,
|
|
3454 ### the read_conversion information is needed to know whether we are looking for C->T or G->A substitutions
|
|
3455 my $alignment_strand;
|
|
3456 my $read_conversion_info;
|
|
3457 my $genome_conversion;
|
|
3458 ### Also extracting the corresponding genomic sequence, +2 extra bases at the end so that we can also make a CpG methylation call and
|
|
3459 ### in addition make differential calls for Cs non-CpG context, which will now be divided into CHG and CHH methylation,
|
|
3460 ### if the C happens to be at the last position of the actually observed sequence
|
|
3461 my $non_bisulfite_sequence;
|
|
3462 ### depending on the conversion we want to make need to capture 1 extra base at the 3' end
|
|
3463
|
|
3464 ### results from CT converted read vs. CT converted genome (+ orientation alignments are reported only)
|
|
3465 if ($methylation_call_params->{$sequence_identifier}->{index} == 0){
|
|
3466 ### [Index 0, sequence originated from (converted) forward strand]
|
|
3467 $counting{CT_CT_count}++;
|
|
3468 $alignment_strand = '+';
|
|
3469 $read_conversion_info = 'CT';
|
|
3470 $genome_conversion = 'CT';
|
|
3471
|
|
3472 ## checking if the substring will be valid or if we can't extract the sequence because we are right at the edge of a chromosome
|
|
3473 if (length($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}}) > $methylation_call_params->{$sequence_identifier}->{position}+length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence})+1){ ## CHH changed to +1
|
|
3474 ### + 2 extra base at the 3' end
|
|
3475 $non_bisulfite_sequence = substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},$methylation_call_params->{$sequence_identifier}->{position},length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence})+2); ## CHH changed to +2
|
|
3476 }
|
|
3477 else{
|
|
3478 $non_bisulfite_sequence = '';
|
|
3479 }
|
|
3480 }
|
|
3481
|
|
3482 ### results from CT converted reads vs. GA converted genome (- orientation alignments are reported only)
|
|
3483 elsif ($methylation_call_params->{$sequence_identifier}->{index} == 1){
|
|
3484 ### [Index 1, sequence originated from (converted) reverse strand]
|
|
3485 $counting{CT_GA_count}++;
|
|
3486 $alignment_strand = '-';
|
|
3487 $read_conversion_info = 'CT';
|
|
3488 $genome_conversion = 'GA';
|
|
3489
|
|
3490 ## checking if the substring will be valid or if we can't extract the sequence because we are right at the edge of a chromosome
|
|
3491 if ($methylation_call_params->{$sequence_identifier}->{position}-2 >= 0){ ## CHH changed to -2 # 02 02 2012 Changed this to >= from >
|
|
3492 ### Extracting 2 extra 5' bases on forward strand which will become 2 extra 3' bases after reverse complementation
|
|
3493 $non_bisulfite_sequence = substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},$methylation_call_params->{$sequence_identifier}->{position}-2,length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence})+2); ## CHH changed to -2/+2
|
|
3494 ## reverse complement!
|
|
3495 $non_bisulfite_sequence = reverse_complement($non_bisulfite_sequence);
|
|
3496 }
|
|
3497 else{
|
|
3498 $non_bisulfite_sequence = '';
|
|
3499 }
|
|
3500 }
|
|
3501
|
|
3502 ### results from GA converted reads vs. CT converted genome (- orientation alignments are reported only)
|
|
3503 elsif ($methylation_call_params->{$sequence_identifier}->{index} == 2){
|
|
3504 ### [Index 2, sequence originated from complementary to (converted) forward strand]
|
|
3505 $counting{GA_CT_count}++;
|
|
3506 $alignment_strand = '-';
|
|
3507 $read_conversion_info = 'GA';
|
|
3508 $genome_conversion = 'CT';
|
|
3509
|
|
3510 ### +2 extra bases on the forward strand 3', which will become 2 extra 5' bases after reverse complementation
|
|
3511 ## checking if the substring will be valid or if we can't extract the sequence because we are right at the edge of a chromosome
|
|
3512 if (length($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}}) > $methylation_call_params->{$sequence_identifier}->{position}+length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence})+1){ ## changed to +1 on 02 02 2012
|
|
3513 $non_bisulfite_sequence = substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},$methylation_call_params->{$sequence_identifier}->{position},length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence})+2); ## CHH changed to +2
|
|
3514 ## reverse complement!
|
|
3515 $non_bisulfite_sequence = reverse_complement($non_bisulfite_sequence);
|
|
3516 }
|
|
3517 else{
|
|
3518 $non_bisulfite_sequence = '';
|
|
3519 }
|
|
3520 }
|
|
3521
|
|
3522 ### results from GA converted reads vs. GA converted genome (+ orientation alignments are reported only)
|
|
3523 elsif ($methylation_call_params->{$sequence_identifier}->{index} == 3){
|
|
3524 ### [Index 3, sequence originated from complementary to (converted) reverse strand]
|
|
3525 $counting{GA_GA_count}++;
|
|
3526 $alignment_strand = '+';
|
|
3527 $read_conversion_info = 'GA';
|
|
3528 $genome_conversion = 'GA';
|
|
3529
|
|
3530 ## checking if the substring will be valid or if we can't extract the sequence because we are right at the edge of a chromosome
|
|
3531 if ($methylation_call_params->{$sequence_identifier}->{position}-2 >= 0){ ## CHH changed to +2 # 02 02 2012 Changed this to >= from >
|
|
3532 ### +2 extra base at the 5' end as we are nominally checking the converted reverse strand
|
|
3533 $non_bisulfite_sequence = substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},$methylation_call_params->{$sequence_identifier}->{position}-2,length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence})+2); ## CHH changed to -2/+2
|
|
3534 }
|
|
3535 else{
|
|
3536 $non_bisulfite_sequence = '';
|
|
3537 }
|
|
3538 }
|
|
3539 else{
|
|
3540 die "Too many bowtie result filehandles\n";
|
|
3541 }
|
|
3542
|
|
3543 $methylation_call_params->{$sequence_identifier}->{alignment_strand} = $alignment_strand;
|
|
3544 $methylation_call_params->{$sequence_identifier}->{read_conversion} = $read_conversion_info;
|
|
3545 $methylation_call_params->{$sequence_identifier}->{genome_conversion} = $genome_conversion;
|
|
3546 $methylation_call_params->{$sequence_identifier}->{unmodified_genomic_sequence} = $non_bisulfite_sequence;
|
|
3547
|
|
3548 ### at this point we can also determine the end position of a read
|
|
3549 $methylation_call_params->{$sequence_identifier}->{end_position} = $methylation_call_params->{$sequence_identifier}->{position}+length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence});
|
|
3550 }
|
|
3551
|
|
3552 sub extract_corresponding_genomic_sequence_single_end_pbat {
|
|
3553 my ($sequence_identifier,$methylation_call_params) = @_;
|
|
3554 ### A bisulfite sequence for 1 location in the genome can theoretically be any of the 4 possible converted strands. We are also giving the
|
|
3555 ### sequence a 'memory' of the conversion we are expecting which we will need later for the methylation call
|
|
3556
|
|
3557 ### the alignment_strand information is needed to determine which strand of the genomic sequence we are comparing the read against,
|
|
3558 ### the read_conversion information is needed to know whether we are looking for C->T or G->A substitutions
|
|
3559 my $alignment_strand;
|
|
3560 my $read_conversion_info;
|
|
3561 my $genome_conversion;
|
|
3562 ### Also extracting the corresponding genomic sequence, +2 extra bases at the end so that we can also make a CpG methylation call and
|
|
3563 ### in addition make differential calls for Cs non-CpG context, which will now be divided into CHG and CHH methylation,
|
|
3564 ### if the C happens to be at the last position of the actually observed sequence
|
|
3565 my $non_bisulfite_sequence;
|
|
3566 ### depending on the conversion we want to make need to capture 1 extra base at the 3' end
|
|
3567
|
|
3568 my $pbat_index = $methylation_call_params->{$sequence_identifier}->{index} + 2; # (we are simply not running indexes 0 or 1!
|
|
3569
|
|
3570 ### results from CT converted read vs. CT converted genome (+ orientation alignments are reported only)
|
|
3571 if ($pbat_index == 0){
|
|
3572 ### [Index 0, sequence originated from (converted) forward strand]
|
|
3573 $counting{CT_CT_count}++;
|
|
3574 $alignment_strand = '+';
|
|
3575 $read_conversion_info = 'CT';
|
|
3576 $genome_conversion = 'CT';
|
|
3577
|
|
3578 ## checking if the substring will be valid or if we can't extract the sequence because we are right at the edge of a chromosome
|
|
3579 if (length($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}}) > $methylation_call_params->{$sequence_identifier}->{position}+length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence})+1){ ## CHH changed to +1
|
|
3580 ### + 2 extra base at the 3' end
|
|
3581 $non_bisulfite_sequence = substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},$methylation_call_params->{$sequence_identifier}->{position},length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence})+2); ## CHH changed to +2
|
|
3582 }
|
|
3583 else{
|
|
3584 $non_bisulfite_sequence = '';
|
|
3585 }
|
|
3586 }
|
|
3587
|
|
3588 ### results from CT converted reads vs. GA converted genome (- orientation alignments are reported only)
|
|
3589 elsif ($pbat_index == 1){
|
|
3590 ### [Index 1, sequence originated from (converted) reverse strand]
|
|
3591 $counting{CT_GA_count}++;
|
|
3592 $alignment_strand = '-';
|
|
3593 $read_conversion_info = 'CT';
|
|
3594 $genome_conversion = 'GA';
|
|
3595
|
|
3596 ## checking if the substring will be valid or if we can't extract the sequence because we are right at the edge of a chromosome
|
|
3597 if ($methylation_call_params->{$sequence_identifier}->{position}-2 >= 0){ ## CHH changed to -2 # 02 02 2012 Changed this to >= from >
|
|
3598 ### Extracting 2 extra 5' bases on forward strand which will become 2 extra 3' bases after reverse complementation
|
|
3599 $non_bisulfite_sequence = substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},$methylation_call_params->{$sequence_identifier}->{position}-2,length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence})+2); ## CHH changed to -2/+2
|
|
3600 ## reverse complement!
|
|
3601 $non_bisulfite_sequence = reverse_complement($non_bisulfite_sequence);
|
|
3602 }
|
|
3603 else{
|
|
3604 $non_bisulfite_sequence = '';
|
|
3605 }
|
|
3606 }
|
|
3607
|
|
3608 ### results from GA converted reads vs. CT converted genome (- orientation alignments are reported only)
|
|
3609 elsif ($pbat_index == 2){
|
|
3610 ### [Index 2, sequence originated from complementary to (converted) forward strand]
|
|
3611 $counting{GA_CT_count}++;
|
|
3612 $alignment_strand = '-';
|
|
3613 $read_conversion_info = 'GA';
|
|
3614 $genome_conversion = 'CT';
|
|
3615
|
|
3616 ### +2 extra bases on the forward strand 3', which will become 2 extra 5' bases after reverse complementation
|
|
3617 ## checking if the substring will be valid or if we can't extract the sequence because we are right at the edge of a chromosome
|
|
3618 if (length($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}}) > $methylation_call_params->{$sequence_identifier}->{position}+length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence})+1){ ## changed to +1 on 02 02 2012
|
|
3619 $non_bisulfite_sequence = substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},$methylation_call_params->{$sequence_identifier}->{position},length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence})+2); ## CHH changed to +2
|
|
3620 ## reverse complement!
|
|
3621 $non_bisulfite_sequence = reverse_complement($non_bisulfite_sequence);
|
|
3622 }
|
|
3623 else{
|
|
3624 $non_bisulfite_sequence = '';
|
|
3625 }
|
|
3626 }
|
|
3627
|
|
3628 ### results from GA converted reads vs. GA converted genome (+ orientation alignments are reported only)
|
|
3629 elsif ($pbat_index == 3){
|
|
3630 ### [Index 3, sequence originated from complementary to (converted) reverse strand]
|
|
3631 $counting{GA_GA_count}++;
|
|
3632 $alignment_strand = '+';
|
|
3633 $read_conversion_info = 'GA';
|
|
3634 $genome_conversion = 'GA';
|
|
3635
|
|
3636 ## checking if the substring will be valid or if we can't extract the sequence because we are right at the edge of a chromosome
|
|
3637 if ($methylation_call_params->{$sequence_identifier}->{position}-2 >= 0){ ## CHH changed to +2 # 02 02 2012 Changed this to >= from >
|
|
3638 ### +2 extra base at the 5' end as we are nominally checking the converted reverse strand
|
|
3639 $non_bisulfite_sequence = substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},$methylation_call_params->{$sequence_identifier}->{position}-2,length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence})+2); ## CHH changed to -2/+2
|
|
3640 }
|
|
3641 else{
|
|
3642 $non_bisulfite_sequence = '';
|
|
3643 }
|
|
3644 }
|
|
3645 else{
|
|
3646 die "Too many bowtie result filehandles\n";
|
|
3647 }
|
|
3648
|
|
3649 $methylation_call_params->{$sequence_identifier}->{alignment_strand} = $alignment_strand;
|
|
3650 $methylation_call_params->{$sequence_identifier}->{read_conversion} = $read_conversion_info;
|
|
3651 $methylation_call_params->{$sequence_identifier}->{genome_conversion} = $genome_conversion;
|
|
3652 $methylation_call_params->{$sequence_identifier}->{unmodified_genomic_sequence} = $non_bisulfite_sequence;
|
|
3653
|
|
3654 ### at this point we can also determine the end position of a read
|
|
3655 $methylation_call_params->{$sequence_identifier}->{end_position} = $methylation_call_params->{$sequence_identifier}->{position}+length($methylation_call_params->{$sequence_identifier}->{bowtie_sequence});
|
|
3656 }
|
|
3657
|
|
3658
|
|
3659 sub extract_corresponding_genomic_sequence_single_end_bowtie2{
|
|
3660 my ($sequence_identifier,$methylation_call_params) = @_;
|
|
3661
|
|
3662 my $MD_tag = $methylation_call_params->{$sequence_identifier}->{mismatch_info};
|
|
3663 my $cigar = $methylation_call_params->{$sequence_identifier}->{CIGAR};
|
|
3664
|
|
3665 ### A bisulfite sequence for 1 location in the genome can theoretically be any of the 4 possible converted strands. We are also giving the
|
|
3666 ### sequence a 'memory' of the conversion we are expecting which we will need later for the methylation call
|
|
3667
|
|
3668 ### the alignment_strand information is needed to determine which strand of the genomic sequence we are comparing the read against,
|
|
3669 ### the read_conversion information is needed to know whether we are looking for C->T or G->A substitutions
|
|
3670 my $alignment_strand;
|
|
3671 my $read_conversion_info;
|
|
3672 my $genome_conversion;
|
|
3673 ### We are now extracting the corresponding genomic sequence, +2 extra bases at the end (or start) so that we can also make a CpG methylation call and
|
|
3674 ### in addition make differential calls for Cs in CHG or CHH context if the C happens to be at the last (or first) position of the actually observed sequence
|
|
3675 my $non_bisulfite_sequence = '';
|
|
3676
|
|
3677 ### Positions in SAM format are 1 based, so we need to subract 1 when getting substrings
|
|
3678 my $pos = $methylation_call_params->{$sequence_identifier}->{position}-1;
|
|
3679
|
|
3680 # parsing CIGAR string
|
|
3681 my @len = split (/\D+/,$cigar); # storing the length per operation
|
|
3682 my @ops = split (/\d+/,$cigar); # storing the operation
|
|
3683 shift @ops; # remove the empty first element
|
|
3684 die "CIGAR string contained a non-matching number of lengths and operations\n" unless (scalar @len == scalar @ops);
|
|
3685
|
|
3686 ### If the sequence aligns best as CT converted reads vs. GA converted genome (OB, index 1) or GA converted reads vs. GA converted genome (CTOB, index 3)
|
|
3687 if ( ($methylation_call_params->{$sequence_identifier}->{index} == 1) or ($methylation_call_params->{$sequence_identifier}->{index} == 3) ){
|
|
3688 ## checking if the substring will be valid or if we can't extract the sequence because we are right at the edge of a chromosome
|
|
3689 unless ( ($pos-2) >= 0){ # exiting with en empty genomic sequence otherwise
|
|
3690 $methylation_call_params->{$sequence_identifier}->{unmodified_genomic_sequence} = $non_bisulfite_sequence;
|
|
3691 return;
|
|
3692 }
|
|
3693 $non_bisulfite_sequence .= substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},$pos-2,2);
|
|
3694 }
|
|
3695 my $indels = 0;
|
|
3696
|
|
3697 foreach (0..$#len){
|
|
3698 if ($ops[$_] eq 'M'){
|
|
3699 #extracting genomic sequence
|
|
3700 $non_bisulfite_sequence .= substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},$pos,$len[$_]);
|
|
3701 # adjusting position
|
|
3702 $pos += $len[$_];
|
|
3703 }
|
|
3704 elsif ($ops[$_] eq 'I'){ # insertion in the read sequence
|
|
3705 # we simply add padding Ns instead of finding genomic sequence. This will not be used to infer methylation calls
|
|
3706 $non_bisulfite_sequence .= 'N' x $len[$_];
|
|
3707 # warn "$non_bisulfite_sequence\n";
|
|
3708 # position doesn't need to be adjusting
|
|
3709 $indels += $len[$_]; # adding this to $indels so we can determine the hemming distance for the SAM output (= single-base substitutions (mismatches, insertions, deletions)
|
|
3710 }
|
|
3711 elsif ($ops[$_] eq 'D'){ # deletion in the read sequence
|
|
3712 # we do not add any genomic sequence but only adjust the position
|
|
3713 $pos += $len[$_];
|
|
3714 $indels += $len[$_]; # adding this to $indels so we can determine the hemming distance for the SAM output (= single-base substitutions (mismatches, insertions, deletions)
|
|
3715 }
|
|
3716 elsif($cigar =~ tr/[NSHPX=]//){ # if these (for standard mapping) illegal characters exist we die
|
|
3717 die "The CIGAR string contained illegal CIGAR operations in addition to 'M', 'I' and 'D': $cigar\n";
|
|
3718 }
|
|
3719 else{
|
|
3720 die "The CIGAR string contained undefined CIGAR operations in addition to 'M', 'I' and 'D': $cigar\n";
|
|
3721 }
|
|
3722 }
|
|
3723
|
|
3724 ### If the sequence aligns best as CT converted reads vs. CT converted genome (OT, index 0) or GA converted reads vs. CT converted genome (CTOT, index 2)
|
|
3725 if ( ($methylation_call_params->{$sequence_identifier}->{index} == 0) or ($methylation_call_params->{$sequence_identifier}->{index} == 2) ){
|
|
3726 ## checking if the substring will be valid or if we can't extract the sequence because we are right at the edge of a chromosome
|
|
3727 unless (length($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}}) >= $pos+2){ # exiting with en empty genomic sequence otherwise
|
|
3728 $methylation_call_params->{$sequence_identifier}->{unmodified_genomic_sequence} = $non_bisulfite_sequence;
|
|
3729 return;
|
|
3730 }
|
|
3731 $non_bisulfite_sequence .= substr ($chromosomes{$methylation_call_params->{$sequence_identifier}->{chromosome}},$pos,2);
|
|
3732 # print "$methylation_call_params->{$sequence_identifier}->{bowtie_sequence}\n$non_bisulfite_sequence\n";
|
|
3733 }
|
|
3734
|
|
3735
|
|
3736
|
|
3737 ### results from CT converted read vs. CT converted genome (+ orientation alignments are reported only)
|
|
3738 if ($methylation_call_params->{$sequence_identifier}->{index} == 0){
|
|
3739 ### [Index 0, sequence originated from (converted) forward strand]
|
|
3740 $counting{CT_CT_count}++;
|
|
3741 $alignment_strand = '+';
|
|
3742 $read_conversion_info = 'CT';
|
|
3743 $genome_conversion = 'CT';
|
|
3744 }
|
|
3745
|
|
3746 ### results from CT converted reads vs. GA converted genome (- orientation alignments are reported only)
|
|
3747 elsif ($methylation_call_params->{$sequence_identifier}->{index} == 1){
|
|
3748 ### [Index 1, sequence originated from (converted) reverse strand]
|
|
3749 $counting{CT_GA_count}++;
|
|
3750 $alignment_strand = '-';
|
|
3751 $read_conversion_info = 'CT';
|
|
3752 $genome_conversion = 'GA';
|
|
3753
|
|
3754 ### reverse complement!
|
|
3755 $non_bisulfite_sequence = reverse_complement($non_bisulfite_sequence);
|
|
3756 }
|
|
3757
|
|
3758 ### results from GA converted reads vs. CT converted genome (- orientation alignments are reported only)
|
|
3759 elsif ($methylation_call_params->{$sequence_identifier}->{index} == 2){
|
|
3760 ### [Index 2, sequence originated from complementary to (converted) forward strand]
|
|
3761 $counting{GA_CT_count}++;
|
|
3762 $alignment_strand = '-';
|
|
3763 $read_conversion_info = 'GA';
|
|
3764 $genome_conversion = 'CT';
|
|
3765
|
|
3766 ### reverse complement!
|
|
3767 $non_bisulfite_sequence = reverse_complement($non_bisulfite_sequence);
|
|
3768 }
|
|
3769
|
|
3770 ### results from GA converted reads vs. GA converted genome (+ orientation alignments are reported only)
|
|
3771 elsif ($methylation_call_params->{$sequence_identifier}->{index} == 3){
|
|
3772 ### [Index 3, sequence originated from complementary to (converted) reverse strand]
|
|
3773 $counting{GA_GA_count}++;
|
|
3774 $alignment_strand = '+';
|
|
3775 $read_conversion_info = 'GA';
|
|
3776 $genome_conversion = 'GA';
|
|
3777
|
|
3778 }
|
|
3779 else{
|
|
3780 die "Too many Bowtie 2 result filehandles\n";
|
|
3781 }
|
|
3782
|
|
3783 $methylation_call_params->{$sequence_identifier}->{alignment_strand} = $alignment_strand;
|
|
3784 $methylation_call_params->{$sequence_identifier}->{read_conversion} = $read_conversion_info;
|
|
3785 $methylation_call_params->{$sequence_identifier}->{genome_conversion} = $genome_conversion;
|
|
3786 $methylation_call_params->{$sequence_identifier}->{unmodified_genomic_sequence} = $non_bisulfite_sequence;
|
|
3787
|
|
3788 ### the end position of a read is stored in $pos
|
|
3789 $methylation_call_params->{$sequence_identifier}->{end_position} = $pos;
|
|
3790 $methylation_call_params->{$sequence_identifier}->{indels} = $indels;
|
|
3791 }
|
|
3792
|
|
3793 ### METHYLATION CALL
|
|
3794
|
|
3795 sub methylation_call{
|
|
3796 my ($identifier,$sequence_actually_observed,$genomic_sequence,$read_conversion) = @_;
|
|
3797 ### splitting both the actually observed sequence and the genomic sequence up into single bases so we can compare them one by one
|
|
3798 my @seq = split(//,$sequence_actually_observed);
|
|
3799 my @genomic = split(//,$genomic_sequence);
|
|
3800 # print join ("\n",$identifier,$sequence_actually_observed,$genomic_sequence,$read_conversion),"\n";
|
|
3801 ### Creating a match-string with different characters for non-cytosine bases (disregarding mismatches here), methyl-Cs or non-methyl Cs in either
|
|
3802 ### CpG, CHH or CHG context
|
|
3803
|
|
3804 #################################################################
|
|
3805 ### . for bases not involving cytosines ###
|
|
3806 ### X for methylated C in CHG context (was protected) ###
|
|
3807 ### x for not methylated C in CHG context (was converted) ###
|
|
3808 ### H for methylated C in CHH context (was protected) ###
|
|
3809 ### h for not methylated C in CHH context (was converted) ###
|
|
3810 ### Z for methylated C in CpG context (was protected) ###
|
|
3811 ### z for not methylated C in CpG context (was converted) ###
|
|
3812 #################################################################
|
|
3813
|
|
3814 my @match =();
|
|
3815 warn "length of \@seq: ",scalar @seq,"\tlength of \@genomic: ",scalar @genomic,"\n" unless (scalar @seq eq (scalar@genomic-2)); ## CHH changed to -2
|
|
3816 my $methyl_CHH_count = 0;
|
|
3817 my $methyl_CHG_count = 0;
|
|
3818 my $methyl_CpG_count = 0;
|
|
3819 my $unmethylated_CHH_count = 0;
|
|
3820 my $unmethylated_CHG_count = 0;
|
|
3821 my $unmethylated_CpG_count = 0;
|
|
3822
|
|
3823 if ($read_conversion eq 'CT'){
|
|
3824 for my $index (0..$#seq) {
|
|
3825 if ($seq[$index] eq $genomic[$index]) {
|
|
3826 ### The residue can only be a C if it was not converted to T, i.e. protected my methylation
|
|
3827 if ($genomic[$index] eq 'C') {
|
|
3828 ### If the residue is a C we want to know if it was in CpG context or in any other context
|
|
3829 my $downstream_base = $genomic[$index+1];
|
|
3830
|
|
3831 if ($downstream_base eq 'G'){
|
|
3832 ++$methyl_CpG_count;
|
|
3833 push @match,'Z'; # protected C, methylated, in CpG context
|
|
3834 }
|
|
3835
|
|
3836 else {
|
|
3837 ### C in not in CpG-context, determining the second downstream base context
|
|
3838 my $second_downstream_base = $genomic[$index+2];
|
|
3839
|
|
3840 if ($second_downstream_base eq 'G'){
|
|
3841 ++$methyl_CHG_count;
|
|
3842 push @match,'X'; # protected C, methylated, in CHG context
|
|
3843 }
|
|
3844 else{
|
|
3845 ++$methyl_CHH_count;
|
|
3846 push @match,'H'; # protected C, methylated, in CHH context
|
|
3847 }
|
|
3848 }
|
|
3849 }
|
|
3850 else {
|
|
3851 push @match, '.';
|
|
3852 }
|
|
3853 }
|
|
3854 elsif ($seq[$index] ne $genomic[$index]) {
|
|
3855 ### for the methylation call we are only interested in mismatches involving cytosines (in the genomic sequence) which were converted into Ts
|
|
3856 ### in the actually observed sequence
|
|
3857 if ($genomic[$index] eq 'C' and $seq[$index] eq 'T') {
|
|
3858 ### If the residue was converted to T we want to know if it was in CpG, CHG or CHH context
|
|
3859 my $downstream_base = $genomic[$index+1];
|
|
3860
|
|
3861 if ($downstream_base eq 'G'){
|
|
3862 ++$unmethylated_CpG_count;
|
|
3863 push @match,'z'; # converted C, not methylated, in CpG context
|
|
3864 }
|
|
3865
|
|
3866 else{
|
|
3867 ### C in not in CpG-context, determining the second downstream base context
|
|
3868 my $second_downstream_base = $genomic[$index+2];
|
|
3869
|
|
3870 if ($second_downstream_base eq 'G'){
|
|
3871 ++$unmethylated_CHG_count;
|
|
3872 push @match,'x'; # converted C, not methylated, in CHG context
|
|
3873 }
|
|
3874 else{
|
|
3875 ++$unmethylated_CHH_count;
|
|
3876 push @match,'h'; # converted C, not methylated, in CHH context
|
|
3877 }
|
|
3878 }
|
|
3879 }
|
|
3880 ### all other mismatches are not of interest for a methylation call
|
|
3881 else {
|
|
3882 push @match,'.';
|
|
3883 }
|
|
3884 }
|
|
3885 else{
|
|
3886 die "There can be only 2 possibilities\n";
|
|
3887 }
|
|
3888 }
|
|
3889 }
|
|
3890 elsif ($read_conversion eq 'GA'){
|
|
3891 # print join ("\n",'***',$identifier,$sequence_actually_observed,$genomic_sequence,$read_conversion,'***'),"\n";
|
|
3892
|
|
3893 for my $index (0..$#seq) {
|
|
3894 if ($seq[$index] eq $genomic[$index+2]) {
|
|
3895 ### The residue can only be a G if the C on the other strand was not converted to T, i.e. protected my methylation
|
|
3896 if ($genomic[$index+2] eq 'G') {
|
|
3897 ### If the residue is a G we want to know if the C on the other strand was in CpG, CHG or CHH context, therefore we need
|
|
3898 ### to look if the base upstream is a C
|
|
3899
|
|
3900 my $upstream_base = $genomic[$index+1];
|
|
3901
|
|
3902 if ($upstream_base eq 'C'){
|
|
3903 ++$methyl_CpG_count;
|
|
3904 push @match,'Z'; # protected C on opposing strand, methylated, in CpG context
|
|
3905 }
|
|
3906
|
|
3907 else{
|
|
3908 ### C in not in CpG-context, determining the second upstream base context
|
|
3909 my $second_upstream_base = $genomic[$index];
|
|
3910
|
|
3911 if ($second_upstream_base eq 'C'){
|
|
3912 ++$methyl_CHG_count;
|
|
3913 push @match,'X'; # protected C on opposing strand, methylated, in CHG context
|
|
3914 }
|
|
3915 else{
|
|
3916 ++$methyl_CHH_count;
|
|
3917 push @match,'H'; # protected C on opposing strand, methylated, in CHH context
|
|
3918 }
|
|
3919 }
|
|
3920 }
|
|
3921 else{
|
|
3922 push @match, '.';
|
|
3923 }
|
|
3924 }
|
|
3925 elsif ($seq[$index] ne $genomic[$index+2]) {
|
|
3926 ### for the methylation call we are only interested in mismatches involving cytosines (in the genomic sequence) which were converted to Ts
|
|
3927 ### on the opposing strand, so G to A conversions in the actually observed sequence
|
|
3928 if ($genomic[$index+2] eq 'G' and $seq[$index] eq 'A') {
|
|
3929 ### If the C residue on the opposing strand was converted to T then we will see an A in the currently observed sequence. We want to know if
|
|
3930 ### the C on the opposing strand was it was in CpG, CHG or CHH context, therefore we need to look one (or two) bases upstream!
|
|
3931
|
|
3932 my $upstream_base = $genomic[$index+1];
|
|
3933
|
|
3934 if ($upstream_base eq 'C'){
|
|
3935 ++$unmethylated_CpG_count;
|
|
3936 push @match,'z'; # converted C on opposing strand, not methylated, in CpG context
|
|
3937 }
|
|
3938
|
|
3939 else{
|
|
3940 ### C in not in CpG-context, determining the second upstream base context
|
|
3941 my $second_upstream_base = $genomic[$index];
|
|
3942
|
|
3943 if ($second_upstream_base eq 'C'){
|
|
3944 ++$unmethylated_CHG_count;
|
|
3945 push @match,'x'; # converted C on opposing strand, not methylated, in CHG context
|
|
3946 }
|
|
3947 else{
|
|
3948 ++$unmethylated_CHH_count;
|
|
3949 push @match,'h'; # converted C on opposing strand, not methylated, in CHH context
|
|
3950 }
|
|
3951 }
|
|
3952 }
|
|
3953 ### all other mismatches are not of interest for a methylation call
|
|
3954 else {
|
|
3955 push @match,'.';
|
|
3956 }
|
|
3957 }
|
|
3958 else{
|
|
3959 die "There can be only 2 possibilities\n";
|
|
3960 }
|
|
3961 }
|
|
3962 }
|
|
3963 else{
|
|
3964 die "Strand conversion info is required to perform a methylation call\n";
|
|
3965 }
|
|
3966
|
|
3967 my $methylation_call = join ("",@match);
|
|
3968
|
|
3969 $counting{total_meCHH_count} += $methyl_CHH_count;
|
|
3970 $counting{total_meCHG_count} += $methyl_CHG_count;
|
|
3971 $counting{total_meCpG_count} += $methyl_CpG_count;
|
|
3972 $counting{total_unmethylated_CHH_count} += $unmethylated_CHH_count;
|
|
3973 $counting{total_unmethylated_CHG_count} += $unmethylated_CHG_count;
|
|
3974 $counting{total_unmethylated_CpG_count} += $unmethylated_CpG_count;
|
|
3975
|
|
3976 # print "\n$sequence_actually_observed\n$genomic_sequence\n",@match,"\n$read_conversion\n\n";
|
|
3977 return $methylation_call;
|
|
3978 }
|
|
3979
|
|
3980 sub read_genome_into_memory{
|
|
3981 ## working directoy
|
|
3982 my $cwd = shift;
|
|
3983 ## reading in and storing the specified genome in the %chromosomes hash
|
|
3984 chdir ($genome_folder) or die "Can't move to $genome_folder: $!";
|
|
3985 print "Now reading in and storing sequence information of the genome specified in: $genome_folder\n\n";
|
|
3986
|
|
3987 my @chromosome_filenames = <*.fa>;
|
|
3988
|
|
3989 ### if there aren't any genomic files with the extension .fa we will look for files with the extension .fasta
|
|
3990 unless (@chromosome_filenames){
|
|
3991 @chromosome_filenames = <*.fasta>;
|
|
3992 }
|
|
3993
|
|
3994 unless (@chromosome_filenames){
|
|
3995 die "The specified genome folder $genome_folder does not contain any sequence files in FastA format (with .fa or .fasta file extensions)\n";
|
|
3996 }
|
|
3997
|
|
3998 foreach my $chromosome_filename (@chromosome_filenames){
|
|
3999
|
|
4000 open (CHR_IN,$chromosome_filename) or die "Failed to read from sequence file $chromosome_filename $!\n";
|
|
4001 ### first line needs to be a fastA header
|
|
4002 my $first_line = <CHR_IN>;
|
|
4003 chomp $first_line;
|
|
4004 $first_line =~ s/\r//;
|
|
4005
|
|
4006 ### Extracting chromosome name from the FastA header
|
|
4007 my $chromosome_name = extract_chromosome_name($first_line);
|
|
4008
|
|
4009 my $sequence;
|
|
4010 while (<CHR_IN>){
|
|
4011 chomp;
|
|
4012 $_ =~ s/\r//;
|
|
4013 if ($_ =~ /^>/){
|
|
4014 ### storing the previous chromosome in the %chromosomes hash, only relevant for Multi-Fasta-Files (MFA)
|
|
4015 if (exists $chromosomes{$chromosome_name}){
|
|
4016 print "chr $chromosome_name (",length $sequence ," bp)\n";
|
|
4017 die "Exiting because chromosome name already exists. Please make sure all chromosomes have a unique name!\n";
|
|
4018 }
|
|
4019 else {
|
|
4020 if (length($sequence) == 0){
|
|
4021 warn "Chromosome $chromosome_name in the multi-fasta file $chromosome_filename did not contain any sequence information!\n";
|
|
4022 }
|
|
4023 print "chr $chromosome_name (",length $sequence ," bp)\n";
|
|
4024 $chromosomes{$chromosome_name} = $sequence;
|
|
4025 }
|
|
4026 ### resetting the sequence variable
|
|
4027 $sequence = '';
|
|
4028 ### setting new chromosome name
|
|
4029 $chromosome_name = extract_chromosome_name($_);
|
|
4030 }
|
|
4031 else{
|
|
4032 $sequence .= uc$_;
|
|
4033 }
|
|
4034 }
|
|
4035
|
|
4036 if (exists $chromosomes{$chromosome_name}){
|
|
4037 print "chr $chromosome_name (",length $sequence ," bp)\t";
|
|
4038 die "Exiting because chromosome name already exists. Please make sure all chromosomes have a unique name.\n";
|
|
4039 }
|
|
4040 else{
|
|
4041 if (length($sequence) == 0){
|
|
4042 warn "Chromosome $chromosome_name in the file $chromosome_filename did not contain any sequence information!\n";
|
|
4043 }
|
|
4044 print "chr $chromosome_name (",length $sequence ," bp)\n";
|
|
4045 $chromosomes{$chromosome_name} = $sequence;
|
|
4046 }
|
|
4047 }
|
|
4048 print "\n";
|
|
4049 chdir $cwd or die "Failed to move to directory $cwd\n";
|
|
4050 }
|
|
4051
|
|
4052 sub extract_chromosome_name {
|
|
4053 ## Bowtie seems to extract the first string after the inition > in the FASTA file, so we are doing this as well
|
|
4054 my $fasta_header = shift;
|
|
4055 if ($fasta_header =~ s/^>//){
|
|
4056 my ($chromosome_name) = split (/\s+/,$fasta_header);
|
|
4057 return $chromosome_name;
|
|
4058 }
|
|
4059 else{
|
|
4060 die "The specified chromosome ($fasta_header) file doesn't seem to be in FASTA format as required!\n";
|
|
4061 }
|
|
4062 }
|
|
4063
|
|
4064 sub reverse_complement{
|
|
4065 my $sequence = shift;
|
|
4066 $sequence =~ tr/CATG/GTAC/;
|
|
4067 $sequence = reverse($sequence);
|
|
4068 return $sequence;
|
|
4069 }
|
|
4070
|
|
4071 sub biTransformFastAFiles {
|
|
4072 my $file = shift;
|
|
4073 my ($dir,$filename);
|
|
4074 if ($file =~ /\//){
|
|
4075 ($dir,$filename) = $file =~ m/(.*\/)(.*)$/;
|
|
4076 }
|
|
4077 else{
|
|
4078 $filename = $file;
|
|
4079 }
|
|
4080
|
|
4081 ### gzipped version of the infile
|
|
4082 if ($file =~ /\.gz$/){
|
|
4083 open (IN,"zcat $file |") or die "Couldn't read from file $file: $!\n";
|
|
4084 }
|
|
4085 else{
|
|
4086 open (IN,$file) or die "Couldn't read from file $file: $!\n";
|
|
4087 }
|
|
4088
|
|
4089 if ($skip){
|
|
4090 warn "Skipping the first $skip reads from $file\n";
|
|
4091 sleep (1);
|
|
4092 }
|
|
4093 if ($upto){
|
|
4094 warn "Processing reads up to sequence no. $upto from $file\n";
|
|
4095 sleep (1);
|
|
4096 }
|
|
4097
|
|
4098 my $C_to_T_infile = my $G_to_A_infile = $filename;
|
|
4099
|
|
4100 if ($gzip){
|
|
4101 $C_to_T_infile =~ s/$/_C_to_T.fa.gz/;
|
|
4102 $G_to_A_infile =~ s/$/_G_to_A.fa.gz/;
|
|
4103 }
|
|
4104 else{
|
|
4105 $C_to_T_infile =~ s/$/_C_to_T.fa/;
|
|
4106 $G_to_A_infile =~ s/$/_G_to_A.fa/;
|
|
4107 }
|
|
4108
|
|
4109 warn "Writing a C -> T converted version of the input file $filename to $temp_dir$C_to_T_infile\n";
|
|
4110
|
|
4111 if ($gzip){
|
|
4112 open (CTOT,"| gzip -c - > ${temp_dir}${C_to_T_infile}") or die "Can't write to file: $!\n";
|
|
4113 }
|
|
4114 else{
|
|
4115 open (CTOT,'>',"$temp_dir$C_to_T_infile") or die "Couldn't write to file $!\n";
|
|
4116 }
|
|
4117
|
|
4118 unless ($directional){
|
|
4119 warn "Writing a G -> A converted version of the input file $filename to $temp_dir$G_to_A_infile\n";
|
|
4120 if ($gzip){
|
|
4121 open (GTOA,"| gzip -c - > ${temp_dir}${G_to_A_infile}") or die "Can't write to file: $!\n";
|
|
4122 }
|
|
4123 else{
|
|
4124 open (GTOA,'>',"$temp_dir$G_to_A_infile") or die "Couldn't write to file $!\n";
|
|
4125 }
|
|
4126 }
|
|
4127
|
|
4128 my $count = 0;
|
|
4129
|
|
4130 while (1){
|
|
4131 my $header = <IN>;
|
|
4132 my $sequence= <IN>;
|
|
4133 last unless ($header and $sequence);
|
|
4134
|
|
4135 $header = fix_IDs($header); # this is to avoid problems with truncated read ID when they contain white spaces
|
|
4136
|
|
4137 ++$count;
|
|
4138
|
|
4139 if ($skip){
|
|
4140 next unless ($count > $skip);
|
|
4141 }
|
|
4142 if ($upto){
|
|
4143 last if ($count > $upto);
|
|
4144 }
|
|
4145
|
|
4146 $sequence = uc$sequence; # make input file case insensitive
|
|
4147
|
|
4148 # detecting if the input file contains tab stops, as this is likely to result in no alignments
|
|
4149 if (index($header,"\t") != -1){
|
|
4150 $seqID_contains_tabs++;
|
|
4151 }
|
|
4152
|
|
4153 ### small check if the sequence seems to be in FastA format
|
|
4154 die "Input file doesn't seem to be in FastA format at sequence $count: $!\n" unless ($header =~ /^>.*/);
|
|
4155
|
|
4156 my $sequence_C_to_T = $sequence;
|
|
4157 $sequence_C_to_T =~ tr/C/T/;
|
|
4158 print CTOT "$header$sequence_C_to_T";
|
|
4159
|
|
4160 unless ($directional){
|
|
4161 my $sequence_G_to_A = $sequence;
|
|
4162 $sequence_G_to_A =~ tr/G/A/;
|
|
4163 print GTOA "$header$sequence_G_to_A";
|
|
4164 }
|
|
4165 }
|
|
4166 close CTOT or die "Failed to close filehandle $!\n";
|
|
4167
|
|
4168 if ($directional){
|
|
4169 warn "\nCreated C -> T converted versions of the FastA file $filename ($count sequences in total)\n\n";
|
|
4170 }
|
|
4171 else{
|
|
4172 close GTOA or die "Failed to close filehandle $!\n";
|
|
4173 warn "\nCreated C -> T as well as G -> A converted versions of the FastA file $filename ($count sequences in total)\n\n";
|
|
4174 }
|
|
4175 return ($C_to_T_infile,$G_to_A_infile);
|
|
4176 }
|
|
4177
|
|
4178 sub biTransformFastAFiles_paired_end {
|
|
4179 my ($file,$read_number) = @_;
|
|
4180
|
|
4181 if ($gzip){
|
|
4182 warn "GZIP compression of temporary files is not supported for paired-end FastA data. Continuing to write uncompressed files\n";
|
|
4183 sleep (2);
|
|
4184 }
|
|
4185
|
|
4186 my ($dir,$filename);
|
|
4187 if ($file =~ /\//){
|
|
4188 ($dir,$filename) = $file =~ m/(.*\/)(.*)$/;
|
|
4189 }
|
|
4190 else{
|
|
4191 $filename = $file;
|
|
4192 }
|
|
4193
|
|
4194 ### gzipped version of the infile
|
|
4195 if ($file =~ /\.gz$/){
|
|
4196 open (IN,"zcat $file |") or die "Couldn't read from file $file: $!\n";
|
|
4197 }
|
|
4198 else{
|
|
4199 open (IN,$file) or die "Couldn't read from file $file: $!\n";
|
|
4200 }
|
|
4201
|
|
4202 if ($skip){
|
|
4203 warn "Skipping the first $skip reads from $file\n";
|
|
4204 sleep (1);
|
|
4205 }
|
|
4206 if ($upto){
|
|
4207 warn "Processing reads up to sequence no. $upto from $file\n";
|
|
4208 sleep (1);
|
|
4209 }
|
|
4210
|
|
4211 my $C_to_T_infile = my $G_to_A_infile = $filename;
|
|
4212 $C_to_T_infile =~ s/$/_C_to_T.fa/;
|
|
4213 $G_to_A_infile =~ s/$/_G_to_A.fa/;
|
|
4214
|
|
4215 if ($directional){
|
|
4216 if ($read_number == 1){
|
|
4217 warn "Writing a C -> T converted version of the input file $filename to $temp_dir$C_to_T_infile\n";
|
|
4218 open (CTOT,'>',"$temp_dir$C_to_T_infile") or die "Couldn't write to file $!\n";
|
|
4219 }
|
|
4220 elsif ($read_number == 2){
|
|
4221 warn "Writing a G -> A converted version of the input file $filename to $temp_dir$G_to_A_infile\n";
|
|
4222 open (GTOA,'>',"$temp_dir$G_to_A_infile") or die "Couldn't write to file $!\n";
|
|
4223 }
|
|
4224 else{
|
|
4225 die "Read number needs to be 1 or 2, but was: $read_number\n\n";
|
|
4226 }
|
|
4227 }
|
|
4228 else{ # all four strand output
|
|
4229 warn "Writing a C -> T converted version of the input file $filename to $temp_dir$C_to_T_infile\n";
|
|
4230 warn "Writing a G -> A converted version of the input file $filename to $temp_dir$G_to_A_infile\n";
|
|
4231 open (CTOT,'>',"$temp_dir$C_to_T_infile") or die "Couldn't write to file $!\n";
|
|
4232 open (GTOA,'>',"$temp_dir$G_to_A_infile") or die "Couldn't write to file $!\n";
|
|
4233 }
|
|
4234
|
|
4235 my $count = 0;
|
|
4236
|
|
4237 while (1){
|
|
4238 my $header = <IN>;
|
|
4239 my $sequence= <IN>;
|
|
4240 last unless ($header and $sequence);
|
|
4241
|
|
4242 $header = fix_IDs($header); # this is to avoid problems with truncated read ID when they contain white spaces
|
|
4243
|
|
4244 ++$count;
|
|
4245
|
|
4246 if ($skip){
|
|
4247 next unless ($count > $skip);
|
|
4248 }
|
|
4249 if ($upto){
|
|
4250 last if ($count > $upto);
|
|
4251 }
|
|
4252
|
|
4253 $sequence = uc$sequence; # make input file case insensitive
|
|
4254
|
|
4255 # detecting if the input file contains tab stops, as this is likely to result in no alignments
|
|
4256 if (index($header,"\t") != -1){
|
|
4257 $seqID_contains_tabs++;
|
|
4258 }
|
|
4259
|
|
4260 ## small check if the sequence seems to be in FastA format
|
|
4261 die "Input file doesn't seem to be in FastA format at sequence $count: $!\n" unless ($header =~ /^>/);
|
|
4262
|
|
4263 if ($read_number == 1){
|
|
4264 if ($bowtie2){
|
|
4265 $header =~ s/$/\/1\/1/;
|
|
4266 }
|
|
4267 else{
|
|
4268 $header =~ s/$/\/1/;
|
|
4269 }
|
|
4270 }
|
|
4271 elsif ($read_number == 2){
|
|
4272 if ($bowtie2){
|
|
4273 $header =~ s/$/\/2\/2/;
|
|
4274 }
|
|
4275 else{
|
|
4276 $header =~ s/$/\/2/;
|
|
4277 }
|
|
4278 }
|
|
4279 else{
|
|
4280 die "Read number needs to be 1 or 2, but was: $read_number\n\n";
|
|
4281 }
|
|
4282 my $sequence_C_to_T = my $sequence_G_to_A = $sequence;
|
|
4283
|
|
4284 $sequence_C_to_T =~ tr/C/T/;
|
|
4285 $sequence_G_to_A =~ tr/G/A/;
|
|
4286
|
|
4287 if ($directional){
|
|
4288
|
|
4289 if ($read_number == 1){
|
|
4290 print CTOT "$header$sequence_C_to_T";
|
|
4291 }
|
|
4292 elsif ($read_number == 2){
|
|
4293 print GTOA "$header$sequence_G_to_A";
|
|
4294 }
|
|
4295 }
|
|
4296 else{
|
|
4297 print CTOT "$header$sequence_C_to_T";
|
|
4298 print GTOA "$header$sequence_G_to_A";
|
|
4299 }
|
|
4300 }
|
|
4301
|
|
4302 if ($directional){
|
|
4303 if ($read_number == 1){
|
|
4304 warn "\nCreated C -> T converted version of the FastA file $filename ($count sequences in total)\n\n";
|
|
4305 }
|
|
4306 else{
|
|
4307 warn "\nCreated G -> A converted version of the FastA file $filename ($count sequences in total)\n\n";
|
|
4308 }
|
|
4309 }
|
|
4310 else{
|
|
4311 warn "\nCreated C -> T as well as G -> A converted versions of the FastA file $filename ($count sequences in total)\n\n";
|
|
4312 }
|
|
4313
|
|
4314 if ($directional){
|
|
4315 if ($read_number == 1){
|
|
4316 return ($C_to_T_infile);
|
|
4317 }
|
|
4318 else{
|
|
4319 return ($G_to_A_infile);
|
|
4320 }
|
|
4321 }
|
|
4322 else{
|
|
4323 return ($C_to_T_infile,$G_to_A_infile);
|
|
4324 }
|
|
4325 }
|
|
4326
|
|
4327
|
|
4328 sub biTransformFastQFiles {
|
|
4329 my $file = shift;
|
|
4330 my ($dir,$filename);
|
|
4331 if ($file =~ /\//){
|
|
4332 ($dir,$filename) = $file =~ m/(.*\/)(.*)$/;
|
|
4333 }
|
|
4334 else{
|
|
4335 $filename = $file;
|
|
4336 }
|
|
4337
|
|
4338 ### gzipped version of the infile
|
|
4339 if ($file =~ /\.gz$/){
|
|
4340 open (IN,"zcat $file |") or die "Couldn't read from file $file: $!\n";
|
|
4341 }
|
|
4342 else{
|
|
4343 open (IN,$file) or die "Couldn't read from file $file: $!\n";
|
|
4344 }
|
|
4345
|
|
4346 if ($skip){
|
|
4347 warn "Skipping the first $skip reads from $file\n";
|
|
4348 sleep (1);
|
|
4349 }
|
|
4350 if ($upto){
|
|
4351 warn "Processing reads up to sequence no. $upto from $file\n";
|
|
4352 sleep (1);
|
|
4353 }
|
|
4354
|
|
4355 my $C_to_T_infile = my $G_to_A_infile = $filename;
|
|
4356
|
|
4357 if ($pbat){ # PBAT-Seq
|
|
4358 if ($gzip){
|
|
4359 $G_to_A_infile =~ s/$/_G_to_A.fastq.gz/;
|
|
4360 }
|
|
4361 else{
|
|
4362 $G_to_A_infile =~ s/$/_G_to_A.fastq/;
|
|
4363 }
|
|
4364
|
|
4365 warn "Writing a G -> A converted version of the input file $filename to $temp_dir$G_to_A_infile\n";
|
|
4366
|
|
4367 if ($gzip){
|
|
4368 open (GTOA,"| gzip -c - > ${temp_dir}${G_to_A_infile}") or die "Can't write to file: $!\n";
|
|
4369 }
|
|
4370 else{
|
|
4371 open (GTOA,'>',"$temp_dir$G_to_A_infile") or die "Couldn't write to file $!\n";
|
|
4372 }
|
|
4373 }
|
|
4374 else{ # directional or non-directional
|
|
4375 if ($gzip){
|
|
4376 $C_to_T_infile =~ s/$/_C_to_T.fastq.gz/;
|
|
4377 }
|
|
4378 else{
|
|
4379 $C_to_T_infile =~ s/$/_C_to_T.fastq/;
|
|
4380 }
|
|
4381
|
|
4382 warn "Writing a C -> T converted version of the input file $filename to $temp_dir$C_to_T_infile\n";
|
|
4383
|
|
4384 if ($gzip){
|
|
4385 open (CTOT,"| gzip -c - > ${temp_dir}${C_to_T_infile}") or die "Can't write to file: $!\n";
|
|
4386 }
|
|
4387 else{
|
|
4388 open (CTOT,'>',"$temp_dir$C_to_T_infile") or die "Couldn't write to file $!\n"; # uncompressed option
|
|
4389 }
|
|
4390
|
|
4391 unless ($directional){
|
|
4392 if ($gzip){
|
|
4393 $G_to_A_infile =~ s/$/_G_to_A.fastq.gz/;
|
|
4394 }
|
|
4395 else{
|
|
4396 $G_to_A_infile =~ s/$/_G_to_A.fastq/;
|
|
4397 }
|
|
4398
|
|
4399 warn "Writing a G -> A converted version of the input file $filename to $temp_dir$G_to_A_infile\n";
|
|
4400
|
|
4401 if ($gzip){
|
|
4402 open (GTOA,"| gzip -c - > ${temp_dir}${G_to_A_infile}") or die "Can't write to file: $!\n";
|
|
4403 }
|
|
4404 else{
|
|
4405 open (GTOA,'>',"$temp_dir$G_to_A_infile") or die "Couldn't write to file $!\n";
|
|
4406 }
|
|
4407 }
|
|
4408 }
|
|
4409
|
|
4410 my $count = 0;
|
|
4411 while (1){
|
|
4412 my $identifier = <IN>;
|
|
4413 my $sequence = <IN>;
|
|
4414 my $identifier2 = <IN>;
|
|
4415 my $quality_score = <IN>;
|
|
4416 last unless ($identifier and $sequence and $identifier2 and $quality_score);
|
|
4417
|
|
4418 $identifier = fix_IDs($identifier); # this is to avoid problems with truncated read ID when they contain white spaces
|
|
4419
|
|
4420 ++$count;
|
|
4421
|
|
4422 if ($skip){
|
|
4423 next unless ($count > $skip);
|
|
4424 }
|
|
4425 if ($upto){
|
|
4426 last if ($count > $upto);
|
|
4427 }
|
|
4428
|
|
4429 $sequence = uc$sequence; # make input file case insensitive
|
|
4430
|
|
4431 # detecting if the input file contains tab stops, as this is likely to result in no alignments
|
|
4432 if (index($identifier,"\t") != -1){
|
|
4433 $seqID_contains_tabs++;
|
|
4434 }
|
|
4435
|
|
4436 ## small check if the sequence file appears to be a FastQ file
|
|
4437 if ($count == 1){
|
|
4438 if ($identifier !~ /^\@/ or $identifier2 !~ /^\+/){
|
|
4439 die "Input file doesn't seem to be in FastQ format at sequence $count: $!\n";
|
|
4440 }
|
|
4441 }
|
|
4442
|
|
4443 if ($pbat){
|
|
4444 my $sequence_G_to_A = $sequence;
|
|
4445 $sequence_G_to_A =~ tr/G/A/;
|
|
4446 print GTOA join ('',$identifier,$sequence_G_to_A,$identifier2,$quality_score);
|
|
4447 }
|
|
4448 else{ # directional or non-directional
|
|
4449 my $sequence_C_to_T = $sequence;
|
|
4450 $sequence_C_to_T =~ tr/C/T/;
|
|
4451 print CTOT join ('',$identifier,$sequence_C_to_T,$identifier2,$quality_score);
|
|
4452
|
|
4453 unless ($directional){
|
|
4454 my $sequence_G_to_A = $sequence;
|
|
4455 $sequence_G_to_A =~ tr/G/A/;
|
|
4456 print GTOA join ('',$identifier,$sequence_G_to_A,$identifier2,$quality_score);
|
|
4457 }
|
|
4458 }
|
|
4459 }
|
|
4460
|
|
4461 if ($directional){
|
|
4462 close CTOT or die "Failed to close filehandle $!\n";
|
|
4463 warn "\nCreated C -> T converted version of the FastQ file $filename ($count sequences in total)\n\n";
|
|
4464 }
|
|
4465 elsif($pbat){
|
|
4466 warn "\nCreated G -> A converted version of the FastQ file $filename ($count sequences in total)\n\n";
|
|
4467 close GTOA or die "Failed to close filehandle $!\n";
|
|
4468 return ($G_to_A_infile);
|
|
4469 }
|
|
4470 else{
|
|
4471 close CTOT or die "Failed to close filehandle $!\n";
|
|
4472 close GTOA or die "Failed to close filehandle $!\n";
|
|
4473 warn "\nCreated C -> T as well as G -> A converted versions of the FastQ file $filename ($count sequences in total)\n\n";
|
|
4474 }
|
|
4475
|
|
4476 return ($C_to_T_infile,$G_to_A_infile);
|
|
4477 }
|
|
4478
|
|
4479 sub biTransformFastQFiles_paired_end {
|
|
4480 my ($file,$read_number) = @_;
|
|
4481 my ($dir,$filename);
|
|
4482
|
|
4483 if ($file =~ /\//){
|
|
4484 ($dir,$filename) = $file =~ m/(.*\/)(.*)$/;
|
|
4485 }
|
|
4486 else{
|
|
4487 $filename = $file;
|
|
4488 }
|
|
4489
|
|
4490 ### gzipped version of the infile
|
|
4491 if ($file =~ /\.gz$/){
|
|
4492 open (IN,"zcat $file |") or die "Couldn't read from file $file: $!\n";
|
|
4493 }
|
|
4494 else{
|
|
4495 open (IN,$file) or die "Couldn't read from file $file: $!\n";
|
|
4496 }
|
|
4497
|
|
4498 if ($skip){
|
|
4499 warn "Skipping the first $skip reads from $file\n";
|
|
4500 sleep (1);
|
|
4501 }
|
|
4502 if ($upto){
|
|
4503 warn "Processing reads up to sequence no. $upto from $file\n";
|
|
4504 sleep (1);
|
|
4505 }
|
|
4506
|
|
4507 my $C_to_T_infile = my $G_to_A_infile = $filename;
|
|
4508
|
|
4509 if ($gzip){
|
|
4510 $C_to_T_infile =~ s/$/_C_to_T.fastq.gz/;
|
|
4511 $G_to_A_infile =~ s/$/_G_to_A.fastq.gz/;
|
|
4512 }
|
|
4513 else{
|
|
4514 $C_to_T_infile =~ s/$/_C_to_T.fastq/;
|
|
4515 $G_to_A_infile =~ s/$/_G_to_A.fastq/;
|
|
4516 }
|
|
4517
|
|
4518 if ($directional){
|
|
4519 if ($read_number == 1){
|
|
4520 warn "Writing a C -> T converted version of the input file $filename to $temp_dir$C_to_T_infile\n";
|
|
4521 if ($gzip){
|
|
4522 open (CTOT,"| gzip -c - > ${temp_dir}${C_to_T_infile}") or die "Can't write to file: $!\n";
|
|
4523 }
|
|
4524 else{
|
|
4525 open (CTOT,'>',"$temp_dir$C_to_T_infile") or die "Couldn't write to file $!\n";
|
|
4526 }
|
|
4527 }
|
|
4528 elsif ($read_number == 2){
|
|
4529 warn "Writing a G -> A converted version of the input file $filename to $temp_dir$G_to_A_infile\n";
|
|
4530 if ($gzip){
|
|
4531 open (GTOA,"| gzip -c - > ${temp_dir}${G_to_A_infile}") or die "Can't write to file: $!\n";
|
|
4532 }
|
|
4533 else{
|
|
4534 open (GTOA,'>',"$temp_dir$G_to_A_infile") or die "Couldn't write to file $!\n";
|
|
4535 }
|
|
4536 }
|
|
4537 else{
|
|
4538 die "Read number needs to be 1 or 2, but was $read_number!\n\n";
|
|
4539 }
|
|
4540 }
|
|
4541 else{
|
|
4542 warn "Writing a C -> T converted version of the input file $filename to $temp_dir$C_to_T_infile\n";
|
|
4543 warn "Writing a G -> A converted version of the input file $filename to $temp_dir$G_to_A_infile\n";
|
|
4544 if ($gzip){
|
|
4545 open (CTOT,"| gzip -c - > ${temp_dir}${C_to_T_infile}") or die "Can't write to file: $!\n";
|
|
4546 open (GTOA,"| gzip -c - > ${temp_dir}${G_to_A_infile}") or die "Can't write to file: $!\n";
|
|
4547 }
|
|
4548 else{
|
|
4549 open (CTOT,'>',"$temp_dir$C_to_T_infile") or die "Couldn't write to file $!\n";
|
|
4550 open (GTOA,'>',"$temp_dir$G_to_A_infile") or die "Couldn't write to file $!\n";
|
|
4551 }
|
|
4552 }
|
|
4553
|
|
4554 my $count = 0;
|
|
4555 while (1){
|
|
4556 my $identifier = <IN>;
|
|
4557 my $sequence = <IN>;
|
|
4558 my $identifier2 = <IN>;
|
|
4559 my $quality_score = <IN>;
|
|
4560 last unless ($identifier and $sequence and $identifier2 and $quality_score);
|
|
4561 ++$count;
|
|
4562
|
|
4563 $identifier = fix_IDs($identifier); # this is to avoid problems with truncated read ID when they contain white spaces
|
|
4564
|
|
4565 if ($skip){
|
|
4566 next unless ($count > $skip);
|
|
4567 }
|
|
4568 if ($upto){
|
|
4569 last if ($count > $upto);
|
|
4570 }
|
|
4571
|
|
4572 $sequence= uc$sequence; # make input file case insensitive
|
|
4573
|
|
4574 ## small check if the sequence file appears to be a FastQ file
|
|
4575 if ($count == 1){
|
|
4576 if ($identifier !~ /^\@/ or $identifier2 !~ /^\+/){
|
|
4577 die "Input file doesn't seem to be in FastQ format at sequence $count: $!\n";
|
|
4578 }
|
|
4579 }
|
|
4580 my $sequence_C_to_T = my $sequence_G_to_A = $sequence;
|
|
4581
|
|
4582 if ($read_number == 1){
|
|
4583 if ($bowtie2){
|
|
4584 $identifier =~ s/$/\/1\/1/;
|
|
4585 }
|
|
4586 else{
|
|
4587 $identifier =~ s/$/\/1/;
|
|
4588 }
|
|
4589 }
|
|
4590 elsif ($read_number == 2){
|
|
4591 if ($bowtie2){
|
|
4592 $identifier =~ s/$/\/2\/2/;
|
|
4593 }
|
|
4594 else{
|
|
4595 $identifier =~ s/$/\/2/;
|
|
4596 }
|
|
4597 }
|
|
4598 else{
|
|
4599 die "Read number needs to be 1 or 2\n";
|
|
4600 }
|
|
4601
|
|
4602 $sequence_C_to_T =~ tr/C/T/;
|
|
4603 $sequence_G_to_A =~ tr/G/A/;
|
|
4604
|
|
4605 if ($directional){
|
|
4606 if ($read_number == 1){
|
|
4607 print CTOT join ('',$identifier,$sequence_C_to_T,$identifier2,$quality_score);
|
|
4608 }
|
|
4609 else{
|
|
4610 print GTOA join ('',$identifier,$sequence_G_to_A,$identifier2,$quality_score);
|
|
4611 }
|
|
4612 }
|
|
4613 else{
|
|
4614 print CTOT join ('',$identifier,$sequence_C_to_T,$identifier2,$quality_score);
|
|
4615 print GTOA join ('',$identifier,$sequence_G_to_A,$identifier2,$quality_score);
|
|
4616 }
|
|
4617 }
|
|
4618
|
|
4619 if ($directional){
|
|
4620 if ($read_number == 1){
|
|
4621 warn "\nCreated C -> T converted version of the FastQ file $filename ($count sequences in total)\n\n";
|
|
4622 }
|
|
4623 else{
|
|
4624 warn "\nCreated G -> A converted version of the FastQ file $filename ($count sequences in total)\n\n";
|
|
4625 }
|
|
4626 }
|
|
4627 else{
|
|
4628 warn "\nCreated C -> T as well as G -> A converted versions of the FastQ file $filename ($count sequences in total)\n\n";
|
|
4629 }
|
|
4630 if ($directional){
|
|
4631 if ($read_number == 1){
|
|
4632 close CTOT or die "Failed to close filehandle $!\n";
|
|
4633 return ($C_to_T_infile);
|
|
4634 }
|
|
4635 else{
|
|
4636 close GTOA or die "Failed to close filehandle $!\n";
|
|
4637 return ($G_to_A_infile);
|
|
4638 }
|
|
4639 }
|
|
4640 else{
|
|
4641 close CTOT or die "Failed to close filehandle $!\n";
|
|
4642 close GTOA or die "Failed to close filehandle $!\n";
|
|
4643 return ($C_to_T_infile,$G_to_A_infile);
|
|
4644 }
|
|
4645 }
|
|
4646
|
|
4647
|
|
4648 ### SPECIAL BOWTIE 1 PAIRED-END FORMAT FOR GZIPPED OUTPUT FILES
|
|
4649
|
|
4650 sub biTransformFastQFiles_paired_end_bowtie1_gzip {
|
|
4651 my ($file_1,$file_2) = @_;
|
|
4652 my ($dir,$filename);
|
|
4653
|
|
4654 if ($file_1 =~ /\//){
|
|
4655 ($dir,$filename) = $file_1 =~ m/(.*\/)(.*)$/;
|
|
4656 }
|
|
4657 else{
|
|
4658 $filename = $file_1;
|
|
4659 }
|
|
4660
|
|
4661 ### gzipped version of infile 1
|
|
4662 if ($file_1 =~ /\.gz$/){
|
|
4663 open (IN_1,"zcat $file_1 |") or die "Couldn't read from file $file_1: $!\n";
|
|
4664 }
|
|
4665 else{
|
|
4666 open (IN_1,$file_1) or die "Couldn't read from file $file_1: $!\n";
|
|
4667 }
|
|
4668 ### gzipped version of infile 2
|
|
4669 if ($file_2 =~ /\.gz$/){
|
|
4670 open (IN_2,"zcat $file_2 |") or die "Couldn't read from file $file_2: $!\n";
|
|
4671 }
|
|
4672 else{
|
|
4673 open (IN_2,$file_2) or die "Couldn't read from file $file_2: $!\n";
|
|
4674 }
|
|
4675
|
|
4676
|
|
4677 if ($skip){
|
|
4678 warn "Skipping the first $skip reads from $file_1 and $file_2\n";
|
|
4679 sleep (1);
|
|
4680 }
|
|
4681 if ($upto){
|
|
4682 warn "Processing reads up to sequence no. $upto from $file_1 and $file_2\n";
|
|
4683 sleep (1);
|
|
4684 }
|
|
4685
|
|
4686 my $CT_plus_GA_infile = my $GA_plus_CT_infile = $filename;
|
|
4687
|
|
4688 $CT_plus_GA_infile =~ s/$/.CT_plus_GA.fastq.gz/;
|
|
4689 $GA_plus_CT_infile =~ s/$/.GA_plus_CT.fastq.gz/;
|
|
4690
|
|
4691 warn "Writing a C -> T converted version of $file_1 and a G -> A converted version of $file_2 to $temp_dir$CT_plus_GA_infile\n";
|
|
4692 open (CTPLUSGA,"| gzip -c - > ${temp_dir}${CT_plus_GA_infile}") or die "Can't write to file: $!\n";
|
|
4693 # open (CTPLUSGA,'>',"$temp_dir$CT_plus_GA_infile") or die "Couldn't write to file $!\n";
|
|
4694
|
|
4695 unless ($directional){
|
|
4696 print "Writing a G -> A converted version of $file_1 and a C -> T converted version of $file_2 to $temp_dir$GA_plus_CT_infile\n";
|
|
4697 open (GAPLUSCT,"| gzip -c - > ${temp_dir}${GA_plus_CT_infile}") or die "Can't write to file: $!\n";
|
|
4698 }
|
|
4699
|
|
4700 ### for Bowtie 1 we need to write a single gzipped file with 1 line per pair of sequences in the the following format:
|
|
4701 ### <seq-ID> <sequence #1 mate> <quality #1 mate> <sequence #2 mate> <quality #2 mate>
|
|
4702
|
|
4703 my $count = 0;
|
|
4704 while (1){
|
|
4705 my $identifier_1 = <IN_1>;
|
|
4706 my $sequence_1 = <IN_1>;
|
|
4707 my $identifier2_1 = <IN_1>;
|
|
4708 my $quality_score_1 = <IN_1>;
|
|
4709
|
|
4710 my $identifier_2 = <IN_2>;
|
|
4711 my $sequence_2 = <IN_2>;
|
|
4712 my $identifier2_2 = <IN_2>;
|
|
4713 my $quality_score_2 = <IN_2>;
|
|
4714
|
|
4715 last unless ($identifier_1 and $sequence_1 and $identifier2_1 and $quality_score_1 and $identifier_2 and $sequence_2 and $identifier2_2 and $quality_score_2);
|
|
4716
|
|
4717 ++$count;
|
|
4718
|
|
4719 ## small check if the sequence file appears to be a FastQ file
|
|
4720 if ($count == 1){
|
|
4721 if ($identifier_1 !~ /^\@/ or $identifier2_1 !~ /^\+/){
|
|
4722 die "Input file 1 doesn't seem to be in FastQ format at sequence $count: $!\n";
|
|
4723 }
|
|
4724 if ($identifier_2 !~ /^\@/ or $identifier2_2 !~ /^\+/){
|
|
4725 die "Input file 2 doesn't seem to be in FastQ format at sequence $count: $!\n";
|
|
4726 }
|
|
4727 }
|
|
4728
|
|
4729 $identifier_1 = fix_IDs($identifier_1); # this is to avoid problems with truncated read ID when they contain white spaces
|
|
4730 chomp $identifier_1;
|
|
4731 chomp $sequence_1;
|
|
4732 chomp $sequence_2;
|
|
4733 chomp $quality_score_1;
|
|
4734 chomp $quality_score_2;
|
|
4735
|
|
4736 $identifier_1 =~ s/^\@//;
|
|
4737 $identifier_1 =~ s/$/\/1/; #adding an extra /1 to the end which is being removed by Bowtie otherwise (which leads to no sequences alignments whatsoever)
|
|
4738
|
|
4739 if ($skip){
|
|
4740 next unless ($count > $skip);
|
|
4741 }
|
|
4742 if ($upto){
|
|
4743 last if ($count > $upto);
|
|
4744 }
|
|
4745
|
|
4746 $sequence_1 = uc$sequence_1; # make input file 1 case insensitive
|
|
4747 $sequence_2 = uc$sequence_2; # make input file 2 case insensitive
|
|
4748
|
|
4749 # print "$identifier_1\t$sequence_1\t$quality_score_1\t$sequence_2\t$quality_score_2\n";
|
|
4750 my $sequence_1_C_to_T = $sequence_1;
|
|
4751 my $sequence_2_G_to_A = $sequence_2;
|
|
4752 $sequence_1_C_to_T =~ tr/C/T/;
|
|
4753 $sequence_2_G_to_A =~ tr/G/A/;
|
|
4754
|
|
4755 print CTPLUSGA "$identifier_1\t$sequence_1_C_to_T\t$quality_score_1\t$sequence_2_G_to_A\t$quality_score_2\n";
|
|
4756
|
|
4757 unless ($directional){
|
|
4758 my $sequence_1_G_to_A = $sequence_1;
|
|
4759 my $sequence_2_C_to_T = $sequence_2;
|
|
4760 $sequence_1_G_to_A =~ tr/G/A/;
|
|
4761 $sequence_2_C_to_T =~ tr/C/T/;
|
|
4762 print GAPLUSCT "$identifier_1\t$sequence_1_G_to_A\t$quality_score_1\t$sequence_2_C_to_T\t$quality_score_2\n";
|
|
4763 }
|
|
4764 }
|
|
4765
|
|
4766 close CTPLUSGA or die "Couldn't close filehandle\n";
|
|
4767 warn "\nCreated C -> T converted version of FastQ file '$file_1' and G -> A converted version of FastQ file '$file_2' ($count sequences in total)\n";
|
|
4768
|
|
4769 if ($directional){
|
|
4770 warn "\n";
|
|
4771 return ($CT_plus_GA_infile);
|
|
4772 }
|
|
4773 else{
|
|
4774 close GAPLUSCT or die "Couldn't close filehandle\n";
|
|
4775 warn "Created G -> A converted version of FastQ file '$file_1' and C -> T converted version of FastQ file '$file_2' ($count sequences in total)\n\n";
|
|
4776 return ($CT_plus_GA_infile,$GA_plus_CT_infile);
|
|
4777 }
|
|
4778 }
|
|
4779
|
|
4780
|
|
4781 sub fix_IDs{
|
|
4782 my $id = shift;
|
|
4783 $id =~ s/[ \t]+/_/g; # replace spaces or tabs with underscores
|
|
4784 return $id;
|
|
4785 }
|
|
4786
|
|
4787 sub ensure_sensical_alignment_orientation_single_end{
|
|
4788 my $index = shift; # index number if the sequence produced an alignment
|
|
4789 my $strand = shift;
|
|
4790 ### setting $orientation to 1 if it is in the correct orientation, and leave it 0 if it is the nonsensical wrong one
|
|
4791 my $orientation = 0;
|
|
4792 ##############################################################################################################
|
|
4793 ## FORWARD converted read against FORWARD converted genome (read: C->T.....C->T.. genome:C->T.......C->T)
|
|
4794 ## here we only want reads in the forward (+) orientation
|
|
4795 if ($fhs[$index]->{name} eq 'CTreadCTgenome') {
|
|
4796 ### if the alignment is (+) we count it, and return 1 for a correct orientation
|
|
4797 if ($strand eq '+') {
|
|
4798 $fhs[$index]->{seen}++;
|
|
4799 $orientation = 1;
|
|
4800 return $orientation;
|
|
4801 }
|
|
4802 ### if the orientation equals (-) the alignment is nonsensical
|
|
4803 elsif ($strand eq '-') {
|
|
4804 $fhs[$index]->{wrong_strand}++;
|
|
4805 return $orientation;
|
|
4806 }
|
|
4807 }
|
|
4808 ###############################################################################################################
|
|
4809 ## FORWARD converted read against reverse converted genome (read: C->T.....C->T.. genome: G->A.......G->A)
|
|
4810 ## here we only want reads in the forward (-) orientation
|
|
4811 elsif ($fhs[$index]->{name} eq 'CTreadGAgenome') {
|
|
4812 ### if the alignment is (-) we count it and return 1 for a correct orientation
|
|
4813 if ($strand eq '-') {
|
|
4814 $fhs[$index]->{seen}++;
|
|
4815 $orientation = 1;
|
|
4816 return $orientation;
|
|
4817 }
|
|
4818 ### if the orientation equals (+) the alignment is nonsensical
|
|
4819 elsif ($strand eq '+') {
|
|
4820 $fhs[$index]->{wrong_strand}++;
|
|
4821 return $orientation;
|
|
4822 }
|
|
4823 }
|
|
4824 ###############################################################################################################
|
|
4825 ## Reverse converted read against FORWARD converted genome (read: G->A.....G->A.. genome: C->T.......C->T)
|
|
4826 ## here we only want reads in the forward (-) orientation
|
|
4827 elsif ($fhs[$index]->{name} eq 'GAreadCTgenome') {
|
|
4828 ### if the alignment is (-) we count it and return 1 for a correct orientation
|
|
4829 if ($strand eq '-') {
|
|
4830 $fhs[$index]->{seen}++;
|
|
4831 $orientation = 1;
|
|
4832 return $orientation;
|
|
4833 }
|
|
4834 ### if the orientation equals (+) the alignment is nonsensical
|
|
4835 elsif ($strand eq '+') {
|
|
4836 $fhs[$index]->{wrong_strand}++;
|
|
4837 return $orientation;
|
|
4838 }
|
|
4839 }
|
|
4840 ###############################################################################################################
|
|
4841 ## Reverse converted read against reverse converted genome (read: G->A.....G->A.. genome: G->A.......G->A)
|
|
4842 ## here we only want reads in the forward (+) orientation
|
|
4843 elsif ($fhs[$index]->{name} eq 'GAreadGAgenome') {
|
|
4844 ### if the alignment is (+) we count it and return 1 for a correct orientation
|
|
4845 if ($strand eq '+') {
|
|
4846 $fhs[$index]->{seen}++;
|
|
4847 $orientation = 1;
|
|
4848 return $orientation;
|
|
4849 }
|
|
4850 ### if the orientation equals (-) the alignment is nonsensical
|
|
4851 elsif ($strand eq '-') {
|
|
4852 $fhs[$index]->{wrong_strand}++;
|
|
4853 return $orientation;
|
|
4854 }
|
|
4855 } else{
|
|
4856 die "One of the above conditions must be true\n";
|
|
4857 }
|
|
4858 }
|
|
4859
|
|
4860 sub ensure_sensical_alignment_orientation_paired_ends{
|
|
4861 my ($index,$id_1,$strand_1,$id_2,$strand_2) = @_; # index number if the sequence produced an alignment
|
|
4862 ### setting $orientation to 1 if it is in the correct orientation, and leave it 0 if it is the nonsensical wrong one
|
|
4863 my $orientation = 0;
|
|
4864 ##############################################################################################################
|
|
4865 ## [Index 0, sequence originated from (converted) forward strand]
|
|
4866 ## CT converted read 1
|
|
4867 ## GA converted read 2
|
|
4868 ## CT converted genome
|
|
4869 ## here we only want read 1 in (+) orientation and read 2 in (-) orientation
|
|
4870 if ($fhs[$index]->{name} eq 'CTread1GAread2CTgenome') {
|
|
4871 ### if the paired-end alignment is read1 (+) and read2 (-) we count it, and return 1 for a correct orientation
|
|
4872 if ($id_1 =~ /1$/ and $strand_1 eq '+' and $id_2 =~ /2$/ and $strand_2 eq '-') {
|
|
4873 $fhs[$index]->{seen}++;
|
|
4874 $orientation = 1;
|
|
4875 return $orientation;
|
|
4876 }
|
|
4877 ### if the read 2 is in (+) orientation and read 1 in (-) the alignment is nonsensical
|
|
4878 elsif ($id_1 =~ /2$/ and $strand_1 eq '+' and $id_2 =~ /1$/ and $strand_2 eq '-') {
|
|
4879 $fhs[$index]->{wrong_strand}++;
|
|
4880 return $orientation;
|
|
4881 }
|
|
4882 else{
|
|
4883 die "id1: $id_1\tid2: $id_2\tThis should be impossible\n";
|
|
4884 }
|
|
4885 }
|
|
4886 ###############################################################################################################
|
|
4887 ## [Index 1, sequence originated from (converted) reverse strand]
|
|
4888 ## GA converted read 1
|
|
4889 ## CT converted read 2
|
|
4890 ## GA converted genome
|
|
4891 ## here we only want read 1 in (+) orientation and read 2 in (-) orientation
|
|
4892 elsif ($fhs[$index]->{name} eq 'GAread1CTread2GAgenome') {
|
|
4893 ### if the paired-end alignment is read1 (+) and read2 (-) we count it, and return 1 for a correct orientation
|
|
4894 if ($id_1 =~ /1$/ and $strand_1 eq '+' and $id_2 =~ /2$/ and $strand_2 eq '-') {
|
|
4895 $fhs[$index]->{seen}++;
|
|
4896 $orientation = 1;
|
|
4897 return $orientation;
|
|
4898 }
|
|
4899 ### if the read 2 is in (+) orientation and read 1 in (-) the alignment is nonsensical
|
|
4900 elsif ($id_1 =~ /2$/ and $strand_1 eq '+' and $id_2 =~ /1$/ and $strand_2 eq '-') {
|
|
4901 $fhs[$index]->{wrong_strand}++;
|
|
4902 return $orientation;
|
|
4903 }
|
|
4904 else{
|
|
4905 die "id1: $id_1\tid2: $id_2\tThis should be impossible\n";
|
|
4906 }
|
|
4907 }
|
|
4908 ###############################################################################################################
|
|
4909 ## [Index 2, sequence originated from complementary to (converted) forward strand]
|
|
4910 ## GA converted read 1
|
|
4911 ## CT converted read 2
|
|
4912 ## CT converted genome
|
|
4913 ## here we only want read 1 in (-) orientation and read 2 in (+) orientation
|
|
4914 elsif ($fhs[$index]->{name} eq 'GAread1CTread2CTgenome') {
|
|
4915 ### if the paired-end alignment is read1 (-) and read2 (+) we count it, and return 1 for a correct orientation
|
|
4916 if ($id_1 =~ /2$/ and $strand_1 eq '+' and $id_2 =~ /1$/ and $strand_2 eq '-') {
|
|
4917 $fhs[$index]->{seen}++;
|
|
4918 $orientation = 1;
|
|
4919 return $orientation;
|
|
4920 }
|
|
4921 ### if the read 2 is in (+) orientation and read 1 in (-) the alignment is nonsensical
|
|
4922 elsif ($id_1 =~ /1$/ and $strand_1 eq '+' and $id_2 =~ /2$/ and $strand_2 eq '-') {
|
|
4923 $fhs[$index]->{wrong_strand}++;
|
|
4924 return $orientation;
|
|
4925 }
|
|
4926 else{
|
|
4927 die "id1: $id_1\tid2: $id_2\tThis should be impossible\n";
|
|
4928 }
|
|
4929 }
|
|
4930 ###############################################################################################################
|
|
4931 ## [Index 3, sequence originated from complementary to (converted) reverse strand]
|
|
4932 ## CT converted read 1
|
|
4933 ## GA converted read 2
|
|
4934 ## GA converted genome
|
|
4935 ## here we only want read 1 in (+) orientation and read 2 in (-) orientation
|
|
4936 elsif ($fhs[$index]->{name} eq 'CTread1GAread2GAgenome') {
|
|
4937 ### if the paired-end alignment is read1 (-) and read2 (+) we count it, and return 1 for a correct orientation
|
|
4938 if ($id_1 =~ /2$/ and $strand_1 eq '+' and $id_2 =~ /1$/ and $strand_2 eq '-') {
|
|
4939 $fhs[$index]->{seen}++;
|
|
4940 $orientation = 1;
|
|
4941 return $orientation;
|
|
4942 }
|
|
4943 ### if the read 2 is in (+) orientation and read 1 in (-) the alignment is nonsensical
|
|
4944 elsif ($id_1 =~ /1$/ and $strand_1 eq '+' and $id_2 =~ /2$/ and $strand_2 eq '-') {
|
|
4945 $fhs[$index]->{wrong_strand}++;
|
|
4946 return $orientation;
|
|
4947 }
|
|
4948 else{
|
|
4949 die "id1: $id_1\tid2: $id_2\tThis should be impossible\n";
|
|
4950 }
|
|
4951 }
|
|
4952 else{
|
|
4953 die "One of the above conditions must be true\n";
|
|
4954 }
|
|
4955 }
|
|
4956
|
|
4957 #####################################################################################################################################################
|
|
4958
|
|
4959 ### Bowtie 1 (default) | PAIRED-END | FASTA
|
|
4960
|
|
4961 sub paired_end_align_fragments_to_bisulfite_genome_fastA {
|
|
4962
|
|
4963 my ($C_to_T_infile_1,$G_to_A_infile_1,$C_to_T_infile_2,$G_to_A_infile_2) = @_;
|
|
4964
|
|
4965 if ($directional){
|
|
4966 warn "Input files are $C_to_T_infile_1 and $G_to_A_infile_2 (FastA)\n";
|
|
4967 }
|
|
4968 else{
|
|
4969 warn "Input files are $C_to_T_infile_1 and $G_to_A_infile_1 and $C_to_T_infile_2 and $G_to_A_infile_2 (FastA)\n";
|
|
4970 }
|
|
4971
|
|
4972 ## Now starting up to 4 instances of Bowtie feeding in the converted sequence files and reading in the first line of the bowtie output, and storing it in the
|
|
4973 ## data structure above
|
|
4974 if ($directional){
|
|
4975 warn "Now running 2 instances of Bowtie against the bisulfite genome of $genome_folder with the specified options: $bowtie_options\n\n";
|
|
4976 }
|
|
4977 else{
|
|
4978 warn "Now running 4 individual instances of Bowtie against the bisulfite genome of $genome_folder with the specified options: $bowtie_options\n\n";
|
|
4979 }
|
|
4980
|
|
4981 foreach my $fh (@fhs) {
|
|
4982
|
|
4983 if ($directional){
|
|
4984 unless ($fh->{inputfile_1}){
|
|
4985 $fh->{last_seq_id} = undef;
|
|
4986 $fh->{last_line_1} = undef;
|
|
4987 $fh->{last_line_2} = undef;
|
|
4988 next;
|
|
4989 }
|
|
4990 }
|
|
4991
|
|
4992 my $bt_options = $bowtie_options;
|
|
4993 if ($fh->{name} eq 'CTread1GAread2CTgenome' or $fh->{name} eq 'GAread1CTread2GAgenome'){
|
|
4994 $bt_options .= ' --norc'; ### ensuring the alignments are only reported in a sensible manner
|
|
4995 }
|
|
4996 else {
|
|
4997 $bt_options .= ' --nofw';
|
|
4998 }
|
|
4999
|
|
5000 warn "Now starting a Bowtie paired-end alignment for $fh->{name} (reading in sequences from $temp_dir$fh->{inputfile_1} and $temp_dir$fh->{inputfile_2}, with the options: $bt_options)\n";
|
|
5001 open ($fh->{fh},"$path_to_bowtie $bt_options $fh->{bisulfiteIndex} -1 $temp_dir$fh->{inputfile_1} -2 $temp_dir$fh->{inputfile_2} |") or die "Can't open pipe to bowtie: $!";
|
|
5002
|
|
5003 my $line_1 = $fh->{fh}->getline();
|
|
5004 my $line_2 = $fh->{fh}->getline();
|
|
5005
|
|
5006 # if Bowtie produces an alignment we store the first line of the output
|
|
5007 if ($line_1 and $line_2) {
|
|
5008 chomp $line_1;
|
|
5009 chomp $line_2;
|
|
5010 my $id_1 = (split(/\t/,$line_1))[0]; # this is the first element of the first bowtie output line (= the sequence identifier)
|
|
5011 my $id_2 = (split(/\t/,$line_2))[0]; # this is the first element of the second bowtie output line
|
|
5012
|
|
5013 ### Bowtie always reports the alignment with the smaller chromosomal position first. This can be either sequence 1 or sequence 2.
|
|
5014 ### We will thus identify which sequence was read 1 and store this ID as last_seq_id
|
|
5015
|
|
5016 if ($id_1 =~ s/\/1$//){ # removing the read 1 tag if present
|
|
5017 $fh->{last_seq_id} = $id_1;
|
|
5018 }
|
|
5019 elsif ($id_2 =~ s/\/1$//){ # removing the read 1 tag if present
|
|
5020 $fh->{last_seq_id} = $id_2;
|
|
5021 }
|
|
5022 else{
|
|
5023 die "Either the first or the second id need to be read 1! ID1 was: $id_1; ID2 was: $id_2\n";
|
|
5024 }
|
|
5025
|
|
5026 $fh->{last_line_1} = $line_1; # this contains either read 1 or read 2
|
|
5027 $fh->{last_line_2} = $line_2; # this contains either read 1 or read 2
|
|
5028 warn "Found first alignment:\n$fh->{last_line_1}\n$fh->{last_line_2}\n";
|
|
5029 }
|
|
5030 # otherwise we just initialise last_seq_id and last_lines as undefined
|
|
5031 else {
|
|
5032 warn "Found no alignment, assigning undef to last_seq_id and last_lines\n";
|
|
5033 $fh->{last_seq_id} = undef;
|
|
5034 $fh->{last_line_1} = undef;
|
|
5035 $fh->{last_line_2} = undef;
|
|
5036 }
|
|
5037 }
|
|
5038 }
|
|
5039
|
|
5040 ### Bowtie 2 | PAIRED-END | FASTA
|
|
5041
|
|
5042 sub paired_end_align_fragments_to_bisulfite_genome_fastA_bowtie2 {
|
|
5043 my ($C_to_T_infile_1,$G_to_A_infile_1,$C_to_T_infile_2,$G_to_A_infile_2) = @_;
|
|
5044 if ($directional){
|
|
5045 warn "Input files are $C_to_T_infile_1 and $G_to_A_infile_2 (FastA)\n";
|
|
5046 }
|
|
5047 else{
|
|
5048 warn "Input files are $C_to_T_infile_1 and $G_to_A_infile_1 and $C_to_T_infile_2 and $G_to_A_infile_2 (FastA)\n";
|
|
5049 }
|
|
5050
|
|
5051 ## Now starting up to 4 instances of Bowtie feeding in the converted sequence files and reading in the first line of the bowtie output, and storing it in the
|
|
5052 ## data structure above
|
|
5053 if ($directional){
|
|
5054 warn "Now running 2 instances of Bowtie 2 against the bisulfite genome of $genome_folder with the specified options: $bowtie_options\n\n";
|
|
5055 }
|
|
5056 else{
|
|
5057 warn "Now running 4 individual instances of Bowtie 2 against the bisulfite genome of $genome_folder with the specified options: $bowtie_options\n\n";
|
|
5058 }
|
|
5059
|
|
5060 foreach my $fh (@fhs) {
|
|
5061
|
|
5062 if ($directional){
|
|
5063 unless ($fh->{inputfile_1}){
|
|
5064 $fh->{last_seq_id} = undef;
|
|
5065 $fh->{last_line_1} = undef;
|
|
5066 $fh->{last_line_2} = undef;
|
|
5067 next;
|
|
5068 }
|
|
5069 }
|
|
5070
|
|
5071 my $bt2_options = $bowtie_options;
|
|
5072 if ($fh->{name} eq 'CTread1GAread2CTgenome' or $fh->{name} eq 'GAread1CTread2GAgenome'){
|
|
5073 $bt2_options .= ' --norc'; ### ensuring the alignments are only reported in a sensible manner
|
|
5074 }
|
|
5075 else {
|
|
5076 $bt2_options .= ' --nofw';
|
|
5077 }
|
|
5078
|
|
5079 warn "Now starting a Bowtie 2 paired-end alignment for $fh->{name} (reading in sequences from $temp_dir$fh->{inputfile_1} and $temp_dir$fh->{inputfile_2}, with the options: $bt2_options))\n";
|
|
5080 open ($fh->{fh},"$path_to_bowtie $bt2_options $fh->{bisulfiteIndex} -1 $temp_dir$fh->{inputfile_1} -2 $temp_dir$fh->{inputfile_2} |") or die "Can't open pipe to bowtie: $!";
|
|
5081
|
|
5082 ### Bowtie 2 outputs out SAM format, so we need to skip everything until the first sequence
|
|
5083 while (1){
|
|
5084 $_ = $fh->{fh}->getline();
|
|
5085 if ($_) {
|
|
5086 last unless ($_ =~ /^\@/); # SAM headers start with @
|
|
5087 }
|
|
5088 else{
|
|
5089 last; # no alignment output
|
|
5090 }
|
|
5091 }
|
|
5092
|
|
5093 my $line_1 = $_;
|
|
5094 my $line_2 = $fh->{fh}->getline();
|
|
5095
|
|
5096 # if Bowtie produces an alignment we store the first line of the output
|
|
5097 if ($line_1 and $line_2) {
|
|
5098 chomp $line_1;
|
|
5099 chomp $line_2;
|
|
5100 my $id_1 = (split(/\t/,$line_1))[0]; # this is the first element of the first bowtie output line (= the sequence identifier)
|
|
5101 my $id_2 = (split(/\t/,$line_2))[0]; # this is the first element of the second bowtie output line
|
|
5102
|
|
5103 ### Bowtie always reports the alignment with the smaller chromosomal position first. This can be either sequence 1 or sequence 2.
|
|
5104 ### We will thus identify which sequence was read 1 and store this ID as last_seq_id
|
|
5105
|
|
5106 if ($id_1 =~ s/\/1$//){ # removing the read 1 /1 tag if present (remember that Bowtie2 clips off /1 or /2 line endings itself, so we added /1/1 or /2/2 to start with
|
|
5107 $fh->{last_seq_id} = $id_1;
|
|
5108 }
|
|
5109 elsif ($id_2 =~ s/\/1$//){ # removing the read 1 /2 tag if present
|
|
5110 $fh->{last_seq_id} = $id_2;
|
|
5111 }
|
|
5112 else{
|
|
5113 warn "Either the first or the second id need to be read 1! ID1 was: $id_1; ID2 was: $id_2\n";
|
|
5114 }
|
|
5115
|
|
5116 $fh->{last_line_1} = $line_1; # this contains either read 1 or read 2
|
|
5117 $fh->{last_line_2} = $line_2; # this contains either read 1 or read 2
|
|
5118 warn "Found first alignment:\n$fh->{last_line_1}\n$fh->{last_line_2}\n";
|
|
5119 }
|
|
5120 # otherwise we just initialise last_seq_id and last_lines as undefined
|
|
5121 else {
|
|
5122 warn "Found no alignment, assigning undef to last_seq_id and last_lines\n";
|
|
5123 $fh->{last_seq_id} = undef;
|
|
5124 $fh->{last_line_1} = undef;
|
|
5125 $fh->{last_line_2} = undef;
|
|
5126 }
|
|
5127 }
|
|
5128 }
|
|
5129
|
|
5130 ### Bowtie 1 (default) | PAIRED-END | FASTQ
|
|
5131
|
|
5132 sub paired_end_align_fragments_to_bisulfite_genome_fastQ {
|
|
5133 my ($C_to_T_infile_1,$G_to_A_infile_1,$C_to_T_infile_2,$G_to_A_infile_2) = @_;
|
|
5134
|
|
5135 if ($directional){
|
|
5136 warn "Input file is $C_to_T_infile_1 (FastQ)\n";
|
|
5137 }
|
|
5138 elsif($pbat){
|
|
5139 warn "Input file is $G_to_A_infile_1 (FastQ; PBAT-Seq)\n";
|
|
5140 }
|
|
5141 else{
|
|
5142 warn "Input files are $C_to_T_infile_1 and $G_to_A_infile_1 (FastQ)\n";
|
|
5143 }
|
|
5144
|
|
5145 ## Now starting up to 4 instances of Bowtie feeding in the converted sequence files and reading in the first line of the bowtie output, and storing it in the
|
|
5146 ## data structure above
|
|
5147 if ($directional or $pbat){
|
|
5148 warn "Now running 2 instances of Bowtie against the bisulfite genome of $genome_folder with the specified options: $bowtie_options\n\n";
|
|
5149 }
|
|
5150 else{
|
|
5151 warn "Now running 4 individual instances of Bowtie against the bisulfite genome of $genome_folder with the specified options: $bowtie_options\n\n";
|
|
5152 }
|
|
5153
|
|
5154 foreach my $fh (@fhs) {
|
|
5155
|
|
5156 if ($directional or $pbat){
|
|
5157 unless ($fh->{inputfile_1}){
|
|
5158 $fh->{last_seq_id} = undef;
|
|
5159 $fh->{last_line_1} = undef;
|
|
5160 $fh->{last_line_2} = undef;
|
|
5161 next; # skipping unwanted filehandles
|
|
5162 }
|
|
5163 }
|
|
5164
|
|
5165 my $bt_options = $bowtie_options;
|
|
5166 if ($fh->{name} eq 'CTread1GAread2CTgenome' or $fh->{name} eq 'GAread1CTread2GAgenome'){
|
|
5167 $bt_options .= ' --norc'; ### ensuring the alignments are only reported in a sensible manner
|
|
5168 }
|
|
5169 else {
|
|
5170 $bt_options .= ' --nofw';
|
|
5171 }
|
|
5172
|
|
5173 if ($gzip){
|
|
5174 warn "Now starting a Bowtie paired-end alignment for $fh->{name} (reading in sequences from ${temp_dir}$fh->{inputfile_1}, with the options: $bt_options)\n";
|
|
5175 open ($fh->{fh},"zcat ${temp_dir}$fh->{inputfile_1} | $path_to_bowtie $bt_options $fh->{bisulfiteIndex} --12 - |") or die "Can't open pipe to bowtie: $!";
|
|
5176 }
|
|
5177 else{
|
|
5178 warn "Now starting a Bowtie paired-end alignment for $fh->{name} (reading in sequences from ${temp_dir}$fh->{inputfile_1} and ${temp_dir}$fh->{inputfile_2}, with the options: $bt_options))\n";
|
|
5179 sleep(5);
|
|
5180 open ($fh->{fh},"$path_to_bowtie $bt_options $fh->{bisulfiteIndex} -1 $temp_dir$fh->{inputfile_1} -2 $temp_dir$fh->{inputfile_2} |") or die "Can't open pipe to bowtie: $!";
|
|
5181 }
|
|
5182
|
|
5183 my $line_1 = $fh->{fh}->getline();
|
|
5184 my $line_2 = $fh->{fh}->getline();
|
|
5185
|
|
5186 # if Bowtie produces an alignment we store the first line of the output
|
|
5187 if ($line_1 and $line_2) {
|
|
5188 chomp $line_1;
|
|
5189 chomp $line_2;
|
|
5190 ### Bowtie always reports the alignment with the smaller chromosomal position first. This can be either sequence 1 or sequence 2.
|
|
5191 ### We will thus identify which sequence was read 1 and store this ID as last_seq_id
|
|
5192
|
|
5193 my $id_1 = (split(/\t/,$line_1))[0]; # this is the first element of the first bowtie output line (= the sequence identifier)
|
|
5194 my $id_2 = (split(/\t/,$line_2))[0]; # this is the first element of the second bowtie output line
|
|
5195
|
|
5196 if ($id_1 =~ s/\/1$//){ # removing the read 1 tag if present
|
|
5197 $fh->{last_seq_id} = $id_1;
|
|
5198 }
|
|
5199 elsif ($id_2 =~ s/\/1$//){ # removing the read 1 tag if present
|
|
5200 $fh->{last_seq_id} = $id_2;
|
|
5201 }
|
|
5202 else{
|
|
5203 die "Either the first or the second id need to be read 1! ID1 was: $id_1; ID2 was: $id_2\n";
|
|
5204 }
|
|
5205
|
|
5206 $fh->{last_line_1} = $line_1; # this contains read 1 or read 2
|
|
5207 $fh->{last_line_2} = $line_2; # this contains read 1 or read 2
|
|
5208 warn "Found first alignment:\n$fh->{last_line_1}\n$fh->{last_line_2}\n";
|
|
5209 }
|
|
5210
|
|
5211 # otherwise we just initialise last_seq_id and last_lines as undefined
|
|
5212 else {
|
|
5213 warn "Found no alignment, assigning undef to last_seq_id and last_lines\n";
|
|
5214 $fh->{last_seq_id} = undef;
|
|
5215 $fh->{last_line_1} = undef;
|
|
5216 $fh->{last_line_2} = undef;
|
|
5217 }
|
|
5218 }
|
|
5219 }
|
|
5220
|
|
5221 ### Bowtie 2 | PAIRED-END | FASTQ
|
|
5222
|
|
5223 sub paired_end_align_fragments_to_bisulfite_genome_fastQ_bowtie2 {
|
|
5224 my ($C_to_T_infile_1,$G_to_A_infile_1,$C_to_T_infile_2,$G_to_A_infile_2) = @_;
|
|
5225 if ($directional){
|
|
5226 warn "Input files are $C_to_T_infile_1 and $G_to_A_infile_2 (FastQ)\n";
|
|
5227 }
|
|
5228 else{
|
|
5229 warn "Input files are $C_to_T_infile_1 and $G_to_A_infile_1 and $C_to_T_infile_2 and $G_to_A_infile_2 (FastQ)\n";
|
|
5230 }
|
|
5231
|
|
5232 ## Now starting up 4 instances of Bowtie 2 feeding in the converted sequence files and reading in the first line of the bowtie output, and storing it in the
|
|
5233 ## data structure above
|
|
5234 if ($directional){
|
|
5235 warn "Now running 2 instances of Bowtie 2 against the bisulfite genome of $genome_folder with the specified options: $bowtie_options\n\n";
|
|
5236 }
|
|
5237 else{
|
|
5238 warn "Now running 4 individual instances of Bowtie 2 against the bisulfite genome of $genome_folder with the specified options: $bowtie_options\n\n";
|
|
5239 }
|
|
5240
|
|
5241 foreach my $fh (@fhs) {
|
|
5242
|
|
5243 if ($directional){
|
|
5244 unless ($fh->{inputfile_1}){
|
|
5245 $fh->{last_seq_id} = undef;
|
|
5246 $fh->{last_line_1} = undef;
|
|
5247 $fh->{last_line_2} = undef;
|
|
5248 next;
|
|
5249 }
|
|
5250 }
|
|
5251
|
|
5252 my $bt2_options = $bowtie_options;
|
|
5253 if ($fh->{name} eq 'CTread1GAread2CTgenome' or $fh->{name} eq 'GAread1CTread2GAgenome'){
|
|
5254 $bt2_options .= ' --norc'; ### ensuring the alignments are only reported in a sensible manner
|
|
5255 }
|
|
5256 else {
|
|
5257 $bt2_options .= ' --nofw';
|
|
5258 }
|
|
5259
|
|
5260 warn "Now starting a Bowtie 2 paired-end alignment for $fh->{name} (reading in sequences from $temp_dir$fh->{inputfile_1} and $temp_dir$fh->{inputfile_2}, with the options: $bt2_options))\n";
|
|
5261 open ($fh->{fh},"$path_to_bowtie $bt2_options $fh->{bisulfiteIndex} -1 $temp_dir$fh->{inputfile_1} -2 $temp_dir$fh->{inputfile_2} |") or die "Can't open pipe to bowtie: $!";
|
|
5262
|
|
5263 ### Bowtie 2 outputs out SAM format, so we need to skip everything until the first sequence
|
|
5264 while (1){
|
|
5265 $_ = $fh->{fh}->getline();
|
|
5266 if ($_) {
|
|
5267 last unless ($_ =~ /^\@/); # SAM headers start with @
|
|
5268 }
|
|
5269 else{
|
|
5270 last; # no alignment output
|
|
5271 }
|
|
5272 }
|
|
5273
|
|
5274 my $line_1 = $_;
|
|
5275 my $line_2 = $fh->{fh}->getline();
|
|
5276
|
|
5277 # if Bowtie produces an alignment we store the first line of the output
|
|
5278 if ($line_1 and $line_2) {
|
|
5279 chomp $line_1;
|
|
5280 chomp $line_2;
|
|
5281 ### Bowtie always reports the alignment with the smaller chromosomal position first. This can be either sequence 1 or sequence 2.
|
|
5282 ### We will thus identify which sequence was read 1 and store this ID as last_seq_id
|
|
5283
|
|
5284 my $id_1 = (split(/\t/,$line_1))[0]; # this is the first element of the first bowtie output line (= the sequence identifier)
|
|
5285 my $id_2 = (split(/\t/,$line_2))[0]; # this is the first element of the second bowtie output line
|
|
5286
|
|
5287 if ($id_1 =~ s/\/1$//){ # removing the read 1 tag if present (remember that Bowtie2 clips off /1 or /2 line endings itself, so we added /1/1 or /2/2 to start with
|
|
5288 $fh->{last_seq_id} = $id_1;
|
|
5289 }
|
|
5290 elsif ($id_2 =~ s/\/1$//){ # removing the read 1 tag if present
|
|
5291 $fh->{last_seq_id} = $id_2;
|
|
5292 }
|
|
5293 else{
|
|
5294 die "Either the first or the second id need to be read 1! ID1 was: $id_1; ID2 was: $id_2\n";
|
|
5295 }
|
|
5296
|
|
5297 $fh->{last_line_1} = $line_1; # this contains read 1 or read 2
|
|
5298 $fh->{last_line_2} = $line_2; # this contains read 1 or read 2
|
|
5299 warn "Found first alignment:\n$fh->{last_line_1}\n$fh->{last_line_2}\n";
|
|
5300 }
|
|
5301
|
|
5302 # otherwise we just initialise last_seq_id and last_lines as undefined
|
|
5303 else {
|
|
5304 warn "Found no alignment, assigning undef to last_seq_id and last_lines\n";
|
|
5305 $fh->{last_seq_id} = undef;
|
|
5306 $fh->{last_line_1} = undef;
|
|
5307 $fh->{last_line_2} = undef;
|
|
5308 }
|
|
5309 }
|
|
5310 }
|
|
5311
|
|
5312 #####################################################################################################################################################
|
|
5313
|
|
5314 ### Bowtie 1 (default) | SINGLE-END | FASTA
|
|
5315 sub single_end_align_fragments_to_bisulfite_genome_fastA {
|
|
5316 my ($C_to_T_infile,$G_to_A_infile) = @_;
|
|
5317 if ($directional){
|
|
5318 warn "Input file is $C_to_T_infile (FastA)\n";
|
|
5319 }
|
|
5320 else{
|
|
5321 warn "Input files are $C_to_T_infile and $G_to_A_infile (FastA)\n";
|
|
5322 }
|
|
5323
|
|
5324 ## Now starting up to 4 instances of Bowtie feeding in the converted sequence files and reading in the first line of the bowtie output, and storing it in
|
|
5325 ## data structure above
|
|
5326 if ($directional){
|
|
5327 warn "Now running 2 instances of Bowtie against the bisulfite genome of $genome_folder with the specified options: $bowtie_options\n\n";
|
|
5328 }
|
|
5329 else{
|
|
5330 warn "Now running 4 individual instances of Bowtie against the bisulfite genome of $genome_folder with the specified options: $bowtie_options\n\n";
|
|
5331 }
|
|
5332
|
|
5333 foreach my $fh (@fhs) {
|
|
5334
|
|
5335 my $bt_options = $bowtie_options;
|
|
5336 if ($fh->{name} eq 'CTreadCTgenome' or $fh->{name} eq 'GAreadGAgenome'){
|
|
5337 $bt_options .= ' --norc'; ### ensuring the alignments are only reported in a sensible manner
|
|
5338 }
|
|
5339 else {
|
|
5340 $bt_options .= ' --nofw';
|
|
5341 }
|
|
5342
|
|
5343 warn "Now starting the Bowtie aligner for $fh->{name} (reading in sequences from $temp_dir$fh->{inputfile} with options: $bt_options)\n";
|
|
5344 if ($gzip){
|
|
5345 open ($fh->{fh},"zcat $temp_dir$fh->{inputfile} | $path_to_bowtie $bt_options $fh->{bisulfiteIndex} - |") or die "Can't open pipe to bowtie: $!";
|
|
5346 }
|
|
5347 else{
|
|
5348 open ($fh->{fh},"$path_to_bowtie $bt_options $fh->{bisulfiteIndex} $temp_dir$fh->{inputfile} |") or die "Can't open pipe to bowtie: $!"; # command for uncompressed data
|
|
5349 }
|
|
5350
|
|
5351 # if Bowtie produces an alignment we store the first line of the output
|
|
5352 $_ = $fh->{fh}->getline();
|
|
5353 if ($_) {
|
|
5354 chomp;
|
|
5355 my $id = (split(/\t/))[0]; # this is the first element of the bowtie output (= the sequence identifier)
|
|
5356 $fh->{last_seq_id} = $id;
|
|
5357 $fh->{last_line} = $_;
|
|
5358 warn "Found first alignment:\t$fh->{last_line}\n";
|
|
5359 }
|
|
5360 # otherwise we just initialise last_seq_id and last_line as undefined
|
|
5361 else {
|
|
5362 warn "Found no alignment, assigning undef to last_seq_id and last_line\n";
|
|
5363 $fh->{last_seq_id} = undef;
|
|
5364 $fh->{last_line} = undef;
|
|
5365 }
|
|
5366 }
|
|
5367 }
|
|
5368
|
|
5369 ### Bowtie 2 | SINGLE-END | FASTA
|
|
5370 sub single_end_align_fragments_to_bisulfite_genome_fastA_bowtie2 {
|
|
5371 my ($C_to_T_infile,$G_to_A_infile) = @_;
|
|
5372 if ($directional){
|
|
5373 warn "Input file is $C_to_T_infile (FastA)\n";
|
|
5374 }
|
|
5375 else{
|
|
5376 warn "Input files are $C_to_T_infile and $G_to_A_infile (FastA)\n";
|
|
5377 }
|
|
5378
|
|
5379 ## Now starting up to 4 instances of Bowtie feeding in the converted sequence files and reading in the first line of the bowtie output, and storing it in
|
|
5380 ## data structure above
|
|
5381 if ($directional){
|
|
5382 warn "Now running 2 instances of Bowtie 2 against the bisulfite genome of $genome_folder with the specified options: $bowtie_options\n\n";
|
|
5383 }
|
|
5384 else{
|
|
5385 warn "Now running 4 individual instances of Bowtie 2 against the bisulfite genome of $genome_folder with the specified options: $bowtie_options\n\n";
|
|
5386 }
|
|
5387
|
|
5388 foreach my $fh (@fhs) {
|
|
5389
|
|
5390 my $bt2_options = $bowtie_options;
|
|
5391 if ($fh->{name} eq 'CTreadCTgenome' or $fh->{name} eq 'GAreadGAgenome'){
|
|
5392 $bt2_options .= ' --norc'; ### ensuring the alignments are only reported in a sensible manner
|
|
5393 }
|
|
5394 else {
|
|
5395 $bt2_options .= ' --nofw';
|
|
5396 }
|
|
5397
|
|
5398 warn "Now starting the Bowtie 2 aligner for $fh->{name} (reading in sequences from $temp_dir$fh->{inputfile} with options: $bt2_options)\n";
|
|
5399 open ($fh->{fh},"$path_to_bowtie $bt2_options $fh->{bisulfiteIndex} -U $temp_dir$fh->{inputfile} |") or die "Can't open pipe to bowtie: $!";
|
|
5400
|
|
5401 ### Bowtie 2 outputs out SAM format, so we need to skip everything until the first sequence
|
|
5402 while (1){
|
|
5403 $_ = $fh->{fh}->getline();
|
|
5404 if ($_) {
|
|
5405 last unless ($_ =~ /^\@/); # SAM headers start with @
|
|
5406 }
|
|
5407 else{
|
|
5408 last; # no alignment output
|
|
5409 }
|
|
5410 }
|
|
5411
|
|
5412 # Bowtie 2 outputs a result line even for sequences without any alignments. We thus store the first line of the output
|
|
5413 if ($_) {
|
|
5414 chomp;
|
|
5415 my $id = (split(/\t/))[0]; # this is the first element of the Bowtie output (= the sequence identifier)
|
|
5416 $fh->{last_seq_id} = $id;
|
|
5417 $fh->{last_line} = $_;
|
|
5418 warn "Found first alignment:\t$fh->{last_line}\n";
|
|
5419 }
|
|
5420 # otherwise we just initialise last_seq_id and last_line as undefinded. This should only happen at the end of a file for Bowtie 2 output
|
|
5421 else {
|
|
5422 warn "Found no alignment, assigning undef to last_seq_id and last_line\n";
|
|
5423 $fh->{last_seq_id} = undef;
|
|
5424 $fh->{last_line} = undef;
|
|
5425 }
|
|
5426 }
|
|
5427 }
|
|
5428
|
|
5429
|
|
5430 ### Bowtie 1 (default) | SINGLE-END | FASTQ
|
|
5431 sub single_end_align_fragments_to_bisulfite_genome_fastQ {
|
|
5432 my ($C_to_T_infile,$G_to_A_infile) = @_;
|
|
5433 if ($directional){
|
|
5434 warn "Input file is $C_to_T_infile (FastQ)\n";
|
|
5435 }
|
|
5436 elsif($pbat){
|
|
5437 warn "Input file is $G_to_A_infile (FastQ)\n";
|
|
5438 }
|
|
5439 else{
|
|
5440 warn "Input files are $C_to_T_infile and $G_to_A_infile (FastQ)\n";
|
|
5441 }
|
|
5442
|
|
5443
|
|
5444 ## Now starting up to 4 instances of Bowtie feeding in the converted sequence files and reading in the first line of the bowtie output, and storing it in
|
|
5445 ## the data structure above
|
|
5446 if ($directional or $pbat){
|
|
5447 warn "Now running 2 instances of Bowtie against the bisulfite genome of $genome_folder with the specified options: $bowtie_options\n\n";
|
|
5448 }
|
|
5449 else{
|
|
5450 warn "Now running 4 individual instances of Bowtie against the bisulfite genome of $genome_folder with the specified options: $bowtie_options\n\n";
|
|
5451 }
|
|
5452
|
|
5453 foreach my $fh (@fhs) {
|
|
5454 my $bt_options = $bowtie_options;
|
|
5455 if ($fh->{name} eq 'CTreadCTgenome' or $fh->{name} eq 'GAreadGAgenome'){
|
|
5456 $bt_options .= ' --norc'; ### ensuring the alignments are only reported in a sensible manner
|
|
5457 }
|
|
5458 else {
|
|
5459 $bt_options .= ' --nofw';
|
|
5460 }
|
|
5461
|
|
5462 warn "Now starting the Bowtie aligner for $fh->{name} (reading in sequences from $temp_dir$fh->{inputfile} with options: $bt_options)\n";
|
|
5463 sleep (5);
|
|
5464
|
|
5465 if ($gzip){
|
|
5466 open ($fh->{fh},"zcat $temp_dir$fh->{inputfile} | $path_to_bowtie $bowtie_options $fh->{bisulfiteIndex} - |") or die "Can't open pipe to bowtie: $!";
|
|
5467 }
|
|
5468 else{
|
|
5469 open ($fh->{fh},"$path_to_bowtie $bowtie_options $fh->{bisulfiteIndex} $temp_dir$fh->{inputfile} |") or die "Can't open pipe to bowtie: $!"; # command for uncompressed data
|
|
5470 }
|
|
5471
|
|
5472 # if Bowtie produces an alignment we store the first line of the output
|
|
5473 $_ = $fh->{fh}->getline();
|
|
5474 if ($_) {
|
|
5475 chomp;
|
|
5476 my $id = (split(/\t/))[0]; # this is the first element of the Bowtie output (= the sequence identifier)
|
|
5477 $fh->{last_seq_id} = $id;
|
|
5478 $fh->{last_line} = $_;
|
|
5479 warn "Found first alignment:\t$fh->{last_line}\n";
|
|
5480 }
|
|
5481 # otherwise we just initialise last_seq_id and last_line as undefined
|
|
5482 else {
|
|
5483 warn "Found no alignment, assigning undef to last_seq_id and last_line\n";
|
|
5484 $fh->{last_seq_id} = undef;
|
|
5485 $fh->{last_line} = undef;
|
|
5486 }
|
|
5487 }
|
|
5488 }
|
|
5489
|
|
5490 ### Bowtie 2 | SINGLE-END | FASTQ
|
|
5491 sub single_end_align_fragments_to_bisulfite_genome_fastQ_bowtie2 {
|
|
5492
|
|
5493 my ($C_to_T_infile,$G_to_A_infile) = @_;
|
|
5494 if ($directional){
|
|
5495 warn "Input file is $C_to_T_infile (FastQ)\n\n";
|
|
5496 }
|
|
5497 else{
|
|
5498 warn "Input files are $C_to_T_infile and $G_to_A_infile (FastQ)\n\n";
|
|
5499 }
|
|
5500
|
|
5501 ## Now starting up to 4 instances of Bowtie 2 feeding in the converted sequence files and reading in the first line of the bowtie output, and storing it in
|
|
5502 ## the data structure above
|
|
5503 if ($directional){
|
|
5504 warn "Now running 2 instances of Bowtie 2 against the bisulfite genome of $genome_folder with the specified options: $bowtie_options\n\n";
|
|
5505 }
|
|
5506 else{
|
|
5507 warn "Now running 4 individual instances of Bowtie 2 against the bisulfite genome of $genome_folder with the specified options: $bowtie_options\n\n";
|
|
5508 }
|
|
5509 foreach my $fh (@fhs) {
|
|
5510 my $bt2_options = $bowtie_options;
|
|
5511 if ($fh->{name} eq 'CTreadCTgenome' or $fh->{name} eq 'GAreadGAgenome'){
|
|
5512 $bt2_options .= ' --norc'; ### ensuring the alignments are only reported in a sensible manner
|
|
5513 }
|
|
5514 else {
|
|
5515 $bt2_options .= ' --nofw';
|
|
5516 }
|
|
5517 warn "Now starting the Bowtie 2 aligner for $fh->{name} (reading in sequences from $temp_dir$fh->{inputfile} with options $bt2_options)\n";
|
|
5518 warn "Using Bowtie 2 index: $fh->{bisulfiteIndex}\n\n";
|
|
5519
|
|
5520 open ($fh->{fh},"$path_to_bowtie $bt2_options $fh->{bisulfiteIndex} -U $temp_dir$fh->{inputfile} |") or die "Can't open pipe to bowtie: $!";
|
|
5521 ### Bowtie 2 outputs out SAM format, so we need to skip everything until the first sequence
|
|
5522 while (1){
|
|
5523 $_ = $fh->{fh}->getline();
|
|
5524 # warn "$_\n";
|
|
5525 # sleep(1);
|
|
5526 if ($_) {
|
|
5527 last unless ($_ =~ /^\@/); # SAM headers start with @
|
|
5528 }
|
|
5529 else {
|
|
5530 last;
|
|
5531 }
|
|
5532 }
|
|
5533
|
|
5534 # Bowtie 2 outputs a result line even for sequences without any alignments. We thus store the first line of the output
|
|
5535 if ($_) {
|
|
5536 chomp;
|
|
5537 my $id = (split(/\t/))[0]; # this is the first element of the Bowtie 2 output (= the sequence identifier)
|
|
5538 $fh->{last_seq_id} = $id;
|
|
5539 $fh->{last_line} = $_;
|
|
5540 warn "Found first alignment:\t$fh->{last_line}\n";
|
|
5541 # warn "storing $id and\n$_\n";
|
|
5542 }
|
|
5543 # otherwise we just initialise last_seq_id and last_line as undefined. This should only happen at the end of a file for Bowtie 2 output
|
|
5544 else {
|
|
5545 warn "Found no alignment, assigning undef to last_seq_id and last_line\n";
|
|
5546 $fh->{last_seq_id} = undef;
|
|
5547 $fh->{last_line} = undef;
|
|
5548 }
|
|
5549 }
|
|
5550 }
|
|
5551
|
|
5552 ###########################################################################################################################################
|
|
5553
|
|
5554 sub reset_counters_and_fhs{
|
|
5555 my $filename = shift;
|
|
5556 %counting=(
|
|
5557 total_meCHH_count => 0,
|
|
5558 total_meCHG_count => 0,
|
|
5559 total_meCpG_count => 0,
|
|
5560 total_unmethylated_CHH_count => 0,
|
|
5561 total_unmethylated_CHG_count => 0,
|
|
5562 total_unmethylated_CpG_count => 0,
|
|
5563 sequences_count => 0,
|
|
5564 no_single_alignment_found => 0,
|
|
5565 unsuitable_sequence_count => 0,
|
|
5566 genomic_sequence_could_not_be_extracted_count => 0,
|
|
5567 unique_best_alignment_count => 0,
|
|
5568 low_complexity_alignments_overruled_count => 0,
|
|
5569 CT_CT_count => 0, #(CT read/CT genome, original top strand)
|
|
5570 CT_GA_count => 0, #(CT read/GA genome, original bottom strand)
|
|
5571 GA_CT_count => 0, #(GA read/CT genome, complementary to original top strand)
|
|
5572 GA_GA_count => 0, #(GA read/GA genome, complementary to original bottom strand)
|
|
5573 CT_GA_CT_count => 0, #(CT read1/GA read2/CT genome, original top strand)
|
|
5574 GA_CT_GA_count => 0, #(GA read1/CT read2/GA genome, complementary to original bottom strand)
|
|
5575 GA_CT_CT_count => 0, #(GA read1/CT read2/CT genome, complementary to original top strand)
|
|
5576 CT_GA_GA_count => 0, #(CT read1/GA read2/GA genome, original bottom strand)
|
|
5577 alignments_rejected_count => 0, # only relevant if --directional was specified
|
|
5578 );
|
|
5579
|
|
5580 if ($directional){
|
|
5581 if ($filename =~ ','){ # paired-end files
|
|
5582 @fhs=(
|
|
5583 { name => 'CTreadCTgenome',
|
|
5584 strand_identity => 'con ori forward',
|
|
5585 bisulfiteIndex => $CT_index_basename,
|
|
5586 seen => 0,
|
|
5587 wrong_strand => 0,
|
|
5588 },
|
|
5589 { name => 'CTreadGAgenome',
|
|
5590 strand_identity => 'con ori reverse',
|
|
5591 bisulfiteIndex => $GA_index_basename,
|
|
5592 seen => 0,
|
|
5593 wrong_strand => 0,
|
|
5594 },
|
|
5595 { name => 'GAreadCTgenome',
|
|
5596 strand_identity => 'compl ori con forward',
|
|
5597 bisulfiteIndex => $CT_index_basename,
|
|
5598 seen => 0,
|
|
5599 wrong_strand => 0,
|
|
5600 },
|
|
5601 { name => 'GAreadGAgenome',
|
|
5602 strand_identity => 'compl ori con reverse',
|
|
5603 bisulfiteIndex => $GA_index_basename,
|
|
5604 seen => 0,
|
|
5605 wrong_strand => 0,
|
|
5606 },
|
|
5607 );
|
|
5608 }
|
|
5609 else{ # single-end files
|
|
5610 @fhs=(
|
|
5611 { name => 'CTreadCTgenome',
|
|
5612 strand_identity => 'con ori forward',
|
|
5613 bisulfiteIndex => $CT_index_basename,
|
|
5614 seen => 0,
|
|
5615 wrong_strand => 0,
|
|
5616 },
|
|
5617 { name => 'CTreadGAgenome',
|
|
5618 strand_identity => 'con ori reverse',
|
|
5619 bisulfiteIndex => $GA_index_basename,
|
|
5620 seen => 0,
|
|
5621 wrong_strand => 0,
|
|
5622 },
|
|
5623 );
|
|
5624 }
|
|
5625 }
|
|
5626 elsif($pbat){
|
|
5627 if ($filename =~ ','){ # paired-end files
|
|
5628 @fhs=(
|
|
5629 { name => 'CTreadCTgenome',
|
|
5630 strand_identity => 'con ori forward',
|
|
5631 bisulfiteIndex => $CT_index_basename,
|
|
5632 seen => 0,
|
|
5633 wrong_strand => 0,
|
|
5634 },
|
|
5635 { name => 'CTreadGAgenome',
|
|
5636 strand_identity => 'con ori reverse',
|
|
5637 bisulfiteIndex => $GA_index_basename,
|
|
5638 seen => 0,
|
|
5639 wrong_strand => 0,
|
|
5640 },
|
|
5641 { name => 'GAreadCTgenome',
|
|
5642 strand_identity => 'compl ori con forward',
|
|
5643 bisulfiteIndex => $CT_index_basename,
|
|
5644 seen => 0,
|
|
5645 wrong_strand => 0,
|
|
5646 },
|
|
5647 { name => 'GAreadGAgenome',
|
|
5648 strand_identity => 'compl ori con reverse',
|
|
5649 bisulfiteIndex => $GA_index_basename,
|
|
5650 seen => 0,
|
|
5651 wrong_strand => 0,
|
|
5652 },
|
|
5653 );
|
|
5654 }
|
|
5655 else{ # single-end files
|
|
5656 @fhs=(
|
|
5657 { name => 'GAreadCTgenome',
|
|
5658 strand_identity => 'compl ori con forward',
|
|
5659 bisulfiteIndex => $CT_index_basename,
|
|
5660 seen => 0,
|
|
5661 wrong_strand => 0,
|
|
5662 },
|
|
5663 { name => 'GAreadGAgenome',
|
|
5664 strand_identity => 'compl ori con reverse',
|
|
5665 bisulfiteIndex => $GA_index_basename,
|
|
5666 seen => 0,
|
|
5667 wrong_strand => 0,
|
|
5668 },
|
|
5669 );
|
|
5670 }
|
|
5671 }
|
|
5672 else{
|
|
5673 @fhs=(
|
|
5674 { name => 'CTreadCTgenome',
|
|
5675 strand_identity => 'con ori forward',
|
|
5676 bisulfiteIndex => $CT_index_basename,
|
|
5677 seen => 0,
|
|
5678 wrong_strand => 0,
|
|
5679 },
|
|
5680 { name => 'CTreadGAgenome',
|
|
5681 strand_identity => 'con ori reverse',
|
|
5682 bisulfiteIndex => $GA_index_basename,
|
|
5683 seen => 0,
|
|
5684 wrong_strand => 0,
|
|
5685 },
|
|
5686 { name => 'GAreadCTgenome',
|
|
5687 strand_identity => 'compl ori con forward',
|
|
5688 bisulfiteIndex => $CT_index_basename,
|
|
5689 seen => 0,
|
|
5690 wrong_strand => 0,
|
|
5691 },
|
|
5692 { name => 'GAreadGAgenome',
|
|
5693 strand_identity => 'compl ori con reverse',
|
|
5694 bisulfiteIndex => $GA_index_basename,
|
|
5695 seen => 0,
|
|
5696 wrong_strand => 0,
|
|
5697 },
|
|
5698 );
|
|
5699 }
|
|
5700 }
|
|
5701
|
|
5702
|
|
5703 sub process_command_line{
|
|
5704 my @bowtie_options;
|
|
5705 my $help;
|
|
5706 my $mates1;
|
|
5707 my $mates2;
|
|
5708 my $path_to_bowtie;
|
|
5709 my $fastq;
|
|
5710 my $fasta;
|
|
5711 my $skip;
|
|
5712 my $qupto;
|
|
5713 my $phred64;
|
|
5714 my $phred33;
|
|
5715 my $solexa;
|
|
5716 my $mismatches;
|
|
5717 my $seed_length;
|
|
5718 my $best;
|
|
5719 my $sequence_format;
|
|
5720 my $version;
|
|
5721 my $quiet;
|
|
5722 my $chunk;
|
|
5723 my $non_directional;
|
|
5724 my $ceiling;
|
|
5725 my $maxins;
|
|
5726 my $minins;
|
|
5727 my $unmapped;
|
|
5728 my $multi_map;
|
|
5729 my $output_dir;
|
|
5730 my $bowtie2;
|
|
5731 my $vanilla;
|
|
5732 my $sam_no_hd;
|
|
5733 my $seed_extension_fails;
|
|
5734 my $reseed_repetitive_seeds;
|
|
5735 my $most_valid_alignments;
|
|
5736 my $score_min;
|
|
5737 my $parallel;
|
|
5738 my $temp_dir;
|
|
5739 my $rdg;
|
|
5740 my $rfg;
|
|
5741 my $non_bs_mm;
|
|
5742 my $samtools_path;
|
|
5743 my $bam;
|
|
5744 my $gzip;
|
|
5745 my $pbat;
|
|
5746
|
|
5747 my $command_line = GetOptions ('help|man' => \$help,
|
|
5748 '1=s' => \$mates1,
|
|
5749 '2=s' => \$mates2,
|
|
5750 'path_to_bowtie=s' => \$path_to_bowtie,
|
|
5751 'f|fasta' => \$fasta,
|
|
5752 'q|fastq' => \$fastq,
|
|
5753 's|skip=i' => \$skip,
|
|
5754 'u|upto=i' => \$qupto,
|
|
5755 'phred33-quals' => \$phred33,
|
|
5756 'phred64-quals|solexa1' => \$phred64,
|
|
5757 'solexa-quals' => \$solexa,
|
|
5758 'n|seedmms=i' => \$mismatches,
|
|
5759 'l|seedlen=i' => \$seed_length,
|
|
5760 'no_best' => \$best,
|
|
5761 'version' => \$version,
|
|
5762 'quiet' => \$quiet,
|
|
5763 'chunkmbs=i' => \$chunk,
|
|
5764 'non_directional' => \$non_directional,
|
|
5765 'I|minins=i' => \$minins,
|
|
5766 'X|maxins=i' => \$maxins,
|
|
5767 'e|maqerr=i' => \$ceiling,
|
|
5768 'un|unmapped' => \$unmapped,
|
|
5769 'ambiguous' => \$multi_map,
|
|
5770 'o|output_dir=s' => \$output_dir,
|
|
5771 'bowtie2' => \$bowtie2,
|
|
5772 'vanilla' => \$vanilla,
|
|
5773 'sam-no-hd' => \$sam_no_hd,
|
|
5774 'D=i' => \$seed_extension_fails,
|
|
5775 'R=i' => \$reseed_repetitive_seeds,
|
|
5776 'score_min=s' => \$score_min,
|
|
5777 'most_valid_alignments=i' => \$most_valid_alignments,
|
|
5778 'p=i' => \$parallel,
|
|
5779 'temp_dir=s' => \$temp_dir,
|
|
5780 'rdg=s' => \$rdg,
|
|
5781 'rfg=s' => \$rfg,
|
|
5782 'non_bs_mm' => \$non_bs_mm,
|
|
5783 'samtools_path=s' => \$samtools_path,
|
|
5784 'bam' => \$bam,
|
|
5785 'gzip' => \$gzip,
|
|
5786 'pbat' => \$pbat,
|
|
5787 );
|
|
5788
|
|
5789
|
|
5790 ### EXIT ON ERROR if there were errors with any of the supplied options
|
|
5791 unless ($command_line){
|
|
5792 die "Please respecify command line options\n";
|
|
5793 }
|
|
5794 ### HELPFILE
|
|
5795 if ($help){
|
|
5796 print_helpfile();
|
|
5797 exit;
|
|
5798 }
|
|
5799 if ($version){
|
|
5800 print << "VERSION";
|
|
5801
|
|
5802
|
|
5803 Bismark - Bisulfite Mapper and Methylation Caller.
|
|
5804
|
|
5805 Bismark Version: $bismark_version
|
|
5806 Copyright 2010-13 Felix Krueger, Babraham Bioinformatics
|
|
5807 www.bioinformatics.babraham.ac.uk/projects/
|
|
5808
|
|
5809
|
|
5810 VERSION
|
|
5811 exit;
|
|
5812 }
|
|
5813
|
|
5814
|
|
5815 ##########################
|
|
5816 ### PROCESSING OPTIONS ###
|
|
5817 ##########################
|
|
5818
|
|
5819 unless ($bowtie2){
|
|
5820 $bowtie2 = 0;
|
|
5821 }
|
|
5822 unless ($sam_no_hd){
|
|
5823 $sam_no_hd =0;
|
|
5824 }
|
|
5825
|
|
5826 ### PATH TO BOWTIE
|
|
5827 ### if a special path to Bowtie 1/2 was specified we will use that one, otherwise it is assumed that Bowtie 1/2 is in the PATH
|
|
5828 if ($path_to_bowtie){
|
|
5829 unless ($path_to_bowtie =~ /\/$/){
|
|
5830 $path_to_bowtie =~ s/$/\//;
|
|
5831 }
|
|
5832 if (-d $path_to_bowtie){
|
|
5833 if ($bowtie2){
|
|
5834 $path_to_bowtie = "${path_to_bowtie}bowtie2";
|
|
5835 }
|
|
5836 else{
|
|
5837 $path_to_bowtie = "${path_to_bowtie}bowtie";
|
|
5838 }
|
|
5839 }
|
|
5840 else{
|
|
5841 die "The path to bowtie provided ($path_to_bowtie) is invalid (not a directory)!\n";
|
|
5842 }
|
|
5843 }
|
|
5844 else{
|
|
5845 if ($bowtie2){
|
|
5846 $path_to_bowtie = 'bowtie2';
|
|
5847 warn "Path to Bowtie 2 specified as: $path_to_bowtie\n"; }
|
|
5848 else{
|
|
5849 $path_to_bowtie = 'bowtie';
|
|
5850 warn "Path to Bowtie specified as: $path_to_bowtie\n";
|
|
5851 }
|
|
5852 }
|
|
5853
|
|
5854 ### OUTPUT REQUESTED AS BAM FILE
|
|
5855 if ($bam){
|
|
5856 if ($vanilla){
|
|
5857 die "Specifying BAM output is not compatible with \"--vanilla\" format. Please respecify\n\n";
|
|
5858 }
|
|
5859
|
|
5860 ### PATH TO SAMTOOLS
|
|
5861 if (defined $samtools_path){
|
|
5862 # if Samtools was specified as full command
|
|
5863 if ($samtools_path =~ /samtools$/){
|
|
5864 if (-e $samtools_path){
|
|
5865 # Samtools executable found
|
|
5866 }
|
|
5867 else{
|
|
5868 die "Could not find an installation of Samtools at the location $samtools_path. Please respecify\n";
|
|
5869 }
|
|
5870 }
|
|
5871 else{
|
|
5872 unless ($samtools_path =~ /\/$/){
|
|
5873 $samtools_path =~ s/$/\//;
|
|
5874 }
|
|
5875 $samtools_path .= 'samtools';
|
|
5876 if (-e $samtools_path){
|
|
5877 # Samtools executable found
|
|
5878 }
|
|
5879 else{
|
|
5880 die "Could not find an installation of Samtools at the location $samtools_path. Please respecify\n";
|
|
5881 }
|
|
5882 }
|
|
5883
|
|
5884 warn "Alignments will be written out in BAM format. Samtools path provided as: '$samtools_path'\n";
|
|
5885 $bam = 1;
|
|
5886 }
|
|
5887 # Check whether Samtools is in the PATH if no path was supplied by the user
|
|
5888 else{
|
|
5889 if (!system "which samtools >/dev/null 2>&1"){ # STDOUT is binned, STDERR is redirected to STDOUT. Returns 0 if samtools is in the PATH
|
|
5890 $samtools_path = `which samtools`;
|
|
5891 chomp $samtools_path;
|
|
5892 warn "Alignments will be written out in BAM format. Samtools found here: '$samtools_path'\n";
|
|
5893 $bam = 1;
|
|
5894 }
|
|
5895 }
|
|
5896
|
|
5897 unless (defined $samtools_path){
|
|
5898 $bam = 2;
|
|
5899 warn "Did not find Samtools on the system. Alignments will be compressed with GZIP instead (.sam.gz)\n";
|
|
5900 }
|
|
5901 sleep (1);
|
|
5902 }
|
|
5903
|
|
5904
|
|
5905 ####################################
|
|
5906 ### PROCESSING ARGUMENTS
|
|
5907
|
|
5908 ### GENOME FOLDER
|
|
5909 my $genome_folder = shift @ARGV; # mandatory
|
|
5910 unless ($genome_folder){
|
|
5911 warn "Genome folder was not specified!\n";
|
|
5912 print_helpfile();
|
|
5913 exit;
|
|
5914 }
|
|
5915
|
|
5916 ### checking that the genome folder, all subfolders and the required bowtie index files exist
|
|
5917 unless ($genome_folder =~/\/$/){
|
|
5918 $genome_folder =~ s/$/\//;
|
|
5919 }
|
|
5920
|
|
5921 if (chdir $genome_folder){
|
|
5922 my $absolute_genome_folder = getcwd; ## making the genome folder path absolute
|
|
5923 unless ($absolute_genome_folder =~/\/$/){
|
|
5924 $absolute_genome_folder =~ s/$/\//;
|
|
5925 }
|
|
5926 warn "Reference genome folder provided is $genome_folder\t(absolute path is '$absolute_genome_folder)'\n";
|
|
5927 $genome_folder = $absolute_genome_folder;
|
|
5928 }
|
|
5929 else{
|
|
5930 die "Failed to move to $genome_folder: $!\nUSAGE: bismark [options] <genome_folder> {-1 <mates1> -2 <mates2> | <singles>} [<hits>] (--help for more details)\n";
|
|
5931 }
|
|
5932
|
|
5933 my $CT_dir = "${genome_folder}Bisulfite_Genome/CT_conversion/";
|
|
5934 my $GA_dir = "${genome_folder}Bisulfite_Genome/GA_conversion/";
|
|
5935
|
|
5936 if ($bowtie2){ ### Bowtie 2 (new)
|
|
5937 ### checking the integrity of $CT_dir
|
|
5938 chdir $CT_dir or die "Failed to move to directory $CT_dir: $!\n";
|
|
5939 my @CT_bowtie_index = ('BS_CT.1.bt2','BS_CT.2.bt2','BS_CT.3.bt2','BS_CT.4.bt2','BS_CT.rev.1.bt2','BS_CT.rev.2.bt2');
|
|
5940 foreach my $file(@CT_bowtie_index){
|
|
5941 unless (-f $file){
|
|
5942 die "The Bowtie 2 index of the C->T converted genome seems to be faulty ($file). Please run the bismark_genome_preparation before running Bismark.\n";
|
|
5943 }
|
|
5944 }
|
|
5945 ### checking the integrity of $GA_dir
|
|
5946 chdir $GA_dir or die "Failed to move to directory $GA_dir: $!\n";
|
|
5947 my @GA_bowtie_index = ('BS_GA.1.bt2','BS_GA.2.bt2','BS_GA.3.bt2','BS_GA.4.bt2','BS_GA.rev.1.bt2','BS_GA.rev.2.bt2');
|
|
5948 foreach my $file(@GA_bowtie_index){
|
|
5949 unless (-f $file){
|
|
5950 die "The Bowtie 2 index of the G->A converted genome seems to be faulty ($file). Please run bismark_genome_preparation before running Bismark.\n";
|
|
5951 }
|
|
5952 }
|
|
5953 }
|
|
5954
|
|
5955 else{ ### Bowtie 1 (default)
|
|
5956 ### checking the integrity of $CT_dir
|
|
5957 chdir $CT_dir or die "Failed to move to directory $CT_dir: $!\n";
|
|
5958 my @CT_bowtie_index = ('BS_CT.1.ebwt','BS_CT.2.ebwt','BS_CT.3.ebwt','BS_CT.4.ebwt','BS_CT.rev.1.ebwt','BS_CT.rev.2.ebwt');
|
|
5959 foreach my $file(@CT_bowtie_index){
|
|
5960 unless (-f $file){
|
|
5961 die "The Bowtie index of the C->T converted genome seems to be faulty ($file). Please run bismark_genome_preparation before running Bismark.\n";
|
|
5962 }
|
|
5963 }
|
|
5964 ### checking the integrity of $GA_dir
|
|
5965 chdir $GA_dir or die "Failed to move to directory $GA_dir: $!\n";
|
|
5966 my @GA_bowtie_index = ('BS_GA.1.ebwt','BS_GA.2.ebwt','BS_GA.3.ebwt','BS_GA.4.ebwt','BS_GA.rev.1.ebwt','BS_GA.rev.2.ebwt');
|
|
5967 foreach my $file(@GA_bowtie_index){
|
|
5968 unless (-f $file){
|
|
5969 die "The Bowtie index of the G->A converted genome seems to be faulty ($file). Please run bismark_genome_preparation before running Bismark.\n";
|
|
5970 }
|
|
5971 }
|
|
5972 }
|
|
5973
|
|
5974 my $CT_index_basename = "${CT_dir}BS_CT";
|
|
5975 my $GA_index_basename = "${GA_dir}BS_GA";
|
|
5976
|
|
5977 ### INPUT OPTIONS
|
|
5978
|
|
5979 ### SEQUENCE FILE FORMAT
|
|
5980 ### exits if both fastA and FastQ were specified
|
|
5981 if ($fasta and $fastq){
|
|
5982 die "Only one sequence filetype can be specified (fastA or fastQ)\n";
|
|
5983 }
|
|
5984
|
|
5985 ### unless fastA is specified explicitely, fastQ sequence format is expected by default
|
|
5986 if ($fasta){
|
|
5987 print "FastA format specified\n";
|
|
5988 $sequence_format = 'FASTA';
|
|
5989 push @bowtie_options, '-f';
|
|
5990 }
|
|
5991 elsif ($fastq){
|
|
5992 print "FastQ format specified\n";
|
|
5993 $sequence_format = 'FASTQ';
|
|
5994 push @bowtie_options, '-q';
|
|
5995 }
|
|
5996 else{
|
|
5997 $fastq = 1;
|
|
5998 print "FastQ format assumed (by default)\n";
|
|
5999 $sequence_format = 'FASTQ';
|
|
6000 push @bowtie_options, '-q';
|
|
6001 }
|
|
6002
|
|
6003 ### SKIP
|
|
6004 if ($skip){
|
|
6005 warn "Skipping the first $skip reads from the input file\n";
|
|
6006 # push @bowtie_options,"-s $skip";
|
|
6007 }
|
|
6008
|
|
6009 ### UPTO
|
|
6010 if ($qupto){
|
|
6011 warn "Processing sequences up to read no. $qupto from the input file\n";
|
|
6012 if ($bowtie2){
|
|
6013 # push @bowtie_options,"--upto $qupto"; ## slightly changed for Bowtie 2
|
|
6014 }
|
|
6015 else{
|
|
6016 # push @bowtie_options,"--qupto $qupto";
|
|
6017 }
|
|
6018 }
|
|
6019
|
|
6020 ### QUALITY VALUES
|
|
6021 if (($phred33 and $phred64) or ($phred33 and $solexa) or ($phred64 and $solexa)){
|
|
6022 die "You can only specify one type of quality value at a time! (--phred33-quals or --phred64-quals or --solexa-quals)";
|
|
6023 }
|
|
6024 if ($phred33){ ## if nothing else is specified $phred33 will be used as default by both Bowtie 1 and 2.
|
|
6025 # Phred quality values work only when -q is specified
|
|
6026 unless ($fastq){
|
|
6027 die "Phred quality values works only when -q (FASTQ) is specified\n";
|
|
6028 }
|
|
6029 if ($bowtie2){
|
|
6030 push @bowtie_options,"--phred33";
|
|
6031 }
|
|
6032 else{
|
|
6033 push @bowtie_options,"--phred33-quals";
|
|
6034 }
|
|
6035 }
|
|
6036 if ($phred64){
|
|
6037 # Phred quality values work only when -q is specified
|
|
6038 unless ($fastq){
|
|
6039 die "Phred quality values work only when -q (FASTQ) is specified\n";
|
|
6040 }
|
|
6041 if ($bowtie2){
|
|
6042 push @bowtie_options,"--phred64";
|
|
6043 }
|
|
6044 else{
|
|
6045 push @bowtie_options,"--phred64-quals";
|
|
6046 }
|
|
6047 }
|
|
6048 else{
|
|
6049 $phred64 = 0;
|
|
6050 }
|
|
6051
|
|
6052 if ($solexa){
|
|
6053 if ($bowtie2){
|
|
6054 die "The option '--solexa-quals' is not compatible with Bowtie 2. Please respecify!\n";
|
|
6055 }
|
|
6056 # Solexa to Phred value conversion works only when -q is specified
|
|
6057 unless ($fastq){
|
|
6058 die "Conversion from Solexa to Phred quality values works only when -q (FASTQ) is specified\n";
|
|
6059 }
|
|
6060 push @bowtie_options,"--solexa-quals";
|
|
6061 }
|
|
6062 else{
|
|
6063 $solexa = 0;
|
|
6064 }
|
|
6065
|
|
6066 ### ALIGNMENT OPTIONS
|
|
6067
|
|
6068 ### MISMATCHES
|
|
6069 if (defined $mismatches){
|
|
6070 if ($bowtie2){
|
|
6071 if ($mismatches == 0 or $mismatches == 1){
|
|
6072 push @bowtie_options,"-N $mismatches";
|
|
6073 }
|
|
6074 else{
|
|
6075 die "Please set the number of multiseed mismatches for Bowtie 2 with '-N <int>' (where <int> can be 0 or 1)\n";
|
|
6076 }
|
|
6077 }
|
|
6078 else{
|
|
6079 if ($mismatches >= 0 and $mismatches <= 3){
|
|
6080 push @bowtie_options,"-n $mismatches";
|
|
6081 }
|
|
6082 else{
|
|
6083 die "Please set the number of seed mismatches for Bowtie 1 with '-n <int>' (where <int> can be 0,1,2 or 3)\n";
|
|
6084 }
|
|
6085 }
|
|
6086 }
|
|
6087 else{
|
|
6088 unless ($bowtie2){
|
|
6089 push @bowtie_options,"-n 1"; # setting -n to 1 by default (for use with Bowtie only) because it is much quicker than the default mode of -n 2
|
|
6090 }
|
|
6091 }
|
|
6092
|
|
6093 ### SEED LENGTH
|
|
6094 if (defined $seed_length){
|
|
6095 if ($bowtie2){
|
|
6096 push @bowtie_options,"-L $seed_length";
|
|
6097 }
|
|
6098 else{
|
|
6099 push @bowtie_options,"-l $seed_length";
|
|
6100 }
|
|
6101 }
|
|
6102
|
|
6103 ### MISMATCH CEILING
|
|
6104 if (defined $ceiling){
|
|
6105 die "The option '-e' is not compatible with Bowtie 2. Please respecify options\n" if ($bowtie2);
|
|
6106 push @bowtie_options,"-e $ceiling";
|
|
6107 }
|
|
6108
|
|
6109
|
|
6110 ### BOWTIE 2 EFFORT OPTIONS
|
|
6111
|
|
6112 ### CONSECUTIVE SEED EXTENSION FAILS
|
|
6113 if (defined $seed_extension_fails){
|
|
6114 die "The option '-D <int>' is only available when using Bowtie 2\n\n" unless ($bowtie2);
|
|
6115 push @bowtie_options,"-D $seed_extension_fails";
|
|
6116 }
|
|
6117
|
|
6118 ### RE-SEEDING REPETITIVE SEEDS
|
|
6119 if (defined $reseed_repetitive_seeds){
|
|
6120 die "The option '-R <int>' is only available when using Bowtie 2\n\n" unless ($bowtie2);
|
|
6121 push @bowtie_options,"-R $reseed_repetitive_seeds";
|
|
6122 }
|
|
6123
|
|
6124
|
|
6125 ### BOWTIE 2 SCORING OPTIONS
|
|
6126 if ($score_min){
|
|
6127 die "The option '--score_min <func>' is only available when using Bowtie 2\n\n" unless ($bowtie2);
|
|
6128 unless ($score_min =~ /^L,.+,.+$/){
|
|
6129 die "The option '--score_min <func>' needs to be in the format <L,value,value> . Please consult \"setting up functions\" in the Bowtie 2 manual for further information\n\n";
|
|
6130 }
|
|
6131 push @bowtie_options,"--score-min $score_min";
|
|
6132 }
|
|
6133 else{
|
|
6134 if ($bowtie2){
|
|
6135 push @bowtie_options,"--score-min L,0,-0.2"; # default setting, more stringent than normal Bowtie2
|
|
6136 }
|
|
6137 }
|
|
6138
|
|
6139 ### BOWTIE 2 READ GAP OPTIONS
|
|
6140 my ($insertion_open,$insertion_extend,$deletion_open,$deletion_extend);
|
|
6141
|
|
6142 if ($rdg){
|
|
6143 die "The option '--rdg <int1>,<int2>' is only available when using Bowtie 2\n\n" unless ($bowtie2);
|
|
6144 if ($rdg =~ /^(\d+),(\d+)$/){
|
|
6145 $deletion_open = $1;
|
|
6146 $deletion_extend = $2;
|
|
6147 }
|
|
6148 else{
|
|
6149 die "The option '--rdg <int1>,<int2>' needs to be in the format <integer,integer> . Please consult \"setting up functions\" in the Bowtie 2 manual for further information\n\n";
|
|
6150 }
|
|
6151 push @bowtie_options,"--rdg $rdg";
|
|
6152 }
|
|
6153 else{
|
|
6154 $deletion_open = 5;
|
|
6155 $deletion_extend = 3;
|
|
6156 }
|
|
6157
|
|
6158 ### BOWTIE 2 REFERENCE GAP OPTIONS
|
|
6159 if ($rfg){
|
|
6160 die "The option '--rfg <int1>,<int2>' is only available when using Bowtie 2\n\n" unless ($bowtie2);
|
|
6161 if ($rfg =~ /^(\d+),(\d+)$/){
|
|
6162 $insertion_open = $1;
|
|
6163 $insertion_extend = $2;
|
|
6164 }
|
|
6165 else{
|
|
6166 die "The option '--rfg <int1>,<int2>' needs to be in the format <integer,integer> . Please consult \"setting up functions\" in the Bowtie 2 manual for further information\n\n";
|
|
6167 }
|
|
6168 push @bowtie_options,"--rfg $rfg";
|
|
6169 }
|
|
6170 else{
|
|
6171 $insertion_open = 5;
|
|
6172 $insertion_extend = 3;
|
|
6173 }
|
|
6174
|
|
6175
|
|
6176 ### BOWTIE 2 PARALLELIZATION OPTIONS
|
|
6177 if (defined $parallel){
|
|
6178 die "The parallelization switch '-p' only works for Bowtie 2. Please respecify!" unless ($bowtie2);
|
|
6179 }
|
|
6180 if ($bowtie2){
|
|
6181 if ($parallel){
|
|
6182 die "Please select a value for -p of 2 or more!\n" unless ($parallel > 1);
|
|
6183 push @bowtie_options,"-p $parallel";
|
|
6184 push @bowtie_options,'--reorder'; ## re-orders the bowtie 2 output so that it does match the input files. This is abolutely required for parallelization to work.
|
|
6185 print "Each Bowtie 2 instance is going to be run with $parallel threads. Please monitor performance closely and tune down if needed!\n";
|
|
6186 sleep (2);
|
|
6187 }
|
|
6188 }
|
|
6189
|
|
6190 ### REPORTING OPTIONS
|
|
6191
|
|
6192 if ($bowtie2){
|
|
6193 push @bowtie_options,'--ignore-quals'; ## All mismatches will receive penalty for mismatches as if they were of high quality, which is 6 by default
|
|
6194
|
|
6195 ### Option -M is deprecated since Bowtie 2 version 2.0.0 beta7. I'll leave this option commented out for a while
|
|
6196 if(defined $most_valid_alignments){
|
|
6197
|
|
6198 warn "\nThe option -M is now deprecated (as of Bowtie 2 version 2.0.0 beta7). What used to be called -M mode is still the default mode. Use the -D and -R options to adjust the effort expended to find valid alignments.\n\n";
|
|
6199 # push @bowtie_options,"-M $most_valid_alignments";sleep (5);
|
|
6200 }
|
|
6201 # else{
|
|
6202 # push @bowtie_options,'-M 10'; # the default behavior for Bowtie 2 is to report (and sort) up to 500 alignments for a given sequence
|
|
6203 # }
|
|
6204 }
|
|
6205 else{ # Because of the way Bismark works we will always use the reporting option -k 2 (report up to 2 valid alignments) for Bowtie 1
|
|
6206 push @bowtie_options,'-k 2';
|
|
6207 }
|
|
6208
|
|
6209 ### --BEST
|
|
6210 if ($bowtie2){
|
|
6211 if ($best){ # Bowtie 2 does away with the concept of --best, so one can also not select --no-best when Bowtie 2 is to be used
|
|
6212 die "The option '--no-best' is not compatible with Bowtie 2. Please respecify options\n";
|
|
6213 }
|
|
6214 }
|
|
6215 else{
|
|
6216 # --best is the default option for Bowtie 1, specifying --no-best can turn it off (e.g. to speed up alignment process)
|
|
6217 unless ($best){
|
|
6218 push @bowtie_options,'--best';
|
|
6219 }
|
|
6220 }
|
|
6221
|
|
6222 ### VANILLA BISMARK (BOWTIE 1) OUTPUT
|
|
6223 if ($vanilla){
|
|
6224 if ($bowtie2){
|
|
6225 die "The options --bowtie2 and the --vanilla are not compatible. Please respecify!\n\n";
|
|
6226 }
|
|
6227 }
|
|
6228 else{
|
|
6229 $vanilla = 0;
|
|
6230 }
|
|
6231
|
|
6232 ### PAIRED-END MAPPING
|
|
6233 if ($mates1){
|
|
6234 my @mates1 = (split (/,/,$mates1));
|
|
6235 die "Paired-end mapping requires the format: -1 <mates1> -2 <mates2>, please respecify!\n" unless ($mates2);
|
|
6236 my @mates2 = (split(/,/,$mates2));
|
|
6237 unless (scalar @mates1 == scalar @mates2){
|
|
6238 die "Paired-end mapping requires the same amounnt of mate1 and mate2 files, please respecify! (format: -1 <mates1> -2 <mates2>)\n";
|
|
6239 }
|
|
6240 while (1){
|
|
6241 my $mate1 = shift @mates1;
|
|
6242 my $mate2 = shift @mates2;
|
|
6243 last unless ($mate1 and $mate2);
|
|
6244 push @filenames,"$mate1,$mate2";
|
|
6245 }
|
|
6246 if ($bowtie2){
|
|
6247 push @bowtie_options,'--no-mixed'; ## By default Bowtie 2 is not looking for single-end alignments if it can't find concordant or discordant alignments
|
|
6248 push @bowtie_options,'--no-discordant';## By default Bowtie 2 is not looking for discordant alignments if it can't find concordant ones
|
|
6249 }
|
|
6250 }
|
|
6251 elsif ($mates2){
|
|
6252 die "Paired-end mapping requires the format: -1 <mates1> -2 <mates2>, please respecify!\n";
|
|
6253 }
|
|
6254
|
|
6255 ### SINGLE-END MAPPING
|
|
6256 # Single-end mapping will be performed if no mate pairs for paired-end mapping have been specified
|
|
6257 my $singles;
|
|
6258 unless ($mates1 and $mates2){
|
|
6259 $singles = join (',',@ARGV);
|
|
6260 unless ($singles){
|
|
6261 die "\nNo filename supplied! Please specify one or more files for single-end Bismark mapping!\n";
|
|
6262 }
|
|
6263 $singles =~ s/\s/,/g;
|
|
6264 @filenames = (split(/,/,$singles));
|
|
6265 warn "\nFiles to be analysed:\n";
|
|
6266 warn "@filenames\n\n";
|
|
6267 sleep (3);
|
|
6268 }
|
|
6269
|
|
6270 ### MININUM INSERT SIZE (PAIRED-END ONLY)
|
|
6271 if (defined $minins){
|
|
6272 die "-I/--minins can only be used for paired-end mapping!\n\n" if ($singles);
|
|
6273 push @bowtie_options,"--minins $minins";
|
|
6274 }
|
|
6275
|
|
6276 ### MAXIMUM INSERT SIZE (PAIRED-END ONLY)
|
|
6277 if (defined $maxins){
|
|
6278 die "-X/--maxins can only be used for paired-end mapping!\n\n" if ($singles);
|
|
6279 push @bowtie_options,"--maxins $maxins";
|
|
6280 }
|
|
6281 else{
|
|
6282 unless ($singles){
|
|
6283 push @bowtie_options,'--maxins 500';
|
|
6284 }
|
|
6285 }
|
|
6286
|
|
6287 ### QUIET prints nothing besides alignments (suppresses warnings)
|
|
6288 if ($quiet){
|
|
6289 push @bowtie_options,'--quiet';
|
|
6290 }
|
|
6291
|
|
6292 ### CHUNKMBS needed to be increased to avoid memory exhaustion warnings for Bowtie 1, particularly for --best (and paired-end) alignments
|
|
6293 unless ($bowtie2){ # Bowtie 2 does not have a chunkmbs option
|
|
6294 if (defined $chunk){
|
|
6295 push @bowtie_options,"--chunkmbs $chunk";
|
|
6296 }
|
|
6297 else{
|
|
6298 push @bowtie_options,'--chunkmbs 512'; ## setting the default to 512MB (up from 64 default)
|
|
6299 }
|
|
6300 }
|
|
6301
|
|
6302
|
|
6303 ### SUMMARY OF ALL BOWTIE OPTIONS
|
|
6304 my $bowtie_options = join (' ',@bowtie_options);
|
|
6305
|
|
6306
|
|
6307 ### STRAND-SPECIFIC LIBRARIES
|
|
6308 my $directional;
|
|
6309 if ($non_directional){
|
|
6310 die "A library can only be specified to be either non-directional or a PBAT-Seq library. Please respecify!\n\n" if ($pbat);
|
|
6311 warn "Library was specified to be not strand-specific (non-directional), therefore alignments to all four possible bisulfite strands (OT, CTOT, OB and CTOB) will be reported\n";
|
|
6312 sleep (3);
|
|
6313 $directional = 0;
|
|
6314 }
|
|
6315 elsif($pbat){
|
|
6316 die "The option --pbat is currently not compatible with --gzip. Please run alignments with uncompressed temporary files, i.e. lose the option --gzip\n" if ($gzip);
|
|
6317 die "The option --pbat is currently not working for Bowtie 2. Please run alignments in default (i.e. Bowtie 1) mode!\n" if ($bowtie2);
|
|
6318 die "The option --pbat is currently only working with FastQ files. Please respecify (i.e. lose the option -f)!\n" if ($fasta);
|
|
6319
|
|
6320 warn "Library was specified as PBAT-Seq (Post-Bisulfite Adapter Tagging), only performing alignments to the complementary strands (CTOT and CTOB)\n";
|
|
6321 sleep (3);
|
|
6322 $directional = 0;
|
|
6323 }
|
|
6324 else{
|
|
6325 warn "Library is assumed to be strand-specific (directional), alignments to strands complementary to the original top or bottom strands will be ignored (i.e. not performed!)\n";
|
|
6326 sleep (3);
|
|
6327 $directional = 1; # default behaviour
|
|
6328 }
|
|
6329
|
|
6330 ### UNMAPPED SEQUENCE OUTPUT
|
|
6331 $unmapped = 0 unless ($unmapped);
|
|
6332
|
|
6333 ### AMBIGUOUS ALIGNMENT SEQUENCE OUTPUT
|
|
6334 $multi_map = 0 unless ($multi_map);
|
|
6335
|
|
6336
|
|
6337 ### OUTPUT DIRECTORY
|
|
6338
|
|
6339 chdir $parent_dir or die "Failed to move back to current working directory\n";
|
|
6340 if ($output_dir){
|
|
6341 unless ($output_dir =~ /\/$/){
|
|
6342 $output_dir =~ s/$/\//;
|
|
6343 }
|
|
6344
|
|
6345 if (chdir $output_dir){
|
|
6346 $output_dir = getcwd; # making the path absolute
|
|
6347 unless ($output_dir =~ /\/$/){
|
|
6348 $output_dir =~ s/$/\//;
|
|
6349 }
|
|
6350 }
|
|
6351 else{
|
|
6352 mkdir $output_dir or die "Unable to create directory $output_dir $!\n";
|
|
6353 warn "Created output directory $output_dir!\n\n";
|
|
6354 chdir $output_dir or die "Failed to move to $output_dir\n";
|
|
6355 $output_dir = getcwd; # making the path absolute
|
|
6356 unless ($output_dir =~ /\/$/){
|
|
6357 $output_dir =~ s/$/\//;
|
|
6358 }
|
|
6359 }
|
|
6360 warn "Output will be written into the directory: $output_dir\n";
|
|
6361 }
|
|
6362 else{
|
|
6363 $output_dir = '';
|
|
6364 }
|
|
6365
|
|
6366 ### TEMPORARY DIRECTORY for C->T and G->A transcribed files
|
|
6367
|
|
6368 chdir $parent_dir or die "Failed to move back to current working directory\n";
|
|
6369 if ($temp_dir){
|
|
6370 warn "\nUsing temp directory: $temp_dir\n";
|
|
6371 unless ($temp_dir =~ /\/$/){
|
|
6372 $temp_dir =~ s/$/\//;
|
|
6373 }
|
|
6374
|
|
6375 if (chdir $temp_dir){
|
|
6376 $temp_dir = getcwd; # making the path absolute
|
|
6377 unless ($temp_dir =~ /\/$/){
|
|
6378 $temp_dir =~ s/$/\//;
|
|
6379 }
|
|
6380 }
|
|
6381 else{
|
|
6382 mkdir $temp_dir or die "Unable to create directory $temp_dir $!\n";
|
|
6383 warn "Created temporary directory $temp_dir!\n\n";
|
|
6384 chdir $temp_dir or die "Failed to move to $temp_dir\n";
|
|
6385 $temp_dir = getcwd; # making the path absolute
|
|
6386 unless ($temp_dir =~ /\/$/){
|
|
6387 $temp_dir =~ s/$/\//;
|
|
6388 }
|
|
6389 }
|
|
6390 warn "Temporary files will be written into the directory: $temp_dir\n";
|
|
6391 }
|
|
6392 else{
|
|
6393 $temp_dir = '';
|
|
6394 }
|
|
6395
|
|
6396 ### OPTIONAL NON-BS MISMATCH OUTPUT AS EXTRA COLUMN IN SAM FILE
|
|
6397 if ($non_bs_mm){
|
|
6398 if ($vanilla){
|
|
6399 die "Option '--non_bs_mm' may only be specified for output in SAM format. Please respecify!\n";
|
|
6400 }
|
|
6401 }
|
|
6402
|
|
6403 return ($genome_folder,$CT_index_basename,$GA_index_basename,$path_to_bowtie,$sequence_format,$bowtie_options,$directional,$unmapped,$multi_map,$phred64,$solexa,$output_dir,$bowtie2,$vanilla,$sam_no_hd,$skip,$qupto,$temp_dir,$non_bs_mm,$insertion_open,$insertion_extend,$deletion_open,$deletion_extend,$gzip,$bam,$samtools_path,$pbat);
|
|
6404 }
|
|
6405
|
|
6406
|
|
6407
|
|
6408 sub generate_SAM_header{
|
|
6409 print OUT "\@HD\tVN:1.0\tSO:unsorted\n"; # @HD = header, VN = version, SO = sort order
|
|
6410 foreach my $chr (keys %chromosomes){
|
|
6411 my $length = length ($chromosomes{$chr});
|
|
6412 print OUT "\@SQ\tSN:$chr\tLN:$length\n"; # @SQ = sequence, SN = seq name, LN = length
|
|
6413 }
|
|
6414 print OUT "\@PG\tID:Bismark\tVN:$bismark_version\tCL:\"bismark $command_line\"\n"; # @PG = program, ID = unique identifier, PN = program name name, VN = program version
|
|
6415 }
|
|
6416
|
|
6417 ### I would like to thank the following individuals for their valuable contributions to the Bismark SAM output format:
|
|
6418 ### O. Tam (Sep 2010), C. Whelan (2011), E. Vidal (2011), T. McBryan (2011), P. Hickey (2011)
|
|
6419
|
|
6420 sub single_end_SAM_output{
|
|
6421 my ($id,$actual_seq,$methylation_call_params,$qual) = @_;
|
|
6422 my $strand = $methylation_call_params->{$id}->{alignment_strand};
|
|
6423 my $chr = $methylation_call_params->{$id}->{chromosome};
|
|
6424 my $start = $methylation_call_params->{$id}->{position};
|
|
6425 my $stop = $methylation_call_params->{$id}->{end_position};
|
|
6426 my $ref_seq = $methylation_call_params->{$id}->{unmodified_genomic_sequence};
|
|
6427 my $methcall = $methylation_call_params->{$id}->{methylation_call};
|
|
6428 my $read_conversion = $methylation_call_params->{$id}->{read_conversion};
|
|
6429 my $genome_conversion = $methylation_call_params->{$id}->{genome_conversion};
|
|
6430 my $number_of_mismatches;
|
|
6431 if ($bowtie2){
|
|
6432 $number_of_mismatches= $methylation_call_params->{$id}->{alignment_score};
|
|
6433 }
|
|
6434 else{
|
|
6435 $number_of_mismatches= $methylation_call_params->{$id}->{number_of_mismatches};
|
|
6436 }
|
|
6437
|
|
6438 ### This is a description of the bitwise FLAG field which needs to be set for the SAM file taken from: "The SAM Format Specification (v1.4-r985), September 7, 2011"
|
|
6439 ## FLAG: bitwise FLAG. Each bit is explained in the following table:
|
|
6440 ## Bit Description Comment Value
|
|
6441 ## 0x1 template has multiple segments in sequencing 0: single-end 1: paired end value: 2**0 ( 1)
|
|
6442 ## 0x2 each segment properly aligned according to the aligner true only for paired-end alignments value: 2**1 ( 2)
|
|
6443 ## 0x4 segment unmapped --- ---
|
|
6444 ## 0x8 next segment in the template unmapped --- ---
|
|
6445 ## 0x10 SEQ being reverse complemented value: 2**4 ( 16)
|
|
6446 ## 0x20 SEQ of the next segment in the template being reversed value: 2**5 ( 32)
|
|
6447 ## 0x40 the first segment in the template read 1 value: 2**6 ( 64)
|
|
6448 ## 0x80 the last segment in the template read 2 value: 2**7 (128)
|
|
6449 ## 0x100 secondary alignment --- ---
|
|
6450 ## 0x200 not passing quality controls --- ---
|
|
6451 ## 0x400 PCR or optical duplicate --- ---
|
|
6452
|
|
6453 #####
|
|
6454
|
|
6455 my $flag; # FLAG variable used for SAM format.
|
|
6456 if ($strand eq "+"){
|
|
6457 if ($read_conversion eq 'CT' and $genome_conversion eq 'CT'){
|
|
6458 $flag = 0; # 0 for "+" strand (OT)
|
|
6459 }
|
|
6460 elsif ($read_conversion eq 'GA' and $genome_conversion eq 'GA'){
|
|
6461 $flag = 16; # 16 for "-" strand (CTOB, yields information for the original bottom strand)
|
|
6462 }
|
|
6463 else{
|
|
6464 die "Unexpected strand and read/genome conversion: strand: $strand, read conversion: $read_conversion, genome_conversion: $genome_conversion\n\n";
|
|
6465 }
|
|
6466 }
|
|
6467 elsif ($strand eq "-"){
|
|
6468 if ($read_conversion eq 'CT' and $genome_conversion eq 'GA'){
|
|
6469 $flag = 16; # 16 for "-" strand (OB)
|
|
6470 }
|
|
6471 elsif ($read_conversion eq 'GA' and $genome_conversion eq 'CT'){
|
|
6472 $flag = 0; # 0 for "+" strand (CTOT, yields information for the original top strand)
|
|
6473 }
|
|
6474 else{
|
|
6475 die "Unexpected strand and read/genome conversion: strand: $strand, read conversion: $read_conversion, genome_conversion: $genome_conversion\n\n";
|
|
6476 }
|
|
6477 }
|
|
6478 else{
|
|
6479 die "Unexpected strand information: $strand\n\n";
|
|
6480 }
|
|
6481
|
|
6482 #####
|
|
6483
|
|
6484 my $mapq = 255; # Assume mapping quality is unavailable
|
|
6485
|
|
6486 #####
|
|
6487
|
|
6488 my $cigar;
|
|
6489 if ($bowtie2){
|
|
6490 $cigar = $methylation_call_params->{$id}->{CIGAR}; # Actual CIGAR string reported by Bowtie 2
|
|
6491 }
|
|
6492 else{
|
|
6493 $cigar = length($actual_seq) . "M"; # Bowtie 1 output does not contain indels (only matches and mismatches)
|
|
6494 }
|
|
6495
|
|
6496 #####
|
|
6497
|
|
6498 my $rnext = "*"; # Paired-end variable
|
|
6499
|
|
6500 #####
|
|
6501
|
|
6502 my $pnext = 0; # Paired-end variable
|
|
6503
|
|
6504 #####
|
|
6505
|
|
6506 my $tlen = 0; # Paired-end variable
|
|
6507
|
|
6508 #####
|
|
6509
|
|
6510 if ($read_conversion eq 'CT'){
|
|
6511 $ref_seq = substr($ref_seq, 0, length($ref_seq) - 2); # Removes additional nucleotides from the 3' end. This only works for the original top or bottom strands
|
|
6512 }
|
|
6513 else{
|
|
6514 $ref_seq = substr($ref_seq, 2, length($ref_seq) - 2); # Removes additional nucleotides from the 5' end. This works for the complementary strands in non-directional libraries
|
|
6515 }
|
|
6516
|
|
6517 if ($strand eq '-'){
|
|
6518 $actual_seq = revcomp($actual_seq); # Sequence represented on the forward genomic strand
|
|
6519 $ref_seq = revcomp($ref_seq); # Required for comparison with actual sequence
|
|
6520 $qual = reverse $qual; # if the sequence was reverse-complemented the quality string needs to be reversed as well
|
|
6521 }
|
|
6522
|
|
6523 #####
|
|
6524
|
|
6525 my $hemming_dist = hemming_dist($actual_seq,$ref_seq); # Edit distance to the reference, i.e. minimal number of one-nucleotide edits needed to transform the read string
|
|
6526 # into the reference string. hemming_dist()
|
|
6527 if ($bowtie2){
|
|
6528 $hemming_dist += $methylation_call_params->{$id}->{indels}; # Adding the number of inserted/deleted bases which we parsed while getting the genomic sequence
|
|
6529 }
|
|
6530
|
|
6531 my $NM_tag = "NM:i:$hemming_dist"; # Optional tag NM: edit distance based on nucleotide differences
|
|
6532
|
|
6533 #####
|
|
6534
|
|
6535 my $XX_tag = make_mismatch_string($actual_seq, $ref_seq); # Optional tag XX: string providing mismatched reference bases in the alignment (NO indel information!)
|
|
6536
|
|
6537 #####
|
|
6538
|
|
6539 my $XM_tag; # Optional tag XM: Methylation Call String
|
|
6540 if ($strand eq '+'){
|
|
6541 $XM_tag = "XM:Z:$methcall";
|
|
6542 }
|
|
6543 elsif ($strand eq '-'){
|
|
6544 $XM_tag = 'XM:Z:'.reverse $methcall; # if the sequence was reverse-complemented the methylation call string needs to be reversed as well
|
|
6545 }
|
|
6546
|
|
6547 #####
|
|
6548
|
|
6549 my $XR_tag = "XR:Z:$read_conversion"; # Optional tag XR: Read Conversion
|
|
6550
|
|
6551 #####
|
|
6552
|
|
6553 my $XG_tag = "XG:Z:$genome_conversion"; # Optional tag XG: Genome Conversion
|
|
6554
|
|
6555 #####
|
|
6556
|
|
6557 # Optionally calculating number of mismatches for Bowtie 2 alignments
|
|
6558
|
|
6559 if ($non_bs_mm) {
|
|
6560 if ($bowtie2) {
|
|
6561
|
|
6562 $number_of_mismatches =~ s/-//; # removing the minus sign
|
|
6563
|
|
6564 ### if Bowtie 2 was used we need to analyse the CIGAR string whether the read contained any indels to determine the number of mismatches
|
|
6565 if ($cigar =~ /(D|I)/) {
|
|
6566 # warn "$cigar\n";
|
|
6567
|
|
6568 # parsing CIGAR string
|
|
6569 my @len = split (/\D+/,$cigar); # storing the length per operation
|
|
6570 my @ops = split (/\d+/,$cigar); # storing the operation
|
|
6571 shift @ops; # remove the empty first element
|
|
6572 die "CIGAR string contained a non-matching number of lengths and operations\n" unless (scalar @len == scalar @ops);
|
|
6573
|
|
6574 foreach (0..$#len) {
|
|
6575 if ($ops[$_] eq 'M') {
|
|
6576 # warn "skipping\n";
|
|
6577 next; # irrelevant
|
|
6578 }
|
|
6579 elsif ($ops[$_] eq 'I') { # insertion in the read sequence
|
|
6580 $number_of_mismatches -= $insertion_open;
|
|
6581 $number_of_mismatches -= $len[$_] * $insertion_extend;
|
|
6582 # warn "Insertion: Subtracting $ops[$_], length $len[$_], open: $insertion_open, extend: $insertion_extend\n";
|
|
6583 }
|
|
6584 elsif ($ops[$_] eq 'D') { # deletion in the read sequence
|
|
6585 $number_of_mismatches -= $deletion_open;
|
|
6586 $number_of_mismatches -= $len[$_] * $deletion_extend;
|
|
6587 # warn "Deletion: Subtracting $ops[$_], length $len[$_], open: $deletion_open, extend: $deletion_extend\n";
|
|
6588 }
|
|
6589 elsif ($cigar =~ tr/[NSHPX=]//) { # if these (for standard mapping) illegal characters exist we die
|
|
6590 die "The CIGAR string contained illegal CIGAR operations in addition to 'M', 'I' and 'D': $cigar\n";
|
|
6591 }
|
|
6592 else {
|
|
6593 die "The CIGAR string contained undefined CIGAR operations in addition to 'M', 'I' and 'D': $cigar\n";
|
|
6594 }
|
|
6595 }
|
|
6596 # warn "Alignment score $number_of_mismatches\n";
|
|
6597 # print "Mismatches $number_of_mismatches\n\n";
|
|
6598 }
|
|
6599 ### Now we have InDel corrected alignment scores
|
|
6600
|
|
6601 ### if the actual sequence contained Ns we need to adjust the number of mismatches. Ns receive a penalty of -1, but normal mismatches receive -6. This might still break if the
|
|
6602 ### sequence contained more than 5 Ns, but this should occur close to never
|
|
6603
|
|
6604 my $seq_N_count = $number_of_mismatches % 6; # modulo 6 will return the integer rest after the division
|
|
6605 # warn "N count: $seq_N_count\n";
|
|
6606 $number_of_mismatches = int ($number_of_mismatches / 6) + $seq_N_count;
|
|
6607 # warn "MM $number_of_mismatches\n";
|
|
6608 }
|
|
6609 }
|
|
6610
|
|
6611 ####
|
|
6612
|
|
6613 my $XA_tag = "XA:Z:$number_of_mismatches";
|
|
6614
|
|
6615 #####
|
|
6616
|
|
6617 # SAM format: QNAME, FLAG, RNAME, 1-based POS, MAPQ, CIGAR, RNEXT, PNEXT, TLEN, SEQ, QUAL, optional fields
|
|
6618 ### optionally print number of non-bisulfite mismatches
|
|
6619 if ($non_bs_mm){
|
|
6620 print OUT join("\t",($id,$flag,$chr,$start,$mapq,$cigar,$rnext,$pnext,$tlen,$actual_seq,$qual,$NM_tag,$XX_tag,$XM_tag,$XR_tag,$XG_tag,$XA_tag)),"\n";
|
|
6621 }
|
|
6622 else{ # default
|
|
6623 # SAM format: QNAME, FLAG, RNAME, 1-based POS, MAPQ, CIGAR, RNEXT, PNEXT, TLEN, SEQ, QUAL, optional fields
|
|
6624 print OUT join("\t",($id,$flag,$chr,$start,$mapq,$cigar,$rnext,$pnext,$tlen,$actual_seq,$qual,$NM_tag,$XX_tag,$XM_tag,$XR_tag,$XG_tag)),"\n";
|
|
6625 }
|
|
6626 }
|
|
6627
|
|
6628 sub paired_end_SAM_output{
|
|
6629 my ($id,$actual_seq_1,$actual_seq_2,$methylation_call_params,$qual_1,$qual_2) = @_;
|
|
6630 my $strand_1 = $methylation_call_params->{$id}->{alignment_read_1}; # Bowtie 1 only reports the read 1 alignment strand
|
|
6631 my $strand_2 = $methylation_call_params->{$id}->{alignment_read_2};
|
|
6632 my $chr = $methylation_call_params->{$id}->{chromosome};
|
|
6633 my $ref_seq_1 = $methylation_call_params->{$id}->{unmodified_genomic_sequence_1};
|
|
6634 my $ref_seq_2 = $methylation_call_params->{$id}->{unmodified_genomic_sequence_2};
|
|
6635 my $methcall_1 = $methylation_call_params->{$id}->{methylation_call_1};
|
|
6636 my $methcall_2 = $methylation_call_params->{$id}->{methylation_call_2};
|
|
6637 my $read_conversion_1 = $methylation_call_params->{$id}->{read_conversion_1};
|
|
6638 my $read_conversion_2 = $methylation_call_params->{$id}->{read_conversion_2};
|
|
6639 my $genome_conversion = $methylation_call_params->{$id}->{genome_conversion};
|
|
6640
|
|
6641 my $id_1 = $id.'/1';
|
|
6642 my $id_2 = $id.'/2';
|
|
6643
|
|
6644 # Allows all degenerate nucleotide sequences in reference genome
|
|
6645 die "Reference sequence ($ref_seq_1) contains invalid nucleotides!\n" if $ref_seq_1 =~ /[^ACTGNRYMKSWBDHV]/i;
|
|
6646 die "Reference sequence ($ref_seq_2) contains invalid nucleotides!\n" if $ref_seq_2 =~ /[^ACTGNRYMKSWBDHV]/i;
|
|
6647
|
|
6648 my $index; # used to store the srand origin of the alignment in a less convoluted way
|
|
6649
|
|
6650 if ($read_conversion_1 eq 'CT' and $genome_conversion eq 'CT'){
|
|
6651 $index = 0; ## this is OT (original top strand)
|
|
6652 }
|
|
6653 elsif ($read_conversion_1 eq 'GA' and $genome_conversion eq 'GA'){
|
|
6654 $index = 1; ## this is CTOB (complementary to OB)
|
|
6655 }
|
|
6656 elsif ($read_conversion_1 eq 'GA' and $genome_conversion eq 'CT'){
|
|
6657 $index = 2; ## this is CTOT (complementary to OT)
|
|
6658 }
|
|
6659 elsif ($read_conversion_1 eq 'CT' and $genome_conversion eq 'GA'){
|
|
6660 $index = 3; ## this is OB (original bottom)
|
|
6661 }
|
|
6662 else {
|
|
6663 die "Unexpected combination of read 1 and genome conversion: $read_conversion_1 / $genome_conversion\n";
|
|
6664 }
|
|
6665
|
|
6666 my $number_of_mismatches_1;
|
|
6667 my $number_of_mismatches_2;
|
|
6668
|
|
6669 if ($bowtie2){ # Bowtie 2 reports always as read 1 then read 2, so this is fine
|
|
6670 $number_of_mismatches_1 = $methylation_call_params->{$id}->{alignment_score_1}; # only needed for custom allele-specific output, not the default!
|
|
6671 $number_of_mismatches_2 = $methylation_call_params->{$id}->{alignment_score_2};
|
|
6672 }
|
|
6673 else{ # Bowtie 1 reports always the leftmost read first. That means we have to reverse the strings if the first read aligned in reverse orientation
|
|
6674 if ($index == 2 or $index == 3){ # CTOT or OB
|
|
6675 $number_of_mismatches_1 = $methylation_call_params->{$id}->{number_of_mismatches_2}; # only needed for custom allele-specific output, not the default!
|
|
6676 $number_of_mismatches_2 = $methylation_call_params->{$id}->{number_of_mismatches_1};
|
|
6677 }
|
|
6678 else{ # if the first read aligned in forward direction it is like for Bowtie 2
|
|
6679 $number_of_mismatches_1 = $methylation_call_params->{$id}->{number_of_mismatches_1}; # only needed for custom allele-specific output, not the default!
|
|
6680 $number_of_mismatches_2 = $methylation_call_params->{$id}->{number_of_mismatches_2};
|
|
6681 }
|
|
6682 }
|
|
6683
|
|
6684
|
|
6685
|
|
6686 ### we need to remove 2 bp of the genomic sequence as we were extracting read + 2bp long fragments to make a methylation call at the
|
|
6687 ### first or last position.
|
|
6688
|
|
6689 if ($index == 0 or $index == 3){ # OT or OB
|
|
6690 $ref_seq_1 = substr($ref_seq_1,0,length($ref_seq_1)-2);
|
|
6691 $ref_seq_2 = substr($ref_seq_2,2,length($ref_seq_2)-2);
|
|
6692 }
|
|
6693 else{ # CTOT or CTOB
|
|
6694 $ref_seq_1 = substr($ref_seq_1,2,length($ref_seq_1)-2);
|
|
6695 $ref_seq_2 = substr($ref_seq_2,0,length($ref_seq_2)-2);
|
|
6696 }
|
|
6697
|
|
6698 #####
|
|
6699
|
|
6700 my $start_read_1;
|
|
6701 my $start_read_2;
|
|
6702 # adjusting end positions
|
|
6703
|
|
6704 if ($bowtie2){
|
|
6705 $start_read_1 = $methylation_call_params->{$id}->{position_1};
|
|
6706 $start_read_2 = $methylation_call_params->{$id}->{position_2};
|
|
6707 }
|
|
6708 else{ # Bowtie 1 output. $strand_1 stores the alignment of Read 1
|
|
6709 if ($strand_1 eq '+'){ # Read 1 aligns to the + strand
|
|
6710 $start_read_1 = $methylation_call_params->{$id}->{start_seq_1};
|
|
6711 $start_read_2 = $methylation_call_params->{$id}->{alignment_end} - length ($actual_seq_2) + 1;
|
|
6712 }
|
|
6713 else{ # read 1 is on the - strand
|
|
6714 $start_read_1 = $methylation_call_params->{$id}->{alignment_end} - length ($actual_seq_1) + 1;
|
|
6715 $start_read_2 = $methylation_call_params->{$id}->{start_seq_1};
|
|
6716 }
|
|
6717 }
|
|
6718
|
|
6719 #####
|
|
6720
|
|
6721 my $end_read_1;
|
|
6722 my $end_read_2;
|
|
6723 # adjusting end positions
|
|
6724
|
|
6725 if ($bowtie2){
|
|
6726 $end_read_1 = $methylation_call_params->{$id}->{end_position_1};
|
|
6727 $end_read_2 = $methylation_call_params->{$id}->{end_position_2};
|
|
6728 }
|
|
6729 else{ # Bowtie 1 output. $strand_1 stores the alignment of Read 1
|
|
6730 if ($strand_1 eq '+'){ # Read 1 aligns to the + strand
|
|
6731 $end_read_1 = $methylation_call_params->{$id}->{start_seq_1} + length ($actual_seq_1)-1;
|
|
6732 $end_read_2 = $methylation_call_params->{$id}->{alignment_end};
|
|
6733 }
|
|
6734 else{
|
|
6735 $end_read_1 = $methylation_call_params->{$id}->{alignment_end};
|
|
6736 $end_read_2 = $methylation_call_params->{$id}->{start_seq_1} + length ($actual_seq_2)-1;
|
|
6737 }
|
|
6738 }
|
|
6739
|
|
6740 #####
|
|
6741
|
|
6742 ### This is a description of the bitwise FLAG field which needs to be set for the SAM file taken from: "The SAM Format Specification (v1.4-r985), September 7, 2011"
|
|
6743 ## FLAG: bitwise FLAG. Each bit is explained in the following table:
|
|
6744 ## Bit Description Comment Value
|
|
6745 ## 0x1 template having multiple segments in sequencing 0: single-end 1: paired end value: 2^^0 ( 1)
|
|
6746 ## 0x2 each segment properly aligned according to the aligner true only for paired-end alignments value: 2^^1 ( 2)
|
|
6747 ## 0x4 segment unmapped --- ---
|
|
6748 ## 0x8 next segment in the template unmapped --- ---
|
|
6749 ## 0x10 SEQ being reverse complemented - strand alignment value: 2^^4 ( 16)
|
|
6750 ## 0x20 SEQ of the next segment in the template being reversed + strand alignment value: 2^^5 ( 32)
|
|
6751 ## 0x40 the first segment in the template read 1 value: 2^^6 ( 64)
|
|
6752 ## 0x80 the last segment in the template read 2 value: 2^^7 (128)
|
|
6753 ## 0x100 secondary alignment --- ---
|
|
6754 ## 0x200 not passing quality controls --- ---
|
|
6755 ## 0x400 PCR or optical duplicate --- ---
|
|
6756
|
|
6757 ### As the FLAG value do not consider that there might be 4 different bisulfite strands of DNA, we are trying to make FLAG tags which take the strand identity into account
|
|
6758
|
|
6759 # strands OT and CTOT will be treated as aligning to the top strand (both sequences are scored as aligning to the top strand)
|
|
6760 # strands OB and CTOB will be treated as aligning to the bottom strand (both sequences are scored as reverse complemented sequences)
|
|
6761
|
|
6762 my $flag_1; # FLAG variable used for SAM format
|
|
6763 my $flag_2;
|
|
6764
|
|
6765 if ($index == 0){ # OT
|
|
6766 $flag_1 = 67; # Read 1 is on the + strand (1+2+64) (Read 2 is technically reverse-complemented, but we do not score it)
|
|
6767 $flag_2 = 131; # Read 2 is on - strand but informative for the OT (1+2+128)
|
|
6768 }
|
|
6769 elsif ($index == 1){ # CTOB
|
|
6770 $flag_1 = 115; # Read 1 is on the + strand, we score for OB (1+2+16+32+64)
|
|
6771 $flag_2 = 179; # Read 2 is on the - strand (1+2+16+32+128)
|
|
6772 }
|
|
6773 elsif ($index == 2){ # CTOT
|
|
6774 $flag_1 = 67; # Read 1 is on the - strand (CTOT) strand, but we score it for OT (1+2+64)
|
|
6775 $flag_2 = 131; # Read 2 is on the + strand, score it for OT (1+2+128)
|
|
6776 }
|
|
6777 elsif ($index == 3){ # OB
|
|
6778 $flag_1 = 115; # Read 1 is on the - strand, we score for OB (1+2+16+32+64)
|
|
6779 $flag_2 = 179; # Read 2 is on the + strand (1+2+16+32+128)
|
|
6780 }
|
|
6781
|
|
6782 #####
|
|
6783
|
|
6784 my $mapq = 255; # Mapping quality is unavailable
|
|
6785
|
|
6786 #####
|
|
6787
|
|
6788 my $cigar_1;
|
|
6789 my $cigar_2;
|
|
6790
|
|
6791 if ($bowtie2){
|
|
6792 $cigar_1 = $methylation_call_params->{$id}->{CIGAR_1}; # Actual CIGAR string reported by Bowtie 2
|
|
6793 $cigar_2 = $methylation_call_params->{$id}->{CIGAR_2};
|
|
6794 }
|
|
6795 else{
|
|
6796 $cigar_1 = length($actual_seq_1) . "M"; # Assume no indels for Bowtie 1 mapping (only matches and mismatches)
|
|
6797 $cigar_2 = length($actual_seq_2) . "M";
|
|
6798 }
|
|
6799
|
|
6800 #####
|
|
6801
|
|
6802 my $rnext = '='; # Chromosome of mate; applies to both reads
|
|
6803
|
|
6804 #####
|
|
6805
|
|
6806 my $pnext_1 = $start_read_2; # Leftmost position of mate
|
|
6807 my $pnext_2 = $start_read_1;
|
|
6808
|
|
6809 #####
|
|
6810
|
|
6811 my $tlen_1; # signed observed Template LENgth (or inferred fragment size)
|
|
6812 my $tlen_2;
|
|
6813
|
|
6814 if ($bowtie2){
|
|
6815
|
|
6816 if ($start_read_1 <= $start_read_2){
|
|
6817
|
|
6818 # Read 1 alignment is leftmost
|
|
6819
|
|
6820 if ($end_read_2 >= $end_read_1){
|
|
6821
|
|
6822 # -------------------------> read 1 reads overlapping
|
|
6823 # <------------------------- read 2
|
|
6824 #
|
|
6825 # or
|
|
6826 #
|
|
6827 # -------------------------> read 1
|
|
6828 # <----------------------- read 2 read 2 contained within read 1
|
|
6829 #
|
|
6830 # or
|
|
6831 #
|
|
6832 # -------------------------> read 1 reads 1 and 2 exactly overlapping
|
|
6833 # <------------------------- read 2
|
|
6834 #
|
|
6835
|
|
6836 # dovetailing of reads is not enabled for Bowtie 2 alignments
|
|
6837
|
|
6838 $tlen_1 = $end_read_2 - $start_read_1 + 1; # Leftmost read has a + sign,
|
|
6839 $tlen_2 = $start_read_1 - $end_read_2 - 1; # Rightmost read has a - sign
|
|
6840 }
|
|
6841 elsif ($end_read_2 < $end_read_1){
|
|
6842
|
|
6843 # -------------------------> read 1
|
|
6844 # <----------- read 2 read 2 contained within read 1
|
|
6845 #
|
|
6846 # or
|
|
6847 #
|
|
6848 # -------------------------> read 1
|
|
6849 # <----------- read 2 read 2 contained within read 1
|
|
6850
|
|
6851 # start and end of read 2 are fully contained within read 1
|
|
6852 $tlen_1 = 0; # Set as 0 when the information is unavailable
|
|
6853 $tlen_2 = 0; # Set as 0 when the information is unavailable
|
|
6854 }
|
|
6855
|
|
6856 }
|
|
6857
|
|
6858 elsif ($start_read_2 < $start_read_1){
|
|
6859
|
|
6860 if ($end_read_1 >= $end_read_2){
|
|
6861
|
|
6862 # Read 2 alignment is leftmost
|
|
6863
|
|
6864 # -------------------------> read 2 reads overlapping
|
|
6865 # <------------------------- read 1
|
|
6866 #
|
|
6867 # or
|
|
6868 #
|
|
6869 # -------------------------> read 2
|
|
6870 # <----------------------- read 1 read 1 contained within read 2
|
|
6871 #
|
|
6872 #
|
|
6873
|
|
6874 $tlen_2 = $end_read_1 - $start_read_2 + 1; # Leftmost read has a + sign,
|
|
6875 $tlen_1 = $start_read_2 - $end_read_1 - 1; # Rightmost read has a - sign
|
|
6876 }
|
|
6877 elsif ($end_read_1 < $end_read_2){
|
|
6878
|
|
6879 # -------------------------> read 2
|
|
6880 # <----------- read 1 read 1 contained within read 2
|
|
6881 #
|
|
6882 # or
|
|
6883 #
|
|
6884 # -------------------------> read 2
|
|
6885 # <----------- read 1 read 1 contained within read 2
|
|
6886
|
|
6887 # start and end of read 1 are fully contained within read 2
|
|
6888 $tlen_1 = 0; # Set as 0 when the information is unavailable
|
|
6889 $tlen_2 = 0; # Set as 0 when the information is unavailable
|
|
6890 }
|
|
6891 }
|
|
6892 }
|
|
6893
|
|
6894 else{ # Bowtie 1
|
|
6895
|
|
6896 if ($end_read_2 >= $end_read_1){
|
|
6897 # Read 1 alignment is leftmost
|
|
6898 # -------------------------> read 1
|
|
6899 # <------------------------- read 2
|
|
6900 # this is the most extreme case for Bowtie 1 alignments, reads do not contain each other, also no dovetailing
|
|
6901
|
|
6902 $tlen_1 = $end_read_2 - $start_read_1 + 1; # Leftmost read has a + sign,
|
|
6903 $tlen_2 = $start_read_1 - $end_read_2 - 1; # Rightmost read has a - sign
|
|
6904 }
|
|
6905 else{
|
|
6906 # Read 2 alignment is leftmost
|
|
6907 # -------------------------> read 2
|
|
6908 # <------------------------- read 1
|
|
6909 # this is the most extreme case for Bowtie 1 alignments, reads do not contain each other, also no dovetailing
|
|
6910
|
|
6911 $tlen_2 = $end_read_1 - $start_read_2 + 1; # Leftmost read has a + sign,
|
|
6912 $tlen_1 = $start_read_2 - $end_read_1 - 1; # Rightmost read has a - sign
|
|
6913 }
|
|
6914 }
|
|
6915
|
|
6916 #####
|
|
6917
|
|
6918 # adjusting the strand of the sequence before we use them to generate mismatch strings
|
|
6919 if ($strand_1 eq '-'){
|
|
6920 $actual_seq_1 = revcomp($actual_seq_1); # Sequence represented on the forward genomic strand
|
|
6921 $ref_seq_1 = revcomp($ref_seq_1); # Required for comparison with actual sequence
|
|
6922 $qual_1 = reverse $qual_1; # we need to reverse the quality string as well
|
|
6923 }
|
|
6924 if ($strand_2 eq '-'){
|
|
6925 $actual_seq_2 = revcomp($actual_seq_2); # Mate sequence represented on the forward genomic strand
|
|
6926 $ref_seq_2 = revcomp($ref_seq_2); # Required for comparison with actual sequence
|
|
6927 $qual_2 = reverse $qual_2; # If the sequence gets reverse complemented we reverse the quality string as well
|
|
6928 }
|
|
6929
|
|
6930 # print "$actual_seq_1\n$ref_seq_1\n\n";
|
|
6931 # print "$actual_seq_2\n$ref_seq_2\n\n";
|
|
6932
|
|
6933 #####
|
|
6934
|
|
6935 my $hemming_dist_1 = hemming_dist($actual_seq_1,$ref_seq_1); # Minimal number of one-nucleotide edits needed to transform the read string into the reference sequence
|
|
6936 my $hemming_dist_2 = hemming_dist($actual_seq_2,$ref_seq_2);
|
|
6937 if ($bowtie2){
|
|
6938 $hemming_dist_1 += $methylation_call_params->{$id}->{indels_1}; # Adding the number of inserted/deleted bases which we parsed while getting the genomic sequence
|
|
6939 $hemming_dist_2 += $methylation_call_params->{$id}->{indels_2}; # Adding the number of inserted/deleted bases which we parsed while getting the genomic sequence
|
|
6940 }
|
|
6941 my $NM_tag_1 = "NM:i:$hemming_dist_1"; # Optional tag NM: edit distance based on nucleotide differences
|
|
6942 my $NM_tag_2 = "NM:i:$hemming_dist_2"; # Optional tag NM: edit distance based on nucleotide differences
|
|
6943
|
|
6944 #####
|
|
6945
|
|
6946 my $XX_tag_1 = make_mismatch_string($actual_seq_1,$ref_seq_1); # Optional tag XX: String providing mismatched reference bases in the alignment (NO indel information!)
|
|
6947 my $XX_tag_2 = make_mismatch_string($actual_seq_2,$ref_seq_2);
|
|
6948
|
|
6949 #####
|
|
6950
|
|
6951 my $XM_tag_1; # Optional tag XM: Methylation call string
|
|
6952 my $XM_tag_2;
|
|
6953
|
|
6954 if ($strand_1 eq '-'){
|
|
6955 $XM_tag_1 = 'XM:Z:'.reverse $methcall_1; # Needs to be reversed if the sequence was reverse complemented
|
|
6956 }
|
|
6957 else{
|
|
6958 $XM_tag_1 = "XM:Z:$methcall_1";
|
|
6959 }
|
|
6960
|
|
6961 if ($strand_2 eq '-'){
|
|
6962 $XM_tag_2 = 'XM:Z:'.reverse $methcall_2; # Needs to be reversed if the sequence was reverse complemented
|
|
6963 }
|
|
6964 else{
|
|
6965 $XM_tag_2 = "XM:Z:$methcall_2";
|
|
6966 }
|
|
6967
|
|
6968 #####
|
|
6969
|
|
6970 my $XR_tag_1 = "XR:Z:$read_conversion_1"; # Optional tag XR: Read 1 conversion state
|
|
6971 my $XR_tag_2 = "XR:Z:$read_conversion_2"; # Optional tag XR: Read 2 conversion state
|
|
6972
|
|
6973 #####
|
|
6974
|
|
6975 my $XG_tag = "XG:Z:$genome_conversion"; # Optional tag XG: Genome Conversion state; valid for both reads
|
|
6976
|
|
6977 #####
|
|
6978
|
|
6979 # Optionally calculating number of mismatches for Bowtie 2 alignments
|
|
6980
|
|
6981 if ($non_bs_mm) {
|
|
6982 if ($bowtie2) {
|
|
6983
|
|
6984 $number_of_mismatches_1 =~ s/-//; # removing the minus sign
|
|
6985 $number_of_mismatches_2 =~ s/-//;
|
|
6986
|
|
6987 ### if Bowtie 2 was used we need to analyse the CIGAR strings whether the reads contained any indels to determine the number of mismatches
|
|
6988
|
|
6989 ### CIGAR 1
|
|
6990 if ($cigar_1 =~ /(D|I)/) {
|
|
6991 # warn "$cigar_1\n";
|
|
6992
|
|
6993 # parsing CIGAR string
|
|
6994 my @len = split (/\D+/,$cigar_1); # storing the length per operation
|
|
6995 my @ops = split (/\d+/,$cigar_1); # storing the operation
|
|
6996 shift @ops; # remove the empty first element
|
|
6997 die "CIGAR string '$cigar_1' contained a non-matching number of lengths and operations\n" unless (scalar @len == scalar @ops);
|
|
6998
|
|
6999 foreach (0..$#len) {
|
|
7000 if ($ops[$_] eq 'M') {
|
|
7001 # warn "skipping\n";
|
|
7002 next; # irrelevant
|
|
7003 }
|
|
7004 elsif ($ops[$_] eq 'I') { # insertion in the read sequence
|
|
7005 $number_of_mismatches_1 -= $insertion_open;
|
|
7006 $number_of_mismatches_1 -= $len[$_] * $insertion_extend;
|
|
7007 # warn "Insertion: Subtracting $ops[$_], length $len[$_], open: $insertion_open, extend: $insertion_extend\n";
|
|
7008 }
|
|
7009 elsif ($ops[$_] eq 'D') { # deletion in the read sequence
|
|
7010 $number_of_mismatches_1 -= $deletion_open;
|
|
7011 $number_of_mismatches_1 -= $len[$_] * $deletion_extend;
|
|
7012 # warn "Deletion: Subtracting $ops[$_], length $len[$_], open: $deletion_open, extend: $deletion_extend\n";
|
|
7013 }
|
|
7014 elsif ($cigar_1 =~ tr/[NSHPX=]//) { # if these (for standard mapping) illegal characters exist we die
|
|
7015 die "The CIGAR string contained illegal CIGAR operations in addition to 'M', 'I' and 'D': $cigar_1\n";
|
|
7016 }
|
|
7017 else {
|
|
7018 die "The CIGAR string contained undefined CIGAR operations in addition to 'M', 'I' and 'D': $cigar_1\n";
|
|
7019 }
|
|
7020 }
|
|
7021
|
|
7022 # warn "Alignment score $number_of_mismatches_1\n";
|
|
7023 # print "Mismatches $number_of_mismatches_1\n\n";
|
|
7024 }
|
|
7025
|
|
7026 ### CIGAR 2
|
|
7027 if ($cigar_2 =~ /(D|I)/) {
|
|
7028 # warn "$cigar_2\n";
|
|
7029
|
|
7030 # parsing CIGAR string
|
|
7031 my @len = split (/\D+/,$cigar_2); # storing the length per operation
|
|
7032 my @ops = split (/\d+/,$cigar_2); # storing the operation
|
|
7033 shift @ops; # remove the empty first element
|
|
7034 die "CIGAR string '$cigar_2' contained a non-matching number of lengths and operations\n" unless (scalar @len == scalar @ops);
|
|
7035
|
|
7036 foreach (0..$#len) {
|
|
7037 if ($ops[$_] eq 'M') {
|
|
7038 # warn "skipping\n";
|
|
7039 next; #irrelevant
|
|
7040 }
|
|
7041 elsif ($ops[$_] eq 'I') { # insertion in the read sequence
|
|
7042 $number_of_mismatches_2 -= $insertion_open;
|
|
7043 $number_of_mismatches_2 -= $len[$_] * $insertion_extend;
|
|
7044 # warn "Insertion: Subtracting $ops[$_], length $len[$_], open: $insertion_open, extend: $insertion_extend\n";
|
|
7045 }
|
|
7046 elsif ($ops[$_] eq 'D') { # deletion in the read sequence
|
|
7047 $number_of_mismatches_2 -= $deletion_open;
|
|
7048 $number_of_mismatches_2 -= $len[$_] * $deletion_extend;
|
|
7049 # warn "Deletion: Subtracting $ops[$_], length $len[$_], open: $deletion_open, extend: $deletion_extend\n";
|
|
7050 }
|
|
7051 elsif ($cigar_2 =~ tr/[NSHPX=]//) { # if these (for standard mapping) illegal characters exist we die
|
|
7052 die "The CIGAR string contained illegal CIGAR operations in addition to 'M', 'I' and 'D': $cigar_2\n";
|
|
7053 }
|
|
7054 else {
|
|
7055 die "The CIGAR string contained undefined CIGAR operations in addition to 'M', 'I' and 'D': $cigar_2\n";
|
|
7056 }
|
|
7057 }
|
|
7058 }
|
|
7059
|
|
7060 ### Now we have InDel corrected Alignment scores
|
|
7061
|
|
7062 ### if the actual sequence contained Ns we need to adjust the number of mismatches. Ns receive a penalty of -1, but normal mismatches receive -6. This might still break if the
|
|
7063 ### sequence contained more than 5 Ns, but this should occur close to never
|
|
7064
|
|
7065 my $seq_1_N_count = $number_of_mismatches_1 % 6; # modulo 6 will return the integer rest after the division
|
|
7066 my $seq_2_N_count = $number_of_mismatches_2 % 6;
|
|
7067 # warn "N count 1: $seq_1_N_count\n";
|
|
7068 # warn "N count 2: $seq_2_N_count\n";
|
|
7069
|
|
7070 $number_of_mismatches_1 = int ($number_of_mismatches_1 / 6) + $seq_1_N_count;
|
|
7071 $number_of_mismatches_2 = int ($number_of_mismatches_2 / 6) + $seq_2_N_count;
|
|
7072
|
|
7073 # warn "MM1 $number_of_mismatches_1 \n";
|
|
7074 # warn "MM2 $number_of_mismatches_2 \n";
|
|
7075 }
|
|
7076 }
|
|
7077
|
|
7078 ####
|
|
7079
|
|
7080 my $XA_tag = "XA:Z:$number_of_mismatches_1";
|
|
7081 my $XB_tag = "XB:Z:$number_of_mismatches_2";
|
|
7082
|
|
7083
|
|
7084 # SAM format: QNAME, FLAG, RNAME, 1-based POS, MAPQ, CIGAR, RNEXT, PNEXT, TLEN, SEQ, QUAL, optional fields
|
|
7085 ### optionally print number of non-bisulfite mismatches
|
|
7086 if ($non_bs_mm){
|
|
7087 print OUT join("\t", ($id_1, $flag_1, $chr, $start_read_1, $mapq, $cigar_1, $rnext, $pnext_1, $tlen_1, $actual_seq_1, $qual_1, $NM_tag_1, $XX_tag_1, $XM_tag_1,$XR_tag_1,$XG_tag,$XA_tag)), "\n";
|
|
7088 print OUT join("\t", ($id_2, $flag_2, $chr, $start_read_2, $mapq, $cigar_2, $rnext, $pnext_2, $tlen_2, $actual_seq_2, $qual_2, $NM_tag_2, $XX_tag_2, $XM_tag_2,$XR_tag_2,$XG_tag,$XB_tag)), "\n";
|
|
7089 }
|
|
7090 else{ # default
|
|
7091 print OUT join("\t", ($id_1, $flag_1, $chr, $start_read_1, $mapq, $cigar_1, $rnext, $pnext_1, $tlen_1, $actual_seq_1, $qual_1, $NM_tag_1, $XX_tag_1, $XM_tag_1,$XR_tag_1,$XG_tag)), "\n";
|
|
7092 print OUT join("\t", ($id_2, $flag_2, $chr, $start_read_2, $mapq, $cigar_2, $rnext, $pnext_2, $tlen_2, $actual_seq_2, $qual_2, $NM_tag_2, $XX_tag_2, $XM_tag_2,$XR_tag_2,$XG_tag)), "\n";
|
|
7093 }
|
|
7094 }
|
|
7095
|
|
7096 sub revcomp{
|
|
7097 my $seq = shift or die "Missing seq to reverse complement\n";
|
|
7098 $seq = reverse $seq;
|
|
7099 $seq =~ tr/ACTGactg/TGACTGAC/;
|
|
7100 return $seq;
|
|
7101 }
|
|
7102
|
|
7103 sub hemming_dist{
|
|
7104 my $matches = 0;
|
|
7105 my @actual_seq = split //,(shift @_);
|
|
7106 my @ref_seq = split //,(shift @_);
|
|
7107 foreach (0..$#actual_seq){
|
|
7108 ++$matches if ($actual_seq[$_] eq $ref_seq[$_]);
|
|
7109 }
|
|
7110 return my $hd = scalar @actual_seq - $matches;
|
|
7111 }
|
|
7112
|
|
7113 sub make_mismatch_string{
|
|
7114 my $actual_seq = shift or die "Missing actual sequence";
|
|
7115 my $ref_seq = shift or die "Missing reference sequence";
|
|
7116 my $XX_tag = "XX:Z:";
|
|
7117 my $tmp = ($actual_seq ^ $ref_seq); # Bitwise comparison
|
|
7118 my $prev_mm_pos = 0;
|
|
7119 while($tmp =~ /[^\0]/g){ # Where bitwise comparison showed a difference
|
|
7120 my $nuc_match = pos($tmp) - $prev_mm_pos - 1; # Generate number of nucleotide that matches since last mismatch
|
|
7121 my $nuc_mm = substr($ref_seq, pos($tmp) - 1, 1) if pos($tmp) <= length($ref_seq); # Obtain reference nucleotide that was different from the actual read
|
|
7122 $XX_tag .= "$nuc_match" if $nuc_match > 0; # Ignore if mismatches are adjacent to each other
|
|
7123 $XX_tag .= "$nuc_mm" if defined $nuc_mm; # Ignore if there is no mismatch (prevents uninitialized string concatenation)
|
|
7124 $prev_mm_pos = pos($tmp); # Position of last mismatch
|
|
7125 }
|
|
7126 my $end_matches = length($ref_seq) - $prev_mm_pos; # Provides number of matches from last mismatch till end of sequence
|
|
7127 $XX_tag .= "$end_matches" if $end_matches > 0; # Ignore if mismatch is at the end of sequence
|
|
7128 return $XX_tag;
|
|
7129 }
|
|
7130
|
|
7131
|
|
7132
|
|
7133 sub print_helpfile{
|
|
7134 print << "HOW_TO";
|
|
7135
|
|
7136
|
|
7137 This program is free software: you can redistribute it and/or modify
|
|
7138 it under the terms of the GNU General Public License as published by
|
|
7139 the Free Software Foundation, either version 3 of the License, or
|
|
7140 (at your option) any later version.
|
|
7141
|
|
7142 This program is distributed in the hope that it will be useful,
|
|
7143 but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
7144 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
7145 GNU General Public License for more details.
|
|
7146 You should have received a copy of the GNU General Public License
|
|
7147 along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
7148
|
|
7149
|
|
7150
|
|
7151 DESCRIPTION
|
|
7152
|
|
7153
|
|
7154 The following is a brief description of command line options and arguments to control the Bismark
|
|
7155 bisulfite mapper and methylation caller. Bismark takes in FastA or FastQ files and aligns the
|
|
7156 reads to a specified bisulfite genome. Sequence reads are transformed into a bisulfite converted forward strand
|
|
7157 version (C->T conversion) or into a bisulfite treated reverse strand (G->A conversion of the forward strand).
|
|
7158 Each of these reads are then aligned to bisulfite treated forward strand index of a reference genome
|
|
7159 (C->T converted) and a bisulfite treated reverse strand index of the genome (G->A conversion of the
|
|
7160 forward strand, by doing this alignments will produce the same positions). These 4 instances of Bowtie (1 or 2)
|
|
7161 are run in parallel. The sequence file(s) are then read in again sequence by sequence to pull out the original
|
|
7162 sequence from the genome and determine if there were any protected C's present or not.
|
|
7163
|
|
7164 As of version 0.7.0 Bismark will only run 2 alignment threads for OT and OB in parallel, the 4 strand mode can be
|
|
7165 re-enabled by using --non_directional.
|
|
7166
|
|
7167 The final output of Bismark is in SAM format by default. For Bowtie 1 one can alos choose to report the old
|
|
7168 'vanilla' output format, which is a single tab delimited file with all sequences that have a unique best
|
|
7169 alignment to any of the 4 possible strands of a bisulfite PCR product. Both formats are described in more detail below.
|
|
7170
|
|
7171
|
|
7172 USAGE: bismark [options] <genome_folder> {-1 <mates1> -2 <mates2> | <singles>}
|
|
7173
|
|
7174
|
|
7175 ARGUMENTS:
|
|
7176
|
|
7177 <genome_folder> The path to the folder containing the unmodified reference genome
|
|
7178 as well as the subfolders created by the Bismark_Genome_Preparation
|
|
7179 script (/Bisulfite_Genome/CT_conversion/ and /Bisulfite_Genome/GA_conversion/).
|
|
7180 Bismark expects one or more fastA files in this folder (file extension: .fa
|
|
7181 or .fasta). The path can be relative or absolute.
|
|
7182
|
|
7183 -1 <mates1> Comma-separated list of files containing the #1 mates (filename usually includes
|
|
7184 "_1"), e.g. flyA_1.fq,flyB_1.fq). Sequences specified with this option must
|
|
7185 correspond file-for-file and read-for-read with those specified in <mates2>.
|
|
7186 Reads may be a mix of different lengths. Bismark will produce one mapping result
|
|
7187 and one report file per paired-end input file pair.
|
|
7188
|
|
7189 -2 <mates2> Comma-separated list of files containing the #2 mates (filename usually includes
|
|
7190 "_2"), e.g. flyA_1.fq,flyB_1.fq). Sequences specified with this option must
|
|
7191 correspond file-for-file and read-for-read with those specified in <mates1>.
|
|
7192 Reads may be a mix of different lengths.
|
|
7193
|
|
7194 <singles> A comma- or space-separated list of files containing the reads to be aligned (e.g.
|
|
7195 lane1.fq,lane2.fq lane3.fq). Reads may be a mix of different lengths. Bismark will
|
|
7196 produce one mapping result and one report file per input file.
|
|
7197
|
|
7198
|
|
7199 OPTIONS:
|
|
7200
|
|
7201
|
|
7202 Input:
|
|
7203
|
|
7204 -q/--fastq The query input files (specified as <mate1>,<mate2> or <singles> are FASTQ
|
|
7205 files (usually having extension .fg or .fastq). This is the default. See also
|
|
7206 --solexa-quals.
|
|
7207
|
|
7208 -f/--fasta The query input files (specified as <mate1>,<mate2> or <singles> are FASTA
|
|
7209 files (usually havin extension .fa, .mfa, .fna or similar). All quality values
|
|
7210 are assumed to be 40 on the Phred scale. FASTA files are expected to contain both
|
|
7211 the read name and the sequence on a single line (and not spread over several lines).
|
|
7212
|
|
7213 -s/--skip <int> Skip (i.e. do not align) the first <int> reads or read pairs from the input.
|
|
7214
|
|
7215 -u/--upto <int> Only aligns the first <int> reads or read pairs from the input. Default: no limit.
|
|
7216
|
|
7217 --phred33-quals FASTQ qualities are ASCII chars equal to the Phred quality plus 33. Default: on.
|
|
7218
|
|
7219 --phred64-quals FASTQ qualities are ASCII chars equal to the Phred quality plus 64. Default: off.
|
|
7220
|
|
7221 --solexa-quals Convert FASTQ qualities from solexa-scaled (which can be negative) to phred-scaled
|
|
7222 (which can't). The formula for conversion is:
|
|
7223 phred-qual = 10 * log(1 + 10 ** (solexa-qual/10.0)) / log(10). Used with -q. This
|
|
7224 is usually the right option for use with (unconverted) reads emitted by the GA
|
|
7225 Pipeline versions prior to 1.3. Works only for Bowtie 1. Default: off.
|
|
7226
|
|
7227 --solexa1.3-quals Same as --phred64-quals. This is usually the right option for use with (unconverted)
|
|
7228 reads emitted by GA Pipeline version 1.3 or later. Default: off.
|
|
7229
|
|
7230 --path_to_bowtie The full path </../../> to the Bowtie (1 or 2) installation on your system. If not
|
|
7231 specified it is assumed that Bowtie (1 or 2) is in the PATH.
|
|
7232
|
|
7233
|
|
7234 Alignment:
|
|
7235
|
|
7236 -n/--seedmms <int> The maximum number of mismatches permitted in the "seed", i.e. the first L base pairs
|
|
7237 of the read (where L is set with -l/--seedlen). This may be 0, 1, 2 or 3 and the
|
|
7238 default is 1. This option is only available for Bowtie 1 (for Bowtie 2 see -N).
|
|
7239
|
|
7240 -l/--seedlen The "seed length"; i.e., the number of bases of the high quality end of the read to
|
|
7241 which the -n ceiling applies. The default is 28. Bowtie (and thus Bismark) is faster for
|
|
7242 larger values of -l. This option is only available for Bowtie 1 (for Bowtie 2 see -L).
|
|
7243
|
|
7244 -e/--maqerr <int> Maximum permitted total of quality values at all mismatched read positions throughout
|
|
7245 the entire alignment, not just in the "seed". The default is 70. Like Maq, bowtie rounds
|
|
7246 quality values to the nearest 10 and saturates at 30. This value is not relevant for
|
|
7247 Bowtie 2.
|
|
7248
|
|
7249 --chunkmbs <int> The number of megabytes of memory a given thread is given to store path descriptors in
|
|
7250 --best mode. Best-first search must keep track of many paths at once to ensure it is
|
|
7251 always extending the path with the lowest cumulative cost. Bowtie tries to minimize the
|
|
7252 memory impact of the descriptors, but they can still grow very large in some cases. If
|
|
7253 you receive an error message saying that chunk memory has been exhausted in --best mode,
|
|
7254 try adjusting this parameter up to dedicate more memory to the descriptors. This value
|
|
7255 is not relevant for Bowtie 2. Default: 512.
|
|
7256
|
|
7257 -I/--minins <int> The minimum insert size for valid paired-end alignments. E.g. if -I 60 is specified and
|
|
7258 a paired-end alignment consists of two 20-bp alignments in the appropriate orientation
|
|
7259 with a 20-bp gap between them, that alignment is considered valid (as long as -X is also
|
|
7260 satisfied). A 19-bp gap would not be valid in that case. Default: 0.
|
|
7261
|
|
7262 -X/--maxins <int> The maximum insert size for valid paired-end alignments. E.g. if -X 100 is specified and
|
|
7263 a paired-end alignment consists of two 20-bp alignments in the proper orientation with a
|
|
7264 60-bp gap between them, that alignment is considered valid (as long as -I is also satisfied).
|
|
7265 A 61-bp gap would not be valid in that case. Default: 500.
|
|
7266
|
|
7267
|
|
7268 Bowtie 1 Reporting:
|
|
7269
|
|
7270 -k <2> Due to the way Bismark works Bowtie will report up to 2 valid alignments. This option
|
|
7271 will be used by default.
|
|
7272
|
|
7273 --best Make Bowtie guarantee that reported singleton alignments are "best" in terms of stratum
|
|
7274 (i.e. number of mismatches, or mismatches in the seed in the case if -n mode) and in
|
|
7275 terms of the quality; e.g. a 1-mismatch alignment where the mismatch position has Phred
|
|
7276 quality 40 is preferred over a 2-mismatch alignment where the mismatched positions both
|
|
7277 have Phred quality 10. When --best is not specified, Bowtie may report alignments that
|
|
7278 are sub-optimal in terms of stratum and/or quality (though an effort is made to report
|
|
7279 the best alignment). --best mode also removes all strand bias. Note that --best does not
|
|
7280 affect which alignments are considered "valid" by Bowtie, only which valid alignments
|
|
7281 are reported by Bowtie. Bowtie is about 1-2.5 times slower when --best is specified.
|
|
7282 Default: on.
|
|
7283
|
|
7284 --no_best Disables the --best option which is on by default. This can speed up the alignment process,
|
|
7285 e.g. for testing purposes, but for credible results it is not recommended to disable --best.
|
|
7286
|
|
7287
|
|
7288 Output:
|
|
7289
|
|
7290 --non_directional The sequencing library was constructed in a non strand-specific manner, alignments to all four
|
|
7291 bisulfite strands will be reported. Default: OFF.
|
|
7292
|
|
7293 (The current Illumina protocol for BS-Seq is directional, in which case the strands complementary
|
|
7294 to the original strands are merely theoretical and should not exist in reality. Specifying directional
|
|
7295 alignments (which is the default) will only run 2 alignment threads to the original top (OT)
|
|
7296 or bottom (OB) strands in parallel and report these alignments. This is the recommended option
|
|
7297 for sprand-specific libraries).
|
|
7298
|
|
7299 --pbat This options may be used for PBAT-Seq libraries (Post-Bisulfite Adapter Tagging; Kobayashi et al.,
|
|
7300 PLoS Genetics, 2012). This is essentially the exact opposite of alignments in 'directional' mode,
|
|
7301 as it will only launch two alignment threads to the CTOT and CTOB strands instead of the normal OT
|
|
7302 and OB ones. Use this option only if you are certain that your libraries were constructed following
|
|
7303 a PBAT protocol (if you don't know what PBAT-Seq is you should not specify this option). The option
|
|
7304 --pbat works only for single-end and paired-end FastQ files for use with Bowtie1 (uncompressed
|
|
7305 temporary files only).
|
|
7306
|
|
7307 --sam-no-hd Suppress SAM header lines (starting with @). This might be useful when very large input files are
|
|
7308 split up into several smaller files to run concurrently and the output files are to be merged.
|
|
7309
|
|
7310 --quiet Print nothing besides alignments.
|
|
7311
|
|
7312 --vanilla Performs bisulfite mapping with Bowtie 1 and prints the 'old' output (as in Bismark 0.5.X) instead
|
|
7313 of SAM format output.
|
|
7314
|
|
7315 -un/--unmapped Write all reads that could not be aligned to a file in the output directory. Written reads will
|
|
7316 appear as they did in the input, without any translation of quality values that may have
|
|
7317 taken place within Bowtie or Bismark. Paired-end reads will be written to two parallel files with _1
|
|
7318 and _2 inserted in their filenames, i.e. _unmapped_reads_1.txt and unmapped_reads_2.txt. Reads
|
|
7319 with more than one valid alignment with the same number of lowest mismatches (ambiguous mapping)
|
|
7320 are also written to _unmapped_reads.txt unless the option --ambiguous is specified as well.
|
|
7321
|
|
7322 --ambiguous Write all reads which produce more than one valid alignment with the same number of lowest
|
|
7323 mismatches or other reads that fail to align uniquely to a file in the output directory.
|
|
7324 Written reads will appear as they did in the input, without any of the translation of quality
|
|
7325 values that may have taken place within Bowtie or Bismark. Paired-end reads will be written to two
|
|
7326 parallel files with _1 and _2 inserted in theit filenames, i.e. _ambiguous_reads_1.txt and
|
|
7327 _ambiguous_reads_2.txt. These reads are not written to the file specified with --un.
|
|
7328
|
|
7329 -o/--output_dir <dir> Write all output files into this directory. By default the output files will be written into
|
|
7330 the same folder as the input file(s). If the specified folder does not exist, Bismark will attempt
|
|
7331 to create it first. The path to the output folder can be either relative or absolute.
|
|
7332
|
|
7333 --temp_dir <dir> Write temporary files to this directory instead of into the same directory as the input files. If
|
|
7334 the specified folder does not exist, Bismark will attempt to create it first. The path to the
|
|
7335 temporary folder can be either relative or absolute.
|
|
7336
|
|
7337 --non_bs_mm Optionally outputs an extra column specifying the number of non-bisulfite mismatches a read during the
|
|
7338 alignment step. This option is only available for SAM format. In Bowtie 2 context, this value is
|
|
7339 just the number of actual non-bisulfite mismatches and ignores potential insertions or deletions.
|
|
7340 The format for single-end reads and read 1 of paired-end reads is 'XA:Z:number of mismatches'
|
|
7341 and 'XB:Z:number of mismatches' for read 2 of paired-end reads.
|
|
7342
|
|
7343 --gzip Temporary bisulfite conversion files will be written out in a GZIP compressed form to save disk
|
|
7344 space. This option is available for most alignment modes but is not available for paired-end FastA
|
|
7345 files. This option might be somewhat slower than writing out uncompressed files, but this awaits
|
|
7346 further testing.
|
|
7347
|
|
7348 --bam The output will be written out in BAM format instead of the default SAM format. Bismark will
|
|
7349 attempt to use the path to Samtools that was specified with '--samtools_path', or, if it hasn't
|
|
7350 been specified, attempt to find Samtools in the PATH. If no installation of Samtools can be found,
|
|
7351 the SAM output will be compressed with GZIP instead (yielding a .sam.gz output file).
|
|
7352
|
|
7353 --samtools_path The path to your Samtools installation, e.g. /home/user/samtools/. Does not need to be specified
|
|
7354 explicitly if Samtools is in the PATH already.
|
|
7355
|
|
7356
|
|
7357
|
|
7358 Other:
|
|
7359
|
|
7360 -h/--help Displays this help file.
|
|
7361
|
|
7362 -v/--version Displays version information.
|
|
7363
|
|
7364
|
|
7365 BOWTIE 2 SPECIFIC OPTIONS
|
|
7366
|
|
7367 --bowtie2 Uses Bowtie 2 instead of Bowtie 1. Bismark limits Bowtie 2 to only perform end-to-end
|
|
7368 alignments, i.e. searches for alignments involving all read characters (also called
|
|
7369 untrimmed or unclipped alignments). Bismark assumes that raw sequence data is adapter
|
|
7370 and/or quality trimmed where appropriate. Default: off.
|
|
7371
|
|
7372 Bowtie 2 alignment options:
|
|
7373
|
|
7374 -N <int> Sets the number of mismatches to allowed in a seed alignment during multiseed alignment.
|
|
7375 Can be set to 0 or 1. Setting this higher makes alignment slower (often much slower)
|
|
7376 but increases sensitivity. Default: 0. This option is only available for Bowtie 2 (for
|
|
7377 Bowtie 1 see -n).
|
|
7378
|
|
7379 -L <int> Sets the length of the seed substrings to align during multiseed alignment. Smaller values
|
|
7380 make alignment slower but more senstive. Default: the --sensitive preset of Bowtie 2 is
|
|
7381 used by default, which sets -L to 20. This option is only available for Bowtie 2 (for
|
|
7382 Bowtie 1 see -l).
|
|
7383
|
|
7384 --ignore-quals When calculating a mismatch penalty, always consider the quality value at the mismatched
|
|
7385 position to be the highest possible, regardless of the actual value. I.e. input is treated
|
|
7386 as though all quality values are high. This is also the default behavior when the input
|
|
7387 doesn't specify quality values (e.g. in -f mode). This option is invariable and on by default.
|
|
7388
|
|
7389
|
|
7390 Bowtie 2 paired-end options:
|
|
7391
|
|
7392 --no-mixed This option disables Bowtie 2's behavior to try to find alignments for the individual mates if
|
|
7393 it cannot find a concordant or discordant alignment for a pair. This option is invariable and
|
|
7394 and on by default.
|
|
7395
|
|
7396 --no-discordant Normally, Bowtie 2 looks for discordant alignments if it cannot find any concordant alignments.
|
|
7397 A discordant alignment is an alignment where both mates align uniquely, but that does not
|
|
7398 satisfy the paired-end constraints (--fr/--rf/--ff, -I, -X). This option disables that behavior
|
|
7399 and it is on by default.
|
|
7400
|
|
7401
|
|
7402 Bowtie 2 effort options:
|
|
7403
|
|
7404 -D <int> Up to <int> consecutive seed extension attempts can "fail" before Bowtie 2 moves on, using
|
|
7405 the alignments found so far. A seed extension "fails" if it does not yield a new best or a
|
|
7406 new second-best alignment. Default: 15.
|
|
7407
|
|
7408 -R <int> <int> is the maximum number of times Bowtie 2 will "re-seed" reads with repetitive seeds.
|
|
7409 When "re-seeding," Bowtie 2 simply chooses a new set of reads (same length, same number of
|
|
7410 mismatches allowed) at different offsets and searches for more alignments. A read is considered
|
|
7411 to have repetitive seeds if the total number of seed hits divided by the number of seeds
|
|
7412 that aligned at least once is greater than 300. Default: 2.
|
|
7413
|
|
7414 Bowtie 2 parallelization options:
|
|
7415
|
|
7416
|
|
7417 -p NTHREADS Launch NTHREADS parallel search threads (default: 1). Threads will run on separate processors/cores
|
|
7418 and synchronize when parsing reads and outputting alignments. Searching for alignments is highly
|
|
7419 parallel, and speedup is close to linear. Increasing -p increases Bowtie 2's memory footprint.
|
|
7420 E.g. when aligning to a human genome index, increasing -p from 1 to 8 increases the memory footprint
|
|
7421 by a few hundred megabytes. This option is only available if bowtie is linked with the pthreads
|
|
7422 library (i.e. if BOWTIE_PTHREADS=0 is not specified at build time). In addition, this option will
|
|
7423 automatically use the option '--reorder', which guarantees that output SAM records are printed in
|
|
7424 an order corresponding to the order of the reads in the original input file, even when -p is set
|
|
7425 greater than 1 (Bismark requires the Bowtie 2 output to be this way). Specifying --reorder and
|
|
7426 setting -p greater than 1 causes Bowtie 2 to run somewhat slower and use somewhat more memory then
|
|
7427 if --reorder were not specified. Has no effect if -p is set to 1, since output order will naturally
|
|
7428 correspond to input order in that case.
|
|
7429
|
|
7430 Bowtie 2 Scoring options:
|
|
7431
|
|
7432 --score_min <func> Sets a function governing the minimum alignment score needed for an alignment to be considered
|
|
7433 "valid" (i.e. good enough to report). This is a function of read length. For instance, specifying
|
|
7434 L,0,-0.2 sets the minimum-score function f to f(x) = 0 + -0.2 * x, where x is the read length.
|
|
7435 See also: setting function options at http://bowtie-bio.sourceforge.net/bowtie2. The default is
|
|
7436 L,0,-0.2.
|
|
7437
|
|
7438 --rdg <int1>,<int2> Sets the read gap open (<int1>) and extend (<int2>) penalties. A read gap of length N gets a penalty
|
|
7439 of <int1> + N * <int2>. Default: 5, 3.
|
|
7440
|
|
7441 --rfg <int1>,<int2> Sets the reference gap open (<int1>) and extend (<int2>) penalties. A reference gap of length N gets
|
|
7442 a penalty of <int1> + N * <int2>. Default: 5, 3.
|
|
7443
|
|
7444
|
|
7445 Bowtie 2 Reporting options:
|
|
7446
|
|
7447 -most_valid_alignments <int> This used to be the Bowtie 2 parameter -M. As of Bowtie 2 version 2.0.0 beta7 the option -M is
|
|
7448 deprecated. It will be removed in subsequent versions. What used to be called -M mode is still the
|
|
7449 default mode, but adjusting the -M setting is deprecated. Use the -D and -R options to adjust the
|
|
7450 effort expended to find valid alignments.
|
|
7451
|
|
7452 For reference, this used to be the old (now deprecated) description of -M:
|
|
7453 Bowtie 2 searches for at most <int>+1 distinct, valid alignments for each read. The search terminates when it
|
|
7454 can't find more distinct valid alignments, or when it finds <int>+1 distinct alignments, whichever
|
|
7455 happens first. Only the best alignment is reported. Information from the other alignments is used to
|
|
7456 estimate mapping quality and to set SAM optional fields, such as AS:i and XS:i. Increasing -M makes
|
|
7457 Bowtie 2 slower, but increases the likelihood that it will pick the correct alignment for a read that
|
|
7458 aligns many places. For reads that have more than <int>+1 distinct, valid alignments, Bowtie 2 does not
|
|
7459 guarantee that the alignment reported is the best possible in terms of alignment score. -M is
|
|
7460 always used and its default value is set to 10.
|
|
7461
|
|
7462
|
|
7463 'VANILLA' Bismark OUTPUT:
|
|
7464
|
|
7465 Single-end output format (tab-separated):
|
|
7466
|
|
7467 (1) <seq-ID>
|
|
7468 (2) <read alignment strand>
|
|
7469 (3) <chromosome>
|
|
7470 (4) <start position>
|
|
7471 (5) <end position>
|
|
7472 (6) <observed bisulfite sequence>
|
|
7473 (7) <equivalent genomic sequence>
|
|
7474 (8) <methylation call>
|
|
7475 (9) <read conversion
|
|
7476 (10) <genome conversion>
|
|
7477 (11) <read quality score (Phred33)>
|
|
7478
|
|
7479
|
|
7480 Paired-end output format (tab-separated):
|
|
7481 (1) <seq-ID>
|
|
7482 (2) <read 1 alignment strand>
|
|
7483 (3) <chromosome>
|
|
7484 (4) <start position>
|
|
7485 (5) <end position>
|
|
7486 (6) <observed bisulfite sequence 1>
|
|
7487 (7) <equivalent genomic sequence 1>
|
|
7488 (8) <methylation call 1>
|
|
7489 (9) <observed bisulfite sequence 2>
|
|
7490 (10) <equivalent genomic sequence 2>
|
|
7491 (11) <methylation call 2>
|
|
7492 (12) <read 1 conversion
|
|
7493 (13) <genome conversion>
|
|
7494 (14) <read 1 quality score (Phred33)>
|
|
7495 (15) <read 2 quality score (Phred33)>
|
|
7496
|
|
7497
|
|
7498 Bismark SAM OUTPUT (default):
|
|
7499
|
|
7500 (1) QNAME (seq-ID)
|
|
7501 (2) FLAG (this flag tries to take the strand a bisulfite read originated from into account (this is different from ordinary DNA alignment flags!))
|
|
7502 (3) RNAME (chromosome)
|
|
7503 (4) POS (start position)
|
|
7504 (5) MAPQ (always 255)
|
|
7505 (6) CIGAR
|
|
7506 (7) RNEXT
|
|
7507 (8) PNEXT
|
|
7508 (9) TLEN
|
|
7509 (10) SEQ
|
|
7510 (11) QUAL (Phred33 scale)
|
|
7511 (12) NM-tag (edit distance to the reference)
|
|
7512 (13) XX-tag (base-by-base mismatches to the reference. This does not include indels)
|
|
7513 (14) XM-tag (methylation call string)
|
|
7514 (15) XR-tag (read conversion state for the alignment)
|
|
7515 (16) XG-tag (genome conversion state for the alignment)
|
|
7516 (17) XA/XB-tag (non-bisulfite mismatches) (optional!)
|
|
7517
|
|
7518 Each read of paired-end alignments is written out in a separate line in the above format.
|
|
7519
|
|
7520
|
|
7521 Last edited on 10 May 2013.
|
|
7522
|
|
7523 HOW_TO
|
|
7524 }
|