Playbook · Tools and pipelines
BoltzMaker: Boltz2 campaign-scale structure and affinity prediction, and binding analysis
10 Jul 2026 · updated 17 Sep 2026 · 37 min read

BoltzMaker
BoltzMaker: Boltz2 campaign-scale structure and affinity prediction, binding analysis, and run control, orchestrated end to end from a single spec file.
| đ App | boltzmaker.mdeller.com | âïž Contact | marc@marcdeller.com | đ GitHub | bellcheddar/BoltzMaker |
A single script that manages a Boltz-2 batch campaign end to end: parse a boltz_input.md spec, generate the per-target Boltz YAML files, preflight the environment and inputs, run boltz predict with a live progress bar (with resume support), and analyze the results into a CSV, an XLSX workbook, and an HTML dashboard â optionally enriched with real protein-ligand interaction analysis via cif2plip. The non-GPU stages â the new wizard, generate, preflight, and full analyze (including PLIP interaction detection and compare-sse) â are also available with no local install at boltzmaker.mdeller.com; only the GPU run step still needs your own hardware (see Web deployment below).
Why it matters: hand-running a Boltz-2 campaign means writing dozens of near-identical YAML files by hand, remembering the right CLI flags for the hardware youâre on, and manually digging through prediction JSONs afterwards, all repetitive, error-prone steps that donât need a human. BoltzMaker turns a single annotated spec into the full pipeline: generated inputs, environment/input validation (including ligand-chemistry sanity checks â undefined stereocentres, ambiguous protonation states, stray salts â so bad input chemistry is caught before hours of compute, not silently mispredicted), a monitored run with Mac-safe memory defaults, and a ready-to-read report. It is useful for: structural biologists and drug-discovery scientists running Boltz-2 structure/affinity panels (single targets, covalent-linkage studies, or multi-chain SAR/selectivity campaigns) who want a repeatable, resumable, well-documented pipeline instead of a pile of hand-edited scripts.
Donât want to hand-write the spec at all? BoltzMaker.py new interviews you in plain English â proteins, partners, ligands, and the constraint sentences â and writes a valid boltz_input.md for you. If youâve already got a reference co-crystal or homology structure for a protein, it can even suggest pocket-contact residues automatically, remapped onto your targetâs own numbering via sequence alignment, instead of you reading them off a structure viewer by hand.
BoltzMaker grew out of five smaller, single-purpose tools written earlier: generate_yaml for building the input YAMLs, simple-zsh-script-to-run-boltz2 for driving the actual boltz predict runs, analyze-boltz2-results for the post-run analysis, cif2plip for protein-ligand interaction profiling, and smiles2grid for rendering a ligand set into a boxed grid of 2D structures. BoltzMaker consolidates all five into one spec-driven pipeline sharing a single campaign format, so the same input file drives every stage instead of juggling separate scripts and re-typing target names between them.
And yes, the name is a nod to Boltmaker, Timothy Taylorâs Champion Beer of Britain and one of the authorâs favourites.
See CHANGELOG.md for whatâs changed recently, and PROJECT_PLAN.md for the roadmap behind the To-Do list below.
đ„ Installation
Two ways to install, depending on whether the computer running BoltzMaker has internet access. Most people want Path 1. (Looking for more detail/troubleshooting on either path? See One-time setup and docs/tier_b_offline_install.md further down.)
Path 1: This computer has internet access
1. Open the Terminal app (Applications -> Utilities -> Terminal, or search for âTerminalâ with Spotlight), then make a folder for BoltzMaker and go into it:
mkdir -p ~/Desktop/BoltzMaker
cd ~/Desktop/BoltzMaker
2. Download BoltzMaker into that folder:
git clone https://github.com/bellcheddar/BoltzMaker.git .
(the trailing . tells it to put the files directly into the folder you just made, rather than inside another new folder). If this is the first time youâve used git, macOS may pop up a dialog asking to install âCommand Line Toolsâ â click Install and wait for it to finish, then run the command again.
3. Run the installer:
./install.sh
Wait for it to finish. It downloads a few GB of software the first time, so it can take a while depending on your connection.
4. Check it worked:
pixi run preflight examples/t4_lysozyme/boltz_input.md
If you see a table of green PASS results, youâre ready to go â see Commands below for what to run next.
Path 2: This computer has no internet access
Use this for a lab machine, server, or any computer thatâs offline or behind a firewall. Youâll need a second computer that does have internet access to prepare a single installer file first.
1. On the computer with internet access, follow Path 1 above, then build the offline installer file:
pixi global install pixi-pack
pixi-pack --platform osx-arm64 --ignore-pypi-non-wheel --create-executable -o boltzmaker-installer.sh
(use --platform linux-64 instead of osx-arm64 if the offline computer runs Linux)
2. Copy boltzmaker-installer.sh, together with the whole BoltzMaker folder, onto the offline computer (USB drive, internal network transfer, etc.).
3. On the offline computer, in a terminal, go to where you copied those files and run:
./boltzmaker-installer.sh -o ./boltzmaker-env
source ./boltzmaker-env/activate.sh
export KMP_DUPLICATE_LIB_OK=TRUE
4. Check it worked:
python3 BoltzMaker.py preflight examples/t4_lysozyme/boltz_input.md
Full details, including a couple of one-time extra steps this path needs, are in docs/tier_b_offline_install.md.
đ§© Architecture
+----------------+ +----------+ +-----------+ +-----+ +---------+
| boltz_input.md | --> | generate | --> | preflight | --> | run | --> | analyze |
+----------------+ +----------+ +-----------+ +-----+ +---------+
| Stage | Command | Produces |
|---|---|---|
| 1. Input | â | boltz_input.md â the family x partners x ligand DSL spec |
| 2. Generate | generate | boltz_yamls/*.yaml + manifest |
| 3. Preflight | preflight | PASS/WARN/FAIL: CLI, GPU/MPS, disk, iCloud, YAML/SMILES/chain-id/chemistry, memory |
| 4. Predict | run | boltz predict -> boltz_output/, live progress + memory monitor, resumable |
| 5. Analyze | analyze | boltz_summary.csv/.xlsx, boltz_dashboard.html, boltz_cif/, plus interaction files if setup-plip has run, plus boltz_sse_comparison.csv/.html for any family with an Apo structure: set (see compare-sse below) |
Each stage reads only the manifest + files the previous stage wrote, so any stage can be re-run on its own (generate, preflight, run, or analyze individually) without repeating the others: all simply chains all four.
đ§ One-time setup
python3 BoltzMaker.py setup
Creates a dedicated .venv (Python 3.12: boltz pins numpy<2.0, which has no prebuilt wheel for newer Pythons) next to BoltzMaker.py and installs boltz, rich, pandas, openpyxl, pyyaml, rdkit, matplotlib, psutil, scipy, gemmi, biopython, plotly, reportlab, and requests into it. This pulls PyTorch (~2-3 GB) and, the first time boltz predict actually runs, Boltz downloads several GB of model weights over the network. Every other command below transparently relaunches itself under this managed environment, so you can keep invoking the script with whatever python3 is on your PATH.
Re-run with --force to recreate the venv from scratch, or -y/--yes to skip the download confirmation prompt.
Optional: setup-plip (protein-ligand interaction analysis)
python3 BoltzMaker.py setup-plip
Entirely optional and separate from the venv above. Builds a .plip_env (via a self-downloaded micromamba, ~1-1.5GB, mostly PyMOLâs own Qt/ Cairo/HDF5 dependencies) and vendors a pinned commit of cif2plip, which converts a Boltz ModelCIF into a strict PDB and runs PLIP on it for real protein-ligand interaction fingerprints (H-bonds, salt bridges, pi-stacking, halogen bonds, metal coordination, etc.). A conda-forge-based environment is used deliberately here rather than pip: PLIP requires OpenBabel and PyMOL as in-process Python imports (not subprocess calls), and empirically, plipâs own installer forces a broken from-source OpenBabel build unless a working OpenBabel is already present, while the standalone PyPI pymol-open-source wheel has a hardcoded broken library path on this platform â conda-forgeâs builds have neither problem.
If .plip_env isnât present, everything below degrades gracefully: analyze skips interaction analysis (dashboard looks exactly as it did before this feature), and the new wizard simply doesnât ask about reference structures. Nothing else in BoltzMaker requires it. preflightâs plip_env check always reports which mode youâre in.
Optional: mkdssp/dssp (for compare-sseâs SSE-boundary-shift metric)
Not bundled or installed by any BoltzMaker command â a small external binary you may already have (brew install dssp on macOS, or conda install -c salilab dssp). Only needed for one specific compare-sse metric (secondary-structure-element boundary shift, used when a structure has no deposited HELIX/SHEET records â true for every Boltz-predicted structure). Every other compare-sse metric works without it.
Alternative: pixi (one unified environment, macOS + Linux/CUDA)
./install.sh
Installs pixi if it isnât already on your machine, then solves and installs everything (boltz, rdkit, PLIPâs OpenBabel/PyMOL stack, and the rest of requirements.txt) as one environment from pixi.toml/pixi.lock, replacing the need to separately run setup + setup-plip above. Reproducible (the committed pixi.lock pins every package on both osx-arm64 and linux-64), and the only path here thatâs actually tested against Linux/CUDA rather than macOS/MPS alone. Once installed:
pixi run preflight my_campaign.md
pixi run all my_campaign.md
(or pixi shell to activate the environment directly and run python3 BoltzMaker.py ... as normal). PLIP needs one extra one-time step, same spirit as setup-plip above: pixi run postinstall.
No internet on the target machine at all? See docs/tier_b_offline_install.md for building a single self-extracting installer script per platform (pixi-pack --create-executable) that needs no pixi, no conda, and no network to run.
đ§Ș Examples
Four entirely public-domain campaigns in examples/, run any of them with python3 BoltzMaker.py all examples/<name>/boltz_input.md:
| Example | Demonstrates | Dashboard | Input |
|---|---|---|---|
t4_lysozyme | One protein (T4 lysozyme L99A, UniProt P00720) + one ligand (benzene). No partners, no pocket_contacts. The minimal shape; smallest/fastest smoke test. | boltz_dashboard.html | boltz_input.md |
egfr_covalent | EGFR kinase domain (UniProt P00533) + a generic covalent fragment, linked via bond_constraints at Cys797. Demonstrates covalent-linkage modelling. | boltz_dashboard.html | boltz_input.md |
adrb2_gs_panel | Beta-2 adrenergic receptor (UniProt P07550), agonist vs antagonist, as two separate Protein: blocks sharing one sequence rather than one family crossed with both ligands: the agonist target co-folds a Gs alpha partner (UniProt P63092), the antagonist target doesnât (Gs only forms a stable complex with the active, agonist-bound receptor in reality â co-folding it with the antagonist too made Boltz predict a near-identical active-like fold for both, 0.38 Angstrom apart; splitting them out gets a real conformational difference, 1.28 Angstrom apart, TM6 shift roughly doubled for the agonist). Demonstrates compare-sse (see below) and why co-folded partners should match each ligandâs real biology, not just get crossed with everything. | boltz_dashboard.html | boltz_input.md |
5ht2_gq_panel | Three serotonin receptors (5-HT2A/2B/2C, UniProt P28223/P41595/P28335), each with a real agonist/antagonist pair (Psilocin/Risperidone, LSD/Balovaptan, Lorcaserin/SB-242084), each predicted both with and without the Gq heterotrimer (GNAQ+GNB1+GNG2) co-folded â a 3x2x2 panel, plus a native ligand-free (Ligands: none) apo target per receptor, used as each receptorâs compare-sse reference since no genuinely apo experimental structure exists for any of the three (checked entity-by-entity across all 59 deposited structures). Demonstrates Ligands: none, a larger size-heterogeneous campaign in one manifest, Apple Silicon MPS support for large multi-chain complexes (see below), and compare-sse against a predicted rather than experimental apo reference â TM6 centroid shift comes out consistently larger for the Gq-bound targets than their no-Gq counterparts across all three receptors, the expected activation signal (see findings.md for the full statistical write-up). | boltz_dashboard.html | boltz_input.md |
Verified end-to-end (generate -> preflight -> real boltz predict -> analyze, including cif2plip interaction analysis) on an Apple M1 Max, 64GB:
| Example | Targets | Total time | Result |
|---|---|---|---|
t4_lysozyme | 1 | 3m 16s | confidence 0.98, pIC50 8.9, 7 hydrophobic contacts matching the known L99A cavity residues |
egfr_covalent | 1 | 5m 31s | confidence 0.92, covalent Cys797 SG-to-fragment bond confirmed at 1.75 Angstrom |
adrb2_gs_panel | 2 | 1h 28m 36s (ADRB2_ISO1, agonist+Gs) + 25m 6s (AR2NG_PRO1, antagonist alone) | confidence 0.79 (ADRB2_ISO1) / 0.83 (AR2NG_PRO1) |
5ht2_gq_panel | 15 | 9 small targets (apo + receptor-alone) ~4-5m each; 6 large receptor+Gq-heterotrimer complexes ~43-48m each | All 15 completed successfully (12 ligand-bound + 3 apo); confidence 0.66-0.81, iPTM up to 0.99 for the ligand-bound complexes |
Run time scales with complex size, not just target count: ADRB2_ISO1âs two-chain receptor+Gs-partner complex took disproportionately longer than the single-chain examples, since attention-style operations scale worse than linearly with sequence length â confirmed directly by AR2NG_PRO1 (same receptor, no partner) finishing in a fraction of the time. One contributing factor on Apple Silicon specifically: torch.linalg.svd (used in the diffusion step) has no MPS implementation and silently falls back to CPU â worth budgeting for on large multi-chain campaigns.
5ht2_gq_panelâs six large 4-chain receptor+Gq-heterotrimer targets (~1250-1280 tokens) originally crashed on Apple Silicon: boltzâs triangular attention computes the full row-wise QK^T score matrix for the whole complex in one unchunked matmul, which exceeds MPSâs single-tensor size ceiling past roughly 1250 residues and crashes the process inside PyTorchâs internal tiled-bmm fallback. Each rowâs attention is independent, so chunking along that axis is exact, not an approximation â setup now patches this directly into the installed boltz package (idempotent, and checked against boltzâs exact source so a future upgrade canât be silently mis-patched). run also wraps boltz predict with caffeinate automatically (macOS only, silently skipped if unavailable) as general sleep-prevention hygiene for long GPU jobs. All 15 5ht2_gq_panel targets, including the six large complexes, now complete successfully â see CHANGELOG.md for the fix in full.
đ§ boltz_input.md format
Plain labelled text â no markdown, no YAML, no brackets, no quoting. One rule: blocks are Label: value lines with a blank line between them; comments start with #. Field names are plain English (Output folder, Predict affinity, Pocket contact) rather than Boltz-internal snake_case. Donât want to hand-write it at all? Run python3 BoltzMaker.py new and answer plain questions instead â see Commands below.
The format has two layers: a family x partners x ligand cross-product (the ergonomic layer: write each protein/ligand once, get every combination as a separate target), and standalone constraint sentences for the two/three-ended relationships (covalent bonds, distance constraints) that donât fit inside one block â each names the protein it belongs to and can be written anywhere in the file.
Settings:
Output folder: ./boltz_yamls # where generated per-target YAMLs are written
Predict affinity: no # off by default -- it's a heavier prediction pass
Protein: RECP1 # short name, MAX 5 CHARACTERS (Boltz stores chain names
# in a fixed 5-char field internally and silently
# truncates longer ones, which then crashes later with a
# confusing error -- `preflight` catches this for you).
# Also names the output file: {protein}_{ligand}.yaml
Sequence: MDILC... # required
Partners: CHNX, CHNY # optional: co-folded chains, defined as their own
# Partner: blocks below
# Ligands: LIG1, LIG3 # optional: restrict this protein to a ligand subset
# (default: crossed with every ligand below)
# Ligands: none # optional: ligand-free (apo) target -- no ligand entity,
# no pocket constraint, no affinity property, whatever
# `Predict affinity:` says. Stem is just the protein name
# (e.g. `RECP1.yaml`, not `RECP1_LIG1.yaml`), runs through
# the same generate/preflight/run/analyze pipeline and the
# same staged `boltz predict` batch as every other target.
# Modifications: SEP:5 # optional: CCD:position tokens for modified residues
# (e.g. phosphoserine)
# Cyclic: yes # optional: cyclic polymer (e.g. a cyclic peptide)
# MSA: empty # optional: path to a precomputed MSA, or "empty" for
# single-sequence mode (skip MSA generation)
# Templates: reference_structure.cif
# optional: structural template file(s), applied to all
# protein chains (no per-chain mapping -- hand-edit the
# generated YAML for that rarer case)
# Apo structure: reference/apo.pdb
# optional: a reference apo/unbound structure, used only
# by `compare-sse` (see below), never by generate/run.
# No genuinely apo experimental structure? Predict one:
# give another `Protein:` block the same `Sequence:` and
# `Ligands: none` (see above), run the campaign once, then
# point `Apo structure:` at its output in `boltz_cif/`.
# Apo chain: A # optional: explicit chain id in the apo structure above
# (omit to auto-detect via sequence identity)
# Family type: gpcr # optional: gpcr / kinase / auto (default) -- selects
# `compare-sse`'s motif annotator
# Group: RECP1 # optional: shared display/report name for multiple
# `Protein:` blocks that are the same underlying receptor
# (e.g. with/without a partner, or a predicted apo
# variant) -- defaults to this block's own name if unset
Partner: CHNX
Sequence: MTLES...
# Type: dna # optional: protein (default) / dna / rna
# Copies: X1, X2 # optional: homo-oligomer chain-id override -- this one partner
# sequence becomes multiple chains
Ligand: LIG1
SMILES: FC(F)CNC(...)=O # exactly one of SMILES/CCD is required
# Role: agonist # optional: agonist / antagonist -- reporting only (dashboard
# chart shapes), never affects generate/run
Ligand: LIG2
CCD: GOL # a Chemical Component Dictionary code (e.g. common crystallization
# additives/ions) instead of a SMILES
Covalent bond: RECP1 residue 44 atom SG to LIG1 residue 1 atom C3
Pocket contact: RECP1 residue 148
Distance constraint: RECP1 residue 10 to RECP1 residue 80 within 8.0 Angstrom
Every protein is crossed with every ligand (unless a protein sets Ligands: to scope itself to a subset, or Ligands: none for a single ligand-free/apo target), producing one {protein}_{ligand}.yaml per pair. See example.md for the full copy-paste template and examples/ for complete working campaigns.
đ Commands
python3 BoltzMaker.py new [boltz_input.md] # write a new campaign by answering plain questions
python3 BoltzMaker.py format boltz_input.md # auto-align comments/blank-lines (cosmetic only)
python3 BoltzMaker.py generate boltz_input.md # write the target YAMLs + manifest
python3 BoltzMaker.py preflight boltz_input.md # environment + input sanity checks
python3 BoltzMaker.py run boltz_input.md # boltz predict, live progress, resumable
python3 BoltzMaker.py analyze boltz_input.md # CSV / XLSX / HTML dashboard
python3 BoltzMaker.py all boltz_input.md # generate -> preflight -> run -> analyze
python3 BoltzMaker.py boltz_input.md # same as `all` (subcommand is optional)
python3 BoltzMaker.py compare-sse boltz_input.md # apo-vs-holo secondary-structure motif shifts (see below)
new interviews you (proteins, partners, ligands, and the three constraint sentence types) and writes the file for you â it wonât overwrite an existing file without asking first. It covers the common case only; rarer fields (modifications, cyclic, MSA override, templates, homo-oligomer copies) are left for hand-editing the file it writes. If setup-plip has been run, it also asks whether you have a reference structure with a ligand already bound (a co-crystal or homology model) for each protein â if so, it runs cif2plip on it, lets you pick the relevant ligand if more than one is detected, and suggests the contacted residues as Pocket contact: constraints, remapped onto your targetâs own numbering via sequence alignment (BLOSUM62 + affine gaps) so the reference structureâs residue numbers donât have to match your targetâs.
format re-aligns trailing comments to a clean column and normalizes blank-line spacing around section/record boundaries, purely cosmetic (it validates the file parses first, and never changes meaning). Pass --check to report whether reformatting is needed without writing anything (exit 1 if so, e.g. for a pre-commit check).
Any field BoltzMaker doesnât recognize (a typo like Predict afinity: instead of Predict affinity:) prints a WARNING naming the block, its name, and the line number, and is otherwise silently dropped, so a misspelled field never just vanishes without a trace.
Common options:
| Option | Default | Description |
|---|---|---|
--output-dir | settings.output_dir | Override where generated YAMLs are written |
--out-dir | ./boltz_output | Boltzâs own --out_dir, next to the md file |
--accelerator | auto | auto / gpu / cpu |
--limit N | none | Cap how many pending targets run submits (smoke test before a full batch) |
--max-retries | 2 | Auto-retry a target that doesnât complete (e.g. an OOM), isolating to one target at a time â see âMemory on Macâ below (0 disables) |
--strict | off | Promote preflight WARN to FAIL |
--skip-interactions | off | Skip cif2plip interaction analysis during analyze, even if setup-plip has been run |
--skip-sse | off | Skip compare-sse apo-vs-holo analysis during analyze, even if a family has Apo structure: set |
Memory-control options (see below for why these matter on Mac/unified-memory hardware):
| Option | Default | Description |
|---|---|---|
--workers | 2 | Matches Boltzâs own default |
--mps-watermark | 1.0 | PYTORCH_MPS_HIGH_WATERMARK_RATIO cap |
--max-parallel-samples | 1 | Boltz --max_parallel_samples |
--recycling-steps | Boltz default | Passthrough |
--sampling-steps | Boltz default | Passthrough |
--diffusion-samples-affinity | Boltz default | Passthrough |
--sampling-steps-affinity | Boltz default | Passthrough |
--max-msa-seqs | Boltz default | Passthrough |
--memory-warn-tokens | 1000 | Preflight size-heuristic WARN threshold |
run is idempotent: targets with a complete prediction (cif + confidence json, and an affinity json if predict_affinity is on) are skipped on re-run, so an interrupted batch can just be re-run as-is.
compare-sse options (see the section below for what it does â it now also runs automatically as part of analyze/all for every family with an Apo structure: set; the standalone command below is for re-running just this analysis on its own, e.g. after adding an apo structure without re-running boltz predict):
| Option | Default | Description |
|---|---|---|
--family | every family with Apo structure: set | Restrict to one Protein family id |
--target | every target for the selected family | Restrict to one target stem |
--out-dir | alongside boltz_input.md | Where to write the CSV/HTML/PyMOL scripts |
--phi-psi-threshold | 30 (degrees) | Per-residue phi/psi delta above this is flagged |
--dfg-distance-threshold | 8.0 (Angstrom) | DFG-Asp to catalytic-Lys Ca-Ca distance below this is classified DFG-in |
--alphac-distance-threshold | 10.0 (Angstrom) | alphaC-Glu to catalytic-Lys Ca-Ca distance below this is classified alphaC-in |
--no-pymol | off | Skip writing .pml session scripts |
--refresh-cache | off | Bypass the GPCRdb/KLIFS/PDBe disk cache for this run |
đ ïž Memory on Mac (unified-memory) hardware
A real 4-chain GPCR+G-protein complex (~1250 combined residues/atoms) used ~65GB RAM on a 64GB M1 Max during testing and swap-thrashed for 20+ minutes with zero progress before being killed, worth knowing about before running anything large. Mitigations built in:
--mps-watermarksetsPYTORCH_MPS_HIGH_WATERMARK_RATIO, which caps how much memory MPS will allocate relative to the deviceâs recommended maximum. At the default1.0, an oversized complex now raises a clean MPS out-of-memory error in the log instead of silently spilling into swap.--workersdefaults to 2 and--max-parallel-samplesto 1: both trade a little parallelism for a much smaller memory footprint, which matters more on unified memory than on a dedicated-VRAM GPU.preflightâsmemory_heuristiccheck WARNs when a targetâs total residue+ligand-atom count crosses--memory-warn-tokens(default 1000), citing the empirical data point above. Itâs a rough heuristic, not a precise memory model.runâs progress bar shows live memory usage (RSS summed across the wholeboltz predictprocess tree), and logs a warning if usage stays above 90% of system RAM for 60+ seconds with no new completed target: a sign of thrashing, not genuine progress.run/allauto-retries (--max-retries, default 2) any target that doesnât complete, isolating every still-incomplete target to its own single-targetboltz predictinvocation from the first retry onward â a real 4-target cascade on5ht2_gq_panel(an OOM on 2 of 6 large targets run together crashed the shared affinity phase for 2 more that had already succeeded) recovered cleanly this way. This means a large campaign can be started and left unattended: a transient OOM no longer needs a human to notice, wait, and manually re-run just the affected targets.
If a target still fails after every automatic retry, thatâs a real finding (this hardware may not be viable for a complex that size), not something to force through.
âïž Progress bar
Two rows during run: the outer bar shows targets done/total, elapsed time, an ETA, and live memory usage. The inner row shows which phase Boltz is in (MSA generation / structure prediction / affinity prediction) and that phaseâs own progress, parsed from Boltzâs own log output. Note: Boltz doesnât expose diffusion- or recycling-step-level progress anywhere in its output (verified against the installed packageâs source), so this is the finest granularity actually available, not a per-step diffusion counter.
đ Outputs
Written next to boltz_input.md:
| Output | Description |
|---|---|
boltz_run_<timestamp>.log | Raw boltz predict output for the run |
boltz_output/ | Boltzâs own prediction output tree |
boltz_cif/ | Every completed targetâs *_model_0.cif, flattened into one folder |
boltz_summary.csv / boltz_summary.xlsx | One row per target: every scalar field from the confidence/affinity JSONs, computed pIC50, the input ligand SMILES, and flags/notes columns (LOW_CONFIDENCE, HIGH_CONFIDENCE_POOR_AFFINITY, LOW_CONFIDENCE_STRONG_AFFINITY, LOW_POCKET_PLDDT, MISSING_OUTPUTS). The XLSX adds a selectivity sheet (ligand x family pIC50 pivot) whenever a campaign spans more than one protein family. When setup-plip has run, also gets a plip_status column (ok / no_interactions / failed / ambiguous_ligand / skipped_no_env) and a plip_<type>_count column per interaction type detected (hydrophobic, hydrogen_bonds, salt_bridges, etc.). |
boltz_summary_view.csv | The same columns shown in the dashboardâs âSummary tableâ (see below) â a trimmed, renamed subset of boltz_summary.csv, for anyone who wants the at-a-glance view in a spreadsheet rather than every raw field |
boltz_ligand_grid.pdf (optional) | A print/share-friendly PDF of the dashboardâs âLigand structuresâ grid â same 5Ă5 pagination, same rendered structures, severity borders and scaffold highlighting as the on-screen version, in the style of smiles2gridâs own PDF output. Only written when a campaign has at least one SMILES ligand to render. |
boltz_plip/ (optional) | Per-target cif2plip output: the converted PDB, PLIPâs XML/TXT reports, the ray-traced binding-site PNG, and the PyMOL .pse session â cached here so re-running analyze doesnât re-profile a target thatâs already been done |
boltz_interactions.csv (optional) | Long format, one row per detected contact across every target: interaction type, residue, distance â the raw data behind the dashboardâs fingerprint heatmap and per-target contact tables |
boltz_dashboard_sessions/ (optional) | Each targetâs PyMOL .pse session, copied here and linked from the dashboard â this is the one thing that makes boltz_dashboard.html no longer a single self-contained file once interaction analysis has run; without setup-plip, the dashboard stays exactly as self-contained as before |
boltz_dashboard.html | Posts its own real content height to any parent window via postMessage on load and resize, so a page embedding it in an iframe (e.g. findings.mdâs âInteractive dashboardâ section) can size the iframe to fit the actual content instead of guessing a fixed height â a cross-origin iframe canât otherwise be measured/resized from the embedding pageâs own JS. A campaign summary table with a third âDetailsâ column alongside Field/Value â a linked path to the input file, each protein/partnerâs id and sequence length, each ligandâs id and SMILES-vs-CCD source, the full list of target stems, which specific ligands got flagged in ligand-chemistry review (linking to the card below), and a plain-English gloss for each of the more cryptic run parameters (accelerator, MPS watermark, recycling/sampling steps, etc.) â tracked across every run invocation in a small hidden sidecar file. Then a âSummary tableâ directly below it: grouped into named column bands (Identity, Confidence, Affinity, Interactions, Structure) with short human headers instead of raw JSON field names, redundant/granular columns (per-chain and per-chain-pair confidence breakdowns, individual ensemble sub-model values) hidden by regex pattern rather than a fixed list â so it scales correctly to campaigns with more than two chains â and two download links, one for the full underlying CSV and one for a CSV matching just this trimmed/renamed view. The âTargetâ column shows a {group}_{partners}_{ligand} display name (e.g. 5HT2A_GNAQ+GNB1+GNG2_RISP, partners omitted when there are none, apo in place of a ligand for a ligand-free target) rather than the internal per-variant family id/stem (H2ANG_RISP, H2AAP) â and this isnât just a table label: the same display name (or its family-level {group}_{partners} form, with no ligand, for whole-family contexts) replaces the internal id in every chart tick/legend/point label (ranked pIC50, ranked confidence, the pIC50-vs-confidence-score and pIC50-vs-binder-probability scatters, interaction counts, the residue-interaction fingerprint heatmap), every per-target/per-family card title, the campaign-summary target list, and the selectivity pivotâs columns (both the dashboard heatmap and the XLSX selectivity sheet) â see compare-sse below for the same treatment there. The raw per-variant ids stay alongside the display name in every underlying CSV/XLSX targets sheet, for cross-referencing against real output filenames. A âPartnerâ column lists each targetâs co-folded partner chain(s) (hidden when the campaign has none), and rows are grouped by Group:/family id with a blue top border marking each new group â the same blue used for column-group boundaries, just rotated. The âFlagsâ column is renamed âSummaryâ and icon-based: a bullseye (affinity) and a shield (confidence) icon per row, each tinted green/amber/red by tier (exact value and interpretation on hover), reusing the existing LOW_CONFIDENCE_THRESHOLD and a symmetric buffer around Boltzâs documented 0.5 binder decision boundary â Boltzâs own docs define these metricsâ [0, 1] range but publish no official tri-colour bands. A MISSING_OUTPUTS failure collapses the cell to a single red cross; a legend to the right of the download links spells out all six tier/icon combinations. Always shown now (previously hidden entirely when nothing was flagged), so a clean campaign reads as a row of green icons rather than a column that silently disappears. A ligand-free (apo) targetâs ligand/affinity/interface/interaction columns (including the bullseye) show an explicit N/A rather than a blank cell or a misleading 0.00, since thereâs no ligand or inter-chain interface for those to describe. Then a âLigand preparationâ card (the same stereocentre/protonation-state/disconnected-fragment checks as preflightâs ligand_preparation check, shown per-ligand rather than as a single summary line), then a âLigand structuresâ card: a paginated 5Ă5 grid of every ligandâs rendered 2D structure (building on smiles2gridâs design, adapted for a single campaignâs scale), with stereocentre/ionizable-group findings highlighted directly on each structure, ligands sharing a Bemis-Murcko scaffold (or, failing that, a verified whole-group maximum-common-substructure) grouped and colour-highlighted together with their depictions aligned to a common orientation, and a captioned legend (badge-by-badge: what S/A/N/Ph/SO3/salt each mean, plus the cluster colour key) stating exactly what was found and on how many ligands â never an unexplained highlight â plus âDownload PDFâ (the same grid as a print/share-friendly file, boltz_ligand_grid.pdf) and âDownload SMILESâ (boltz_ligands.csv: ID, SMILES, stereocentre/ionizable-group/fragment findings, MW, cLogP, TPSA) links side by side on one line, matching the Summary tableâs own download-links style. Then interactive Plotly charts in a grid (ranked pIC50, ranked confidence, a âpIC50 vs confidence scoreâ scatter, interaction counts by type, then a âpIC50 vs binder probabilityâ scatter (binder probability on x, pIC50 on y) â hover/zoom/pan; plotly.js itself is vendored and inlined into the file rather than CDN-loaded, so the dashboard has no runtime dependency on an external script host). The two scatter charts colour each point by tier via a continuous colourscale + colorbar legend (the same style as the Family x ligand selectivity heatmapâs own colorbar) â confidence tier (matching the Summary tableâs shield icon) for pIC50-vs-confidence-score, affinity tier (matching the bullseye icon) for pIC50-vs-binder-probability â and, when a Ligand: block sets the optional Role: agonist/Role: antagonist field, shape-code points by pharmacology (circle = agonist, diamond = antagonist) with a legend positioned inside the plot areaâs top-left corner (not Plotlyâs default outside-right position, which would otherwise collide with the colorbar); campaigns that donât set Role: see a single unshaped trace, unchanged from before. When setup-plip has run: a per-family residue-interaction fingerprint heatmap (also interactive Plotly â shown for every family with interaction data, even a single ligand, though the similarity-based reordering that helps SAR ranking within a series only kicks in from 3+ ligands) and, per target, its binding-site image (residues labelled and interaction distances shown â PLIPâs own images have neither, so these are re-rendered from its PyMOL session with both added, with a âDownload imageâ link of its own) next to an interactive, auto-rotating 3Dmol.js view of the same predicted structure (built directly from the mmCIF, ligand highlighted), side by side with a table of that targetâs contacts (with its own âDownload CSVâ link) plus a download link for the full PyMOL session. Finally, a âSecondary structure shiftsâ card (see compare-sse below): a âFamily coverageâ table (every protein family in the campaign, with its status â OK and a target/motif count, or a plain-English reason it was skipped, e.g. âNo apo structure configuredâ), an âOverall shift statisticsâ summary, and, when thereâs data, the full per-motif table plus its own Plotly charts. |
boltz_sse_comparison.csv / .html | Written automatically by analyze/all whenever any family has Apo structure: set (or on demand via the standalone compare-sse command). One row per family/target/motif: Ca RMSD, centroid shift, helix-axis rotation/kink angles, SSE boundary shift, flagged phi/psi residues, and (kinases) DFG-in/out and alphaC-in/out states â a metric that genuinely wasnât computed for a motif shows as N/A, not a blank cell. The Family/Target columns, chart legends, and family-coverage table all show the same {group}_{partners} / {group}_{partners}_{ligand} display names used throughout the main dashboard; the CSV also keeps the raw family_id/target_stem columns alongside for cross-referencing. The HTML is a standalone dashboard (family coverage, overall shift statistics, Plotly bar chart + motif x target heatmap); the same content is also embedded directly into boltz_dashboard.html (see below) |
boltz_sse_family_status.json | One entry per protein family: ok (with a target/motif count) / no_apo_structure / apo_not_found / annotation_failed / no_predicted_structures â the machine-readable form of the dashboardâs âFamily coverageâ table, so a family with no Apo structure: configured reads as ânot configuredâ rather than silently missing |
boltz_sse_comparison_sessions/ (optional) | A plain-text PyMOL .pml script per target â colours/labels each motif, highlights the ones with a significant shift |
đŹ Ligand validation & scaffold highlighting
Two related but distinct checks run over every SMILES ligand before you commit hours of boltz predict time to them, and both surface directly in the dashboard.
Why this exists: Boltz folds whatever chemistry itâs given â an undefined stereocentre, an unintended protonation state, or a stray counterion left in a SMILES string doesnât raise an error, it just silently changes the predicted pose and affinity. These are exactly the mistakes a non-specialist (or a tired specialist) makes typing SMILES by hand, and theyâre invisible until youâre staring at a confusing result with no idea the input was ever wrong.
Ligand preparation (validity checks)
At parse time, every ligand SMILES is canonicalized (RDKit) so the same molecule is represented consistently everywhere downstream â the generated YAML, the summary table, and cif2plipâs own ligand-matching (see the InChIKey-based matching note in CHANGELOG.md). Then, both at preflight (as the ligand_preparation check) and again in the dashboardâs âLigand preparationâ card, each ligand is checked for:
| Check | How | What it means |
|---|---|---|
| Undefined stereocentres | Chem.FindMolChiralCenters(includeUnassigned=True) | A stereocentre exists in the molecule but the SMILES doesnât specify which enantiomer/diastereomer â Boltz will fold some version of it, possibly not the one you intended |
| Disconnected fragments | Chem.GetMolFrags() returns more than one fragment | Likely a salt or counterion left in the SMILES (e.g. a sodium carboxylate written as two components) |
| Ionizable groups | SMARTS match: carboxylic acid, primary/secondary amine, phenol, sulfonic acid | The groupâs protonation state at physiological pH isnât specified by a plain SMILES â worth a deliberate choice, not a default assumption |
All of this is advisory, not a hard failure â these can be legitimate, deliberate modelling choices â but theyâre the kind of thing worth a second look before trusting downstream numbers.
Scaffold highlighting (the âLigand structuresâ grid)
Separately, the dashboardâs ligand grid tries to answer a different question: do any of these ligands share a chemical core? This matters most for SAR (structure-activity relationship) campaigns, where a chemist is usually testing close analogues on purpose, and seeing the shared scaffold at a glance (with the parts that differ jumping out) is more useful than reading each SMILES individually. Two tiers, in order, and nothing is highlighted unless one of them actually finds something real:
- Exact Bemis-Murcko scaffold match â ligands whose ring systems + connecting linkers are chemically identical are grouped, threshold-free. This is the dominant case for a real SAR series.
- Fallback for near-analogues: ligands left over are grouped by Morgan/Tanimoto fingerprint similarity, then a maximum common substructure (MCS) is computed across the whole group and verified to actually match every member â so the claim is a proven substructure match, not just an assigned similarity score.
Small or trivial shared fragments (below 8 heavy atoms â e.g. âthey all contain a benzene ringâ) are deliberately not highlighted; a group has to share something structurally meaningful to be called out. Ligands in the same scaffold group also have their 2D depiction aligned to a common orientation, so the shared core is drawn in the same position across cells and visually âsnaps together.â
Whatâs highlighted and how, directly on each rendered structure â the same badges shown on each ligand cell and spelled out in the panelâs own legend:
| Badge | Colour | Meaning |
|---|---|---|
S | đȘ Magenta | Undefined stereocentre (RDKit also draws its own (?) marker at the atom) |
A | đ§ Amber | Carboxylic acid â protonation state not specified |
N | đ§ Amber | Primary/secondary amine â protonation state not specified |
Ph | đ§ Amber | Phenol â protonation state not specified |
SO3 | đ§ Amber | Sulfonic acid â protonation state not specified |
salt | đ„ Red | Disconnected fragment (salt/counterion) â flagged on the border and badge only, not atom-highlighted (thereâs no single meaningful atom to point at) |
| â | one of six colour-blind-safe palette colours | Atoms in a shared scaffold/substructure â consistent per group, with a legend entry naming the group and how many ligands share it (e.g. âshared scaffold â 3/5 ligandsâ) |
A specific finding (stereocentre, ionizable group) always takes priority over the softer scaffold highlight if they overlap on the same atom, since itâs the more actionable signal. If no ligand shares a real scaffold with any other, the panel says so plainly (âno shared scaffold or substructure detectedâ) rather than forcing a highlight onto something coincidental. CCD-code ligands have no SMILES to render and show a plain placeholder instead of an empty cell.
đ§Ź compare-sse: apo vs holo secondary-structure shifts
Why this exists: a confidence score tells you what Boltz predicted, not how the protein moved in response to ligand binding â a real structural question whenever you have both a reference apo (unbound) structure and a predicted holo one for the same protein. compare-sse answers it in terms a structural biologist actually reasons in (âTM6 swung out 4.2 Angstromâ, âthe DFG motif flipped from in to outâ), not raw DSSP fragment coordinates.
Itâs a core part of analyze/all: any family with an Apo structure: field set (see boltz_input.md format above) gets compared automatically, no separate command needed, and the result is embedded directly into boltz_dashboard.html. A family with no apo structure configured isnât silently skipped either â the dashboardâs âFamily coverageâ table says so explicitly, alongside any family that was compared. Pass --skip-sse to analyze/all to opt out, or use the standalone compare-sse command below to re-run just this analysis on its own (its own --family/--target flags let you target one family/target instead of the whole campaign).
Motifs are annotated by one of three pluggable sources, auto-selected per family (or set explicitly with Family type:):
| Family type | Motifs | Source |
|---|---|---|
gpcr | TM1-7, H8, ECL1-3, ICL1-3 | GPCRdbâs structure-based generic-numbering service (Ballesteros-Weinstein / GPCRdb schemes) |
kinase | hinge, gatekeeper, catalytic loop (HRD), DFG motif, alphaC-Glu, catalytic Lys | KLIFSâs public REST API (its fixed 85-residue pocket alignment) |
auto (default) | whichever of the above applies, else⊠| âŠfalls back to Pfam domain boundaries via PDBeâs SIFTS residue mapping â the universal last resort for any protein outside the two families above |
Apo is superposed onto holo using only the familyâs stable, non-binding-site-adjacent residues (via gemmiâs superpose_positions), so a ligand-induced local shift canât skew the global fit. Each motif then gets:
| Metric | What it means |
|---|---|
| Ca RMSD / centroid shift | How far the motif moved, post-superposition |
| Helix-axis rotation angle | For helical motifs â e.g. the classic TM6 âoutward swingâ on GPCR activation |
| Helix kink angle (apo/holo/delta) | Whether a helix straightened or kinked more |
| SSE boundary shift | Did the helix/strand get longer or shorter â needs deposited HELIX/SHEET records, or an optional external mkdssp/dssp binary as a fallback (see One-time setup above); every other metric works without it |
| Flagged phi/psi residues | Per-residue backbone dihedral outliers above --phi-psi-threshold |
| DFG-in/out, alphaC-in/out (kinases only) | A coarse Ca-Ca distance classifier, not a full dihedral model â good for detecting a state change between apo and holo, not publication-grade conformational classification |
A metric that genuinely wasnât computed for a given motif (e.g. axis rotation for a loop, DFG state for a non-kinase family, boundary shift with no DSSP data available) shows as an explicit N/A in both the CSV and every dashboard table, not a blank cell.
Above the per-motif table, both boltz_dashboard.htmlâs embedded card and the standalone boltz_sse_comparison.html show:
| Section | Content |
|---|---|
| Family coverage | One row per protein family: OK (with a target/motif count) / No apo structure configured / Apo structure file not found / No motif annotation available / No predicted (holo) structures yet |
| Overall shift statistics | Targets/motifs compared; mean/median/max Ca RMSD (and which target + motif had the largest shift); mean centroid shift; total flagged phi/psi residues; kinase DFG/alphaC state-change counts |
python3 BoltzMaker.py compare-sse boltz_input.md
Writes, next to boltz_input.md (or --out-dir): boltz_sse_comparison.csv (one row per family/target/motif) and boltz_sse_family_status.json (the family-coverage table above, machine-readable), a standalone self-contained boltz_sse_comparison.html (Plotly bar chart + motif x target heatmap, vendored the same way as the main dashboard), and boltz_sse_comparison_sessions/<target>.pml â a plain-text PyMOL script per target that colours/labels each motif and highlights the ones with a significant shift. Itâs just text: opens in any local PyMOL install, no pymol dependency in BoltzMakerâs own venv.
When auto-run by analyze/all, a campaign with no apo structures configured anywhere just gets a dashboard section saying so â it never aborts the rest of the pipeline over an optional, additive feature. The standalone command above still exits with a clear error if you explicitly pass a --family/--target that matches nothing, since thatâs a real mistake worth stopping for.
đ©č Troubleshooting / FAQ
| Problem | Fix |
|---|---|
setup-plip fails, or pip install plip tries to build OpenBabel from source | This is expected without conda-forge â plipâs own installer forces a from-source OpenBabel rebuild unless OpenBabel is already importable inside pipâs build sandbox, and the standalone PyPI pymol-open-source wheel has a hardcoded broken library path on at least some machines. setup-plip works around both by building a conda-forge env via a self-downloaded micromamba â just re-run python3 BoltzMaker.py setup-plip --force if a previous attempt left a half-built .plip_env. |
A preflight/analyze step involving .plip_env errors with ModuleNotFoundError: No module named 'chatmol' (or similar) | A stray ~/.pymolrc.py on your machine (e.g. from an unrelated PyMOL plugin) is being loaded by the bundled PyMOL. BoltzMaker already overrides HOME for these subprocess calls so this shouldnât reach you, but if it does, check ~/.pymolrc.py for anything referencing a package not installed in .plip_env. |
run seems to hang with no progress, or your Mac gets extremely slow | Check the memory-usage figure in the progress bar and see âMemory on Macâ earlier in this document â this is very likely swap-thrashing, not a genuine stall. Re-run with a lower --mps-watermark, --workers 1, and --max-parallel-samples 1. |
A targetâs YAML/CIF exists on disk but BoltzMaker says itâs missing, or preflight hangs | Check for iCloud âOptimize Mac Storageâ dataless files â preflightâs icloud_materialize check handles this automatically, but a very large campaign can take a while to force-download everything on first run. |
boltz fails during setup with a numpy build error | Youâre likely on Python 3.13+. boltz pins numpy<2.0, which has no prebuilt wheel past cp312 â _find_boltz_python() already looks for a python3.12 specifically; install one (brew install python@3.12) if it canât find one. |
| A target fails preflight with a chain-id-length error | Boltz truncates chain IDs to 5 characters internally (a fixed-width field in its own schema) and silently corrupts longer ones rather than erroring at parse time â shorten the protein/partner/ligand name in boltz_input.md. |
| The dashboardâs charts (or the binding-site 3D view) donât render, or look unstyled | plotly.js and 3Dmol.js are both vendored and inlined (not CDN-loaded), so this shouldnât happen from a missing network connection â Google Fonts is still loaded from a CDN for styling, though, so the page needs internet access at least once for the fonts to look right (falls back to a generic sans-serif otherwise; charts, 3D views, and data are unaffected). If they genuinely donât render, check that vendor/plotly-2.35.2.min.js and vendor/3Dmol-2.5.5-min.js exist next to BoltzMaker.py â analyze prints a warning and falls back to the relevant CDN (which is known not to work in some HTML-preview contexts) if either is missing. |
đ Web deployment
Live at boltzmaker.mdeller.com. A Flask frontend for the non-GPU stages of the pipeline â upload a boltz_input.md (or build one with the wizard), run generate/preflight and download the results, or upload a completed campaign folder (from a run you did locally) for full analyze, PLIP interaction detection, and compare-sse, all in the browser. The GPU run step is deliberately never hosted: thereâs no GPU on the droplet, so that stage still runs on your own hardware and you bring the boltz_output/ back for analysis.
Source and tested-CLI isolation. The web app lives in web/, developed on a separate web git branch/worktree so the tested BoltzMaker.py on main is never touched by web-app work. BoltzMaker.py was never designed to be imported (it relaunches itself into a managed venv at module import time), so the Flask app only ever invokes it as a subprocess, with an explicit minimal environment and a per-command timeout, through a dedicated trimmed venv (.venv/, torch/boltz-free, ~500MB â every torch/boltz reference in the script is a function-local lazy import) kept separate from the Flask-serving venv (web/.venv/).
Optional PLIP. If the dropletâs .plip_env (the same conda-forge PyMOL/OpenBabel environment setup-plip builds locally, ~1-1.5GB) is present, analyze runs full protein-ligand interaction detection and compare-sse automatically; if not, both degrade gracefully rather than erroring, exactly as they do locally.
Serving stack: gunicorn (3 sync workers, 900s timeout to cover PLIP rendering and compare-sseâs GPCRdb/KLIFS/PDBe lookups) behind nginx (Letâs Encrypt TLS via certbot, HTTP/2, rate-limited upload endpoints, 200MB body cap), as a hardened systemd service (dedicated unprivileged user, memory/CPU caps). Every request gets its own scratch directory, deleted when the request finishes and swept by a five-minute cleanup timer as a backstop; uploaded zips are checked for zip-slip and zip-bomb payloads (path-traversal/absolute-path rejection, uncompressed-size and entry-count caps) before anything is extracted.
đ§« Testing
.venv/bin/pip install -r requirements-dev.txt
.venv/bin/pytest tests/
27 tests covering compare-sseâs annotators against real fixture data (a real apo EGFR kinase-domain structure vs the egfr_covalent exampleâs real holo prediction; a real apo beta2-adrenergic-receptor structure vs adrb2_gs_panelâs real holo predictions), with GPCRdb/KLIFS/PDBe network calls swapped for an injectable fake client seeded with real, previously-verified API responses â fully offline and fast (~2s). Plus grammar and CLI-resolution tests for the parser fields above.
đ Citation
Passaro, S., Corso, G., Wohlwend, J., Reveiz, M., Thaler, S., Somnath, V.R., Getz, N., Portnoi, T., Roy, J., Stark, H., Kwabi-Addo, D., Beaini, D., Jaakkola, T., Barzilay, R. (2025). Boltz-2: Towards Accurate and Efficient Binding Affinity Prediction. bioRxiv. https://doi.org/10.1101/2025.06.14.659707
Mirdita, M., SchĂŒtze, K., Moriwaki, Y., Heo, L., Ovchinnikov, S., Steinegger, M. (2022). ColabFold: making protein folding accessible to all. Nature Methods. https://doi.org/10.1038/s41592-022-01488-1
Schake, P., Bolz, S.N. et al. (2025). PLIP 2025: introducing protein-protein interactions to the protein-ligand interaction profiler. Nucleic Acids Research, gkaf361. https://doi.org/10.1093/nar/gkaf361
The PyMOL Molecular Graphics System, Version 3.1, Schrödinger, LLC.
Rego, N., Koes, D. (2015). 3Dmol.js: molecular visualization with WebGL. Bioinformatics, 31(8), 1322-1324. https://doi.org/10.1093/bioinformatics/btu829
đ License
MIT © Marc C. Deller
đ To do
- ⏠Share pip cache between the two environments (
PIP_CACHE_DIR) sosetup-plipdoesnât re-download wheels the main venv already fetched - â
Pin exact dependency versions and add a cached/offline install mode for reproducible installs â via
pixi.toml/pixi.lockandinstall.sh(see Alternative: pixi above anddocs/tier_b_offline_install.md), not by pinningsetup/setup-plipâs own unpinnedrequirements.txt-driven installs, which remain as they were - ⏠Add
BoltzMaker.py doctorâ a post-install check that imports boltz/rdkit/plip/openbabel/pymol in-process and reports exactly which env/feature is broken - ⏠Add an explicit Boltz model-weights cache dir + a
preflightcheck for it (ties into the existing iCloud dataless-file eviction check) - ⏠Detect the MPS
torch.linalg.svdCPU-fallback atpreflightand warn with an estimated runtime multiplier for large multi-chain complexes - ⏠Add a residue/chain-count-based runtime pre-estimator, plus a toggle to log which ops fall back to CPU on MPS
- ⏠Checkpoint
runat the per-sample level (not just per-target) so an interrupted multi-hour complex resumes without recomputing completed diffusion samples - ⏠Add a cross-target selectivity/triage view to the dashboard (confidence-vs-affinity quadrant flags for âhigh-confidence, high-affinityâ hits)
- â Bundle Plotly.js locally instead of via CDN so the dashboard renders fully offline/air-gapped
- ⏠Add a smoke-test suite: an end-to-end fixture run in CI plus unit tests for the
boltz_input.mdparser and JSON-metric flattening - â Add a ligand-preparation/validation step (canonicalization, stereocentre/protonation-state flagging, disconnected-fragment detection) so bad input chemistry is caught before hours of compute, not silently mispredicted
- ⏠Add a retrospective
benchmarkmode: pull known actives/co-crystal data for a target family (ChEMBL/BindingDB/PDB) and report predicted-vs-measured pIC50 correlation + pose RMSD, so a user has a per-target-family trust score before committing to a real campaign - â Add a 3Dmol.js rotating structure view to each targetâs binding-site panel, placed next to the existing static PyMOL image (keep the static image, add a âDownload imageâ link for it â doesnât exist yet â and add the interactive 3Dmol.js view alongside rather than replacing anything)
đ€ Author
Marc C. Deller, D.Phil. Structural biologist & drug discovery scientist
| đ | marcdeller.com | âïž | marc@marcdeller.com | đ | github.com/bellcheddar/BoltzMaker |