# Score and assemble the track
One command does everything: on-target scoring, off-target enumeration under both CFD matrices, and
assembly of the three browser files. With `--ucscgb_chunk_max` it also **chunks the genome, fans the
off-target passes across every allocated GPU, and merges the pieces** into a single track.
```bash
crisprware score_guides \
-b $PROJ/hg38_primary_chr_gRNA/hg38_primary_chr_gRNA.bed \
-i $PROJ/hg38_primary_chr_crisprots/hg38_primary_chr_crisprots \
--cas12a_scorer deepcpf1 enpam_gb enseq_deepcpf1 seq_deepcpf1variants \
--cas12a_variant AsCas12a \
--ucscgb $PROJ/track_hg38_cas12a \
--chrom_sizes $PROJ/hg38.chrom.sizes \
--crispr_ots_bin $BIN \
--ucscgb_scanner gpu \
--ucscgb_cfd_threshold 0.023 \
--ucscgb_list_cap 1000 \
--ucscgb_blank_threshold 0 \
--mismatches 4 \
--threads 8 \
--ucscgb_chunk_max 3000000 \
-o $PROJ
```
## Flags, grouped
**Inputs**
: `-b` guide BED (from step 2). `-i` the off-target index (from step 1). `--chrom_sizes` for `bedToBigBed`.
`--crispr_ots_bin` the GPU engine.
**On-target scores** (four columns in the bigBed)
: `--cas12a_scorer deepcpf1 enpam_gb enseq_deepcpf1 seq_deepcpf1variants` with
`--cas12a_variant AsCas12a` produces **DeepCpf1**, **EnPAM-GB**, **EnCas12a-DeepCpf1**, and
**AsCas12a-DeepCpf1**.
**Off-target scoring**
: `--ucscgb` is the switch that emits the UCSC track (its value is the output track dir). The pass runs
the engine with **two CFD matrices** (enCas12a + AsCas12a/2xNLS, the second is on by default), giving
both nucleases' specificity. `--mismatches 4` is the search radius.
**Off-target list shaping**
: `--ucscgb_cfd_threshold 0.023` floors which off-targets are *listed* (counts are unfloored).
`--ucscgb_list_cap 1000` caps stored off-targets per guide. `--ucscgb_blank_threshold 0` disables the
"too many off-targets, blank the list" behavior.
**Scale / hardware**
: `--ucscgb_scanner gpu` uses the GPU engine. `--threads 8` is the CPU thread count **per chunk**.
`--ucscgb_chunk_max 3000000` turns on chunking (see below).
## What `--ucscgb_chunk_max` does
Without it, the whole genome is one pass: simplest, but peak memory and disk scale with the full guide
set, and only one GPU is used. With `--ucscgb_chunk_max N` the guide BED is split into `<= N`-guide
chunks, each scored as an isolated single-pass `--ucscgb` run, then the pieces are merged (recomputing
`crisprDetails.tab` byte offsets):
```{mermaid}
flowchart LR
G["guide BED
135.9 M"] --> S["split into
46 x 3 M chunks"]
S --> W0["GPU 0"]
S --> W1["GPU 1"]
S --> Wn["GPU n"]
W0 --> M["merge
(rebase offsets)"]
W1 --> M
Wn --> M
M --> T["one track"]
classDef s fill:#eaf2fb,stroke:#3a6ea5,color:#16314a;
class G,S,W0,W1,Wn,M,T s;
```
- One persistent worker per GPU, each claiming a device from a shared queue, so chunks never share a GPU.
- GPUs are **auto-detected** from `CUDA_VISIBLE_DEVICES` (your SLURM allocation). Override with
`--ucscgb_gpus 0,1,2`.
- Each chunk's large off-target file is deleted as soon as it assembles, so peak disk is bounded by the
chunks in flight, not the whole genome.
- `--ucscgb_keep_chunks` keeps the per-chunk dirs for debugging.
```{warning}
The bigBed **percentile** columns (score, color, the "NN%" display) are ranked **within each chunk**, the
merge does not recompute global percentiles. Raw specificity, raw on-target scores (the parenthesized
values), and the off-target **counts** are exact and population-independent. For genome-global
percentiles, re-rank the stored raw values in a post-pass; no rescoring is needed.
```
## Worked example (hg38, 7x A5500)
135.9 M guides, 46 chunks of 3 M, GPUs auto-detected from the allocation:
```text
46 chunks x ~2.1 h/chunk / 7 workers ~= 17.3 h wall
per chunk: on-target ~2 min | GPU off-target enumerate ~30-42 min | assembly (sort+bigBed) ~90 min
final merge (46 pieces): 72 min
peak job RSS ~157 GB | transient chunk disk ~1.6 TB (bounded)
```
The assembly sort, not the GPU scan, is the wall-time bottleneck (GPU duty cycle ~28%), so more GPUs help
the enumerate stage but not the assembly tail.