Remove cyclic import based on atom.py

Partially addresses https://github.com/jensengroup/propka-3.1/issues/49
This commit is contained in:
Nathan Baker
2020-05-30 09:26:13 -07:00
parent 461edda3b2
commit 397d5e10aa
7 changed files with 155 additions and 153 deletions

View File

@@ -1,8 +1,7 @@
"""Atom class - contains all atom information found in the PDB file""" """Atom class - contains all atom information found in the PDB file"""
import string import string
import propka.lib
import propka.group
from . import hybrid36 from . import hybrid36
from propka.lib import make_tidy_atom_label
# Format strings that get used in multiple places (or are very complex) # Format strings that get used in multiple places (or are very complex)
@@ -50,6 +49,9 @@ class Atom(object):
self.z = None self.z = None
self.group = None self.group = None
self.group_type = None self.group_type = None
self.group_label = None
self.group_model_pka = None
self.group_model_pka_set = None
self.number_of_bonded_elements = {} self.number_of_bonded_elements = {}
self.cysteine_bridge = False self.cysteine_bridge = False
self.bonded_atoms = [] self.bonded_atoms = []
@@ -267,7 +269,7 @@ class Atom(object):
model_pka = PKA_FMT.format(self.group.model_pka) model_pka = PKA_FMT.format(self.group.model_pka)
str_ = INPUT_LINE_FMT.format( str_ = INPUT_LINE_FMT.format(
type=self.type.upper(), r=self, type=self.type.upper(), r=self,
atom_label=propka.lib.make_tidy_atom_label(self.name, self.element), atom_label=make_tidy_atom_label(self.name, self.element),
group=group, pka=model_pka) group=group, pka=model_pka)
return str_ return str_
@@ -313,21 +315,11 @@ class Atom(object):
self.occ = self.occ.replace('ALG', 'titratable_ligand') self.occ = self.occ.replace('ALG', 'titratable_ligand')
self.occ = self.occ.replace('BLG', 'titratable_ligand') self.occ = self.occ.replace('BLG', 'titratable_ligand')
self.occ = self.occ.replace('LG', 'non_titratable_ligand') self.occ = self.occ.replace('LG', 'non_titratable_ligand')
# try to initialise the group self.group_label = "{0:s}_group".format(self.occ)
try:
group_attr = "{0:s}_group".format(self.occ)
group_attr = getattr(propka.group, group_attr)
self.group = group_attr(self)
except:
# TODO - be more specific with expection handling here
str_ = (
'{0:s} in input_file is not recognized as a group'.format(
self.occ))
raise Exception(str_)
# set the model pKa value # set the model pKa value
if self.beta != '-': if self.beta != '-':
self.group.model_pka = float(self.beta) self.group_model_pka = float(self.beta)
self.group.model_pka_set = True self.group_model_pka_set = True
# set occ and beta to standard values # set occ and beta to standard values
self.occ = '1.00' self.occ = '1.00'
self.beta = '0.00' self.beta = '0.00'
@@ -344,7 +336,7 @@ class Atom(object):
""" """
str_ = PDB_LINE_FMT1.format( str_ = PDB_LINE_FMT1.format(
type=self.type.upper(), r=self, type=self.type.upper(), r=self,
atom_label=propka.lib.make_tidy_atom_label(self.name, self.element)) atom_label=make_tidy_atom_label(self.name, self.element))
return str_ return str_
def make_mol2_line(self, id_): def make_mol2_line(self, id_):
@@ -359,7 +351,7 @@ class Atom(object):
""" """
str_ = MOL2_LINE_FMT.format( str_ = MOL2_LINE_FMT.format(
id=id_, r=self, id=id_, r=self,
atom_label=propka.lib.make_tidy_atom_label(self.name, self.element)) atom_label=make_tidy_atom_label(self.name, self.element))
return str_ return str_
def make_pdb_line2(self, numb=None, name=None, res_name=None, chain_id=None, def make_pdb_line2(self, numb=None, name=None, res_name=None, chain_id=None,
@@ -397,7 +389,7 @@ class Atom(object):
str_ = PDB_LINE_FMT2.format( str_ = PDB_LINE_FMT2.format(
numb=numb, res_name=res_name, chain_id=chain_id, res_num=res_num, numb=numb, res_name=res_name, chain_id=chain_id, res_num=res_num,
x=x, y=y, z=z, occ=occ, beta=beta, x=x, y=y, z=z, occ=occ, beta=beta,
atom_label=propka.lib.make_tidy_atom_label(name, self.element) atom_label=make_tidy_atom_label(name, self.element)
) )
return str_ return str_
@@ -408,7 +400,7 @@ class Atom(object):
Returns: Returns:
String with label""" String with label"""
return propka.lib.make_tidy_atom_label(self.name, self.element) return make_tidy_atom_label(self.name, self.element)
def __str__(self): def __str__(self):
"""Return an undefined-format string version of this atom.""" """Return an undefined-format string version of this atom."""

View File

@@ -1,8 +1,5 @@
"""PROPKA calculations.""" """PROPKA calculations."""
import math import math
import propka.protonate
import propka.bonds
from propka.lib import warning, info
# TODO - this file should be broken into three separate files: # TODO - this file should be broken into three separate files:

View File

@@ -6,6 +6,7 @@ from propka.ligand_pka_values import LigandPkaValues
from propka.determinant import Determinant from propka.determinant import Determinant
from propka.lib import info, warning from propka.lib import info, warning
# Constants that start with "UNK_" are a mystery to me # Constants that start with "UNK_" are a mystery to me
UNK_PKA_SCALING = -1.36 UNK_PKA_SCALING = -1.36
PROTONATOR = propka.protonate.Protonate(verbose=False) PROTONATOR = propka.protonate.Protonate(verbose=False)
@@ -1417,3 +1418,15 @@ def is_ion_group(parameters, atom):
if atom.res_name.strip() in parameters.ions.keys(): if atom.res_name.strip() in parameters.ions.keys():
return IonGroup(atom) return IonGroup(atom)
return None return None
def initialize_atom_group(atom):
"""Initialize an atom group.
Args:
atom: atom to initialize
"""
# try to initialise the group
group_attr = globals()[atom.group_label]
atom.group = group_attr(atom)
atom.group.model_pka = atom.group_model_pka
atom.group.model_pka_set = atom.group_model_pka_set

View File

@@ -6,7 +6,7 @@ import propka.molecular_container
import propka.calculations import propka.calculations
import propka.parameters import propka.parameters
import propka.pdb import propka.pdb
import propka.lib from propka.output import write_mol2_for_atoms
from propka.lib import info, warning from propka.lib import info, warning
@@ -133,7 +133,7 @@ class LigandPkaValues:
""" """
# print out structure unless we are using user-modified structure # print out structure unless we are using user-modified structure
if not reuse: if not reuse:
propka.pdb.write_mol2_for_atoms(atoms, filename) write_mol2_for_atoms(atoms, filename)
# check that we actually have a file to work with # check that we actually have a file to work with
if not os.path.isfile(filename): if not os.path.isfile(filename):
errstr = ( errstr = (
@@ -141,7 +141,7 @@ class LigandPkaValues:
"- generating one".format( "- generating one".format(
filename)) filename))
warning(errstr) warning(errstr)
propka.pdb.write_mol2_for_atoms(atoms, filename) write_mol2_for_atoms(atoms, filename)
# Marvin calculate pKa values # Marvin calculate pKa values
fmt = ( fmt = (
'pka -a {num1} -b {num2} --min {min_ph} ' 'pka -a {num1} -b {num2} --min {min_ph} '
@@ -197,3 +197,4 @@ class LigandPkaValues:
if len(indices) != len(values) != len(types): if len(indices) != len(values) != len(types):
raise Exception('Lengths of atoms and pka values mismatch') raise Exception('Lengths of atoms and pka values mismatch')
return indices, values, types return indices, values, types

View File

@@ -1,13 +1,11 @@
"""Molecular container for storing all contents of PDB files.""" """Molecular container for storing all contents of PDB files."""
import os import os
import sys import sys
import propka.pdb
import propka.version import propka.version
import propka.output from propka.pdb import read_input
import propka.group from propka.output import write_input
import propka.lib
from propka.conformation_container import ConformationContainer from propka.conformation_container import ConformationContainer
from propka.lib import info, warning from propka.lib import info, warning, make_grid
# TODO - these are constants whose origins are a little murky # TODO - these are constants whose origins are a little murky
@@ -78,7 +76,7 @@ class Molecular_container:
self.find_covalently_coupled_groups() self.find_covalently_coupled_groups()
# write out the input file # write out the input file
filename = self.file.replace(input_file_extension, '.propka_input') filename = self.file.replace(input_file_extension, '.propka_input')
propka.pdb.write_input(self, filename) write_input(self, filename)
elif input_file_extension == '.propka_input': elif input_file_extension == '.propka_input':
#input is a propka_input file #input is a propka_input file
[self.conformations, self.conformation_names] = ( [self.conformations, self.conformation_names] = (
@@ -155,7 +153,7 @@ class Molecular_container:
else: else:
str_ = ( str_ = (
'Group {0:s} could not be found in ' 'Group {0:s} could not be found in '
'conformation {0:s}.'.format( 'conformation {1:s}.'.format(
group.atom.residue_label, name)) group.atom.residue_label, name))
warning(str_) warning(str_)
# ... and store the average value # ... and store the average value
@@ -214,7 +212,7 @@ class Molecular_container:
""" """
# calculate stability profile # calculate stability profile
profile = [] profile = []
for ph in propka.lib.make_grid(*grid): for ph in make_grid(*grid):
conf = self.conformations[conformation] conf = self.conformations[conformation]
ddg = conf.calculate_folding_energy(ph=ph, reference=reference) ddg = conf.calculate_folding_energy(ph=ph, reference=reference)
profile.append([ph, ddg]) profile.append([ph, ddg])
@@ -244,7 +242,7 @@ class Molecular_container:
list of charge state values list of charge state values
""" """
charge_profile = [] charge_profile = []
for ph in propka.lib.make_grid(*grid): for ph in make_grid(*grid):
conf = self.conformations[conformation] conf = self.conformations[conformation]
q_unfolded, q_folded = conf.calculate_charge( q_unfolded, q_folded = conf.calculate_charge(
self.version.parameters, ph=ph) self.version.parameters, ph=ph)

View File

@@ -1,6 +1,6 @@
"""Output routines.""" """Output routines."""
from datetime import date from datetime import date
from propka.lib import info from propka.lib import info, open_file_for_writing
def print_header(): def print_header():
@@ -11,8 +11,8 @@ def print_header():
info(str_) info(str_)
def write_pdb(protein, pdbfile=None, filename=None, include_hydrogens=False, def write_pdb_for_protein(
_=None): protein, pdbfile=None, filename=None, include_hydrogens=False, _=None):
"""Write a residue to the new PDB file. """Write a residue to the new PDB file.
Args: Args:
@@ -50,6 +50,16 @@ def write_pdb(protein, pdbfile=None, filename=None, include_hydrogens=False,
pdbfile.close() pdbfile.close()
def write_pdb_for_conformation(conformation, filename):
"""Write PDB conformation to a file.
Args:
conformation: conformation container
filename: filename for output
"""
write_pdb_for_atoms(conformation.atoms, filename)
def write_pka(protein, parameters, filename=None, conformation='1A', def write_pka(protein, parameters, filename=None, conformation='1A',
reference="neutral", _="folding", verbose=False, reference="neutral", _="folding", verbose=False,
__=None): __=None):
@@ -461,3 +471,104 @@ def make_interaction_map(name, list_, interaction):
tag = ' X ' tag = ' X '
res += '{0:>10s}| '.format(tag) res += '{0:>10s}| '.format(tag)
return res return res
def write_pdb_for_atoms(atoms, filename, make_conect_section=False):
"""Write out PDB file for atoms.
Args:
atoms: list of atoms
filename: name of file
make_conect_section: generate a CONECT PDB section
"""
out = open_file_for_writing(filename)
for atom in atoms:
out.write(atom.make_pdb_line())
if make_conect_section:
for atom in atoms:
out.write(atom.make_conect_line())
out.close()
def get_bond_order(atom1, atom2):
"""Get the order of a bond between two atoms.
Args:
atom1: first atom in bond
atom2: second atom in bond
Returns:
string with bond type
"""
type_ = '1'
pi_electrons1 = atom1.num_pi_elec_2_3_bonds
pi_electrons2 = atom2.num_pi_elec_2_3_bonds
if '.ar' in atom1.sybyl_type:
pi_electrons1 -= 1
if '.ar' in atom2.sybyl_type:
pi_electrons2 -= 1
if pi_electrons1 > 0 and pi_electrons2 > 0:
type_ = '{0:d}'.format(min(pi_electrons1, pi_electrons2)+1)
if '.ar' in atom1.sybyl_type and '.ar' in atom2.sybyl_type:
type_ = 'ar'
return type_
def write_mol2_for_atoms(atoms, filename):
"""Write out MOL2 file for atoms.
Args:
atoms: list of atoms
filename: name of file
"""
# TODO - header needs to be converted to format string
header = '@<TRIPOS>MOLECULE\n\n{natom:d} {id:d}\nSMALL\nUSER_CHARGES\n'
atoms_section = '@<TRIPOS>ATOM\n'
for i, atom in enumerate(atoms):
atoms_section += atom.make_mol2_line(i+1)
bonds_section = '@<TRIPOS>BOND\n'
id_ = 1
for i, atom1 in enumerate(atoms):
for j, atom2 in enumerate(atoms, i+1):
if atom1 in atom2.bonded_atoms:
type_ = get_bond_order(atom1, atom2)
bonds_section += '{0:>7d} {1:>7d} {2:>7d} {3:>7s}\n'.format(
id_, i+1, j+1, type_)
id_ += 1
substructure_section = '@<TRIPOS>SUBSTRUCTURE\n\n'
if len(atoms) > 0:
substructure_section = (
'@<TRIPOS>SUBSTRUCTURE\n{0:<7d} {1:>10s} {2:>7d}\n'.format(
atoms[0].res_num, atoms[0].res_name, atoms[0].numb))
out = open_file_for_writing(filename)
out.write(header.format(natom=len(atoms), id=id_-1))
out.write(atoms_section)
out.write(bonds_section)
out.write(substructure_section)
out.close()
def write_input(molecular_container, filename):
"""Write PROPKA input file for molecular container.
Args:
molecular_container: molecular container
filename: output file name
"""
out = open_file_for_writing(filename)
for conformation_name in molecular_container.conformation_names:
out.write('MODEL {0:s}\n'.format(conformation_name))
# write atoms
for atom in molecular_container.conformations[conformation_name].atoms:
out.write(atom.make_input_line())
# write bonds
for atom in molecular_container.conformations[conformation_name].atoms:
out.write(atom.make_conect_line())
# write covalently coupled groups
for group in (
molecular_container.conformations[conformation_name].groups):
out.write(group.make_covalently_coupled_line())
# write non-covalently coupled groups
for group in (
molecular_container.conformations[conformation_name].groups):
out.write(group.make_non_covalently_coupled_line())
out.write('ENDMDL\n')
out.close()

View File

@@ -1,7 +1,8 @@
"""PDB parsing functionality.""" """Read and parse PDB-like input files."""
import propka.lib import propka.lib
from propka.lib import warning from propka.lib import warning
from propka.atom import Atom from propka.atom import Atom
from propka.group import initialize_atom_group
from propka.conformation_container import ConformationContainer from propka.conformation_container import ConformationContainer
@@ -158,118 +159,6 @@ def get_atom_lines_from_pdb(pdb_file, ignore_residues=[], keep_protons=False,
terminal = None terminal = None
def write_pdb(conformation, filename):
"""Write PDB conformation to a file.
Args:
conformation: conformation container
filename: filename for output
"""
write_pdb_for_atoms(conformation.atoms, filename)
def write_pdb_for_atoms(atoms, filename, make_conect_section=False):
"""Write out PDB file for atoms.
Args:
atoms: list of atoms
filename: name of file
make_conect_section: generate a CONECT PDB section
"""
out = propka.lib.open_file_for_writing(filename)
for atom in atoms:
out.write(atom.make_pdb_line())
if make_conect_section:
for atom in atoms:
out.write(atom.make_conect_line())
out.close()
def write_mol2_for_atoms(atoms, filename):
"""Write out MOL2 file for atoms.
Args:
atoms: list of atoms
filename: name of file
"""
# TODO - header needs to be converted to format string
header = '@<TRIPOS>MOLECULE\n\n{natom:d} {id:d}\nSMALL\nUSER_CHARGES\n'
atoms_section = '@<TRIPOS>ATOM\n'
for i, atom in enumerate(atoms):
atoms_section += atom.make_mol2_line(i+1)
bonds_section = '@<TRIPOS>BOND\n'
id_ = 1
for i, atom1 in enumerate(atoms):
for j, atom2 in enumerate(atoms, i+1):
if atom1 in atom2.bonded_atoms:
type_ = get_bond_order(atom1, atom2)
bonds_section += '{0:>7d} {1:>7d} {2:>7d} {3:>7s}\n'.format(
id_, i+1, j+1, type_)
id_ += 1
substructure_section = '@<TRIPOS>SUBSTRUCTURE\n\n'
if len(atoms) > 0:
substructure_section = (
'@<TRIPOS>SUBSTRUCTURE\n{0:<7d} {1:>10s} {2:>7d}\n'.format(
atoms[0].res_num, atoms[0].res_name, atoms[0].numb))
out = propka.lib.open_file_for_writing(filename)
out.write(header.format(natom=len(atoms), id=id_-1))
out.write(atoms_section)
out.write(bonds_section)
out.write(substructure_section)
out.close()
def get_bond_order(atom1, atom2):
"""Get the order of a bond between two atoms.
Args:
atom1: first atom in bond
atom2: second atom in bond
Returns:
string with bond type
"""
type_ = '1'
pi_electrons1 = atom1.num_pi_elec_2_3_bonds
pi_electrons2 = atom2.num_pi_elec_2_3_bonds
if '.ar' in atom1.sybyl_type:
pi_electrons1 -= 1
if '.ar' in atom2.sybyl_type:
pi_electrons2 -= 1
if pi_electrons1 > 0 and pi_electrons2 > 0:
type_ = '{0:d}'.format(min(pi_electrons1, pi_electrons2)+1)
if '.ar' in atom1.sybyl_type and '.ar' in atom2.sybyl_type:
type_ = 'ar'
return type_
def write_input(molecular_container, filename):
"""Write PROPKA input file for molecular container.
Args:
molecular_container: molecular container
filename: output file name
"""
out = propka.lib.open_file_for_writing(filename)
for conformation_name in molecular_container.conformation_names:
out.write('MODEL {0:s}\n'.format(conformation_name))
# write atoms
for atom in molecular_container.conformations[conformation_name].atoms:
out.write(atom.make_input_line())
# write bonds
for atom in molecular_container.conformations[conformation_name].atoms:
out.write(atom.make_conect_line())
# write covalently coupled groups
for group in (
molecular_container.conformations[conformation_name].groups):
out.write(group.make_covalently_coupled_line())
# write non-covalently coupled groups
for group in (
molecular_container.conformations[conformation_name].groups):
out.write(group.make_non_covalently_coupled_line())
out.write('ENDMDL\n')
out.close()
def read_input(input_file, parameters, molecule): def read_input(input_file, parameters, molecule):
"""Read PROPKA input file for molecular container. """Read PROPKA input file for molecular container.
@@ -316,6 +205,7 @@ def get_atom_lines_from_input(input_file, tags=['ATOM ', 'HETATM']):
if tag in tags: if tag in tags:
atom = Atom(line=line) atom = Atom(line=line)
atom.get_input_parameters() atom.get_input_parameters()
initialize_atom_group(atom)
atom.groups_extracted = 1 atom.groups_extracted = 1
atom.is_protonated = True atom.is_protonated = True
atoms[atom.numb] = atom atoms[atom.numb] = atom