"""
Version-based configuration
===========================
Contains version-specific methods and parameters.
TODO - this module unnecessarily confuses the code. Can we eliminate it?
"""
import logging
from typing import Sequence, Tuple
from propka.atom import Atom
from propka.hydrogens import setup_bonding_and_protonation, setup_bonding
from propka.hydrogens import setup_bonding_and_protonation_30_style
from propka.energy import radial_volume_desolvation, calculate_pair_weight
from propka.energy import hydrogen_bond_energy, hydrogen_bond_interaction
from propka.energy import electrostatic_interaction, check_coulomb_pair
from propka.energy import coulomb_energy, check_exceptions
from propka.energy import backbone_reorganization
from propka.parameters import Parameters
_LOGGER = logging.getLogger(__name__)
[docs]
class Version:
"""Store version-specific methods and parameters."""
def __init__(self, parameters: Parameters):
self.parameters = parameters
self.desolvation_model = self.empty_function
self.weight_pair_method = self.empty_function
self.hydrogen_bond_interaction_model = self.empty_function
self.sidechain_interaction_model = self.empty_function
self.electrostatic_interaction_model = self.empty_function
self.coulomb_interaction_model = self.empty_function
self.check_coulomb_pair_method = self.empty_function
self.backbone_reorganisation_method = self.empty_function
self.exception_check_method = self.empty_function
self.molecular_preparation_method = self.empty_function
self.prepare_bonds = self.empty_function
[docs]
@staticmethod
def empty_function(*args):
"""Placeholder function so we don't use uninitialized variables.
Args:
args: whatever arguments would have been passed to the function
Raises:
NotImplementedError
"""
err = "Called an empty Version function with args {0:s}".format(args)
raise NotImplementedError(err)
[docs]
def calculate_desolvation(self, group):
"""Calculate desolvation energy using assigned model."""
return self.desolvation_model(self.parameters, group)
[docs]
def calculate_pair_weight(self, num_volume1, num_volume2):
"""Calculate pair weight using assigned model."""
return self.weight_pair_method(
self.parameters, num_volume1, num_volume2)
[docs]
def hydrogen_bond_interaction(self, group1, group2):
"""Calculate H-bond energy using assigned model."""
return self.hydrogen_bond_interaction_model(group1, group2, self)
[docs]
def calculate_side_chain_energy(self, distance, dpka_max, cutoff, _,
f_angle):
"""Calculate sidechain energy using assigned model."""
return self.sidechain_interaction_model(
distance, dpka_max, cutoff, f_angle)
[docs]
def electrostatic_interaction(self, group1, group2, distance):
"""Calculate electrostatic energy using assigned model."""
return self.electrostatic_interaction_model(
group1, group2, distance, self)
[docs]
def calculate_coulomb_energy(self, distance, weight):
"""Calculate Coulomb energy using assigned model."""
return self.coulomb_interaction_model(
distance, weight, self.parameters)
[docs]
def check_coulomb_pair(self, group1, group2, distance):
"""Check Coulomb pair using assigned model."""
return self.check_coulomb_pair_method(
self.parameters, group1, group2, distance)
[docs]
def calculate_backbone_reorganization(self, conformation):
"""Calculate backbone reorganization using assigned model."""
return self.backbone_reorganisation_method(
self.parameters, conformation)
[docs]
def check_exceptions(self, group1, group2):
"""Calculate exceptions using assigned model."""
return self.exception_check_method(self, group1, group2)
[docs]
def setup_bonding_and_protonation(self, molecular_container):
"""Setup bonding and protonation using assigned model."""
return self.molecular_preparation_method(molecular_container)
[docs]
def setup_bonding(self, molecular_container):
"""Setup bonding using assigned model."""
return self.prepare_bonds(self.parameters, molecular_container)
[docs]
def get_hydrogen_bond_parameters(self, atom1: Atom, atom2: Atom) -> Tuple[float, Sequence[float]]:
"""Get hydrogen bond parameters for two atoms."""
raise NotImplementedError("abstract method")
[docs]
class VersionA(Version):
"""TODO - figure out what this is."""
def __init__(self, parameters):
"""Initialize object with parameters."""
# set the calculation rutines used in this version
super().__init__(parameters)
self.molecular_preparation_method = setup_bonding_and_protonation
self.prepare_bonds = setup_bonding
self.desolvation_model = radial_volume_desolvation
self.weight_pair_method = calculate_pair_weight
self.sidechain_interaction_model = hydrogen_bond_energy
self.hydrogen_bond_interaction_model = hydrogen_bond_interaction
self.electrostatic_interaction_model = electrostatic_interaction
self.check_coulomb_pair_method = check_coulomb_pair
self.coulomb_interaction_model = coulomb_energy
self.backbone_interaction_model = hydrogen_bond_energy
self.backbone_reorganisation_method = backbone_reorganization
self.exception_check_method = check_exceptions
[docs]
def get_hydrogen_bond_parameters(self, atom1, atom2):
"""Get hydrogen bond parameters for two atoms.
Args:
atom1: first atom
atom2: second atom
Returns:
[dpka_max, cutoff]
"""
dpka_max = self.parameters.sidechain_interaction
cutoff = self.parameters.sidechain_cutoffs.get_value(
atom1.group_type, atom2.group_type)
return dpka_max, cutoff
[docs]
def get_backbone_hydrogen_bond_parameters(self, backbone_atom, atom):
"""Get hydrogen bond parameters between backbone atom and other atom.
Args:
backbone_atom: backbone atom
atom: other atom
Returns
[v, [c1, c3]] TODO - figure out what this is
"""
if backbone_atom.group_type == 'BBC':
if (
atom.group_type in
self.parameters.backbone_CO_hydrogen_bond.keys()
):
[v, c1, c2] = self.parameters.backbone_CO_hydrogen_bond[
atom.group_type]
return [v, [c1, c2]]
if backbone_atom.group_type == 'BBN':
if (
atom.group_type in
self.parameters.backbone_NH_hydrogen_bond.keys()
):
[v, c1, c2] = self.parameters.backbone_NH_hydrogen_bond[
atom.group_type]
return [v, [c1, c2]]
return None
[docs]
class SimpleHB(VersionA):
"""A simple hydrogen bond version."""
def __init__(self, parameters):
"""Initialize object with parameters."""
# set the calculation rutines used in this version
super().__init__(parameters)
_LOGGER.info('Using simple hb model')
[docs]
def get_hydrogen_bond_parameters(self, atom1, atom2):
"""Get hydrogen bond parameters for two atoms.
Args:
atom1: first atom
atom2: second atom
Returns:
[dpka_max, cutoff]
"""
return self.parameters.hydrogen_bonds.get_value(
atom1.element, atom2.element)
[docs]
def get_backbone_hydrogen_bond_parameters(self, backbone_atom, atom):
"""Get hydrogen bond parameters between backbone atom and other atom.
Args:
backbone_atom: backbone atom
atom: other atom
Returns
[v, [c1, c3]] TODO - figure out what this is
"""
return self.parameters.hydrogen_bonds.get_value(
backbone_atom.element, atom.element)
[docs]
class ElementBasedLigandInteractions(VersionA):
"""TODO - figure out what this is."""
def __init__(self, parameters):
"""Initialize object with parameters."""
# set the calculation rutines used in this version
super().__init__(parameters)
_LOGGER.info('Using detailed SC model!')
return
[docs]
def get_hydrogen_bond_parameters(self, atom1, atom2):
"""Get hydrogen bond parameters for two atoms.
Args:
atom1: first atom
atom2: second atom
Returns:
[dpka_max, cutoff]
"""
if 'hetatm' not in [atom1.type, atom2.type]:
# this is a protein-protein interaction
dpka_max = self.parameters.sidechain_interaction.get_value(
atom1.group_type, atom2.group_type)
cutoff = self.parameters.sidechain_cutoffs.get_value(
atom1.group_type, atom2.group_type)
return [dpka_max, cutoff]
# at least one ligand atom is involved in this interaction
# make sure that we are using the heavy atoms for finding paramters
elements = []
for atom in [atom1, atom2]:
if atom.element == 'H':
elements.append(atom.bonded_atoms[0].element)
else:
elements.append(atom.element)
return self.parameters.hydrogen_bonds.get_value(
elements[0], elements[1])
[docs]
def get_backbone_hydrogen_bond_parameters(self, backbone_atom, atom):
"""Get hydrogen bond parameters between backbone atom and other atom.
Args:
backbone_atom: backbone atom
atom: other atom
Returns
[v, [c1, c3]] TODO - figure out what this is
"""
if atom.type == 'atom':
# this is a backbone-protein interaction
if (backbone_atom.group_type == 'BBC'
and atom.group_type
in self.parameters.backbone_CO_hydrogen_bond.keys()):
[v, c1, c2] = self.parameters.backbone_CO_hydrogen_bond[
atom.group_type]
return [v, [c1, c2]]
if (backbone_atom.group_type == 'BBN'
and atom.group_type
in self.parameters.backbone_NH_hydrogen_bond.keys()):
[v, c1, c2] = self.parameters.backbone_NH_hydrogen_bond[
atom.group_type]
return [v, [c1, c2]]
else:
# this is a backbone-ligand interaction
# make sure that we are using the heavy atoms for finding paramters
elements = []
for atom2 in [backbone_atom, atom]:
if atom2.element == 'H':
elements.append(atom2.bonded_atoms[0].element)
else:
elements.append(atom2.element)
res = self.parameters.hydrogen_bonds.get_value(
elements[0], elements[1])
if not res:
_LOGGER.info(
'Could not determine backbone interaction parameters '
'for: %s %s',
backbone_atom, atom)
return None
return None
[docs]
class Propka30(Version):
"""Version class for PROPKA 3.0."""
def __init__(self, parameters):
"""Initialize object with parameters."""
# set the calculation routines used in this version
super().__init__(parameters)
self.molecular_preparation_method = (
setup_bonding_and_protonation_30_style)
self.desolvation_model = radial_volume_desolvation
self.weight_pair_method = calculate_pair_weight
self.sidechain_interaction_model = hydrogen_bond_energy
self.check_coulomb_pair_method = check_coulomb_pair
self.coulomb_interaction_model = coulomb_energy
self.backbone_reorganisation_method = backbone_reorganization
self.exception_check_method = check_exceptions
[docs]
def get_hydrogen_bond_parameters(self, atom1, atom2):
"""Get hydrogen bond parameters for two atoms.
Args:
atom1: first atom
atom2: second atom
Returns:
[dpka_max, cutoff]
"""
dpka_max = self.parameters.sidechain_interaction.get_value(
atom1.group_type, atom2.group_type)
cutoff = self.parameters.sidechain_cutoffs.get_value(
atom1.group_type, atom2.group_type)
return dpka_max, cutoff