Compare commits
2 Commits
7d64f3abc0
...
a73dbc94a2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a73dbc94a2 | ||
|
|
d42240f739 |
22
.gitignore
vendored
Normal file
22
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
# Python
|
||||||
|
*/__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.pyo
|
||||||
|
*.pyd
|
||||||
|
.Python
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
.venv/
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.log
|
||||||
BIN
src/__pycache__/dunbrack.cpython-314.pyc
Normal file
BIN
src/__pycache__/dunbrack.cpython-314.pyc
Normal file
Binary file not shown.
BIN
src/__pycache__/rotamers.cpython-314.pyc
Normal file
BIN
src/__pycache__/rotamers.cpython-314.pyc
Normal file
Binary file not shown.
BIN
src/dependentRotamerData.zip
Normal file
BIN
src/dependentRotamerData.zip
Normal file
Binary file not shown.
78
src/dunbrack.py
Normal file
78
src/dunbrack.py
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
# vim: set expandtab shiftwidth=4 softtabstop=4:
|
||||||
|
|
||||||
|
# === UCSF ChimeraX Copyright ===
|
||||||
|
# Copyright 2022 Regents of the University of California. All rights reserved.
|
||||||
|
# The ChimeraX application is provided pursuant to the ChimeraX license
|
||||||
|
# agreement, which covers academic and commercial uses. For more details, see
|
||||||
|
# <https://www.rbvi.ucsf.edu/chimerax/docs/licensing.html>
|
||||||
|
#
|
||||||
|
# This particular file is part of the ChimeraX library. You can also
|
||||||
|
# redistribute and/or modify it under the terms of the GNU Lesser General
|
||||||
|
# Public License version 2.1 as published by the Free Software Foundation.
|
||||||
|
# For more details, see
|
||||||
|
# <https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html>
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||||
|
# EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ADDITIONAL LIABILITY
|
||||||
|
# LIMITATIONS ARE DESCRIBED IN THE GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
# VERSION 2.1
|
||||||
|
#
|
||||||
|
# This notice must be embedded in or attached to all copies, including partial
|
||||||
|
# copies, of the software or any revisions or derivations thereof.
|
||||||
|
# === UCSF ChimeraX Copyright ===
|
||||||
|
|
||||||
|
_dependent_cache = {}
|
||||||
|
_independent_cache = {}
|
||||||
|
|
||||||
|
from rotamers import RotamerLibrary, RotamerParams, UnsupportedResTypeError, NoResidueRotamersError
|
||||||
|
|
||||||
|
class DunbrackRotamerLibrary(RotamerLibrary):
|
||||||
|
|
||||||
|
@property
|
||||||
|
def citation(self):
|
||||||
|
return """Shapovalov, M.S., and Dunbrack, R.L., Jr. (2011)
|
||||||
|
A Smoothed Backbone-Dependent Rotamer Library for Proteins
|
||||||
|
Derived from Adaptive Kernel Density Estimates and Regressions
|
||||||
|
Structure, 19, 844-858."""
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cite_pubmed_id(self):
|
||||||
|
return 21645855
|
||||||
|
|
||||||
|
_rotamer_res_names = set(["ARG", "ASN", "ASP", "CPR", "CYD", "CYH", "CYS", "GLN", "GLU", "HIS", "ILE",
|
||||||
|
"LEU", "LYS", "MET", "PHE", "PRO", "SER", "THR", "TPR", "TRP", "TYR", "VAL"])
|
||||||
|
@property
|
||||||
|
def residue_names(self):
|
||||||
|
return self._rotamer_res_names
|
||||||
|
|
||||||
|
@property
|
||||||
|
def res_name_descriptions(self):
|
||||||
|
mapping = super().res_name_descriptions
|
||||||
|
mapping.update({
|
||||||
|
"CPR": "cis proline",
|
||||||
|
"CYD": "disulfide-bonded cysteine",
|
||||||
|
"CYH": "non-disulfide-bonded cysteine",
|
||||||
|
"CYS": "cysteine (CYD+CYH)",
|
||||||
|
"PRO": "proline (CPR+TPR)",
|
||||||
|
"TPR": "trans proline"
|
||||||
|
})
|
||||||
|
return mapping
|
||||||
|
|
||||||
|
@property
|
||||||
|
def res_name_mapping(self):
|
||||||
|
return { "CPR": "PRO", "CYD": "CYS", "CYH": "CYS", "TPR": "PRO" }
|
||||||
|
|
||||||
|
def rotamer_params(self, res_name, phi, psi, *, cis=False):
|
||||||
|
if phi is None or psi is None:
|
||||||
|
file_name = res_name
|
||||||
|
archive = "independentRotamerData2002.zip"
|
||||||
|
cache = _independent_cache
|
||||||
|
else:
|
||||||
|
from math import floor
|
||||||
|
phi = floor((phi + 5) / 10.0) * 10
|
||||||
|
psi = floor((psi + 5) / 10.0) * 10
|
||||||
|
file_name = "%s%d%d" % (res_name, phi, psi)
|
||||||
|
archive = "dependentRotamerData.zip"
|
||||||
|
cache = _dependent_cache
|
||||||
|
return self._get_params(res_name, file_name, cache, archive)
|
||||||
226
src/rotamers.py
Normal file
226
src/rotamers.py
Normal file
|
|
@ -0,0 +1,226 @@
|
||||||
|
# vim: set expandtab shiftwidth=4 softtabstop=4:
|
||||||
|
|
||||||
|
# === UCSF ChimeraX Copyright ===
|
||||||
|
# Copyright 2022 Regents of the University of California. All rights reserved.
|
||||||
|
# The ChimeraX application is provided pursuant to the ChimeraX license
|
||||||
|
# agreement, which covers academic and commercial uses. For more details, see
|
||||||
|
# <https://www.rbvi.ucsf.edu/chimerax/docs/licensing.html>
|
||||||
|
#
|
||||||
|
# This particular file is part of the ChimeraX library. You can also
|
||||||
|
# redistribute and/or modify it under the terms of the GNU Lesser General
|
||||||
|
# Public License version 2.1 as published by the Free Software Foundation.
|
||||||
|
# For more details, see
|
||||||
|
# <https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html>
|
||||||
|
#
|
||||||
|
# THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||||
|
# EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||||
|
# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. ADDITIONAL LIABILITY
|
||||||
|
# LIMITATIONS ARE DESCRIBED IN THE GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
# VERSION 2.1
|
||||||
|
#
|
||||||
|
# This notice must be embedded in or attached to all copies, including partial
|
||||||
|
# copies, of the software or any revisions or derivations thereof.
|
||||||
|
# === UCSF ChimeraX Copyright ===
|
||||||
|
|
||||||
|
from abc import abstractmethod
|
||||||
|
|
||||||
|
class NoResidueRotamersError(ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class UnsupportedResTypeError(NoResidueRotamersError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
class RotamerParams:
|
||||||
|
def __init__(self, p, chis):
|
||||||
|
""" 'p' is the probability of this rotamer. 'chis' is a list of the chi angles. """
|
||||||
|
self.p = p
|
||||||
|
self.chis = chis
|
||||||
|
|
||||||
|
class RotamerLibrary:
|
||||||
|
"""Provide all the information needed to display/list and use a rotamer library.
|
||||||
|
|
||||||
|
Needed methods are described in their doc strings, and methods that must be
|
||||||
|
implemented by subclasses are marked with the 'abstractmethod' decorator.
|
||||||
|
|
||||||
|
If possible, reading the actual rotamer library itself should be done in a
|
||||||
|
"lazy" (i.e. on demand) manner if possible to minimize startup time.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# def __init__(self, name, ui_name):
|
||||||
|
# # name as given in Provider tag
|
||||||
|
# self.name = name
|
||||||
|
# self.ui_name = ui_name
|
||||||
|
|
||||||
|
@property
|
||||||
|
def citation(self):
|
||||||
|
"""If your library has a reference to cite, the text of the citation. Used as the 'cite'
|
||||||
|
argument to the chimerax.ui.widgets.Citation constructor. Example:
|
||||||
|
|
||||||
|
Shapovaeov, M.S., and Dunbrack, R.L., Jr. (2011)
|
||||||
|
A Smoothed Backbone-Dependent Rotamer Library for Proteins
|
||||||
|
Derived from Adaptive Kernel Density Estimates and Regressions
|
||||||
|
Structure, 19, 844-858.
|
||||||
|
"""
|
||||||
|
return None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cite_pubmed_id(self):
|
||||||
|
"""The (integer) PubMed ID corresponding to your citation"""
|
||||||
|
return None
|
||||||
|
|
||||||
|
# display_name and description is now a Provider attribute (display_name changed to ui_name),
|
||||||
|
# since they are needed even if the library is not yet installed
|
||||||
|
|
||||||
|
def map_res_name(self, res_name, exemplar=None):
|
||||||
|
"""Take a residue name and map it to a name that this library supports. For, example if
|
||||||
|
the library supports HIE, HID, and HID but not HIS per se, then map "HIS" to one of the
|
||||||
|
three supported names. 'exemplar', if provided, is an example residue whose name needs
|
||||||
|
mapping. The default implementation handles some common HIS, PRO and CYS variants.
|
||||||
|
|
||||||
|
This routine should return None if no mapping can be determined, though "ALA" and "GLY"
|
||||||
|
should simply return themselves.
|
||||||
|
"""
|
||||||
|
if res_name == "ALA" or res_name == "GLY":
|
||||||
|
return res_name
|
||||||
|
|
||||||
|
supported_names = set(self.residue_names)
|
||||||
|
if res_name == "HIS":
|
||||||
|
if "HID" in supported_names and "HIE" in supported_names:
|
||||||
|
if exemplar:
|
||||||
|
if exemplar.find_atom("HD1"):
|
||||||
|
if "HIP" in supported_names and exemplar.find_atom("HE2"):
|
||||||
|
return "HIP"
|
||||||
|
return "HID"
|
||||||
|
return "HIE"
|
||||||
|
return "HID"
|
||||||
|
elif res_name in ["HID", "HIE", "HIP"]:
|
||||||
|
if res_name in supported_names:
|
||||||
|
return res_name
|
||||||
|
if "HIS" in supported_names:
|
||||||
|
return "HIS"
|
||||||
|
elif res_name == "CYS":
|
||||||
|
if "CYH" in supported_names or "CYD" in supported_names:
|
||||||
|
if exemplar:
|
||||||
|
sg = exemplar.find_atom("SG")
|
||||||
|
if sg:
|
||||||
|
for nb in sg.neighbors:
|
||||||
|
if nb.residue != sg.residue:
|
||||||
|
if "CYD" in supported_names:
|
||||||
|
return "CYD"
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if "CYH" in supported_names:
|
||||||
|
return "CYH"
|
||||||
|
elif res_name == "CYH" or res_name == "CYD":
|
||||||
|
if res_name in supported_names:
|
||||||
|
return res_name
|
||||||
|
if "CYS" in supported_names:
|
||||||
|
return "CYS"
|
||||||
|
elif res_name == "PRO":
|
||||||
|
if "CPR" in supported_names or "TPR" in supported_names:
|
||||||
|
if exemplar:
|
||||||
|
omega = exemplar.omega
|
||||||
|
if omega is not None and abs(omega) < 90:
|
||||||
|
if "CPR" in supported_names:
|
||||||
|
return "CPR"
|
||||||
|
elif "TPR" in supported_names:
|
||||||
|
return "TPR"
|
||||||
|
if res_name in supported_names:
|
||||||
|
return res_name
|
||||||
|
return None
|
||||||
|
|
||||||
|
std_rotamer_res_names = frozenset(["ARG", "ASN", "ASP", "CYS", "GLN", "GLU", "HIS", "ILE", "LEU",
|
||||||
|
"LYS", "MET", "PHE", "PRO", "SER", "THR", "TRP", "TYR", "VAL"])
|
||||||
|
@property
|
||||||
|
def residue_names(self):
|
||||||
|
"""A set of the residue names that this rotamer library provides rotamers for. Typically just
|
||||||
|
the 18 standard amino acids that actually have side chains, but some libraries provide
|
||||||
|
rotamers for certain protonation states (e.g. CYH for non-disulphide cysteine) or conformers
|
||||||
|
(e.g. CPR for cis-proline).
|
||||||
|
"""
|
||||||
|
return self.std_rotamer_res_names.copy()
|
||||||
|
|
||||||
|
std_rotamer_res_descriptions = { "ALA": "alanine", "ASN": "asparagine", "ASP": "aspartic acid",
|
||||||
|
"CYS": "cysteine", "GLN": "glutamine", "GLU": "glutamic acid", "GLY": "glycine",
|
||||||
|
"HIS": "histidine", "ILE": "isoleucine", "LEU": "leucine", "LYS": "lysine",
|
||||||
|
"MET": "methionine", "PHE": "phenylalinine", "PRO": "proline", "SER": "serine",
|
||||||
|
"THR": "threonine", "TRP": "tryptophan", "TYR": "tyrosine", "VAL": "valine",
|
||||||
|
}
|
||||||
|
@property
|
||||||
|
def res_name_descriptions(self):
|
||||||
|
"""A dictionary mapping the 3-letter residue name to a full text description of the residue,
|
||||||
|
e.g. "leucine" for LEU or "doubly protonated histidine" for HIP. All normal amino acids
|
||||||
|
are included in the default implementation. All residues provided by the library should
|
||||||
|
be in the dictionary that this property returns.
|
||||||
|
"""
|
||||||
|
return self.std_rotamer_res_descriptions.copy()
|
||||||
|
|
||||||
|
@property
|
||||||
|
def res_name_mapping(self):
|
||||||
|
"""For libraries that have non-standard residue names that correspond to certain states of
|
||||||
|
standard residues (see the residue_names method), this dictionary maps the non-standard
|
||||||
|
name to the corresponding standard name.
|
||||||
|
"""
|
||||||
|
return {}
|
||||||
|
|
||||||
|
# @property
|
||||||
|
# def res_template_func(self):
|
||||||
|
# """If a rotamer library supports non-standard residues, this should return a function that
|
||||||
|
# when given the residue name as its argument, returns a TmplResidue that can be used
|
||||||
|
# to build out the rotamer (and returns None for residues not in the library).
|
||||||
|
# """
|
||||||
|
# from chimerax.atomic import TmplResidue
|
||||||
|
# return TmplResidue.get_template
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def rotamer_params(self, res_name, phi, psi, *, cis=False):
|
||||||
|
"""Return a list of RotamerParams instances corresponding to the residue name 'res_name' and
|
||||||
|
the backbone angle 'phi' and 'psi'. Backbone-independent libraries will ignore phi and psi.
|
||||||
|
Note that phi or psi can be None for chain-terminal residues. Backbone-dependent libraries
|
||||||
|
will have to use some fallback procedure for generating parameters in those cases, or throw
|
||||||
|
NoResidueRotamersError. If 'res_name' does not correspond to a name supported by the library,
|
||||||
|
throw UnsupportedResTypeError.
|
||||||
|
|
||||||
|
For rotamer libraries that support cis vs. trans rotamers, the cis keyword can be used
|
||||||
|
to decide which rotamers to return.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _get_params(self, res_name, file_name, cache, archive):
|
||||||
|
"""Possibly useful utility routine for fetching parameters stored in zip archives"""
|
||||||
|
try:
|
||||||
|
return cache[file_name]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
base_name = self._non_cistrans_res_name(res_name)
|
||||||
|
if base_name not in self.residue_names:
|
||||||
|
raise UnsupportedResTypeError(
|
||||||
|
"%s library does not support residue type '%s'" % (self.ui_name, base_name))
|
||||||
|
import os.path, inspect
|
||||||
|
my_dir = os.path.split(inspect.getfile(self.__class__))[0]
|
||||||
|
from zipfile import ZipFile
|
||||||
|
zf = ZipFile(os.path.join(my_dir, archive), "r")
|
||||||
|
try:
|
||||||
|
data = zf.read(file_name)
|
||||||
|
except KeyError:
|
||||||
|
raise NoResidueRotamersError(
|
||||||
|
"'%s' library has no rotamers for '%s'" % (self.ui_name, file_name))
|
||||||
|
from struct import unpack, calcsize
|
||||||
|
sz1 = calcsize("!ii")
|
||||||
|
num_rotamers, num_params, = unpack("!ii", data[:sz1])
|
||||||
|
sz2 = calcsize("!%df" % num_params)
|
||||||
|
rotamers = []
|
||||||
|
for i in range(num_rotamers):
|
||||||
|
params = unpack("!%df" % num_params, data[sz1 + i * sz2 : sz1 + (i+1) * sz2])
|
||||||
|
p = params[0]
|
||||||
|
chis = params[1:]
|
||||||
|
rotamers.append(RotamerParams(p, chis))
|
||||||
|
cache[file_name] = rotamers
|
||||||
|
return rotamers
|
||||||
|
|
||||||
|
def _non_cistrans_res_name(self, res_name):
|
||||||
|
if res_name.endswith('cis'):
|
||||||
|
return res_name[:-4]
|
||||||
|
if res_name.endswith('trans'):
|
||||||
|
return res_name[:-6]
|
||||||
|
return res_name
|
||||||
10
src/test_rotamers.py
Normal file
10
src/test_rotamers.py
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
from dunbrack import DunbrackRotamerLibrary
|
||||||
|
|
||||||
|
rl = DunbrackRotamerLibrary()
|
||||||
|
|
||||||
|
res_name = "LYS"
|
||||||
|
|
||||||
|
print(f"Rotamers for {res_name}")
|
||||||
|
|
||||||
|
for rotamer in rl.rotamer_params(res_name, 100, 100):
|
||||||
|
print(rotamer.p, rotamer.chis)
|
||||||
Loading…
Reference in New Issue
Block a user