diff --git a/doc/tests/CMakeLists.txt b/doc/tests/CMakeLists.txt index 25154b379fda594dee9f5f97c0f99736b1300831..df61bbb87e86667c3572a225a808dcac5794e405 100644 --- a/doc/tests/CMakeLists.txt +++ b/doc/tests/CMakeLists.txt @@ -32,6 +32,7 @@ set (DOC_TEST_SCRIPTS scripts/loop_frag_db.py scripts/loop_fragger.py scripts/loop_torsion_sampler.py + scripts/loop_all_atom.py scripts/scoring_main.py diff --git a/doc/tests/scripts/loop_all_atom.py b/doc/tests/scripts/loop_all_atom.py new file mode 100644 index 0000000000000000000000000000000000000000..d491873f1cd5db0cdc0d855c6eacf55d43034662 --- /dev/null +++ b/doc/tests/scripts/loop_all_atom.py @@ -0,0 +1,26 @@ +from ost import io, geom +from promod3 import loop + +# load example (has res. numbering starting at 1) +ent = io.LoadPDB("data/1CRN.pdb") +res_list = ent.residues +seqres_str = ''.join([r.one_letter_code for r in res_list]) + +# initialize an environment +env = loop.AllAtomEnv(seqres_str) +env.SetInitialEnvironment(ent) + +# get all atom position for part of it +all_atoms = loop.AllAtomPositions(res_list[35:44]) +# change pos. at res. index 1 (= res. number 37) +idx_ca_res_37 = all_atoms.GetIndex(1, "CA") +new_pos = all_atoms.GetPos(idx_ca_res_37) + geom.Vec3(0.2, 0.2, 0.2) +all_atoms.SetPos(idx_ca_res_37, new_pos) +# insert into ent and store updated entity +all_atoms.InsertInto(1, ent.chains[0], 37) +io.SavePDB(ent, "all_atom_pos.pdb") + +# update environment with new positions +env.SetEnvironment(all_atoms, 36) +# store all positions of environment +io.SavePDB(env.GetAllAtomPositions().ToEntity(), "all_atom_env.pdb") diff --git a/doc/tests/test_doctests.py b/doc/tests/test_doctests.py index 31a1b93f8d59a531559ee2b6d1ecfb20ab4f7a79..201aee01b7ed2b33c9c0827805bfae70bfaed01e 100644 --- a/doc/tests/test_doctests.py +++ b/doc/tests/test_doctests.py @@ -271,6 +271,16 @@ class DocTests(unittest.TestCase): # clean up os.remove('torsion_plot.png') + def testAllAtom(self): + # run it + self.checkPMRun('loop_all_atom.py', [], 0) + # check that result exists and is readable + io.LoadPDB('all_atom_pos.pdb') + io.LoadPDB('all_atom_env.pdb') + # clean up + os.remove('all_atom_pos.pdb') + os.remove('all_atom_env.pdb') + ################################################################ def testScoringMain(self): diff --git a/loop/doc/CMakeLists.txt b/loop/doc/CMakeLists.txt index 47136ac14ad223f27623bdf64a885967df745dd0..a6f2ae30e67f8ae7dfbfbc86b4f9aecbe844c1ad 100644 --- a/loop/doc/CMakeLists.txt +++ b/loop/doc/CMakeLists.txt @@ -1,8 +1,9 @@ set(LOOP_RST index.rst - torsion_sampler.rst backbone.rst + torsion_sampler.rst structure_db.rst + all_atom.rst load_loop_objects.rst ) diff --git a/loop/doc/all_atom.rst b/loop/doc/all_atom.rst new file mode 100644 index 0000000000000000000000000000000000000000..3d8a99fee1c2a96ae6321d738de367ae2dbe35b8 --- /dev/null +++ b/loop/doc/all_atom.rst @@ -0,0 +1,356 @@ +Handling All Atom Positions +================================================================================ + +.. currentmodule:: promod3.loop + +To represent and handle all atom loops, we a container +(:class:`AllAtomPositions`) to represent arbitrary amino acid sequences with the +positions of all their heavy atom and an environment (:class:`AllAtomEnv`) to +handle changes during loop modelling. + +The example below showcases some operations on the two classes: + +.. literalinclude:: ../../../tests/doc/scripts/loop_all_atom.py + + +The AllAtomEnv class +-------------------------------------------------------------------------------- + +.. class:: AllAtomEnv(seqres) + + The all atom environment contains and handles positions of all atoms during + loop modelling. It is linked to a (list of) seqres (one per chain) at + construction. The idea is to initialize it at the beginning of the modelling + process with all known positions and then update the environment whenever a + new loop is being added. + + :param seqres: Internal SEQRES to be set (single chain or list with one per + chain). Whenever setting structural data, consistency with this SEQRES is enforced. + :type seqres: :class:`str` / :class:`ost.seq.SequenceHandle` / + :class:`list` of :class:`str` / :class:`ost.seq.SequenceList` + + Indexing to access parts of the environment: + + * *chain_idx*: Index of chain as it occurs in *seqres* (0 for single sequence) + * *start_resnum*: Residue number defining the position in the SEQRES of chain + with index *chain_idx*. **The numbering starts for every chain with the + value 1**. + + .. method:: SetInitialEnvironment(env_structure) + + Sets full environment. Existing data is cleared first. + + :param env_structure: Structral data to be set as environment. The chains + in *env_structure* are expected to be in the same + order as the SEQRES items provided in constructor. + :type env_structure: :class:`ost.mol.EntityHandle` + + :raises: :exc:`~exceptions.RuntimeError` if *env* is inconsistent with + SEQRES set in constructor. This can be because of corrupt residue + numbers or sequence mismatches. + + .. method:: SetEnvironment(new_pos, start_resnum, chain_idx=0) + + Add/update atom positions in environment. In the end, all residues covered + in *new_pos* will be set as in *new_pos*. This means, that positions in the + env. may be reset, newly set or cleared. + + :param new_pos: Structural data to be set as environment. + :type new_pos: :class:`AllAtomPositions` + :param start_resnum: Res. number defining the start position in the SEQRES. + :type start_resnum: :class:`int` / :class:`ost.mol.ResNum` + :param chain_idx: Index of chain the structural data belongs to. + :type chain_idx: :class:`int` + + :raises: :exc:`~exceptions.RuntimeError` if sequence of *new_pos* is + inconsistent with previously SEQRES set in constructor or when + either *start_resnum* or *chain_idx* point to invalid positions in + the SEQRES. + + .. method:: ClearEnvironment(start_resnum, num_residues, chain_idx=0) + + Clears a stretch of length *num_residues* in the environment in chain + with idx *chain_idx* starting from residue number *start_resnum* + + :param start_resnum: Start of stretch to clear + :type start_resnum: :class:`int` + :param num_residues: Length of stretch to clear + :type num_residues: :class:`int` + :param chain_idx: Chain the stretch belongs to + :type chain_idx: :class:`int` + + :raises: :exc:`~exceptions.RuntimeError` when either *start_resnum* or + *chain_idx* point to invalid positions in the SEQRES. + + .. method:: GetSeqres() + + :return: SEQRES that was set in constructor (one sequence per chain). + :rtype: :class:`ost.seq.SequenceList` + + .. method:: GetAllAtomPositions() + + :return: Reference (use with caution!) to the internal storage of all atom + positions for the environment. All residues of all chains are + stored continuously in there. + :rtype: :class:`AllAtomPositions` + + +The AllAtomPositions class +-------------------------------------------------------------------------------- + +.. class:: AllAtomPositions + + Container for the positions of all heavy atoms of an arbitrary amino acid sequence. This is tailored for fast operations within |C++| codes. The Python export described here, is mainly meant for debugging or to initialize the object and feed it to other classes using it. + + Indexing of positions and residues: + + - residue indexing is in the range [0, :meth:`GetNumResidues`-1] and is in the + order of the sequence used to initialize the object + - indexing of single atoms is in the range [0, :meth:`GetNumAtoms`-1]. For + each residue you can find the bounds with :meth:`GetFirstIndex` and + :meth:`GetLastIndex` or find a single atom with :meth:`GetIndex` + + Each atom position is initially unset (unless a list of residues is passed + when constructing it) and can only be set with calls to :meth:`SetPos` or + :meth:`SetResidue`. + + .. method:: AllAtomPositions() + + Creates empty object. Use :meth:`Initialize` to set a sequence.. + + .. method:: AllAtomPositions(sequence) + + Creates a container for the given *sequence* with all positions unset. + + :param sequence: Sequence of amino acid one letter codes. + :type sequence: :class:`str` + + :raises: :exc:`~exceptions.RuntimeError` if *sequence* contains a one letter + code which is not one of the 20 default amino acids. + + .. method:: AllAtomPositions(residues) + + Creates a container for the given *residues*. Both sequence and positions + are extracted from the given residues. + + :param residues: List of residues + :type residues: :class:`ost.mol.ResidueHandleList` + + :raises: :exc:`~exceptions.RuntimeError` if any residue has a one letter + code which is not one of the 20 default amino acids. + + .. method:: AllAtomPositions(sequence, residues) + + Creates a container for the given *sequence* and extracts positions from + *residues*. The residues may be different amino acids than the given + *sequence* (see :meth:`SetResidue`). + + :param sequence: Sequence of amino acid one letter codes. + :type sequence: :class:`str` + :param residues: List of residues from which positions are extracted. + :type residues: :class:`ost.mol.ResidueHandleList` + + :raises: :exc:`~exceptions.RuntimeError` if *sequence* contains a one letter + code which is not one of the 20 default amino acids or if + *sequence* and *residues* are inconsistent in size. + + .. method:: Initialize(sequence) + + Initializes the container for the given *sequence* with all positions unset. + Existing data is overwritten. + + :param sequence: Sequence of amino acid one letter codes. + :type sequence: :class:`str` + + :raises: :exc:`~exceptions.RuntimeError` if *sequence* contains a one letter + code which is not one of the 20 default amino acids. + + .. method:: SetResidue(res_index, res) + + Set positions for residue at index *res_index* given the atoms in *res*. + For each expected heavy atom, we search for an atom of that name in *res* + and if found set the corresponding position, otherwise we unset it. + + :param res_index: Residue index + :type res_index: :class:`int` + :param res: Residue providing atoms + :type res: :class:`ost.mol.ResidueHandle` + + :raises: :exc:`~exceptions.RuntimeError` if *res_index* out of bounds. + + .. method:: SetResidue(res_index, other, other_res_index) + + Set positions for residue at index *res_index* given the positions of + residue at index *other_res_index* in *other* position container. Each + position is set or cleared according to the data in *other*. + + :param res_index: Residue index + :type res_index: :class:`int` + :param other: Position container from which we take data + :type other: :class:`AllAtomPositions` + :param other_res_index: Residue index in *other* + :type other_res_index: :class:`int` + + :raises: :exc:`~exceptions.RuntimeError` if *res_index* or *other_res_index* + out of bounds or if residues in the two containers are inconsistent + (different amino acids). + + .. method:: SetPos(index, pos) + + :param index: Set position at that index. + :type index: :class:`int` + :param pos: Set position to *pos*. + :type pos: :class:`ost.geom.Vec3` + + :raises: :exc:`~exceptions.RuntimeError` if *index* out of bounds. + + .. method:: ClearPos(index) + + :param index: Unset position at that index. + :type index: :class:`int` + + :raises: :exc:`~exceptions.RuntimeError` if *index* out of bounds. + + .. method:: ClearResidue(res_index) + + :param res_index: Unset all positions for residue at that index. + :type res_index: :class:`int` + + :raises: :exc:`~exceptions.RuntimeError` if *res_index* out of bounds. + + .. method:: GetPos(index) + + :return: Position at given index. + :rtype: :class:`ost.geom.Vec3` + :param index: Atom position index. + :type index: :class:`int` + + :raises: :exc:`~exceptions.RuntimeError` if *index* out of bounds. + + .. method:: IsSet(index) + + :return: True, if the position at that index is currently set. + :rtype: :class:`bool` + :param index: Atom position index. + :type index: :class:`int` + + :raises: :exc:`~exceptions.RuntimeError` if *index* out of bounds. + + .. method:: GetIndex(res_index, atom_name) + + :return: Atom position index for atom named *atom_name* (standard PDB + naming) for residue at index *res_index*. + :rtype: :class:`int` + :param res_index: Residue index + :type res_index: :class:`int` + :param atom_name: Atom name + :type atom_name: :class:`str` + + :raises: :exc:`~exceptions.RuntimeError` if *res_index* out of bounds or + if *atom_name* is not one of that residue's heavy atoms. + + .. method:: GetFirstIndex(res_index) + + :return: First atom position index for residue at index *res_index*. + :rtype: :class:`int` + :param res_index: Residue index + :type res_index: :class:`int` + + :raises: :exc:`~exceptions.RuntimeError` if *res_index* out of bounds. + + .. method:: GetLastIndex(res_index) + + :return: Last atom position index for residue at index *res_index*. + :rtype: :class:`int` + :param res_index: Residue index + :type res_index: :class:`int` + + :raises: :exc:`~exceptions.RuntimeError` if *res_index* out of bounds. + + .. method:: GetAA(res_index) + + :return: Amino acid type of residue at index *res_index*. + :rtype: :class:`ost.conop.AminoAcid` + :param res_index: Residue index + :type res_index: :class:`int` + + :raises: :exc:`~exceptions.RuntimeError` if *res_index* out of bounds. + + .. method:: IsAnySet(res_index) + + :return: True, if any atom position of residue at index *res_index* is set. + :rtype: :class:`bool` + :param res_index: Residue index + :type res_index: :class:`int` + + :raises: :exc:`~exceptions.RuntimeError` if *res_index* out of bounds. + + .. method:: IsAllSet(res_index) + + :return: True, if all atom positions of residue at index *res_index* are set. + :rtype: :class:`bool` + :param res_index: Residue index + :type res_index: :class:`int` + + :raises: :exc:`~exceptions.RuntimeError` if *res_index* out of bounds. + + .. method:: GetNumAtoms() + + :return: Number of atom positions stored in this container. + :rtype: :class:`int` + + .. method:: GetNumResidues() + + :return: Number of residues stored in this container. + :rtype: :class:`int` + + .. method:: GetSequence() + + :return: Sequence of one letter codes of all residues stored here. + :rtype: :class:`str` + + .. method:: Extract(from, to) + + :return: Container with residues with indices in range [*from*, *to*-1]. + :rtype: :class:`AllAtomPositions` + :param from: First residue index + :type from: :class:`int` + :param to: One after last residue index + :type to: :class:`int` + + :raises: :exc:`~exceptions.RuntimeError` if *from* >= *to* or if any residue + index is out of bounds. + + .. method:: Extract(res_indices) + + :return: Container with residues with indices in *res_indices*. + :rtype: :class:`AllAtomPositions` + :param res_indices: List of residue index + :type res_indices: :class:`list` of :class:`int` + + :raises: :exc:`~exceptions.RuntimeError` if any residue index is out of + bounds. + + .. method:: ToEntity() + + :return: All residues packed in a single chain as an OST entity. + Connectivity resolved with :class:`ost.conop.HeuristicProcessor`. + :rtype: :class:`ost.mol.EntityHandle` + + .. method:: InsertInto(res_index, chain, res_num) + + Insert a single residue (taken from given index) into the *chain* (with + given res. number). Existing data is replaced and atoms are (re)connected + according to the default connectivity of that amino acid. Peptide links to + neighboring residues are set according to residue numbering. To make this + function efficient, we require the backbone atoms (N, C, CA) to be set. + + :param res_index: Residue index + :type res_index: :class:`int` + :param chain: Chain into which we insert + :type chain: :class:`ost.mol.ChainHandle` + :param start_resnum: Residue number for the inserted residue + :type start_resnum: :class:`int` / :class:`ost.mol.ResNum` + + :raises: :exc:`~exceptions.RuntimeError` if *res_index* out of bounds, if + *chain* is invalid or if not all backbone atoms (N, C, CA) set. diff --git a/loop/doc/index.rst b/loop/doc/index.rst index 0039b60caca7f3f93abdf8d553e989b71b53b120..7120d93106bb3e44a81f71bce43c8ccce46ee99a 100644 --- a/loop/doc/index.rst +++ b/loop/doc/index.rst @@ -20,4 +20,5 @@ Contents: backbone torsion_sampler structure_db + all_atom load_loop_objects diff --git a/loop/pymod/export_all_atom_env.cc b/loop/pymod/export_all_atom_env.cc index 0f502f0e2146b4c01c9ac2d3a216d5efb4c8d7b5..63e0353a00f3e28779dcd31af84e8e1f27c01d4d 100644 --- a/loop/pymod/export_all_atom_env.cc +++ b/loop/pymod/export_all_atom_env.cc @@ -75,8 +75,9 @@ void export_AllAtomEnv() { (arg("new_pos"), arg("start_resnum"), arg("chain_idx") = 0)) .def("SetEnvironment", WrapSetEnvironmentResNum, (arg("new_pos"), arg("start_resnum"), arg("chain_idx") = 0)) - .def("SetEnvironment", WrapSetEnvironmentResIndices, - (arg("new_pos"), arg("res_indices"))) + // variant with indices only to be exported if Pythonic way to get indices + // .def("SetEnvironment", WrapSetEnvironmentResIndices, + // (arg("new_pos"), arg("res_indices"))) .def("ClearEnvironment", &AllAtomEnv::ClearEnvironment, (arg("start_resnum"), arg("num_residues"), arg("chain_idx") = 0)) .def("GetSeqres", &AllAtomEnv::GetSeqres) diff --git a/loop/pymod/export_all_atom_positions.cc b/loop/pymod/export_all_atom_positions.cc index 2caefb7b6aba14b91496652c6bfcde67ab1fe5b9..334d2e77bcfb994b81bd55f3750c319bd7d3ee63 100644 --- a/loop/pymod/export_all_atom_positions.cc +++ b/loop/pymod/export_all_atom_positions.cc @@ -79,8 +79,8 @@ void export_AllAtomPositions() { .def("GetPos", WrapGetPos, (arg("index")), return_value_policy<copy_const_reference>()) .def("IsSet", WrapIsSet, (arg("index"))) - .def("GetIndex", WrapGetIndex, (arg("index"), arg("atom_name"))) - + + .def("GetIndex", WrapGetIndex, (arg("res_index"), arg("atom_name"))) .def("GetFirstIndex", &AllAtomPositions::GetFirstIndex, (arg("res_index"))) .def("GetLastIndex", &AllAtomPositions::GetLastIndex, (arg("res_index"))) .def("GetAA", &AllAtomPositions::GetAA, (arg("res_index"))) diff --git a/scoring/doc/backbone_score_env.rst b/scoring/doc/backbone_score_env.rst index cab4cc3488669549dd569408c96c6373e56182a2..20657cb38acba081c4b2f507ca3debe7b714cbdc 100644 --- a/scoring/doc/backbone_score_env.rst +++ b/scoring/doc/backbone_score_env.rst @@ -52,7 +52,7 @@ BackboneScoreEnv class :param bb_list: Structural data to be set as environment. :type bb_list: :class:`~promod3.loop.BackboneList` :param start_resnum: Res. number defining the position in the SEQRES. - :type start_resnum: :class:`int` + :type start_resnum: :class:`int` / :class:`ost.mol.ResNum` :param chain_idx: Index of chain the structural data belongs to. :type chain_idx: :class:`int`