Classifiers
Classifiers assign van der Waals radii and polarity classes to atoms based on residue and atom names. The choice of classifier directly affects SASA values, so understanding the differences is important.
Quick Recommendation#
- CCD (default for
calc/batch): ProtOr-compatible hybridization-based radii (Tsai et al. 1999), extended with CCD bond topology analysis for non-standard residues. Uses united-atom radii (implicit H). Recommended for crystal structures. - ProtOr: Static ProtOr-compatible radii only. Use this for protein-only structures such as AFDB models when runtime CCD bond-topology analysis is unnecessary.
- NACCESS (default for
traj): Use for MD trajectories with explicit hydrogens, or when reproducing NACCESS-based studies. - OONS: Larger aliphatic carbon radii (2.00 Å). Use when reproducing or comparing with OONS-based studies.
How Classifiers Work#
- Residue-specific match: Exact match of (residue name, atom name) pair
- ANY fallback: Generic atom definitions shared across residues (NACCESS and OONS only)
- Element estimation: Generic radius of the atom's element if no match found. The element comes from the input; see Handling Unknown Atoms
Classifier Comparison#
Radii Reference Table#
All classifiers use united-atom radii (implicit hydrogens) — heavy atom radii already account for the volume of attached hydrogen atoms. The CCD column differentiates radii by hybridization state and implicit hydrogen count, following Tsai et al. 1999.
Explicit hydrogens and united-atom radii
CCD and ProtOr use united-atom radii where heavy atom radii already include the contribution of implicit hydrogens (e.g., C sp3 with 3 implicit H = 1.88 Å). Using --include-hydrogens with CCD/ProtOr causes double-counting and inaccurate SASA values. A warning is printed if this combination is detected.
For MD trajectories with explicit hydrogens, use zsasa traj which defaults to NACCESS. NACCESS has no hydrogen entries, so every explicit H atom gets the element-based fallback radius (H = 1.10 Å), whatever its name (H, HA, HG21, ...).
| Element | Context | ProtOr / CCD | NACCESS | OONS | Polarity |
|---|---|---|---|---|---|
| Carbon | sp2, 0 H — carbonyl (backbone C, ASN CG, etc.) | 1.61 | 1.76 | 1.55 | apolar / apolar / polar (OONS) |
| sp2, 0 H — aromatic without H (PHE CG, TRP CE2, etc.) | 1.61 | 1.76 | 1.75 | apolar (all) | |
| sp2, 1+ H — aromatic CH (PHE CD1, TYR CE1, etc.) | 1.76 | 1.76 | 1.75 | apolar (all) | |
| sp3, 1–3 H — aliphatic (CA, CB, CG, CD, etc.) | 1.88 | 1.87 | 2.00 | apolar (all) | |
| Nitrogen | all (amide, amino, imino, etc.) | 1.64 | 1.50–1.65 | 1.55 | polar (all) |
| Oxygen | sp2, 0 H — carbonyl (C=O) | 1.42 | 1.40 | 1.40 | polar (all) |
| sp3, 0–1 H — hydroxyl, ether | 1.46 | 1.40 | 1.40 | polar (all) | |
| sp3, 2 H — water | 1.46 | 1.40 | 1.40 | polar (all) | |
| Sulfur | sp3 — thiol, thioether | 1.77 | 1.85 | 2.00 | polar / apolar (NACCESS) / polar |
| Selenium | sp3 — MSE, SEC | 1.90 | 1.80 | 1.90 | polar / apolar (NACCESS) / polar |
| Phosphorus | sp3 — nucleic acid backbone | 1.80 | 1.90 | 1.80 | polar / apolar (NACCESS) / polar |
Sulfur, Selenium, and Phosphorus polarity
NACCESS treats S, Se, and P as apolar, while ProtOr, OONS, and CCD treat them as polar. This affects polar/nonpolar SASA partitioning for CYS, MET, MSE, SEC residues and nucleic acid backbones. In the CLI, the classes of the active classifier decide the Non-polar and All polar columns of --format=rsa and the atom-class block of --polar; see Output & Analysis.
Key Differences#
| Property | CCD | ProtOr | NACCESS | OONS |
|---|---|---|---|---|
| Basis | Hybridization state + CCD topology | Hybridization state | Atom type | Atom type |
| ANY fallback | No | No | Yes | Yes |
| S, Se, P polarity | Polar | Polar | Apolar | Polar |
| Carbonyl C polarity | Apolar | Apolar | Apolar | Polar |
| Non-standard residues | CCD bond topology when resources are available | Element fallback | Element fallback | Element fallback |
| Reference | Tsai et al. 1999 + wwPDB CCD | Tsai et al. 1999 | Hubbard & Thornton 1993 | Ooi et al. 1987 |
ProtOr skips CCD resource parsing
--classifier=protor uses the static ProtOr-compatible radii table and does not load inline, external, or SDF-derived CCD resources. Prefer it for protein-only mmCIF/PDB inputs where CCD topology analysis is unnecessary.
Usage#
# CCD (default for PDB/mmCIF)
zsasa calc --classifier=ccd structure.cif output.json
# NACCESS
zsasa calc --classifier=naccess structure.cif output.json
# OONS
zsasa calc --classifier=oons structure.cif output.json
# CCD
zsasa calc --classifier=ccd structure.cif output.json
# CCD including ligands, ions and waters (HETATM records)
zsasa calc --classifier=ccd --include-hetatm structure.cif output.json
# CCD with external dictionary (for non-standard residues)
zsasa calc --classifier=ccd --ccd=components.zsdc structure.cif output.json
# Custom config file
zsasa calc --config=my_radii.toml structure.cif output.jsonfrom zsasa import classify_atoms, calculate_sasa, ClassifierType
# Step 1: Classify atoms to get radii (CCD is the default)
classification = classify_atoms(residue_names, atom_names, ClassifierType.CCD)
# Step 2: Calculate SASA using classified radii
result = calculate_sasa(coords, classification.radii)
# CCD classifier
classification = classify_atoms(residue_names, atom_names, ClassifierType.CCD)
result = calculate_sasa(coords, classification.radii)from zsasa import ClassifierType
from zsasa.integrations.gemmi import calculate_sasa_from_structure
# Classifier is applied automatically
result = calculate_sasa_from_structure(
"protein.cif",
classifier=ClassifierType.CCD,
)
# CCD classifier
result = calculate_sasa_from_structure(
"protein.cif",
classifier=ClassifierType.CCD,
)Custom Classifier#
Custom classifiers are an advanced feature for studies that require a specific radius table. For most structures, keep the default CCD classifier because it covers standard residues and uses CCD bond topology for ligands, cofactors, and modified residues.
Custom classifier files must be TOML files with the .toml extension.
TOML Format#
name = "my-classifier"
[types]
C_ALI = { radius = 1.87, class = "apolar" }
C_CAR = { radius = 1.76, class = "apolar" }
N = { radius = 1.65, class = "polar" }
O = { radius = 1.40, class = "polar" }
S = { radius = 1.85, class = "apolar" }
[[atoms]]
residue = "ANY"
atom = "CA"
type = "C_ALI"
[[atoms]]
residue = "ALA"
atom = "CB"
type = "C_ALI"Use it from the CLI with:
zsasa calc --config=my_radii.toml structure.cif output.jsonThe file is checked strictly, and a rejected file stops the run with an error that names the problem and the line (Error loading config file 'my_radii.toml' (line 4): RadiusOutOfRange):
- Every
radiusis a number greater than 0 and at most 100 Å, the same range JSON input uses (RadiusOutOfRange). A value such as1e300,infornanwould make the areas overflow, and earlier versions accepted it. - Nothing may follow a value on its line except a
#comment (UnexpectedCharacter). - A key appears once per table,
[[atoms]]entry and inline table (DuplicateKey; a type name defined twice isDuplicateType), and each[table]header appears once (DuplicateTable). Earlier versions used the first value and ignored the rest silently.
Migrating from legacy FreeSASA-style text#
Older custom classifier text files are no longer supported as input. Convert type definitions and atom mappings to TOML:
# Old FreeSASA-style text
C_ALI 1.87 apolar
ANY CA C_ALIbecomes:
[types]
C_ALI = { radius = 1.87, class = "apolar" }
[[atoms]]
residue = "ANY"
atom = "CA"
type = "C_ALI"CCD Classifier#
The CCD classifier is the default classifier in zsasa. It derives van der Waals radii from the wwPDB Chemical Component Dictionary (CCD) bond topology, using the same hybridization-based radii as ProtOr (Tsai et al. 1999). Unlike NACCESS and OONS — which only cover standard amino acids and nucleotides — the CCD classifier can assign hybridization-aware radii to any chemical component: ligands, modified residues, cofactors, post-translational modifications, and more. Use --classifier=protor when you want the static ProtOr-compatible table without runtime CCD resource parsing.
Why CCD?#
NACCESS and OONS have a fixed set of known residues. When they encounter a non-standard residue (e.g., HEM, ATP, NAG), they fall back to generic element-based radii that ignore hybridization. This can lead to inaccurate SASA values for structures containing ligands or modified residues.
The CCD classifier solves this by analyzing bond topology (single, double, aromatic bonds and hydrogen count) to determine the hybridization state of each atom, then mapping it to the corresponding ProtOr-compatible radius. This gives you hybridization-aware radii for any component with CCD data.
How It Works#
The CCD classifier uses a 3-level lookup:
- Hardcoded table (compile-time, O(1)): Pre-compiled ProtOr radii for 45 common residues — the 20 standard amino acids, selenoamino acids (SEC, MSE), non-standard amino acids (PYL, ASX, GLX), post-translationally modified residues (HYP, MLY, SEP, TPO), modified nucleotide (PSU), nucleotides (A, C, G, I, T, U, DA, DC, DG, DI, DT, DU), capping groups (ACE, NH2), and water (HOH).
- Runtime CCD analysis: For non-standard residues, bond topology from CCD data is analyzed at runtime to derive hybridization-aware radii. CCD data comes from two sources:
- Inline CCD —
_chem_comp_atom/_chem_comp_bondloops embedded in mmCIF structure files - External CCD — a separate CCD dictionary loaded via
--ccd=<path>(CIF or ZSDC format)
- Inline CCD —
- Element fallback: If no CCD data is available, a generic van der Waals radius based on the element symbol is used.
Inline CCD vs External CCD
Inline CCD refers to the _chem_comp_atom and _chem_comp_bond data loops that many mmCIF files include alongside coordinate data. These describe the bond topology for each chemical component present in the structure.
External CCD is a separate dictionary file (the full wwPDB CCD or subsets of it) that you provide via --ccd=<path>. This works with both mmCIF and PDB input formats.
Input Format Support#
| Input Format | Hardcoded Table | Inline CCD | External CCD (--ccd=) |
|---|---|---|---|
mmCIF (calc) |
✅ | ✅ auto-extracted with --classifier=ccd |
✅ |
mmCIF (batch) |
✅ | ✅ auto-extracted with --classifier=ccd; fast-skipped when absent |
✅ |
| PDB | ✅ | — (not available in PDB format) | ✅ |
- mmCIF calc input: Inline CCD data (if present) is automatically parsed with
--classifier=ccd. Use--classifier=protorto skip CCD resource parsing for protein-only structures. - mmCIF batch input: Inline CCD data is automatically parsed with
--classifier=ccdwhen present. AFDB-like files without_chem_comp_atomare detected quickly and do not pay the full inline CCD extraction cost. Use--classifier=protorfor protein-only runs that should skip all CCD resources. - PDB input: PDB format does not include bond topology, so inline CCD is not available. For non-standard residues, provide an external CCD dictionary with
--ccd=<path>to get the same hybridization-aware radii.
# mmCIF calc — inline CCD is automatically used with CCD
zsasa calc --classifier=ccd structure.cif output.json
# protein-only mmCIF — skip CCD resource parsing
zsasa calc --classifier=protor structure.cif output.json
# PDB — use external CCD for non-standard residues
zsasa calc --classifier=ccd --ccd=HEM.cif structure.pdb output.json
# PDB with full CCD dictionary (covers all ~35,000 components)
zsasa calc --classifier=ccd --ccd=components.zsdc structure.pdb output.jsonPython
The Python functions classify_atoms, get_radius and get_atom_class, and the gemmi, BioPython and Biotite integrations, use only the hardcoded table for ClassifierType.CCD; they do not analyze bond topology. Atoms outside the table get a NaN radius, which the integrations replace with the element's radius. process_directory() uses inline mmCIF CCD data and SDF bonds like zsasa batch, but has no --ccd option.
PDB users
If your PDB file contains only standard amino acids and nucleotides, no external dictionary is needed — the hardcoded table covers all standard residues. For non-standard residues, provide --ccd=<path> to get hybridization-aware radii.
HETATM Records#
HETATM records (ligands, ions, waters) are excluded by default with every classifier, including CCD. Pass --include-hetatm (CLI), include_hetatm = true (workflow) or include_hetatm=True (Python) to include them; the CCD classifier then assigns radii to ligands from their component definitions. Waters are HETATM records too, so including HETATM also counts crystallographic waters.
Modified residues that a file records as HETATM inside a polymer chain (for example selenomethionine, MSE) are excluded by default as well, which leaves a gap in the chain. Include HETATM when a structure has such residues.
Versions up to 0.9.1 included HETATM automatically whenever the CCD classifier was active on the command line, which made the default CLI result differ from the Python process_directory() default.
External CCD Dictionary#
You can provide CCD data for specific components or the entire wwPDB CCD:
# Single component CIF (downloadable from RCSB)
zsasa calc --classifier=ccd --ccd=HEM.cif structure.pdb output.json
# Multiple components concatenated
cat HEM.cif PO4.cif ATP.cif > my_ligands.cif
zsasa calc --classifier=ccd --ccd=my_ligands.cif structure.pdb output.json
# Gzipped CIF
zsasa calc --classifier=ccd --ccd=components.cif.gz structure.pdb output.json
# Pre-compiled binary format (faster loading)
zsasa calc --classifier=ccd --ccd=components.zsdc structure.pdb output.jsonComponent CIF files can be downloaded from RCSB Ligand Expo, e.g.:
https://files.rcsb.org/ligands/view/HEM.cifhttps://files.rcsb.org/ligands/view/ATP.cif
compile-dict Subcommand#
The compile-dict subcommand converts a CCD dictionary from CIF text to compact binary ZSDC format for faster loading:
# Download the full CCD dictionary (~35,000 components)
wget https://files.wwpdb.org/pub/pdb/data/monomers/components.cif.gz
# Compile to binary ZSDC format
zsasa compile-dict components.cif.gz -o components.zsdc
# Use with CCD classifier
zsasa calc --classifier=ccd --ccd=components.zsdc structure.pdb output.jsonSupported input formats for compile-dict:
.cif— CIF text.cif.gz— gzip-compressed CIF text.cif.zst— Zstandard-compressed CIF text
A component in the ZSDC file can hold at most 65,535 atoms and 65,535 bonds. compile-dict exits with an error, and writes no file, if the input has no components or a component exceeds those limits. --ccd= rejects a ZSDC file with invalid lengths, counts or bond orders instead of loading it.
Hybridization Analysis#
The CCD classifier determines van der Waals radii through the following process:
- Parse bond graph — Extract atoms and bonds from CCD
_chem_comp_atom/_chem_comp_bondloops - Analyze bonds — For each non-hydrogen atom, count single, double, and aromatic bonds
- Determine hybridization — Classify as sp, sp2, or sp3 based on bond pattern
- Count hydrogens — Add the hydrogens listed in the component to those implied by unfilled valence
- Map to ProtOr radius — Each (element, hybridization) pair maps to a specific ProtOr-compatible radius; for carbon the radius also depends on whether it carries any hydrogen
For example, an aromatic CH carbon like PHE CD1 (two ring bonds to heavy atoms, one hydrogen) is classified as sp2 with a hydrogen, receiving a radius of 1.76 Å. A carboxyl carbon like ASP CG (one double bond to O, two single bonds to heavy atoms, no hydrogen) is also sp2 but receives 1.61 Å. A typical aliphatic carbon like ALA CB (one single bond to CA, three hydrogens) is sp3, receiving 1.88 Å.
The result does not depend on whether the component lists its hydrogens. wwPDB CCD entries and most SDF files list them; a hydrogen-free definition gives the same radii because the missing hydrogens are inferred from the unfilled valence.
Supported Residues#
Amino Acids#
All 20 standard amino acids are supported by all classifiers. Additional residues vary by classifier:
- All classifiers: SEC (selenocysteine), MSE (selenomethionine)
- CCD, ProtOr and OONS: PYL (pyrrolysine)
- CCD and ProtOr only: HYP (hydroxyproline), MLY (N-dimethyllysine), SEP (phosphoserine), TPO (phosphothreonine)
Nucleic Acids#
RNA: A, C, G, I, T, U DNA: DA, DC, DG, DI, DT, DU Modified: PSU (pseudouridine; CCD, ProtOr and OONS)
CCD Coverage#
With an external CCD dictionary or inline CCD data from mmCIF files, the CCD classifier can handle any of the ~35,000 components in the wwPDB Chemical Component Dictionary — including ligands, cofactors, modified residues, and post-translational modifications.
Handling Unknown Atoms#
When a classifier cannot find a matching (residue, atom name) pair:
- NACCESS/OONS check the
ANYfallback entries - If still unmatched, the atom gets the generic van der Waals radius of its element. The element is read from the input: the element column of a PDB file (or, where that is missing, the atom name columns),
type_symbolof mmCIF/BinaryCIF, or theelementfield of JSON - Only when there is no element to read is it guessed from the residue and atom names: JSON input without an
elementfield, or, for NACCESS/OONS, an atom whose element is unknown
The CLI reports both groups, for example Classifier 'NACCESS': 154 atoms classified, 150 fallback for a 20-residue peptide with 150 explicit hydrogens.
The name-based guess in step 3 follows PDB naming:
- A name starting with H, C, N, O, P or S is that element:
HG21is hydrogen,CD1is carbon,NAinHEMis nitrogen,PBinATPis phosphorus - A monatomic ion is a residue named after its atom:
ZNinZNis zinc,NAinNAis sodium - Other names are read as a two-letter element where one exists (
FE,BR1)
It cannot recognize a metal or halogen named like an organic atom inside a larger residue (CU1 in CUA, CL1 in a ligand), so supply the element whenever the input format allows it. The Python functions classify_atoms and get_radius take no element and use the same guess for NACCESS and OONS.
The CCD classifier avoids this element fallback for non-standard residues by deriving hybridization-aware radii from CCD bond topology. To maximize coverage:
- Use
zsasa calc --classifier=ccdwith mmCIF input when inline CCD categories are present - Provide
--ccd=<path>for PDB input or batch workloads
To avoid ambiguity (e.g., "CA" = Carbon-alpha vs Calcium), include an element field (atomic numbers) in JSON input:
{
"atom_name": ["CA", "CA"],
"element": [6, 20]
}- Atomic number 6 = Carbon (Cα)
- Atomic number 20 = Calcium (metal ion)
References#
- Hubbard, S. J.; Thornton, J. M. NACCESS, Computer Program. UCL, 1993.
- Tsai, J.; Taylor, R.; Chothia, C.; Gerstein, M. The Packing Density in Proteins. J. Mol. Biol. 1999, 290(1), 253-266.
- Ooi, T.; Oobatake, M.; Némethy, G.; Scheraga, H. A. Accessible Surface Areas. Proc. Natl. Acad. Sci. 1987, 84(10), 3086-3090.
- Mantina, M. et al. Consistent van der Waals Radii. J. Phys. Chem. A 2009, 113(19), 5806-5812.