Completed
Push — master ( ea74a1...6ddd48 )
by Rich
04:03
created

Conformer.canonicalize()   A

Complexity

Conditions 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 6
ccs 3
cts 3
cp 1
rs 9.4285
cc 1
crap 1
1
#! /usr/bin/env python
2
#
3
# Copyright (C) 2015-2016 Rich Lewis <[email protected]>
4
# License: 3-clause BSD
5
6 1
"""
7
## skchem.core.conformer
8
9
Defining conformers in scikit-chem.
10
"""
11
12 1
import warnings
13
14 1
import rdkit.Chem
0 ignored issues
show
Configuration introduced by
The import rdkit.Chem could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
15 1
from rdkit.Chem.rdDepictor import Compute2DCoords
0 ignored issues
show
Configuration introduced by
The import rdkit.Chem.rdDepictor could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
16 1
from rdkit.Chem.rdDistGeom import EmbedMolecule, EmbedMultipleConfs
0 ignored issues
show
Configuration introduced by
The import rdkit.Chem.rdDistGeom could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
Unused Code introduced by
Unused EmbedMolecule imported from rdkit.Chem.rdDistGeom
Loading history...
17
18 1
import numpy as np
0 ignored issues
show
Configuration introduced by
The import numpy could not be resolved.

This can be caused by one of the following:

1. Missing Dependencies

This error could indicate a configuration issue of Pylint. Make sure that your libraries are available by adding the necessary commands.

# .scrutinizer.yml
before_commands:
    - sudo pip install abc # Python2
    - sudo pip3 install abc # Python3
Tip: We are currently not using virtualenv to run pylint, when installing your modules make sure to use the command for the correct version.

2. Missing __init__.py files

This error could also result from missing __init__.py files in your module folders. Make sure that you place one file in each sub-folder.

Loading history...
19
20 1
from .base import ChemicalObject, ChemicalObjectView
21
22
23 1
class Conformer(rdkit.Chem.rdchem.Conformer, ChemicalObject):
24
25
    """ Class representing a Conformer in scikit-chem. """
26
27 1
    @property
28
    def owner(self):
29
30
        """ skchem.Mol: the owning molecule. """
31 1
        from .mol import Mol
32 1
        return Mol.from_super(self.GetOwningMol())
0 ignored issues
show
Bug introduced by
The Instance of Conformer does not seem to have a member named GetOwningMol.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
33
34 1
    @property
35
    def positions(self):
36
37
        """ np.ndarray: the atom positions in the conformer.
38
39
        Note:
40
            This is a copy of the data, not the data itself.  You cannot
41
            allocate to a slice of this.
42
        """
43
44
        # cant slice this array sadly.
45
46 1
        return np.array([self.GetAtomPosition(i) for i in range(len(self))])
0 ignored issues
show
Bug introduced by
The Instance of Conformer does not seem to have a member named GetAtomPosition.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
47
48 1
    @positions.setter
49
    def positions(self, val):
0 ignored issues
show
Coding Style introduced by
This method should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
50
51 1
        assert val.shape[0] == len(self), 'Positions must be given only for ' \
52
                                          'atoms in the molecule.'
53 1
        assert 1 < val.shape[1] <= 3, 'Positions must be 2 or 3 dimensional.'
54
55 1
        if val.shape[1] == 2:
56 1
            val = np.hstack((val, np.zeros((len(val), 1))))
57
58 1
        self.Set3D(bool((val[:, 2] != 0).any()))
0 ignored issues
show
Bug introduced by
The Instance of Conformer does not seem to have a member named Set3D.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
59
60 1
        return [self.SetAtomPosition(i, v) for i, v in enumerate(val)]
0 ignored issues
show
Bug introduced by
The Instance of Conformer does not seem to have a member named SetAtomPosition.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
61
62 1
    @property
63
    def centre_of_mass(self):
64
65
        """ np.array: the centre of mass of the comformer. """
66
67 1
        atomic_mass = self.owner.atoms.atomic_mass
68 1
        return atomic_mass.dot(self.positions) / atomic_mass.sum()
69
70 1
    @property
71
    def geometric_centre(self):
72
73
        """ np.array: the geometric centre of the conformer. """
74
75 1
        return self.positions.mean(axis=0)
76
77 1
    def centre_representation(self, centre_of_mass=True):
78
79
        """ Centre representation to the center of mass.
80
81
        Args:
82
            centre_of_mass (bool):
83
                Whether to use the masses of atoms to calculate the centre of
84
                mass, or just use the mean position coordinate.
85
86
        Returns:
87
            Conformer
88
        """
89
90 1
        if centre_of_mass:
91 1
            self.positions -= self.centre_of_mass
92
        else:
93 1
            self.positions -= self.geometric_centre
94
95 1
    def _inertia_tensor(self):
96
97
        """ Calculate the inertia tensor. """
98
99 1
        mass = self.owner.atoms.atomic_mass
100 1
        pos = self.positions
101
102 1
        return np.array([[((pos[:, (i % 3) - 1] ** 2 +
103
                            pos[:, (j % 3) - 2] ** 2 if (i == j)
104
                            else - pos[:, i] * pos[:, j]) * mass).sum()
105
                          for i in range(3)] for j in range(3)])
106
107 1
    def align_with_principal_axes(self):
108
109
        """ Align the reference frame with the principal axes of inertia. """
110
111 1
        eig_val, eig_vects = np.linalg.eigh(self._inertia_tensor())
0 ignored issues
show
Unused Code introduced by
The variable eig_val seems to be unused.
Loading history...
112 1
        self.positions = self.positions.dot(eig_vects)
113
114 1
    def canonicalize(self):
115
116
        """ Center the reference frame at the centre of mass and """
117
118 1
        self.centre_representation(centre_of_mass=True)
119 1
        self.align_with_principal_axes()
120
121 1
    @property
122
    def id(self):
0 ignored issues
show
Coding Style Naming introduced by
The name id does not conform to the attribute naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
123
124
        """ The ID of the conformer. """
125
126 1
        return self.GetId()
0 ignored issues
show
Bug introduced by
The Instance of Conformer does not seem to have a member named GetId.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
127
128 1
    @id.setter
129
    def id(self, value):
0 ignored issues
show
Coding Style Naming introduced by
The name id does not conform to the attribute naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style introduced by
This method should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
130
131 1
        warnings.warn('Setting the conformer value directly '
132
                      'may cause issues.', UserWarning)
133 1
        self.SetId(int(value))
0 ignored issues
show
Bug introduced by
The Instance of Conformer does not seem to have a member named SetId.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
134
135 1
    @property
136
    def is_3d(self):
137
138
        """ bool: whether the conformer is three dimensional. """
139
140 1
        return self.Is3D()
0 ignored issues
show
Bug introduced by
The Instance of Conformer does not seem to have a member named Is3D.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
141
142 1
    @is_3d.setter
143
    def is_3d(self, val):
0 ignored issues
show
Coding Style introduced by
This method should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
Unused Code introduced by
The argument val seems to be unused.
Loading history...
144
145 1
        pos = self.positions
146 1
        pos[:, 2] = 0
147 1
        self.positions = pos
148
149 1
    def __len__(self):
150
151 1
        return self.GetNumAtoms()
0 ignored issues
show
Bug introduced by
The Instance of Conformer does not seem to have a member named GetNumAtoms.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
152
153 1
    def __repr__(self):
154 1
        return '<{klass} id="{id}" at {address}>'.format(
155
            klass=self.__class__.__name__,
156
            id=self.GetId(),
0 ignored issues
show
Bug introduced by
The Instance of Conformer does not seem to have a member named GetId.

This check looks for calls to members that are non-existent. These calls will fail.

The member could have been renamed or removed.

Loading history...
157
            address=hex(id(self)))
158
159
160 1
class ConformerView(ChemicalObjectView):
0 ignored issues
show
Coding Style introduced by
This class should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
161
162 1
    def __getitem__(self, index):
163
164 1
        res = super(ConformerView, self).__getitem__(index)
165 1
        if res is None:
166 1
            try:
167 1
                return Conformer.from_super(
168
                    self.owner.GetConformer(int(index)))
169 1
            except ValueError:
170 1
                raise IndexError(
171
                        'Conformer id {} not available (choose one '
0 ignored issues
show
Coding Style introduced by
Wrong hanging indentation.
'Conformer id {} not available (choose one '
| ^
Loading history...
172
                        'of {}).'.format(index, tuple(self.id)))
173
        else:
174 1
            return res
175
176 1
    def __setitem__(self, key, value):
177
178 1
        assert isinstance(value, Conformer), 'Only `Conformer`s can be added.'
179 1
        assert len(value) == len(self.owner.atoms), \
180
            '`Conformers` must have the same number of atoms as the `Mol`.'
181 1
        self.owner.RemoveConformer(int(key))
182 1
        with warnings.catch_warnings():
183 1
            warnings.simplefilter('ignore')
184 1
            value.id = key
185 1
        self.owner.AddConformer(value)
186
187 1
    def __delitem__(self, key):
188
189 1
        self.owner.RemoveConformer(key)
190
191 1
    def __iter__(self):
192 1
        return ConformerIterator(self)
193
194 1
    def __len__(self):
195
196 1
        return self.owner.GetNumConformers()
197
198 1
    def append(self, value):
0 ignored issues
show
Coding Style introduced by
This method should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
199
200 1
        assert isinstance(value, rdkit.Chem.Conformer)
201 1
        self[max(self.id) + 1] = value
202
203 1
    def append_2d(self, **kwargs):
204
205
        """ Append a 2D conformer. """
206
207 1
        kwargs['clearConfs'] = False
208 1
        Compute2DCoords(self.owner, **kwargs)
209
210 1
    def append_3d(self, n_conformers=1, **kwargs):
211
212
        """ Append (a) 3D conformer(s), roughly embedded but not optimized.
213
214
        Args:
215
            n_conformers (int):
216
                The number of conformers to append.
217
            Further kwargs are passed to `EmbedMultipleConfs`.
218
219
        """
220
221 1
        kwargs.setdefault('numConfs', n_conformers)
222 1
        kwargs['clearConfs'] = False
223 1
        EmbedMultipleConfs(self.owner, **kwargs)
224
225 1
    @property
226
    def positions(self):
0 ignored issues
show
Coding Style introduced by
This method should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
227
228 1
        return np.array([conformer.positions for conformer in self])
229
230 1
    @property
231
    def is_3d(self):
0 ignored issues
show
Coding Style introduced by
This method should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
232
233 1
        return np.array([conformer.is_3d for conformer in self])
234
235 1
    @property
236
    def id(self):
0 ignored issues
show
Coding Style Naming introduced by
The name id does not conform to the attribute naming conventions ([a-z_][a-z0-9_]{2,30}$).

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style introduced by
This method should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
237
238 1
        return np.array([conf.GetId() for conf in self.owner.GetConformers()])
239
240
241 1
class ConformerIterator(object):
242
243
    """  Iterator for chemical object views.  """
244
245 1
    def __init__(self, view):
246
        """ Create an iterator from a chemical object view. """
247 1
        self.view = view
248 1
        self._ids = view.id
249 1
        self._current = 0
250 1
        self._high = len(view)
251
252 1
    def __next__(self):
253 1
        if self._current >= self._high:
254 1
            raise StopIteration
255
        else:
256 1
            self._current += 1
257 1
            return self.view[self._ids[self._current - 1]]
258
259
    # py2 compat
260
    next = __next__
261