1
2
3
4
5
6
7
8
9 """
10 cdk - A Cinfony module for accessing the CDK from CPython and Jython
11
12 Global variables:
13 cdk - the underlying CDK Java library (org.openscience.cdk)
14 informats - a dictionary of supported input formats
15 outformats - a dictionary of supported output formats
16 descs - a list of supported descriptors
17 fps - a list of supported fingerprint types
18 forcefields - a list of supported forcefields
19 """
20 import sys
21 import os
22
23 if sys.platform[:4] == "java":
24 import org.openscience.cdk as cdk
25 import java
26 import javax
27
28
29 InvalidSmilesException = cdk.exception.InvalidSmilesException
30 CDKException = cdk.exception.CDKException
31 NullPointerException = java.lang.NullPointerException
32
33 else:
34 from jpype import *
35
36 if not isJVMStarted():
37 _jvm = os.environ['JPYPE_JVM']
38 if _jvm[0] == '"':
39 _jvm = _jvm[1:-1]
40 _cp = os.environ['CLASSPATH']
41 startJVM(_jvm, "-Djava.class.path=" + _cp)
42
43 cdk = JPackage("org").openscience.cdk
44 try:
45 _testmol = cdk.Molecule()
46 except TypeError:
47 raise ImportError, "The CDK Jar file cannot be found."
48
49
50 InvalidSmilesException = JavaException
51 CDKException = JavaException
52 NullPointerException = JavaException
55 de = cdk.qsar.DescriptorEngine(cdk.qsar.DescriptorEngine.MOLECULAR)
56 descdict = {}
57 for desc in de.getDescriptorInstances():
58 spec = desc.getSpecification()
59 descclass = de.getDictionaryClass(spec)
60 if "proteinDescriptor" not in descclass:
61
62 name = str(spec.getSpecificationReference().split("#")[-1])
63 descdict[name] = desc
64 return descdict
65
66 _descdict = _getdescdict()
67 descs = _descdict.keys()
68 """A list of supported descriptors"""
69 _fingerprinters = {"daylight":cdk.fingerprint.Fingerprinter
70 , "graph":cdk.fingerprint.GraphOnlyFingerprinter
71 , "maccs":cdk.fingerprint.MACCSFingerprinter
72 , "estate":cdk.fingerprint.EStateFingerprinter
73 , "extended":cdk.fingerprint.ExtendedFingerprinter
74 , "hybridization":cdk.fingerprint.HybridizationFingerprinter
75 , "klekota-roth":cdk.fingerprint.KlekotaRothFingerprinter
76 , "pubchem":cdk.fingerprint.PubchemFingerprinter
77 , "substructure":cdk.fingerprint.SubstructureFingerprinter
78 }
79 fps = _fingerprinters.keys()
80 """A list of supported fingerprint types"""
81 _formats = {'smi': "SMILES" , 'sdf': "MDL SDF",
82 'mol2': "MOL2", 'mol': "MDL MOL",
83 "inchi":"InChI",
84 "inchikey":"InChIKey"}
85 _informats = {'sdf': cdk.io.MDLV2000Reader, 'mol': cdk.io.MDLV2000Reader}
86 informats = dict([(_x, _formats[_x]) for _x in ['smi', 'sdf', 'mol', 'inchi']])
87 """A dictionary of supported input formats"""
88 _outformats = {'mol': cdk.io.MDLV2000Writer,
89 'mol2': cdk.io.Mol2Writer,
90 'sdf': cdk.io.SDFWriter}
91 outformats = dict([(_x, _formats[_x]) for _x in _outformats.keys() + ['smi', 'inchi', 'inchikey']])
92 """A dictionary of supported output formats"""
93 forcefields = list(cdk.modeling.builder3d.ModelBuilder3D.getInstance().getFfTypes())
94 """A list of supported forcefields"""
95
96 _isofact = cdk.config.IsotopeFactory.getInstance(cdk.ChemObject().getBuilder())
97
98 _bondtypes = {1: cdk.CDKConstants.BONDORDER_SINGLE,
99 2: cdk.CDKConstants.BONDORDER_DOUBLE,
100 3: cdk.CDKConstants.BONDORDER_TRIPLE}
101 _revbondtypes = dict([(_y,_x) for (_x,_y) in _bondtypes.iteritems()])
104 """Paper over some differences between JPype and Jython"""
105
106 if type(integer) != type(42):
107 integer = integer.intValue()
108 return integer
109
111 """Iterate over the molecules in a file.
112
113 Required parameters:
114 format - see the informats variable for a list of available
115 input formats
116 filename
117
118 You can access the first molecule in a file using the next() method
119 of the iterator:
120 mol = readfile("smi", "myfile.smi").next()
121
122 You can make a list of the molecules in a file using:
123 mols = list(readfile("smi", "myfile.smi"))
124
125 You can iterate over the molecules in a file as shown in the
126 following code snippet:
127 >>> atomtotal = 0
128 >>> for mol in readfile("sdf", "head.sdf"):
129 ... atomtotal += len(mol.atoms)
130 ...
131 >>> print atomtotal
132 43
133 """
134 format = format.lower()
135 if not os.path.isfile(filename):
136 raise IOError, "No such file: '%s'" % filename
137 builder = cdk.DefaultChemObjectBuilder.getInstance()
138 if format=="sdf":
139 return (Molecule(mol) for mol in cdk.io.iterator.IteratingMDLReader(
140 java.io.FileInputStream(java.io.File(filename)),
141 builder)
142 )
143 elif format=="smi":
144 return (Molecule(mol) for mol in cdk.io.iterator.IteratingSmilesReader(
145 java.io.FileInputStream(java.io.File(filename)),
146 builder
147 ))
148 elif format == 'inchi':
149 inputfile = open(filename, 'rb')
150 return (readstring('inchi', line.rstrip()) for line in inputfile)
151 elif format in informats:
152 reader = _informats[format](java.io.FileInputStream(java.io.File(filename)))
153 chemfile = reader.read(cdk.ChemFile())
154 manip = cdk.tools.manipulator.ChemFileManipulator
155 return iter(Molecule(manip.getAllAtomContainers(chemfile)[0]),)
156 else:
157 raise ValueError,"%s is not a recognised CDK format" % format
158
160 """Read in a molecule from a string.
161
162 Required parameters:
163 format - see the informats variable for a list of available
164 input formats
165 string
166
167 Example:
168 >>> input = "C1=CC=CS1"
169 >>> mymol = readstring("smi", input)
170 >>> len(mymol.atoms)
171 5
172 """
173 format = format.lower()
174 if format=="smi":
175 sp = cdk.smiles.SmilesParser(cdk.DefaultChemObjectBuilder.getInstance())
176 try:
177 ans = sp.parseSmiles(string)
178 except InvalidSmilesException, ex:
179 if sys.platform[:4] != "java":
180
181 ex = ex.message()
182 raise IOError, ex
183 return Molecule(ans)
184 elif format == 'inchi':
185 factory = cdk.inchi.InChIGeneratorFactory.getInstance()
186 intostruct = factory.getInChIToStructure(string,cdk.DefaultChemObjectBuilder.getInstance())
187 return Molecule(intostruct.getAtomContainer())
188 elif format in informats:
189 reader = _informats[format](java.io.StringReader(string))
190 chemfile = reader.read(cdk.ChemFile())
191 manip = cdk.tools.manipulator.ChemFileManipulator
192 return Molecule(manip.getAllAtomContainers(chemfile)[0])
193 else:
194 raise ValueError,"%s is not a recognised CDK format" % format
195
197 """Represent a file to which *output* is to be sent.
198
199 Required parameters:
200 format - see the outformats variable for a list of available
201 output formats
202 filename
203
204 Optional parameters:
205 overwite -- if the output file already exists, should it
206 be overwritten? (default is False)
207
208 Methods:
209 write(molecule)
210 close()
211 """
212 - def __init__(self, format, filename, overwrite=False):
213 self.format = format.lower()
214 self.filename = filename
215 if not overwrite and os.path.isfile(self.filename):
216 raise IOError, "%s already exists. Use 'overwrite=True' to overwrite it." % self.filename
217 if not format in outformats:
218 raise ValueError,"%s is not a recognised CDK format" % format
219 if self.format in ('smi','inchi', 'inchikey'):
220 self._outputfile = open(self.filename, "w")
221 else:
222 self._writer = java.io.FileWriter(java.io.File(self.filename))
223 self._molwriter = _outformats[self.format](self._writer)
224 self.total = 0
225
226 - def write(self, molecule):
227 """Write a molecule to the output file.
228
229 Required parameters:
230 molecule
231 """
232 if not self.filename:
233 raise IOError, "Outputfile instance is closed."
234 if self.format in ('smi','inchi', 'inchikey'):
235 self._outputfile.write("%s\n" % molecule.write(format))
236 else:
237 self._molwriter.write(molecule.Molecule)
238 self.total += 1
239
241 """Close the Outputfile to further writing."""
242 self.filename = None
243 if self.format in ('smi','inchi', 'inchikey'):
244 self._outputfile.close()
245 else:
246 self._molwriter.close()
247 self._writer.close()
248
250 """Represent a cdkjpype Molecule.
251
252 Required parameters:
253 Molecule -- a CDK Molecule or any type of cinfony Molecule
254
255 Attributes:
256 atoms, data, exactmass, formula, molwt, title
257
258 Methods:
259 addh(), calcfp(), calcdesc(), draw(), removeh(), write()
260
261 The underlying CDK Molecule can be accessed using the attribute:
262 Molecule
263 """
264 _cinfony = True
265
277
278 @property
280 @property
282 @property
287 @property
289 clone = Molecule(self.Molecule.clone())
290 clone.addh()
291 manip = cdk.tools.manipulator.MolecularFormulaManipulator
292 mf = manip.getMolecularFormula(clone.Molecule)
293 return manip.getMajorIsotopeMass(mf)
294 @property
296 clone = Molecule(self.Molecule.clone())
297 clone.addh()
298 atommanip = cdk.tools.manipulator.AtomContainerManipulator
299 return atommanip.getNaturalExactMass(clone.Molecule)
302 title = property(_gettitle, _settitle)
303 @property
305 gt = cdk.geometry.GeometryTools
306 if gt.has2DCoordinates(self.Molecule) or gt.has3DCoordinates(self.Molecule):
307 return (1, self.write("mol"))
308 else:
309 return (0, self.write("smi"))
310
312 """Iterate over the Atoms of the Molecule.
313
314 This allows constructions such as the following:
315 for atom in mymol:
316 print atom
317 """
318 return iter(self.atoms)
319
322
324 """Add hydrogens."""
325 atommanip = cdk.tools.manipulator.AtomContainerManipulator
326 atommanip.convertImplicitToExplicitHydrogens(self.Molecule)
327
329 """Remove hydrogens."""
330 atommanip = cdk.tools.manipulator.AtomContainerManipulator
331 self.Molecule = atommanip.removeHydrogens(self.Molecule)
332
333 - def write(self, format="smi", filename=None, overwrite=False):
334 """Write the molecule to a file or return a string.
335
336 Optional parameters:
337 format -- see the informats variable for a list of available
338 output formats (default is "smi")
339 filename -- default is None
340 overwite -- if the output file already exists, should it
341 be overwritten? (default is False)
342
343 If a filename is specified, the result is written to a file.
344 Otherwise, a string is returned containing the result.
345
346 To write multiple molecules to the same file you should use
347 the Outputfile class.
348 """
349 format = format.lower()
350 if format not in outformats:
351 raise ValueError,"%s is not a recognised CDK format" % format
352
353 if filename is not None and not overwrite and os.path.isfile(filename):
354 raise IOError, "%s already exists. Use 'overwrite=True' to overwrite it." % filename
355
356 if format == "smi":
357 sg = cdk.smiles.SmilesGenerator()
358
359 sg.setUseAromaticityFlag(True)
360 smiles = sg.createSMILES(self.Molecule)
361 if filename:
362 output = open(filename, "w")
363 print >> output, smiles
364 output.close()
365 return
366 else:
367 return smiles
368 elif format in ('inchi', 'inchikey'):
369 factory = cdk.inchi.InChIGeneratorFactory.getInstance()
370 gen = factory.getInChIGenerator(self.Molecule)
371 if format == 'inchi':
372 return gen.getInchi()
373 else:
374 return gen.getInchiKey()
375
376 else:
377 if filename is None:
378 writer = java.io.StringWriter()
379 else:
380 writer = java.io.FileWriter(java.io.File(filename))
381 molwriter = _outformats[format](writer)
382 molwriter.write(self.Molecule)
383 molwriter.close()
384 writer.close()
385 if filename == None:
386 return str(writer.toString())
387
388 - def calcfp(self, fp="daylight"):
389 """Calculate a molecular fingerprint.
390
391 Optional parameters:
392 fptype -- the fingerprint type (default is "daylight"). See the
393 fps variable for a list of of available fingerprint
394 types.
395 """
396 fp = fp.lower()
397 if fp in _fingerprinters:
398 fingerprinter = _fingerprinters[fp]()
399 else:
400 raise ValueError, "%s is not a recognised CDK Fingerprint type" % fp
401 return Fingerprint(fingerprinter.getFingerprint(self.Molecule))
402
404 """Calculate descriptor values.
405
406 Optional parameter:
407 descnames -- a list of names of descriptors
408
409 If descnames is not specified, all available descriptors are
410 calculated. See the descs variable for a list of available
411 descriptors.
412 """
413 if not descnames:
414 descnames = descs
415 ans = {}
416 for descname in descnames:
417 try:
418 desc = _descdict[descname]
419 except KeyError:
420 raise ValueError, "%s is not a recognised CDK descriptor type" % descname
421 try:
422 value = desc.calculate(self.Molecule).getValue()
423 if hasattr(value, "get"):
424 for i in range(value.length()):
425 ans[descname + ".%d" % i] = value.get(i)
426 elif hasattr(value, "doubleValue"):
427 ans[descname] = value.doubleValue()
428 else:
429 ans[descname] = _intvalue(value)
430 except CDKException, ex:
431
432 pass
433 except NullPointerException, ex:
434
435 pass
436 return ans
437
438 - def draw(self, show=True, filename=None, update=False,
439 usecoords=False):
440 """Create a 2D depiction of the molecule.
441
442 There is no option to display or write an image file of
443 the depiction. For this, you should use the CDK from
444 Jython or else the depiction engine of one of the other
445 toolkits.
446
447 When using jpype, arguments will be ignored: calling this function is
448 equivalent to calling the draw() method of one of the other Cinfony
449 modules with parameters:
450 show=False, filename=None, update=True, usecoords=False
451 """
452 if sys.platform[:4] != "java":
453 show=False
454 filename=None
455 update=True
456 usecoords=False
457
458 mol = Molecule(self.Molecule.clone())
459 cdk.aromaticity.CDKHueckelAromaticityDetector.detectAromaticity(mol.Molecule)
460
461 if not usecoords:
462
463 sdg = cdk.layout.StructureDiagramGenerator()
464 sdg.setMolecule(mol.Molecule)
465 sdg.generateCoordinates()
466 mol = Molecule(sdg.getMolecule())
467 if update:
468 for atom, newatom in zip(self.atoms, mol.atoms):
469 coords = newatom.Atom.getPoint2d()
470 atom.Atom.setPoint3d(javax.vecmath.Point3d(
471 coords.x, coords.y, 0.0))
472 else:
473 if self.atoms[0].Atom.getPoint2d() is None:
474
475 for atom, newatom in zip(self.atoms, mol.atoms):
476 coords = atom.Atom.getPoint3d()
477 newatom.Atom.setPoint2d(javax.vecmath.Point2d(
478 coords.x, coords.y))
479
480 if sys.platform[:4] != "java":
481
482 return
483 mol.removeh()
484 canvas = _Canvas(mol.Molecule)
485
486 if filename:
487 canvas.writetofile(filename)
488 if show:
489 canvas.popup()
490 else:
491 canvas.frame.dispose()
492
493 if sys.platform[:4] == "java":
494 - class _Canvas(javax.swing.JPanel):
495 """
496 Class used by Molecule.draw() in jython
497 """
499 self.mol = mol
500
501 self.frame = javax.swing.JFrame()
502 generators = []
503 generators.append(cdk.renderer.generators.BasicSceneGenerator())
504 generators.append(cdk.renderer.generators.BasicBondGenerator())
505 generators.append(cdk.renderer.generators.RingGenerator())
506 generators.append(cdk.renderer.generators.BasicAtomGenerator())
507 self.renderer = cdk.renderer.AtomContainerRenderer(generators,
508 cdk.renderer.font.AWTFontManager())
509
510 drawArea = java.awt.Rectangle(300, 300)
511 self.renderer.setup(mol, drawArea)
512 image = java.awt.image.BufferedImage(300, 300,
513 java.awt.image.BufferedImage.TYPE_INT_RGB)
514 screenSize = java.awt.Dimension(300, 300)
515 self.setPreferredSize(screenSize)
516 self.setBackground(java.awt.Color.WHITE)
517 self.frame.getContentPane().add(self)
518 self.frame.pack()
519 self.frame.setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE)
520
522 javax.swing.JPanel.paint(self, g)
523 self.renderer.paint(self.mol, cdk.renderer.visitor.AWTDrawVisitor(g),
524 java.awt.Rectangle(300, 300), True);
525
527 self.frame.visible = True
528
530 img = self.createImage(300, 300)
531 g2 = img.getGraphics()
532 g2.setColor(java.awt.Color.WHITE)
533 g2.fillRect(0, 0, 300, 300)
534 self.paint(g2)
535 javax.imageio.ImageIO.write(img, "png", java.io.File(filename))
536
538 """A Molecular Fingerprint.
539
540 Required parameters:
541 fingerprint -- a vector calculated by one of the fingerprint methods
542
543 Attributes:
544 fp -- the underlying fingerprint object
545 bits -- a list of bits set in the Fingerprint
546
547 Methods:
548 The "|" operator can be used to calculate the Tanimoto coeff. For example,
549 given two Fingerprints 'a', and 'b', the Tanimoto coefficient is given by:
550 tanimoto = a | b
551 """
553 self.fp = fingerprint
555 return cdk.similarity.Tanimoto.calculate(self.fp, other.fp)
557 if attr == "bits":
558
559 bits = []
560 idx = self.fp.nextSetBit(0)
561 while idx >= 0:
562 bits.append(idx)
563 idx = self.fp.nextSetBit(idx + 1)
564 return bits
565 else:
566 raise AttributeError, "Fingerprint has no attribute %s" % attr
568 return self.fp.toString()
569
571 """Represent a cdkjpype Atom.
572
573 Required parameters:
574 Atom -- a CDK Atom
575
576 Attributes:
577 atomicnum, coords, formalcharge
578
579 The original CDK Atom can be accessed using the attribute:
580 Atom
581 """
582
585
586 @property
588 _isofact.configure(self.Atom)
589 return _intvalue(self.Atom.getAtomicNumber())
590 @property
599 @property
603
605 c = self.coords
606 return "Atom: %d (%.2f %.2f %.2f)" % (self.atomicnum, c[0], c[1], c[2])
607
609 """A Smarts Pattern Matcher
610
611 Required parameters:
612 smartspattern
613
614 Methods:
615 findall()
616
617 Example:
618 >>> mol = readstring("smi","CCN(CC)CC") # triethylamine
619 >>> smarts = Smarts("[#6][#6]") # Matches an ethyl group
620 >>> print smarts.findall(mol)
621 [(1, 2), (4, 5), (6, 7)]
622 """
624 """Initialise with a SMARTS pattern."""
625 self.smarts = cdk.smiles.smarts.SMARTSQueryTool(smartspattern)
626
628 """Find all matches of the SMARTS pattern to a particular molecule.
629
630 Required parameters:
631 molecule
632 """
633 match = self.smarts.matches(molecule.Molecule)
634 return list(self.smarts.getUniqueMatchingAtoms())
635
637 """Store molecule data in a dictionary-type object
638
639 Required parameters:
640 Molecule -- a CDK Molecule
641
642 Methods and accessor methods are like those of a dictionary except
643 that the data is retrieved on-the-fly from the underlying Molecule.
644
645 Example:
646 >>> mol = readfile("sdf", 'head.sdf').next()
647 >>> data = mol.data
648 >>> print data
649 {'Comment': 'CORINA 2.61 0041 25.10.2001', 'NSC': '1'}
650 >>> print len(data), data.keys(), data.has_key("NSC")
651 2 ['Comment', 'NSC'] True
652 >>> print data['Comment']
653 CORINA 2.61 0041 25.10.2001
654 >>> data['Comment'] = 'This is a new comment'
655 >>> for k,v in data.iteritems():
656 ... print k, "-->", v
657 Comment --> This is a new comment
658 NSC --> 1
659 >>> del data['NSC']
660 >>> print len(data), data.keys(), data.has_key("NSC")
661 1 ['Comment'] False
662 """
666 return self._mol.getProperties()
668 if not key in self:
669 raise KeyError, "'%s'" % key
671 return list(self._data().keySet())
673 return list(self._data().values())
675 return [(k, self[k]) for k in self._data().keySet()]
677 return iter(self.keys())
679 return iter(self.items())
681 return len(self._data())
683 return key in self._data()
685 self._testforkey(key)
686 self._mol.removeProperty(key)
688 for key in self:
689 del self[key]
692 - def update(self, dictionary):
693 for k, v in dictionary.iteritems():
694 self[k] = v
696 self._testforkey(key)
697 return self._mol.getProperty(key)
699 self._mol.setProperty(key, str(value))
702
703 if __name__=="__main__":
704 mol = readstring("smi", "CC(=O)Cl")
705 mol.title = "Noel"
706 mol.draw()
707
708 for mol in readfile("sdf", "head.sdf"):
709 pass
710