fix openeye check
try linux logo
try adding apple
try apple instead of osx
update badges and logo
add unnecessary print statements
fix exception handling and strings in cp_toolkits
try to unify mol_toolkit docs and usage
add temporarily so I can switch branches
fix broken cp_openeye
fix regex patterns to be r'string'
make environment.py more pythonic
update doc string at the top of environment.py
add version to conf.py
clean READMEs and test examples
update order on travis tests
updating doc strings and renaming MolFromSmiles to classmethod
add extraverbose run to travis
fix examples and molfromsmiles imports
refactor, add examples, fix imports throughout
add smirnoffish file and update data/smarts_files README
fix examples in README with new names
fix dictionary additions to be outside loops
fix return on rdk Mol
add blank line before Atom
address issue #84
update doc strings in environment.py
update chemicalenvironment tests with new smirks requirements
fix class references to not break in 3.7
update mol_toolkit tests to use TypeError instead of generic Exception
update doc strings in single_graph
remove AUTHOR section from individual scripts
remove docs/api/py in favor of new examples/py files
ref examples/*py in docs instead of local python files
update doc strings in cluster_graph
fix test_examples so it actually finds *.py files and runs them
openeye version in travis
stop example tests
rename examples test
update environment strings
Remove dep warning - switch #absclsmeth decorator to #abs and #classmeth
resolve py37 err --> make mol_toolkit.Mol have same sig as actual mols
add chemrxiv and update description
add version 1 to readme
1 |
"""
|
|
2 |
cp_openeye.py
|
|
3 |
|
|
4 |
Cheminformatics tools using OpenEye Toolkits
|
|
5 |
|
|
6 |
The classes provided here follow the structure in adapters.
|
|
7 |
This is a wrapper allowing our actual package to use openeye toolkits
|
|
8 |
"""
|
|
9 |
|
|
10 | 41 |
from chemper.mol_toolkits.adapters import MolAdapter, AtomAdapter, BondAdapter |
11 | 41 |
from openeye import oechem |
12 |
|
|
13 |
|
|
14 |
# Note - doc strings on these functions are inherited from
|
|
15 |
# there Adapters. To read these strings see adapters.py.
|
|
16 |
|
|
17 |
# =======================================
|
|
18 |
# Molecule Class
|
|
19 |
# =======================================
|
|
20 |
|
|
21 | 41 |
class Mol(MolAdapter): |
22 | 41 |
def __init__(self, mol): |
23 |
"""
|
|
24 |
ChemPer created from an OEMol
|
|
25 |
|
|
26 |
Parameters
|
|
27 |
----------
|
|
28 |
mol : openeye OEMol object
|
|
29 |
openeye molecule to convert to ChemPer Mol object
|
|
30 |
"""
|
|
31 | 41 |
if not isinstance(mol, oechem.OEMolBase): |
32 | 41 |
raise TypeError("Expecting an OEMol object instead of %s" % type(mol)) |
33 | 41 |
self.mol = mol |
34 |
|
|
35 | 41 |
def __str__(self): return self.get_smiles() |
36 |
|
|
37 | 41 |
@classmethod
|
38 |
def from_smiles(cls, smiles): |
|
39 | 41 |
mol = oechem.OEMol() |
40 | 41 |
if not oechem.OESmilesToMol(mol, smiles): |
41 | 41 |
raise ValueError('Could not parse SMILES %s' % smiles) |
42 | 41 |
oechem.OEAddExplicitHydrogens(mol) |
43 | 41 |
return cls(mol) |
44 |
|
|
45 | 41 |
def set_aromaticity_mdl(self): |
46 | 41 |
oechem.OEClearAromaticFlags(self.mol) |
47 | 41 |
oechem.OEAssignAromaticFlags(self.mol, oechem.OEAroModel_MDL) |
48 | 41 |
oechem.OEAssignHybridization(self.mol) |
49 |
|
|
50 | 41 |
def get_atoms(self): |
51 | 41 |
return [Atom(a) for a in self.mol.GetAtoms()] |
52 |
|
|
53 | 41 |
def get_atom_by_index(self, idx): |
54 | 41 |
return Atom(self.mol.GetAtom(oechem.OEHasAtomIdx(idx))) |
55 |
|
|
56 | 41 |
def get_bonds(self): |
57 | 41 |
return [Bond(b) for b in self.mol.GetBonds()] |
58 |
|
|
59 | 41 |
def get_bond_by_index(self, idx): |
60 | 41 |
return Bond(self.mol.GetBond(oechem.OEHasBondIdx(idx))) |
61 |
|
|
62 | 41 |
def get_bond_by_atoms(self, atom1, atom2): |
63 | 41 |
if not atom1.is_connected_to(atom2): |
64 | 41 |
return None |
65 | 41 |
return Bond(self.mol.GetBond(atom1.atom, atom2.atom)) |
66 |
|
|
67 | 41 |
def smirks_search(self, smirks): |
68 | 41 |
cmol = oechem.OEMol(self.mol) |
69 |
|
|
70 | 41 |
matches = list() |
71 |
|
|
72 | 41 |
ss = oechem.OESubSearch() |
73 | 41 |
if not ss.Init(smirks): |
74 | 41 |
raise ValueError("Error parsing SMIRKS %s" % smirks) |
75 |
|
|
76 |
# set maximum matches in substructure search to infinite (0 in API)
|
|
77 | 41 |
ss.SetMaxMatches(0) |
78 | 41 |
for match in ss.Match(cmol, False): |
79 | 41 |
d = dict() |
80 | 41 |
for ma in match.GetAtoms(): |
81 | 41 |
smirks_idx = ma.pattern.GetMapIdx() |
82 |
# if the map index is 0 then it isn't a "tagged" atom in the SMIRKS
|
|
83 | 41 |
if smirks_idx !=0: |
84 | 41 |
d[smirks_idx] = self.get_atom_by_index(ma.target.GetIdx()) |
85 |
|
|
86 | 41 |
matches.append(d) |
87 |
|
|
88 | 41 |
return matches |
89 |
|
|
90 | 41 |
def get_smiles(self): |
91 | 41 |
smiles = oechem.OEMolToSmiles(self.mol) |
92 | 41 |
return smiles |
93 |
|
|
94 |
# =======================================
|
|
95 |
# Atom Class
|
|
96 |
# =======================================
|
|
97 |
|
|
98 | 41 |
class Atom(AtomAdapter): |
99 | 41 |
def __init__(self, atom): |
100 |
"""
|
|
101 |
ChemPer Atom created from an OEAtom
|
|
102 |
|
|
103 |
Parameters
|
|
104 |
----------
|
|
105 |
atom: OEAtomBase
|
|
106 |
Atom object from an OpenEye molecule
|
|
107 |
"""
|
|
108 | 41 |
if not isinstance(atom, oechem.OEAtomBase): |
109 | 41 |
raise TypeError("Expecting an OEAtomBase object instead of %s" % type(atom)) |
110 | 41 |
self.atom = atom |
111 | 41 |
self._idx = self.atom.GetIdx() |
112 |
|
|
113 | 41 |
def __str__(self): return "%i%s" % (self._idx, |
114 |
oechem.OEGetAtomicSymbol(self.atomic_number())) |
|
115 |
|
|
116 | 41 |
def atomic_number(self): return self.atom.GetAtomicNum() |
117 |
|
|
118 | 41 |
def degree(self): return self.atom.GetDegree() |
119 |
|
|
120 | 41 |
def connectivity(self): |
121 | 41 |
return len([b for b in self.atom.GetBonds()]) |
122 |
|
|
123 | 41 |
def valence(self): return self.atom.GetValence() |
124 |
|
|
125 | 41 |
def formal_charge(self): return self.atom.GetFormalCharge() |
126 |
|
|
127 | 41 |
def hydrogen_count(self): return self.atom.GetTotalHCount() |
128 |
|
|
129 | 41 |
def ring_connectivity(self): |
130 | 41 |
return len([b for b in self.atom.GetBonds(oechem.OEBondIsInRing())]) |
131 |
|
|
132 | 41 |
def min_ring_size(self): |
133 | 41 |
return oechem.OEAtomGetSmallestRingSize(self.atom) |
134 |
|
|
135 | 41 |
def is_aromatic(self): return self.atom.IsAromatic() |
136 |
|
|
137 | 41 |
def get_index(self): return self._idx |
138 |
|
|
139 | 41 |
def is_connected_to(self, atom2): |
140 | 41 |
if not isinstance(atom2.atom, oechem.OEAtomBase): |
141 |
return False |
|
142 | 41 |
return self.atom.IsConnected(atom2.atom) |
143 |
|
|
144 | 41 |
def get_neighbors(self): |
145 | 41 |
return [Atom(a) for a in self.atom.GetAtoms()] |
146 |
|
|
147 | 41 |
def get_bonds(self): |
148 | 41 |
return [Bond(b) for b in self.atom.GetBonds()] |
149 |
|
|
150 | 41 |
def get_molecule(self): |
151 | 41 |
mol = oechem.OEMol(self.atom.GetParent()) |
152 | 41 |
self.atom = mol.GetAtom(oechem.OEHasAtomIdx(self._idx)) |
153 | 41 |
return Mol(mol) |
154 |
|
|
155 |
# =======================================
|
|
156 |
# Bond Class
|
|
157 |
# =======================================
|
|
158 |
|
|
159 |
|
|
160 | 41 |
class Bond(BondAdapter): |
161 | 41 |
def __init__(self, bond): |
162 |
"""
|
|
163 |
ChemPer Bond created from an OEBond
|
|
164 |
|
|
165 |
Parameters
|
|
166 |
----------
|
|
167 |
bond: OEBondBase
|
|
168 |
Bond object from an OpenEye molecule
|
|
169 |
"""
|
|
170 | 41 |
if not isinstance(bond, oechem.OEBondBase): |
171 | 41 |
raise TypeError("Expecting an OEBondBase object instead of %s" % type(bond)) |
172 | 41 |
self.bond = bond |
173 |
|
|
174 |
# save index
|
|
175 | 41 |
self._idx = self.bond.GetIdx() |
176 |
|
|
177 |
# store order information
|
|
178 | 41 |
self._order = self.bond.GetOrder() |
179 | 41 |
if self.is_aromatic(): |
180 | 41 |
self._order = 1.5 |
181 |
|
|
182 | 41 |
orders = {1:'-', 2:'=', 3:'#', 1.5:':'} |
183 | 41 |
self._order_symbol = orders.get(self._order, '~') |
184 |
|
|
185 |
# save atoms in bond
|
|
186 | 41 |
self._beginning = Atom(self.bond.GetBgn()) |
187 | 41 |
self._end = Atom(self.bond.GetEnd()) |
188 |
|
|
189 | 41 |
def __str__(self): |
190 |
return "%i %s%s%s" % (self.get_index(), self._beginning, |
|
191 |
self._order_symbol, self._end) |
|
192 |
|
|
193 | 41 |
def get_order(self): return self._order |
194 |
|
|
195 | 41 |
def get_atoms(self): return [self._beginning, self._end] |
196 |
|
|
197 | 41 |
def is_ring(self): return self.bond.IsInRing() |
198 |
|
|
199 | 41 |
def is_aromatic(self): return self.bond.IsAromatic() |
200 |
|
|
201 | 41 |
def is_single(self): return self._order == 1 |
202 |
|
|
203 | 41 |
def is_double(self): return self._order == 2 |
204 |
|
|
205 | 41 |
def is_triple(self): return self._order == 3 |
206 |
|
|
207 | 41 |
def get_molecule(self): |
208 | 41 |
mol = oechem.OEMol(self.bond.GetParent()) |
209 | 41 |
self.bond = mol.GetBond(oechem.OEHasBondIdx(self._idx)) |
210 | 41 |
return Mol(mol) |
211 |
|
|
212 | 41 |
def get_index(self): return self._idx |
213 |
|
|
214 |
# =====================================================================
|
|
215 |
# functions for importing molecules from files
|
|
216 |
# =====================================================================
|
|
217 |
|
|
218 | 41 |
def mols_from_mol2(mol2_file): |
219 | 41 |
return mols_from_file(mol2_file) |
220 |
|
|
221 | 41 |
def mols_from_file(mol_file): |
222 |
"""
|
|
223 |
Parses a standard molecule file into ChemPer molecules using OpenEye toolkits
|
|
224 |
|
|
225 |
Parameters
|
|
226 |
----------
|
|
227 |
mol_file: str
|
|
228 |
relative or full path to molecule containing the molecule file
|
|
229 |
that is accessible from the current working directory
|
|
230 |
|
|
231 |
Returns
|
|
232 |
-------
|
|
233 |
mols: list[ChemPer Mol]
|
|
234 |
list of molecules in the mol2 file as ChemPer Mols
|
|
235 |
"""
|
|
236 | 41 |
import os |
237 | 41 |
if not os.path.exists(mol_file): |
238 | 41 |
from chemper.chemper_utils import get_data_path |
239 | 41 |
mol_path = get_data_path(os.path.join('molecules', mol_file)) |
240 |
|
|
241 | 41 |
if not os.path.exists(mol_path): |
242 |
raise IOError("File '%s' not found locally or in chemper/data/molecules." % mol_file) |
|
243 |
else: |
|
244 | 41 |
mol_file = mol_path |
245 |
|
|
246 | 41 |
molecules = list() |
247 |
|
|
248 |
# make Openeye input file stream
|
|
249 | 41 |
ifs = oechem.oemolistream(mol_file) |
250 |
|
|
251 | 41 |
oemol = oechem.OECreateOEGraphMol() |
252 | 41 |
while oechem.OEReadMolecule(ifs, oemol): |
253 |
# if an SD file, the molecule name may be in the SD tags
|
|
254 | 41 |
if oemol.GetTitle() == '': |
255 |
name = oechem.OEGetSDData(oemol, 'name').strip() |
|
256 |
oemol.SetTitle(name) |
|
257 |
# Append to list.
|
|
258 | 41 |
molecules.append(Mol(oechem.OEMol(oemol))) |
259 | 41 |
ifs.close() |
260 |
|
|
261 | 41 |
return molecules |
262 |
|
Read our documentation on viewing source code .