Source code for vermouth.processors.sort_molecule_atoms
# Copyright 2018 University of Groningen
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""
Provides a processor that sorts atoms within molecules.
"""
from functools import partial
from .processor import Processor
from ..log_helpers import StyleAdapter, get_logger
LOGGER = StyleAdapter(get_logger(__name__))
[docs]
class SortMoleculeAtoms(Processor):
"""
Sort the atoms within a molecule by the attributes listed in the
:attr:`sortby_attrs`. Optionally, new atom indices are assigned to the node
attribute :attr:`target_attr`.
Sorting nodes is useful because a lot of software assumes chains and
residues are listed contiguously. In particular this gets important when we
add atoms --- for instance missing atoms identified by
:class:`vermouth.processors.repair_graph.RepairGraph`).
Nodes in the molecule are reordered according to the node attributes listed
in :attr:`sortby_attrs`. The atom keys are left identical, only the order
of the nodes is changed. Optionally, the new indices can be assigned to
nodes :attr:`target_attr` attribute.
Attributes
----------
sortby_attrs: collections.abc.Sequence[collections.abc.Hashable]
Nodes will be sorted by these node attributes.
target_attr: collections.abc.Hashable
If not ``None``, new indices will be assigned to this node attribute,
starting with 1. It is a good idea to make sure this attribute is also
listed in :attr:`sortby_attrs` so that the sorting is stable.
"""
def __init__(self, sortby_attrs=('chain', 'resid', 'resname', 'insertion_code', 'atomid'),
target_attr=None):
self.sortby_attrs = sortby_attrs
self.target_attr = target_attr
if self.target_attr is not None and self.target_attr not in self.sortby_attrs:
LOGGER.warning("{} is not in {}. Atom sorting may be unstable.",
self.target_attr, self.sortby_attrs)
[docs]
def run_molecule(self, molecule):
node_order = sorted(molecule, key=partial(_keyfunc, molecule, attrs=self.sortby_attrs))
for new_idx, node_key in enumerate(node_order, 1):
molecule._node.move_to_end(node_key)
if self.target_attr is not None:
molecule.nodes[node_key][self.target_attr] = new_idx
return molecule
def _keyfunc(graph, node_idx, attrs):
"""
Reduce a molecule node to a tuple of chain, resid, and resname.
"""
return [graph.nodes[node_idx].get(attr) for attr in attrs]