Merge pull request #187 from speleo3/remove-unused

Remove unused code
This commit is contained in:
Thomas Holder
2024-02-19 08:39:07 +01:00
committed by GitHub
12 changed files with 76 additions and 418 deletions

View File

@@ -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
=======

View File

@@ -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

View File

@@ -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

View File

@@ -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)

View File

@@ -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.

View File

@@ -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':

View File

@@ -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'

View File

@@ -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",

View File

@@ -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

View File

@@ -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.

View File

@@ -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:

View File

@@ -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