Remove logging abstraction to facilitate debugging and reduce bloat.

Also performed minor delinting.

Fixes #108
This commit is contained in:
Nathan Baker
2020-11-29 16:09:45 -08:00
parent 67039aabc0
commit 9350799be1
16 changed files with 436 additions and 331 deletions

View File

@@ -5,12 +5,14 @@ Bonds
PROPKA representation of bonds. PROPKA representation of bonds.
""" """
import logging
import math import math
import json import json
import pkg_resources import pkg_resources
import propka.calculations import propka.calculations
# TODO - replace the info/warning imports with logging functionality
from propka.lib import info
_LOGGER = logging.getLogger(__name__)
# TODO - should these constants be defined higher up in the module? # TODO - should these constants be defined higher up in the module?
@@ -30,8 +32,8 @@ class BondMaker:
""" """
def __init__(self): def __init__(self):
# predefined bonding distances # predefined bonding distances
self.distances = {'S-S' : DISULFIDE_DISTANCE, self.distances = {'S-S': DISULFIDE_DISTANCE,
'F-F' : FLUORIDE_DISTANCE} 'F-F': FLUORIDE_DISTANCE}
self.distances_squared = {} self.distances_squared = {}
for key in self.distances: for key in self.distances:
self.distances_squared[key] = ( self.distances_squared[key] = (
@@ -53,22 +55,22 @@ class BondMaker:
'C': ['CA', 'O'], 'O': ['C']} 'C': ['CA', 'O'], 'O': ['C']}
self.num_pi_elec_bonds_backbone = {'C': 1, 'O': 1} self.num_pi_elec_bonds_backbone = {'C': 1, 'O': 1}
self.num_pi_elec_conj_bonds_backbone = {'N': 1} self.num_pi_elec_conj_bonds_backbone = {'N': 1}
self.num_pi_elec_bonds_sidechains = {'ARG-CZ' : 1, 'ARG-NH1': 1, self.num_pi_elec_bonds_sidechains = {'ARG-CZ': 1, 'ARG-NH1': 1,
'ASN-OD1': 1, 'ASN-CG' : 1, 'ASN-OD1': 1, 'ASN-CG': 1,
'ASP-OD1': 1, 'ASP-CG' : 1, 'ASP-OD1': 1, 'ASP-CG': 1,
'GLU-OE1': 1, 'GLU-CD' : 1, 'GLU-OE1': 1, 'GLU-CD': 1,
'GLN-OE1': 1, 'GLN-CD' : 1, 'GLN-OE1': 1, 'GLN-CD': 1,
'HIS-CG' : 1, 'HIS-CD2': 1, 'HIS-CG': 1, 'HIS-CD2': 1,
'HIS-ND1': 1, 'HIS-CE1': 1, 'HIS-ND1': 1, 'HIS-CE1': 1,
'PHE-CG' : 1, 'PHE-CD1': 1, 'PHE-CG': 1, 'PHE-CD1': 1,
'PHE-CE1': 1, 'PHE-CZ' : 1, 'PHE-CE1': 1, 'PHE-CZ': 1,
'PHE-CE2': 1, 'PHE-CD2': 1, 'PHE-CE2': 1, 'PHE-CD2': 1,
'TRP-CG' : 1, 'TRP-CD1': 1, 'TRP-CG': 1, 'TRP-CD1': 1,
'TRP-CE2': 1, 'TRP-CD2': 1, 'TRP-CE2': 1, 'TRP-CD2': 1,
'TRP-CE3': 1, 'TRP-CZ3': 1, 'TRP-CE3': 1, 'TRP-CZ3': 1,
'TRP-CH2': 1, 'TRP-CZ2': 1, 'TRP-CH2': 1, 'TRP-CZ2': 1,
'TYR-CG' : 1, 'TYR-CD1': 1, 'TYR-CG': 1, 'TYR-CD1': 1,
'TYR-CE1': 1, 'TYR-CZ' : 1, 'TYR-CE1': 1, 'TYR-CZ': 1,
'TYR-CE2': 1, 'TYR-CD2': 1} 'TYR-CE2': 1, 'TYR-CD2': 1}
self.num_pi_elec_conj_bonds_sidechains = {'ARG-NE': 1, 'ARG-NH2': 1, self.num_pi_elec_conj_bonds_sidechains = {'ARG-NE': 1, 'ARG-NH2': 1,
'ASN-ND2': 1, 'GLN-NE2': 1, 'ASN-ND2': 1, 'GLN-NE2': 1,
@@ -90,13 +92,13 @@ class BondMaker:
Args: Args:
protein: the protein to search for bonds protein: the protein to search for bonds
""" """
info('++++ Side chains ++++') _LOGGER.info('++++ Side chains ++++')
# side chains # side chains
for chain in protein.chains: for chain in protein.chains:
for residue in chain.residues: for residue in chain.residues:
if residue.res_name.replace(' ', '') not in ['N+', 'C-']: if residue.res_name.replace(' ', '') not in ['N+', 'C-']:
self.find_bonds_for_side_chain(residue.atoms) self.find_bonds_for_side_chain(residue.atoms)
info('++++ Backbones ++++') _LOGGER.info('++++ Backbones ++++')
# backbone # backbone
last_residues = [] last_residues = []
for chain in protein.chains: for chain in protein.chains:
@@ -108,11 +110,11 @@ class BondMaker:
self.connect_backbone(chain.residues[i-1], self.connect_backbone(chain.residues[i-1],
chain.residues[i]) chain.residues[i])
last_residues.append(chain.residues[i]) last_residues.append(chain.residues[i])
info('++++ terminal oxygen ++++') _LOGGER.info('++++ terminal oxygen ++++')
# terminal OXT # terminal OXT
for last_residue in last_residues: for last_residue in last_residues:
self.find_bonds_for_terminal_oxygen(last_residue) self.find_bonds_for_terminal_oxygen(last_residue)
info('++++ cysteines ++++') _LOGGER.info('++++ cysteines ++++')
# Cysteines # Cysteines
for chain in protein.chains: for chain in protein.chains:
for i in range(0, len(chain.residues)): for i in range(0, len(chain.residues)):
@@ -204,8 +206,8 @@ class BondMaker:
if key in list(self.num_pi_elec_conj_bonds_sidechains.keys()): if key in list(self.num_pi_elec_conj_bonds_sidechains.keys()):
atom1.num_pi_elec_conj_2_3_bonds = ( atom1.num_pi_elec_conj_2_3_bonds = (
self.num_pi_elec_conj_bonds_sidechains[key]) self.num_pi_elec_conj_bonds_sidechains[key])
if not atom1.name in self.backbone_atoms: if atom1.name not in self.backbone_atoms:
if not atom1.name in self.terminal_oxygen_names: if atom1.name not in self.terminal_oxygen_names:
for atom2 in atoms: for atom2 in atoms:
if atom2.name in ( if atom2.name in (
self self
@@ -266,7 +268,6 @@ class BondMaker:
Returns: Returns:
list of atoms list of atoms
""" """
#self.find_bonds_for_protein(molecule)
atoms = [] atoms = []
for chain in molecule.chains: for chain in molecule.chains:
for residue in chain.residues: for residue in chain.residues:
@@ -424,9 +425,9 @@ class BondMaker:
""" """
if atom1 == atom2: if atom1 == atom2:
return return
if not atom1 in atom2.bonded_atoms: if atom1 not in atom2.bonded_atoms:
atom2.bonded_atoms.append(atom1) atom2.bonded_atoms.append(atom1)
if not atom2 in atom1.bonded_atoms: if atom2 not in atom1.bonded_atoms:
atom1.bonded_atoms.append(atom2) atom1.bonded_atoms.append(atom2)
def generate_protein_bond_dictionary(self, atoms): def generate_protein_bond_dictionary(self, atoms):
@@ -441,21 +442,21 @@ class BondMaker:
name_i = atom.name name_i = atom.name
resi_j = bonded_atom.res_name resi_j = bonded_atom.res_name
name_j = bonded_atom.name name_j = bonded_atom.name
if not name_i in ( if name_i not in (
self.backbone_atoms self.backbone_atoms
or not name_j in self.backbone_atoms): or name_j not in self.backbone_atoms):
if not name_i in ( if name_i not in (
self.terminal_oxygen_names self.terminal_oxygen_names
and not name_j in self.terminal_oxygen_names): and name_j not in self.terminal_oxygen_names):
if not resi_i in list(self.protein_bonds.keys()): if resi_i not in list(self.protein_bonds.keys()):
self.protein_bonds[resi_i] = {} self.protein_bonds[resi_i] = {}
if not name_i in self.protein_bonds[resi_i]: if name_i not in self.protein_bonds[resi_i]:
self.protein_bonds[resi_i][name_i] = [] self.protein_bonds[resi_i][name_i] = []
if not name_j in self.protein_bonds[resi_i][name_i]: if name_j not in self.protein_bonds[resi_i][name_i]:
self.protein_bonds[resi_i][name_i].append(name_j) self.protein_bonds[resi_i][name_i].append(name_j)
if not resi_j in list(self.protein_bonds.keys()): if resi_j not in list(self.protein_bonds.keys()):
self.protein_bonds[resi_j] = {} self.protein_bonds[resi_j] = {}
if not name_j in self.protein_bonds[resi_j]: if name_j not in self.protein_bonds[resi_j]:
self.protein_bonds[resi_j][name_j] = [] self.protein_bonds[resi_j][name_j] = []
if not name_i in self.protein_bonds[resi_j][name_j]: if name_i not in self.protein_bonds[resi_j][name_j]:
self.protein_bonds[resi_j][name_j].append(name_i) self.protein_bonds[resi_j][name_j].append(name_i)

View File

@@ -4,6 +4,7 @@ Molecular data structures
Container data structure for molecular conformations. Container data structure for molecular conformations.
""" """
import logging
import functools import functools
import propka.ligand import propka.ligand
from propka.output import make_interaction_map from propka.output import make_interaction_map
@@ -12,11 +13,14 @@ from propka.coupled_groups import NCCG
from propka.determinants import set_backbone_determinants, set_ion_determinants from propka.determinants import set_backbone_determinants, set_ion_determinants
from propka.determinants import set_determinants from propka.determinants import set_determinants
from propka.group import Group, is_group from propka.group import Group, is_group
from propka.lib import info
_LOGGER = logging.getLogger(__name__)
#: A large number that gets multipled with the integer obtained from applying #: A large number that gets multipled with the integer obtained from applying
#: :func:`ord` to the atom chain ID. Used in calculating atom keys for sorting. #: :func:`ord` to the atom chain ID. Used in calculating atom keys for
#: sorting.
UNICODE_MULTIPLIER = 1e7 UNICODE_MULTIPLIER = 1e7
#: A number that gets mutiplied with an atom's residue number. Used in #: A number that gets mutiplied with an atom's residue number. Used in
@@ -54,7 +58,8 @@ class ConformationContainer:
else: else:
group = atom.group group = atom.group
# if the atom has been checked in a another conformation, check # if the atom has been checked in a another conformation, check
# if it has a group that should be used in this conformation as well # if it has a group that should be used in this conformation
# as well
if group: if group:
self.setup_and_add_group(group) self.setup_and_add_group(group)
@@ -66,7 +71,7 @@ class ConformationContainer:
'Covalent coupling map for {0:s}'.format(str(self)), 'Covalent coupling map for {0:s}'.format(str(self)),
self.get_covalently_coupled_groups(), self.get_covalently_coupled_groups(),
lambda g1, g2: g1 in g2.covalently_coupled_groups) lambda g1, g2: g1 in g2.covalently_coupled_groups)
info(map_) _LOGGER.info(map_)
# check if we should set a common charge centre as well # check if we should set a common charge centre as well
if self.parameters.common_charge_centre: if self.parameters.common_charge_centre:
self.set_common_charge_centres() self.set_common_charge_centres()
@@ -108,7 +113,7 @@ class ConformationContainer:
'Covalent coupling map for {0:s}'.format(str(self)), 'Covalent coupling map for {0:s}'.format(str(self)),
self.get_covalently_coupled_groups(), self.get_covalently_coupled_groups(),
lambda g1, g2: g1 in g2.covalently_coupled_groups) lambda g1, g2: g1 in g2.covalently_coupled_groups)
info(map_) _LOGGER.info("Coupling map:\n%s", map_)
def find_non_covalently_coupled_groups(self, verbose=False): def find_non_covalently_coupled_groups(self, verbose=False):
"""Find non-covalently coupled groups and set common charge centres. """Find non-covalently coupled groups and set common charge centres.
@@ -116,7 +121,8 @@ class ConformationContainer:
Args: Args:
verbose: verbose output verbose: verbose output
""" """
# check if non-covalent coupling has already been set up in an input file # check if non-covalent coupling has already been set up in an input
# file
if len(list(filter(lambda g: len(g.non_covalently_coupled_groups) > 0, if len(list(filter(lambda g: len(g.non_covalently_coupled_groups) > 0,
self.get_titratable_groups()))) > 0: self.get_titratable_groups()))) > 0:
self.non_covalently_coupled_groups = True self.non_covalently_coupled_groups = True
@@ -193,7 +199,7 @@ class ConformationContainer:
version: version object version: version object
options: option object options: option object
""" """
info('\nCalculating pKas for', self) _LOGGER.info('Calculating pKas for %s', self)
# calculate desolvation # calculate desolvation
for group in self.get_titratable_groups() + self.get_ions(): for group in self.get_titratable_groups() + self.get_ions():
version.calculate_desolvation(group) version.calculate_desolvation(group)
@@ -215,7 +221,7 @@ class ConformationContainer:
penalised_labels = self.coupling_effects() penalised_labels = self.coupling_effects()
if (self.parameters.remove_penalised_group if (self.parameters.remove_penalised_group
and len(penalised_labels) > 0): and len(penalised_labels) > 0):
info('Removing penalised groups!!!') _LOGGER.info('Removing penalised groups!!!')
for group in self.get_titratable_groups(): for group in self.get_titratable_groups():
group.remove_determinants(penalised_labels) group.remove_determinants(penalised_labels)
# re-calculating the total pKa values # re-calculating the total pKa values
@@ -257,7 +263,7 @@ class ConformationContainer:
# group with the highest pKa is allowed to titrate... # group with the highest pKa is allowed to titrate...
continue continue
group.coupled_titrating_group = first_group group.coupled_titrating_group = first_group
#... and the rest are penalised # ... and the rest are penalised
penalised_labels.append(group.label) penalised_labels.append(group.label)
return penalised_labels return penalised_labels
@@ -433,9 +439,9 @@ class ConformationContainer:
def get_groups_for_calculations(self): def get_groups_for_calculations(self):
"""Get a list of groups that should be included in results report. """Get a list of groups that should be included in results report.
If --titrate_only option is specified, only residues that are titratable If --titrate_only option is specified, only residues that are
and are in that list are included; otherwise all titratable residues titratable and are in that list are included; otherwise all titratable
and CYS residues are included. residues and CYS residues are included.
Returns: Returns:
list of groups list of groups
@@ -525,7 +531,7 @@ class ConformationContainer:
if not atom.molecular_container: if not atom.molecular_container:
atom.molecular_container = self.molecular_container atom.molecular_container = self.molecular_container
# store chain id for bookkeeping # store chain id for bookkeeping
if not atom.chain_id in self.chains: if atom.chain_id not in self.chains:
self.chains.append(atom.chain_id) self.chains.append(atom.chain_id)
def copy_atom(self, atom): def copy_atom(self, atom):
@@ -556,7 +562,7 @@ class ConformationContainer:
""" """
my_residue_labels = {a.residue_label for a in self.atoms} my_residue_labels = {a.residue_label for a in self.atoms}
for atom in other.atoms: for atom in other.atoms:
if not atom.residue_label in my_residue_labels: if atom.residue_label not in my_residue_labels:
self.copy_atom(atom) self.copy_atom(atom)
def find_group(self, group): def find_group(self, group):

View File

@@ -4,12 +4,14 @@ Coupling between groups
Describe and analyze energetic coupling between groups. Describe and analyze energetic coupling between groups.
""" """
import logging
import itertools import itertools
import propka.lib import propka.lib
from propka.group import Group from propka.group import Group
from propka.output import make_interaction_map from propka.output import make_interaction_map
from propka.lib import info
_LOGGER = logging.getLogger(__name__)
class NonCovalentlyCoupledGroups: class NonCovalentlyCoupledGroups:
@@ -80,7 +82,8 @@ class NonCovalentlyCoupledGroups:
if (abs(group1.intrinsic_pka - group2.intrinsic_pka) if (abs(group1.intrinsic_pka - group2.intrinsic_pka)
> self.parameters.max_intrinsic_pka_diff and return_on_fail): > self.parameters.max_intrinsic_pka_diff and return_on_fail):
return {'coupling_factor': -1.0} return {'coupling_factor': -1.0}
# if everything is OK, calculate the coupling factor and return all info # if everything is OK, calculate the coupling factor and return all
# info
factor = ( factor = (
self.get_free_energy_diff_factor(default_energy, swapped_energy) self.get_free_energy_diff_factor(default_energy, swapped_energy)
* self.get_pka_diff_factor(group1.intrinsic_pka, * self.get_pka_diff_factor(group1.intrinsic_pka,
@@ -107,7 +110,7 @@ class NonCovalentlyCoupledGroups:
if intrinsic_pka_diff <= self.parameters.max_intrinsic_pka_diff: if intrinsic_pka_diff <= self.parameters.max_intrinsic_pka_diff:
res = ( res = (
1-(intrinsic_pka_diff 1-(intrinsic_pka_diff
/self.parameters.max_intrinsic_pka_diff)**2) / self.parameters.max_intrinsic_pka_diff)**2)
return res return res
def get_free_energy_diff_factor(self, energy1, energy2): def get_free_energy_diff_factor(self, energy1, energy2):
@@ -139,7 +142,7 @@ class NonCovalentlyCoupledGroups:
res = ( res = (
(interaction_energy-self.parameters.min_interaction_energy) (interaction_energy-self.parameters.min_interaction_energy)
/ (1.0+interaction_energy / (1.0+interaction_energy
-self.parameters.min_interaction_energy)) - self.parameters.min_interaction_energy))
return res return res
def identify_non_covalently_coupled_groups(self, conformation, def identify_non_covalently_coupled_groups(self, conformation,
@@ -162,17 +165,25 @@ class NonCovalentlyCoupledGroups:
'\n' '\n'
' Detecting non-covalently coupled residues\n' ' Detecting non-covalently coupled residues\n'
'{sep}\n' '{sep}\n'
' Maximum pKa difference: {c.max_intrinsic_pka_diff:>4.2f} pKa units\n' ' Maximum pKa difference: '
' Minimum interaction energy: {c.min_interaction_energy:>4.2f} pKa units\n' '{c.max_intrinsic_pka_diff:>4.2f} pKa units\n'
' Maximum free energy diff.: {c.max_free_energy_diff:>4.2f} pKa units\n' ' Minimum interaction energy: '
' Minimum swap pKa shift: {c.min_swap_pka_shift:>4.2f} pKa units\n' '{c.min_interaction_energy:>4.2f} pKa units\n'
' pH: {c.pH:>6} \n' ' Maximum free energy diff.: '
' Reference: {c.reference}\n' '{c.max_free_energy_diff:>4.2f} pKa units\n'
' Min pKa: {c.min_pka:>4.2f}\n' ' Minimum swap pKa shift: '
' Max pKa: {c.max_pka:>4.2f}\n' '{c.min_swap_pka_shift:>4.2f} pKa units\n'
' pH: '
'{c.pH:>6} \n'
' Reference: '
'{c.reference}\n'
' Min pKa: '
'{c.min_pka:>4.2f}\n'
' Max pKa: '
'{c.max_pka:>4.2f}\n'
'\n') '\n')
sep = "-" * 103 sep = "-" * 103
info(info_fmt.format(sep=sep, c=self)) _LOGGER.info(info_fmt.format(sep=sep, c=self))
# find coupled residues # find coupled residues
titratable_groups = conformation.get_titratable_groups() titratable_groups = conformation.get_titratable_groups()
if not conformation.non_covalently_coupled_groups: if not conformation.non_covalently_coupled_groups:
@@ -202,7 +213,7 @@ class NonCovalentlyCoupledGroups:
'Non-covalent coupling map for {0:s}'.format(str(conformation)), 'Non-covalent coupling map for {0:s}'.format(str(conformation)),
conformation.get_non_covalently_coupled_groups(), conformation.get_non_covalently_coupled_groups(),
lambda g1, g2: g1 in g2.non_covalently_coupled_groups) lambda g1, g2: g1 in g2.non_covalently_coupled_groups)
info(map_) _LOGGER.info(map_)
for system in conformation.get_coupled_systems( for system in conformation.get_coupled_systems(
conformation.get_non_covalently_coupled_groups(), conformation.get_non_covalently_coupled_groups(),
Group.get_non_covalently_coupled_groups): Group.get_non_covalently_coupled_groups):
@@ -215,7 +226,7 @@ class NonCovalentlyCoupledGroups:
conformation: conformation to print conformation: conformation to print
system: system to print system: system to print
""" """
info( _LOGGER.info(
'System containing {0:d} groups:'.format(len(system))) 'System containing {0:d} groups:'.format(len(system)))
# make list of interactions within this system # make list of interactions within this system
interactions = list(itertools.combinations(system, 2)) interactions = list(itertools.combinations(system, 2))
@@ -230,7 +241,7 @@ class NonCovalentlyCoupledGroups:
coup_info += ( coup_info += (
self.make_data_to_string(data, interaction[0], interaction[1]) self.make_data_to_string(data, interaction[0], interaction[1])
+ '\n\n') + '\n\n')
info(coup_info) _LOGGER.info(coup_info)
# make list of possible combinations of swap to try out # make list of possible combinations of swap to try out
combinations = propka.lib.generate_combinations(interactions) combinations = propka.lib.generate_combinations(interactions)
# Make possible swap combinations # Make possible swap combinations
@@ -246,7 +257,7 @@ class NonCovalentlyCoupledGroups:
for interaction in combination: for interaction in combination:
self.swap_interactions([interaction[0]], [interaction[1]]) self.swap_interactions([interaction[0]], [interaction[1]])
swap_info += self.print_determinants_section(system, 'Swapped') swap_info += self.print_determinants_section(system, 'Swapped')
info(swap_info) _LOGGER.info(swap_info)
@staticmethod @staticmethod
def get_interaction(group1, group2, include_side_chain_hbs=True): def get_interaction(group1, group2, include_side_chain_hbs=True):

View File

@@ -6,10 +6,13 @@ Energy calculations.
""" """
import math import math
from propka.lib import warning import logging
from propka.calculations import squared_distance, get_smallest_distance from propka.calculations import squared_distance, get_smallest_distance
_LOGGER = logging.getLogger(__name__)
# TODO - I have no idea what these constants mean so I labeled them "UNK_" # TODO - I have no idea what these constants mean so I labeled them "UNK_"
UNK_MIN_DISTANCE = 2.75 UNK_MIN_DISTANCE = 2.75
MIN_DISTANCE_4TH = math.pow(UNK_MIN_DISTANCE, 4) MIN_DISTANCE_4TH = math.pow(UNK_MIN_DISTANCE, 4)
@@ -72,7 +75,10 @@ def calculate_scale_factor(parameters, weight):
Returns: Returns:
scaling factor scaling factor
""" """
scale_factor = 1.0 - (1.0 - parameters.desolvationSurfaceScalingFactor)*(1.0 - weight) scale_factor = (
1.0 - (1.0 - parameters.desolvationSurfaceScalingFactor)
* (1.0 - weight)
)
return scale_factor return scale_factor
@@ -83,7 +89,8 @@ def calculate_weight(parameters, num_volume):
Args: Args:
parameters: parameters for desolvation calculation parameters: parameters for desolvation calculation
num_volume: number of heavy atoms within desolvation calculation volume num_volume: number of heavy atoms within desolvation calculation
volume
Returns: Returns:
desolvation weight desolvation weight
""" """
@@ -135,12 +142,14 @@ def hydrogen_bond_energy(dist, dpka_max, cutoffs, f_angle=1.0):
def angle_distance_factors(atom1=None, atom2=None, atom3=None, center=None): def angle_distance_factors(atom1=None, atom2=None, atom3=None, center=None):
"""Calculate distance and angle factors for three atoms for backbone interactions. """Calculate distance and angle factors for three atoms for backbone
interactions.
NOTE - you need to use atom1 to be the e.g. ASP atom if distance is reset at NOTE - you need to use atom1 to be the e.g. ASP atom if distance is reset
return: [O1 -- H2-N3]. at return: [O1 -- H2-N3].
Also generalized to be able to be used for residue 'centers' for C=O COO interactions. Also generalized to be able to be used for residue 'centers' for C=O COO
interactions.
Args: Args:
atom1: first atom for calculation (could be None) atom1: first atom for calculation (could be None)
@@ -194,9 +203,11 @@ def hydrogen_bond_interaction(group1, group2, version):
# find the smallest distance between interacting atoms # find the smallest distance between interacting atoms
atoms1 = group1.get_interaction_atoms(group2) atoms1 = group1.get_interaction_atoms(group2)
atoms2 = group2.get_interaction_atoms(group1) atoms2 = group2.get_interaction_atoms(group1)
[closest_atom1, dist, closest_atom2] = get_smallest_distance(atoms1, atoms2) [closest_atom1, dist, closest_atom2] = get_smallest_distance(
atoms1, atoms2
)
if None in [closest_atom1, closest_atom2]: if None in [closest_atom1, closest_atom2]:
warning( _LOGGER.warning(
'Side chain interaction failed for {0:s} and {1:s}'.format( 'Side chain interaction failed for {0:s} and {1:s}'.format(
group1.label, group2.label)) group1.label, group2.label))
return None return None
@@ -210,11 +221,16 @@ def hydrogen_bond_interaction(group1, group2, version):
return None return None
# check that bond distance criteria is met # check that bond distance criteria is met
min_hbond_dist = version.parameters.min_bond_distance_for_hydrogen_bonds min_hbond_dist = version.parameters.min_bond_distance_for_hydrogen_bonds
if group1.atom.is_atom_within_bond_distance(group2.atom, min_hbond_dist, 1): if group1.atom.is_atom_within_bond_distance(
group2.atom, min_hbond_dist, 1
):
return None return None
# set angle factor # set angle factor
f_angle = 1.0 f_angle = 1.0
if group2.type in version.parameters.angular_dependent_sidechain_interactions: if (
group2.type in
version.parameters.angular_dependent_sidechain_interactions
):
if closest_atom2.element == 'H': if closest_atom2.element == 'H':
heavy_atom = closest_atom2.bonded_atoms[0] heavy_atom = closest_atom2.bonded_atoms[0]
hydrogen = closest_atom2 hydrogen = closest_atom2
@@ -225,7 +241,10 @@ def hydrogen_bond_interaction(group1, group2, version):
# is closer to the titratable atom than the hydrogen. In either # is closer to the titratable atom than the hydrogen. In either
# case, we set the angle factor to 0 # case, we set the angle factor to 0
f_angle = 0.0 f_angle = 0.0
elif group1.type in version.parameters.angular_dependent_sidechain_interactions: elif (
group1.type in
version.parameters.angular_dependent_sidechain_interactions
):
if closest_atom1.element == 'H': if closest_atom1.element == 'H':
heavy_atom = closest_atom1.bonded_atoms[0] heavy_atom = closest_atom1.bonded_atoms[0]
hydrogen = closest_atom1 hydrogen = closest_atom1
@@ -236,7 +255,9 @@ def hydrogen_bond_interaction(group1, group2, version):
# is closer to the titratable atom than the hydrogen. In either # is closer to the titratable atom than the hydrogen. In either
# case, we set the angle factor to 0 # case, we set the angle factor to 0
f_angle = 0.0 f_angle = 0.0
weight = version.calculate_pair_weight(group1.num_volume, group2.num_volume) weight = version.calculate_pair_weight(
group1.num_volume, group2.num_volume
)
exception, value = version.check_exceptions(group1, group2) exception, value = version.check_exceptions(group1, group2)
if exception: if exception:
# Do nothing, value should have been assigned. # Do nothing, value should have been assigned.
@@ -256,12 +277,15 @@ def electrostatic_interaction(group1, group2, dist, version):
dist: distance between groups dist: distance between groups
version: version-specific object with parameters and functions version: version-specific object with parameters and functions
Returns: Returns:
electrostatic interaction energy or None (if no interaction is appropriate) electrostatic interaction energy or None (if no interaction is
appropriate)
""" """
# check if we should do coulomb interaction at all # check if we should do coulomb interaction at all
if not version.check_coulomb_pair(group1, group2, dist): if not version.check_coulomb_pair(group1, group2, dist):
return None return None
weight = version.calculate_pair_weight(group1.num_volume, group2.num_volume) weight = version.calculate_pair_weight(
group1.num_volume, group2.num_volume
)
value = version.calculate_coulomb_energy(dist, weight) value = version.calculate_coulomb_energy(dist, weight)
return value return value
@@ -334,7 +358,9 @@ def backbone_reorganization(_, conformation):
weight = titratable_group.buried weight = titratable_group.buried
dpka = 0.00 dpka = 0.00
for bbc_group in bbc_groups: for bbc_group in bbc_groups:
center = [titratable_group.x, titratable_group.y, titratable_group.z] center = [
titratable_group.x, titratable_group.y, titratable_group.z
]
atom2 = bbc_group.get_interaction_atoms(titratable_group)[0] atom2 = bbc_group.get_interaction_atoms(titratable_group)[0]
dist, f_angle, _ = angle_distance_factors(atom2=atom2, dist, f_angle, _ = angle_distance_factors(atom2=atom2,
atom3=bbc_group.atom, atom3=bbc_group.atom,
@@ -359,7 +385,8 @@ def check_exceptions(version, group1, group2):
group2: second group for check group2: second group for check
Returns: Returns:
1. Boolean indicating atypical behavior, 1. Boolean indicating atypical behavior,
2. value associated with atypical interaction (None if Boolean is False) 2. value associated with atypical interaction (None if Boolean is
False)
""" """
res_type1 = group1.type res_type1 = group1.type
res_type2 = group2.type res_type2 = group2.type
@@ -398,7 +425,8 @@ def check_coo_arg_exception(group_coo, group_arg, version):
version: version object version: version object
Returns: Returns:
1. Boolean indicating atypical behavior, 1. Boolean indicating atypical behavior,
2. value associated with atypical interaction (None if Boolean is False) 2. value associated with atypical interaction (None if Boolean is
False)
""" """
exception = True exception = True
value_tot = 0.00 value_tot = 0.00
@@ -409,13 +437,18 @@ def check_coo_arg_exception(group_coo, group_arg, version):
atoms_arg.extend(group_arg.get_interaction_atoms(group_coo)) atoms_arg.extend(group_arg.get_interaction_atoms(group_coo))
for _ in ["shortest", "runner-up"]: for _ in ["shortest", "runner-up"]:
# find the closest interaction pair # find the closest interaction pair
[closest_coo_atom, dist, closest_arg_atom] = get_smallest_distance(atoms_coo, [closest_coo_atom, dist, closest_arg_atom] = get_smallest_distance(
atoms_arg) atoms_coo, atoms_arg
[dpka_max, cutoff] = version.get_hydrogen_bond_parameters(closest_coo_atom, )
closest_arg_atom) [dpka_max, cutoff] = version.get_hydrogen_bond_parameters(
closest_coo_atom, closest_arg_atom
)
# calculate and sum up interaction energy # calculate and sum up interaction energy
f_angle = 1.00 f_angle = 1.00
if group_arg.type in version.parameters.angular_dependent_sidechain_interactions: if (
group_arg.type in
version.parameters.angular_dependent_sidechain_interactions
):
atom3 = closest_arg_atom.bonded_atoms[0] atom3 = closest_arg_atom.bonded_atoms[0]
dist, f_angle, _ = angle_distance_factors(closest_coo_atom, dist, f_angle, _ = angle_distance_factors(closest_coo_atom,
closest_arg_atom, closest_arg_atom,
@@ -437,18 +470,23 @@ def check_coo_coo_exception(group1, group2, version):
version: version object version: version object
Returns: Returns:
1. Boolean indicating atypical behavior, 1. Boolean indicating atypical behavior,
2. value associated with atypical interaction (None if Boolean is False) 2. value associated with atypical interaction (None if Boolean is
False)
""" """
exception = True exception = True
interact_groups12 = group1.get_interaction_atoms(group2) interact_groups12 = group1.get_interaction_atoms(group2)
interact_groups21 = group2.get_interaction_atoms(group1) interact_groups21 = group2.get_interaction_atoms(group1)
[closest_atom1, dist, closest_atom2] = get_smallest_distance(interact_groups12, [closest_atom1, dist, closest_atom2] = get_smallest_distance(
interact_groups21) interact_groups12, interact_groups21
[dpka_max, cutoff] = version.get_hydrogen_bond_parameters(closest_atom1, )
closest_atom2) [dpka_max, cutoff] = version.get_hydrogen_bond_parameters(
closest_atom1, closest_atom2
)
f_angle = 1.00 f_angle = 1.00
value = hydrogen_bond_energy(dist, dpka_max, cutoff, f_angle) value = hydrogen_bond_energy(dist, dpka_max, cutoff, f_angle)
weight = calculate_pair_weight(version.parameters, group1.num_volume, group2.num_volume) weight = calculate_pair_weight(
version.parameters, group1.num_volume, group2.num_volume
)
value = value * (1.0 + weight) value = value * (1.0 + weight)
return exception, value return exception, value
@@ -462,7 +500,8 @@ def check_coo_his_exception(group1, group2, version):
version: version object version: version object
Returns: Returns:
1. Boolean indicating atypical behavior, 1. Boolean indicating atypical behavior,
2. value associated with atypical interaction (None if Boolean is False) 2. value associated with atypical interaction (None if Boolean is
False)
""" """
exception = False exception = False
if check_buried(group1.num_volume, group2.num_volume): if check_buried(group1.num_volume, group2.num_volume):
@@ -479,7 +518,8 @@ def check_oco_his_exception(group1, group2, version):
version: version object version: version object
Returns: Returns:
1. Boolean indicating atypical behavior, 1. Boolean indicating atypical behavior,
2. value associated with atypical interaction (None if Boolean is False) 2. value associated with atypical interaction (None if Boolean is
False)
""" """
exception = False exception = False
if check_buried(group1.num_volume, group2.num_volume): if check_buried(group1.num_volume, group2.num_volume):
@@ -496,7 +536,8 @@ def check_cys_his_exception(group1, group2, version):
version: version object version: version object
Returns: Returns:
1. Boolean indicating atypical behavior, 1. Boolean indicating atypical behavior,
2. value associated with atypical interaction (None if Boolean is False) 2. value associated with atypical interaction (None if Boolean is
False)
""" """
exception = False exception = False
if check_buried(group1.num_volume, group2.num_volume): if check_buried(group1.num_volume, group2.num_volume):
@@ -513,7 +554,8 @@ def check_cys_cys_exception(group1, group2, version):
version: version object version: version object
Returns: Returns:
1. Boolean indicating atypical behavior, 1. Boolean indicating atypical behavior,
2. value associated with atypical interaction (None if Boolean is False) 2. value associated with atypical interaction (None if Boolean is
False)
""" """
exception = False exception = False
if check_buried(group1.num_volume, group2.num_volume): if check_buried(group1.num_volume, group2.num_volume):

View File

@@ -4,12 +4,15 @@ Data structures for groups
Routines and classes for storing groups important to PROPKA calculations. Routines and classes for storing groups important to PROPKA calculations.
""" """
import logging
import math import math
import propka.ligand import propka.ligand
import propka.protonate import propka.protonate
from propka.ligand_pka_values import LigandPkaValues from propka.ligand_pka_values import LigandPkaValues
from propka.determinant import Determinant from propka.determinant import Determinant
from propka.lib import info, warning
_LOGGER = logging.getLogger(__name__)
# Constants that start with "UNK_" are a mystery to me # Constants that start with "UNK_" are a mystery to me
@@ -20,7 +23,7 @@ EXPECTED_ATOMS_ACID_INTERACTIONS = {
'COO': {'O': 2}, 'HIS': {'H': 2, 'N': 2}, 'CYS': {'S': 1}, 'TYR': {'O': 1}, 'COO': {'O': 2}, 'HIS': {'H': 2, 'N': 2}, 'CYS': {'S': 1}, 'TYR': {'O': 1},
'LYS': {'N': 1}, 'ARG': {'H': 5, 'N': 3}, 'ROH': {'O': 1}, 'LYS': {'N': 1}, 'ARG': {'H': 5, 'N': 3}, 'ROH': {'O': 1},
'AMD': {'H': 2, 'N': 1}, 'TRP': {'H': 1, 'N': 1}, 'N+': {'N': 1}, 'AMD': {'H': 2, 'N': 1}, 'TRP': {'H': 1, 'N': 1}, 'N+': {'N': 1},
'C-': {'O': 2}, 'BBN': {'H': 1, 'N': 1,}, 'BBC': {'O': 1}, 'C-': {'O': 2}, 'BBN': {'H': 1, 'N': 1}, 'BBC': {'O': 1},
'NAR': {'H': 1, 'N': 1}, 'NAM': {'H': 1, 'N': 1}, 'F': {'F': 1}, 'NAR': {'H': 1, 'N': 1}, 'NAM': {'H': 1, 'N': 1}, 'F': {'F': 1},
'Cl': {'Cl': 1}, 'OH': {'H': 1, 'O': 1}, 'OP': {'O': 1}, 'O3': {'O': 1}, 'Cl': {'Cl': 1}, 'OH': {'H': 1, 'O': 1}, 'OP': {'O': 1}, 'O3': {'O': 1},
'O2': {'O': 1}, 'SH': {'S': 1}, 'CG': {'H': 5, 'N': 3}, 'O2': {'O': 1}, 'SH': {'S': 1}, 'CG': {'H': 5, 'N': 3},
@@ -106,9 +109,9 @@ class Group:
other: other group for coupling other: other group for coupling
""" """
# do the coupling # do the coupling
if not other in self.covalently_coupled_groups: if other not in self.covalently_coupled_groups:
self.covalently_coupled_groups.append(other) self.covalently_coupled_groups.append(other)
if not self in other.covalently_coupled_groups: if self not in other.covalently_coupled_groups:
other.covalently_coupled_groups.append(self) other.covalently_coupled_groups.append(self)
def couple_non_covalently(self, other): def couple_non_covalently(self, other):
@@ -118,9 +121,9 @@ class Group:
other: other group for coupling other: other group for coupling
""" """
# do the coupling # do the coupling
if not other in self.non_covalently_coupled_groups: if other not in self.non_covalently_coupled_groups:
self.non_covalently_coupled_groups.append(other) self.non_covalently_coupled_groups.append(other)
if not self in other.non_covalently_coupled_groups: if self not in other.non_covalently_coupled_groups:
other.non_covalently_coupled_groups.append(self) other.non_covalently_coupled_groups.append(self)
def get_covalently_coupled_groups(self): def get_covalently_coupled_groups(self):
@@ -399,26 +402,26 @@ class Group:
str_ = 'Missing atoms or failed protonation for ' str_ = 'Missing atoms or failed protonation for '
str_ += '{0:s} ({1:s}) -- please check the structure'.format( str_ += '{0:s} ({1:s}) -- please check the structure'.format(
self.label, self.type) self.label, self.type)
warning(str_) _LOGGER.warning(str_)
warning('{0:s}'.format(str(self))) _LOGGER.warning('{0:s}'.format(str(self)))
num_acid = sum( num_acid = sum(
[EXPECTED_ATOMS_ACID_INTERACTIONS[self.type][e] [EXPECTED_ATOMS_ACID_INTERACTIONS[self.type][e]
for e in EXPECTED_ATOMS_ACID_INTERACTIONS[self.type].keys()]) for e in EXPECTED_ATOMS_ACID_INTERACTIONS[self.type].keys()])
num_base = sum( num_base = sum(
[EXPECTED_ATOMS_BASE_INTERACTIONS[self.type][e] [EXPECTED_ATOMS_BASE_INTERACTIONS[self.type][e]
for e in EXPECTED_ATOMS_BASE_INTERACTIONS[self.type].keys()]) for e in EXPECTED_ATOMS_BASE_INTERACTIONS[self.type].keys()])
warning( _LOGGER.warning(
'Expected {0:d} interaction atoms for acids, found:'.format( 'Expected {0:d} interaction atoms for acids, found:'.format(
num_acid)) num_acid))
for i in range(len(self.interaction_atoms_for_acids)): for i in range(len(self.interaction_atoms_for_acids)):
warning( _LOGGER.warning(
' {0:s}'.format( ' {0:s}'.format(
str(self.interaction_atoms_for_acids[i]))) str(self.interaction_atoms_for_acids[i])))
warning( _LOGGER.warning(
'Expected {0:d} interaction atoms for bases, found:'.format( 'Expected {0:d} interaction atoms for bases, found:'.format(
num_base)) num_base))
for i in range(len(self.interaction_atoms_for_bases)): for i in range(len(self.interaction_atoms_for_bases)):
warning( _LOGGER.warning(
' {0:s}'.format( ' {0:s}'.format(
str(self.interaction_atoms_for_bases[i]))) str(self.interaction_atoms_for_bases[i])))
@@ -672,7 +675,9 @@ class HISGroup(Group):
# Find the atoms in the histidine ring # Find the atoms in the histidine ring
ring_atoms = propka.ligand.is_ring_member(self.atom) ring_atoms = propka.ligand.is_ring_member(self.atom)
if len(ring_atoms) != 5: if len(ring_atoms) != 5:
warning('His group does not seem to contain a ring', self) _LOGGER.warning(
'His group does not seem to contain a ring %s', self
)
# protonate ring # protonate ring
for ring_atom in ring_atoms: for ring_atom in ring_atoms:
PROTONATOR.protonate_atom(ring_atom) PROTONATOR.protonate_atom(ring_atom)
@@ -682,7 +687,8 @@ class HISGroup(Group):
else: else:
# Missing side-chain atoms # Missing side-chain atoms
self.set_center([self.atom]) self.set_center([self.atom])
# TODO - perhaps it would be better to ignore this group completely? # TODO - perhaps it would be better to ignore this group
# completely?
# find the hydrogens on the ring-nitrogens # find the hydrogens on the ring-nitrogens
hydrogens = [] hydrogens = []
nitrogens = [ra for ra in ring_atoms if ra.element == 'N'] nitrogens = [ra for ra in ring_atoms if ra.element == 'N']
@@ -815,7 +821,7 @@ class CtermGroup(Group):
the_other_oxygen = the_carbons[0].get_bonded_elements('O') the_other_oxygen = the_carbons[0].get_bonded_elements('O')
the_other_oxygen.remove(self.atom) the_other_oxygen.remove(self.atom)
# set the center and interaction atoms # set the center and interaction atoms
the_oxygens = [self.atom]+ the_other_oxygen the_oxygens = [self.atom] + the_other_oxygen
self.set_center(the_oxygens) self.set_center(the_oxygens)
self.set_interaction_atoms(the_oxygens, the_oxygens) self.set_interaction_atoms(the_oxygens, the_oxygens)
@@ -866,7 +872,7 @@ class NARGroup(Group):
Group.__init__(self, atom) Group.__init__(self, atom)
self.type = 'NAR' self.type = 'NAR'
self.residue_type = 'NAR' self.residue_type = 'NAR'
info('Found NAR group:', atom) _LOGGER.info('Found NAR group: %s', atom)
def setup_atoms(self): def setup_atoms(self):
"""Set up atoms in group.""" """Set up atoms in group."""
@@ -889,7 +895,7 @@ class NAMGroup(Group):
Group.__init__(self, atom) Group.__init__(self, atom)
self.type = 'NAM' self.type = 'NAM'
self.residue_type = 'NAM' self.residue_type = 'NAM'
info('Found NAM group:', atom) _LOGGER.info('Found NAM group: %s', atom)
def setup_atoms(self): def setup_atoms(self):
"""Set up atoms in this group.""" """Set up atoms in this group."""
@@ -909,7 +915,7 @@ class FGroup(Group):
Group.__init__(self, atom) Group.__init__(self, atom)
self.type = 'F' self.type = 'F'
self.residue_type = 'F' self.residue_type = 'F'
info('Found F group:', atom) _LOGGER.info('Found F group: %s', atom)
class ClGroup(Group): class ClGroup(Group):
@@ -919,7 +925,7 @@ class ClGroup(Group):
Group.__init__(self, atom) Group.__init__(self, atom)
self.type = 'Cl' self.type = 'Cl'
self.residue_type = 'Cl' self.residue_type = 'Cl'
info('Found Cl group:', atom) _LOGGER.info('Found Cl group: %s', atom)
class OHGroup(Group): class OHGroup(Group):
@@ -929,7 +935,7 @@ class OHGroup(Group):
Group.__init__(self, atom) Group.__init__(self, atom)
self.type = 'OH' self.type = 'OH'
self.residue_type = 'OH' self.residue_type = 'OH'
info('Found OH group:', atom) _LOGGER.info('Found OH group: %s', atom)
def setup_atoms(self): def setup_atoms(self):
"""Set up atoms in this group.""" """Set up atoms in this group."""
@@ -949,7 +955,7 @@ class OPGroup(Group):
Group.__init__(self, atom) Group.__init__(self, atom)
self.type = 'OP' self.type = 'OP'
self.residue_type = 'OP' self.residue_type = 'OP'
info('Found OP group:', atom) _LOGGER.info('Found OP group: %s', atom)
def setup_atoms(self): def setup_atoms(self):
"""Set up atoms in this group.""" """Set up atoms in this group."""
@@ -970,7 +976,7 @@ class O3Group(Group):
Group.__init__(self, atom) Group.__init__(self, atom)
self.type = 'O3' self.type = 'O3'
self.residue_type = 'O3' self.residue_type = 'O3'
info('Found O3 group:', atom) _LOGGER.info('Found O3 group: %s', atom)
class O2Group(Group): class O2Group(Group):
@@ -983,7 +989,7 @@ class O2Group(Group):
Group.__init__(self, atom) Group.__init__(self, atom)
self.type = 'O2' self.type = 'O2'
self.residue_type = 'O2' self.residue_type = 'O2'
info('Found O2 group:', atom) _LOGGER.info('Found O2 group: %s', atom)
class SHGroup(Group): class SHGroup(Group):
@@ -993,7 +999,7 @@ class SHGroup(Group):
Group.__init__(self, atom) Group.__init__(self, atom)
self.type = 'SH' self.type = 'SH'
self.residue_type = 'SH' self.residue_type = 'SH'
info('Found SH group:', atom) _LOGGER.info('Found SH group: %s', atom)
class CGGroup(Group): class CGGroup(Group):
@@ -1003,7 +1009,7 @@ class CGGroup(Group):
Group.__init__(self, atom) Group.__init__(self, atom)
self.type = 'CG' self.type = 'CG'
self.residue_type = 'CG' self.residue_type = 'CG'
info('Found CG group:', atom) _LOGGER.info('Found CG group: %s', atom)
def setup_atoms(self): def setup_atoms(self):
"""Set up atoms in this group.""" """Set up atoms in this group."""
@@ -1026,7 +1032,7 @@ class C2NGroup(Group):
Group.__init__(self, atom) Group.__init__(self, atom)
self.type = 'C2N' self.type = 'C2N'
self.residue_type = 'C2N' self.residue_type = 'C2N'
info('Found C2N group:', atom) _LOGGER.info('Found C2N group: %s', atom)
def setup_atoms(self): def setup_atoms(self):
"""Set up atoms in this group.""" """Set up atoms in this group."""
@@ -1050,7 +1056,7 @@ class OCOGroup(Group):
Group.__init__(self, atom) Group.__init__(self, atom)
self.type = 'OCO' self.type = 'OCO'
self.residue_type = 'OCO' self.residue_type = 'OCO'
info('Found OCO group:', atom) _LOGGER.info('Found OCO group: %s', atom)
def setup_atoms(self): def setup_atoms(self):
"""Set up atoms in group.""" """Set up atoms in group."""
@@ -1071,7 +1077,7 @@ class N30Group(Group):
Group.__init__(self, atom) Group.__init__(self, atom)
self.type = 'N30' self.type = 'N30'
self.residue_type = 'N30' self.residue_type = 'N30'
info('Found N30 group:', atom) _LOGGER.info('Found N30 group: %s', atom)
def setup_atoms(self): def setup_atoms(self):
"""Set up atoms in this group.""" """Set up atoms in this group."""
@@ -1093,7 +1099,7 @@ class N31Group(Group):
Group.__init__(self, atom) Group.__init__(self, atom)
self.type = 'N31' self.type = 'N31'
self.residue_type = 'N31' self.residue_type = 'N31'
info('Found N31 group:', atom) _LOGGER.info('Found N31 group: %s', atom)
def setup_atoms(self): def setup_atoms(self):
"""Set up atoms in this group.""" """Set up atoms in this group."""
@@ -1115,7 +1121,7 @@ class N32Group(Group):
Group.__init__(self, atom) Group.__init__(self, atom)
self.type = 'N32' self.type = 'N32'
self.residue_type = 'N32' self.residue_type = 'N32'
info('Found N32 group:', atom) _LOGGER.info('Found N32 group: %s', atom)
def setup_atoms(self): def setup_atoms(self):
"""Set up atoms in this group.""" """Set up atoms in this group."""
@@ -1137,7 +1143,7 @@ class N33Group(Group):
Group.__init__(self, atom) Group.__init__(self, atom)
self.type = 'N33' self.type = 'N33'
self.residue_type = 'N33' self.residue_type = 'N33'
info('Found N33 group:', atom) _LOGGER.info('Found N33 group: %s', atom)
def setup_atoms(self): def setup_atoms(self):
"""Set up atoms in this group.""" """Set up atoms in this group."""
@@ -1159,7 +1165,7 @@ class NP1Group(Group):
Group.__init__(self, atom) Group.__init__(self, atom)
self.type = 'NP1' self.type = 'NP1'
self.residue_type = 'NP1' self.residue_type = 'NP1'
info('Found NP1 group:', atom) _LOGGER.info('Found NP1 group: %s', atom)
def setup_atoms(self): def setup_atoms(self):
"""Set up atoms in group.""" """Set up atoms in group."""
@@ -1181,7 +1187,7 @@ class N1Group(Group):
Group.__init__(self, atom) Group.__init__(self, atom)
self.type = 'N1' self.type = 'N1'
self.residue_type = 'N1' self.residue_type = 'N1'
info('Found N1 group:', atom) _LOGGER.info('Found N1 group: %s', atom)
class IonGroup(Group): class IonGroup(Group):
@@ -1191,7 +1197,7 @@ class IonGroup(Group):
Group.__init__(self, atom) Group.__init__(self, atom)
self.type = 'ION' self.type = 'ION'
self.residue_type = atom.res_name.strip() self.residue_type = atom.res_name.strip()
info('Found ion group:', atom) _LOGGER.info('Found ion group: %s', atom)
class NonTitratableLigandGroup(Group): class NonTitratableLigandGroup(Group):
@@ -1223,8 +1229,10 @@ class TitratableLigandGroup(Group):
# this is not true if we are reading an input file # this is not true if we are reading an input file
if atom.marvin_pka: if atom.marvin_pka:
self.model_pka = atom.marvin_pka self.model_pka = atom.marvin_pka
info('Titratable ligand group ', _LOGGER.info(
atom, self.model_pka, self.charge) 'Titratable ligand group %s %s %s',
atom, self.model_pka, self.charge
)
self.model_pka_set = True self.model_pka_set = True
@@ -1273,12 +1281,12 @@ def is_protein_group(parameters, atom):
""" """
if atom.type != 'atom': if atom.type != 'atom':
return None return None
### Check for termial groups # Check for termial groups
if atom.terminal == 'N+': if atom.terminal == 'N+':
return NtermGroup(atom) return NtermGroup(atom)
elif atom.terminal == 'C-': elif atom.terminal == 'C-':
return CtermGroup(atom) return CtermGroup(atom)
### Backbone # Backbone
if atom.type == 'atom' and atom.name == 'N': if atom.type == 'atom' and atom.name == 'N':
# ignore proline backbone nitrogens # ignore proline backbone nitrogens
if atom.res_name != 'PRO': if atom.res_name != 'PRO':
@@ -1287,7 +1295,7 @@ def is_protein_group(parameters, atom):
# ignore C- carboxyl # ignore C- carboxyl
if atom.count_bonded_elements('O') == 1: if atom.count_bonded_elements('O') == 1:
return BBCGroup(atom) return BBCGroup(atom)
### Filters for side chains based on PDB protein atom names # Filters for side chains based on PDB protein atom names
key = '{0:s}-{1:s}'.format(atom.res_name, atom.name) key = '{0:s}-{1:s}'.format(atom.res_name, atom.name)
if key in parameters.protein_group_mapping.keys(): if key in parameters.protein_group_mapping.keys():
class_str = "{0:s}Group".format(parameters.protein_group_mapping[key]) class_str = "{0:s}Group".format(parameters.protein_group_mapping[key])
@@ -1305,7 +1313,7 @@ def is_ligand_group_by_groups(_, atom):
Returns: Returns:
group for atom or None group for atom or None
""" """
### Ligand group filters # Ligand group filters
if atom.type != 'hetatm': if atom.type != 'hetatm':
return None return None
PROTONATOR.protonate_atom(atom) PROTONATOR.protonate_atom(atom)
@@ -1327,7 +1335,8 @@ def is_ligand_group_by_groups(_, atom):
if atom.sybyl_type == 'N.1': if atom.sybyl_type == 'N.1':
return N1Group(atom) return N1Group(atom)
if atom.sybyl_type == 'N.pl3': if atom.sybyl_type == 'N.pl3':
# make sure that this atom is not part of a guadinium or amidinium group # make sure that this atom is not part of a guadinium or amidinium
# group
bonded_carbons = atom.get_bonded_elements('C') bonded_carbons = atom.get_bonded_elements('C')
if len(bonded_carbons) == 1: if len(bonded_carbons) == 1:
bonded_nitrogens = bonded_carbons[0].get_bonded_elements('N') bonded_nitrogens = bonded_carbons[0].get_bonded_elements('N')
@@ -1426,6 +1435,7 @@ def is_ion_group(parameters, atom):
return IonGroup(atom) return IonGroup(atom)
return None return None
def initialize_atom_group(atom): def initialize_atom_group(atom):
"""Initialize an atom group. """Initialize an atom group.

View File

@@ -6,12 +6,14 @@ Calculations related to hydrogen placement.
""" """
import math import math
from propka.lib import info import logging
from propka.protonate import Protonate from propka.protonate import Protonate
from propka.bonds import BondMaker from propka.bonds import BondMaker
from propka.atom import Atom from propka.atom import Atom
_LOGGER = logging.getLogger(__name__)
def setup_bonding_and_protonation(molecular_container): def setup_bonding_and_protonation(molecular_container):
"""Set up bonding and protonation for a molecule. """Set up bonding and protonation for a molecule.
@@ -68,7 +70,7 @@ def protonate_30_style(molecular_container):
molecular_container: molecule molecular_container: molecule
""" """
for name in molecular_container.conformation_names: for name in molecular_container.conformation_names:
info('Now protonating', name) _LOGGER.info('Now protonating %s', name)
# split atom into residues # split atom into residues
curres = -1000000 curres = -1000000
residue = [] residue = []
@@ -78,19 +80,19 @@ def protonate_30_style(molecular_container):
if atom.res_num != curres: if atom.res_num != curres:
curres = atom.res_num curres = atom.res_num
if len(residue) > 0: if len(residue) > 0:
#backbone # backbone
[o_atom, c_atom] = add_backbone_hydrogen( [o_atom, c_atom] = add_backbone_hydrogen(
residue, o_atom, c_atom) residue, o_atom, c_atom)
#arginine # arginine
if residue[0].res_name == 'ARG': if residue[0].res_name == 'ARG':
add_arg_hydrogen(residue) add_arg_hydrogen(residue)
#histidine # histidine
if residue[0].res_name == 'HIS': if residue[0].res_name == 'HIS':
add_his_hydrogen(residue) add_his_hydrogen(residue)
#tryptophan # tryptophan
if residue[0].res_name == 'TRP': if residue[0].res_name == 'TRP':
add_trp_hydrogen(residue) add_trp_hydrogen(residue)
#amides # amides
if residue[0].res_name in ['GLN', 'ASN']: if residue[0].res_name in ['GLN', 'ASN']:
add_amd_hydrogen(residue) add_amd_hydrogen(residue)
residue = [] residue = []
@@ -116,7 +118,6 @@ def add_arg_hydrogen(residue):
Returns: Returns:
list of hydrogen atoms list of hydrogen atoms
""" """
#info('Adding arg H',residue)
for atom in residue: for atom in residue:
if atom.name == "CD": if atom.name == "CD":
cd_atom = atom cd_atom = atom
@@ -348,5 +349,3 @@ def make_new_h(atom, x, y, z):
atom.bonded_atoms.append(new_h) atom.bonded_atoms.append(new_h)
atom.conformation_container.add_atom(new_h) atom.conformation_container.add_atom(new_h)
return new_h return new_h

View File

@@ -6,8 +6,11 @@ Iterative functions for pKa calculations. These appear to mostly
involve :class:`propka.determinant.Determinant` instances. involve :class:`propka.determinant.Determinant` instances.
""" """
import logging
from propka.determinant import Determinant from propka.determinant import Determinant
from propka.lib import info, debug
_LOGGER = logging.getLogger(__name__)
# TODO - these are undocumented constants # TODO - these are undocumented constants
@@ -215,9 +218,12 @@ def add_determinants(iterative_interactions, version, _=None):
iteratives.append(new_iterative) iteratives.append(new_iterative)
done_group.append(group) done_group.append(group)
# Initialize iterative scheme # Initialize iterative scheme
debug( _LOGGER.debug(
"\n --- pKa iterations ({0:d} groups, {1:d} interactions) ---".format( "\n --- pKa iterations ({0:d} groups, {1:d} interactions) "
len(iteratives), len(iterative_interactions))) "---".format(
len(iteratives), len(iterative_interactions)
)
)
converged = False converged = False
iteration = 0 iteration = 0
# set non-iterative pka values as first step # set non-iterative pka values as first step
@@ -267,26 +273,27 @@ def add_determinants(iterative_interactions, version, _=None):
itres.pka_iter.append(itres.pka_new) itres.pka_iter.append(itres.pka_new)
if iteration == 10: if iteration == 10:
info("did not converge in {0:d} iterations".format(iteration)) _LOGGER.info(
"did not converge in {0:d} iterations".format(iteration)
)
break break
# printing pKa iterations # printing pKa iterations
# formerly was conditioned on if options.verbosity >= 2 - now unnecessary # formerly was conditioned on if options.verbosity >= 2 - now unnecessary
str_ = ' ' str_ = ' '
for index in range(iteration+1): for index in range(iteration+1):
str_ += "{0:>8d}".format(index) str_ += "{0:>8d}".format(index)
debug(str_) _LOGGER.debug(str_)
for itres in iteratives: for itres in iteratives:
str_ = "{0:s} ".format(itres.label) str_ = "{0:s} ".format(itres.label)
for pka in itres.pka_iter: for pka in itres.pka_iter:
str_ += "{0:>8.2f}".format(pka) str_ += "{0:>8.2f}".format(pka)
if not itres.converged: if not itres.converged:
str_ += " *" str_ += " *"
debug(str_) _LOGGER.debug(str_)
# creating real determinants and adding them to group object # creating real determinants and adding them to group object
for itres in iteratives: for itres in iteratives:
for type_ in ['sidechain', 'backbone', 'coulomb']: for type_ in ['sidechain', 'backbone', 'coulomb']:
for interaction in itres.determinants[type_]: for interaction in itres.determinants[type_]:
#info('done',itres.group.label,interaction[0],interaction[1])
value = interaction[1] value = interaction[1]
if value > UNK_MIN_VALUE or value < -UNK_MIN_VALUE: if value > UNK_MIN_VALUE or value < -UNK_MIN_VALUE:
group = interaction[0] group = interaction[0]

View File

@@ -11,10 +11,7 @@ import argparse
import pkg_resources import pkg_resources
_LOGGER = logging.getLogger("propka") _LOGGER = logging.getLogger(__name__)
_STDOUT_HANDLER = logging.StreamHandler(sys.stdout)
_STDOUT_HANDLER.setFormatter(logging.Formatter("%(message)s"))
_LOGGER.addHandler(_STDOUT_HANDLER)
EXPECTED_ATOM_NUMBERS = {'ALA': 5, 'ARG': 11, 'ASN': 8, 'ASP': 8, 'CYS': 6, EXPECTED_ATOM_NUMBERS = {'ALA': 5, 'ARG': 11, 'ASN': 8, 'ASP': 8, 'CYS': 6,
@@ -53,7 +50,7 @@ def protein_precheck(conformations, names):
"{res:s} in conformation {conf:s}".format( "{res:s} in conformation {conf:s}".format(
num=len(res_atoms), res=residue_label, num=len(res_atoms), res=residue_label,
conf=name)) conf=name))
warning(str_) _LOGGER.warning(str_)
continue continue
# check number of atoms in residue # check number of atoms in residue
if len(res_atoms) != EXPECTED_ATOM_NUMBERS[res_name]: if len(res_atoms) != EXPECTED_ATOM_NUMBERS[res_name]:
@@ -61,7 +58,7 @@ def protein_precheck(conformations, names):
"{res:s} in conformation {conf:s}".format( "{res:s} in conformation {conf:s}".format(
num=len(res_atoms), res=residue_label, num=len(res_atoms), res=residue_label,
conf=name)) conf=name))
warning(str_) _LOGGER.warning(str_)
def resid_from_atom(atom): def resid_from_atom(atom):
@@ -101,7 +98,7 @@ def make_molecule(atom, atoms):
list of atoms list of atoms
""" """
bonded_atoms = [a for a in atoms if atom in a.bonded_atoms] bonded_atoms = [a for a in atoms if atom in a.bonded_atoms]
res_atoms = [atom,] res_atoms = [atom]
for bond_atom in bonded_atoms: for bond_atom in bonded_atoms:
if bond_atom in atoms: if bond_atom in atoms:
atoms.remove(bond_atom) atoms.remove(bond_atom)
@@ -190,9 +187,9 @@ def build_parser(parser=None):
"""Build an argument parser for PROPKA. """Build an argument parser for PROPKA.
Args: Args:
parser: existing parser. If this is not None, then the PROPKA parser will parser: existing parser. If this is not None, then the PROPKA parser
be created as a subparser to this existing parser. Otherwise, a will be created as a subparser to this existing parser.
new parser will be created. Otherwise, a new parser will be created.
Returns: Returns:
ArgumentParser object. ArgumentParser object.
""" """
@@ -209,7 +206,10 @@ def build_parser(parser=None):
group.add_argument("input_pdb", help="read data from <filename>") group.add_argument("input_pdb", help="read data from <filename>")
group.add_argument( group.add_argument(
"-f", "--file", action="append", dest="filenames", default=[], "-f", "--file", action="append", dest="filenames", default=[],
help="read data from <filename>, i.e. <filename> is added to arguments") help=(
"read data from <filename>, i.e. <filename> is added to arguments"
)
)
group.add_argument( group.add_argument(
"-r", "--reference", dest="reference", default="neutral", "-r", "--reference", dest="reference", default="neutral",
help=("setting which reference to use for stability calculations " help=("setting which reference to use for stability calculations "
@@ -221,8 +221,8 @@ def build_parser(parser=None):
group.add_argument( group.add_argument(
"-i", "--titrate_only", dest="titrate_only", "-i", "--titrate_only", dest="titrate_only",
help=('Treat only the specified residues as titratable. Value should ' help=('Treat only the specified residues as titratable. Value should '
'be a comma-separated list of "chain:resnum" values; for example: ' 'be a comma-separated list of "chain:resnum" values; for '
'-i "A:10,A:11"')) 'example: -i "A:10,A:11"'))
group.add_argument( group.add_argument(
"-t", "--thermophile", action="append", dest="thermophiles", "-t", "--thermophile", action="append", dest="thermophiles",
help=("defining a thermophile filename; usually used in " help=("defining a thermophile filename; usually used in "
@@ -265,7 +265,11 @@ def build_parser(parser=None):
"related properties [0.0, 14.0, 0.1]")) "related properties [0.0, 14.0, 0.1]"))
group.add_argument( group.add_argument(
"--mutator", dest="mutator", "--mutator", dest="mutator",
help="setting approach for mutating <filename> [alignment/scwrl/jackal]") help=(
"setting approach for mutating <filename> "
"[alignment/scwrl/jackal]"
)
)
group.add_argument( group.add_argument(
"--mutator-option", dest="mutator_options", action="append", "--mutator-option", dest="mutator_options", action="append",
help="setting property for mutator [e.g. type=\"side-chain\"]") help="setting property for mutator [e.g. type=\"side-chain\"]")
@@ -368,47 +372,3 @@ def get_sorted_configurations(configuration_keys):
def configuration_compare(conf): def configuration_compare(conf):
"""TODO - figure out what this function does.""" """TODO - figure out what this function does."""
return 100*int(conf[1:-2]) + ord(conf[-1]) return 100*int(conf[1:-2]) + ord(conf[-1])
def _args_to_str(arg_list):
"""Summarize list of arguments in string.
Args:
arg_list: list of arguments
Returns:
string
"""
return " ".join(map(str, arg_list))
def info(*args):
"""Log a message to info.
Level defaults to INFO unless overridden.
Args:
args: argument list
"""
_LOGGER.info(_args_to_str(args))
def debug(*args):
"""Log a message to debug.
Level defaults to DEBUG unless overridden.
Args:
args: argument list
"""
_LOGGER.debug(_args_to_str(args))
def warning(*args):
"""Log a message to warning.
Level defaults to WARNING unless overridden.
Args:
args: argument list
"""
_LOGGER.warning(_args_to_str(args))

View File

@@ -9,11 +9,15 @@ programs are required).
.. _Marvin: https://chemaxon.com/products/marvin .. _Marvin: https://chemaxon.com/products/marvin
""" """
import logging
import os import os
import subprocess import subprocess
import sys import sys
from propka.output import write_mol2_for_atoms from propka.output import write_mol2_for_atoms
from propka.lib import info, warning, split_atoms_into_molecules from propka.lib import split_atoms_into_molecules
_LOGGER = logging.getLogger(__name__)
class LigandPkaValues: class LigandPkaValues:
@@ -29,9 +33,9 @@ class LigandPkaValues:
# attempt to find Marvin executables in the path # attempt to find Marvin executables in the path
self.molconvert = self.find_in_path('molconvert') self.molconvert = self.find_in_path('molconvert')
self.cxcalc = self.find_in_path('cxcalc') self.cxcalc = self.find_in_path('cxcalc')
info('Found Marvin executables:') _LOGGER.info('Found Marvin executables:')
info(self.cxcalc) _LOGGER.info(self.cxcalc)
info(self.molconvert) _LOGGER.info(self.molconvert)
@staticmethod @staticmethod
def find_in_path(program): def find_in_path(program):
@@ -50,7 +54,7 @@ class LigandPkaValues:
if len(locs) == 0: if len(locs) == 0:
str_ = "'Error: Could not find {0:s}.".format(program) str_ = "'Error: Could not find {0:s}.".format(program)
str_ += ' Please make sure that it is found in the path.' str_ += ' Please make sure that it is found in the path.'
info(str_) _LOGGER.info(str_)
sys.exit(-1) sys.exit(-1)
return locs[0] return locs[0]
@@ -146,7 +150,7 @@ class LigandPkaValues:
"Didn't find a user-modified file '{0:s}' " "Didn't find a user-modified file '{0:s}' "
"- generating one".format( "- generating one".format(
filename)) filename))
warning(errstr) _LOGGER.warning(errstr)
write_mol2_for_atoms(atoms, filename) write_mol2_for_atoms(atoms, filename)
# Marvin calculate pKa values # Marvin calculate pKa values
fmt = ( fmt = (
@@ -159,16 +163,21 @@ class LigandPkaValues:
[self.cxcalc, filename]+options.split(), stdout=subprocess.PIPE, [self.cxcalc, filename]+options.split(), stdout=subprocess.PIPE,
stderr=subprocess.PIPE).communicate() stderr=subprocess.PIPE).communicate()
if len(errors) > 0: if len(errors) > 0:
info('***********************************************************' _LOGGER.info(
'***********************************************************'
'*********************************************') '*********************************************')
info('* Warning: Marvin execution failed: ' _LOGGER.info(
'* Warning: Marvin execution failed: '
' *') ' *')
info('* {0:<100s} *'.format(errors)) _LOGGER.info('* {0:<100s} *'.format(errors))
info('* ' _LOGGER.info(
'* '
' *') ' *')
info('* Please edit the ligand mol2 file and re-run PropKa with ' _LOGGER.info(
'* Please edit the ligand mol2 file and re-run PropKa with '
'the -l option: {0:>29s} *'.format(filename)) 'the -l option: {0:>29s} *'.format(filename))
info('***********************************************************' _LOGGER.info(
'***********************************************************'
'*********************************************') '*********************************************')
sys.exit(-1) sys.exit(-1)
# extract calculated pkas # extract calculated pkas
@@ -177,7 +186,9 @@ class LigandPkaValues:
for i, index in enumerate(indices): for i, index in enumerate(indices):
atoms[index].marvin_pka = pkas[i] atoms[index].marvin_pka = pkas[i]
atoms[index].charge = {'a': -1, 'b': 1}[types[i]] atoms[index].charge = {'a': -1, 'b': 1}[types[i]]
info('{0:s} model pKa: {1:<.2f}'.format(atoms[index], pkas[i])) _LOGGER.info(
'{0:s} model pKa: {1:<.2f}'.format(atoms[index], pkas[i])
)
@staticmethod @staticmethod
def extract_pkas(output): def extract_pkas(output):

View File

@@ -4,11 +4,15 @@ PDB molecular container
Molecular container for storing all contents of PDB files. Molecular container for storing all contents of PDB files.
""" """
import logging
import os import os
import propka.version import propka.version
from propka.output import write_propka, write_pka, print_header, print_result from propka.output import write_propka, write_pka, print_header, print_result
from propka.conformation_container import ConformationContainer from propka.conformation_container import ConformationContainer
from propka.lib import info, warning, make_grid from propka.lib import make_grid
_LOGGER = logging.getLogger(__name__)
# TODO - these are constants whose origins are a little murky # TODO - these are constants whose origins are a little murky
@@ -55,13 +59,11 @@ class MolecularContainer:
def find_covalently_coupled_groups(self): def find_covalently_coupled_groups(self):
"""Find covalently coupled groups.""" """Find covalently coupled groups."""
info('-' * 103)
for name in self.conformation_names: for name in self.conformation_names:
self.conformations[name].find_covalently_coupled_groups() self.conformations[name].find_covalently_coupled_groups()
def find_non_covalently_coupled_groups(self): def find_non_covalently_coupled_groups(self):
"""Find non-covalently coupled groups.""" """Find non-covalently coupled groups."""
info('-' * 103)
verbose = self.options.display_coupled_residues verbose = self.options.display_coupled_residues
for name in self.conformation_names: for name in self.conformation_names:
self.conformations[name].find_non_covalently_coupled_groups( self.conformations[name].find_non_covalently_coupled_groups(
@@ -110,7 +112,7 @@ class MolecularContainer:
'Group {0:s} could not be found in ' 'Group {0:s} could not be found in '
'conformation {1:s}.'.format( 'conformation {1:s}.'.format(
group.atom.residue_label, name)) group.atom.residue_label, name))
warning(str_) _LOGGER.warning(str_)
# ... and store the average value # ... and store the average value
avr_group = avr_group / len(self.conformation_names) avr_group = avr_group / len(self.conformation_names)
avr_conformation.groups.append(avr_group) avr_conformation.groups.append(avr_group)

View File

@@ -4,11 +4,14 @@ Output
Output routines. Output routines.
""" """
import logging
from datetime import date from datetime import date
from propka.lib import info
from . import __version__ from . import __version__
_LOGGER = logging.getLogger(__name__)
def open_file_for_writing(input_file): def open_file_for_writing(input_file):
"""Open file or file-like stream for writing. """Open file or file-like stream for writing.
@@ -49,7 +52,7 @@ def print_header():
str_ = "{0:s}\n".format(get_propka_header()) str_ = "{0:s}\n".format(get_propka_header())
str_ += "{0:s}\n".format(get_references_header()) str_ += "{0:s}\n".format(get_references_header())
str_ += "{0:s}\n".format(get_warning_header()) str_ += "{0:s}\n".format(get_warning_header())
info(str_) _LOGGER.info("\n%s", str_)
def write_pdb_for_protein( def write_pdb_for_protein(
@@ -69,7 +72,7 @@ def write_pdb_for_protein(
filename = "{0:s}.pdb".format(protein.name) filename = "{0:s}.pdb".format(protein.name)
# TODO - this would be better as a context manager # TODO - this would be better as a context manager
pdbfile = open(filename, 'w') pdbfile = open(filename, 'w')
info("writing pdbfile {0:s}".format(filename)) _LOGGER.info("writing pdbfile {0:s}".format(filename))
close_file = True close_file = True
else: else:
# don't close the file, it was opened in a different place # don't close the file, it was opened in a different place
@@ -122,7 +125,7 @@ def write_pka(protein, parameters, filename=None, conformation='1A',
# TODO - this would be much better with a context manager # TODO - this would be much better with a context manager
file_ = open(filename, 'w') file_ = open(filename, 'w')
if verbose: if verbose:
info("Writing {0:s}".format(filename)) _LOGGER.info("Writing {0:s}".format(filename))
# writing propka header # writing propka header
str_ = "{0:s}\n".format(get_propka_header()) str_ = "{0:s}\n".format(get_propka_header())
str_ += "{0:s}\n".format(get_references_header()) str_ += "{0:s}\n".format(get_references_header())
@@ -172,7 +175,7 @@ def print_tm_profile(protein, reference="neutral", window=[0., 14., 1.],
and (ph % window[2] < 0.01 and (ph % window[2] < 0.01
or ph % window[2] > 0.99*window[2])): or ph % window[2] > 0.99*window[2])):
str_ += "{0:>6.2f}{1:>10.2f}\n".format(ph, tm_) str_ += "{0:>6.2f}{1:>10.2f}\n".format(ph, tm_)
info(str_) _LOGGER.info(str_)
def print_result(protein, conformation, parameters): def print_result(protein, conformation, parameters):
@@ -196,9 +199,9 @@ def print_pka_section(protein, conformation, parameters):
""" """
# geting the determinants section # geting the determinants section
str_ = get_determinant_section(protein, conformation, parameters) str_ = get_determinant_section(protein, conformation, parameters)
info(str_) _LOGGER.info("pKa determinants:\n%s", str_)
str_ = get_summary_section(protein, conformation, parameters) str_ = get_summary_section(protein, conformation, parameters)
info(str_) _LOGGER.info("pKa summary:\n%s", str_)
def get_determinant_section(protein, conformation, parameters): def get_determinant_section(protein, conformation, parameters):
@@ -291,8 +294,9 @@ def get_folding_profile_section(
else: else:
str_ += "The pH of optimum stability is {0:>4.1f}".format(ph_opt) str_ += "The pH of optimum stability is {0:>4.1f}".format(ph_opt)
str_ += ( str_ += (
" for which the free energy is {0:>6.1f} kcal/mol at 298K\n".format( " for which the free energy is {0:>6.1f} kcal/mol at "
dg_opt)) "298K\n".format(dg_opt)
)
if dg_min is None or dg_max is None: if dg_min is None or dg_max is None:
str_ += "Could not determine pH values where the free energy" str_ += "Could not determine pH values where the free energy"
str_ += " is within 80 % of minimum\n" str_ += " is within 80 % of minimum\n"
@@ -332,7 +336,10 @@ def get_charge_profile_section(protein, conformation='AVR', _=None):
if pi_pro is None or pi_mod is None: if pi_pro is None or pi_mod is None:
str_ += "Could not determine the pI\n\n" str_ += "Could not determine the pI\n\n"
else: else:
str_ += f"The pI is {pi_pro:>5.2f} (folded) and {pi_mod:>5.2f} (unfolded)\n" str_ += (
f"The pI is {pi_pro:>5.2f} (folded) and {pi_mod:>5.2f} "
f"(unfolded)\n"
)
return str_ return str_
@@ -590,6 +597,7 @@ def write_mol2_for_atoms(atoms, filename):
out.write(substructure_section) out.write(substructure_section)
out.close() out.close()
def write_propka(molecular_container, filename): def write_propka(molecular_container, filename):
"""Write PROPKA input file for molecular container. """Write PROPKA input file for molecular container.

View File

@@ -2,13 +2,18 @@
Configuration file parameters Configuration file parameters
============================= =============================
Holds parameters and settings that can be set in :file:`propka.cfg`. The file format consists of lines of ``keyword value [value ...]``, blank lines, and comment lines (introduced with ``#``). Holds parameters and settings that can be set in :file:`propka.cfg`. The file
format consists of lines of ``keyword value [value ...]``, blank lines, and
comment lines (introduced with ``#``).
The module attributes below list the names and types of all key words The module attributes below list the names and types of all key words
in configuration file. in configuration file.
""" """
from propka.lib import info, warning import logging
_LOGGER = logging.getLogger(__name__)
#: matrices #: matrices
@@ -126,7 +131,7 @@ class Parameters:
""" """
dict_ = getattr(self, words[0]) dict_ = getattr(self, words[0])
key = words[1] key = words[1]
if not key in dict_: if key not in dict_:
dict_[key] = [] dict_[key] = []
for value in words[2:]: for value in words[2:]:
if isinstance(value, list): if isinstance(value, list):
@@ -204,12 +209,12 @@ class Parameters:
def print_interaction_parameters(self): def print_interaction_parameters(self):
"""Print interaction parameters.""" """Print interaction parameters."""
info('--------------- Model pKa values ----------------------') _LOGGER.info('--------------- Model pKa values ----------------------')
for k in self.model_pkas: for k in self.model_pkas:
info('{0:>3s} {1:8.2f}'.format(k, self.model_pkas[k])) _LOGGER.info('{0:>3s} {1:8.2f}'.format(k, self.model_pkas[k]))
info('') _LOGGER.info('')
info('--------------- Interactions --------------------------') _LOGGER.info('--------------- Interactions --------------------------')
agroups = [ agroups = [
'COO', 'HIS', 'CYS', 'TYR', 'SER', 'N+', 'LYS', 'AMD', 'ARG', 'COO', 'HIS', 'CYS', 'TYR', 'SER', 'N+', 'LYS', 'AMD', 'ARG',
'TRP', 'ROH', 'CG', 'C2N', 'N30', 'N31', 'N32', 'N33', 'NAR', 'TRP', 'ROH', 'CG', 'C2N', 'N30', 'N31', 'N32', 'N33', 'NAR',
@@ -234,7 +239,9 @@ class Parameters:
map_interaction = '' map_interaction = ''
if group2 in map_: if group2 in map_:
for val in map_[group2]: for val in map_[group2]:
fmt = "|{grp1:>3s} {grp2:>3s} {mat:1s} {val1:4} {val2:4}" fmt = (
"|{grp1:>3s} {grp2:>3s} {mat:1s} {val1:4} {val2:4}"
)
map_interaction += fmt.format( map_interaction += fmt.format(
group1, val, self.interaction_matrix[group1][val], group1, val, self.interaction_matrix[group1][val],
self.sidechain_cutoffs.get_value(group1, val)[0], self.sidechain_cutoffs.get_value(group1, val)[0],
@@ -260,18 +267,18 @@ class Parameters:
group1, group2)[1] group1, group2)[1]
!= 4)): != 4)):
map_interaction += '? ' map_interaction += '? '
info(interaction, map_interaction) _LOGGER.info("%s %s", interaction, map_interaction)
if group1 == group2: if group1 == group2:
break break
info('-') _LOGGER.info('-')
info('--------------- Exceptions ----------------------------') _LOGGER.info('--------------- Exceptions ----------------------------')
info('COO-HIS', self.COO_HIS_exception) _LOGGER.info('COO-HIS %s', self.COO_HIS_exception)
info('OCO-HIS', self.OCO_HIS_exception) _LOGGER.info('OCO-HIS %s', self.OCO_HIS_exception)
info('CYS-HIS', self.CYS_HIS_exception) _LOGGER.info('CYS-HIS %s', self.CYS_HIS_exception)
info('CYS-CYS', self.CYS_CYS_exception) _LOGGER.info('CYS-CYS %s', self.CYS_CYS_exception)
info('--------------- Mapping -------------------------------') _LOGGER.info('--------------- Mapping -------------------------------')
info(""" _LOGGER.info("""
Titratable: Titratable:
CG ARG CG ARG
C2N ARG C2N ARG
@@ -318,14 +325,16 @@ O2
"\\midrule", "\\midrule",
"\\endfirsthead", "\\endfirsthead",
"", "",
"\\multicolumn{{5}}{{l}}{\\emph{{continued from the previous page}}}\\\\", "\\multicolumn{{5}}{{l}}{\\emph{{continued from the previous "
"page}}}\\\\",
"\\toprule", "\\toprule",
"Group1 & Group2 & Interaction & c1 &c2 \\\\", "Group1 & Group2 & Interaction & c1 &c2 \\\\",
"\\midrule", "\\midrule",
"\\endhead", "\\endhead",
"", "",
"\\midrule", "\\midrule",
"\\multicolumn{{5}}{{r}}{\\emph{{continued on the next page}}}\\\\", "\\multicolumn{{5}}{{r}}{\\emph{{continued on the next "
"page}}}\\\\",
"\\endfoot", "\\endfoot",
"", "",
"\\bottomrule", "\\bottomrule",
@@ -350,11 +359,12 @@ O2
if group1 == group2: if group1 == group2:
break break
str_ += ' \\end{{longtable}}\n' str_ += ' \\end{{longtable}}\n'
info(str_) _LOGGER.info(str_)
def print_interactions_latex(self): def print_interactions_latex(self):
"""Print interactions in LaTeX.""" """Print interactions in LaTeX."""
# TODO - are these the same lists as above? Convert to module constants. # TODO - are these the same lists as above? Convert to module
# constants.
agroups = ['COO', 'HIS', 'CYS', 'TYR', 'SER', 'N+', 'LYS', 'AMD', agroups = ['COO', 'HIS', 'CYS', 'TYR', 'SER', 'N+', 'LYS', 'AMD',
'ARG', 'TRP', 'ROH', 'CG', 'C2N', 'N30', 'N31', 'N32', 'ARG', 'TRP', 'ROH', 'CG', 'C2N', 'N30', 'N31', 'N32',
'N33', 'NAR', 'OCO', 'NP1', 'OH', 'O3', 'CL', 'F', 'NAM', 'N33', 'NAR', 'OCO', 'NP1', 'OH', 'O3', 'CL', 'F', 'NAM',
@@ -371,14 +381,16 @@ O2
"\\midrule", "\\midrule",
"\\endfirsthead", "\\endfirsthead",
"", "",
"\\multicolumn{{5}}{{l}}{\\emph{{continued from the previous page}}}\\\\", "\\multicolumn{{5}}{{l}}{\\emph{{continued from the previous "
"page}}}\\\\",
"\\toprule", "\\toprule",
"Group1 & Group2 & Interaction & c1 &c2 \\\\", "Group1 & Group2 & Interaction & c1 &c2 \\\\",
"\\midrule", "\\midrule",
"\\endhead", "\\endhead",
"", "",
"\\midrule", "\\midrule",
"\\multicolumn{{5}}{{r}}{\\emph{{continued on the next page}}}\\\\", "\\multicolumn{{5}}{{r}}{\\emph{{continued on the next "
"page}}}\\\\",
"\\endfoot", "\\endfoot",
"", "",
"\\bottomrule", "\\bottomrule",
@@ -388,7 +400,10 @@ O2
str_ = "\n".join(lines) str_ = "\n".join(lines)
for group1 in agroups: for group1 in agroups:
for group2 in agroups: for group2 in agroups:
fmt = '{g1:>3s} & {g2:>3s} & {mat:1s} & {val1:>4s} & {val2:>4s}\\\\ \n' fmt = (
'{g1:>3s} & {g2:>3s} & {mat:1s} & {val1:>4s} & '
'{val2:>4s}\\\\ \n'
)
str_ += fmt.format( str_ += fmt.format(
group1, group2, self.interaction_matrix[group1][group2], group1, group2, self.interaction_matrix[group1][group2],
str(self.sidechain_cutoffs.get_value(group1, group2)[0]), str(self.sidechain_cutoffs.get_value(group1, group2)[0]),
@@ -396,7 +411,7 @@ O2
if group1 == group2: if group1 == group2:
break break
str_ += ' \\end{{longtable}}\n' str_ += ' \\end{{longtable}}\n'
info(str_) _LOGGER.info(str_)
class InteractionMatrix: class InteractionMatrix:
@@ -421,7 +436,7 @@ class InteractionMatrix:
""" """
new_group = words[0] new_group = words[0]
self.ordered_keys.append(new_group) self.ordered_keys.append(new_group)
if not new_group in self.dictionary.keys(): if new_group not in self.dictionary.keys():
self.dictionary[new_group] = {} self.dictionary[new_group] = {}
for i, group in enumerate(self.ordered_keys): for i, group in enumerate(self.ordered_keys):
if len(words) > i+1: if len(words) > i+1:
@@ -524,8 +539,8 @@ class PairwiseMatrix:
str_ = ( str_ = (
'Parameter value for {0:s}, {1:s} defined more ' 'Parameter value for {0:s}, {1:s} defined more '
'than once'.format(key1, key2)) 'than once'.format(key1, key2))
warning(str_) _LOGGER.warning(str_)
if not key1 in self.dictionary: if key1 not in self.dictionary:
self.dictionary[key1] = {} self.dictionary[key1] = {}
self.dictionary[key1][key2] = value self.dictionary[key1][key2] = value

View File

@@ -7,11 +7,14 @@ The :class:`Protonate` processes a
protons. protons.
""" """
import logging
import math import math
import propka.bonds import propka.bonds
import propka.atom import propka.atom
from propka.vector_algebra import rotate_vector_around_an_axis, Vector from propka.vector_algebra import rotate_vector_around_an_axis, Vector
from propka.lib import warning, debug
_LOGGER = logging.getLogger(__name__)
class Protonate: class Protonate:
@@ -49,7 +52,7 @@ class Protonate:
Args: Args:
molecules: molecular containers molecules: molecular containers
""" """
debug('----- Protonation started -----') _LOGGER.debug('----- Protonation started -----')
# Remove all currently present hydrogen atoms # Remove all currently present hydrogen atoms
self.remove_all_hydrogen_atoms(molecules) self.remove_all_hydrogen_atoms(molecules)
# protonate all atoms # protonate all atoms
@@ -81,11 +84,11 @@ class Protonate:
if atom.type == 'atom': if atom.type == 'atom':
key = '{0:3s}-{1:s}'.format(atom.res_name, atom.name) key = '{0:3s}-{1:s}'.format(atom.res_name, atom.name)
if atom.terminal: if atom.terminal:
debug(atom.terminal) _LOGGER.debug("%s", atom.terminal)
key = atom.terminal key = atom.terminal
if key in self.standard_charges: if key in self.standard_charges:
atom.charge = self.standard_charges[key] atom.charge = self.standard_charges[key]
debug('Charge', atom, atom.charge) _LOGGER.debug('Charge %s %s', atom, atom.charge)
atom.charge_set = True atom.charge_set = True
# atom is a ligand atom # atom is a ligand atom
elif atom.type == 'hetatm': elif atom.type == 'hetatm':
@@ -130,21 +133,25 @@ class Protonate:
Args: Args:
atom: atom for calculation atom: atom for calculation
""" """
debug('*'*10) _LOGGER.debug('*'*10)
debug('Setting number of protons to add for', atom) _LOGGER.debug('Setting number of protons to add for %s', atom)
atom.number_of_protons_to_add = 8 atom.number_of_protons_to_add = 8
debug(" 8") _LOGGER.debug(" 8")
atom.number_of_protons_to_add -= self.valence_electrons[atom.element] atom.number_of_protons_to_add -= self.valence_electrons[atom.element]
debug('Valence electrons: {0:>4d}'.format( _LOGGER.debug('Valence electrons: {0:>4d}'.format(
-self.valence_electrons[atom.element])) -self.valence_electrons[atom.element]))
atom.number_of_protons_to_add -= len(atom.bonded_atoms) atom.number_of_protons_to_add -= len(atom.bonded_atoms)
debug('Number of bonds: {0:>4d}'.format(-len(atom.bonded_atoms))) _LOGGER.debug(
'Number of bonds: {0:>4d}'.format(-len(atom.bonded_atoms))
)
atom.number_of_protons_to_add -= atom.num_pi_elec_2_3_bonds atom.number_of_protons_to_add -= atom.num_pi_elec_2_3_bonds
debug('Pi electrons: {0:>4d}'.format(-atom.num_pi_elec_2_3_bonds)) _LOGGER.debug(
'Pi electrons: {0:>4d}'.format(-atom.num_pi_elec_2_3_bonds)
)
atom.number_of_protons_to_add += int(atom.charge) atom.number_of_protons_to_add += int(atom.charge)
debug('Charge: {0:>4.1f}'.format(atom.charge)) _LOGGER.debug('Charge: {0:>4.1f}'.format(atom.charge))
debug('-'*10) _LOGGER.debug('-'*10)
debug(atom.number_of_protons_to_add) _LOGGER.debug(atom.number_of_protons_to_add)
def set_steric_number_and_lone_pairs(self, atom): def set_steric_number_and_lone_pairs(self, atom):
"""Set steric number and lone pairs for atom. """Set steric number and lone pairs for atom.
@@ -155,39 +162,41 @@ class Protonate:
# If we already did this, there is no reason to do it again # If we already did this, there is no reason to do it again
if atom.steric_num_lone_pairs_set: if atom.steric_num_lone_pairs_set:
return return
debug('='*10) _LOGGER.debug('='*10)
debug('Setting steric number and lone pairs for', atom) _LOGGER.debug('Setting steric number and lone pairs for %s', atom)
atom.steric_number = 0 atom.steric_number = 0
debug('{0:>65s}: {1:>4d}'.format( _LOGGER.debug('{0:>65s}: {1:>4d}'.format(
'Valence electrons', self.valence_electrons[atom.element])) 'Valence electrons', self.valence_electrons[atom.element]))
atom.steric_number += self.valence_electrons[atom.element] atom.steric_number += self.valence_electrons[atom.element]
debug('{0:>65s}: {1:>4d}'.format( _LOGGER.debug('{0:>65s}: {1:>4d}'.format(
'Number of bonds', len(atom.bonded_atoms))) 'Number of bonds', len(atom.bonded_atoms)))
atom.steric_number += len(atom.bonded_atoms) atom.steric_number += len(atom.bonded_atoms)
debug('{0:>65s}: {1:>4d}'.format( _LOGGER.debug('{0:>65s}: {1:>4d}'.format(
'Number of hydrogen atoms to add', atom.number_of_protons_to_add)) 'Number of hydrogen atoms to add', atom.number_of_protons_to_add))
atom.steric_number += atom.number_of_protons_to_add atom.steric_number += atom.number_of_protons_to_add
debug('{0:>65s}: {1:>4d}'.format( _LOGGER.debug('{0:>65s}: {1:>4d}'.format(
'Number of pi-electrons in double and triple bonds(-)', 'Number of pi-electrons in double and triple bonds(-)',
atom.num_pi_elec_2_3_bonds)) atom.num_pi_elec_2_3_bonds))
atom.steric_number -= atom.num_pi_elec_2_3_bonds atom.steric_number -= atom.num_pi_elec_2_3_bonds
debug('{0:>65s}: {1:>4d}'.format( _LOGGER.debug('{0:>65s}: {1:>4d}'.format(
'Number of pi-electrons in conjugated double and triple bonds(-)', 'Number of pi-electrons in conjugated double and triple bonds(-)',
atom.num_pi_elec_conj_2_3_bonds)) atom.num_pi_elec_conj_2_3_bonds))
atom.steric_number -= atom.num_pi_elec_conj_2_3_bonds atom.steric_number -= atom.num_pi_elec_conj_2_3_bonds
debug('{0:>65s}: {1:>4d}'.format( _LOGGER.debug('{0:>65s}: {1:>4d}'.format(
'Number of donated co-ordinated bonds', 0)) 'Number of donated co-ordinated bonds', 0))
atom.steric_number += 0 atom.steric_number += 0
debug('{0:>65s}: {1:>4.1f}'.format( _LOGGER.debug('{0:>65s}: {1:>4.1f}'.format(
'Charge(-)', atom.charge)) 'Charge(-)', atom.charge))
atom.steric_number -= atom.charge atom.steric_number -= atom.charge
atom.steric_number = math.floor(atom.steric_number/2.0) atom.steric_number = math.floor(atom.steric_number/2.0)
atom.number_of_lone_pairs = ( atom.number_of_lone_pairs = (
atom.steric_number-len(atom.bonded_atoms)-atom.number_of_protons_to_add) atom.steric_number - len(atom.bonded_atoms)
debug('-'*70) - atom.number_of_protons_to_add
debug('{0:>65s}: {1:>4d}'.format( )
_LOGGER.debug('-'*70)
_LOGGER.debug('{0:>65s}: {1:>4d}'.format(
'Steric number', atom.steric_number)) 'Steric number', atom.steric_number))
debug('{0:>65s}: {1:>4d}'.format( _LOGGER.debug('{0:>65s}: {1:>4d}'.format(
'Number of lone pairs', atom.number_of_lone_pairs)) 'Number of lone pairs', atom.number_of_lone_pairs))
atom.steric_num_lone_pairs_set = True atom.steric_num_lone_pairs_set = True
@@ -198,12 +207,14 @@ class Protonate:
atom: atom for calculation atom: atom for calculation
""" """
# decide which method to use # decide which method to use
debug('PROTONATING', atom) _LOGGER.debug('PROTONATING %s', atom)
if atom.steric_number in list(self.protonation_methods.keys()): if atom.steric_number in list(self.protonation_methods.keys()):
self.protonation_methods[atom.steric_number](atom) self.protonation_methods[atom.steric_number](atom)
else: else:
warning('Do not have a method for protonating', atom, _LOGGER.warning(
'(steric number: {0:d})'.format(atom.steric_number)) 'Do not have a method for protonating %s %s', atom,
'(steric number: {0:d})'.format(atom.steric_number)
)
def trigonal(self, atom): def trigonal(self, atom):
"""Add hydrogens in trigonal geometry. """Add hydrogens in trigonal geometry.
@@ -211,7 +222,9 @@ class Protonate:
Args: Args:
atom: atom to protonate atom: atom to protonate
""" """
debug('TRIGONAL - {0:d} bonded atoms'.format(len(atom.bonded_atoms))) _LOGGER.debug(
'TRIGONAL - {0:d} bonded atoms'.format(len(atom.bonded_atoms))
)
rot_angle = math.radians(120.0) rot_angle = math.radians(120.0)
cvec = Vector(atom1=atom) cvec = Vector(atom1=atom)
# 0 bonds # 0 bonds
@@ -269,7 +282,7 @@ class Protonate:
Args: Args:
atom: atom to protonate. atom: atom to protonate.
""" """
debug( _LOGGER.debug(
'TETRAHEDRAL - {0:d} bonded atoms'.format(len(atom.bonded_atoms))) 'TETRAHEDRAL - {0:d} bonded atoms'.format(len(atom.bonded_atoms)))
# TODO - might be good to move tetrahedral angle to constant # TODO - might be good to move tetrahedral angle to constant
rot_angle = math.radians(109.5) rot_angle = math.radians(109.5)
@@ -321,7 +334,8 @@ class Protonate:
chain_id=atom.chain_id, chain_id=atom.chain_id,
res_num=atom.res_num, res_num=atom.res_num,
x=round(position.x, 3), # round of to three decimal points to x=round(position.x, 3), # round of to three decimal points to
# avoid round-off differences in input file # avoid round-off differences in input
# file
y=round(position.y, 3), y=round(position.y, 3),
z=round(position.z, 3), z=round(position.z, 3),
occ=None, occ=None,
@@ -350,7 +364,7 @@ class Protonate:
proton.residue_label = "{0:<3s}{1:>4d}{2:>2s}".format( proton.residue_label = "{0:<3s}{1:>4d}{2:>2s}".format(
proton.name, proton.res_num, proton.chain_id) proton.name, proton.res_num, proton.chain_id)
i += 1 i += 1
debug('added', new_h, 'to', atom) _LOGGER.debug('added %s %s %s', new_h, 'to', atom)
def set_bond_distance(self, bvec, element): def set_bond_distance(self, bvec, element):
"""Set bond distance between atom and element. """Set bond distance between atom and element.
@@ -368,6 +382,6 @@ class Protonate:
str_ = ( str_ = (
'Bond length for {0:s} not found, using the standard value ' 'Bond length for {0:s} not found, using the standard value '
'of {1:f}'.format(element, dist)) 'of {1:f}'.format(element, dist))
warning(str_) _LOGGER.warning(str_)
bvec = bvec.rescale(dist) bvec = bvec.rescale(dist)
return bvec return bvec

View File

@@ -11,18 +11,23 @@ function. If similar functionality is desired from a Python script
""" """
import logging import logging
import sys
from propka.lib import loadOptions from propka.lib import loadOptions
from propka.input import read_parameter_file, read_molecule_file from propka.input import read_parameter_file, read_molecule_file
from propka.parameters import Parameters from propka.parameters import Parameters
from propka.molecular_container import MolecularContainer from propka.molecular_container import MolecularContainer
_LOGGER = logging.getLogger("PROPKA") _LOGGER = logging.getLogger(__name__)
def main(optargs=None): def main(optargs=None):
"""Read in structure files, calculate pKa values, and print pKa files.""" """Read in structure files, calculate pKa values, and print pKa files."""
# loading options, flags and arguments # loading options, flags and arguments
logger = logging.getLogger("")
stdout_handler = logging.StreamHandler(sys.stdout)
stdout_handler.setFormatter(logging.Formatter("%(message)s"))
logger.addHandler(stdout_handler)
optargs = optargs if optargs is not None else [] optargs = optargs if optargs is not None else []
options = loadOptions(*optargs) options = loadOptions(*optargs)
pdbfiles = options.filenames pdbfiles = options.filenames

View File

@@ -4,8 +4,12 @@ Vector calculations
Vector algebra for PROPKA. Vector algebra for PROPKA.
""" """
import logging
import math import math
from propka.lib import info, get_sorted_configurations from propka.lib import get_sorted_configurations
_LOGGER = logging.getLogger(__name__)
class Vector: class Vector:
@@ -63,7 +67,7 @@ class Vector:
elif type(other) in [int, float]: elif type(other) in [int, float]:
return Vector(self.x * other, self.y * other, self.z * other) return Vector(self.x * other, self.y * other, self.z * other)
else: else:
info('{0:s} not supported'.format(type(other))) _LOGGER.info('{0:s} not supported'.format(type(other)))
raise TypeError raise TypeError
def __rmul__(self, other): def __rmul__(self, other):

View File

@@ -6,7 +6,7 @@ Contains version-specific methods and parameters.
TODO - this module unnecessarily confuses the code. Can we eliminate it? TODO - this module unnecessarily confuses the code. Can we eliminate it?
""" """
from propka.lib import info import logging
from propka.hydrogens import setup_bonding_and_protonation, setup_bonding from propka.hydrogens import setup_bonding_and_protonation, setup_bonding
from propka.hydrogens import setup_bonding_and_protonation_30_style from propka.hydrogens import setup_bonding_and_protonation_30_style
from propka.energy import radial_volume_desolvation, calculate_pair_weight from propka.energy import radial_volume_desolvation, calculate_pair_weight
@@ -16,6 +16,9 @@ from propka.energy import coulomb_energy, check_exceptions
from propka.energy import backbone_reorganization from propka.energy import backbone_reorganization
_LOGGER = logging.getLogger(__name__)
class Version: class Version:
"""Store version-specific methods and parameters.""" """Store version-specific methods and parameters."""
def __init__(self, parameters): def __init__(self, parameters):
@@ -140,12 +143,18 @@ class VersionA(Version):
[v, [c1, c3]] TODO - figure out what this is [v, [c1, c3]] TODO - figure out what this is
""" """
if backbone_atom.group_type == 'BBC': if backbone_atom.group_type == 'BBC':
if atom.group_type in self.parameters.backbone_CO_hydrogen_bond.keys(): if (
atom.group_type in
self.parameters.backbone_CO_hydrogen_bond.keys()
):
[v, c1, c2] = self.parameters.backbone_CO_hydrogen_bond[ [v, c1, c2] = self.parameters.backbone_CO_hydrogen_bond[
atom.group_type] atom.group_type]
return [v, [c1, c2]] return [v, [c1, c2]]
if backbone_atom.group_type == 'BBN': if backbone_atom.group_type == 'BBN':
if atom.group_type in self.parameters.backbone_NH_hydrogen_bond.keys(): if (
atom.group_type in
self.parameters.backbone_NH_hydrogen_bond.keys()
):
[v, c1, c2] = self.parameters.backbone_NH_hydrogen_bond[ [v, c1, c2] = self.parameters.backbone_NH_hydrogen_bond[
atom.group_type] atom.group_type]
return [v, [c1, c2]] return [v, [c1, c2]]
@@ -159,7 +168,7 @@ class SimpleHB(VersionA):
"""Initialize object with parameters.""" """Initialize object with parameters."""
# set the calculation rutines used in this version # set the calculation rutines used in this version
super().__init__(parameters) super().__init__(parameters)
info('Using simple hb model') _LOGGER.info('Using simple hb model')
def get_hydrogen_bond_parameters(self, atom1, atom2): def get_hydrogen_bond_parameters(self, atom1, atom2):
"""Get hydrogen bond parameters for two atoms. """Get hydrogen bond parameters for two atoms.
@@ -193,7 +202,7 @@ class ElementBasedLigandInteractions(VersionA):
"""Initialize object with parameters.""" """Initialize object with parameters."""
# set the calculation rutines used in this version # set the calculation rutines used in this version
super().__init__(parameters) super().__init__(parameters)
info('Using detailed SC model!') _LOGGER.info('Using detailed SC model!')
return return
def get_hydrogen_bond_parameters(self, atom1, atom2): def get_hydrogen_bond_parameters(self, atom1, atom2):
@@ -259,8 +268,9 @@ class ElementBasedLigandInteractions(VersionA):
res = self.parameters.hydrogen_bonds.get_value( res = self.parameters.hydrogen_bonds.get_value(
elements[0], elements[1]) elements[0], elements[1])
if not res: if not res:
info( _LOGGER.info(
'Could not determine backbone interaction parameters for:', 'Could not determine backbone interaction parameters '
'for: %s %s',
backbone_atom, atom) backbone_atom, atom)
return None return None
return None return None