From 89a6b6fb574e508220e8b11d433dccabeb9a2dd9 Mon Sep 17 00:00:00 2001 From: thomas bush Date: Wed, 3 Jun 2026 11:17:33 -0400 Subject: [PATCH 1/5] added singularity container support --- containers/switchcraft.def | 75 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 containers/switchcraft.def diff --git a/containers/switchcraft.def b/containers/switchcraft.def new file mode 100644 index 0000000..586f7d0 --- /dev/null +++ b/containers/switchcraft.def @@ -0,0 +1,75 @@ +Bootstrap: docker +From: python:3.12-slim + + +# Singularity Image for SwitchCraft +# +%files +# Sections where you can specify which files from host you want to copy in the Image. +# For bigger files (e.g., models weights) you can: +# --bind host_path:image_path + . /opt/switchcraft + +%post + export DEBIAN_FRONTEND=noninteractive + apt-get update && apt-get install -y --no-install-recommends \ + git build-essential \ + && rm -rf /var/lib/apt/lists/* + + pip install --no-cache-dir --upgrade pip + pip install --no-cache-dir torch torchvision \ + --index-url https://download.pytorch.org/whl/cu128 + pip install -e /opt/switchcraft/boltz + pip install prody + + + +%environment +# It defines environment variables that will be set at runtime -> not available at build time +# if you need envs available at build time define them in the 'post' section + + +%runscript + cd /opt/switchcraft || exit 1 + exec python switchcraft.py "$@" + +%test +# Runs at end of build. Boltz weights (ccd.pkl, boltz1_conf.ckpt) are bind-mounted at runtime. + echo "Testing PyTorch and CUDA" + python -c "import torch; print('torch', torch.__version__); print('cuda available', torch.cuda.is_available())" + + echo "Testing boltz import" + python -c "import boltz; print('boltz ok')" + + echo "Testing switchcraft imports (weights optional at build)" + cd /opt/switchcraft + python << 'EOF' +import os +import yaml +from boltz.model.model import Boltz1 +from boltz.main import BoltzDiffusionParams +from utils import motif_utils, mydesign_utils +from losses import ( + MotifLoss, AntiMotifLoss, ContactLoss, HelixBiasLoss, + ConfChangeLoss, LigandContactLoss, AntiLigandContactLoss, + SheetBiasLoss, SequenceSimilarityLoss, RadiusOfGyrationLoss, +) + +ccd = "/opt/switchcraft/boltz/ccd.pkl" +if os.path.isfile(ccd): + from designer import MultistateDesigner + print("designer ok (ccd.pkl present)") +else: + print("SKIP designer: bind boltz/ (ccd.pkl) at runtime per README") +print("switchcraft import checks passed") +EOF + +%labels + + + + +%help + SwitchCraft container. Bind Boltz weights at runtime: + -B $PWD/boltz:/opt/switchcraft/boltz + Run with GPU: singularity exec --nv switchcraft.sif python switchcraft.py --config tasks/... From 1c8af6b51ece50cb02db157f5d05337bec38be51 Mon Sep 17 00:00:00 2001 From: thomas bush Date: Wed, 3 Jun 2026 11:36:22 -0400 Subject: [PATCH 2/5] added cuequivariance to %post --- containers/switchcraft.def | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/containers/switchcraft.def b/containers/switchcraft.def index 586f7d0..c47811f 100644 --- a/containers/switchcraft.def +++ b/containers/switchcraft.def @@ -19,14 +19,15 @@ From: python:3.12-slim pip install --no-cache-dir --upgrade pip pip install --no-cache-dir torch torchvision \ --index-url https://download.pytorch.org/whl/cu128 + # Boltz imports cuequivariance_torch at module load (not in boltz pyproject deps) + pip install --no-cache-dir cuequivariance-torch cuequivariance-ops-torch-cu12 pip install -e /opt/switchcraft/boltz pip install prody %environment -# It defines environment variables that will be set at runtime -> not available at build time -# if you need envs available at build time define them in the 'post' section + export BOLTZ_USE_CUEQ=1 %runscript @@ -38,6 +39,9 @@ From: python:3.12-slim echo "Testing PyTorch and CUDA" python -c "import torch; print('torch', torch.__version__); print('cuda available', torch.cuda.is_available())" + echo "Testing cuequivariance_torch" + python -c "import cuequivariance_torch; print('cuequivariance_torch ok')" + echo "Testing boltz import" python -c "import boltz; print('boltz ok')" From e804547c35d527f2b87b3aeeeab37634ed178431 Mon Sep 17 00:00:00 2001 From: thomas bush Date: Wed, 3 Jun 2026 11:51:20 -0400 Subject: [PATCH 3/5] singularity contaier with cache for boltz + LigandMPNN --- containers/switchcraft.def | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/containers/switchcraft.def b/containers/switchcraft.def index c47811f..2f52a80 100644 --- a/containers/switchcraft.def +++ b/containers/switchcraft.def @@ -13,7 +13,7 @@ From: python:3.12-slim %post export DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -y --no-install-recommends \ - git build-essential \ + git build-essential wget ca-certificates \ && rm -rf /var/lib/apt/lists/* pip install --no-cache-dir --upgrade pip @@ -24,7 +24,16 @@ From: python:3.12-slim pip install -e /opt/switchcraft/boltz pip install prody + python << 'PY' +from boltz.main import download +from pathlib import Path +cache = Path("/opt/switchcraft/boltz") +cache.mkdir(parents=True, exist_ok=True) +download(cache) +PY + cd /opt/switchcraft/LigandMPNN && bash get_model_params.sh ./model_params + echo "SwitchCraft container built successfully" %environment export BOLTZ_USE_CUEQ=1 @@ -35,7 +44,7 @@ From: python:3.12-slim exec python switchcraft.py "$@" %test -# Runs at end of build. Boltz weights (ccd.pkl, boltz1_conf.ckpt) are bind-mounted at runtime. +# Runs at end of build. Boltz/LigandMPNN weights are baked in under %post. echo "Testing PyTorch and CUDA" python -c "import torch; print('torch', torch.__version__); print('cuda available', torch.cuda.is_available())" @@ -64,7 +73,7 @@ if os.path.isfile(ccd): from designer import MultistateDesigner print("designer ok (ccd.pkl present)") else: - print("SKIP designer: bind boltz/ (ccd.pkl) at runtime per README") + print("SKIP designer: boltz/ccd.pkl missing (download step failed?)") print("switchcraft import checks passed") EOF @@ -74,6 +83,5 @@ EOF %help - SwitchCraft container. Bind Boltz weights at runtime: - -B $PWD/boltz:/opt/switchcraft/boltz - Run with GPU: singularity exec --nv switchcraft.sif python switchcraft.py --config tasks/... + SwitchCraft with Boltz + LigandMPNN weights pre-downloaded. + Run: singularity exec --nv switchcraft.sif --config tasks/pos_allostery.yaml From 173b79cce2d824334368d3e24034593e1c5c9edf Mon Sep 17 00:00:00 2001 From: thomas bush Date: Wed, 3 Jun 2026 12:16:11 -0400 Subject: [PATCH 4/5] slurm script for switchcraft --- slurm_scripts/launch_switchcraft.slurm | 46 ++++++++++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 slurm_scripts/launch_switchcraft.slurm diff --git a/slurm_scripts/launch_switchcraft.slurm b/slurm_scripts/launch_switchcraft.slurm new file mode 100644 index 0000000..158f9a6 --- /dev/null +++ b/slurm_scripts/launch_switchcraft.slurm @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +#SBATCH --nodes= +#SBATCH --ntasks-per-node= +#SBATCH --cpus-per-task= +#SBATCH --gpus-per-node= +#SBATCH --mem= +# Partition/account set by caller via sbatch flags (from config) +#SBATCH --partition= +#SBATCH --account= +#SBATCH --time=01:00:00 +# Log dir from env (caller passes -o) +#SBATCH --output=%x.%A_%a.out + +# Minimal example (repo + Boltz weights baked in SIF; only output dir on host): +# mkdir -p /scratch/${USER}/switchcraft_out +# sbatch launch_switchcraft.slurm \ +# tasks/pos_allostery.yaml \ +# /path/to/switchcraft.sif \ +# /scratch/${USER}/switchcraft_out +# +# Args: +# CONFIG_FILE — YAML under /opt/switchcraft (e.g. tasks/pos_allostery.yaml) +# SWITCHCRAFT_IMAGE — path to .sif +# OUTPUT_DIR — host scratch dir (bind-mounted for writes) + +set -euo pipefail + +CONFIG_FILE="${1}" +SWITCHCRAFT_IMAGE="${2}" +OUTPUT_DIR="${3}" + +mkdir -p "${OUTPUT_DIR}" + +echo "Running ${SWITCHCRAFT_IMAGE}" +echo " config: ${CONFIG_FILE}" +echo " output: ${OUTPUT_DIR}" + +singularity exec --nv --pwd /opt/switchcraft \ + -B "${OUTPUT_DIR}:${OUTPUT_DIR}" \ + "${SWITCHCRAFT_IMAGE}" \ + python switchcraft.py \ + --config "${CONFIG_FILE}" \ + --outpath "${OUTPUT_DIR}" \ + --verbose + +echo "Run completed." From 3d8c7caa46ca4ec535bad053ccee4c97fa387011 Mon Sep 17 00:00:00 2001 From: thomas bush Date: Wed, 3 Jun 2026 12:30:57 -0400 Subject: [PATCH 5/5] info for singularity +slurm --- README.md | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d1bbed0..95eec4e 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,71 @@ cd LigandMPNN && bash get_model_params.sh ./model_params && cd .. --- +## Singularity / Apptainer container + +For GPU clusters, a pre-built environment is provided under `containers/switchcraft.def`. The image installs SwitchCraft, Boltz, cuEquivariance, **Boltz weights** (`boltz/boltz1_conf.ckpt`, `boltz/ccd.pkl`), and **LigandMPNN weights** at build time, so routine runs do not need extra weight bind-mounts. + +### Build the image + +From the repository root (Linux; build requires network access): + +```bash +apptainer build switchcraft.sif containers/switchcraft.def +# or: singularity build switchcraft.sif containers/switchcraft.def +``` + +Build-time `%test` may print `libcuda.so.1` warnings and `cuda available False` without a GPU; that is expected. Use `--nv` at **runtime** on a GPU node. + +### Minimal interactive run + +On a GPU node, only the **output directory** on the host needs to be bound. Use config paths relative to `/opt/switchcraft` (same as the Quick Start examples under `tasks/`): + +```bash +mkdir -p /scratch/${USER}/switchcraft_out + +apptainer exec --nv --pwd /opt/switchcraft \ + -B /scratch/${USER}/switchcraft_out:/scratch/${USER}/switchcraft_out \ + switchcraft.sif \ + python switchcraft.py \ + --config tasks/pos_allostery.yaml \ + --outpath /scratch/${USER}/switchcraft_out \ + --verbose +``` + +Built-in configs (`tasks/pos_allostery.yaml`, etc.) and `motifs/` are already in the image; you do not need to copy them unless you are customizing a design. + +**Custom YAML on the host:** bind the file into the image and pass its path under `/opt/switchcraft`, e.g. `-B /scratch/${USER}/my_design.yaml:/opt/switchcraft/my_design.yaml` and `--config my_design.yaml` (still use `--pwd /opt/switchcraft`). + +### SLURM + +`slurm_scripts/launch_switchcraft.slurm` wraps the same invocation. Replace the `#SBATCH` placeholders (``, ``, etc.) for your site, then submit: + +```bash +mkdir -p /scratch/${USER}/switchcraft_out + +sbatch slurm_scripts/launch_switchcraft.slurm \ + tasks/pos_allostery.yaml \ + /path/to/switchcraft.sif \ + /scratch/${USER}/switchcraft_out +``` + +| Argument | Meaning | +|----------|---------| +| `CONFIG_FILE` | YAML path inside the image (e.g. `tasks/pos_allostery.yaml`) | +| `SWITCHCRAFT_IMAGE` | Path to `switchcraft.sif` | +| `OUTPUT_DIR` | Host directory for results (bind-mounted) | + +**Interactive test** (e.g. inside `salloc` on a GPU node, no `sbatch`): + +```bash +bash slurm_scripts/launch_switchcraft.slurm \ + tasks/pos_allostery.yaml \ + /path/to/switchcraft.sif \ + /scratch/${USER}/switchcraft_out +``` + +--- + ## Quick Start Each design run is driven by a YAML config file that fully specifies the design problem. Template configs for example tasks live in `tasks/`. @@ -74,7 +139,7 @@ Command line arguments that can be provided include - `--recycles`: Boltz recycling steps during optimization - `--ligandmpnn_seqs`: if >0, run LigandMPNN redesign producing N sequences per design - `--verbose`: whether to use progress bars (recommended) -- `--out`: output directory +- `-o` / `--outpath`: output directory (README shorthand: output dir) - `--num_designs`: total number of designs ---