53ab3f1e293a91ddf71ec7568f4a6ff827bcd278
lrnassar
  Fri Oct 2 13:52:36 2026 -0700
Native GPN-Star track (Ye, Benegas et al., Nature 2026) on hg38, mm39, galGal6, dm6 and ce11, from the authors' Hugging Face hub. Each model is a multiWig sequence logo plus a four-allele -LLR composite using negateValues. The three hg38 models (V/M/P) sit in predictionScoresSuper via human/gpnStar.ra with /gbdb/$D bigDataUrls so -strict drops them on other human assemblies; the other four get a standalone gpnStar superTrack. The authors' bigWigs come from pyBigWig, whose bwAddIntervalSpanSteps writes the last section of each run 6 bases too long (pyBigWig #166) and makes bigWigAverageOverBed and bigWigCorrelate abort, so gpnStarRebuild.sh re-encodes them with wigToBigWig and gpnStarVerify.sh checks every per-base value is unchanged. Entropy scores and the GenArk-only Arabidopsis set are left out, and pennantIcon still has #TBD placeholders for the newsarch anchor and date. refs #38451

diff --git src/hg/makeDb/scripts/gpnStar/gpnStarCheckSections.py src/hg/makeDb/scripts/gpnStar/gpnStarCheckSections.py
new file mode 100755
index 00000000000..ffc1b61b24b
--- /dev/null
+++ src/hg/makeDb/scripts/gpnStar/gpnStarCheckSections.py
@@ -0,0 +1,35 @@
+#!/usr/bin/env python3
+"""Report bigWig fixedStep sections whose header end-start disagrees with itemCount.
+
+The GPN-Star bigWigs from Hugging Face have such sections (end overshoots by 6 before
+each coverage gap), which makes kent bigWigAverageOverBed and bigWigCorrelate abort.
+
+usage: gpnStarCheckSections.py file.bw nShow
+"""
+import struct, sys, zlib
+f = open(sys.argv[1], "rb"); nShow = int(sys.argv[2])
+h = f.read(64)
+magic, ver, zl, chromTree, dataOff, indexOff = struct.unpack("<IHHQQQ", h[:32])
+uncompBuf = struct.unpack("<I", h[52:56])[0]
+f.seek(indexOff); rh = f.read(48)
+def leaves(off):
+    f.seek(off); isLeaf, _, cnt = struct.unpack("<BBH", f.read(4))
+    if isLeaf:
+        items = [struct.unpack("<IIIIQQ", f.read(32))[4:] for _ in range(cnt)]
+        for it in items: yield it
+    else:
+        kids = [struct.unpack("<IIIIQ", f.read(24))[4] for _ in range(cnt)]
+        for k in kids: yield from list(leaves(k))
+shown = bad = total = 0
+for off, size in list(leaves(indexOff + 48)):
+    f.seek(off); raw = f.read(size)
+    buf = zlib.decompress(raw) if uncompBuf else raw
+    chromId, start, end, step, span, typ, _, cnt = struct.unpack("<IIIIIBBH", buf[:24])
+    payload = len(buf) - 24
+    total += 1
+    mismatch = (typ == 3 and step == 1 and span == 1 and end - start != cnt)
+    bad += mismatch
+    if shown < nShow or (mismatch and bad <= 3):
+        print(f"type={typ} start={start} end={end} end-start={end-start} itemCount={cnt} payloadFloats={payload//4} {'MISMATCH' if mismatch else ''}")
+        shown += 1
+print(f"sections={total} fixedStep step1 sections with end-start != itemCount: {bad}")