Initial removal of PROPKA input

This commit is contained in:
IAlibay
2020-11-17 23:31:24 +00:00
parent 9dfd87a0eb
commit 239c8183c9
11 changed files with 63 additions and 284 deletions

View File

@@ -13,9 +13,6 @@ from . import hybrid36
# Format strings that get used in multiple places (or are very complex)
PKA_FMT = "{:6.2f}"
INPUT_LINE_FMT = (
"{type:6s}{r.numb:>5d} {atom_label} {r.res_name}{r.chain_id:>2s}"
"{r.res_num:>4d}{r.x:>12.3f}{r.y:>8.3f}{r.z:>8.3f}{group:>6s}{pka:>6s} \n")
PDB_LINE_FMT1 = (
"{type:6s}{r.numb:>5d} {atom_label} {r.res_name}{r.chain_id:>2s}"
"{r.res_num:>4d}{r.x:>12.3f}{r.y:>8.3f}{r.z:>8.3f}{r.occ:>6s}"
@@ -33,7 +30,13 @@ STR_FMT = (
class Atom:
"""Atom class - contains all atom information found in the PDB file"""
"""Atom class - contains all atom information found in the PDB file
.. versionchanged:: 3.4.0
:meth:`make_input_line` and :meth:`get_input_parameters` have been
removed as reading/writing PROPKA input is no longer supported.
"""
def __init__(self, line=None):
"""Initialize Atom object.
@@ -255,31 +258,6 @@ class Atom:
new_atom.icode = self.icode
return new_atom
def make_input_line(self):
"""PDB line for this atom.
TODO - Could be @property method/attribute
TODO - figure out difference between make_pdb_line, make_input_line,
and make_pdb_line2
Returns:
String with PDB-format line.
"""
group = '-'
model_pka = '-'
if self.group:
group = self.group.type
if self.terminal == 'C-':
group = 'C-' ## circumventing C-/COO parameter unification
if self.group.titratable:
model_pka = PKA_FMT.format(self.group.model_pka)
str_ = INPUT_LINE_FMT.format(
type=self.type.upper(), r=self,
atom_label=make_tidy_atom_label(self.name, self.element),
group=group, pka=model_pka)
return str_
def make_conect_line(self):
"""PDB line for bonding within this molecule.
@@ -298,45 +276,11 @@ class Atom:
res += '\n'
return res
def get_input_parameters(self):
"""Extract the input parameters stored in the occupancy and b-factor
fields in input files"""
# Set the group type
if self.occ != '-':
# make sure to set the terminal
if self.occ in ['N+', 'C-']:
self.terminal = self.occ
# save the ligand group charge
if self.occ == 'BLG':
self.charge = +1
elif self.occ == 'ALG':
self.charge = -1
# generic ions
if self.occ in ['1P', '2P', '1N', '2N']:
self.res_name = self.occ
self.occ = 'Ion'
# correct the group type
self.occ = self.occ.replace('N+', 'Nterm')
self.occ = self.occ.replace('C-', 'Cterm')
self.occ = self.occ.replace('ION', 'Ion')
self.occ = self.occ.replace('ALG', 'titratable_ligand')
self.occ = self.occ.replace('BLG', 'titratable_ligand')
self.occ = self.occ.replace('LG', 'non_titratable_ligand')
self.group_label = "{0:s}_group".format(self.occ)
# set the model pKa value
if self.beta != '-':
self.group_model_pka = float(self.beta)
self.group_model_pka_set = True
# set occ and beta to standard values
self.occ = '1.00'
self.beta = '0.00'
def make_pdb_line(self):
"""Create PDB line.
TODO - this could/should be a @property method/attribute
TODO - figure out difference between make_pdb_line, make_input_line,
and make_pdb_line2
TODO - figure out difference between make_pdb_line, and make_pdb_line2
Returns:
String with PDB line.
@@ -367,8 +311,7 @@ class Atom:
"""Create a PDB line.
TODO - this could/should be a @property method/attribute
TODO - figure out difference between make_pdb_line, make_input_line,
and make_pdb_line2
TODO - figure out difference between make_pdb_line, and make_pdb_line2
Returns:
String with PDB line.

View File

@@ -25,7 +25,13 @@ RESIDUE_MULTIPLIER = 1000
class ConformationContainer:
"""Container for molecular conformations"""
"""Container for molecular conformations
.. versionchanged:: 3.4.0
Removed :meth:`additional_setup_when_reading_input_files` as reading
PROPKA inputs is no longer supported.
"""
def __init__(self, name='', parameters=None, molecular_container=None):
"""Initialize conformation container.

View File

@@ -40,7 +40,14 @@ EXPECTED_ATOMS_BASE_INTERACTIONS = {
class Group:
"""Class for storing groups important to pKa calculations."""
"""Class for storing groups important to pKa calculations.
.. versionchanged:: 3.4.0
Removed :meth:`make_covalently_coupled_line` and
:meth:`make_non_covalently_coupled_line` as writing PROPKA inputs is no
longer supported.
"""
def __init__(self, atom):
"""Initialize with an atom.
@@ -178,48 +185,6 @@ class Group:
self.determinants[type_].append(
Determinant(new_determinant.group, new_determinant.value))
def make_covalently_coupled_line(self):
"""Create line for covalent coupling.
Returns:
string
"""
# first check if there are any coupled groups at all
if len(self.covalently_coupled_groups) == 0:
return ''
line = 'CCOUPL{0:5d}'.format(self.atom.numb)
# extract and sort numbers of coupled groups
coupled = []
for group in self.covalently_coupled_groups:
coupled.append(group.atom.numb)
coupled.sort()
# write 'em out
for num in coupled:
line += '{0:5d}'.format(num)
line += '\n'
return line
def make_non_covalently_coupled_line(self):
"""Create line for non-covalent coupling.
Returns:
string
"""
# first check if there are any coupled groups at all
if len(self.non_covalently_coupled_groups) == 0:
return ''
line = 'NCOUPL{0:5d}'.format(self.atom.numb)
# extract and sort numbers of coupled groups
coupled = []
for group in self.non_covalently_coupled_groups:
coupled.append(group.atom.numb)
coupled.sort()
# write 'em out
for num in coupled:
line += '{0:5d}'.format(num)
line += '\n'
return line
def __eq__(self, other):
"""Needed for creating sets of groups."""
if self.atom.type == 'atom':

View File

@@ -3,6 +3,11 @@ Input handling
==============
Input routines.
.. versionchanged:: 3.4.0
Methods to read PROPKA input files (:func:`read_propka` and
:func:`get_atom_lines_from_input`) have been removed.
"""
from pathlib import Path
from pkg_resources import resource_filename
@@ -67,14 +72,17 @@ def read_molecule_file(filename: str, mol_container, stream=None):
usually have an associated file name, an appropirate file name should
be passed to the ``filename`` argument. In this case, ``filename`` is
not opened for reading, but instead is used to help recognise the file
type (based on the extension being either `.pdb` or `.propka_input`)
and also uses that given ``filename`` to assign a name to the input
type (based on the extension being `.pdb`) and also uses that given
``filename`` to assign a name to the input
:class:`~propka.molecular_container.MolecularContainer` object.
>>> read_molecule_file('test.pdb', mol_container,
stream=string_io_object)
<propka.molecular_container.MolecularContainer at 0x7f6e0c8f2310>
.. versionchanged:: 3.4.0
PROPKA input files (extension: `.propka_input`) are no longer read.
"""
input_path = Path(filename)
mol_container.name = input_path.stem
@@ -109,17 +117,6 @@ def read_molecule_file(filename: str, mol_container, stream=None):
mol_container.conformations[name].sort_atoms()
# find coupled groups
mol_container.find_covalently_coupled_groups()
elif input_file_extension.lower() == '.propka_input':
# input is a propka_input file
conformations, conformation_names = read_propka(
input_file, mol_container.version.parameters, mol_container)
mol_container.conformations = conformations
mol_container.conformation_names = conformation_names
# Extract groups - this merely sets up the groups found in the
# input file
mol_container.extract_groups()
# do some additional set up
mol_container.additional_setup_when_reading_input_file()
else:
str_ = "Unknown input file type {0!s} for file {1!s}".format(
input_file_extension, input_path)
@@ -220,94 +217,6 @@ def get_atom_lines_from_pdb(pdb_file, ignore_residues=[], keep_protons=False,
terminal = None
def read_propka(input_file, parameters, molecule):
"""Read PROPKA input file for molecular container.
Args:
input_file: input file
parameters: parameters for parsing/setup
molecule: molecular container
Returns:
list with [conformations, names of conformations]
"""
conformations = {}
# read in all atoms in the input file
lines = get_atom_lines_from_input(input_file)
for (name, atom) in lines:
if not name in conformations.keys():
conformations[name] = ConformationContainer(
name=name, parameters=parameters,
molecular_container=molecule)
conformations[name].add_atom(atom)
# make a sorted list of conformation names
names = sorted(conformations.keys(), key=conformation_sorter)
return [conformations, names]
def get_atom_lines_from_input(input_file, tags=['ATOM ', 'HETATM']):
"""Get atom lines from a PROPKA input file.
Args:
input_file: input file
tags: tags defining atom lines
Yields:
conformation container, list of atoms
"""
lines = open_file_for_reading(input_file).readlines()
conformation = ''
atoms = {}
numbers = []
for line in lines:
tag = line[0:6]
# set the conformation
if tag == 'MODEL ':
conformation = line[6:].strip()
# found an atom - save it
if tag in tags:
atom = Atom(line=line)
atom.get_input_parameters()
initialize_atom_group(atom)
atom.groups_extracted = 1
atom.is_protonated = True
atoms[atom.numb] = atom
numbers.append(atom.numb)
# found bonding information - apply it
if tag == 'CONECT' and len(line) > 14:
conect_numbers = [line[i:i+5] for i in range(6, len(line)-1, 5)]
center_atom = atoms[int(conect_numbers[0])]
for num in conect_numbers[1:]:
bond_atom = atoms[int(num)]
# remember to check for cysteine bridges
if center_atom.element == 'S' and bond_atom.element == 'S':
center_atom.cysteine_bridge = True
bond_atom.cysteine_bridge = True
# set up bonding
if not bond_atom in center_atom.bonded_atoms:
center_atom.bonded_atoms.append(bond_atom)
if not center_atom in bond_atom.bonded_atoms:
bond_atom.bonded_atoms.append(center_atom)
# found info on covalent coupling
if tag == 'CCOUPL' and len(line) > 14:
conect_numbers = [line[i:i+5] for i in range(6, len(line)-1, 5)]
center_atom = atoms[int(conect_numbers[0])]
for num in conect_numbers[1:]:
cov_atom = atoms[int(num)]
center_atom.group.couple_covalently(cov_atom.group)
# found info on non-covalent coupling
if tag == 'NCOUPL' and len(line) > 14:
conect_numbers = [line[i:i+5] for i in range(6, len(line)-1, 5)]
center_atom = atoms[int(conect_numbers[0])]
for num in conect_numbers[1:]:
cov_atom = atoms[int(num)]
center_atom.group.couple_non_covalently(cov_atom.group)
# this conformation is done - yield the atoms
if tag == 'ENDMDL':
for num in numbers:
yield (conformation, atoms[num])
# prepare for next conformation
atoms = {}
numbers = []
def read_pdb(pdb_file, parameters, molecule):
"""Parse a PDB file.

View File

@@ -195,6 +195,11 @@ def build_parser(parser=None):
new parser will be created.
Returns:
ArgumentParser object.
.. versionchanged:: 3.4.0
Argument `--generate-propka-input` has been removed as writing PROPKA
input files is no longer supported.
"""
if parser is not None:
group = parser.add_argument_group(title="PROPKA invoation options")
@@ -285,9 +290,6 @@ def build_parser(parser=None):
group.add_argument(
"-q", "--quiet", action="store_const", const="WARNING",
dest="log_level", help="suppress non-warning messages")
group.add_argument(
"--generate-propka-input", action="store_true",
help="Generate a PROPKA input file")
group.add_argument(
"--protonate-all", dest="protonate_all", action="store_true",
help="Protonate all atoms (will not influence pKa calculation)",

View File

@@ -6,7 +6,7 @@ Molecular container for storing all contents of PDB files.
"""
import os
import propka.version
from propka.output import write_propka, write_pka, print_header, print_result
from propka.output import write_pka, print_header, print_result
from propka.conformation_container import ConformationContainer
from propka.lib import info, warning, make_grid
@@ -22,6 +22,12 @@ class MolecularContainer:
TODO - this class name does not conform to PEP8 but has external use.
We should deprecate and change eventually.
.. versionchanged:: 3.4.0
Removed :meth:`write_propka` and
:meth:`additional_setup_when_reading_input_file` as reading and writing
PROPKA input files is no longer supported.
"""
def __init__(self, parameters, options=None):
@@ -72,11 +78,6 @@ class MolecularContainer:
for name in self.conformation_names:
self.conformations[name].extract_groups()
def additional_setup_when_reading_input_file(self):
"""Additional setup."""
for name in self.conformation_names:
self.conformations[name].additional_setup_when_reading_input_file()
def calculate_pka(self):
"""Calculate pKa values."""
# calculate for each conformation
@@ -123,16 +124,6 @@ class MolecularContainer:
self.conformation_names[0]].chains
self.conformations['AVR'] = avr_conformation
def write_propka(self, filename=None):
"""Write PROPKA input file.
Args:
filename: file to write to
"""
if filename is None:
filename = os.path.join('{0:s}.propka_input'.format(self.name))
write_propka(self, filename)
def write_pka(self, filename=None, reference="neutral",
direction="folding", options=None):
"""Write pKa information to a file.

View File

@@ -3,6 +3,11 @@ Output
======
Output routines.
.. versionchanged::3.4.0
Removed :func:`write_proka` as writing PROPKA input files is no longer
supported.
"""
from datetime import date
from propka.lib import info
@@ -589,30 +594,3 @@ def write_mol2_for_atoms(atoms, filename):
out.write(bonds_section)
out.write(substructure_section)
out.close()
def write_propka(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

@@ -21,7 +21,12 @@ _LOGGER = logging.getLogger("PROPKA")
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.
.. versionchanged:: 3.4.0
Removed ability to write out PROPKA input files.
"""
# loading options, flags and arguments
optargs = optargs if optargs is not None else []
options = loadOptions(*optargs)
@@ -32,8 +37,6 @@ def main(optargs=None):
my_molecule = read_molecule_file(pdbfile, my_molecule)
my_molecule.calculate_pka()
my_molecule.write_pka()
if options.generate_propka_input:
my_molecule.write_propka()
def single(filename: str, optargs: tuple = (), stream=None,
@@ -93,6 +96,9 @@ def single(filename: str, optargs: tuple = (), stream=None,
:func:`propka.input.read_molecule_file`
.. versionchanged:: 3.4.0
Removed ability to write out PROPKA input files.
"""
# Deal with input optarg options
optargs = tuple(optargs)
@@ -113,8 +119,6 @@ def single(filename: str, optargs: tuple = (), stream=None,
my_molecule.calculate_pka()
# write outputs
if options.generate_propka_input:
my_molecule.write_propka()
if write_pka:
my_molecule.write_pka()

View File

@@ -78,8 +78,6 @@ def run_propka(options, pdb_path, tmp_path):
molecule = read_molecule_file(str(pdb_path), molecule)
molecule.calculate_pka()
molecule.write_pka()
if args.generate_propka_input:
molecule.write_propka()
finally:
os.chdir(cwd)

View File

@@ -30,6 +30,7 @@ def test_single_file(tmpdir, pdb, options):
with tmpdir.as_cwd():
pkrun.single(filename, options)
compare_output(pdb, Path.cwd(), ref_path)
assert os.path.isfile(f'{pdb}.pka')
@pytest.mark.parametrize("pdb, options", [
@@ -51,6 +52,7 @@ def test_single_filestream(tmpdir, pdb, options):
with tmpdir.as_cwd():
pkrun.single(filename, options, stream=filestream)
compare_output(pdb, Path.cwd(), ref_path)
assert os.path.isfile(f'{pdb}.pka')
filestream.close()
@@ -69,27 +71,10 @@ def test_single_nopka(tmpdir):
assert not os.path.isfile(f"{pdb}.pka")
def test_single_propka_input(tmpdir):
"""Basic test to check that the propka_input file is written when
`--generate-propka-input` is passed"""
pdb = "1FTJ-Chain-A"
options = ('--generate-propka-input',)
ref_path, pdb_path = get_paths(pdb)
filename = f"{pdb}.pdb"
with open(pdb_path, 'r') as writer:
filestream = StringIO(writer.read())
with tmpdir.as_cwd():
pkrun.single(filename, options, stream=filestream)
assert os.path.isfile(f"{pdb}.propka_input")
def test_single_extra_files_logwarn(tmpdir, caplog):
"""Tests that a logging warning is thrown if passing files via optargs"""
pdb = "1FTJ-Chain-A"
options = ('-f foo.pdb bar.pdb', '-f test.pdb test2.pdb',
'--generate-propka-input')
options = ('-f foo.pdb bar.pdb', '-f test.pdb test2.pdb')
ref_path, pdb_path = get_paths(pdb)
filename = str(pdb_path)

View File

@@ -38,8 +38,6 @@ def run_propka_stream(options, input_file, filename):
molecule = read_molecule_file(filename, molecule, stream=input_file)
molecule.calculate_pka()
molecule.write_pka()
if args.generate_propka_input:
molecule.write_propka()
@pytest.mark.parametrize("pdb, options", [