Coot Essential API Functions

This document contains the core Coot API functions needed for typical validation and model-building workflows. Reading these function signatures at session start eliminates the need for searching.

Setup & Configuration

coot.set_refinement_immediate_replacement(1)
# CRITICAL: Call this before any refinement operations to make them synchronous
# Without this, refinement results may not be available immediately

coot.set_imol_refinement_map(imol_map)
# CRITICAL: Call this to tell Coot which map to use for refinement.
# Must be called once per session (or whenever the map changes).
# Without this, refine_residues_py() will fail silently.
# Example: coot.set_imol_refinement_map(1)

Molecule Management

coot.is_valid_model_molecule(imol) -> int  # Returns 1 if valid model, 0 otherwise
coot.is_valid_map_molecule(imol) -> int    # Returns 1 if valid map, 0 otherwise
coot.molecule_name(imol) -> str            # Returns the molecule filename/description
coot.n_chains(imol) -> int                 # Returns number of chains
coot.coot_version() -> str                 # Returns Coot version string
coot.load_tutorial_model_and_data()        # Loads tutorial RNase structure + maps

# Download structures and data from PDBe
coot.network_get_accession_code_entity(pdb_accession_code, mode)
# Downloads model and/or structure factors from PDBe
# Parameters:
#   pdb_accession_code: str - PDB accession code (e.g., "4wa9")
#   mode: int - 0 for coordinates (.pdb or .cif), 1 for structure factors (.mtz)
# Example - fetch both model and data:
#   coot.network_get_accession_code_entity("4wa9", 0)  # Get coordinates
#   coot.network_get_accession_code_entity("4wa9", 1)  # Get structure factors to make a map

Checkpoints - Model State Management

When experimenting with the various model building tools available to address a particular model-building problem it is useful to create backup checkpoints. This allows you to return to a particular state, so that you can try alternative model-building parameters or functions, or combinations of functions.

coot.make_backup_checkpoint(imol, description_string) -> int
# Creates a named checkpoint of the molecule's current state
# Returns: checkpoint index for later restoration
#
# Parameters:
#   imol: Model molecule index
#   description_string: Human-readable description (e.g., "before rotamer fix")
#
# CRITICAL: Always checkpoint before experimental or risky operations
#
# Example:
checkpoint_idx = coot.make_backup_checkpoint(0, "before rotamer fix")
coot.auto_fit_best_rotamer(0, "A", 42, "", "", 1, 1, 0.01)
# ... check if improvement worked ...
if not improved:
    coot.restore_to_backup_checkpoint(0, checkpoint_idx)

coot.restore_to_backup_checkpoint(imol, checkpoint_index)
# Restores molecule to a previously saved checkpoint state
# Parameters:
#   imol: Model molecule index
#   checkpoint_index: Index returned by make_backup_checkpoint()

coot.compare_current_model_to_backup(imol, checkpoint_index) -> dict
# Compares current model state to a checkpoint to see what changed
# Parameters:
#   imol: Model molecule index
#   checkpoint_index: Index of checkpoint to compare against

Chain and Residue Information

# Requires: import coot_utils
coot_utils.chain_ids(imol) -> list         # Returns list of chain IDs, e.g., ['A', 'B']

# Direct C++ functions (preferred when possible)
coot.chain_id_py(imol, chain_index) -> str # Get chain ID by index

Secondary Structure Information

coot.get_header_secondary_structure_info(imol) -> dict
# Returns secondary structure from PDB header
# Returns: {'helices': [...], 'strands': [...]}
#
# Each helix dict contains:
#   serNum, helixID, initChainID, initSeqNum, endChainID, endSeqNum, length, comment
#
# Each strand dict contains:
#   SheetID, strandNo, initChainID, initSeqNum, endChainID, endSeqNum
#
# Example - get beta barrel strands:
ss = coot.get_header_secondary_structure_info(0)
if 'strands' in ss:
    for strand in ss['strands']:
        print(f"Strand {strand['strandNo']}: {strand['initSeqNum']}-{strand['endSeqNum']}")
coot.set_go_to_atom_chain_residue_atom_name(chain_id, resno, atom_name) -> int
# Centers view on specified atom. Returns 1 on success.
# Example: coot.set_go_to_atom_chain_residue_atom_name("A", 42, "CA")

coot.closest_atom_simple_py() -> list
# Returns: [imol, chain_id, resno, ins_code, atom_name, alt_conf]
# Gets the atom closest to screen center across all displayed molecules

coot.closest_atom_py(imol) -> list
# Same as above but for specific molecule

coot.active_atom_spec_py() -> list
# Returns the currently "active" atom specification (or False if none found)
# (found, (imol, atom_spec))
#   found: Boolean indicating if an atom exists close to the center
#   molecule_number: Integer molecule ID
#   atom_spec: List [chain_id, resno, ins_code, atom_name, alt_conf]


Residue Inspection

coot.residue_info_py(imol, chain_id, resno, ins_code) -> list
# Returns detailed atom information for a residue
#
# Parameters:
#   imol: Model molecule index
#   chain_id: Chain identifier (e.g., "A")
#   resno: Residue number
#   ins_code: Insertion code (use "" if none)
#
# Returns: List of atom entries, each containing:
#   [[atom_name, alt_conf], [occupancy, b_factor, element, ?], [x, y, z], atom_index]
#
# Example output for a complete CYS:
#   [[' N  ', ''], [1.0, 12.5, ' N', ''], [x, y, z], 100],
#   [[' CA ', ''], [1.0, 11.2, ' C', ''], [x, y, z], 101],
#   [[' CB ', ''], [1.0, 14.3, ' C', ''], [x, y, z], 102],
#   [[' SG ', ''], [1.0, 18.1, ' S', ''], [x, y, z], 103],  # Sulfur!
#   [[' C  ', ''], [1.0, 10.8, ' C', ''], [x, y, z], 104],
#   [[' O  ', ''], [1.0, 11.0, ' O', ''], [x, y, z], 105]

#
# NOTE: b_factor may be a list [b_iso, B11, B22, B33, B12, B13, B23] for anisotropic
# Always handle safely:
#   def get_b(atom): b = atom[1][1]; return b[0] if isinstance(b, list) else b


# Check for missing atoms in a residue
atoms = coot.residue_info_py(0, "A", 72, "")
atom_names = [a[0][0].strip() for a in atoms]
print(f"Atoms present: {atom_names}")

# Expected atoms for common residues
expected_atoms = {
    'CYS': ['N', 'CA', 'CB', 'SG', 'C', 'O'],
    'ILE': ['N', 'CA', 'CB', 'CG1', 'CG2', 'CD1', 'C', 'O'],
    'GLY': ['N', 'CA', 'C', 'O'],
    'PHE': ['N', 'CA', 'CB', 'CG', 'CD1', 'CD2', 'CE1', 'CE2', 'CZ', 'C', 'O'],
}

# Find missing atoms
res_type = coot.residue_name_py(0, "A", 72, "")
if res_type in expected_atoms:
    missing = [a for a in expected_atoms[res_type] if a not in atom_names]
    if missing:
        print(f"WARNING: Missing atoms in {res_type}: {missing}")

Validation - Density Fit

coot.map_to_model_correlation_stats_per_residue_range_py(
    imol,           # Model molecule number
    chain_id,       # Chain identifier (e.g., "A")
    imol_map,       # Map molecule number
    n_per_range,    # Residues per window (use 1 for per-residue)
    exclude_NOC     # 0=include all atoms, 1=exclude backbone N,O,C
) -> list
# Returns: [[all_atom_stats], [sidechain_stats]]
# Each stats list: [[residue_spec, [n_points, correlation]], ...]
# residue_spec = [chain_id, resno, ins_code]

# Example - find worst fitting residues:
stats = coot.map_to_model_correlation_stats_per_residue_range_py(0, "A", 1, 1, 0)
all_atom = stats[0]
worst = sorted(all_atom, key=lambda x: x[1][1])[:5]  # 5 worst by correlatio

# Mainchain vs sidechain correlation for a single residue:
coot.map_to_model_correlation_py(imol, residue_specs, neighb_specs, atom_mask_mode, imol_map)
# atom_mask_mode: 0=all atoms, 1=mainchain only, 2=sidechain only
# Use to distinguish backbone problems from sidechain problems before choosing a fix

# Per-atom density probing — the most powerful backbone diagnostic:
sigma = coot.map_sigma_py(imol_map)
d = coot.density_at_point(imol_map, x, y, z) / sigma  # value in sigma units
# Backbone atom < 0.5σ = problem; carbonyl O near 0σ with good CA = pepflip needed

Validation - Geometry

coot.all_molecule_ramachandran_score_py(imol) -> list
# Returns a list of exactly 6 elements (confirmed from C++ source):
#   [0]: overall score (float)
#   [1]: n_residues (int)
#   [2]: score_non_sec_str (float)
#   [3]: n_residues_non_sec_str (int)
#   [4]: n_zeros (int)
#   [5]: per-residue list (list of per-residue entries)
#
# Each per-residue entry: [[phi, psi], [chain_id, resno, ins_code], probability, [prev_resname, this_resname, next_resname]]
# NOTE: rama_data[5] and rama_data[-1] are equivalent and both correct.
# LOW probability = BAD (outlier). Outlier threshold: prob < 0.02
#
# Example:
#   per_res = coot.all_molecule_ramachandran_score_py(imol)[5]
#   outliers = [r for r in per_res if r[2] < 0.02]
#   worst = min(per_res, key=lambda x: x[2])

coot.rotamer_graphs_py(imol) -> list
# Returns: [[chain_id, resno, ins_code, score_percentage, resname], ...]
# LOW score = BAD rotamer

coot.molecule_atom_overlaps_py(imol, n_pairs) -> list
# Returns worst n_pairs atom overlaps (use -1 for all)
# Each overlap: {
#   'atom-1-spec': [imol, chain, resno, ins, atom_name, alt],
#   'atom-2-spec': [imol, chain, resno, ins, atom_name, alt],
#   'overlap-volume': float  # in ų, >5.0 is severe
# }

Validation - Unmodeled Density

coot.find_blobs_py(imol_model, imol_map, sigma_cutoff) -> list
# Finds unmodeled density blobs
# Returns: [[position, score], ...]
# position is a list or 3 floats, (for x, y, z)
# Use sigma_cutoff=3.0 for difference maps, 1.0 for 2mFo-DFc
# Higher score = larger/stronger blob

Validation - Hydrogen Bonds

coot.get_hydrogen_bonds_py(imol, selection_1, selection_2, mcdonald_and_thornton) -> list
# Find hydrogen bonds between two atom selections.
# selection_1, selection_2: MMDB selection strings (e.g. "//A/35", "//A")
#   Note: selection_1 and selection_2 can be the same, e.g. "//A" for intra-chain H-bonds
# mcdonald_and_thornton: 0 if model has no H atoms, 1 if it does
# Returns list of H-bond candidates, each a list of 12 elements:
#   [0]  hydrogen atom (dict or None)
#   [1]  donor atom (dict)
#   [2]  acceptor atom (dict)
#   [3]  donor neighbour atom (dict or None)
#   [4]  acceptor neighbour atom (dict or None)
#   [5]  angle_1 (float, degrees)
#   [6]  angle_2 (float, degrees)
#   [7]  angle_3 (float, degrees)
#   [8]  distance (float, Å)
#   [9]  ligand_atom_is_donor (bool)
#   [10] hydrogen_is_ligand_atom (bool)
#   [11] bond_has_hydrogen_flag (bool)
# Atom dicts have keys: x, y, z, name, element, chain, residue_name, occ, b_iso, altLoc
#
# Example:
hbonds = coot.get_hydrogen_bonds_py(0, "//A/35", "//A", 0)
for hb in hbonds:
    donor    = hb[1]
    acceptor = hb[2]
    dist     = hb[8]
    d_str = donor['chain'] + " " + donor['residue_name'] + " " + donor['name'].strip()
    a_str = acceptor['chain'] + " " + acceptor['residue_name'] + " " + acceptor['name'].strip()
    print("H-bond: " + d_str + " -> " + a_str + "  dist=" + str(dist))

Refinement

coot.refine_residues_py(imol, residue_specs) -> list
# Real-space refinement of specified residues
# residue_specs = [["A", 42, ""], ["A", 43, ""], ...]  # [chain, resno, ins_code]
# Returns: ['', status, lights] where:
#   status: 0=converged, -2=GSL_CONTINUE (call again), 27=no progress
#   lights: list of [name, label, value] refinement statistics, or False
# CRITICAL: if status == -2, call refine_residues_py() again (up to 3 times total)
# Example robust call:
#   for _ in range(3):
#       result = coot.refine_residues_py(imol, specs)
#       if result and result[1] != -2: break
#   accepted = coot.accept_moving_atoms_py()  # get traffic lights

Model Building - Rotamers

coot.auto_fit_best_rotamer(
    imol,              # Model molecule
    chain_id,          # Chain (e.g., "A")
    resno,             # Residue number
    ins_code,          # Insertion code (usually "")
    altloc,            # Alt conf (usually "")
    imol_map,          # Map for density scoring
    clash_flag,        # 1=check clashes, 0=ignore
    lowest_probability # Minimum rotamer probability (e.g., 0.01)
) -> float
# Returns new rotamer score, or -99.9 if residue has no rotamers (GLY, ALA)

Model Building - Backbone

coot.pepflip(imol, chain_id, resno, ins_code, altloc)
# Flips the peptide bond at specified residue
# Use for fixing cis/trans peptide issues or Ramachandran outliers
# or other false minimum backbone conformations.
# Follow with refinement of surrounding residues

CRITICAL: Always Render Validation Results as Interactive SVG Widgets

When presenting validation results, geometry analysis, per-atom density data, or any tabular data about multiple residues, ALWAYS render an interactive SVG widget using visualize:show_widget. Never just print a wall of text.

The user can click on each residue block to navigate directly to it in Coot or trigger a fix. This is far more useful than stdout and makes results immediately actionable.

When to render a widget — trigger situations:

  • After running full validation (Ramachandran, rotamers, density fit, clashes, geometry)
  • After a per-atom backbone density probe scan
  • After any survey comparing multiple residues or chains
  • After a before/after fix comparison showing improvement
  • Any time there are more than ~5 residues worth of results to show

Severity colour coding:

  • c-red — severe issues (Rama score < 0.001, rotamer 0%, corr < 0.3, omega > 20° off)
  • c-amber — moderate issues (Rama 0.001–0.01, rotamer < 5%, corr 0.3–0.65)
  • c-gray — informational / OK residues
  • c-teal — unmodelled density blobs / features to investigate
  • c-green — successfully fixed residues (before/after comparisons)

onclick patterns — make them actionable, not just informational:

# Navigation
onclick="sendPrompt('Go to A/41 GLU and show me the density')"

# Investigation
onclick="sendPrompt('Go to B/257 GLU and investigate — negative correlation')"

# Fix requests
onclick="sendPrompt('Fix the clash between A/2 CA and A/89 CZ')"
onclick="sendPrompt('Try pepflip at B/262 and refine')"
onclick="sendPrompt('Fix rotamer A/32 GLN — 0% score')"

# Comparative
onclick="sendPrompt('Go to A/260 ALA — worst Ramachandran in chain A')"

The onclick prompt should describe the action to take, not just what the residue is. A user clicking a red block should trigger the next useful step automatically.

Minimal widget template for validation results:

<svg width="100%" viewBox="0 0 680 [H]">
<!-- Section header -->
<text class="th" x="40" y="28">Ramachandran outliers</text>

<!-- Severe issue -->
<g class="node c-red" onclick="sendPrompt('Go to A/41 GLU and investigate Ramachandran outlier')">
  <rect x="40" y="38" width="280" height="50" rx="8" stroke-width="0.5"/>
  <text class="th" x="180" y="57" text-anchor="middle" dominant-baseline="central">A/41  GLU</text>
  <text class="ts" x="180" y="74" text-anchor="middle" dominant-baseline="central">score 0.00004  phi=112°</text>
</g>

<!-- Moderate issue -->
<g class="node c-amber" onclick="sendPrompt('Go to A/35 VAL and investigate')">
  <rect x="340" y="38" width="280" height="50" rx="8" stroke-width="0.5"/>
  <text class="th" x="480" y="57" text-anchor="middle" dominant-baseline="central">A/35  VAL</text>
  <text class="ts" x="480" y="74" text-anchor="middle" dominant-baseline="central">score 0.006</text>
</g>
</svg>

Geometry widget — always include ideal, actual, Z score:

For bond/angle/omega distortions, display as rows with severity colour. Omega torsion outliers (|omega − 180°| > 15°) are the most sensitive backbone diagnostic and should always be highlighted — they identify misplaced backbone immediately.

<g class="node c-red" onclick="sendPrompt('Go to A/259 SER and investigate distorted backbone')">
  <rect x="36" y="50" width="600" height="22" rx="4" stroke-width="0.5"/>
  <text class="ts" x="40" y="65">A/258→A/259  omega</text>
  <text class="ts" x="380" y="65">60.0°  (ideal 180°)</text>
  <text class="ts" x="510" y="65" style="fill:#A32D2D">+24.0σ  distorted!</text>
</g>

Typical Validation & Fix Workflow

# 1. Setup
coot.set_refinement_immediate_replacement(1)

# 2. Check what's loaded
for i in range(10):
    if coot.is_valid_model_molecule(i):
        print(f"Model {i}: {coot.molecule_name(i)}")
    if coot.is_valid_map_molecule(i):
        print(f"Map {i}: {coot.molecule_name(i)}")

# 3. Validate density fit
stats = coot.map_to_model_correlation_stats_per_residue_range_py(0, "A", 1, 1, 0)
worst = sorted(stats[0], key=lambda x: x[1][1])[:10]

# 4. Check for clashes
overlaps = coot.molecule_atom_overlaps_py(0, 30)
severe = [o for o in overlaps if o['overlap-volume'] > 5.0]

# 5. Fix bad rotamers
coot.auto_fit_best_rotamer(0, "A", 89, "", "", 1, 1, 0.01)
coot.refine_residues_py(0, [["A", 89, ""]])

# 6. Fix backbone issues
coot.pepflip(0, "A", 41, "", "")
coot.refine_residues_py(0, [["A", 40, ""], ["A", 41, ""], ["A", 42, ""]])

# 7. Re-validate
overlaps_after = coot.molecule_atom_overlaps_py(0, 10)

# 8. ALWAYS render results as an interactive SVG widget — see section above

Important Notes

  1. Always call set_refinement_immediate_replacement(1) first - makes refinement synchronous
  2. Use coot.*_py() functions directly - faster than coot_utils wrappers
  3. Import coot_utils only when needed - for convenience functions like chain_ids()
  4. The coot module is auto-imported - no import statement needed