@@ -2,6 +2,17 @@
|
||||
Changelog
|
||||
*********
|
||||
|
||||
Upcoming release (TBD)
|
||||
======================
|
||||
|
||||
Changes
|
||||
-------
|
||||
|
||||
* Removed the following unused command line options: ``-t``, ``--thermophile``,
|
||||
``-a``, ``--alignment``, ``-m``, ``--mutation``, ``--mutator``,
|
||||
``--mutator-option``
|
||||
* Removed several unused API functions
|
||||
|
||||
Current
|
||||
=======
|
||||
|
||||
|
||||
@@ -10,9 +10,9 @@ PROPKA predicts the pKa values of ionizable groups in proteins and
|
||||
protein-ligand complexes based in the 3D structure. The
|
||||
:program:`propka3` command has the following options::
|
||||
|
||||
propka3 [-h] [-f FILENAMES] [-r REFERENCE] [-c CHAINS] [-i TITRATE_ONLY] [-t THERMOPHILES] [-a ALIGNMENT] [-m MUTATIONS]
|
||||
propka3 [-h] [-f FILENAMES] [-r REFERENCE] [-c CHAINS] [-i TITRATE_ONLY]
|
||||
[-v VERSION_LABEL] [-p PARAMETERS] [--log-level {DEBUG,INFO,WARNING,ERROR,CRITICAL}] [-o PH] [-w WINDOW WINDOW WINDOW]
|
||||
[-g GRID GRID GRID] [--mutator MUTATOR] [--mutator-option MUTATOR_OPTIONS] [-d] [-l] [-k] [-q] [--protonate-all]
|
||||
[-g GRID GRID GRID] [-d] [-l] [-k] [-q] [--protonate-all]
|
||||
input_pdb
|
||||
|
||||
|
||||
@@ -47,21 +47,6 @@ protein-ligand complexes based in the 3D structure. The
|
||||
should be a comma-separated list of "chain:resnum"
|
||||
values; for example: ``-i "A:10,A:11"`` (default: None)
|
||||
|
||||
.. option:: -t THERMOPHILES, --thermophile THERMOPHILES
|
||||
|
||||
defining a thermophile filename; usually used in
|
||||
'alignment-mutations' (default: None)
|
||||
|
||||
.. option:: -a ALIGNMENT, --alignment ALIGNMENT
|
||||
|
||||
alignment file connecting <filename> and <thermophile>
|
||||
[<thermophile>.pir] (default: None)
|
||||
|
||||
.. option:: -m MUTATIONS, --mutation MUTATIONS
|
||||
|
||||
specifying mutation labels which is used to modify
|
||||
<filename> according to, e.g. N25R/N181D (default: None)
|
||||
|
||||
.. option:: --version
|
||||
|
||||
show program's version number and exit
|
||||
@@ -90,16 +75,6 @@ protein-ligand complexes based in the 3D structure. The
|
||||
setting the pH-grid to calculate e.g. stability related
|
||||
properties (default: (0.0, 14.0, 0.1))
|
||||
|
||||
.. option:: --mutator MUTATOR
|
||||
|
||||
setting approach for mutating <filename>
|
||||
[alignment/scwrl/jackal] (default: None)
|
||||
|
||||
.. option:: --mutator-option MUTATOR_OPTIONS
|
||||
|
||||
setting property for mutator [e.g. type="side-chain"]
|
||||
(default: None)
|
||||
|
||||
.. option:: -d, --display-coupled-residues
|
||||
|
||||
Displays alternative pKa values due to coupling of
|
||||
|
||||
@@ -7,8 +7,7 @@ The :class:`Atom` class contains all atom information found in the PDB file.
|
||||
"""
|
||||
|
||||
import string
|
||||
from typing import cast, List, NoReturn, Optional, TYPE_CHECKING
|
||||
import warnings
|
||||
from typing import List, Optional, TYPE_CHECKING
|
||||
|
||||
from propka.lib import make_tidy_atom_label
|
||||
from . import hybrid36
|
||||
@@ -46,7 +45,6 @@ class Atom:
|
||||
group: Optional["Group"] = None
|
||||
group_type: Optional[str] = None
|
||||
cysteine_bridge: bool = False
|
||||
residue: NoReturn = None # type: ignore[assignment]
|
||||
conformation_container: Optional["ConformationContainer"] = None
|
||||
molecular_container: Optional["MolecularContainer"] = None
|
||||
is_protonated: bool = False
|
||||
@@ -87,7 +85,6 @@ class Atom:
|
||||
Args:
|
||||
line: Line from a PDB file to set properties of atom.
|
||||
"""
|
||||
self.number_of_bonded_elements: NoReturn = cast(NoReturn, {}) # FIXME unused?
|
||||
self.bonded_atoms: List[Atom] = []
|
||||
self.set_properties(line)
|
||||
fmt = "{r.name:3s}{r.res_num:>4d}{r.chain_id:>2s}"
|
||||
@@ -302,45 +299,6 @@ class Atom:
|
||||
atom_label=make_tidy_atom_label(self.name, self.element))
|
||||
return str_
|
||||
|
||||
def make_pdb_line2(self, numb=None, name=None, res_name=None, chain_id=None,
|
||||
res_num=None, x=None, y=None, z=None, occ=None,
|
||||
beta=None):
|
||||
"""Create a PDB line.
|
||||
|
||||
TODO - this could/should be a @property method/attribute
|
||||
TODO - figure out difference between make_pdb_line, and make_pdb_line2
|
||||
|
||||
Returns:
|
||||
String with PDB line.
|
||||
"""
|
||||
warnings.warn("only used by unused function")
|
||||
if numb is None:
|
||||
numb = self.numb
|
||||
if name is None:
|
||||
name = self.name
|
||||
if res_name is None:
|
||||
res_name = self.res_name
|
||||
if chain_id is None:
|
||||
chain_id = self.chain_id
|
||||
if res_num is None:
|
||||
res_num = self.res_num
|
||||
if x is None:
|
||||
x = self.x
|
||||
if y is None:
|
||||
y = self.y
|
||||
if z is None:
|
||||
z = self.z
|
||||
if occ is None:
|
||||
occ = self.occ
|
||||
if beta is None:
|
||||
beta = self.beta
|
||||
str_ = PDB_LINE_FMT2.format(
|
||||
numb=numb, res_name=res_name, chain_id=chain_id, res_num=res_num,
|
||||
x=x, y=y, z=z, occ=occ, beta=beta,
|
||||
atom_label=make_tidy_atom_label(name, self.element)
|
||||
)
|
||||
return str_
|
||||
|
||||
def get_tidy_label(self):
|
||||
"""Returns a 'tidier' atom label for printing the new pdbfile
|
||||
|
||||
@@ -353,13 +311,3 @@ class Atom:
|
||||
def __str__(self):
|
||||
"""Return an undefined-format string version of this atom."""
|
||||
return STR_FMT.format(r=self)
|
||||
|
||||
def set_residue(self, residue: NoReturn):
|
||||
""" Makes a reference to the parent residue
|
||||
|
||||
Args:
|
||||
residue: the parent residue
|
||||
"""
|
||||
raise NotImplementedError("unused")
|
||||
if self.residue is None:
|
||||
self.residue = residue
|
||||
|
||||
@@ -87,63 +87,6 @@ class BondMaker:
|
||||
self.backbone_atoms = list(self.intra_residue_backbone_bonds.keys())
|
||||
self.terminal_oxygen_names = ['OXT', 'O\'\'']
|
||||
|
||||
def find_bonds_for_protein(self, protein):
|
||||
"""Bonds proteins based on the way atoms normally bond.
|
||||
|
||||
Args:
|
||||
protein: the protein to search for bonds
|
||||
"""
|
||||
raise NotImplementedError("unused")
|
||||
_LOGGER.info('++++ Side chains ++++')
|
||||
# side chains
|
||||
for chain in protein.chains:
|
||||
for residue in chain.residues:
|
||||
if residue.res_name.replace(' ', '') not in ['N+', 'C-']:
|
||||
self.find_bonds_for_side_chain(residue.atoms)
|
||||
_LOGGER.info('++++ Backbones ++++')
|
||||
# backbone
|
||||
last_residues = []
|
||||
for chain in protein.chains:
|
||||
for i in range(1, len(chain.residues)):
|
||||
if (chain.residues[i-1].res_name.replace(' ', '')
|
||||
not in ['N+', 'C-']):
|
||||
if (chain.residues[i].res_name.replace(' ', '')
|
||||
not in ['N+', 'C-']):
|
||||
self.connect_backbone(chain.residues[i-1],
|
||||
chain.residues[i])
|
||||
last_residues.append(chain.residues[i])
|
||||
_LOGGER.info('++++ terminal oxygen ++++')
|
||||
# terminal OXT
|
||||
for last_residue in last_residues:
|
||||
self.find_bonds_for_terminal_oxygen(last_residue)
|
||||
_LOGGER.info('++++ cysteines ++++')
|
||||
# Cysteines
|
||||
for chain in protein.chains:
|
||||
for i in range(0, len(chain.residues)):
|
||||
if chain.residues[i].res_name == 'CYS':
|
||||
for j in range(0, len(chain.residues)):
|
||||
if chain.residues[j].res_name == 'CYS' and j != i:
|
||||
self.check_for_cysteine_bonds(chain.residues[i],
|
||||
chain.residues[j])
|
||||
|
||||
def check_for_cysteine_bonds(self, cys1, cys2):
|
||||
"""Looks for potential bonds between two cysteines.
|
||||
|
||||
Args:
|
||||
cys1: one of the cysteines to check
|
||||
cys1: one of the cysteines to check
|
||||
"""
|
||||
raise NotImplementedError("unused")
|
||||
for atom1 in cys1.atoms:
|
||||
if atom1.name == 'SG':
|
||||
for atom2 in cys2.atoms:
|
||||
if atom2.name == 'SG':
|
||||
dist = propka.calculations.squared_distance(atom1,
|
||||
atom2)
|
||||
# TODO - is SS_dist_squared an attribute of this class?
|
||||
if dist < self.SS_dist_squared:
|
||||
self.make_bond(atom1, atom2)
|
||||
|
||||
def find_bonds_for_terminal_oxygen(self, residue):
|
||||
"""Look for bonds for terminal oxygen.
|
||||
|
||||
@@ -423,34 +366,3 @@ class BondMaker:
|
||||
atom2.bonded_atoms.append(atom1)
|
||||
if atom2 not in atom1.bonded_atoms:
|
||||
atom1.bonded_atoms.append(atom2)
|
||||
|
||||
def generate_protein_bond_dictionary(self, atoms):
|
||||
"""Generate dictionary of protein bonds.
|
||||
|
||||
Args:
|
||||
atoms: list of atoms for bonding
|
||||
"""
|
||||
for atom in atoms:
|
||||
for bonded_atom in atom.bonded_atoms:
|
||||
resi_i = atom.res_name
|
||||
name_i = atom.name
|
||||
resi_j = bonded_atom.res_name
|
||||
name_j = bonded_atom.name
|
||||
if name_i not in (
|
||||
self.backbone_atoms
|
||||
or name_j not in self.backbone_atoms):
|
||||
if name_i not in (
|
||||
self.terminal_oxygen_names
|
||||
and name_j not in self.terminal_oxygen_names):
|
||||
if resi_i not in list(self.protein_bonds.keys()):
|
||||
self.protein_bonds[resi_i] = {}
|
||||
if name_i not in self.protein_bonds[resi_i]:
|
||||
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)
|
||||
if resi_j not in list(self.protein_bonds.keys()):
|
||||
self.protein_bonds[resi_j] = {}
|
||||
if name_j not in self.protein_bonds[resi_j]:
|
||||
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)
|
||||
|
||||
@@ -6,7 +6,7 @@ Container data structure for molecular conformations.
|
||||
"""
|
||||
import logging
|
||||
import functools
|
||||
from typing import Callable, Dict, Iterable, Iterator, List, NoReturn, Optional, TYPE_CHECKING, Set
|
||||
from typing import Callable, Dict, Iterable, Iterator, List, Optional, TYPE_CHECKING, Set
|
||||
|
||||
from propka.lib import Options
|
||||
from propka.version import Version
|
||||
@@ -497,18 +497,6 @@ class ConformationContainer:
|
||||
group for group in self.groups
|
||||
if group.residue_type in self.parameters.ions.keys()]
|
||||
|
||||
def get_group_names(self, group_list: NoReturn) -> NoReturn: # FIXME unused?
|
||||
"""Get names of groups in list.
|
||||
|
||||
Args:
|
||||
group_list: list to check
|
||||
Returns:
|
||||
list of groups
|
||||
"""
|
||||
if TYPE_CHECKING:
|
||||
assert False
|
||||
return [group for group in self.groups if group.type in group_list]
|
||||
|
||||
def get_ligand_atoms(self) -> List["Atom"]:
|
||||
"""Get atoms associated with ligands.
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ Routines and classes for storing groups important to PROPKA calculations.
|
||||
"""
|
||||
import logging
|
||||
import math
|
||||
from typing import cast, Dict, Iterable, List, NoReturn, Optional
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
import propka.ligand
|
||||
from propka.parameters import Parameters
|
||||
@@ -119,8 +119,6 @@ class Group:
|
||||
fmt = "{type:<3s}{name:>4s}{chain:>2s}"
|
||||
self.label = fmt.format(
|
||||
type=self.residue_type, name=atom.name, chain=atom.chain_id)
|
||||
# container for squared distances
|
||||
self.squared_distances: NoReturn = cast(NoReturn, {}) # FIXME unused?
|
||||
|
||||
def couple_covalently(self, other: "Group") -> None:
|
||||
"""Couple this group with another group.
|
||||
@@ -162,46 +160,6 @@ class Group:
|
||||
"""
|
||||
return self.non_covalently_coupled_groups
|
||||
|
||||
def share_determinants(self, others: Iterable["Group"]) -> None:
|
||||
"""Share determinants between this group and others.
|
||||
|
||||
Args:
|
||||
others: list of other groups
|
||||
"""
|
||||
raise NotImplementedError("unused")
|
||||
# for each determinant type
|
||||
for other in others:
|
||||
if other == self:
|
||||
the_other = other
|
||||
continue
|
||||
for type_ in ['sidechain', 'backbone', 'coulomb']:
|
||||
for det in other.determinants[type_]:
|
||||
self.share_determinant(det, type_)
|
||||
# recalculate pka values
|
||||
self.calculate_total_pka()
|
||||
the_other.calculate_total_pka()
|
||||
|
||||
def share_determinant(self, new_determinant: Determinant, type_: str) -> None:
|
||||
"""Add determinant to this group's list of determinants.
|
||||
|
||||
Args:
|
||||
new_determinant: determinant to add
|
||||
type_: type of determinant
|
||||
"""
|
||||
added = False
|
||||
# first check if we already have a determinant with this label
|
||||
for own_determinant in self.determinants[type_]:
|
||||
if own_determinant.group == new_determinant.group:
|
||||
# if so, find the average value
|
||||
avr = 0.5*(own_determinant.value + new_determinant.value)
|
||||
own_determinant.value = avr
|
||||
new_determinant.value = avr
|
||||
added = True
|
||||
# otherwise we just add the determinant to our list
|
||||
if not added:
|
||||
self.determinants[type_].append(
|
||||
Determinant(new_determinant.group, new_determinant.value))
|
||||
|
||||
def __eq__(self, other):
|
||||
"""Needed for creating sets of groups."""
|
||||
if self.atom.type == 'atom':
|
||||
|
||||
@@ -202,7 +202,7 @@ def add_iterative_ion_pair(object1: "Iterative", object2: "Iterative",
|
||||
object2.determinants['sidechain'].append(interaction)
|
||||
|
||||
|
||||
def add_determinants(iterative_interactions: List[Interaction], version: Version, _=None):
|
||||
def add_determinants(iterative_interactions: List[Interaction], version: Version):
|
||||
"""Add determinants iteratively.
|
||||
|
||||
The iterative pKa scheme. Later it is all added in 'calculateTotalPKA'
|
||||
|
||||
@@ -8,10 +8,11 @@ Implements many of the main functions used to call PROPKA.
|
||||
import logging
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
from typing import Iterable, Iterator, List, TYPE_CHECKING, NoReturn, Optional, Tuple, TypeVar
|
||||
from typing import Dict, Iterable, Iterator, List, TYPE_CHECKING, NoReturn, Optional, Tuple, TypeVar
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from propka.atom import Atom
|
||||
from propka.conformation_container import ConformationContainer
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
@@ -30,7 +31,6 @@ EXPECTED_ATOM_NUMBERS = {'ALA': 5, 'ARG': 11, 'ASN': 8, 'ASP': 8, 'CYS': 6,
|
||||
|
||||
class Options:
|
||||
# Note: All the "NoReturn" members appear to be unused
|
||||
alignment: NoReturn # Optional[List[str]]
|
||||
chains: Optional[List[str]]
|
||||
display_coupled_residues: bool = False
|
||||
filenames: List[str] # List[Path]?
|
||||
@@ -38,20 +38,17 @@ class Options:
|
||||
input_pdb: str # Path?
|
||||
keep_protons: bool = False
|
||||
log_level: str = 'INFO'
|
||||
mutations: NoReturn # Optional[List[str]]
|
||||
mutator: NoReturn # Optional[str] # alignment/scwrl/jackal
|
||||
mutator_options: NoReturn # Optional[List[str]]
|
||||
pH: NoReturn # float = 7.0
|
||||
parameters: Path
|
||||
protonate_all: bool = False
|
||||
reference: NoReturn # str = 'neutral'
|
||||
reuse_ligand_mol2_file: bool = False # only used by unused function
|
||||
thermophiles: NoReturn # Optional[List[str]]
|
||||
titrate_only: Optional[List[_T_RESIDUE_TUPLE]]
|
||||
window: Tuple[float, float, float] = (0.0, 14.0, 1.0)
|
||||
|
||||
|
||||
def protein_precheck(conformations, names):
|
||||
def protein_precheck(conformations: Dict[str, "ConformationContainer"],
|
||||
names: Iterable[str]):
|
||||
"""Check protein for correct number of atoms, etc.
|
||||
|
||||
Args:
|
||||
@@ -60,7 +57,7 @@ def protein_precheck(conformations, names):
|
||||
for name in names:
|
||||
atoms = conformations[name].atoms
|
||||
# Group the atoms by their residue:
|
||||
atoms_by_residue = {}
|
||||
atoms_by_residue: Dict[str, List[Atom]] = {}
|
||||
for atom in atoms:
|
||||
if atom.element != 'H':
|
||||
res_id = resid_from_atom(atom)
|
||||
@@ -272,18 +269,6 @@ def build_parser(parser=None):
|
||||
help=('Treat only the specified residues as titratable. Value should '
|
||||
'be a comma-separated list of "chain:resnum" values; for '
|
||||
'example: -i "A:10,A:11"'))
|
||||
group.add_argument(
|
||||
"-t", "--thermophile", action="append", dest="thermophiles",
|
||||
help=("defining a thermophile filename; usually used in "
|
||||
"'alignment-mutations'"))
|
||||
group.add_argument(
|
||||
"-a", "--alignment", action="append", dest="alignment",
|
||||
help=("alignment file connecting <filename> and <thermophile> "
|
||||
"[<thermophile>.pir]"))
|
||||
group.add_argument(
|
||||
"-m", "--mutation", action="append", dest="mutations",
|
||||
help=("specifying mutation labels which is used to modify "
|
||||
"<filename> according to, e.g. N25R/N181D"))
|
||||
group.add_argument(
|
||||
"--version", action="version", version=f"%(prog)s {propka.__version__}")
|
||||
group.add_argument(
|
||||
@@ -311,16 +296,6 @@ def build_parser(parser=None):
|
||||
default=(0.0, 14.0, 0.1),
|
||||
help=("setting the pH-grid to calculate e.g. stability "
|
||||
"related properties [0.0, 14.0, 0.1]"))
|
||||
group.add_argument(
|
||||
"--mutator", dest="mutator",
|
||||
help=(
|
||||
"setting approach for mutating <filename> "
|
||||
"[alignment/scwrl/jackal]"
|
||||
)
|
||||
)
|
||||
group.add_argument(
|
||||
"--mutator-option", dest="mutator_options", action="append",
|
||||
help="setting property for mutator [e.g. type=\"side-chain\"]")
|
||||
group.add_argument(
|
||||
"-d", "--display-coupled-residues", dest="display_coupled_residues",
|
||||
action="store_true",
|
||||
|
||||
@@ -195,7 +195,7 @@ class MolecularContainer:
|
||||
stability_range = (min(stable_values), max(stable_values))
|
||||
return profile, opt, range_80pct, stability_range
|
||||
|
||||
def get_charge_profile(self, conformation: str = 'AVR', grid=[0., 14., .1]):
|
||||
def get_charge_profile(self, conformation: str = 'AVR', grid=(0., 14., .1)):
|
||||
"""Get charge profile for conformation as function of pH.
|
||||
|
||||
Args:
|
||||
@@ -212,13 +212,13 @@ class MolecularContainer:
|
||||
charge_profile.append([ph, q_unfolded, q_folded])
|
||||
return charge_profile
|
||||
|
||||
def get_pi(self, conformation: str = 'AVR', grid=[0., 14., 1], *,
|
||||
def get_pi(self, conformation: str = 'AVR', grid=(0., 14.), *,
|
||||
precision: float = 1e-4) -> Tuple[float, float]:
|
||||
"""Get the isoelectric points for folded and unfolded states.
|
||||
|
||||
Args:
|
||||
conformation: conformation to test
|
||||
grid: grid of pH values [min, max, step]
|
||||
grid: pH window [min, max]
|
||||
precision: Compute pI up to this precision
|
||||
Returns:
|
||||
1. Folded state PI
|
||||
|
||||
142
propka/output.py
142
propka/output.py
@@ -14,7 +14,7 @@ from datetime import date
|
||||
from decimal import Decimal
|
||||
from os import PathLike
|
||||
from pathlib import Path
|
||||
from typing import IO, AnyStr, List, NoReturn, Optional, Union, TYPE_CHECKING
|
||||
from typing import IO, AnyStr, List, Optional, Tuple, Union, TYPE_CHECKING
|
||||
import warnings
|
||||
|
||||
from .parameters import Parameters
|
||||
@@ -52,20 +52,6 @@ def open_file_for_writing(input_file: _TextIOSource) -> IO[str]:
|
||||
return input_file
|
||||
|
||||
|
||||
def write_file(filename, lines):
|
||||
"""Writes a new file.
|
||||
|
||||
Args:
|
||||
filename: name of file
|
||||
lines: lines to write to file
|
||||
"""
|
||||
warnings.warn("unused and untested by propka")
|
||||
file_ = open_file_for_writing(filename)
|
||||
for line in lines:
|
||||
file_.write("{0:s}\n".format(line))
|
||||
file_.close()
|
||||
|
||||
|
||||
def print_header():
|
||||
"""Print header section of output."""
|
||||
str_ = "{0:s}\n".format(get_propka_header())
|
||||
@@ -74,46 +60,6 @@ def print_header():
|
||||
_LOGGER.info("\n%s", str_)
|
||||
|
||||
|
||||
def write_pdb_for_protein(
|
||||
protein, pdbfile=None, filename=None, include_hydrogens=False, _=None):
|
||||
"""Write a residue to the new PDB file.
|
||||
|
||||
Args:
|
||||
protein: protein object
|
||||
pdbfile: PDB file
|
||||
filename: file to write to
|
||||
include_hydrogens: Boolean indicating whether to include hydrogens
|
||||
options: options object
|
||||
"""
|
||||
raise NotImplementedError("unused")
|
||||
if pdbfile is None:
|
||||
# opening file if not given
|
||||
if filename is None:
|
||||
filename = "{0:s}.pdb".format(protein.name)
|
||||
# TODO - this would be better as a context manager
|
||||
pdbfile = open(filename, 'w')
|
||||
_LOGGER.info("writing pdbfile {0:s}".format(filename))
|
||||
close_file = True
|
||||
else:
|
||||
# don't close the file, it was opened in a different place
|
||||
close_file = False
|
||||
numb = 0
|
||||
for chain in protein.chains:
|
||||
for residue in chain.residues:
|
||||
if residue.res_name not in ["N+ ", "C- "]:
|
||||
for atom in residue.atoms:
|
||||
if (not include_hydrogens) and atom.name[0] == "H":
|
||||
# don't print
|
||||
pass
|
||||
else:
|
||||
numb += 1
|
||||
line = atom.make_pdb_line2(numb=numb)
|
||||
line += "\n"
|
||||
pdbfile.write(line)
|
||||
if close_file:
|
||||
pdbfile.close()
|
||||
|
||||
|
||||
def write_pdb_for_conformation(conformation: "ConformationContainer",
|
||||
filename: _PathArg):
|
||||
"""Write PDB conformation to a file.
|
||||
@@ -129,22 +75,19 @@ def write_pdb_for_conformation(conformation: "ConformationContainer",
|
||||
def write_pka(protein: "MolecularContainer",
|
||||
parameters: Parameters,
|
||||
filename: Optional[_PathArg] = None,
|
||||
conformation='1A',
|
||||
reference="neutral", _="folding", verbose=False,
|
||||
__=None):
|
||||
conformation: str = '1A',
|
||||
reference: str = "neutral",
|
||||
*,
|
||||
verbose: bool = True):
|
||||
"""Write the pKa-file based on the given protein.
|
||||
|
||||
Args:
|
||||
protein: protein object
|
||||
filename: output file name
|
||||
conformation: TODO - figure this out
|
||||
conformation: specific conformation
|
||||
reference: reference state
|
||||
_: "folding" or other
|
||||
verbose: Boolean flag for verbosity
|
||||
__: options object
|
||||
"""
|
||||
# TODO - the code immediately overrides the verbose argument; why?
|
||||
verbose = True
|
||||
if filename is None:
|
||||
filename = "{0:s}.pka".format(protein.name)
|
||||
if verbose:
|
||||
@@ -168,39 +111,6 @@ def write_pka(protein: "MolecularContainer",
|
||||
Path(filename).write_text(str_, encoding="utf-8")
|
||||
|
||||
|
||||
def print_tm_profile(protein: NoReturn, reference="neutral", window=[0., 14., 1.],
|
||||
__=[0., 0.], tms=None, ref=None, _=False,
|
||||
options=None):
|
||||
"""Print Tm profile.
|
||||
|
||||
I think Tm refers to the denaturation temperature.
|
||||
|
||||
Args:
|
||||
protein: protein object
|
||||
reference: reference state
|
||||
window: pH window [min, max, step]
|
||||
__: temperature range [min, max]
|
||||
tms: TODO - figure this out
|
||||
ref: TODO - figure this out (probably reference state?)
|
||||
_: Boolean for verbosity
|
||||
options: options object
|
||||
"""
|
||||
raise NotImplementedError("unused")
|
||||
profile = protein.getTmProfile(
|
||||
reference=reference, grid=[0., 14., 0.1], tms=tms, ref=ref,
|
||||
options=options)
|
||||
if profile is None:
|
||||
str_ = "Could not determine Tm-profile\n"
|
||||
else:
|
||||
str_ = " suggested Tm-profile for {0:s}\n".format(protein.name)
|
||||
for (ph, tm_) in profile:
|
||||
if (ph >= window[0] and ph <= window[1]
|
||||
and (ph % window[2] < 0.01
|
||||
or ph % window[2] > 0.99*window[2])):
|
||||
str_ += "{0:>6.2f}{1:>10.2f}\n".format(ph, tm_)
|
||||
_LOGGER.info(str_)
|
||||
|
||||
|
||||
def print_result(protein: "MolecularContainer", conformation: str, parameters: Parameters):
|
||||
"""Prints all resulting output from determinants and down.
|
||||
|
||||
@@ -283,8 +193,11 @@ def get_summary_section(protein: "MolecularContainer", conformation: str,
|
||||
|
||||
def get_folding_profile_section(
|
||||
protein: "MolecularContainer",
|
||||
conformation='AVR', direction="folding", reference="neutral",
|
||||
window=[0., 14., 1.0], _=False, __=None):
|
||||
conformation: str = 'AVR',
|
||||
direction: str = "folding",
|
||||
reference: str = "neutral",
|
||||
window: Tuple[float, float, float] = (0., 14., 1.),
|
||||
):
|
||||
"""Returns string with the folding profile section of the results.
|
||||
|
||||
Args:
|
||||
@@ -293,8 +206,6 @@ def get_folding_profile_section(
|
||||
direction: 'folding' or other
|
||||
reference: reference state
|
||||
window: pH window [min, max, step]
|
||||
_: Boolean for verbose output
|
||||
__: options object
|
||||
Returns:
|
||||
string
|
||||
"""
|
||||
@@ -340,7 +251,8 @@ def get_folding_profile_section(
|
||||
return str_
|
||||
|
||||
|
||||
def get_charge_profile_section(protein, conformation='AVR', _=None):
|
||||
def get_charge_profile_section(protein: "MolecularContainer",
|
||||
conformation: str = 'AVR'):
|
||||
"""Returns string with the charge profile section of the results.
|
||||
|
||||
Args:
|
||||
@@ -371,34 +283,6 @@ def get_charge_profile_section(protein, conformation='AVR', _=None):
|
||||
return str_
|
||||
|
||||
|
||||
def write_jackal_scap_file(mutation_data=None, filename="1xxx_scap.list",
|
||||
_=None):
|
||||
"""Write a scap file for, i.e., generating a mutated protein
|
||||
|
||||
TODO - figure out what this is
|
||||
"""
|
||||
raise NotImplementedError("unused")
|
||||
with open(filename, 'w') as file_:
|
||||
for chain_id, _, res_num, code2 in mutation_data:
|
||||
str_ = "{chain:s}, {num:d}, {code:s}\n".format(
|
||||
chain=chain_id, num=res_num, code=code2)
|
||||
file_.write(str_)
|
||||
|
||||
|
||||
def write_scwrl_sequence_file(sequence, filename="x-ray.seq", _=None):
|
||||
"""Write a scwrl sequence file for, e.g., generating a mutated protein
|
||||
|
||||
TODO - figure out what this is
|
||||
"""
|
||||
warnings.warn("unused and untested by propka")
|
||||
with open(filename, 'w') as file_:
|
||||
start = 0
|
||||
while len(sequence[start:]) > 60:
|
||||
file_.write("{0:s}s\n".format(sequence[start:start+60]))
|
||||
start += 60
|
||||
file_.write("{0:s}\n".format(sequence[start:]))
|
||||
|
||||
|
||||
def get_propka_header():
|
||||
"""Create the header.
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ in configuration file.
|
||||
"""
|
||||
import logging
|
||||
from dataclasses import dataclass, field
|
||||
from typing import Dict, List
|
||||
from typing import Callable, Dict, List, Sequence, Tuple, TypeVar, Union
|
||||
|
||||
try:
|
||||
# New in version 3.10, deprecated since version 3.12
|
||||
@@ -39,6 +39,8 @@ class squared_property:
|
||||
setattr(instance, self._name_not_squared, value**0.5)
|
||||
|
||||
|
||||
T = TypeVar("T")
|
||||
|
||||
_T_MATRIX: TypeAlias = "InteractionMatrix"
|
||||
_T_PAIR_WISE_MATRIX: TypeAlias = "PairwiseMatrix"
|
||||
_T_NUMBER_DICTIONARY = Dict[str, float]
|
||||
@@ -118,13 +120,10 @@ class Parameters:
|
||||
CYS_CYS_exception: float = 3.60
|
||||
min_ligand_model_pka: float = -10.0
|
||||
max_ligand_model_pka: float = 20.0
|
||||
# include_H_in_interactions: NoReturn = None
|
||||
coupling_max_number_of_bonds: int = 3
|
||||
min_bond_distance_for_hydrogen_bonds: int = 4
|
||||
# coupling_penalty: NoReturn = None
|
||||
shared_determinants: _T_BOOL = False
|
||||
common_charge_centre: _T_BOOL = False
|
||||
# hide_penalised_group: NoReturn = None
|
||||
remove_penalised_group: _T_BOOL = True
|
||||
max_intrinsic_pka_diff: float = 2.0
|
||||
min_interaction_energy: float = 0.5
|
||||
@@ -158,8 +157,10 @@ class Parameters:
|
||||
self.parse_to_matrix(words)
|
||||
elif typeannotation is _T_STRING_DICTIONARY:
|
||||
self.parse_to_string_dictionary(words)
|
||||
elif typeannotation is int or typeannotation is _T_BOOL:
|
||||
self.parse_parameter(words, int)
|
||||
else:
|
||||
self.parse_parameter(words)
|
||||
self.parse_parameter(words, float)
|
||||
|
||||
def parse_to_number_dictionary(self, words):
|
||||
"""Parse field to number dictionary.
|
||||
@@ -222,14 +223,14 @@ class Parameters:
|
||||
value = tuple(words[1:])
|
||||
matrix.add(value)
|
||||
|
||||
def parse_parameter(self, words):
|
||||
def parse_parameter(self, words, typefunc: Callable[[str], T]):
|
||||
"""Parse field to parameters.
|
||||
|
||||
Args:
|
||||
words: strings to parse
|
||||
"""
|
||||
assert len(words) == 2, words
|
||||
value = float(words[1])
|
||||
value = typefunc(words[1])
|
||||
setattr(self, words[0], value)
|
||||
|
||||
def parse_string(self, words):
|
||||
@@ -451,37 +452,40 @@ O2
|
||||
class InteractionMatrix:
|
||||
"""Interaction matrix class."""
|
||||
|
||||
def __init__(self, name):
|
||||
def __init__(self, name: str):
|
||||
"""Initialize with name of matrix.
|
||||
|
||||
Args:
|
||||
name: name of interaction matrix
|
||||
"""
|
||||
self.name = name
|
||||
self.value = None
|
||||
self.ordered_keys = []
|
||||
self.dictionary = {}
|
||||
self.ordered_keys: List[str] = []
|
||||
self.dictionary: Dict[str, Dict[str, Union[str, float]]] = {}
|
||||
|
||||
def add(self, words):
|
||||
def add(self, words: Sequence[str]):
|
||||
"""Add values to matrix.
|
||||
|
||||
Args:
|
||||
words: values to add
|
||||
"""
|
||||
len_expected = len(self.ordered_keys) + 2
|
||||
if len(words) != len_expected:
|
||||
raise ValueError(f"Expected {len_expected} arguments, got {words!r}")
|
||||
new_group = words[0]
|
||||
self.ordered_keys.append(new_group)
|
||||
if new_group not in self.dictionary.keys():
|
||||
self.dictionary[new_group] = {}
|
||||
for i, group in enumerate(self.ordered_keys):
|
||||
if len(words) > i+1:
|
||||
value: Union[str, float]
|
||||
try:
|
||||
self.value = float(words[i+1])
|
||||
value = float(words[i+1])
|
||||
except ValueError:
|
||||
self.value = words[i+1]
|
||||
self.dictionary[group][new_group] = self.value
|
||||
self.dictionary[new_group][group] = self.value
|
||||
value = words[i+1]
|
||||
self.dictionary[group][new_group] = value
|
||||
self.dictionary[new_group][group] = value
|
||||
|
||||
def get_value(self, item1, item2):
|
||||
def get_value(self, item1: str, item2: str) -> Union[str, float, None]:
|
||||
"""Get specific matrix value.
|
||||
|
||||
Args:
|
||||
@@ -495,7 +499,7 @@ class InteractionMatrix:
|
||||
except KeyError:
|
||||
return None
|
||||
|
||||
def __getitem__(self, group):
|
||||
def __getitem__(self, group: str):
|
||||
"""Get specific group from matrix.
|
||||
|
||||
Args:
|
||||
@@ -531,17 +535,17 @@ class InteractionMatrix:
|
||||
class PairwiseMatrix:
|
||||
"""Pairwise interaction matrix class."""
|
||||
|
||||
def __init__(self, name):
|
||||
def __init__(self, name: str):
|
||||
"""Initialize pairwise matrix.
|
||||
|
||||
Args:
|
||||
name: name of pairwise interaction
|
||||
"""
|
||||
self.name = name
|
||||
self.dictionary = {}
|
||||
self.default = [0.0, 0.0]
|
||||
self.dictionary: Dict[str, Dict[str, Tuple[float, float]]] = {}
|
||||
self.default = (0.0, 0.0)
|
||||
|
||||
def add(self, words):
|
||||
def add(self, words: Sequence[str]):
|
||||
"""Add information to the matrix.
|
||||
|
||||
TODO - this function unnecessarily bundles arguments into a tuple
|
||||
@@ -551,16 +555,17 @@ class PairwiseMatrix:
|
||||
"""
|
||||
# assign the default value
|
||||
if len(words) == 3 and words[0] == 'default':
|
||||
self.default = [float(words[1]), float(words[2])]
|
||||
self.default = (float(words[1]), float(words[2]))
|
||||
return
|
||||
# assign non-default values
|
||||
assert len(words) == 4
|
||||
group1 = words[0]
|
||||
group2 = words[1]
|
||||
value = [float(words[2]), float(words[3])]
|
||||
value = (float(words[2]), float(words[3]))
|
||||
self.insert(group1, group2, value)
|
||||
self.insert(group2, group1, value)
|
||||
|
||||
def insert(self, key1, key2, value):
|
||||
def insert(self, key1: str, key2: str, value: Tuple[float, float]):
|
||||
"""Insert value into matrix.
|
||||
|
||||
Args:
|
||||
@@ -578,7 +583,7 @@ class PairwiseMatrix:
|
||||
self.dictionary[key1] = {}
|
||||
self.dictionary[key1][key2] = value
|
||||
|
||||
def get_value(self, item1, item2):
|
||||
def get_value(self, item1: str, item2: str) -> Tuple[float, float]:
|
||||
"""Get specified value from matrix.
|
||||
|
||||
Args:
|
||||
@@ -592,7 +597,7 @@ class PairwiseMatrix:
|
||||
except KeyError:
|
||||
return self.default
|
||||
|
||||
def __getitem__(self, group):
|
||||
def __getitem__(self, group: str):
|
||||
"""Get item from matrix corresponding to specific group.
|
||||
|
||||
Args:
|
||||
|
||||
@@ -7,6 +7,7 @@ Contains version-specific methods and parameters.
|
||||
TODO - this module unnecessarily confuses the code. Can we eliminate it?
|
||||
"""
|
||||
import logging
|
||||
from typing import Sequence, Tuple
|
||||
from propka.atom import Atom
|
||||
from propka.hydrogens import setup_bonding_and_protonation, setup_bonding
|
||||
from propka.hydrogens import setup_bonding_and_protonation_30_style
|
||||
@@ -15,6 +16,7 @@ from propka.energy import hydrogen_bond_energy, hydrogen_bond_interaction
|
||||
from propka.energy import electrostatic_interaction, check_coulomb_pair
|
||||
from propka.energy import coulomb_energy, check_exceptions
|
||||
from propka.energy import backbone_reorganization
|
||||
from propka.parameters import Parameters
|
||||
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
@@ -22,7 +24,7 @@ _LOGGER = logging.getLogger(__name__)
|
||||
|
||||
class Version:
|
||||
"""Store version-specific methods and parameters."""
|
||||
def __init__(self, parameters):
|
||||
def __init__(self, parameters: Parameters):
|
||||
self.parameters = parameters
|
||||
self.desolvation_model = self.empty_function
|
||||
self.weight_pair_method = self.empty_function
|
||||
@@ -99,7 +101,7 @@ class Version:
|
||||
"""Setup bonding using assigned model."""
|
||||
return self.prepare_bonds(self.parameters, molecular_container)
|
||||
|
||||
def get_hydrogen_bond_parameters(self, atom1: Atom, atom2: Atom) -> tuple:
|
||||
def get_hydrogen_bond_parameters(self, atom1: Atom, atom2: Atom) -> Tuple[float, Sequence[float]]:
|
||||
"""Get hydrogen bond parameters for two atoms."""
|
||||
raise NotImplementedError("abstract method")
|
||||
|
||||
@@ -136,7 +138,7 @@ class VersionA(Version):
|
||||
dpka_max = self.parameters.sidechain_interaction
|
||||
cutoff = self.parameters.sidechain_cutoffs.get_value(
|
||||
atom1.group_type, atom2.group_type)
|
||||
return [dpka_max, cutoff]
|
||||
return dpka_max, cutoff
|
||||
|
||||
def get_backbone_hydrogen_bond_parameters(self, backbone_atom, atom):
|
||||
"""Get hydrogen bond parameters between backbone atom and other atom.
|
||||
@@ -311,4 +313,4 @@ class Propka30(Version):
|
||||
atom1.group_type, atom2.group_type)
|
||||
cutoff = self.parameters.sidechain_cutoffs.get_value(
|
||||
atom1.group_type, atom2.group_type)
|
||||
return [dpka_max, cutoff]
|
||||
return dpka_max, cutoff
|
||||
|
||||
Reference in New Issue
Block a user