Source code for prolint.plotting.structure

"""Structure export module.

This module provides functions for exporting contact metrics
to PDB files for visualization in molecular viewers.
"""

from typing import Optional, Literal
import tempfile


[docs] def write_pdb( contacts, metric: Literal["mean", "max", "sum", "occupancy"] = "occupancy", target_resname: Optional[str] = None, filename: Optional[str] = None, frame: int = 0, ) -> str: """Write contact metrics to a PDB file for visualization. Exports query atoms to a PDB file with metric values stored in the B-factor column for coloring in molecular viewers. Parameters ---------- contacts : ComputedContacts Computed contact data. metric : {"mean", "max", "sum", "occupancy"}, default="occupancy" Metric to write to B-factor column. target_resname : str, optional Filter by database residue name (e.g., "CHOL"). filename : str, optional Output filename. If None, creates a temporary file. frame : int, default=0 Trajectory frame to use for coordinates. Returns ------- str Path to the written PDB file. Examples -------- >>> from prolint.plotting import write_pdb >>> pdb_path = write_pdb(contacts, metric="occupancy") >>> # Open in PyMOL/VMD and color by B-factor """ universe = contacts.provider.query.universe query = contacts.provider.query # Compute metric values metrics = contacts.compute_metric(metric, target_resname=target_resname) # Build resid -> value mapping (use global value for each residue) bfactors = {} for resid, db_data in metrics.items(): # Sum across all database types if target_resname is None if target_resname: bfactors[resid] = db_data.get(target_resname, {}).get("global", 0.0) else: # Average across all database types values = [d["global"] for d in db_data.values()] bfactors[resid] = sum(values) / len(values) if values else 0.0 # Set frame universe.trajectory[frame] # Generate filename if not provided if filename is None: fd, filename = tempfile.mkstemp(suffix=".pdb", prefix="prolint_") import os os.close(fd) # Write PDB atoms = query.atoms with open(filename, "w") as f: f.write(f"REMARK Metric: {metric}\n") if target_resname: f.write(f"REMARK Database: {target_resname}\n") f.write(f"REMARK Frame: {frame}\n") if universe.dimensions is not None: dims = universe.dimensions f.write( f"CRYST1{dims[0]:9.3f}{dims[1]:9.3f}{dims[2]:9.3f}" f"{dims[3]:7.2f}{dims[4]:7.2f}{dims[5]:7.2f} P 1 1\n" ) for i, atom in enumerate(atoms): serial = (i + 1) % 100000 name = atom.name[:4].ljust(4) resname = atom.resname[:3].ljust(3) chain = getattr(atom, "chainID", " ") or " " resid = atom.resid % 10000 x, y, z = atom.position occupancy = getattr(atom, "occupancy", 1.0) tempfactor = bfactors.get(atom.resid, 0.0) element = getattr(atom, "element", atom.name[0])[:2].rjust(2) f.write( f"ATOM {serial:5d} {name}{' '}{resname} {chain}{resid:4d} " f"{x:8.3f}{y:8.3f}{z:8.3f}{occupancy:6.2f}{tempfactor:6.2f}" f" {element}\n" ) f.write("END\n") return filename