diff --git a/propka/conformation_container.py b/propka/conformation_container.py index c91f998..9defd81 100644 --- a/propka/conformation_container.py +++ b/propka/conformation_container.py @@ -13,6 +13,7 @@ from propka.determinants import set_backbone_determinants, set_ion_determinants from propka.determinants import set_determinants from propka.group import Group, is_group +from typing import Iterable _LOGGER = logging.getLogger(__name__) @@ -553,9 +554,22 @@ def top_up(self, other): Args: other: conformation container with atoms to add """ + self.top_up_from_atoms(other.atoms) + + def top_up_from_atoms(self, other_atoms: Iterable["propka.atom.Atom"]): + """Adds atoms which are missing from this container. + + Args: + other_atoms: Reference atoms + """ my_residue_labels = {a.residue_label for a in self.atoms} - for atom in other.atoms: + res_names = {(a.chain_id, a.res_num): a.res_name for a in self.atoms} + for atom in other_atoms: if atom.residue_label not in my_residue_labels: + if res_names.setdefault((atom.chain_id, atom.res_num), + atom.res_name) != atom.res_name: + # don't merge different residue types, e.g. alt-loc mutant + continue self.copy_atom(atom) def find_group(self, group): diff --git a/propka/molecular_container.py b/propka/molecular_container.py index f1b2b36..37aba70 100644 --- a/propka/molecular_container.py +++ b/propka/molecular_container.py @@ -52,10 +52,13 @@ def __init__(self, parameters, options=None): def top_up_conformations(self): """Makes sure that all atoms are present in all conformations.""" - first = self.conformations[self.conformation_names[0]] - for name in self.conformation_names[1:]: - if len(self.conformations[name]) < len(first): - self.conformations[name].top_up(first) + ref_atoms = { + atom.residue_label: atom + for name in reversed(self.conformation_names) + for atom in self.conformations[name].atoms + } + for conf in self.conformations.values(): + conf.top_up_from_atoms(ref_atoms.values()) def find_covalently_coupled_groups(self): """Find covalently coupled groups.""" diff --git a/tests/pdb/conf-alt-AB-mutant.pdb b/tests/pdb/conf-alt-AB-mutant.pdb new file mode 100644 index 0000000..15c1a96 --- /dev/null +++ b/tests/pdb/conf-alt-AB-mutant.pdb @@ -0,0 +1,23 @@ +ATOM 1 N GLY 1 2.037 -0.982 0.836 1.00 0.00 N +ATOM 2 CA GLY 1 3.462 -0.865 0.540 1.00 0.00 C +ATOM 3 C GLY 1 4.291 -1.573 1.584 1.00 0.00 C +ATOM 4 O GLY 1 3.777 -2.158 2.541 1.00 0.00 O +ATOM 5 N ASER 2 5.576 -1.566 1.473 1.00 0.00 N +ATOM 6 CA ASER 2 6.377 -2.251 2.483 1.00 0.00 C +ATOM 7 C ASER 2 7.852 -2.130 2.177 1.00 0.00 C +ATOM 8 O ASER 2 8.265 -1.521 1.190 1.00 0.00 O +ATOM 9 CB ASER 2 5.943 -3.732 2.620 1.00 0.00 C +ATOM 10 OG ASER 2 6.285 -4.520 1.474 1.00 0.00 O +ATOM 11 N BVAL 2 5.577 -1.568 1.470 1.00 0.00 N +ATOM 12 CA BVAL 2 6.380 -2.234 2.491 1.00 0.00 C +ATOM 13 C BVAL 2 7.853 -2.130 2.173 1.00 0.00 C +ATOM 14 O BVAL 2 8.265 -1.521 1.190 1.00 0.00 O +ATOM 15 CB BVAL 2 5.939 -3.746 2.618 1.00 0.00 C +ATOM 16 CG1BVAL 2 5.960 -4.584 1.310 1.00 0.00 C +ATOM 17 CG2BVAL 2 6.774 -4.559 3.636 1.00 0.00 C +ATOM 18 N GLY 3 8.703 -2.681 2.974 1.00 0.00 N +ATOM 19 CA GLY 3 10.128 -2.564 2.677 1.00 0.00 C +ATOM 20 C GLY 3 10.957 -3.272 3.721 1.00 0.00 C +ATOM 21 O GLY 3 10.444 -3.857 4.678 1.00 0.00 O +TER +END diff --git a/tests/pdb/conf-alt-AB.pdb b/tests/pdb/conf-alt-AB.pdb new file mode 100644 index 0000000..3edd879 --- /dev/null +++ b/tests/pdb/conf-alt-AB.pdb @@ -0,0 +1,19 @@ +ATOM 1 N GLY 1 2.037 -0.982 0.836 1.00 0.00 N +ATOM 2 CA GLY 1 3.462 -0.865 0.540 1.00 0.00 C +ATOM 3 C GLY 1 4.291 -1.573 1.584 1.00 0.00 C +ATOM 4 O GLY 1 3.777 -2.158 2.541 1.00 0.00 O +ATOM 5 N SER 2 5.576 -1.566 1.473 1.00 0.00 N +ATOM 6 CA ASER 2 6.377 -2.251 2.483 1.00 0.00 C +ATOM 7 CA BSER 2 6.377 -2.251 2.483 1.00 0.00 C +ATOM 8 C SER 2 7.852 -2.130 2.177 1.00 0.00 C +ATOM 9 O SER 2 8.265 -1.521 1.190 1.00 0.00 O +ATOM 10 CB ASER 2 5.943 -3.732 2.620 1.00 0.00 C +ATOM 11 CB BSER 2 5.943 -3.732 2.620 1.00 0.00 C +ATOM 12 OG ASER 2 6.285 -4.520 1.474 1.00 0.00 O +ATOM 13 OG BSER 2 6.994 -4.577 3.101 1.00 0.00 O +ATOM 14 N GLY 3 8.703 -2.681 2.974 1.00 0.00 N +ATOM 15 CA GLY 3 10.128 -2.564 2.677 1.00 0.00 C +ATOM 16 C GLY 3 10.957 -3.272 3.721 1.00 0.00 C +ATOM 17 O GLY 3 10.444 -3.857 4.678 1.00 0.00 O +TER +END diff --git a/tests/pdb/conf-alt-BC.pdb b/tests/pdb/conf-alt-BC.pdb new file mode 100644 index 0000000..c7fdeec --- /dev/null +++ b/tests/pdb/conf-alt-BC.pdb @@ -0,0 +1,19 @@ +ATOM 1 N GLY 1 2.037 -0.982 0.836 1.00 0.00 N +ATOM 2 CA GLY 1 3.462 -0.865 0.540 1.00 0.00 C +ATOM 3 C GLY 1 4.291 -1.573 1.584 1.00 0.00 C +ATOM 4 O GLY 1 3.777 -2.158 2.541 1.00 0.00 O +ATOM 5 N SER 2 5.576 -1.566 1.473 1.00 0.00 N +ATOM 6 CA BSER 2 6.377 -2.251 2.483 1.00 0.00 C +ATOM 7 CA CSER 2 6.377 -2.251 2.483 1.00 0.00 C +ATOM 8 C SER 2 7.852 -2.130 2.177 1.00 0.00 C +ATOM 9 O SER 2 8.265 -1.521 1.190 1.00 0.00 O +ATOM 10 CB BSER 2 5.943 -3.732 2.620 1.00 0.00 C +ATOM 11 CB CSER 2 5.943 -3.732 2.620 1.00 0.00 C +ATOM 12 OG BSER 2 6.285 -4.520 1.474 1.00 0.00 O +ATOM 13 OG CSER 2 6.994 -4.577 3.101 1.00 0.00 O +ATOM 14 N GLY 3 8.703 -2.681 2.974 1.00 0.00 N +ATOM 15 CA GLY 3 10.128 -2.564 2.677 1.00 0.00 C +ATOM 16 C GLY 3 10.957 -3.272 3.721 1.00 0.00 C +ATOM 17 O GLY 3 10.444 -3.857 4.678 1.00 0.00 O +TER +END diff --git a/tests/pdb/conf-model-missing-atoms.pdb b/tests/pdb/conf-model-missing-atoms.pdb new file mode 100644 index 0000000..ecc96b2 --- /dev/null +++ b/tests/pdb/conf-model-missing-atoms.pdb @@ -0,0 +1,45 @@ +MODEL 1 +ATOM 1 N GLY 1 2.037 -0.982 0.836 1.00 0.00 N +ATOM 2 CA GLY 1 3.462 -0.865 0.540 1.00 0.00 C +ATOM 3 C GLY 1 4.291 -1.573 1.584 1.00 0.00 C +ATOM 4 O GLY 1 3.777 -2.158 2.541 1.00 0.00 O +ATOM 5 N VAL 2 5.577 -1.568 1.470 1.00 0.00 N +ATOM 6 CA VAL 2 6.380 -2.234 2.491 1.00 0.00 C +ATOM 7 C VAL 2 7.853 -2.130 2.173 1.00 0.00 C +ATOM 8 O VAL 2 8.265 -1.521 1.190 1.00 0.00 O +ATOM 9 CB VAL 2 5.939 -3.746 2.618 1.00 0.00 C +ATOM 10 N GLY 3 8.703 -2.681 2.974 1.00 0.00 N +ATOM 11 CA GLY 3 10.128 -2.564 2.677 1.00 0.00 C +ATOM 12 C GLY 3 10.957 -3.272 3.721 1.00 0.00 C +ATOM 13 O GLY 3 10.444 -3.857 4.678 1.00 0.00 O +TER +ENDMDL +MODEL 2 +ATOM 14 N GLY 1 2.037 -0.982 0.836 1.00 0.00 N +ATOM 15 CA GLY 1 3.462 -0.865 0.540 1.00 0.00 C +ATOM 16 C GLY 1 4.291 -1.573 1.584 1.00 0.00 C +ATOM 17 O GLY 1 3.777 -2.158 2.541 1.00 0.00 O +ATOM 18 N VAL 2 5.577 -1.568 1.470 1.00 0.00 N +ATOM 19 CA VAL 2 6.380 -2.234 2.491 1.00 0.00 C +ATOM 20 C VAL 2 7.853 -2.130 2.173 1.00 0.00 C +ATOM 21 O VAL 2 8.265 -1.521 1.190 1.00 0.00 O +ATOM 22 CB VAL 2 5.939 -3.746 2.618 1.00 0.00 C +ATOM 23 CG1 VAL 2 5.960 -4.584 1.310 1.00 0.00 C +ATOM 24 CG2 VAL 2 6.774 -4.559 3.636 1.00 0.00 C +ATOM 25 N GLY 3 8.703 -2.681 2.974 1.00 0.00 N +ATOM 26 CA GLY 3 10.128 -2.564 2.677 1.00 0.00 C +ATOM 27 C GLY 3 10.957 -3.272 3.721 1.00 0.00 C +ATOM 28 O GLY 3 10.444 -3.857 4.678 1.00 0.00 O +TER +ENDMDL +MODEL 3 +ATOM 29 CA GLY 1 3.462 -0.865 0.540 1.00 0.00 C +ATOM 30 C GLY 1 4.291 -1.573 1.584 1.00 0.00 C +ATOM 31 O GLY 1 3.777 -2.158 2.541 1.00 0.00 O +ATOM 32 N VAL 2 5.577 -1.568 1.470 1.00 0.00 N +ATOM 33 CA VAL 2 6.380 -2.234 2.491 1.00 0.00 C +ATOM 34 C VAL 2 7.853 -2.130 2.173 1.00 0.00 C +ATOM 35 O VAL 2 8.265 -1.521 1.190 1.00 0.00 O +TER +ENDMDL +END diff --git a/tests/pdb/conf-model-mutant.pdb b/tests/pdb/conf-model-mutant.pdb new file mode 100644 index 0000000..f568b52 --- /dev/null +++ b/tests/pdb/conf-model-mutant.pdb @@ -0,0 +1,36 @@ +MODEL 1 +ATOM 1 N GLY 1 2.037 -0.982 0.836 1.00 0.00 N +ATOM 2 CA GLY 1 3.462 -0.865 0.540 1.00 0.00 C +ATOM 3 C GLY 1 4.291 -1.573 1.584 1.00 0.00 C +ATOM 4 O GLY 1 3.777 -2.158 2.541 1.00 0.00 O +ATOM 5 N SER 2 5.576 -1.566 1.473 1.00 0.00 N +ATOM 6 CA SER 2 6.377 -2.251 2.483 1.00 0.00 C +ATOM 7 C SER 2 7.852 -2.130 2.177 1.00 0.00 C +ATOM 8 O SER 2 8.265 -1.521 1.190 1.00 0.00 O +ATOM 9 CB SER 2 5.943 -3.732 2.620 1.00 0.00 C +ATOM 10 OG SER 2 6.285 -4.520 1.474 1.00 0.00 O +ATOM 11 N GLY 3 8.703 -2.681 2.974 1.00 0.00 N +ATOM 12 CA GLY 3 10.128 -2.564 2.677 1.00 0.00 C +ATOM 13 C GLY 3 10.957 -3.272 3.721 1.00 0.00 C +ATOM 14 O GLY 3 10.444 -3.857 4.678 1.00 0.00 O +TER +ENDMDL +MODEL 2 +ATOM 15 N GLY 1 2.037 -0.982 0.836 1.00 0.00 N +ATOM 16 CA GLY 1 3.462 -0.865 0.540 1.00 0.00 C +ATOM 17 C GLY 1 4.291 -1.573 1.584 1.00 0.00 C +ATOM 18 O GLY 1 3.777 -2.158 2.541 1.00 0.00 O +ATOM 19 N VAL 2 5.577 -1.568 1.470 1.00 0.00 N +ATOM 20 CA VAL 2 6.380 -2.234 2.491 1.00 0.00 C +ATOM 21 C VAL 2 7.853 -2.130 2.173 1.00 0.00 C +ATOM 22 O VAL 2 8.265 -1.521 1.190 1.00 0.00 O +ATOM 23 CB VAL 2 5.939 -3.746 2.618 1.00 0.00 C +ATOM 24 CG1 VAL 2 5.960 -4.584 1.310 1.00 0.00 C +ATOM 25 CG2 VAL 2 6.774 -4.559 3.636 1.00 0.00 C +ATOM 26 N GLY 3 8.703 -2.681 2.974 1.00 0.00 N +ATOM 27 CA GLY 3 10.128 -2.564 2.677 1.00 0.00 C +ATOM 28 C GLY 3 10.957 -3.272 3.721 1.00 0.00 C +ATOM 29 O GLY 3 10.444 -3.857 4.678 1.00 0.00 O +TER +ENDMDL +END diff --git a/tests/test_molecular_container.py b/tests/test_molecular_container.py new file mode 100644 index 0000000..02f154e --- /dev/null +++ b/tests/test_molecular_container.py @@ -0,0 +1,56 @@ +from propka.input import read_parameter_file, read_molecule_file +from propka.lib import loadOptions +from propka.molecular_container import MolecularContainer +from propka.parameters import Parameters +from pathlib import Path +from pytest import approx + +TESTS = Path(__file__).resolve().parent + + +def load_molecule(code): + pdb_path = TESTS / f"pdb/{code}.pdb" + options = [str(pdb_path)] + args = loadOptions(options) + parameters = read_parameter_file(args.parameters, Parameters()) + molecule = MolecularContainer(parameters, args) + molecule = read_molecule_file(str(pdb_path), molecule) + return molecule + + +def test_MolecularContainer_get_pi(): + molecule = load_molecule("1HPX") + molecule.average_of_conformations() + molecule.calculate_pka() + pi_folded, pi_unfolded = molecule.get_pi() + assert pi_folded == approx(9.54, abs=1e-2) + assert pi_unfolded == approx(8.90, abs=1e-2) + + +def test_MolecularContainer_top_up_conformations(): + molecule = load_molecule("conf-alt-AB") + assert len(molecule.conformations) == 2 + assert len(molecule.conformations["1A"]) == 16 + assert len(molecule.conformations["1B"]) == 16 + + molecule = load_molecule("conf-alt-BC") + assert len(molecule.conformations) == 3 + assert len(molecule.conformations["1A"]) == 16 + assert len(molecule.conformations["1B"]) == 16 + assert len(molecule.conformations["1C"]) == 16 + + molecule = load_molecule("conf-alt-AB-mutant") + assert len(molecule.conformations) == 2 + assert len(molecule.conformations["1A"]) == 16 + assert len(molecule.conformations["1B"]) == 17 + + molecule = load_molecule("conf-model-mutant") + assert len(molecule.conformations) == 2 + assert len(molecule.conformations["1A"]) == 16 + assert len(molecule.conformations["2A"]) == 17 + + molecule = load_molecule("conf-model-missing-atoms") + assert len(molecule.conformations) == 3 + assert len(molecule.conformations["1A"]) == 17 + assert len(molecule.conformations["2A"]) == 17 + assert len(molecule.conformations["3A"]) == 17