@@ -748,42 +748,72 @@ def read_pair_generator(bam, region_string=None):
748748
749749def create_primer_lookup (ref_len_tuple , amplicons : list [Amplicon ], padding = 35 ):
750750 """
751- Returns a dict of chroms, each containing a (N, chrom_len) shaped array
751+ Create a lookup table for efficient primer position queries across reference genomes.
752+
753+ Each chromosome gets its own 2D lookup array where:
754+ - Rows represent non-overlapping "pools"* of amplicons at their corresponding positions.
755+ - Columns represent genomic positions
756+ - Values are Amplicon objects or None
757+
758+ The function automatically determines the minimum number of rows needed to ensure
759+ no amplicons overlap within the same row when accounting for padding.
760+
761+ * Amplicons are placed in the first available row where they don't overlap, not their pool index.
762+
763+ Parameters
764+ ----------
765+ ref_len_tuple : list[tuple[str, int]]
766+ List of tuples containing (chromosome_name, chromosome_length) pairs
767+ from the reference genome
768+ amplicons : list[Amplicon]
769+ List of Amplicon objects containing primer scheme information
770+ padding : int, optional
771+ Number of bases to extend amplicon boundaries on both sides to allow
772+ for fuzzy matching of reads with barcodes/adapters (default: 35)
773+
774+ Returns
775+ -------
776+ dict[str, np.ndarray]
777+ Dictionary mapping chromosome names to 2D numpy arrays of shape (N, chrom_len+1)
778+ where N is the minimum number of rows needed to prevent amplicon overlap.
779+ Array elements are either Amplicon objects or None.
780+
781+
752782 """
753783 lookups = {}
754784 for chrom , chromlen in ref_len_tuple :
755- a = np .empty_like (None , shape = (1 , chromlen + 1 ))
785+ lookup_array = np .empty_like (None , shape = (1 , chromlen + 1 ))
756786 for amp in amplicons :
757787 added = False
758788 if amp .chrom == chrom :
759789 # If amplicon clashes with any in same pool add new row
760- amp_slice = a [
790+ amp_slice = lookup_array [
761791 :,
762792 max (amp .amplicon_start - padding , 0 ) : min (
763793 amp .amplicon_end + padding , chromlen
764794 ),
765795 ]
766796 for i , row in enumerate (amp_slice ): # Check each row for collision
767797 if row [row != None ].size == 0 :
768- a [
798+ lookup_array [
769799 i ,
770800 max (amp .amplicon_start - padding , 0 ) : min (
771801 amp .amplicon_end + padding , chromlen
772802 ),
773803 ] = amp
774804 added = True
775- # If not added, add new row to array and add to that.
805+ # If not added, create new row, add the amplicon to that then add back to original array
776806 if not added :
777- b = np .empty_like (None , shape = (1 , chromlen + 1 ))
778- b [
807+ new_row = np .empty_like (None , shape = (1 , chromlen + 1 ))
808+ new_row [
779809 0 ,
780810 max (amp .amplicon_start - padding , 0 ) : min (
781811 amp .amplicon_end + padding , chromlen
782812 ),
783813 ] = amp
784- a = np .vstack ((a , b ))
814+ lookup_array = np .vstack ((lookup_array , new_row ))
785815
786- lookups [chrom ] = a
816+ lookups [chrom ] = lookup_array
787817 return lookups
788818
789819
0 commit comments