2c0adaa48b2f9a14109c3f90713405f259d920bc
max
  Wed Sep 9 06:08:46 2026 -0700
hg38: pcLAI local ancestry track for HPRC Release 2 haplotypes

Point cloud local ancestry inference (pcLAI) for HPRC Release 2, projected
onto GRCh38: a composite with one subtrack per haplotype, both haplotypes of
231 samples plus CHM13, 463 in all. 11,936,603 windows, autosomes only, no
windows dropped from the source files.

Each window carries the (PC1,PC2) coordinate pcLAI predicts for it, the
discretized ancestry centroid, and a confidence score. The details page draws
the window's position against the 1000 Genomes reference panel that defines
the space, via detailsScript/scatterPlot; metaDataUrl is what lets hgTrackUi
serve that panel file for a native (non-hub) track.

thickStart is one base before chromStart in 54,811 of the windows (0.46%),
against the format the pcLAI authors document, so bedToBigBed rejects it.
Neither thick column carries information here, so both are set to the item
bounds rather than dropping those windows.

Testing only for now, alpha, no ticket yet; a ticket may follow if this
becomes a real track. The makeDoc carries the detail in the meantime,
including what was deliberately left undone.

diff --git src/hg/makeDb/scripts/hprcPclai/hprcPclaiDownload.sh src/hg/makeDb/scripts/hprcPclai/hprcPclaiDownload.sh
new file mode 100755
index 00000000000..b9c49e27a03
--- /dev/null
+++ src/hg/makeDb/scripts/hprcPclai/hprcPclaiDownload.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+# Download the pcLAI GRCh38-coordinate BED files for all HPRC Release 2 haplotypes.
+# The index CSV lists one s3:// path per haplotype; s3:// is rewritten to the
+# public https endpoint of the same bucket. The submissions bucket resets
+# connections under load, so downloads are retried and run only 8-wide.
+# Usage: hprcPclaiDownload.sh <index.csv> <outDir>
+set -u -o pipefail
+idx=$1; outDir=$2
+mkdir -p "$outDir"
+
+fetchOne() {
+    # args: sample haplotype s3path outDir
+    local samp=$1 hap=$2 s3=$3 outDir=$4
+    local out="$outDir/$samp.$hap.bed"
+    [ -s "$out" ] && { echo "HAVE $samp.$hap"; return 0; }
+    local url=${s3/s3:\/\/human-pangenomics\//https://s3-us-west-2.amazonaws.com/human-pangenomics/}
+    curl -sfL --retry 8 --retry-delay 3 --retry-all-errors "$url" -o "$out.tmp" \
+        || { echo "FAIL $samp.$hap $url" >&2; rm -f "$out.tmp"; return 1; }
+    [ -s "$out.tmp" ] || { echo "EMPTY $samp.$hap $url" >&2; rm -f "$out.tmp"; return 1; }
+    mv "$out.tmp" "$out"
+    echo "OK $samp.$hap"
+}
+export -f fetchOne
+
+# strip the CRLF the index CSV uses, skip the header
+tail -n +2 "$idx" | tr -d '\r' \
+  | awk -F, 'NF>=4 {print $1"\t"$2"\t"$4}' \
+  | parallel --colsep '\t' -j 8 fetchOne {1} {2} {3} "$outDir"