@@ -1419,6 +1419,7 @@ def _extract_tract_sequences(
14191419 alignments_queries : Dict [str , str ],
14201420 ref1_label : str ,
14211421 ref2_label : str ,
1422+ is_intra_clade : bool ,
14221423 min_consecutive : int = 1 ,
14231424 line_len : int = 80 ,
14241425 include_indels : bool = False ,
@@ -1436,6 +1437,14 @@ def _extract_tract_sequences(
14361437
14371438 Tract boundaries use all positions in each sample's allegiances (including
14381439 indel columns when -include-indels was used). Clade labels: ``"ia"`` (ref1), ``"ib"`` (ref2).
1440+ When is_intra_clade is True (e.g. Ia vs Ib or IIa vs IIb), we keep "all
1441+ non-opposite" bases for each clade (Ia tract = not Ib; Ib tract = not Ia),
1442+ so ambiguous/other positions remain in both masked sequences.
1443+
1444+ When is_intra_clade is False (inter-clade, e.g. I vs II), we only keep
1445+ positions confidently assigned to that clade (strict tracts) using
1446+ _extract_tracts_as_n_full_length, so masked sequences contain only Ia or
1447+ only Ib tract positions and everything else becomes N.
14391448 """
14401449 out_dir .mkdir (parents = True , exist_ok = True )
14411450 ref1_out = out_dir / f"{ ref1_label } _recombinant_ancestral_tract.fa"
@@ -1466,18 +1475,28 @@ def _extract_tract_sequences(
14661475 n_skip_no_seq += 1
14671476 continue
14681477
1469- # ref1 (Ia) file: keep clade I bases and all positions not confidently Ib.
1470- # ref2 (Ib) file: keep clade IIb bases and all positions not confidently Ia.
14711478 safe_id = _safe_fasta_id (sample_id )
1472- seq1 = _extract_full_length_non_opposite (aligned_seq , allegiances , keep_clade = "ia" )
1479+ # ref1 (Ia) file
1480+ if is_intra_clade :
1481+ # Intra-clade: keep clade Ia bases and all positions not confidently Ib.
1482+ seq1 = _extract_full_length_non_opposite (aligned_seq , allegiances , keep_clade = "ia" )
1483+ else :
1484+ # Inter-clade: strict Ia tracts only (Ia bases; everything else → N).
1485+ seq1 = _extract_tracts_as_n_full_length (aligned_seq , merged_tracts , keep_clade = "ia" )
14731486 len1 = len (seq1 )
14741487 non_n1 = sum (1 for b in seq1 .upper () if b in "ACGT" )
14751488 cov1 = (100.0 * non_n1 / len1 ) if len1 else 0.0
14761489 fh1 .write (f">{ safe_id } _{ ref1_label } _tract_HC_{ cov1 :.2f} %\n " )
14771490 for i in range (0 , len1 , line_len ):
14781491 fh1 .write (seq1 [i : i + line_len ] + "\n " )
14791492
1480- seq2 = _extract_full_length_non_opposite (aligned_seq , allegiances , keep_clade = "ib" )
1493+ # ref2 (Ib/IIb) file
1494+ if is_intra_clade :
1495+ # Intra-clade: keep clade Ib bases and all positions not confidently Ia.
1496+ seq2 = _extract_full_length_non_opposite (aligned_seq , allegiances , keep_clade = "ib" )
1497+ else :
1498+ # Inter-clade: strict Ib/IIb tracts only.
1499+ seq2 = _extract_tracts_as_n_full_length (aligned_seq , merged_tracts , keep_clade = "ib" )
14811500 len2 = len (seq2 )
14821501 non_n2 = sum (1 for b in seq2 .upper () if b in "ACGT" )
14831502 cov2 = (100.0 * non_n2 / len2 ) if len2 else 0.0
@@ -3596,6 +3615,7 @@ def row(r: Dict[str, Any]) -> str:
35963615 alignments_queries = alignments_queries ,
35973616 ref1_label = ref1_label ,
35983617 ref2_label = ref2_label ,
3618+ is_intra_clade = is_intra_clade ,
35993619 min_consecutive = int (getattr (args , "breakpoint_min_snps" , 1 )),
36003620 include_indels = getattr (args , "include_indels" , False ),
36013621 )
0 commit comments