Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Navigate protein structure hierarchy using Biopython Bio.PDB SMCRA model. Use when accessing models, chains, residues, and atoms, iterating over structure levels, or extracting sequences from PDB files.
.claude/skills/bio-pdb-structure-navigation/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-01 | ✗→✓ | ▲ Improved | — | — |
| case-07 | ✗→✓ | ▲ Improved | — | — |
| case-13 | ✗→✓ | ▲ Improved | — | — |
| case-16 | ✗→✓ | ▲ Improved | — | — |
| case-18 | ✗→✓ | ▲ Improved | — | — |
Reference examples tested with: BioPython 1.83+
Before using code patterns, verify installed versions match. If versions differ:
pip show <package> then help(module.function) to check signaturesIf code throws ImportError, AttributeError, or TypeError, introspect the installed package and adapt the example to match the actual API rather than retrying.
"Access residues and atoms in a PDB structure" → Navigate the Structure-Model-Chain-Residue-Atom hierarchy to iterate over components, extract sequences, and access atomic coordinates.
structure[0]['A'][100]['CA'].get_vector() for direct accessNavigate the Structure-Model-Chain-Residue-Atom (SMCRA) hierarchy to access and iterate over structure components.
pythonfrom Bio.PDB import PDBParser, PPBuilder, Selection from Bio.Data.PDBData import protein_letters_3to1
Structure
|
+-- Model (0, 1, ...) # NMR ensembles, crystal asymmetric unit
|
+-- Chain (A, B, ...) # Polypeptide chains, ligands
|
+-- Residue # Amino acids, nucleotides, hetero groups
|
+-- Atom # Individual atomspythonfrom Bio.PDB import PDBParser parser = PDBParser(QUIET=True) structure = parser.get_structure('protein', 'protein.pdb') # Access by index/ID model = structure[0] # First model chain = model['A'] # Chain A residue = chain[100] # Residue 100 (simple numbering) residue = chain[(' ', 100, ' ')] # Full residue ID (hetfield, resseq, icode) atom = residue['CA'] # C-alpha atom
python# Iterate all levels for model in structure: for chain in model: for residue in chain: for atom in residue: print(f'{chain.id}:{residue.id[1]}:{atom.name}') # Shortcut iterators (all levels below current) for chain in structure.get_chains(): print(f'Chain: {chain.id}') for residue in structure.get_residues(): print(f'Residue: {residue.resname}') for atom in structure.get_atoms(): print(f'Atom: {atom.name} at {atom.coord}')
python# Residue ID is a tuple: (hetfield, resseq, icode) for residue in chain: hetfield, resseq, icode = residue.id print(f'Residue {resseq}{icode}: {residue.resname}') # hetfield values: # ' ' - standard amino acid # 'W' - water # 'H_xxx' - hetero residue (ligand, modified residue) # Filter standard residues only standard_residues = [r for r in chain if r.id[0] == ' '] # Filter water waters = [r for r in chain if r.id[0] == 'W'] # Filter hetero atoms (ligands) hetero = [r for r in chain if r.id[0].startswith('H_')]
pythonfor atom in residue: print(f'Name: {atom.name}') print(f'Element: {atom.element}') print(f'Coordinates: {atom.coord}') print(f'B-factor: {atom.bfactor}') print(f'Occupancy: {atom.occupancy}') print(f'Full ID: {atom.full_id}') print(f'Serial number: {atom.serial_number}')
python# Full hierarchical ID from any entity atom = structure[0]['A'][100]['CA'] print(atom.get_full_id()) # ('protein', 0, 'A', (' ', 100, ' '), ('CA', ' ')) # Components: (structure_id, model_id, chain_id, residue_id, atom_id)
python# Check if entity has child if chain.has_id(100): residue = chain[100] # Check if residue has atom if residue.has_id('CA'): ca = residue['CA'] # Get list of all children chains = structure[0].get_list() residues = chain.get_list() atoms = residue.get_list()
python# Navigate up hierarchy atom = structure[0]['A'][100]['CA'] residue = atom.get_parent() chain = residue.get_parent() model = chain.get_parent() structure = model.get_parent()
pythonfrom Bio.PDB import PDBParser, PPBuilder parser = PDBParser(QUIET=True) structure = parser.get_structure('protein', 'protein.pdb') ppb = PPBuilder() for pp in ppb.build_peptides(structure): seq = pp.get_sequence() print(f'Polypeptide: {seq}') print(f'Length: {len(seq)}') # Get all sequences as list sequences = [pp.get_sequence() for pp in ppb.build_peptides(structure)]
pythonfrom Bio.PDB import CaPPBuilder # Use when backbone is incomplete # Connects residues if CA atoms are within 4.3 Angstroms ppb = CaPPBuilder() for pp in ppb.build_peptides(structure): print(f'Fragment: {pp.get_sequence()}')
pythonfrom Bio.Data.PDBData import protein_letters_3to1 # Three-letter to one-letter conversion three_letter = 'ALA' one_letter = protein_letters_3to1.get(three_letter, 'X') print(f'{three_letter} -> {one_letter}') # ALA -> A # Build sequence manually sequence = '' for residue in chain: if residue.id[0] == ' ': # Standard residue code = protein_letters_3to1.get(residue.resname, 'X') sequence += code print(f'Sequence: {sequence}')
pythonfrom Bio.PDB import Selection # Extract entities at specific level # Codes: S=structure, M=model, C=chain, R=residue, A=atom # Get all residues from structure residues = Selection.unfold_entities(structure, 'R') print(f'Total residues: {len(residues)}') # Get all atoms from a chain atoms = Selection.unfold_entities(chain, 'A') print(f'Atoms in chain: {len(atoms)}') # Get all chains from model chains = Selection.unfold_entities(model, 'C')
python# Check for disorder if atom.is_disordered(): print(f'Atom {atom.name} has multiple conformations') print(f'Alt locations: {atom.disordered_get_id_list()}') # Select specific conformation atom.disordered_select('A') print(f'Coord for alt A: {atom.coord}') # Get all conformations for altloc in atom.disordered_get_id_list(): atom.disordered_select(altloc) print(f' {altloc}: {atom.coord}') # Get unpacked list (all conformations) all_atoms = atom.disordered_get_list()
python# Point mutations at same position if residue.is_disordered(): print(f'Disordered residue at {residue.id}') names = residue.disordered_get_id_list() print(f'Alternative residues: {names}') # Select specific residue type residue.disordered_select('ALA')
python# Get backbone atoms backbone_names = ['N', 'CA', 'C', 'O'] for residue in chain: backbone = [residue[name] for name in backbone_names if residue.has_id(name)] # Get all C-alpha atoms ca_atoms = [r['CA'] for r in structure.get_residues() if r.has_id('CA')] print(f'Found {len(ca_atoms)} CA atoms') # Get sidechain atoms for residue in chain: sidechain = [a for a in residue if a.name not in ['N', 'CA', 'C', 'O']]
python# Get only amino acids amino_acids = [r for r in chain if r.id[0] == ' '] # Get specific amino acid types arginines = [r for r in chain if r.resname == 'ARG'] charged = [r for r in chain if r.resname in ['ARG', 'LYS', 'ASP', 'GLU']] # Get hetero atoms ligands = [r for r in chain if r.id[0].startswith('H_')] for lig in ligands: print(f'Ligand: {lig.resname} at position {lig.id[1]}')
python# Count at each level n_models = len(list(structure.get_models())) n_chains = len(list(structure.get_chains())) n_residues = len(list(structure.get_residues())) n_atoms = len(list(structure.get_atoms())) print(f'Models: {n_models}, Chains: {n_chains}') print(f'Residues: {n_residues}, Atoms: {n_atoms}') # Count per chain for chain in structure.get_chains(): n_res = len([r for r in chain if r.id[0] == ' ']) print(f'Chain {chain.id}: {n_res} amino acids')
python# NMR structures have multiple models parser = PDBParser(QUIET=True) structure = parser.get_structure('nmr', 'nmr_structure.pdb') n_models = len(list(structure.get_models())) print(f'NMR ensemble with {n_models} conformers') # Iterate over models for model in structure: # Each model is a separate conformation ca_coords = [r['CA'].coord for r in model.get_residues() if r.has_id('CA')] print(f'Model {model.id}: {len(ca_coords)} CA atoms')
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-10 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-01 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-11 | pass→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-07 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-13 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-15 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-16 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-21 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-12 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-05 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-19 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-04 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-17 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-18 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-14 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-06 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-02 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-08 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-03 | fail→pass | — | — | — | — | — | — | — | — | — | — | — | — |
case-22 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-09 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
case-20 | fail→fail | — | — | — | — | — | — | — | — | — | — | — | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +36 percentage points is the difference between those two pass rates over the 22 comparable cases.
The per-case answers from this run were removed by the retention sweep, so the case table below shows the verdicts without the text either arm produced. The counts above were recorded at the time and are unaffected. Answers are now kept for 180 days.
Other measured skills in the registry, with their headline benchmark lift.