@@ -130,6 +130,10 @@ class get_readpairs:
130130 # Used in place of a real read in incomplete pairs before they are completed
131131 PendingMate = object ()
132132
133+ # Read key. To allow for multi-mapping, we also use the mapping position
134+ def get_pair_key (self , read ):
135+ return (read .qname , read .reference_start if not read .is_read2 == 0 else read .next_reference_start )
136+
133137 def drain_queue_forcibly (self ):
134138 # Forcibly convert incomplete queued pairs into singletons.
135139 # These necessarily contains only a read1, but not read2, since we
@@ -139,15 +143,15 @@ def drain_queue_forcibly(self):
139143 if pair [1 ] is get_readpairs .PendingMate :
140144 pair [1 ] = None
141145 U .warn ("inconsistent BAM file: only read1 of proper pair %s found on chromosome %s, treating as unpaired" %
142- (pair [0 ]. qname , pair [0 ].reference_name ))
146+ (str ( self . get_pair_key ( pair [0 ])) , pair [0 ].reference_name ))
143147 yield pair
144148 # Forcibly convert also incomplete unqueued pairs into singletons.
145149 # These are the ones that contain only a read2 but no read1
146150 for pair in self .incomplete_pairs .values ():
147151 if pair [0 ] is get_readpairs .PendingMate :
148152 pair [0 ] = None
149153 U .warn ("inconsistent BAM file: only read2 of proper pair %s found on chromosome %s, treating as unpaired" %
150- (pair [1 ]. qname , pair [1 ].reference_name ))
154+ (str ( self . get_pair_key ( pair [1 ])) , pair [1 ].reference_name ))
151155 yield pair
152156 self .incomplete_pairs .clear ()
153157
@@ -157,35 +161,42 @@ def __call__(self, inreads):
157161 self .current_chr = None
158162
159163 for read in inreads :
160- read_i = int (read .is_read2 )
161164 read_chr = read .reference_name
165+ # Read index. 0 for read1 and 1 for read2
166+ read_i = int (read .is_read2 )
167+ pair_key = self .get_pair_key (read )
162168
163169 # Output leftover incomplete read pairs if the chrosomome changes
164170 if self .current_chr is not None and self .current_chr != read_chr :
165171 for queue_entry in self .drain_queue_forcibly ():
166172 yield queue_entry
167173 self .current_chr = read_chr
168174
169- # Queue read, either as a singleton, or as part of a read pair
170- if read .is_unmapped or read .mate_is_unmapped or not read .is_proper_pair :
175+ # Queue read, either as a singleton, or as part of a read pair.
176+ # Proper pairs should always have both mates mapped to the same chromosome,
177+ # but to avoid weird behaviour for broken BAM file, we re-check.
178+ if ((not read .is_proper_pair ) or
179+ read .is_unmapped or
180+ read .mate_is_unmapped or
181+ (read .reference_id != read .next_reference_id )):
171182 # Read is not part of a proper pair. Enqueue as singleton
172183 pair = [None , None ]
173184 pair [read_i ] = read
174185 self .queue .append (pair )
175186 else :
176- if read . qname in self .incomplete_pairs :
187+ if pair_key in self .incomplete_pairs :
177188 # Read is part of an already queued (incomplete) read pair. Complete it.
178- pair = self .incomplete_pairs .pop (read . qname )
189+ pair = self .incomplete_pairs .pop (pair_key )
179190 if pair [read_i ] is not get_readpairs .PendingMate :
180- U .warn ("inconsistent BAM file: both mates %s flagged to be read%d" %
181- (read . qname , read_i + 1 ))
191+ U .warn ("inconsistent BAM file: both mates of %s flagged to be read%d" %
192+ (str ( pair_key ) , read_i + 1 ))
182193 read_i = 1 - read_i
183194 pair [read_i ] = read
184195 else :
185196 # Read is part of a new read pair. Create incomplete pair
186197 pair = [get_readpairs .PendingMate , get_readpairs .PendingMate ]
187198 pair [read_i ] = read
188- self .incomplete_pairs [read . qname ] = pair
199+ self .incomplete_pairs [pair_key ] = pair
189200 # Enqueue pair in the correct place for its 1st read
190201 if read_i == 0 :
191202 self .queue .append (pair )
0 commit comments