Module protkit.properties.dihedral_angles
Expand source code
#!/usr/bin/env python3
# -*- coding:utf-8 -*-
# Authors: Fred Senekal (FS)
# Contact: fred@silicogenesis.com
# License: GPLv3
from typing import Dict
from protkit.structure.atom import Atom
from protkit.structure.residue import Residue
from protkit.structure.chain import Chain
from protkit.structure.protein import Protein
from protkit.geometry.math import Math
class DihedralAngles:
# Definitions taken from: http://www.mlb.co.jp/linux/science/garlic/doc/commands/dihedrals.html
# See also: https://www.cryst.bbk.ac.uk/PPS95/course/3_geometry/conform.html
# See also: https://swissmodel.expasy.org/course/text/chapter3.htm#:~:text=Due%20to%20almost%20constant%20bond,cluster%20around%20energetically%20preferred%20conformations.
# https://leimao.github.io/blog/Dihedral-Angles/
# Code for dihedral angle calculation from:
# https://charmm-gui.org/?doc=lecture&module=scientific&lesson=9
# https://github.com/PDB-REDO/dssp/blob/trunk/src/dssp.cpp
# https://gist.github.com/hypnopump/30d6bfdb8358d3a57d010c9a501fda56
DIHEDRAL_ANGLES = {
"PHI": ("C", "N", "CA", "C"),
"PSI": ("N", "CA", "C", "N"),
"OMEGA": ("CA", "C", "N", "CA"),
# The peptide bond formed by the residues I and I + 1 is assigned to the residue I + 1. The same applies to the omega angle. For that reason no omega angle is assigned to the first residue.
"ALA": {},
"ARG": {
"CHI1": ("N", "CA", "CB", "CG"),
"CHI2": ("CA", "CB", "CG", "CD"),
"CHI3": ("CB", "CG", "CD", "NE"),
"CHI4": ("CG", "CD", "NE", "CZ"),
"CHI5": ("CD", "NE", "CZ", "NH1")
},
"ASN": {
"CHI1": ("N", "CA", "CB", "CG"),
"CHI2": ("CA", "CB", "CG", "OD1")
},
"ASP": {
"CHI1": ("N", "CA", "CB", "CG"),
"CHI2": ("CA", "CB", "CG", "OD1")
},
"CYS": {
"CHI1": ("N", "CA", "CB", "SG")
},
"GLU": {
"CHI1": ("N", "CA", "CB", "CG"),
"CHI2": ("CA", "CB", "CG", "CD"),
"CHI3": ("CB", "CG", "CD", "OE1")
},
"GLN": {
"CHI1": ("N", "CA", "CB", "CG"),
"CHI2": ("CA", "CB", "CG", "CD"),
"CHI3": ("CB", "CG", "CD", "OE1")
},
"GLY": {},
"HIS": {
"CHI1": ("N", "CA", "CB", "CG"),
"CHI2": ("CA", "CB", "CG", "ND1")
},
"ILE": {
"CHI1": ("N", "CA", "CB", "CG1"),
"CHI2": ("CA", "CB", "CG1", "CD1") # Website ref calls this CD
},
"LEU": {
"CHI1": ("N", "CA", "CB", "CG"),
"CHI2": ("CA", "CB", "CG", "CD1")
},
"LYS": {
"CHI1": ("N", "CA", "CB", "CG"),
"CHI2": ("CA", "CB", "CG", "CD"),
"CHI3": ("CB", "CG", "CD", "CE"),
"CHI4": ("CG", "CD", "CE", "NZ")
},
"MET": {
"CHI1": ("N", "CA", "CB", "CG"),
"CHI2": ("CA", "CB", "CG", "SD"),
"CHI3": ("CB", "CG", "SD", "CE")
},
"PHE": {
"CHI1": ("N", "CA", "CB", "CG"),
"CHI2": ("CA", "CB", "CG", "CD1"),
},
"PRO": {
"CHI1": ("N", "CA", "CB", "CG"),
"CHI2": ("CA", "CB", "CG", "CD"),
},
"SER": {
"CHI1": ("N", "CA", "CB", "OG")
},
"THR": {
"CHI1": ("N", "CA", "CB", "OG1"),
},
"TRP": {
"CHI1": ("N", "CA", "CB", "CG"),
"CHI2": ("CA", "CB", "CG", "CD1"),
},
"TYR": {
"CHI1": ("N", "CA", "CB", "CG"),
"CHI2": ("CA", "CB", "CG", "CD1"),
},
"VAL": {
"CHI1": ("N", "CA", "CB", "CG1"),
}
}
@staticmethod
def dihedral_angle(a1: Atom, a2: Atom, a3: Atom, a4: Atom) -> float:
"""
Calculates the dihedral angle between four atoms.
Args:
a1 (Atom): The first atom.
a2 (Atom): The second atom.
a3 (Atom): The third atom.
a4 (Atom): The fourth atom.
Returns:
float: The dihedral angle between the four atoms.
"""
return Math.dihedral_angle(a1.x, a1.y, a1.z, a2.x, a2.y, a2.z, a3.x, a3.y, a3.z, a4.x, a4.y, a4.z)
@staticmethod
def dihedral_angles_of_residue(residue: Residue,
previous_residue: Residue = None,
assign_attribute: bool = False,
key: str = "dihedral_angles") -> Dict[str, float]:
"""
Returns the dihedral angles of a residue.
Args:
residue (Residue): The residue object
previous_residue (Residue): The previous residue object
assign_attribute (bool): Whether to assign the dihedral angles as attributes to the residue object
key (str): The key to use when assigning the dihedral angles as attributes to the residue object
Returns:
dict: A dictionary containing the dihedral angles of the residue side chain. If the previous residue
is defined, the phi, psi and omega angles are also calculated.
"""
angles = {}
defined_angles = DihedralAngles.DIHEDRAL_ANGLES.get(residue.residue_type, set())
if previous_residue:
n1 = previous_residue.get_atom("N")
ca1 = previous_residue.get_atom("CA")
c1 = previous_residue.get_atom("C")
n2 = residue.get_atom("N")
ca2 = residue.get_atom("CA")
c2 = residue.get_atom("C")
angles["PHI"] = DihedralAngles.dihedral_angle(c1, n2, ca2, c2)
angles["PSI"] = DihedralAngles.dihedral_angle(n1, ca1, c1, n2)
angles["OMEGA"] = DihedralAngles.dihedral_angle(ca1, c1, n2, ca2)
for angle_name, (atom1, atom2, atom3, atom4) in defined_angles.items():
a1 = residue.get_atom(atom1)
a2 = residue.get_atom(atom2)
a3 = residue.get_atom(atom3)
a4 = residue.get_atom(atom4)
if a1 and a2 and a3 and a4:
angles[angle_name] = DihedralAngles.dihedral_angle(a1, a2, a3, a4)
else:
angles[angle_name] = None
if assign_attribute:
residue.set_attribute(key, angles)
return angles
@staticmethod
def dihedral_angles_of_chain(chain: Chain,
assign_attribute: bool = False,
key: str = "dihedral_angles"):
"""
Returns the dihedral angles of a chain.
Args:
chain (Chain): The chain object
assign_attribute (bool): Whether to assign the dihedral angles as attributes to the chain object
key (str): The key to use when assigning the dihedral angles as attributes to the chain object
Returns:
dict: A dictionary containing the dihedral angles of the chain
"""
angles = {}
previous_residue = None
for residue in chain.residues:
angles[residue.residue_code] = DihedralAngles.dihedral_angles_of_residue(
residue,
previous_residue=previous_residue,
assign_attribute=assign_attribute, key=key)
previous_residue = residue
return angles
@staticmethod
def dihedral_angles_of_protein(protein: Protein,
assign_attribute: bool = False,
key: str = "dihedral_angles"):
"""
Returns the dihedral angles of a protein.
Args:
protein (Protein): The protein object
assign_attribute (bool): Whether to assign the dihedral angles as attributes to the protein object
key (str): The key to use when assigning the dihedral angles as attributes to the protein object
"""
angles = {}
for chain in protein.chains:
angles[chain.chain_id] = DihedralAngles.dihedral_angles_of_chain(chain, assign_attribute=assign_attribute, key=key)
return angles
Classes
class DihedralAngles
-
Expand source code
class DihedralAngles: # Definitions taken from: http://www.mlb.co.jp/linux/science/garlic/doc/commands/dihedrals.html # See also: https://www.cryst.bbk.ac.uk/PPS95/course/3_geometry/conform.html # See also: https://swissmodel.expasy.org/course/text/chapter3.htm#:~:text=Due%20to%20almost%20constant%20bond,cluster%20around%20energetically%20preferred%20conformations. # https://leimao.github.io/blog/Dihedral-Angles/ # Code for dihedral angle calculation from: # https://charmm-gui.org/?doc=lecture&module=scientific&lesson=9 # https://github.com/PDB-REDO/dssp/blob/trunk/src/dssp.cpp # https://gist.github.com/hypnopump/30d6bfdb8358d3a57d010c9a501fda56 DIHEDRAL_ANGLES = { "PHI": ("C", "N", "CA", "C"), "PSI": ("N", "CA", "C", "N"), "OMEGA": ("CA", "C", "N", "CA"), # The peptide bond formed by the residues I and I + 1 is assigned to the residue I + 1. The same applies to the omega angle. For that reason no omega angle is assigned to the first residue. "ALA": {}, "ARG": { "CHI1": ("N", "CA", "CB", "CG"), "CHI2": ("CA", "CB", "CG", "CD"), "CHI3": ("CB", "CG", "CD", "NE"), "CHI4": ("CG", "CD", "NE", "CZ"), "CHI5": ("CD", "NE", "CZ", "NH1") }, "ASN": { "CHI1": ("N", "CA", "CB", "CG"), "CHI2": ("CA", "CB", "CG", "OD1") }, "ASP": { "CHI1": ("N", "CA", "CB", "CG"), "CHI2": ("CA", "CB", "CG", "OD1") }, "CYS": { "CHI1": ("N", "CA", "CB", "SG") }, "GLU": { "CHI1": ("N", "CA", "CB", "CG"), "CHI2": ("CA", "CB", "CG", "CD"), "CHI3": ("CB", "CG", "CD", "OE1") }, "GLN": { "CHI1": ("N", "CA", "CB", "CG"), "CHI2": ("CA", "CB", "CG", "CD"), "CHI3": ("CB", "CG", "CD", "OE1") }, "GLY": {}, "HIS": { "CHI1": ("N", "CA", "CB", "CG"), "CHI2": ("CA", "CB", "CG", "ND1") }, "ILE": { "CHI1": ("N", "CA", "CB", "CG1"), "CHI2": ("CA", "CB", "CG1", "CD1") # Website ref calls this CD }, "LEU": { "CHI1": ("N", "CA", "CB", "CG"), "CHI2": ("CA", "CB", "CG", "CD1") }, "LYS": { "CHI1": ("N", "CA", "CB", "CG"), "CHI2": ("CA", "CB", "CG", "CD"), "CHI3": ("CB", "CG", "CD", "CE"), "CHI4": ("CG", "CD", "CE", "NZ") }, "MET": { "CHI1": ("N", "CA", "CB", "CG"), "CHI2": ("CA", "CB", "CG", "SD"), "CHI3": ("CB", "CG", "SD", "CE") }, "PHE": { "CHI1": ("N", "CA", "CB", "CG"), "CHI2": ("CA", "CB", "CG", "CD1"), }, "PRO": { "CHI1": ("N", "CA", "CB", "CG"), "CHI2": ("CA", "CB", "CG", "CD"), }, "SER": { "CHI1": ("N", "CA", "CB", "OG") }, "THR": { "CHI1": ("N", "CA", "CB", "OG1"), }, "TRP": { "CHI1": ("N", "CA", "CB", "CG"), "CHI2": ("CA", "CB", "CG", "CD1"), }, "TYR": { "CHI1": ("N", "CA", "CB", "CG"), "CHI2": ("CA", "CB", "CG", "CD1"), }, "VAL": { "CHI1": ("N", "CA", "CB", "CG1"), } } @staticmethod def dihedral_angle(a1: Atom, a2: Atom, a3: Atom, a4: Atom) -> float: """ Calculates the dihedral angle between four atoms. Args: a1 (Atom): The first atom. a2 (Atom): The second atom. a3 (Atom): The third atom. a4 (Atom): The fourth atom. Returns: float: The dihedral angle between the four atoms. """ return Math.dihedral_angle(a1.x, a1.y, a1.z, a2.x, a2.y, a2.z, a3.x, a3.y, a3.z, a4.x, a4.y, a4.z) @staticmethod def dihedral_angles_of_residue(residue: Residue, previous_residue: Residue = None, assign_attribute: bool = False, key: str = "dihedral_angles") -> Dict[str, float]: """ Returns the dihedral angles of a residue. Args: residue (Residue): The residue object previous_residue (Residue): The previous residue object assign_attribute (bool): Whether to assign the dihedral angles as attributes to the residue object key (str): The key to use when assigning the dihedral angles as attributes to the residue object Returns: dict: A dictionary containing the dihedral angles of the residue side chain. If the previous residue is defined, the phi, psi and omega angles are also calculated. """ angles = {} defined_angles = DihedralAngles.DIHEDRAL_ANGLES.get(residue.residue_type, set()) if previous_residue: n1 = previous_residue.get_atom("N") ca1 = previous_residue.get_atom("CA") c1 = previous_residue.get_atom("C") n2 = residue.get_atom("N") ca2 = residue.get_atom("CA") c2 = residue.get_atom("C") angles["PHI"] = DihedralAngles.dihedral_angle(c1, n2, ca2, c2) angles["PSI"] = DihedralAngles.dihedral_angle(n1, ca1, c1, n2) angles["OMEGA"] = DihedralAngles.dihedral_angle(ca1, c1, n2, ca2) for angle_name, (atom1, atom2, atom3, atom4) in defined_angles.items(): a1 = residue.get_atom(atom1) a2 = residue.get_atom(atom2) a3 = residue.get_atom(atom3) a4 = residue.get_atom(atom4) if a1 and a2 and a3 and a4: angles[angle_name] = DihedralAngles.dihedral_angle(a1, a2, a3, a4) else: angles[angle_name] = None if assign_attribute: residue.set_attribute(key, angles) return angles @staticmethod def dihedral_angles_of_chain(chain: Chain, assign_attribute: bool = False, key: str = "dihedral_angles"): """ Returns the dihedral angles of a chain. Args: chain (Chain): The chain object assign_attribute (bool): Whether to assign the dihedral angles as attributes to the chain object key (str): The key to use when assigning the dihedral angles as attributes to the chain object Returns: dict: A dictionary containing the dihedral angles of the chain """ angles = {} previous_residue = None for residue in chain.residues: angles[residue.residue_code] = DihedralAngles.dihedral_angles_of_residue( residue, previous_residue=previous_residue, assign_attribute=assign_attribute, key=key) previous_residue = residue return angles @staticmethod def dihedral_angles_of_protein(protein: Protein, assign_attribute: bool = False, key: str = "dihedral_angles"): """ Returns the dihedral angles of a protein. Args: protein (Protein): The protein object assign_attribute (bool): Whether to assign the dihedral angles as attributes to the protein object key (str): The key to use when assigning the dihedral angles as attributes to the protein object """ angles = {} for chain in protein.chains: angles[chain.chain_id] = DihedralAngles.dihedral_angles_of_chain(chain, assign_attribute=assign_attribute, key=key) return angles
Class variables
var DIHEDRAL_ANGLES
Static methods
def dihedral_angle(a1: Atom, a2: Atom, a3: Atom, a4: Atom) ‑> float
-
Calculates the dihedral angle between four atoms.
Args
a1
:Atom
- The first atom.
a2
:Atom
- The second atom.
a3
:Atom
- The third atom.
a4
:Atom
- The fourth atom.
Returns
float
- The dihedral angle between the four atoms.
Expand source code
@staticmethod def dihedral_angle(a1: Atom, a2: Atom, a3: Atom, a4: Atom) -> float: """ Calculates the dihedral angle between four atoms. Args: a1 (Atom): The first atom. a2 (Atom): The second atom. a3 (Atom): The third atom. a4 (Atom): The fourth atom. Returns: float: The dihedral angle between the four atoms. """ return Math.dihedral_angle(a1.x, a1.y, a1.z, a2.x, a2.y, a2.z, a3.x, a3.y, a3.z, a4.x, a4.y, a4.z)
def dihedral_angles_of_chain(chain: Chain, assign_attribute: bool = False, key: str = 'dihedral_angles')
-
Returns the dihedral angles of a chain.
Args
chain
:Chain
- The chain object
assign_attribute
:bool
- Whether to assign the dihedral angles as attributes to the chain object
key
:str
- The key to use when assigning the dihedral angles as attributes to the chain object
Returns
dict
- A dictionary containing the dihedral angles of the chain
Expand source code
@staticmethod def dihedral_angles_of_chain(chain: Chain, assign_attribute: bool = False, key: str = "dihedral_angles"): """ Returns the dihedral angles of a chain. Args: chain (Chain): The chain object assign_attribute (bool): Whether to assign the dihedral angles as attributes to the chain object key (str): The key to use when assigning the dihedral angles as attributes to the chain object Returns: dict: A dictionary containing the dihedral angles of the chain """ angles = {} previous_residue = None for residue in chain.residues: angles[residue.residue_code] = DihedralAngles.dihedral_angles_of_residue( residue, previous_residue=previous_residue, assign_attribute=assign_attribute, key=key) previous_residue = residue return angles
def dihedral_angles_of_protein(protein: Protein, assign_attribute: bool = False, key: str = 'dihedral_angles')
-
Returns the dihedral angles of a protein.
Args
protein
:Protein
- The protein object
assign_attribute
:bool
- Whether to assign the dihedral angles as attributes to the protein object
key
:str
- The key to use when assigning the dihedral angles as attributes to the protein object
Expand source code
@staticmethod def dihedral_angles_of_protein(protein: Protein, assign_attribute: bool = False, key: str = "dihedral_angles"): """ Returns the dihedral angles of a protein. Args: protein (Protein): The protein object assign_attribute (bool): Whether to assign the dihedral angles as attributes to the protein object key (str): The key to use when assigning the dihedral angles as attributes to the protein object """ angles = {} for chain in protein.chains: angles[chain.chain_id] = DihedralAngles.dihedral_angles_of_chain(chain, assign_attribute=assign_attribute, key=key) return angles
def dihedral_angles_of_residue(residue: Residue, previous_residue: Residue = None, assign_attribute: bool = False, key: str = 'dihedral_angles') ‑> Dict[str, float]
-
Returns the dihedral angles of a residue.
Args
residue
:Residue
- The residue object
previous_residue
:Residue
- The previous residue object
assign_attribute
:bool
- Whether to assign the dihedral angles as attributes to the residue object
key
:str
- The key to use when assigning the dihedral angles as attributes to the residue object
Returns
dict
- A dictionary containing the dihedral angles of the residue side chain. If the previous residue is defined, the phi, psi and omega angles are also calculated.
Expand source code
@staticmethod def dihedral_angles_of_residue(residue: Residue, previous_residue: Residue = None, assign_attribute: bool = False, key: str = "dihedral_angles") -> Dict[str, float]: """ Returns the dihedral angles of a residue. Args: residue (Residue): The residue object previous_residue (Residue): The previous residue object assign_attribute (bool): Whether to assign the dihedral angles as attributes to the residue object key (str): The key to use when assigning the dihedral angles as attributes to the residue object Returns: dict: A dictionary containing the dihedral angles of the residue side chain. If the previous residue is defined, the phi, psi and omega angles are also calculated. """ angles = {} defined_angles = DihedralAngles.DIHEDRAL_ANGLES.get(residue.residue_type, set()) if previous_residue: n1 = previous_residue.get_atom("N") ca1 = previous_residue.get_atom("CA") c1 = previous_residue.get_atom("C") n2 = residue.get_atom("N") ca2 = residue.get_atom("CA") c2 = residue.get_atom("C") angles["PHI"] = DihedralAngles.dihedral_angle(c1, n2, ca2, c2) angles["PSI"] = DihedralAngles.dihedral_angle(n1, ca1, c1, n2) angles["OMEGA"] = DihedralAngles.dihedral_angle(ca1, c1, n2, ca2) for angle_name, (atom1, atom2, atom3, atom4) in defined_angles.items(): a1 = residue.get_atom(atom1) a2 = residue.get_atom(atom2) a3 = residue.get_atom(atom3) a4 = residue.get_atom(atom4) if a1 and a2 and a3 and a4: angles[angle_name] = DihedralAngles.dihedral_angle(a1, a2, a3, a4) else: angles[angle_name] = None if assign_attribute: residue.set_attribute(key, angles) return angles