Source code for cvpack.helix_hbond_content

"""
.. class:: HelixHBondContent
   :platform: Linux, MacOS, Windows
   :synopsis: Alpha-helix hydrogen-bond content of a sequence of residues

.. classauthor:: Charlles Abreu <craabreu@gmail.com>

"""

import re as regex
import typing as t

import openmm
from openmm import app as mmapp
from openmm import unit as mmunit

from .collective_variable import CollectiveVariable
from .units import Quantity, ScalarQuantity


[docs] class HelixHBondContent(CollectiveVariable, openmm.CustomBondForce): r""" The alpha-helix hydrogen-bond content of a sequence of `n` residues: .. math:: \alpha_{\rm HB}({\bf r}) = \sum_{i=5}^n B_m\left( \frac{\| {\bf r}^{\rm H}_i - {\bf r}^{\rm O}_{i-4} \|}{d_{\rm HB}} \right) where :math:`{\bf r}^{\rm H}_k` and :math:`{\bf r}^{\rm O}_k` are the positions of the hydrogen and oxygen atoms bonded, respectively, to the backbone nitrogen and carbon atoms of residue :math:`k`. In addition, :math:`d_{\rm HB}` is the threshold distance for a hydrogen bond and :math:`B_m(x)` is a smooth step function given by .. math:: B_m(x) = \frac{1}{1 + x^{2m}} where :math:`m` is an integer parameter that controls its steepness. Optionally, this collective variable can be normalized to the range :math:`[0, 1]`. .. note:: The residues must be a contiguous sequence from a single chain, ordered from the N- to the C-terminus. Due to an OpenMM limitation, the maximum supported number of residues is 37. Parameters ---------- residues The residues in the sequence. pbc Whether to use periodic boundary conditions. thresholdDistance The threshold distance for a hydrogen bond. halfExponent The parameter :math:`m` of the step function. name The name of the collective variable. Example ------- >>> import cvpack >>> import openmm >>> from openmm import app, unit >>> from openmmtools import testsystems >>> model = testsystems.LysozymeImplicit() >>> residues = list(model.topology.residues())[59:80] >>> print(*[r.name for r in residues]) LYS ASP GLU ... ILE LEU ARG >>> helix_content = cvpack.HelixHBondContent(residues) >>> helix_content.addToSystem(model.system) >>> platform = openmm.Platform.getPlatformByName('Reference') >>> integrator = openmm.VerletIntegrator(0) >>> context = openmm.Context(model.system, integrator, platform) >>> context.setPositions(model.positions) >>> helix_content.getValue(context) 15.880... dimensionless """ def __init__( self, residues: t.Sequence[mmapp.topology.Residue], pbc: bool = False, thresholdDistance: ScalarQuantity = Quantity(0.33 * mmunit.nanometers), halfExponent: int = 3, normalize: bool = False, name: str = "helix_hbond_content", ) -> None: def find_atom(residue: mmapp.topology.Residue, pattern: t.Pattern) -> int: for atom in residue.atoms(): if regex.match(pattern, atom.name): return atom.index raise ValueError( f"Could not find atom matching regex " f"'{pattern.pattern}'" f" in residue {residue.name}{residue.id}" ) numerator = 1 / (len(residues) - 4) if normalize else 1 threshold = thresholdDistance if mmunit.is_quantity(threshold): threshold = threshold.value_in_unit_system(mmunit.md_unit_system) super().__init__(f"{numerator}/(1+x^{2*halfExponent}); x=r/{threshold}") hydrogen_pattern = regex.compile(r"\b(H|1H|HN1|HT1|H1|HN)\b") oxygen_pattern = regex.compile(r"\b(O|OCT1|OC1|OT1|O1)\b") for i in range(4, len(residues)): self.addBond( find_atom(residues[i - 4], oxygen_pattern), find_atom(residues[i], hydrogen_pattern), [], ) self.setUsesPeriodicBoundaryConditions(pbc) self._registerCV( name, mmunit.dimensionless, residues=residues, pbc=pbc, thresholdDistance=thresholdDistance, halfExponent=halfExponent, normalize=normalize, )
HelixHBondContent.registerTag("!cvpack.HelixHBondContent")