Source code for htpolynet.core.bondtemplate

"""Bond-context descriptor classes.

These are pure data objects that describe a bond by the chemical
context (atom names, residue names, order, bystanders, one-aways)
of its two participating atoms.  They carry no cure-specific logic
and have no htpolynet dependencies.

Author: Cameron F. Abrams <cfa22@drexel.edu>
"""
import logging

from copy import deepcopy

logger = logging.getLogger(__name__)


[docs] class BondTemplate: def __init__(self, names, resnames, intraresidue, order, bystander_resnames, bystander_atomnames, oneaway_resnames, oneaway_atomnames): """Creates a BondTemplate object. Args: names (list-like container of two ints): names of atoms in the bond resnames (list-like container of two strs): names of the two residues to which the atoms belong intraresidue (bool): True if this is an intraresidue bond order (int): bond order (1=single, 2=double, ...) bystander_resnames (list of two list-like containers of strs): lists of names of bystander residues (residues also bound to one of the atoms), one for each atom in names bystander_atomnames (list of two list-like containers of strs): lists of names of atoms in bystander residues (parallel to bystander_resnames) oneaway_resnames (list-like container of strs): names of one-away residues (residues bound one bond away from the new interresidue bond; only relevant for C=C free-radical polymerization) oneaway_atomnames (list-like container of strs): names of atoms in one-away residues """ self.names = names self.resnames = resnames self.intraresidue = intraresidue self.bystander_resnames = bystander_resnames self.bystander_atomnames = bystander_atomnames self.oneaway_resnames = oneaway_resnames self.oneaway_atomnames = oneaway_atomnames self.order = order
[docs] def reverse(self): """Reverses the order of all parallel lists in a BondTemplate object.""" self.names = self.names[::-1] self.resnames = self.resnames[::-1] self.bystander_resnames = self.bystander_resnames[::-1] self.bystander_atomnames = self.bystander_atomnames[::-1] self.oneaway_resnames = self.oneaway_resnames[::-1] self.oneaway_atomnames = self.oneaway_atomnames[::-1]
def __str__(self): return f'BondTemplate {self.names} resnames {self.resnames} intraresidue? {self.intraresidue} order {self.order} bystander-resnames {self.bystander_resnames} bystander-atomnames {self.bystander_atomnames} oneaway-resnames {self.oneaway_resnames} oneaway-atomnames {self.oneaway_atomnames}' def __eq__(self,other): check = self.names == other.names check = check and self.intraresidue == other.intraresidue check = check and self.resnames == other.resnames check = check and self.bystander_resnames == other.bystander_resnames check = check and self.bystander_atomnames == other.bystander_atomnames check = check and self.oneaway_resnames == other.oneaway_resnames check = check and self.oneaway_atomnames == other.oneaway_atomnames return check
[docs] def is_reverse_of(self,other): """Returns True if self and other are reverse of each other. Args: other (BondTemplate): another BondTemplate object Returns: bool: True if self and other are reverse copies of each other """ rb = deepcopy(other) rb.reverse() return self == rb
[docs] def bystander_count(self): """Total number of bystander (resname, atomname) pairs across both sides. Used by find_template's best-match selection: more bystanders = more specific template, which should win when multiple templates can match an instance under subset semantics. """ return sum(len(s) for s in self.bystander_resnames)
[docs] def matches(self, other): """Return True if `self` (a parameterization-stage template) is a valid description of `other` (an as-found system bond instance). Strict equality on: - atom names (``names``) - residue names (``resnames``) and ``intraresidue`` flag - one-away context (``oneaway_resnames`` / ``oneaway_atomnames``) — chain-extension templates use these to discriminate dimer- vs trimer- vs tetramer-context bonds, so the match has to be exact. Subset semantics on bystanders: - paired ``(bystander_resname, bystander_atomname)`` items are treated as a multiset on each side; every item the template declares must appear in the instance, but the instance may carry additional bystanders the template does not mention. Rationale: a small-fragment parameterization template (e.g. the CY-CY cyanate dimer) carries no inter-residue context at the cure-reactive atom because the fragment is a single residue. The system-instance bond, formed inside an assembled monomer (e.g. BCY = BPA + 2 CY), naturally picks up the BPA-O ester bystander at the same atom. That extra structural context does not change the local chemistry of the bond template, so the template should still match. Exact bystander equality would force the cure reactant to mirror every inter-residue partner the instance carries, which forecloses the small-fragment idiom. Args: other (BondTemplate): the instance bond to test for compatibility Returns: bool: True if self can serve as a template for other """ if self.names != other.names: return False if self.intraresidue != other.intraresidue: return False if self.resnames != other.resnames: return False if self.oneaway_resnames != other.oneaway_resnames: return False if self.oneaway_atomnames != other.oneaway_atomnames: return False for side in (0, 1): t_pairs = list(zip(self.bystander_resnames[side], self.bystander_atomnames[side])) i_pairs = list(zip(other.bystander_resnames[side], other.bystander_atomnames[side])) remaining = list(i_pairs) for tp in t_pairs: if tp in remaining: remaining.remove(tp) else: return False return True
[docs] def matches_reverse_of(self, other): """Like ``matches`` but tries the reversed orientation of ``other``.""" rb = deepcopy(other) rb.reverse() return self.matches(rb)
BondTemplateList = list[BondTemplate]
[docs] class ReactionBond: def __init__(self, idx, resids, order, bystanders, bystanders_atomidx, oneaways, oneaways_atomidx): """Generates a new ReactionBond object. Args: idx (list-like container of two ints): indices of two atoms that form the bond resids (list-like container of two ints): resids of the two atoms that form the bond order (int): order of the bond (1=single, 2=double, ...) bystanders (list of two list-like containers of ints): two lists of indices of interresidue resids already bound to each atom in idx bystanders_atomidx (list of two list-like containers of ints): two lists of indices of interresidue atoms already bound to each atom in idx oneaways (list of ints): list of one-away resids oneaways_atomidx (list of ints): list of one-away atom indices """ self.idx = idx self.resids = resids self.bystander_resids = bystanders self.bystander_atomidx = bystanders_atomidx self.oneaway_resids = oneaways self.oneaway_atomidx = oneaways_atomidx self.order = order
[docs] def reverse(self): self.idx = self.idx[::-1] self.resids = self.resids[::-1] self.bystander_resids = self.bystander_resids[::-1] self.bystander_atomidx = self.bystander_atomidx[::-1] self.oneaway_resids = self.oneaway_resids[::-1] self.oneaway_atomidx = self.oneaway_atomidx[::-1]
def __str__(self): return f'ReactionBond {self.idx} resids {self.resids} order {self.order} bystander-resids {self.bystander_resids} oneaway-resids {self.oneaway_resids} oneaway-atomidx {self.oneaway_atomidx}'
ReactionBondList = list[ReactionBond]