moleculekit.tools.residue_titration module#

Prepare non-standard residues for pKa titration and fold the result back into per-residue build templates.

The public entry points are capNonstandardResiduesForTitration() and templatesFromTitration(), both dict-in / dict-out ({key: smiles}), with optional file endpoints for tools that communicate over files: capNonstandardResiduesForTitration(outfile=...) writes the titration input as a key,SMILES,base CSV, and templatesFromTitration accepts the pKa tool’s output CSV path in place of the dict. The only assumption made about the tool (e.g. AcePka) is that it rewrites the SMILES column and echoes every other column through, which makes the round-trip self-contained: the echoed base column guarantees cap-stripping re-derives each anchor from exactly the base SMILES that was titrated, with no RCSB re-fetch in between.

Each residue is built from its full base SMILES (an RCSB ligand descriptor or a caller override), so it carries the complete chemistry even when the deposited structure has a trimmed sidechain. Every outgoing inter-residue bond is then capped with an inert stand-in for its real partner, so the pKa tool sees a chemically sane molecule that titrates the residue’s own groups in a faithful environment. Most caps are neutral (an amide, a methyl); a phosphodiester partner is kept as a phosphate, so an internal nucleotide carries the real, charged backbone environment a terminal one would not:

  • a backbone peptide bond gets an amide cap - acetyl on the N side, N-methyl on the carbonyl C side (the ACE / NME that keep a mid-chain backbone neutral);

  • a sidechain / scaffold crosslink gets an inert cap chosen to keep the junction atom non-titratable: acetyl on a severed nitrogen, N-methyl on an amide carbonyl carbon, and for any other junction a cap reflecting the real partner element (a nitrogen partner gives an amide, an oxygen partner a hydroxyl, a phosphorus partner a phosphate, everything else a methyl). When a condensation crosslink (glycosidic, phosphoester, …) left the junction atom fully valent in the free-form SMILES, the displaced leaving group (the base-SMILES atom absent from the deposited residue) is stripped first;

  • a genuine free terminus or free-ligand end is left uncapped, so the pKa tool assigns its charge (a real C-terminus deprotonates, an N-terminus protonates).

The backbone atoms are located by their structure names (N / C) mapped onto the SMILES; crosslink atoms are found by walking mol.bonds and mapped the same way. Coordinates never affect the returned SMILES - only connectivity does. After titration, templatesFromTitration() strips the caps back off (the residue is a subgraph of its capped molecule) to yield one template SMILES per residue for the builder.

moleculekit.tools.residue_titration.capNonstandardResiduesForTitration(mol, specs, smiles=None, outfile=None, _logger=True)#

Build per-context pKa-titration input SMILES for the non-standard residues, keyed by new_resname or resname.

Each chain-resident NCAA is capped for its chain context, and every scaffold / covalent-ligand residue has its non-peptide crosslink(s) inert- capped the same way (see _cap_residue_smiles()); a genuinely free ligand is passed through uncapped, as is any residue whose backbone or crosslink cannot be capped (a warning is logged and it is titrated whole). Entries are deduplicated by key so an NCAA appearing in several places is titrated once per unique (chemistry, context).

The result is a plain {key: smiles} dict. The caller runs its own pKa tool over the values (e.g. AcePka) and passes the protonated result back to templatesFromTitration() - as a dict of the same keys, or, for a file-based tool, as the path to its output CSV (see outfile).

Parameters:
  • mol (Molecule) – The molecule the specs were detected in.

  • specs (list) – The specs returned by moleculekit.tools.nonstandard_residues.detectNonStandardResidues().

  • smiles (dict | None) – Optional {resname: smiles} overrides; resnames absent here are fetched from RCSB by their CCD code.

  • outfile (str | None) – When given, also write the result as a CSV with a key, SMILES and base column per entry - the input for a file-based pKa tool that rewrites SMILES and echoes the other columns through (e.g. AcePka). The echoed base column is what lets templatesFromTitration() re-derive each anchor from exactly the base SMILES that was titrated, with no state carried in between.

Returns:

titration{key: smiles} to titrate, one entry per unique template-requiring residue.

Return type:

dict[str, str]

Raises:

RuntimeError – If a residue needing a template has neither an override in smiles nor a fetchable RCSB SMILES.

moleculekit.tools.residue_titration.templatesFromTitration(mol, specs, protonated, smiles=None, outfile=None)#

Strip the caps off pKa-titrated SMILES back into per-residue templates.

Inverse of capNonstandardResiduesForTitration(), driven by the same mol and specs so no intermediate state has to be carried between the two calls. For each template-requiring chain, scaffold, or covalent- ligand residue, its uncapped anchor is re-derived (_uncapped_residue_smiles()) and used as a relaxed substructure query (_relaxed_query()) to locate that residue’s own atoms inside its protonated, capped SMILES; the matched atoms are extracted (_submol_from_atoms()), dropping the ACE / NME / crosslink caps that fall outside the match. A pKa tool only ever changes protonation state, never the heavy-atom skeleton, so the anchor is guaranteed to be a subgraph of the protonated molecule. Residues titrated whole (genuinely free ligands, or residues whose backbone or crosslink could not be capped) are returned unchanged.

Parameters:
  • mol (Molecule) – The molecule the specs were detected in (the same one passed to capNonstandardResiduesForTitration()).

  • specs (list) – The specs from moleculekit.tools.nonstandard_residues.detectNonStandardResidues().

  • protonated (dict | str | PathLike) – {key: smiles} of the pKa-tool-protonated titration output, keyed exactly as capNonstandardResiduesForTitration() returned - or the path to the tool’s output CSV (e.g. AcePka’s protonated.csv). The CSV must carry key and SMILES columns; when the base column written by capNonstandardResiduesForTitration(outfile=...) was echoed through by the tool, each anchor is re-derived from that echoed base - provably the same SMILES that was titrated, with no RCSB re-fetch and no smiles= overrides to carry between sessions.

  • smiles (dict | None) – The same {resname: smiles} overrides passed to capNonstandardResiduesForTitration(), so anchors are re-derived from identical base SMILES. Not needed when protonated is a CSV path with a base column.

  • outfile (str | None) – When given, also write the templates as JSON mapping each key to {"smiles": template} - the shape residue-template consumers take.

Returns:

templates{key: smiles}, one entry per key, with caps removed and the pKa tool’s protonation applied.

Return type:

dict[str, str]

Raises:

RuntimeError – If protonated is missing a key, a protonated SMILES cannot be parsed, or a residue’s anchor cannot be located inside its protonated SMILES.