references/citation.bib
@MANUAL{Schrodinger2015,
AUTHOR = {{Schrödinger, LLC}},
DATE = {2015},
TITLE = {The {PyMOL} Molecular Graphics System, Version~2.0},
}
google-deepmind/science-skills · GitHub
Visualize, analyze, and render protein and molecular structures using PyMOL. Use when the user wants to create images of protein structures, perform structural alignments or superposition, measure distances or contacts, highlight binding sites or active site residues, color by B-factor/pLDDT, or analyze protein-ligand interactions. Do not use for docking, molecular dynamics, or sequence-only analysis.
프로젝트 폴더에서 아래 명령어를 실행하고, 설치할 에이전트를 선택하세요.
npx skills add google-deepmind/science-skills --skill pymol설치 명령을 직접 실행해야 적용됩니다. 지원 에이전트와 필요한 권한·라이선스는 제작자의 안내를 확인하세요.
references/citation.bib@MANUAL{Schrodinger2015,
AUTHOR = {{Schrödinger, LLC}},
DATE = {2015},
TITLE = {The {PyMOL} Molecular Graphics System, Version~2.0},
}
references/PYMOL_REFERENCE.md# PyMOL Quick Reference
## Mandatory Initialization Boilerplate
Every PyMOL script **must** start with this exact sequence. The order matters —
reversing the import and `finish_launching()` will crash.
```python
import pymol
pymol.pymol_argv = ["pymol", "-cq"]
pymol.finish_launching()
from pymol import cmd
```
- `-c` = command-line mode (no GUI)
- `-q` = quiet (suppress startup messages)
## Rendering Backend
PyMOL runs with **OSMesa** (software rendering). There is no GPU or X display.
- Use `cmd.png(path, width, height, dpi)` for output.
- `cmd.ray()` works but is slow — use it only when you need ray-traced
quality.
- Never use `cmd.draw()` (requires hardware OpenGL).
- Always set `cmd.set("ray_opaque_background", 1)` if you want a white
background instead of transparent.
## Selection Syntax
### Identifiers
Selector | Example | Selects
-------------- | ------------------ | ----------------------------------
`chain` | `chain A` | All atoms in chain A
`resi` | `resi 100` | Residue number 100
`resi` (range) | `resi 100-200` | Residues 100 through 200
`resi` (list) | `resi 100+102+150` | Specific residues
`resn` | `resn ALA` | All alanine residues
`name` | `name CA` | All C-alpha atoms
`ss` | `ss h` | Helices (h), sheets (s), loops (l)
### Structure types
Selector | Selects
----------------- | ------------------------------------------
`polymer.protein` | All protein atoms
`organic` | Organic ligands (non-polymer, non-solvent)
`solvent` | Water molecules
`hetatm` | Heteroatoms (ligands, ions, water)
`all` | Everything
### Logical operators
Operator | Example | Meaning
-------- | -------------------------------- | ------------
`and` | `chain A and resi 100` | Intersection
`or` | `resn ALA or resn GLY` | Union
`not` | `not solvent` | Negation
`()` | `chain A and (not resi 100-110)` | Grouping
### Proximity selectors
Selector | Example
------------------------- | ---------------------------------------------
`within X of (selection)` | `polymer.protein within 4 of organic`
`byres (selection)` | `byres (polymer.protein within 4 of organic)`
`around X` | `resi 100 around 5`
### Named selections
```python
cmd.select("binding_site", "byres (polymer.protein within 4 of organic)")
cmd.select("alpha_carbons", "name CA and polymer.protein")
```
## File Paths
- When running with `uv run`, files are accessed directly from the host
filesystem.
- Paths are relative to the directory where you run the command, or you can
use absolute paths.
- Ensure output directories exist before trying to write to them.
## Common Commands
### Loading structures
```python
cmd.load("data/structure.cif", "myprotein")
cmd.load("data/structure.pdb", "myprotein")
```
### Display modes
```python
cmd.show("cartoon", "polymer.protein")
cmd.show("sticks", "resi 100-110")
cmd.show("surface", "polymer.protein")
cmd.show("spheres", "resi 50")
cmd.hide("everything", "solvent")
```
### Coloring
```python
cmd.color("green", "ss h")
cmd.color("cyan", "chain A")
cmd.spectrum("b", "red_white_blue", "polymer.protein")
cmd.spectrum("count", "rainbow", "polymer.protein")
```
### Structural operations
```python
cmd.align("mobile", "target")
cmd.super("mobile", "target")
cmd.select("site", "resi 100-120 and chain A")
cmd.distance("dist1", "resi 100 and name CA", "resi 200 and name CA")
```
### Output
```python
cmd.png("output/image.png", width=1200, height=900, dpi=150)
cmd.save("output/modified.pdb", "myprotein")
```
### Cleanup (REQUIRED)
```python
cmd.quit()
```
## Common Pitfalls
1. **`cmd.quit()` is mandatory** — without it, the PyMOL process hangs and the
container will time out.
2. **Selection case sensitivity** — `"chain a"` is NOT the same as `"chain A"`.
3. **`cmd.fetch()` will fail** — there is no network inside the container. Use
`cmd.load()` with pre-downloaded files.
4. **`cmd.png()` before `cmd.quit()`** — ensure all rendering is done before
quitting.
5. **Paths are relative to current directory** — ensure you run the script from
the correct directory or use absolute paths.
6. **Container timeout** — the default timeout is **300 seconds** (5 minutes),
which is sufficient for most rendering tasks. For long operations (e.g.,
ray-tracing large complexes), increase with `--container_timeout=<seconds>`.
Do not reduce below 60 seconds.
7. **Distance objects are NOT selections** — `cmd.distance()` creates a
measurement object, not an atom selection. Do NOT use `cmd.count_atoms()` on
distance objects — it will error. Only use `cmd.count_atoms()` on valid atom
selections (e.g., by residue name, chain, or proximity).
8. **Selection names must be valid identifiers** — names passed to
`cmd.select("name", ...)` must be alphanumeric and underscores only, start
with a letter, and contain no spaces. `binding_site` is valid; `binding
site` or `1_ligand` will crash PyMOL.
9. **Multi-state structures (NMR)** — for NMR ensembles or multi-model files,
restrict distance measurements, alignments, and rendering to `state=1` to
prevent visual clutter and errors across all states simultaneously.
references/RECIPES.md# Common Recipes
Copy-paste ready recipes for common PyMOL visualization tasks. Each recipe
assumes the init boilerplate has already been set up (see
[PYMOL_REFERENCE.md](PYMOL_REFERENCE.md)).
### Cartoon with secondary structure coloring
```python
cmd.load("data/structure.cif", "protein")
cmd.show("cartoon")
cmd.color("green", "ss h")
cmd.color("yellow", "ss s")
cmd.color("gray", "ss l+''")
cmd.orient()
cmd.png("output/cartoon.png", width=1200, height=900, dpi=150)
cmd.save("output/session.pse")
```
### B-factor (pLDDT) coloring
```python
cmd.load("data/AF-P00520-F1-model_v4.cif", "protein")
cmd.show("cartoon")
cmd.spectrum("b", "red_white_blue", "polymer.protein")
cmd.orient()
cmd.png("output/bfactor.png", width=1200, height=900, dpi=150)
cmd.save("output/session.pse")
```
### AlphaFold pLDDT coloring (canonical thresholds)
```python
cmd.load("data/structure.cif", "protein")
cmd.show("cartoon")
# Define AlphaFold's canonical pLDDT colors
cmd.set_color("af_very_low", [0xFF, 0x7D, 0x45]) # orange, pLDDT < 50
cmd.set_color("af_low", [0xFF, 0xDB, 0x13]) # yellow, 50 <= pLDDT < 70
cmd.set_color("af_confident", [0x65, 0xCB, 0xF3]) # light blue, 70 <= pLDDT < 90
cmd.set_color("af_very_high", [0x00, 0x53, 0xD6]) # dark blue, pLDDT >= 90
# Apply from lowest to highest threshold
cmd.color("af_very_low", "polymer.protein")
cmd.color("af_low", "polymer.protein and b > 50")
cmd.color("af_confident", "polymer.protein and b > 70")
cmd.color("af_very_high", "polymer.protein and b > 90")
cmd.orient()
cmd.png("output/plddt.png", width=1200, height=900, dpi=150)
cmd.save("output/session.pse")
```
### Highlight specific residues
```python
cmd.load("data/structure.cif", "protein")
cmd.show("cartoon")
cmd.color("gray", "all")
cmd.select("active_site", "chain A and resi 100+102+150")
cmd.show("sticks", "active_site")
cmd.color("red", "active_site")
cmd.orient()
cmd.png("output/highlight.png", width=1200, height=900, dpi=150)
cmd.save("output/session.pse")
```
### Surface rendering
```python
cmd.load("data/structure.cif", "protein")
cmd.show("cartoon", "polymer.protein")
cmd.color("green", "polymer.protein and ss h")
cmd.color("yellow", "polymer.protein and ss s")
cmd.color("gray", "polymer.protein and (ss l+'')")
cmd.show("surface", "polymer.protein")
cmd.set("surface_color", "white", "polymer.protein")
cmd.set("transparency", 0.3, "polymer.protein")
cmd.orient()
cmd.png("output/surface.png", width=1200, height=900, dpi=150)
cmd.save("output/session.pse")
```
### Electrostatic surface rendering
```python
cmd.load("data/structure.cif", "protein")
cmd.remove("solvent")
cmd.show("cartoon", "polymer.protein")
cmd.color("gray80", "polymer.protein")
util.protein_vacuum_esp("polymer.protein", quiet=0)
cmd.show("surface", "polymer.protein")
cmd.set("transparency", 0.0, "polymer.protein")
cmd.set("two_sided_lighting", 1)
cmd.orient()
cmd.png("output/electrostatic.png", width=1200, height=900, dpi=150)
cmd.save("output/session.pse")
```
### Multi-chain complex colors
```python
cmd.load("data/complex.cif", "complex")
cmd.remove("solvent")
cmd.show("cartoon", "polymer.protein")
chain_colors = ["cyan", "salmon", "green", "yellow", "magenta",
"orange", "slate", "limon", "deeppurple", "wheat"]
chains = cmd.get_chains("complex")
print(f"Chains found: {', '.join(chains)}")
for i, chain in enumerate(chains):
color = chain_colors[i % len(chain_colors)]
cmd.color(color, f"chain {chain}")
print(f" Chain {chain}: {color} ({cmd.count_atoms(f'chain {chain} and name CA')} residues)")
cmd.orient()
cmd.png("output/chains.png", width=1200, height=900, dpi=150)
cmd.save("output/session.pse")
```
### B-factor putty analysis
```python
cmd.load("data/structure.cif", "protein")
cmd.remove("solvent")
cmd.show("cartoon", "polymer.protein")
cmd.cartoon("putty", "polymer.protein")
cmd.set("cartoon_putty_scale_min", 0.3)
cmd.set("cartoon_putty_scale_max", 3.0)
cmd.set("cartoon_putty_transform", 0)
cmd.spectrum("b", "blue_white_red", "polymer.protein")
cmd.orient()
cmd.png("output/putty.png", width=1200, height=900, dpi=150)
cmd.save("output/session.pse")
```
### Cavity and pocket visualization (including ligand focus)
```python
cmd.load("data/structure.cif", "protein")
cmd.remove("solvent")
cmd.show("cartoon", "polymer.protein")
cmd.color("gray80", "polymer.protein")
# If a ligand is present, isolate one to focus the cavity view
if cmd.count_atoms("organic") > 0:
# Isolate a single ligand to prevent zoomed-out views on symmetrical complexes
first_atom = cmd.get_model("organic").atom[0]
cmd.select("target_ligand", f"organic and chain '{first_atom.chain}' and resi '{first_atom.resi}'")
cmd.show("sticks", "target_ligand")
util.cnc("target_ligand")
# Orient on the single ligand and zoom with enough buffer to see the pocket context
cmd.orient("target_ligand")
cmd.zoom("target_ligand", buffer=10.0)
else:
cmd.orient()
cmd.show("surface", "polymer.protein")
cmd.set("surface_color", "white", "polymer.protein")
cmd.set("transparency", 0.6, "polymer.protein")
cmd.set("surface_cavity_mode", 1)
cmd.set("surface_cavity_radius", 5.0)
cmd.set("surface_cavity_cutoff", -1.0)
cmd.set("cavity_cull", 50)
cmd.set("two_sided_lighting", 1)
cmd.png("output/cavities.png", width=1200, height=900, dpi=150)
cmd.save("output/session.pse")
```
**`surface_cavity_mode` values:** `0` = no cavity (default), `1` = show cavities
only, `2` = show outer surface trimmed around cavities.
### Multi-structure batch rendering
For comparing multiple structures side-by-side or generating consistent renders
across a set of PDB files. This is common in design campaigns.
```python
import os
import glob
structures = glob.glob("data/*.pdb") + glob.glob("data/*.cif")
print(f"Found {len(structures)} structures to render")
for struct_path in sorted(structures):
name = os.path.splitext(os.path.basename(struct_path))[0]
cmd.load(struct_path, name)
n_atoms = cmd.count_atoms(name)
if n_atoms == 0:
print(f" SKIP {name}: 0 atoms loaded")
cmd.delete(name)
continue
cmd.show("cartoon", name)
cmd.color("green", f"{name} and ss h")
cmd.color("yellow", f"{name} and ss s")
cmd.color("gray", f"{name} and (ss l+'')")
cmd.orient(name)
cmd.png(f"output/{name}.png", width=1200, height=900, dpi=150)
print(f" Rendered {name} ({n_atoms} atoms)")
cmd.delete(name)
```
### Measure distance between residues
```python
cmd.load("data/structure.cif", "protein")
cmd.show("cartoon")
cmd.distance("d1", "chain A and resi 10 and name CA", "chain A and resi 50 and name CA")
print(f"Distance: {cmd.get_distance('chain A and resi 10 and name CA', 'chain A and resi 50 and name CA'):.2f} A")
cmd.orient()
cmd.png("output/distance.png", width=1200, height=900, dpi=150)
cmd.save("output/session.pse")
```
### Zoom into binding pocket
```python
cmd.load("data/complex.pdb", "complex")
cmd.show("cartoon", "polymer.protein")
cmd.color("gray", "polymer.protein")
cmd.select("pocket", "byres (polymer.protein within 5.0 of organic)")
cmd.show("sticks", "pocket")
cmd.color("cyan", "pocket")
cmd.zoom("pocket", buffer=3.0)
cmd.png("output/pocket_zoom.png", width=1200, height=900, dpi=150)
cmd.save("output/session.pse")
```
### Protein-ligand interaction
```python
cmd.load("data/complex.pdb", "complex")
# Isolate a single ligand to prevent zoomed-out views on symmetrical complexes
if cmd.count_atoms("organic") > 0:
first_atom = cmd.get_model("organic").atom[0]
cmd.select("target_ligand", f"organic and chain '{first_atom.chain}'")
else:
raise RuntimeError("No ligand found.")
cmd.select("binding_site", "byres (polymer.protein within 4.0 of target_ligand)")
# Styled rendering with heteroatom coloring
cmd.show("cartoon", "polymer.protein")
cmd.set("cartoon_transparency", 0.4, "polymer.protein")
cmd.color("gray80", "polymer.protein")
cmd.show("sticks", "binding_site")
util.cbac("binding_site")
util.cnc("binding_site")
cmd.show("sticks", "target_ligand")
util.cbag("target_ligand")
util.cnc("target_ligand")
cmd.hide("sticks", "hydro")
# Show pocket waters
cmd.select("pocket_waters", "solvent within 4.0 of target_ligand")
cmd.show("spheres", "pocket_waters")
cmd.color("red", "pocket_waters")
cmd.set("sphere_scale", 0.15, "pocket_waters")
# Polar contacts (hydrogen bonds)
cmd.distance("polar_contacts", "target_ligand", "binding_site", cutoff=3.5, mode=2)
cmd.set("dash_color", "yellow")
cmd.set("dash_gap", 0.4)
cmd.set("dash_radius", 0.08)
cmd.hide("labels", "polar_contacts")
cmd.orient("target_ligand | binding_site")
cmd.zoom("target_ligand | binding_site", buffer=3.0)
cmd.png("output/ligand.png", width=1200, height=900, dpi=150)
cmd.save("output/session.pse")
```
### In silico mutagenesis
```python
cmd.load("data/structure.cif", "protein")
cmd.show("cartoon")
cmd.wizard("mutagenesis")
cmd.get_wizard().set_mode("ALA")
cmd.get_wizard().do_select("chain A and resi 100")
cmd.get_wizard().apply()
cmd.set_wizard()
cmd.orient()
cmd.png("output/mutant.png", width=1200, height=900, dpi=150)
cmd.save("output/session.pse")
```
### Two-structure superposition with RMSD
PyMOL provides three structural alignment methods:
- `align` — sequence-dependent; best when structures share >40% sequence
identity
- `super` — sequence-independent superposition; use when structures are
structurally similar but have poor sequence identity
- `cealign` — combinatorial extension; use when there is neither sequence nor
strong structural similarity
The recipe below attempts `align` first and falls back to `cealign`
automatically.
```python
cmd.load("data/structure1.cif", "model1")
cmd.load("data/structure2.cif", "model2")
try:
result = cmd.align("model2", "model1")
if result[1] < 20:
raise ValueError("Poor sequence alignment")
print(f"Method: align (>40% sequence identity)")
print(f"RMSD: {result[0]:.3f} A over {result[1]} atoms")
except Exception:
result = cmd.cealign("model1", "model2")
print(f"Method: cealign (low sequence identity fallback)")
print(f"RMSD: {result['RMSD']:.3f} A over {result['alignment_length']} atoms")
cmd.show("cartoon", "all")
cmd.color("cyan", "model1")
cmd.color("salmon", "model2")
cmd.orient()
cmd.png("output/superposition.png", width=1200, height=900, dpi=150)
cmd.save("output/session.pse")
```
### Load and modify an existing session
```python
cmd.load("data/previous_session.pse")
cmd.color("marine", "chain A")
cmd.show("surface", "chain B")
cmd.orient()
cmd.png("output/modified.png", width=1200, height=900, dpi=150)
cmd.save("output/session.pse")
```
To run any of these recipes, place the code in a Python script with the required
header and boilerplate (see [SKILL.md](../SKILL.md)) and run it with:
```bash
uv run your_script.py
```
SKILL.md---
name: pymol
description: >
Visualize, analyze, and render protein and molecular structures using PyMOL.
Use when the user wants to create images of protein structures, perform
structural alignments or superposition, measure distances or contacts,
highlight binding sites or active site residues, color by B-factor/pLDDT,
or analyze protein-ligand interactions. Do not use for docking,
molecular dynamics, or sequence-only analysis.
---
# PyMOL
## Prerequisites
1. **`uv`**: Read the `uv` skill and follow its Setup instructions to ensure
`uv` is installed and on PATH.
2. **User Notification**: If .licenses/pymol_LICENSE.txt does not already exist
in the workspace root directory then (1) prominently notify the user to
check the license at https://www.pymol.org/, then (2) create the file
recording the notification text and timestamp.
## Overview
All PyMOL commands run on the host via `uv` with OSMesa software rendering — no
GPU, display, or X server is needed. Structure files must be downloaded to the
host before running PyMOL.
**Do NOT use when:**
- The user wants to run AlphaFold predictions.
- The user wants docking or molecular dynamics simulations.
- The user only has a sequence and no structure file — fetch the structure
first. Check if any other installed skills can retrieve structures from the
PDB or AlphaFold Database before proceeding.
## Setup (Agent Instructions)
Ensure that `uv` is installed on the host system. The PyMOL scripts use PEP 0723
headers to declare their dependencies, and `uv run` will automatically handle
installing them (including `pymol-open-source-whl`) when the script is executed.
## Core Rules
- **Output paths must be absolute or relative to the user's project root.**
Always run PyMOL scripts from the user's project directory.
- **Software rendering only.** Use `cmd.png()` for output. Never use
`cmd.draw()` or `cmd.ray()` with hardware acceleration — OSMesa does not
support it. Set environment variable `PYOPENGL_PLATFORM=osmesa` for headless
rendering.
- **Always save a `.pse` session file** alongside any PNG output. This lets
the user open the session in their local PyMOL for further inspection.
- **Always call `cmd.quit()`** at the end of every PyMOL script. Omitting it
causes the process to stop responding.
- **Init boilerplate is mandatory.** Every PyMOL script must begin with the
initialization sequence. `from pymol import cmd` must come after
`finish_launching()`, not before.
- See [references/PYMOL_REFERENCE.md](references/PYMOL_REFERENCE.md) for
selection syntax, common commands, and gotchas.
- **Pre-Flight File Check**: Before writing the PyMOL script or running it,
you MUST verify that the requested structure file actually exists on the
host machine.
- **Verify Structure Load**: After loading a structure with `cmd.load()`,
always verify it succeeded by checking `cmd.count_atoms("all")`. If the
result is 0, print an error to stdout and call `cmd.quit()` immediately.
- **Notification**: If this skill is used, ensure this is mentioned in the
output.
## Quick Start
* Ensure structure files are downloaded to a directory in the user's project.
* Write a PyMOL Python script (e.g., `render.py`) with the required init
boilerplate and PEP 0723 header.
* Run it via `uv run`: `bash uv run render.py`
### Minimal example script (`render.py`)
```python
# /// script
# requires-python = ">=3.10, <3.13"
# dependencies = [
# "pymol-open-source-whl",
# ]
# ///
import os
import sys
# Set environment variable for headless rendering
os.environ["PYOPENGL_PLATFORM"] = "osmesa"
import pymol # pytype: disable=import-error
pymol.pymol_argv = ["pymol", "-cq"]
pymol.finish_launching()
from pymol import cmd # pytype: disable=import-error
cmd.load("AF-P00520-F1-model_v4.cif", "structure")
cmd.show("cartoon")
cmd.color("green", "ss h")
cmd.color("yellow", "ss s")
cmd.color("gray", "ss l+''")
cmd.orient()
cmd.set("ray_opaque_background", 1)
cmd.png("output/render.png", width=1200, height=900, dpi=150)
cmd.save("output/session.pse")
cmd.quit()
```
## Common Recipes
See [references/RECIPES.md](references/RECIPES.md) for complete, copy-paste
ready recipes. Available recipes:
- **Cartoon with secondary structure coloring** — basic helix/sheet/loop
coloring
- **B-factor (pLDDT) coloring** — continuous spectrum coloring by B-factor
- **AlphaFold pLDDT coloring** — canonical threshold-based confidence colors
- **Highlight specific residues** — show active site or key residues as sticks
- **Surface rendering** — transparent surface over cartoon
- **Electrostatic surface rendering** — vacuum electrostatics (qualitative)
- **Multi-chain complex colors** — automatic per-chain coloring
- **B-factor putty analysis** — tube width proportional to flexibility
- **Cavity and pocket visualization** — surface cavity detection with ligand
focus
- **Multi-structure batch rendering** — render a directory of structures
- **Measure distance between residues** — CA–CA distance with labels
- **Zoom into binding pocket** — simple pocket focus
- **Protein-ligand interaction** — ligand isolation, styled rendering, polar
contacts
- **Two-structure superposition with RMSD** — align/cealign with auto-fallback
- **In silico mutagenesis** — mutate residues with the mutagenesis wizard
- **Load and modify an existing session** — re-open a `.pse` file
## Interpreting Output
- The `output/` directory contains PNG images and a `.pse` session file.
- Any measurements or metrics (distances, RMSD, atom counts) are printed to
stdout by the PyMOL script. Report these values to the user.
- Present PNG images to the user and describe the visualization.
- Tell the user they can open the `.pse` file in their local PyMOL to further
explore, rotate, or modify the visualization.
- If the user wants modifications, load the saved `.pse` in a new script and
re-run.
- Large sessions with surfaces can exceed the `--max_output_mb` limit (default
500 MB). Increase it with `--max_output_mb=1000` if needed.