Tutorial

Designing a cyclic binder for PD-L1.

A complete walkthrough from input PDB to Fmoc SPPS in nine steps. Expect ~250 trajectories → 50 accepted cyclic designs in ~4 hours on a single A100, then 2–3 candidates ready for synthesis.

Prerequisite

Complete the Installation Guide first. Activate the bindcraft environment: conda activate bindcraft.

The target: PD-L1

PD-L1 (Programmed Death-Ligand 1, also called B7-H1 or CD274) is the binding partner of PD-1 on T cells. Antibodies that block the PD-1/PD-L1 interaction — pembrolizumab, nivolumab, atezolizumab — have transformed oncology. A small cyclic peptide that achieves the same blockade could offer oral bioavailability and lower immunogenicity than a full antibody.

We use the X-ray structure PDB 5JDS — the extracellular IgV domain of human PD-L1, chain A — as our target. The antibody-binding epitope we mimic is around residues 48, 53, 66 (the loop-helix-loop that contacts PD-1).

Pipeline

Pipeline diagram

flowchart TD
    PDB[Input: 5JDS.pdb chain A] --> CLEAN[Step 1: Clean + pick hotspots]
    CLEAN --> PRE[Step 2: peptide_cyclic_3stage.json preset]
    PRE --> FILT[Step 3: peptide_cyclic_filters.json preset]
    FILT --> RUN[Step 4: bindcraft.py --cyclic]
    RUN --> MON[Step 5: tail logs + watch files]
    MON --> CSV[Step 6: final_design_stats.csv]
    CSV --> PICK[Step 7: top 5 by i_pTM + cyclic_NC_distance]
    PICK --> VAL[Step 8: AfCycDesign / RF2-cyclic]
    VAL --> SYNTH[Step 9: Fmoc SPPS + cyclize]
01

Prepare the target PDB

Download 5JDS, extract chain A, strip waters and ligands:

prepare_target.sh
mkdir -p ~/pdl1_cyclic && cd ~/pdl1_cyclic
wget -q https://files.rcsb.org/download/5JDS.pdb

# Keep only chain A, ATOM records; renumber from 1
python - <<'PY'
from Bio.PDB import PDBParser, PDBIO, Select
class ChainA(Select):
    def accept_chain(self, chain): return chain.id == 'A'
    def accept_model(self, model): return model.id == 0
    def accept_residue(self, r):
        return r.id[0] == ' ' and r.resname in {
            'ALA','ARG','ASN','ASP','CYS','GLN','GLU','GLY','HIS','ILE',
            'LEU','LYS','MET','PHE','PRO','SER','THR','TRP','TYR','VAL'}
io = PDBIO(); io.set_structure(PDBParser(QUIET=True).get_structure('p','5JDS.pdb'))
io.save('PDL1_clean.pdb', ChainA())
PY

Inspect the cleaned structure in PyMOL or py3dmol. The PD-1-binding epitope on PD-L1 spans the C-C' loop and the C strand. Choose three hotspots that flank the binding site:

  • A48 — Tyr at the C-C' loop tip
  • A53 — Asp in the C' strand (PD-1 contact)
  • A66 — Tyr at the start of the C'' strand

These residues receive extra AF2 attention via ColabDesign's hotspot mask. The binder is not required to contact all three, but most passing designs will.

02

Choose the design preset

Cyclic BindCraft ships a new preset at settings_advanced/peptide_cyclic_3stage.json. It is the closest analog to stock BindCraft's peptide_3stage_multimer.json with cyclic-specific tweaks. The most important keys:

KeyDefaultMeaning
cyclic_bindertrueEnables the cyclic patch (relpos wrap, bonded edge, bond loss, constrained relax)
cyclic_chains"B"The binder is chain B; this is the only chain to cyclize
lengths[25, 55]Binder length range. 25 = floor for stable rings; 55 = ceiling for synthesis cost
design_algorithm"3stage"design_logits → design_soft → design_pssm_semigreedy
predict_initial_guesstrueInitialize AF2 from target template; essential for short binders
weights_helicity0.5Promotes α-helical content (reduced from 0.95 — cyclic peptides can be helix or sheet)
weights_cyclic_bond_loss0.5Harmonic penalty on N(1)-C(L) bond geometry
cyclic_bond_target_NC1.33Target N–C amide bond length (Å)
cyclic_bond_target_CACA3.8Target Cα–Cα distance across amide (Å)
cyclic_bond_target_omega180.0Target ω dihedral (180° = trans)
num_seqs20MPNN samples per trajectory
omit_AAs"C"Exclude Cys from MPNN sampling (head-to-tail only, no disulfides)
acceptance_rate0.05Lower than stock 0.10 (cyclic designs are harder)

Tip

You usually do not need to edit this file. If your target is large (>300 residues) lower inter_contact_distance to 16. If you want β-hairpin binders, set weights_helicity to -0.5.

03

Configure the cyclic filter preset

The filter preset at settings_filters/peptide_cyclic_filters.json extends stock peptide_filters.json with cyclic-specific columns:

FilterThresholdDirectionMeaning
Average_pLDDT0.75Mean pLDDT (relaxed from 0.80 — AF2 monomer is OOD for cyclic)
Average_i_pTM0.40Interface pTM
Average_i_pAE0.35Interface pAE (Å-equivalent)
Average_dG−10.0Rosetta InterfaceAnalyzer ΔG (kcal/mol)
Average_Binder_RMSD3.5RMSD trajectory vs. AF2 re-prediction (Å; relaxed from 2.5)
Average_Hotspot_RMSD4.0RMSD of target hotspot Cα (Å; relaxed from 3.0)
Average_Cyclic_NC_Distance5.0N(1)–C(L) atom distance (Å) — closure gate
Average_Cyclic_CACA_Distance6.0Cα(1)–Cα(L) distance — ring compactness
Average_Cyclic_Omega150.0|ω| dihedral — trans amide check (≥150° = trans)

The cyclic columns are computed by compute_cyclic_closure_metrics() in functions/cyclic_utils.py and appended to every design's metric dict before the JSON filter engine runs.

04

Launch the design run

Create a target-settings JSON specific to PD-L1 (settings_target/PDL1_cyclic.json):

settings_target/PDL1_cyclic.json
{
  "design_path": "/home/USER/pdl1_cyclic/",
  "binder_name": "PDL1cyc",
  "starting_pdb": "/home/USER/pdl1_cyclic/PDL1_clean.pdb",
  "chains": "A",
  "target_hotspot_residues": "A48,A53,A66",
  "lengths": [25, 45],
  "number_of_final_designs": 50
}

Note lengths: [25, 45] — we cap at 45 because PD-L1's epitope is small; a 25–45 aa cyclic binder covers it efficiently. Launch:

launch.sh
cd ~/cyclic-bindcraft/repo
python -u bindcraft.py \
  --cyclic \
  --target   /home/USER/pdl1_cyclic/PDL1_clean.pdb \
  --hotspots "A48,A53,A66" \
  --settings settings_target/PDL1_cyclic.json \
  --filters  settings_filters/peptide_cyclic_filters.json \
  --advanced settings_advanced/peptide_cyclic_3stage.json \
  --output   /home/USER/pdl1_cyclic/

Stock BindCraft takes --settings / --filters / --advanced only; the cyclic patch adds the optional --cyclic, --target, --hotspots, and --output shortcuts that override the JSON.

05

Monitor progress

In a separate terminal:

monitor.sh
cd /home/USER/pdl1_cyclic/
watch -n 30 'ls -1 Trajectory/ | wc -l; ls -1 Accepted/ | wc -l; tail -n 5 failure_csv.csv'
tail -F /home/USER/pdl1_cyclic/*.log 2>/dev/null

What appears, in order:

  1. Trajectory/<binder>_l<len>_s<seed>.pdb — raw AF2 hallucination (chain A target, chain B binder, no closure yet)
  2. Trajectory/Relaxed/<...>.pdb — PyRosetta-relaxed trajectory (cyclic bond declared; CONECT record present)
  3. Trajectory/LowConfidence/<...>.pdb — terminated early (pLDDT < 0.7 or <3 hotspot contacts)
  4. Trajectory/Clashing/<...>.pdb — Cα clash detected
  5. MPNN/Binder/<...>_model1.pdb, _model2.pdb — AF2 re-prediction per MPNN sequence
  6. MPNN/Relaxed/<...>_model1.pdb — Rosetta-relaxed complex
  7. Accepted/<...>_model<N>.pdb — design that passes all filters
  8. Rejected/<...>_model<N>.pdb — design that failed filters
  9. trajectory_stats.csv, mpnn_design_stats.csv, final_design_stats.csv, failure_csv.csv

Each trajectory takes ~3 minutes on an A100. With acceptance_rate: 0.1 and start_monitoring: 1000 you should see the first accepted design around trajectory #50 (≈2.5 hours). To reach 50 accepted designs, expect ~4 hours total.

06

Interpret the output CSV

Open final_design_stats.csv. The most important columns:

ColumnTypeMeaning
Designstre.g. PDL1cyc_l33_s482910_mpnn2 — name, length, seed, MPNN idx
SequencestrThe binder sequence (cyclic; residue L is bonded to residue 1)
LengthintBinder length in residues
Average_pLDDTfloatMean AF2 pLDDT; >0.80 is well-folded
Average_i_pTMfloatInterface pTM; >0.45 means AF2 is confident the binder docks
Average_i_pAEfloatInterface predicted aligned error; <0.30 is excellent
Average_dGfloatRosetta InterfaceAnalyzer ΔG (kcal/mol)
Average_SAPfloatSurface aggregation propensity; <35
Average_Binder_RMSDfloatRMSD trajectory vs. AF2-re-predicted binder alone (Å)
cyclic_NC_distancefloatN(1)–C(L) atom distance — must be ≤ 1.5 for true closure
cyclic_omegafloatω dihedral — must be within 180 ± 30° for trans amide
cyclic_CACA_distancefloatCα(1)–Cα(L) distance — should be 3.5–4.5 for clean trans amide

The cyclic_* columns are the ones that distinguish this from a stock BindCraft CSV.

07

Pick the top 5 designs

Sort by Average_i_pTM descending, then re-rank by cyclic_NC_distance ascending. Apply these selection criteria:

  1. Top quartile by Average_i_pTM.
  2. Cyclic closure: cyclic_NC_distance < 1.4 Å and cyclic_omega ∈ [170°, 190°].
  3. AF2 agreement: Average_Binder_RMSD < 1.5 Å.
  4. Rosetta quality: Average_dG < -25 kcal/mol and Average_SAP < 30.
  5. Interface size: Average_n_InterfaceResidues >= 6.
  6. Diversity: Pick at most one design per binder-length bucket (e.g. one 30-mer, one 35-mer, etc.) to maximize sequence diversity.
pick_top5.py
import pandas as pd
df = pd.read_csv('/home/USER/pdl1_cyclic/final_design_stats.csv')
df = df[(df.cyclic_NC_distance < 1.4) &
        (df.cyclic_omega.between(170, 190)) &
        (df.Average_Binder_RMSD < 1.5) &
        (df.Average_dG < -25) &
        (df.Average_SAP < 30)]
df = df.sort_values(['Average_i_pTM','cyclic_NC_distance'],
                    ascending=[False, True])
# keep at most one per length bucket
top = df.groupby('Length').head(1).head(5)
top.to_csv('top5.csv', index=False)
print(top[['Design','Length','Average_i_pTM','Average_dG',
           'cyclic_NC_distance','cyclic_omega']])

A realistic output after a 4-hour run on 1×A100:

DesignLengthi_pTMdGNCω
PDL1cyc_l31_s318404_mpnn1310.71−34.21.34179.4
PDL1cyc_l33_s482910_mpnn2330.69−31.81.35181.1
PDL1cyc_l38_s71925_mpnn1380.68−36.91.33178.6
PDL1cyc_l29_s192847_mpnn3290.66−28.41.36182.3
PDL1cyc_l42_s602381_mpnn1420.65−39.71.34177.9

These are your top 5. Five is the right number for a first synthesis round.

08

Optional: cross-validate with AfCycDesign / RF2-cyclic

AF2-with-cyclic-relpos is an approximation of a true cyclic-prediction network. For designs you intend to synthesize, run an independent cyclic-aware predictor:

AfCycDesign

afcycdesign.sh
# Install once
pip install git+https://github.com/bhardwajlab/AfCycDesign.git

# Re-predict each top-5 design's binder+target complex
for design in top5_*.pdb; do
  python -m afcycdesign.predict \
    --target PDL1_clean.pdb \
    --binder $design \
    --cyclic_chain B \
    --output ${design%.pdb}_afcyc.pdb
done

Acceptance criteria: AfCycDesign iPAE < 0.3 (PD-L1 is moderate-difficulty; for harder targets use <0.4) and RMSD < 1.5 Å between the BindCraft design and the AfCycDesign prediction.

RF2-cyclic (orthogonal)

If both AfCycDesign and RF2-cyclic agree, you have high confidence. Disagreement is a strong signal to drop the design. Typically 3 of 5 survive this cross-validation.

09

Synthesize and test

Send the surviving 2–3 designs to a peptide-synthesis core. The chemistry is standard:

  1. Fmoc SPPS of the linear protected peptide on 2-chlorotrityl chloride resin (so the C-terminus is cleaved as a free acid under mild acid).
  2. Cleavage from resin with 1% TFA in DCM (preserves side-chain protecting groups).
  3. Head-to-tail cyclization in dilute solution (1 mM peptide in DMF) with PyAOP / DIEA (1.5 eq) for 2–4 h at RT — this forms the N→C amide bond.
  4. Global deprotection with TFA/TIS/water (95/2.5/2.5) for 2 h.
  5. Purification by reverse-phase HPLC; confirm by MALDI-TOF (mass should equal sum(residue_masses) − 18.02 for the lost water in the new amide bond).
  6. Affinity by SPR against immobilized PD-L1 (target: K_D < 1 µM for first round; <100 nM after one round of affinity maturation).
  7. Stability in human serum at 37°C (cyclic peptides should retain >80% after 24 h, vs. minutes for linear controls).

Expect ~1 in 3 designs to show measurable binding in the first round. Cyclic peptides that pass the design filters and the AfCycDesign cross-validation typically have 1–10 µM K_D; one round of point-mutation affinity maturation gets them to <100 nM.

Success criteria

What "success" looks like

For a 4-hour, 1×A100 PD-L1 run with lengths: [25, 45] and number_of_final_designs: 50:

  • ~250 trajectories started
  • ~120 reach AF2 validation (the rest terminate in LowConfidence or Clashing)
  • ~50–80 MPNN sequences pass the AF2 per-model filters
  • ~50 designs accepted into Accepted/ (the loop stops at number_of_final_designs)
  • Top 5 by i_pTM all have cyclic_NC_distance < 1.4 Å, cyclic_omega ∈ [170°, 190°], Average_dG < -30 kcal/mol
  • After AfCycDesign cross-validation, 2–3 survive for synthesis

This is consistent with the success rates reported in the RFpeptide paper for similar targets.