Analysis code for reproducing the results presented in:
"Entrenchment of germline amino-acid differences in antibody affinity maturation" by Noam Harel, Kevin Sung, Will Dumm, Mackenzie M. Johnson, David Rich, Julia Fukuyama, Hugh K. Haddox, Frederick A. Matsen IV
bioRxiv preprint: 10.64898/2026.04.21.720000
Follow the instructions to install netam into a virtual environment, using commit bd208d0 (v0.2.4+3), the version used for the paper:
pip install git+https://github.com/matsengrp/netam.git@bd208d0pip install aaindex Levenshtein logomaker statsmodelsThe rate validation analysis requires shmex from the thrifty-experiments-1 repository:
git clone https://github.com/matsengrp/thrifty-experiments-1.git
cd thrifty-experiments-1
pip install -e .All data required to reproduce the analyses are available as a single archive on Zenodo. Download dasm-epistasis-data.tar from Zenodo and extract it:
tar xf dasm-epistasis-data.tarThis creates a dasm-epistasis-data/ directory containing:
| Directory | Contents |
|---|---|
v3/ |
PCP files (Jaffe, Tang, Rodriguez) and Chothia-numbered ANARCI outputs |
v1/ |
Out-of-frame PCP dataset (Tang-SHM, used by shmex) |
trained_models/ |
DASM (4M params) and DNSM (1M params) model weights and branch lengths |
dasm_test_output/ |
Cached DASM evaluation results on Rodriguez (see note below) |
dnsm_test_output/ |
Cached observed mutation datasets on Jaffe, Tang, and Rodriguez, computed via the DNSM framework for convenience (see note below) |
neutral_mutability_cache/ |
Pre-computed neutral mutability DataFrames for Jaffe, Tang, and Rodriguez (see note below) |
Note on cached outputs: The dasm_test_output/, dnsm_test_output/, and neutral_mutability_cache/ directories contain pre-computed intermediate results that are included for convenience. The analysis code will regenerate these automatically if they are missing, but recomputation is expensive. Only the PCP files, ANARCI outputs, and trained models are strictly required.
Note on the DASM model: The DASM model (dasm_4m-v1jaffeCC+v1tangCC-joint) is included in the archive. It was trained on the Jaffe and Tang heavy-chain datasets as described in Matsen et al. (2026), and is distinct from netam's pretrained paired-chain model DASMHumV1.0-4M.
cp dnsmex/local_config.py.template dnsmex/local_config.pyEdit dnsmex/local_config.py if needed. The default assumes the archive was extracted to ~/dasm-epistasis-data; update ZENODO_DATA_DIR if you extracted it elsewhere.
The rates_analysis_productive_non_productive.ipynb notebook uses the shmex package, which expects data at ~/data/v1/. Create a symlink from the extracted data:
mkdir -p ~/data
ln -s /path/to/dasm-epistasis-data/v1 ~/data/v1Run v_families_entrenchment_dasm.ipynb first — it produces Figs 2, 5, S6, S11, and writes entrenchment results to _output/entrenchment_analysis/ that are required by the other notebooks and scripts.
The remaining notebooks and scripts can then be run in any order:
shannon_entropy_entrenchment.ipynb— Shannon entropy at entrenched vs. non-entrenched sites (Figs 2C, S10)grantham_distance_analysis.ipynb— Physicochemical distance (Grantham) of entrenched substitutions (Fig 4)solvent_accessibility_analysis.ipynb— Relative solvent accessibility (RSA) and partner contact analysis at entrenched sites (Figs 3, 5C, S7, S8, S9, S12). Uses the committed SASA data.germline.ipynb— V-gene pairwise amino acid similarity (Fig S1)cdr3_dependence_of_selection.ipynb— Whether selection at the entrenched sites depends on CDR-H3 length, charge and hydrophobicity (Fig S16). Uses the committed SASA data.within_family_validation.ipynb— Per-V-gene-allele median selection factors at the within-family entrenched sites, checking whether pooling alleles within a family drives the entrenchment calls (Fig S2)rates_analysis_productive_non_productive.ipynb— Mutation rate validation using out-of-frame sequences as neutral baseline (Fig 7A)rates_analysis_productive_w_thrifty_multi.ipynb— Mutation rate validation using Thrifty-predicted neutral rates as baseline (Figs 7B-C, S14). Note: This notebook loads a large dataframe so run with enough memory.contact_distance_analysis.ipynb— Atomic contacts within 5 Å at entrenched sites, compared with the RSA burial analysis (Fig S17). Uses the committed SASA and contact data.entrenchment_threshold_sensitivity.py— Threshold sensitivity of the reciprocal-entrenchment calls (Figs S3–S5)create_combined_entrenched_sites.py— Writes_output/combined_entrenched_sites.csv(supplementary data file)create_combined_validation_table.py— Writes_output/combined_validation_table_entrenched_for_paper.csv(supplementary data file). Run after the tworates_analysis_*notebooks.
The SAbDab ANARCI annotations (data/sabdab/sabdab_anarci_annotations.tsv), SASA results (_output/sasa_human_chothia_anarci.csv.gz) and atomic contact results (_output/contacts_human_chothia.csv.gz) are committed to the repository and loaded automatically by the notebooks. You only need the steps below to regenerate them from the PDB structures.
annotate_sabdab_anarci.py assigns V/J germline genes to SAbDab structures using ANARCI. It reads the SAbDab bulk download TSV, extracts heavy and light chain sequences from PDB files using BioPython, and runs ANARCI with species-constrained germline assignment.
pip install anarci
python annotate_sabdab_anarci.py --output data/sabdab/sabdab_anarci_annotations.tsvrun_sasa_analysis.py computes per-residue solvent accessibility across six structural scenarios (full complex, antibody alone, heavy chain alone, etc.) and derives burial effects from each binding partner. It applies a 6-stage filtering pipeline: file discovery, organism filtering (exact match), chain completeness, heavy-chain-only antibody exclusion, V/J gene consistency, and optional file limits. run_contact_analysis.py computes per-residue atomic distances and contact counts on the same filtered structures.
You will need:
- SAbDab summary tables and ANARCI annotations: included in
data/sabdab/(2024-01-26 snapshot) - PDB structure files: download from RCSB PDB or via SAbDab's bulk download. Set
SABDAB_PDB_DIRindnsmex/local_config.pyto the root of your local copy (the directory containingpdb/opig-chothia). - DSSP: install via
conda install -c conda-forge dssp
python run_sasa_analysis.py --scheme opig-chothia --organism "homo sapiens" --output _output/sasa_human_chothia_anarci.csv
python run_contact_analysis.py --scheme opig-chothia --organism "homo sapiens" --output _output/contacts_human_chothia.csv.gzFig S15 requires two things beyond the setup above:
- partis (see its installation instructions;
partismust be on yourPATH) rodriguez-airr-seq-race-prod-NoWinCheck_igh_pcp_2024-11-12_MASKED_NI.csv.gz, the Rodriguez PCP file that includes naive-rooted pairs. It is available as a separate file in the same Zenodo record; place it indasm-epistasis-data/v3/.
From the repository root, run in order:
python germline-misassignment-simulation/simulate_mrca.py # simulate MRCAs from naive-to-MRCA PCPs with Thrifty+DASM
python germline-misassignment-simulation/prepare_partis_input.py # write FASTAs of the original and simulated MRCAs
bash germline-misassignment-simulation/run_partis_cache_parameters.sh original_mrcas # annotate with partis
bash germline-misassignment-simulation/run_partis_cache_parameters.sh simulated_mrcasThen run germline-misassignment-simulation/partis_misassignment_analysis.ipynb to produce the figure.
| Figure | Panel | Description | Source |
|---|---|---|---|
| Fig 1 | Entrenchment flow diagram | Manually created (entrenchment_flow_figure.pdf, SVG in tex repo) |
|
| Fig 2 | A,B | Reciprocal selection + entrenchment within IGHV1/3 | v_families_entrenchment_dasm.ipynb |
| Fig 2 | C | Shannon entropy at within-family entrenched sites | shannon_entropy_entrenchment.ipynb |
| Fig 3 | A–C | Structural images | Manually created (ChimeraX) |
| Fig 3 | D | RSA at within-family entrenched sites | solvent_accessibility_analysis.ipynb |
| Fig 4 | Grantham distance at entrenched sites | grantham_distance_analysis.ipynb |
|
| Fig 5 | A,B | Between-family entrenchment with within-family overlay | v_families_entrenchment_dasm.ipynb |
| Fig 5 | C | RSA at between-family entrenched sites | solvent_accessibility_analysis.ipynb |
| Fig 6 | Structural entrenchment at sites 9 and 73 | Manually created (ChimeraX) | |
| Fig 7 | A | Mutation rate validation using out-of-frame baseline | rates_analysis_productive_non_productive.ipynb |
| Fig 7 | B,C | Mutation rate validation using Thrifty baseline | rates_analysis_productive_w_thrifty_multi.ipynb |
| Figure | Description | Source |
|---|---|---|
| Fig S1 | V-gene pairwise similarity | germline.ipynb |
| Fig S2 | Per-V-gene-allele median selection factors at entrenched sites | within_family_validation.ipynb |
| Fig S3 | Threshold sensitivity within IGHV1 | entrenchment_threshold_sensitivity.py |
| Fig S4 | Threshold sensitivity within IGHV3 | entrenchment_threshold_sensitivity.py |
| Fig S5 | Threshold sensitivity IGHV1 vs IGHV3 | entrenchment_threshold_sensitivity.py |
| Fig S6 | Within-family entrenchment for IGHV4 | v_families_entrenchment_dasm.ipynb |
| Fig S7 | RSA at IGHV1 within-family entrenched sites | solvent_accessibility_analysis.ipynb |
| Fig S8 | RSA grid by site and amino acid | solvent_accessibility_analysis.ipynb |
| Fig S9 | RSA at between-family entrenched sites | solvent_accessibility_analysis.ipynb |
| Fig S10 | Shannon entropy (3-category entrenchment) | shannon_entropy_entrenchment.ipynb |
| Fig S11 | Between-family entrenchment for additional V-family pairs | v_families_entrenchment_dasm.ipynb |
| Fig S12 | Backbone angles at site 9 (A); density at sites 7–11 (B–C) | solvent_accessibility_analysis.ipynb (A); manually created in ChimeraX (B–C) |
| Fig S13 | Density at site 73 in IGHV1 and IGHV3 | Manually created (ChimeraX; see paper Methods) |
| Fig S14 | Pairwise validation for IGHV4 comparisons | rates_analysis_productive_w_thrifty_multi.ipynb |
| Fig S15 | Germline allele misassignment simulation | germline-misassignment-simulation/partis_misassignment_analysis.ipynb |
| Fig S16 | Dependence of selection on CDR-H3 length, and burial by the CDR-H3 against its length | cdr3_dependence_of_selection.ipynb |
| Fig S17 | Atomic contacts within 5 Å at IGHV3 within-family entrenched sites | contact_distance_analysis.ipynb |
| Table S1 | Hydrogen bonding at sites 73–75 | ChimeraX hbonds (interactive); command and per-structure output in site_73_structural/hbond_raw_data.md |
| File | Description |
|---|---|
_output/combined_entrenched_sites.csv |
Combined list of all entrenched amino acid substitutions identified from both within-family and between-family comparisons. Each row specifies the V family, site, parent and target amino acid, the source comparison, and whether it is a within- or between-family result. Generated by create_combined_entrenched_sites.py. |
_output/combined_validation_table_entrenched_for_paper.csv |
Summary of all entrenched sites with DASM selection factors and mutation rate ratio validation. Each row represents one entrenched substitution (parent amino acid → child amino acid) at a given site and V family. Columns include: DASM median log selection factor; out-of-frame validation columns (prefix oof_) with pseudocount-adjusted mutation counts, total branch lengths, and log rate ratios; Thrifty validation columns (prefix thrifty_) with pseudocount-adjusted counts and log ratios. Generated by create_combined_validation_table.py. See the paper methods for details. |
| File | Role |
|---|---|
utils.py |
Core utilities imported by most notebooks |
rates_analysis_util.py |
Rate analysis utilities for validation notebooks |
cdr3_stratification_util.py |
CDR-H3 descriptors and CDR-H3 contact frequencies for the CDR-H3 dependence notebook |
sasa_plotting.py |
RSA/SASA plotting functions |
annotate_sabdab_anarci.py |
V/J gene annotation of SAbDab structures using ANARCI |
run_sasa_analysis.py |
SASA data generation from PDB structures |
run_contact_analysis.py |
Atomic distance and contact data generation from PDB structures |
create_germline_codon_tables.py |
Generates germline reference data |
site9_discrepancy_analysis.ipynb |
Investigation of site 9 validation discrepancy |
dnsmex/ |
Core library for DNSM/DASM analysis |
germline/ |
Germline reference data (OGRDB-derived); to regenerate, run create_germline_codon_tables.py (requires ANARCI) |
branch-length-regression/ |
Linear relationship between DASM branch lengths and mutation frequency, used in the Thrifty validation |
Notebooks not listed above, and the old/ directory, contain exploratory or superseded analyses that are not used in the paper.