b687dd9018670941ce30f8a4582d6597c5a974d8
lrnassar
  Tue Sep 1 14:57:43 2026 -0700
lrSv1kLin: fix 2bp insertion span, drop dead numConsolidated field, refresh lrSvAll merge. refs #38099

The Lin 1218 VCFs set INFO/END = POS+1 on insertions, and the converter took
chromEnd from END, so every insertion was drawn 2bp wide with svLen 2 instead
of the 1bp anchor base. That contradicted the track's own description page and
the coordinate convention in the makeDoc, and it kept 107,980 Lin insertions
from merging in lrSvAll. Insertions now clamp chromEnd to the anchor; deletions
are unchanged and still verify span == |SVLEN| against the source VCFs.

Dropped numConsolidated from the converter and the .as: the NumConsolidated
INFO key is declared in the VCF header but never appears on a data line, so the
column was 0 on all 1.2M rows and added a meaningless line to every detail page.

Rebuilt lin1218 on hg38 and hs1 (item counts and variant names unchanged) and
re-ran the merge: lrSvAll 2,963,093 -> 2,855,267 rows as the duplicate insertion
rows collapse.

Bumped seven filter.svLen/insLen maxima in lrSv.ra that were short of the data
after the August deletion narrowing, three of them only visible on hs1.

lrSvAll.html said the 1000 Genomes linear set was not in the merge, which is no
longer true, and gave no warning that sourceCount double-counts because Lin1218
already absorbs HPRC, HGSVC3 and both 1KG ONT callsets. Corrected the merge key
description and refreshed ten stale cells in the lrSv.html summary table.

diff --git src/hg/makeDb/scripts/lrSv/lrSv1kLin1218VcfToBed.py src/hg/makeDb/scripts/lrSv/lrSv1kLin1218VcfToBed.py
index d58656e4dcb..60bd6827685 100644
--- src/hg/makeDb/scripts/lrSv/lrSv1kLin1218VcfToBed.py
+++ src/hg/makeDb/scripts/lrSv/lrSv1kLin1218VcfToBed.py
@@ -68,71 +68,81 @@
             fields = line.rstrip("\n").split("\t")
             chrom = fields[0]
             pos = int(fields[1])
             info = parseInfo(fields[7])
 
             svType = normalizeSvType(info.get("SVTYPE", "."))
             svLenRaw = toInt(info.get("SVLEN", "0"))
             end = toInt(info.get("END", str(pos)))
 
             chromStart = pos - 1
             chromEnd = end
             # VCF POS is the anchor base (present in both REF and ALT); it is not
             # part of a deletion, so the deleted region starts one base to the
             # right. Drop the anchor from the left of DEL intervals so svLen ==
             # |SVLEN| and coordinates match anchor-excluded callsets (e.g.
-            # HGSVC3). INS keeps the anchor-based position.
+            # HGSVC3).
+            # An insertion has no deleted span: its item is the single anchor
+            # base at the attach point, so span == 1. This VCF sets END = POS+1
+            # on insertions, which would draw them 2 bp wide, so clamp chromEnd
+            # back to the anchor. That matches the other VCF-derived subtracks
+            # (CoLoRSdb, AoU, GA4K, CARD, gustafson, 1kgOnt, and deCODE for its
+            # single-base-REF records).
             if svType == "DEL":
                 chromStart += 1
+            elif svType in ("INS", "MEI"):
+                chromEnd = chromStart + 1
+            # Only DEL and INS occur in either source VCF. A future INV/DUP/CPX
+            # would fall through to chromEnd = END with chromStart = pos-1, i.e.
+            # the same off-by-one just fixed for DEL, so revisit the anchor
+            # question per type before trusting such a record.
             if chromEnd <= chromStart:
                 chromEnd = chromStart + 1
 
             svLen = chromEnd - chromStart
             insLen = abs(svLenRaw) if svType in ("INS", "MEI") else 0
 
             ac = toInt(info.get("AC", "0"))
             an = toInt(info.get("AN", "0"))
             af = toFloat(info.get("AF", "0"))
             afAfr = toFloat(info.get("AF_AFR", "0"))
             afAmr = toFloat(info.get("AF_AMR", "0"))
             afEas = toFloat(info.get("AF_EAS", "0"))
             afEur = toFloat(info.get("AF_EUR", "0"))
             afSas = toFloat(info.get("AF_SAS", "0"))
             ns = toInt(info.get("NS", "0"))
-            numConsolidated = toInt(info.get("NumConsolidated", "0"))
 
             color = SV_COLORS.get(svType, "100,100,100")
             featLen = insLen if svType in ("INS", "MEI") else svLen
             name = svName(svType, featLen, ac)
 
             row = [
                 chrom,
                 str(chromStart),
                 str(chromEnd),
                 name,
                 "0",
                 ".",
                 str(chromStart),
                 str(chromEnd),
                 color,
                 svType,
                 str(svLen),
                 str(insLen),
                 str(ac),
                 str(an),
                 f"{af:.6f}",
                 f"{afAfr:.6f}",
                 f"{afAmr:.6f}",
                 f"{afEas:.6f}",
                 f"{afEur:.6f}",
                 f"{afSas:.6f}",
                 str(ns),
-                str(numConsolidated),
             ]
             fOut.write("\t".join(row) + "\n")
             nWritten += 1
 
     print(f"Wrote {nWritten} records to {outFile}", file=sys.stderr)
 
 
 if __name__ == "__main__":
     main()