moleculekit.tools.nonstandard_residues module#
Discovery helper for non-standard residues that need user-driven AMBER parameterization before building.
A “non-standard residue” is any residue whose resname is not in
moleculekit’s canonical amino-acid / nucleic / water / ion sets.
detectNonStandardResidues() inspects the molecule (without
mutating it) and returns one spec per non-standard residue, plus one
per canonical residue covalently bonded to a non-canonical one:
ChainResidueSpec- one spec per chain-resident residue that needs special handling during AMBER parameterization: a non-canonical amino acid embedded in a polypeptide chain (selenomethionine, norleucine, a stapled NCAA, etc.) OR a canonical amino acid whose sidechain is covalently bonded to something other than its peptide neighbours (a Cys forming a thioether or disulfide, a Glu CD - Lys NZ isopeptide).
ScaffoldSpec- free non-canonical residue with two or more non-peptide bonds to other residues (the central scaffold of a bicyclic peptide, a multi-anchor covalent inhibitor).
CovalentLigandSpec- free non-canonical residue with exactly one non-peptide bond to another residue (single-anchor covalent inhibitor, single-Cys heme).
LigandSpec- free non-canonical residue with no covalent bonds (small-molecule binding-pocket ligand, fatty acid).
GlycanSpec- one spec per sugar residue of a glycan (N- or O-linked, or a free oligosaccharide). The protein residue a glycan’s stem sugar attaches to (an Asn N-glycosylated by a sugar, a Ser/Thr/Hyp O-glycosylated by one) never gets a spec of its own; its rename travels on the stem sugar’sanchor_*fields instead.
Pass the spec list to moleculekit.tools.preparation.systemPrepare()
via detect_specs=specs to apply the proposed renames + H-drops on
the prepared molecule.
- class moleculekit.tools.nonstandard_residues.ChainResidueSpec(resname, residue, new_resname=None, anchor_atom=None, is_n_term=False, is_c_term=False)#
Bases:
objectOne spec per chain-resident residue that needs special handling during AMBER parameterization: a non-canonical amino acid embedded in a polypeptide chain (selenomethionine, norleucine, a stapled NCAA, etc.) OR a canonical amino acid whose sidechain is covalently bonded to something other than its peptide neighbours (a Cys forming a thioether or disulfide, a Glu CD - Lys NZ isopeptide). A glycan-anchor residue (Asn N-glycosylated by a sugar, a Ser/Thr/Hyp O-glycosylated by one) is excluded: it gets no spec of its own, see
GlycanSpec.Fields:
resname: the residue’s resname in the inputMolecule("GLU","NLE","CYS", …).residue:UniqueResidueIDfor the residue (segid / chain / resid / insertion).new_resname: the resname to rename to before downstream parameterization. Set whenever a rename is needed:Canonical AA at a junction:
"CYX"for both ends of a CYS-SG <-> CYS-SG disulfide; an auto-generated 3-charXX#name otherwise, shared across residues sharing the bucket(resname, anchor_atom, partner_resname, n_term, c_term)so antechamber runs once per unique chemistry.NCAA appearing with multiple terminus configurations: the existing
_disambiguate_terminus_resnames()prefixes"N"/"C"/"B"so each terminus form gets its own prepi (otherwise tLeap’s secondloadAmberPrepwould clobber the first).Nonewhen no rename is needed (plain mid-chain NCAA, single-terminus-config NCAA, etc.).
anchor_atom: the name of the residue’s sidechain atom that participates in a non-peptide inter-residue bond ("SG"for a Cys-thioether,"CD"for a Glu CD-LYS NZ isopeptide,"NZ"for the Lys end of the same isopeptide,"CE"for an NLE staple, …).Nonewhen the residue has no non-peptide bond (plain chain-resident NCAA);anchor_atom is not Noneis the single source of truth for “is this residue at a non-peptide junction?” - what the oldCrosslinkedNCAASpec/NCAASpecdistinction encoded. For residues with multiple non-peptide partners the detector picks the deterministically-first partner (sorted by partner residue index, then anchor atom name). For canonical-AA renamed entries this is also the partner used as the bucket key; for NCAA entries (where there’s no bucket key) the same deterministic order applies.is_n_term/is_c_term: chain termini flags.
- residue: UniqueResidueID#
- class moleculekit.tools.nonstandard_residues.CovalentLigandSpec(resname, residue)#
Bases:
objectA non-canonical residue that is not peptide-bonded into a chain and has exactly one non-peptide bond going out to another residue. Examples: a single-anchor covalent inhibitor, a single-Cys heme. A sugar residue is excluded even though it fits this shape: it always gets a
GlycanSpecinstead.- residue: UniqueResidueID#
- class moleculekit.tools.nonstandard_residues.GlycanSpec(resname, residue, new_resname=None, atom_renames=<factory>, anchor_residue=None, anchor_new_resname=None, anchor_atom=None, free_reducing_end=False)#
Bases:
objectOne spec per sugar residue of a glycan (N- or O-linked, or a free oligosaccharide with no protein anchor at all). Emitted instead of
ScaffoldSpec/CovalentLigandSpec/LigandSpecfor every residue recognized inmoleculekit.tools.glycans.GLYCAM_SUGARS. The protein residue a glycan’s stem sugar attaches to (ASN / SER / THR / HYP, seemoleculekit.tools.glycans.GLYCAN_ANCHORS) never gets a spec of its own; its rename travels on the stem sugar’sanchor_*fields instead.Fields:
resname: the sugar’s resname in the inputMolecule("NAG","BMA","SIA", …).residue:UniqueResidueIDfor the residue (segid / chain / resid / insertion).new_resname: the 3-character GLYCAM-06 unit name to rename to (e.g."4YB"), built bymoleculekit.tools.glycans.glycamResname()from the sugar identity and the positions other sugars are linked onto it at. Its first character encodes those positions, recoverable withmoleculekit.tools.glycans.linkedPositionsFromGlycamResname().atom_renames: PDB atom name -> GLYCAM atom name for this sugar’s own substituent atoms (the N-acetyl / N-glycolyl carbons), copied frommoleculekit.tools.glycans.SugarTemplate.atom_renames. Empty for sugars with no such substituent.anchor_residue:UniqueResidueIDof the protein residue this sugar’s anomeric carbon bonds to, set only on a stem sugar directly attached to the protein.Nonefor every other sugar in the tree (linked onto a parent sugar instead) and for a free oligosaccharide with no protein anchor.anchor_new_resname: the GLYCAM anchor-residue nameanchor_residuemust be renamed to ("NLN","OLS","OLT", or"OLP"; seemoleculekit.tools.glycans.GLYCAN_ANCHORS).Nonewhenanchor_residueisNone.anchor_atom: name of the anchor residue’s side-chain atom the anomeric carbon bonds to (e.g."ND2").Nonewhenanchor_residueisNone.free_reducing_end: True when nothing is bonded to this sugar’s own anomeric carbon (a free reducing end), regardless of whether the anomeric hydroxyl atom itself is resolved in the input structure.
- anchor_residue: UniqueResidueID | None = None#
- residue: UniqueResidueID#
- class moleculekit.tools.nonstandard_residues.LigandSpec(resname, residue)#
Bases:
objectA non-canonical residue with no covalent bonds to any other residue (a free, non-covalently bound ligand). Examples: small-molecule drug ligands in binding pockets, fatty acids, lipid head-groups. The parameterizer treats it standalone with no caps.
- residue: UniqueResidueID#
- class moleculekit.tools.nonstandard_residues.ScaffoldSpec(resname, residue)#
Bases:
objectA non-canonical residue that is not peptide-bonded into a chain and has two or more non-peptide bonds going out to other residues. Examples: the central scaffold of a bicyclic / tricyclic peptide, a multi-anchor covalent inhibitor.
- residue: UniqueResidueID#
- class moleculekit.tools.nonstandard_residues.TemplateMismatchReport(resname, copies, heavy_atoms_structure, heavy_atoms_template, name=None, formula=None)#
Bases:
objectWhat
diagnoseTemplateMismatch()found for one failing residue.str()renders it as a single human-readable line naming the compound and comparing per-copy heavy-atom counts, e.g.03P: <name> [<formula>] - 1 copy(ies), 15 heavy atoms per copy in the structure vs 25 in the template.
- moleculekit.tools.nonstandard_residues.applyResidueTemplates(mol, residue_templates, specs)#
Template each residue named in
residue_templates, routed per residue.Each
residue_templatesentry provides exactly one ofsmiles/fileand is applied to the specific residue(s) identified byspecs(as returned bydetectNonStandardResidues()) rather than to every residue that merely shares a resname. AChainResidueSpecis keyed bynew_resname or resname: the detector prefixesresnamewith"N"/"C"/"B"when the same non-canonical amino acid appears with more than one terminus configuration inmol, so a caller can supply a different template per context (e.g. a free-acid C-terminal SMILES vs. a mid-chain, amide-bonded one). When no template is registered under a prefixed key, the plainresnamekey is used as a fallback and a warning is logged, since the same template is then applied to a terminus-specific context it was not necessarily written for. Any other spec type (ScaffoldSpec,CovalentLigandSpec,LigandSpec) is keyed by its plainresnameand applied once to every atom of that resname (all copies templated together).A
.cif/.sdffilevalue is loaded as a reference Molecule and matched viatemplateResidueFromMolecule(.cifby atom name,.sdfconverted to SMILES internally and matched by graph); asmilesvalue is matched viatemplateResidueFromSmiles. Either way the source supplies bonds, bond orders, formal charges and hydrogens and is never appended to the structure. A spec with no matching template entry (after the fallback) is left untouched. Conversely, anyresidue_templateskey that never matched a spec (typo’d or stale, e.g. naming a terminus context that was not actually detected) logs a warning naming the key rather than being silently ignored.Run this before
moleculekit.tools.preparation.systemPrepare(), and pass it the same spec list you then hand tosystemPrepare– do not re-detect afterwards. Templating changes no field any spec carries: it assigns intra-residue bonds, bond orders, formal charges and hydrogens, while every spec field is decided by resname and inter-residue connectivity. Re-detecting is not merely redundant, it loses specs on an input whose bonds were not explicit: templating leaves the molecule with bonds for the templated residues only, which is enough fordetectNonStandardResidues()to stop distance-guessing, so the disulfides and isopeptides elsewhere in the structure go undetected.- Parameters:
mol (
Molecule) – The structure to template, mutated in place.residue_templates (
dict[str,dict]orNone) – Mapping of a residue name (or terminus-prefixed residue name) to a{"smiles": ...}/{"file": ...}spec.Noneor empty is a no-op.specs (
list) – Per-residue specs, as returned bydetectNonStandardResidues().
Examples
>>> specs = detectNonStandardResidues(mol) >>> applyResidueTemplates(mol, {"NLE": {"smiles": "CCCC[C@@H](C=O)N"}, ... "CNLE": {"file": "cnle.cif"}}, specs)
- moleculekit.tools.nonstandard_residues.detectNonStandardResidues(mol, guess_bonds=True)#
Walk
moland emit one spec per residue that needs special handling by a downstream parameterizer / builder.Inspects
mol.bonds(without mutating the molecule) and classifies every non-canonical residue plus every canonical residue at a non-peptide junction into one of five spec types:ChainResidueSpec— chain-resident residue that needs special parameterization: a non-canonical amino acid embedded in a polypeptide chain (selenomethionine, norleucine, stapled-peptide residues, …) OR a canonical AA whose sidechain is covalently bonded to anything other than its peptide neighbours (Cys-Cys disulfide, Cys thioether to a heme, Glu-Lys isopeptide, Tyr coordinating a metal, …). Canonical AAs at a junction always receivenew_resname:"CYX"for both ends of a CYS-SG <-> CYS-SG disulfide; an auto-generated 3-charXX#name otherwise. Residues that share the same(canonical_resname, anchor_atom, partner_resname, is_n_term, is_c_term)bucket key collapse to the sameXX#so the parameterizer emits one prepi shared across them. Chain-terminal forms get their own buckets because they carry extra atoms (OXTon the C-terminus,H1/H2/H3on the N-terminus) and different charges. A glycan-anchor residue (Asn N-glycosylated by a sugar, a Ser/Thr/Hyp O-glycosylated by one) is excluded from this bucketing entirely: seeGlycanSpecbelow.ScaffoldSpec— non-chain-resident residue with 2+ non-peptide bonds (bicyclic-peptide central scaffold, multi-anchor covalent inhibitor).CovalentLigandSpec— non-chain-resident residue with exactly one non-peptide bond (single-anchor covalent inhibitor, single-Cys heme).LigandSpec— non-chain-resident residue with no covalent bonds (free small-molecule ligand, fatty acid).GlycanSpec- a sugar residue recognized inmoleculekit.tools.glycans.GLYCAM_SUGARS(N-linked, O-linked, or a free oligosaccharide), regardless of how many non-peptide bonds it carries. The protein residue a glycan’s stem sugar attaches to gets no spec of its own; its rename travels on the stem sugar’sanchor_*fields instead.
Chain-resident NCAAs that appear with more than one terminus configuration in the same molecule are disambiguated post hoc by setting
new_resnameon the terminal specs ("N"+resnamefor N-term,"C"+resnamefor C-term,"B"+resnamefor a single- residue chain). When every instance of an NCAA shares the same terminus configuration,new_resnamestaysNone.Metal-coordination contacts where the metal is a standalone ion residue (e.g. PDB
LINKrecords between a Zn²⁺ residue and a Zn-chelating inhibitor, or 3PTB’s Ca²⁺ coordinated by GLU/ASN/VAL oxygens) are skipped — the inhibitor stays a freeLigandSpec, and the protein residues are left alone. Coordinations where the metal lives inside a cofactor (e.g. Fe inside HEM coordinated to a Tyr-OH or Cys-SG) are kept: the cofactor becomes aCovalentLigandSpecand the donating canonical AA becomes aChainResidueSpec, because the donor’s protonation state changes (Tyr-O⁻, Cys-S⁻) and needs a custom prepi. Bonds touching water are always skipped.Note
Plain Cys–Cys disulfides are not returned as separate specs for the caller to process. Both Cys residues are instead silently renamed to
CYXinsidemoleculekit.tools.preparation.systemPrepare()(which calls this function internally). TheChainResidueSpecentries for disulfide-bonded cysteines exist only to carry thenew_resname="CYX"rename; the parameterization of the S–S bond is handled by AMBER’s built-in CYX template, so no user intervention is required.- Parameters:
mol (
moleculekit.molecule.Molecule) – Input molecule. Should already carry covalent bonds (read from a PDBCONECTblock, a CIF_struct_connblock, or set up viaMolecule.templateResidueFromSmiles()). Ifmol.bondsis empty andguess_bondsis True, the detector falls back to distance-based bond guessing viamol._guessBonds()and logs a warning. The molecule is not mutated.guess_bonds (
bool) – Whenmol.bondsis empty, guess bonds from atom coordinates so crosslinks (disulfides, glycosidic bonds, …) can still be found. Set to False to skip guessing and rely only on explicit input bonds: useful for modelled structures whose slightly-off geometry produces spurious close contacts that would otherwise be flagged as bonds. When guessing is on, non-peptide bonds landing on a canonical amino acid’s backboneO/CA(atoms that never form a real crosslink) are treated as guessing artifacts and ignored; explicit input bonds are always trusted.
- Returns:
Flat list mixing
ChainResidueSpec,ScaffoldSpec,CovalentLigandSpec,LigandSpec, andGlycanSpecentries. Ordered by residue index inmol. Empty when the molecule has no non-standard residues and no sidechain crosslinks.- Return type:
list[PerResidueSpec]- Raises:
RuntimeError – If a canonical residue is bonded at an anchor atom that is not listed in
moleculekit.tools._anchor_variants.ANCHOR_TABLE(the anchor needs to be registered there before the residue can be re-templated). Also raised if an NCAA resname is 4+ characters long and requires terminus-disambiguation prefixing, which would exceed AMBER’s 4-character prepi unit-name limit.
Examples
Detect non-standard residues, template them with SMILES, then prepare:
>>> from moleculekit.molecule import Molecule >>> from moleculekit.tools.nonstandard_residues import detectNonStandardResidues >>> from moleculekit.tools.preparation import systemPrepare >>> mol = Molecule("3ptb") >>> specs = detectNonStandardResidues(mol)
For a molecule that has a non-canonical residue (e.g. “LIG”) that needs SMILES-based templating before preparation:
>>> # Template the non-canonical residue with its SMILES >>> lig_mask = mol.resname == "LIG" >>> mol.remove("hydrogen") >>> mol.templateResidueFromSmiles(lig_mask, smiles="...", addHs=True) >>> # Now pass the specs so systemPrepare does not re-detect >>> pmol, specs = systemPrepare(mol, detect_specs=specs)
When no non-standard-residue handling is needed, pass specs directly:
>>> pmol, specs, df = systemPrepare(mol, return_details=True)
- moleculekit.tools.nonstandard_residues.diagnoseTemplateMismatch(mol, resname, smiles)#
Diagnose why a residue template failed to match the structure’s atoms.
When a builder raises because a template SMILES “contains heavy atoms which could not be matched to the residue”, this names the molecule and compares how many heavy atoms each copy actually has in the structure against how many the template defines - a residue resolved to a fraction of its own definition is unmodelled density, not different chemistry. The RCSB component lookup is best-effort: a resname RCSB does not know (e.g. a docking tool’s
LIG/UNL) still gets its counts diagnosed, withname/formulaleftNone.- Parameters:
- Returns:
report – Copy count, per-copy heavy-atom count in the structure, heavy-atom count of the template, and the RCSB component name/formula when available. Print it for a one-line human-readable summary.
- Return type:
- Raises:
ValueError – If
smilescannot be parsed or no residue namedresnameexists inmol.
- moleculekit.tools.nonstandard_residues.geometric_interresidue_links(mol, atoms_a, atoms_b, frame=None, amide_dist=None, phosphodiester_dist=None)#
Return the geometric inter-residue covalent links between two residues as a list of
(idx_a, idx_b, kind)tuples, whereidx_ais an atom ofatoms_a,idx_ban atom ofatoms_b, andkindis one of:"peptide": a backboneCof one residue withinamide_distof the other’s backboneN(the standard main-chain amide)."isopeptide": an amide where one partner is a backboneNorCand the other is a SIDE-CHAIN carbon or nitrogen - a side-chain carbonyl acylating a backbone N (gamma-glutamyl / beta-aspartyl, e.g. microcystin’s ACB.CG->N) or a backbone carboxyl acylating a side-chain amino (epsilon-poly-lysine, C->NZ)."phosphodiester": anO3'/C3'withinphosphodiester_distof aP.
This is the single shared definition of inter-residue geometry consulted by
autoSegment(segment grouping), residue templating (boundary-atom H reduction),infer_nonstandard_junction_bonds()andsystemPrepare(terminus assignment), so they agree on one geometry. It is a pure geometric read:mol.bondsis NOT consulted andmolis not modified. Deposited bonds are handled separately by each caller because they carry different trust: a deposited backbone bond is honored ungated (it is real and in the file), whereas a geometric isopeptide is gated to non-canonical junctions (proximity alone must not invent a crosslink between two standard residues).- Parameters:
mol (
Molecule) – The molecule. Not modified.atoms_a (
numpy.ndarray) – Atom-index array of the first residue to test.atoms_b (
numpy.ndarray) – Atom-index array of the second residue to test.frame (
int) – Coordinate frame to use; defaults tomol.frame.amide_dist (
float) – Max C-N separation for a peptide / isopeptide link. Defaults toAMIDE_LINK_DIST.phosphodiester_dist (
float) – Max O3’/C3’-P separation for a phosphodiester link. Defaults toPHOSPHODIESTER_LINK_DIST.
- Returns:
links –
(idx_a, idx_b, kind)tuples, one per detected link.- Return type:
- moleculekit.tools.nonstandard_residues.getResidueMask(mol, spec)#
Boolean mask over
molselecting the atoms ofspec’s residue.Matches on the residue identity (segid, chain, resid, insertion) and its resname, accepting either the original
spec.resnameor, when set, the renamedspec.new_resname. The same call therefore works whethermolis the structure the spec was detected in (its resname isspec.resname) or one that has already been through the detect-spec renames (its resname isspec.new_resname).- Parameters:
mol (
Molecule) – The molecule the spec was detected in, before or after renaming.spec (
ChainResidueSpecorScaffoldSpecorCovalentLigandSpecorLigandSpecorGlycanSpec) – A spec returned bydetectNonStandardResidues().
- Returns:
mask – Boolean mask, True on the atoms of
spec’s residue.- Return type:
- moleculekit.tools.nonstandard_residues.infer_nonstandard_junction_bonds(mol, max_dist=1.8)#
Infer inter-residue backbone-continuation bonds that the input connectivity omits, at junctions involving a non-canonical residue.
Some deposited structures carry a non-standard residue whose backbone is continued by an undeposited amide bond - a side-chain isopeptide (microcystin’s beta-methyl-Asp CG acylating the next residue’s backbone N) or the reverse, a backbone carboxyl acylating the next residue’s side-chain amino (as in epsilon-poly-lysine, alpha-C -> Lys NZ). Without that bond
autoSegmentsplits the chain anddetectNonStandardResiduescannot find the anchor. This recovers it from geometry WITHOUT modifyingmol; callers fold the result into their own connectivity analysis transiently.For each pair of consecutive residues (file order, same chain) that carry NO inter-residue bond between them and where at least one residue is non-canonical, a single amide C-N bond is inferred when a backbone atom of one residue lies within
max_distof a complementary heavy atom of the other:the later residue’s backbone
Nto the nearest carbon of the earlier residue (side-chain or backbone carboxyl -> backbone amino), orthe earlier residue’s backbone
Cto the nearest nitrogen of the later residue (backbone carboxyl -> side-chain or backbone amino).
Two canonical residues with a missing bond are left untouched (a real chain gap is never invented). Requiring a backbone N/C endpoint excludes pure side-chain crosslinks (disulfides, staples), which are not chain continuations.
- moleculekit.tools.nonstandard_residues.requiresTemplate(spec)#
Whether a detected residue spec needs a user-supplied template.
True for genuinely non-standard residues (free ligands, non-canonical amino acids, scaffolds), i.e. those needing a SMILES or CIF template to add bonds, bond orders and hydrogens before parameterization. False for a canonical residue the detector reports only because it was renamed at a covalent junction (its resname stays canonical), for force-field-shipped modified residues, and for a
GlycanSpec: a sugar’s resname is not canonical, but it gets its pdb2pqr topology from a shipped GLYCAM residue CIF rather than from a user-supplied SMILES/CIF template.- Parameters:
spec (
ChainResidueSpecorScaffoldSpecorCovalentLigandSpecorLigandSpecorGlycanSpec) – A spec returned bydetectNonStandardResidues().- Returns:
True if
specrequires a supplied template.- Return type:
- moleculekit.tools.nonstandard_residues.residuesRequiringTemplate(mol, guess_bonds=True)#
Return the resnames in
molthat need a user-supplied template.Runs
detectNonStandardResidues()and keeps the residues that are genuinely non-standard (free ligands, non-canonical amino acids and scaffolds), i.e. the ones for which a SMILES or CIF template must be supplied to add bonds, bond orders and hydrogens before they can be parameterized. Canonical residues that the detector reports only because they were renamed at a covalent junction (a disulfideCYS->CYX, …) keep their canonical resname and are excluded, since the force field already provides templates for them. Modified residues the force field ships (MSE,SEP, …) are canonical here too and are likewise excluded. Sugar residues (GlycanSpec) are excluded as well: they get their pdb2pqr topology from a shipped GLYCAM residue CIF, never from a user-supplied template.- Parameters:
mol (
Molecule) – The molecule to inspect.guess_bonds (
bool) – Passed through todetectNonStandardResidues().
- Returns:
resnames – The sorted, unique resnames that require a supplied template.
- Return type: