$npx -y skills add adaptyvbio/protein-design-skills --skill pdbFetch and analyze protein structures from RCSB PDB. Use this skill when: (1) Need to download a structure by PDB ID, (2) Search for similar structures, (3) Prepare target for binder design, (4) Extract specific chains or domains, (5) Get structure metadata. For sequence lookup, u
| 1 | # PDB Database Access |
| 2 | |
| 3 | **Note**: This skill uses the RCSB PDB web API directly. No Modal deployment needed - all operations run locally via HTTP requests. |
| 4 | |
| 5 | ## Fetching Structures |
| 6 | |
| 7 | ### By PDB ID |
| 8 | ```bash |
| 9 | # Download PDB file |
| 10 | curl -o 1alu.pdb "https://files.rcsb.org/download/1ALU.pdb" |
| 11 | |
| 12 | # Download mmCIF |
| 13 | curl -o 1alu.cif "https://files.rcsb.org/download/1ALU.cif" |
| 14 | ``` |
| 15 | |
| 16 | ### Using Python |
| 17 | ```python |
| 18 | from Bio.PDB import PDBList |
| 19 | |
| 20 | pdbl = PDBList() |
| 21 | pdbl.retrieve_pdb_file("1ABC", pdir="structures/", file_format="pdb") |
| 22 | ``` |
| 23 | |
| 24 | ### Using RCSB API |
| 25 | ```python |
| 26 | import requests |
| 27 | |
| 28 | def fetch_pdb(pdb_id: str, format: str = "pdb") -> str: |
| 29 | """Fetch structure from RCSB PDB.""" |
| 30 | url = f"https://files.rcsb.org/download/{pdb_id}.{format}" |
| 31 | response = requests.get(url) |
| 32 | response.raise_for_status() |
| 33 | return response.text |
| 34 | |
| 35 | def fetch_fasta(pdb_id: str) -> str: |
| 36 | """Fetch sequence in FASTA format.""" |
| 37 | url = f"https://www.rcsb.org/fasta/entry/{pdb_id}" |
| 38 | return requests.get(url).text |
| 39 | |
| 40 | # Example usage |
| 41 | pdb_content = fetch_pdb("1ALU") |
| 42 | with open("1ALU.pdb", "w") as f: |
| 43 | f.write(pdb_content) |
| 44 | ``` |
| 45 | |
| 46 | ## Structure Preparation |
| 47 | |
| 48 | ### Selecting Chains |
| 49 | ```python |
| 50 | from Bio.PDB import PDBParser, PDBIO, Select |
| 51 | |
| 52 | class ChainSelect(Select): |
| 53 | def __init__(self, chain_id): |
| 54 | self.chain_id = chain_id |
| 55 | |
| 56 | def accept_chain(self, chain): |
| 57 | return chain.id == self.chain_id |
| 58 | |
| 59 | # Extract chain A |
| 60 | parser = PDBParser() |
| 61 | structure = parser.get_structure("protein", "1abc.pdb") |
| 62 | io = PDBIO() |
| 63 | io.set_structure(structure) |
| 64 | io.save("chain_A.pdb", ChainSelect("A")) |
| 65 | ``` |
| 66 | |
| 67 | ### Trimming to Binding Region |
| 68 | ```python |
| 69 | def trim_around_residues(pdb_file, center_residues, buffer=10.0): |
| 70 | """Trim structure to region around specified residues.""" |
| 71 | parser = PDBParser() |
| 72 | structure = parser.get_structure("protein", pdb_file) |
| 73 | |
| 74 | # Get center coordinates |
| 75 | center_coords = [] |
| 76 | for res in structure.get_residues(): |
| 77 | if res.id[1] in center_residues: |
| 78 | center_coords.extend([a.coord for a in res.get_atoms()]) |
| 79 | |
| 80 | center = np.mean(center_coords, axis=0) |
| 81 | |
| 82 | # Keep residues within buffer |
| 83 | class RegionSelect(Select): |
| 84 | def accept_residue(self, res): |
| 85 | for atom in res.get_atoms(): |
| 86 | if np.linalg.norm(atom.coord - center) < buffer: |
| 87 | return True |
| 88 | return False |
| 89 | |
| 90 | io = PDBIO() |
| 91 | io.set_structure(structure) |
| 92 | io.save("trimmed.pdb", RegionSelect()) |
| 93 | ``` |
| 94 | |
| 95 | ## Searching PDB |
| 96 | |
| 97 | ### RCSB Search API |
| 98 | ```python |
| 99 | import requests |
| 100 | |
| 101 | query = { |
| 102 | "query": { |
| 103 | "type": "terminal", |
| 104 | "service": "full_text", |
| 105 | "parameters": { |
| 106 | "value": "EGFR kinase domain" |
| 107 | } |
| 108 | }, |
| 109 | "return_type": "entry" |
| 110 | } |
| 111 | |
| 112 | response = requests.post( |
| 113 | "https://search.rcsb.org/rcsbsearch/v2/query", |
| 114 | json=query |
| 115 | ) |
| 116 | results = response.json() |
| 117 | ``` |
| 118 | |
| 119 | ### By Sequence Similarity |
| 120 | ```python |
| 121 | query = { |
| 122 | "query": { |
| 123 | "type": "terminal", |
| 124 | "service": "sequence", |
| 125 | "parameters": { |
| 126 | "value": "MKTAYIAKQRQISFVK...", |
| 127 | "evalue_cutoff": 1e-10, |
| 128 | "identity_cutoff": 0.9 |
| 129 | } |
| 130 | } |
| 131 | } |
| 132 | ``` |
| 133 | |
| 134 | ## Structure Analysis |
| 135 | |
| 136 | ### Get Chain Info |
| 137 | ```python |
| 138 | def get_structure_info(pdb_file): |
| 139 | parser = PDBParser(QUIET=True) |
| 140 | structure = parser.get_structure("protein", pdb_file) |
| 141 | |
| 142 | info = { |
| 143 | "chains": [], |
| 144 | "total_residues": 0 |
| 145 | } |
| 146 | |
| 147 | for model in structure: |
| 148 | for chain in model: |
| 149 | residues = list(chain.get_residues()) |
| 150 | info["chains"].append({ |
| 151 | "id": chain.id, |
| 152 | "length": len(residues), |
| 153 | "first_res": residues[0].id[1], |
| 154 | "last_res": residues[-1].id[1] |
| 155 | }) |
| 156 | info["total_residues"] += len(residues) |
| 157 | |
| 158 | return info |
| 159 | ``` |
| 160 | |
| 161 | ### Find Interface Residues |
| 162 | ```python |
| 163 | def find_interface_residues(pdb_file, chain_a, chain_b, distance=4.0): |
| 164 | """Find residues at interface between two chains.""" |
| 165 | parser = PDBParser(QUIET=True) |
| 166 | structure = parser.get_structure("complex", pdb_file) |
| 167 | |
| 168 | interface_a = set() |
| 169 | interface_b = set() |
| 170 | |
| 171 | for res_a in structure[0][chain_a].get_residues(): |
| 172 | for res_b in structure[0][chain_b].get_residues(): |
| 173 | for atom_a in res_a.get_atoms(): |
| 174 | for atom_b in res_b.get_atoms(): |
| 175 | if atom_a - atom_b < distance: |
| 176 | interface_a.add(res_a.id[1]) |
| 177 | interface_b.add(res_b.id[1]) |
| 178 | |
| 179 | return interface_a, interface_b |
| 180 | ``` |
| 181 | |
| 182 | ## Common Tasks for Binder Design |
| 183 | |
| 184 | ### Target Preparation Checklist |
| 185 | 1. Download structure: |