1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 import warnings
17 import Numeric, string, os
18
19 from NetworkEditor.items import NetworkNode
20 from Vision import UserLibBuild
21
22 from DejaVu.VisionInterface.GeometryNodes import GeometryNode
23
24 from MolKit.molecule import Atom, AtomSet, Molecule, MoleculeSet
25 from MolKit.protein import Residue, ResidueSet, Chain, ChainSet
26 from MolKit.moleculeWriter import MoleculeWriter
27 from MolKit.pdbWriter import PdbWriter, PdbqWriter, PdbqsWriter, PdbqtWriter
28 from MolKit.pqrWriter import PqrWriter
29 from MolKit.pdbParser import PdbParser
30 from MolKit.chargeCalculator import GasteigerChargeCalculator, KollmanChargeCalculator
31 from MolKit.hydrogenBuilder import HydrogenBuilder, PolarHydrogenBuilder
32
33 try:
34 from mslib import MSMS, _msms
35 msmsFound=1
36 except ImportError:
37 import traceback
38 traceback.print_exc()
39 msmsFound=0
40
41
43 try:
44 from MolKit.VisionInterface.MolKitNodes import molkitlib
45 net.editor.addLibraryInstance(
46 molkitlib, 'MolKit.VisionInterface.MolKitNodes', 'molkitlib')
47 except:
48 import traceback
49 traceback.print_exc()
50 warnings.warn(
51 'Warning! Could not import molitlib from MolKit.VisionInterface')
52
53
55 try:
56 from DejaVu.VisionInterface.DejaVuNodes import vizlib
57 net.editor.addLibraryInstance(
58 vizlib, 'DejaVu.VisionInterface.DejaVuNodes', 'vizlib')
59 except:
60 import traceback
61 traceback.print_exc()
62 warnings.warn(
63 'Warning! Could not import vizlib from DejaVu/VisionInterface')
64
65
67 try:
68 from symserv.VisionInterface.SymservNodes import symlib
69 net.editor.addLibraryInstance(
70 symlib, 'symserv.VisionInterface.SymservNodes', 'symlib')
71 except:
72 import traceback
73 traceback.print_exc()
74 warnings.warn(
75 'Warning! Could not import symlib from symserv/VisionInterface')
76
77
78
80 """inputs PDB file, parses SYMTRY records and returns a list of (4x4)
81 matrices."""
82
83 - def __init__(self, name='PDB_SYMTRY', **kw):
84 kw['name']=name
85 apply( NetworkNode.__init__, (self,), kw)
86
87 code = """def doit(self, mol):
88 if mol:
89 matrices = []
90 all = lines = mol.data[0].parser.allLines
91 rem290 = filter(lambda x: x[:18]=="REMARK 290 SMTRY", all)
92 for i in range(0, len(rem290), 3):
93 lines = []
94 mat = Numeric.identity(4).astype('f')
95 for j in range(3):
96 line1 = rem290[i+j]
97 r1 = float(line1[24:33])
98 r2 = float(line1[34:43])
99 r3 = float(line1[44:53])
100 t = float(line1[54:68])
101 mat[j] = (r1, r2, r3, t)
102 mat[3] = (0., 0., 0., 1.)
103 #matrices.append( Numeric.array( Numeric.transpose( mat) ) )
104 matrices.append( mat )
105
106 self.outputData(matrices=matrices)\n"""
107
108 if code: self.setFunction(code)
109
110 ip = self.inputPortsDescr
111 ip.append(datatype='MoleculeSet', name='molecule')
112
113 op = self.outputPortsDescr
114 op.append(datatype='instancemat(0)', name='matrices')
115
116
120
121
122
124
125 - def __init__(self, name='centerAndRad', **kw):
126 kw['name'] = name
127 apply( NetworkNode.__init__, (self,), kw)
128
129 ip = self.inputPortsDescr
130 ip.append(datatype='MoleculeSet', name='molecules')
131
132 op = self.outputPortsDescr
133 op.append(datatype='None', name='center')
134 op.append(datatype='float', name='radius')
135
136 code = """def doit(self, molecules):
137 import Numeric
138 from math import sqrt
139 centers = []
140 radii = []
141 for mol in molecules:
142 c = Numeric.array(mol.chains.residues.atoms.coords)
143 g = Numeric.add.reduce(c) / len(c)
144 centers.append( g )
145 d = c-[g]
146 radii.append( sqrt(max(Numeric.add.reduce(d*d, 1))) )
147
148 if len(centers):
149 self.outputData(center=centers, radius=radii)
150 """
151 if code: self.setFunction(code)
152
153
154
156 """based on the MolKit.Read fucntion. Reads any file format recognized
157 by MolKit. This currently includes PDB, PDBQ, PDBQS, MOL2 and PQR.
158 Parse-able keywords can be specified in the param.panel of this node.
159 Input: filename (string)
160 Output: MoleculeSet"""
161
162 - def __init__(self, name='Read Molecule', **kw):
163 kw['name'] = name
164 apply( NetworkNode.__init__, (self,), kw )
165
166 fileTypes=[('pdb', '*.pdb'),('pdbq', '*.pdbq'),('pdbqs', '*.pdbqs'),
167 ('pdbqt', '*.pdbqt'),('pqr', '*.pqr'),('mol2', '*.mol2'),('all', '*')]
168
169 self.widgetDescr['filename'] = {
170 'class':'NEEntryWithFileBrowser', 'master':'node',
171 'filetypes':fileTypes, 'title':'read molecule', 'width':10,
172 'labelCfg':{'text':'file:'},
173 }
174
175 ip = self.inputPortsDescr
176 ip.append(datatype='string', name='filename')
177
178 op = self.outputPortsDescr
179 op.append(datatype='MoleculeSet', name='MolSets')
180
181 code = """def doit(self, filename):
182 if filename and len(filename):
183 from MolKit import Read
184 mols = Read(filename)
185 if mols:
186 self.outputData(MolSets=mols)\n"""
187
188 self.setFunction(code)
189
191
192 pass
193
194
196 """Save as PDB."""
197
198 - def __init__(self, name='Write PDB', **kw):
199 kw['name'] = name
200 apply( NetworkNode.__init__, (self,), kw )
201
202 fileTypes=[('pdb', '*.pdb'), ('all', '*')]
203
204 self.widgetDescr['filename'] = {
205 'class':'NEEntryWithFileSaver', 'master':'node',
206 'filetypes':fileTypes, 'title':'save PDB', 'width':10,
207 'labelCfg':{'text':'file:'},
208 }
209
210 self.inputPortsDescr.append(datatype='list', name='nodes')
211 self.inputPortsDescr.append(datatype='string', name='filename')
212
213 code = """def doit(self, nodes, filename):
214 if filename and len(filename):
215 mol = nodes.top.uniq()
216 assert len(mol)==1
217
218 recType = 'all' # this could be exposed to the user?
219 sort = 1
220
221 if sort:
222 nodes.sort()
223
224 mol[0].write(filename, PdbWriter(), nodes, recType )\n"""
225
226 self.setFunction(code)
227
228
230 """Write any file format recognized
231 by MolKit. This currently includes PDB, PDBQ, PDBQS, MOL2 and PQR.
232 Parse-able keywords can be specified in the param.panel of this node.
233 Input: MoleculeSet
234 Input: filename (string)"""
235
236 - def __init__(self, name='Write Molecule', **kw):
237 self.writers = {}
238 self.writers['pdb'] = PdbWriter
239 self.writers['pqr'] = PqrWriter
240 self.writers['pdbq'] = PdbqWriter
241 self.writers['pdbqs'] = PdbqsWriter
242 self.writers['pdbqt'] = PdbqtWriter
243 self.required_attrs = {}
244 self.required_attrs['pdb'] = []
245 self.required_attrs['pqr'] = ['charge', 'radius']
246 self.required_attrs['pdbq'] = ['charge']
247 self.required_attrs['pdbqs'] = ['charge', 'AtSolPar', 'AtVol']
248 self.required_attrs['pdbqt'] = ['charge', 'autodock_element']
249
250 kw['name'] = name
251 apply( NetworkNode.__init__, (self,), kw )
252
253 fileTypes=[('pdb', '*.pdb'),('pdbq', '*.pdbq'),('pdbqs', '*.pdbqs'),
254 ('pdbqt', '*.pdbqt'),('pqr', '*.pqr')]
255
256 self.widgetDescr['filename'] = {
257 'class':'NEEntryWithFileBrowser', 'master':'node',
258 'filetypes':fileTypes, 'title':'write molecule', 'width':10,
259 'labelCfg':{'text':'output file:'},
260 }
261
262 ip = self.inputPortsDescr
263 ip.append(datatype='MoleculeSet', name='mols')
264 ip.append(datatype='string', name='filename')
265 ip.append(datatype='int', required=False, name='sort')
266 ip.append(datatype='list', required=False, name='records')
267
268 op = self.outputPortsDescr
269 op.append(datatype='string', name='filename')
270
271 code = """def doit(self, mols, filename, sort=False, records=['ATOM','HETATM']):
272 if mols and filename and len(filename):
273 ext = filename.split('.')[-1]
274 if not self.writers.has_key(ext):
275 print 'unrecognized outputfile type', ext
276 return
277 writer = self.writers[ext]()
278 #check for required attributes
279 #pqr requires charges, radius
280 #pdbq requires charges
281 #pdbqt requires charges and types
282 #pdbqs requires charges and solvation parameters
283 mol = mols[0]
284 ats = mol.allAtoms
285 for attr in self.required_attrs[ext]:
286 try:
287 getattr(ats, attr)
288 except:
289 print "all atoms do not have required attribute:", attr
290 return 'ERROR'
291 #need support for specifying records (?)
292 #NB: pqrWriter doesnot have same signature: no records parameter
293 try: #should work if ext!='pqr'
294 writer.write(filename, mol.allAtoms, sort=sort, records=records)
295 except:
296 writer.write(filename, mol.allAtoms, sort=sort)
297 self.outputData(filename=filename)\n"""
298
299 self.setFunction(code)
300
302
303 pass
304
305
307
308 - def __init__(self, name='Assign Radii', **kw):
309 kw['name'] = name
310 apply( NetworkNode.__init__, (self,), kw )
311
312 self.widgetDescr['united'] = {
313 'class':'NECheckButton', 'master':'node',
314 'labelCfg':{'text':'united radii'},
315 }
316
317 ip = self.inputPortsDescr
318 ip.append(datatype='MoleculeSet', name='molecules')
319 ip.append(datatype='int', name='united')
320
321 op = self.outputPortsDescr
322 op.append(datatype='MoleculeSet', name='molecules')
323 op.append(datatype='list', name='radii')
324
325 code = """def doit(self, molecules, united ):
326 rad = []
327 for mol in molecules:
328 rad.extend(mol.defaultRadii(united=united))
329 if rad:
330 self.outputData(molecules=molecules, radii=rad)
331 """
332
333 self.setFunction(code)
334
335
337
338 - def __init__(self, name='Calculate Gasteiger Charges', **kw):
339 kw['name'] = name
340 apply( NetworkNode.__init__, (self,), kw )
341
342 ip = self.inputPortsDescr
343 ip.append(datatype='MoleculeSet', name='mols')
344
345 op = self.outputPortsDescr
346 op.append(datatype='MoleculeSet', name='mols')
347 op.append(datatype='float', name='charge_total')
348
349 code = """def doit(self, mols ):
350 calculator = GasteigerChargeCalculator()
351 for mol in mols:
352 calculator.addCharges(mol.allAtoms)
353 import Numeric
354 charge_total = Numeric.add.reduce(mols.allAtoms.charge)
355 self.outputData(mols=mols, charge_total=charge_total)
356 """
357
358 self.setFunction(code)
359
360
362
363 - def __init__(self, name='Add Kollman Charges', **kw):
364 kw['name'] = name
365 apply( NetworkNode.__init__, (self,), kw )
366
367 ip = self.inputPortsDescr
368 ip.append(datatype='MoleculeSet', name='mols')
369
370 op = self.outputPortsDescr
371 op.append(datatype='MoleculeSet', name='mols')
372 op.append(datatype='float', name='charge_total')
373
374 code = """def doit(self, mols ):
375 calculator = KollmanChargeCalculator()
376 for mol in mols:
377 calculator.addCharges(mol.allAtoms)
378 import Numeric
379 charge_total = Numeric.add.reduce(mols.allAtoms.charge)
380 self.outputData(mols=mols, charge_total=charge_total)
381 """
382
383 self.setFunction(code)
384
385
387
388 - def __init__(self, name='Add Hydrogens', **kw):
389 kw['name'] = name
390 apply( NetworkNode.__init__, (self,), kw )
391
392 ip = self.inputPortsDescr
393 ip.append(datatype='MoleculeSet', name='molecules')
394
395 op = self.outputPortsDescr
396 op.append(datatype='MoleculeSet', name='molecules', balloon='molecules with added hydrogens')
397 op.append(datatype='int', name='new_h_ct', balloon='number of hydrogens added to molecules')
398
399 code = """def doit(self, molecules):
400 h_builder = HydrogenBuilder()
401 for mol in molecules:
402 new_h_ct = h_builder.addHydrogens(mol)
403 self.outputData(molecules=molecules, new_h_ct=new_h_ct)
404 """
405
406 self.setFunction(code)
407
408
410
411 - def __init__(self, name='Add Polar Hydrogens', **kw):
412 kw['name'] = name
413 apply( NetworkNode.__init__, (self,), kw )
414
415 ip = self.inputPortsDescr
416 ip.append(datatype='MoleculeSet', name='molecules')
417
418 op = self.outputPortsDescr
419 op.append(datatype='MoleculeSet', name='molecules')
420 op.append(datatype='int', name='new_h_ct')
421
422 code = """def doit(self, molecules):
423 h_builder = PolarHydrogenBuilder()
424 for mol in molecules:
425 new_h_ct = h_builder.addHydrogens(mol)
426 self.outputData(molecules=molecules, new_h_ct=new_h_ct )
427 """
428
429 self.setFunction(code)
430
431
433
434 - def __init__(self, name='ParseCryst1', **kw):
435 kw['name'] = name
436 apply( NetworkNode.__init__, (self,), kw )
437
438 ip = self.inputPortsDescr
439 ip.append(datatype='MoleculeSet', name='molecules')
440
441 op = self.outputPortsDescr
442 op.append(datatype='MoleculeSet', name='molecules')
443
444 code = """def doit(self, molecules):
445 for mol in molecules:
446 p = mol.parser
447 if not isinstance(p, PdbParser):
448 print 'WARNING molecule %s was not read from a PDB file'%mol.name
449 continue
450 p.parse_PDB_CRYST1( p.getRecords(p.allLines, 'CRYST1') )
451
452 self.outputData(molecules=molecules)
453 """
454
455 self.setFunction(code)
456
457
459 """This node selects subsets of nodes (i.e. Atoms, Residues, Chains, ...)
460 from a list of incomming molecules.
461 First all nodes of the currently selected level are found, then a
462 selection operation is performed on these nodes.
463 Selection can be specified using regular expressions or lambda functions.
464
465 Example:
466 # set the level to Residue and NOTE that the port name changes :)
467 type ^G* to select all residues with names starting with a 'G'
468 type lambda x: x._uniqIndex < 10 to select the 10 first residues
469 """
471 if len(self.outputPorts)==0:
472 return
473 newlevel = self.levels[level][0]
474 if newlevel==self.level:
475 return
476 self.level = newlevel
477 p = self.outputPorts[0]
478 p.setDataType(self.levels[level][1].__name__)
479
480
481 - def __init__(self, name='Select Nodes', **kw):
482 kw['name'] = name
483 apply( NetworkNode.__init__, (self,), kw )
484
485
486 self.levels = {'Atom': (Atom, AtomSet),
487 'Residue': (Residue, ResidueSet),
488 'Chain': (Chain, ChainSet),
489 'Molecule': (Molecule, MoleculeSet) }
490
491 self.level = Atom
492
493 self.widgetDescr['selectionString'] = {
494 'class':'NEEntry', 'master':'node', 'width':14,
495 'labelGridCfg':{'sticky':'w'},
496 'widgetGridCfg':{'sticky':'w'},
497 'labelCfg':{'text':'select:'},
498 }
499
500 self.widgetDescr['nodeType'] = {
501 'class':'NEComboBox', 'master':'node',
502 'choices':self.levels.keys(),
503 'fixedChoices':True,
504 'initialValue':'Atom',
505 'entryfield_entry_width':8,
506 'selectioncommand':self.setLevel,
507 'labelGridCfg':{'sticky':'w'},
508 'widgetGridCfg':{'sticky':'w'},
509 'labelCfg':{'text':'level:'},
510 }
511
512 ip = self.inputPortsDescr
513 ip.append(datatype='TreeNodeSet', name='nodes')
514 ip.append(datatype='string', name='nodeType')
515 ip.append(datatype='string', required=False, name='selectionString')
516
517 op = self.outputPortsDescr
518 op.append(datatype='AtomSet', name='nodes')
519
520 code = """def doit(self, molecules, nodeType, \
521 selectionString=None):
522 elemType, setType = self.levels[nodeType]
523 result = setType([])
524 for mol in molecules:
525 selnodes = setType(mol.findType(elemType))
526 if selectionString:
527 selnodes = selnodes.get(selectionString)
528 result = result + selnodes
529 if len(result):
530 result.sort()
531 self.outputData(nodes=result)\n"""
532
533 self.setFunction(code)
534
536
538 kw['name'] = name
539 apply( NetworkNode.__init__, (self,), kw )
540
541 ip = self.inputPortsDescr
542 ip.append(datatype='AtomSet', name='atomSet1')
543 ip.append(datatype='AtomSet', name='atomSet2')
544
545 op = self.outputPortsDescr
546 op.append(datatype='float', name='rmsd')
547
548 from mglutil.math.rmsd import RMSDCalculator
549 self.RMSD = RMSDCalculator()
550
551 code = """def doit(self, atomSet1, atomSet2):
552 if atomSet1 is None or atomSet2 is None:
553 return
554 if len(atomSet1)!=len(atomSet2):
555 return
556
557 # sort the two atomSet
558 needSorting = False
559 for i in range(len(atomSet1)):
560 if atomSet1[i].name != atomSet2[i].name:
561 needSorting = True
562 break
563
564
565 if needSorting:
566 tmp = AtomSet()
567 names = atomSet2.name
568 for atom in atomSet1:
569 try:
570 idx = names.index(atom.name)
571 tmp.append(atomSet2[idx])
572 except:
573 print atom.name, 'not found in', atomSet2
574 mob = tmp.coords
575 else:
576 mob = atomSet2.coords
577
578 ref = atomSet1.coords
579 self.RMSD.setRefCoords(ref)
580 rmsd = self.RMSD.computeRMSD(mob)
581
582 self.outputData(rmsd=rmsd)
583
584 """
585
586 self.setFunction(code)
587
588
592
593
595
597 kw['name'] = name
598 apply( GeometryNode.__init__, (self,'CPK'), kw )
599
600
601
602
603
604
605 self.widgetDescr['quality'] = {
606 'class':'NEThumbWheel', 'master':'node',
607 'width':60, 'height':18, 'oneTurn':10,
608 'lockType':1, 'min':3, 'increment':1, 'type':'int', 'wheelPad':2,
609 'initialValue':8,
610 'labelCfg':{'text':'quality:'},
611 }
612
613 ip = self.inputPortsDescr
614 ip.append(datatype='AtomSet', name='atoms')
615
616 ip.append(datatype='float(0)', required=False, name='radii')
617
618
619 ip.append(datatype='colorfloat3or4(0)', required=False, name='colors')
620 ip.append(datatype='int', required=False, name='quality')
621
622
623
624
625
626
627 self.rearrangePorts()
628
629 code = """def doit(self, atoms, radii=None, colors=None, quality=None,
630 name=None, geoms=None, instanceMatrices=None, geomOptions=None, parent=None):
631 GeometryNode.doit(self, name, geoms, instanceMatrices, geomOptions, parent)
632 if atoms is None:
633 return
634 try:
635 len(radii)
636 islist = True
637 except TypeError:
638 islist = False
639 if islist:
640 assert len(radii)==len(atoms.coords)
641 if colors is None:
642 colors = ( (0.7, 0.7, 0.7), )
643
644
645 if self.selectedGeomIndex is not None:
646 g = self.geom()
647 if colors is not None:
648 inheritMaterial = False
649 else:
650 inheritMaterial = None
651 g.Set(vertices=atoms.coords, radii=radii, materials=colors,
652 quality=quality, inheritMaterial=inheritMaterial)
653 self.outputData(CPK=g, allGeometries=self.geoms)
654 else:
655 self.outputData(CPK=None, allGeometries=self.geoms)
656
657 """
658 self.setFunction(code)
659
660
662 from DejaVu.Spheres import Spheres
663 self.geoms.append(Spheres(name))
664 return 1
665
666
670
671
673
674 - def __init__(self, name='Sticks', **kw):
675 kw['name'] = name
676 apply( NetworkNode.__init__, (self,), kw )
677
678 self.indices = None
679
680
681
682 from DejaVu.Cylinders import Cylinders
683 self.sticks = Cylinders('sticks', inheritMaterial=0)
684
685 self.widgetDescr['cradii'] = {
686 'class':'NEThumbWheel', 'master':'node',
687 'width':60, 'height':18, 'wheelPad':2, 'oneTurn':1,
688 'lockType':1, 'min':0, 'increment':.1, 'type':'float',
689 'initialValue':0.25,
690 'labelCfg':{'text':'radius:'},
691 }
692
693 ip = self.inputPortsDescr
694 ip.append(datatype='AtomSet', name='atoms')
695 ip.append(datatype='colorsRGB', required=False, name='colors')
696 ip.append(datatype='instancemat(0)', required=False,
697 name='instance matrices')
698 ip.append(datatype='float', required=False, name='cradii')
699
700 op = self.outputPortsDescr
701 op.append(datatype='geom', name='sticks')
702
703 code = """def doit(self, atoms, colors=None, instanceMatrices=None, \
704 cradii=None):
705 if atoms is None:
706 return
707 if colors is None:
708 colors = ( (0.7, 0.7, 0.7), )
709
710 if instanceMatrices:
711 mat = Numeric.array(instanceMatrices)
712 else:
713 mat = [Numeric.identity(4).astype('f')]
714
715 if cradii is None:
716 cradii = 0.25
717 bonds, atnobnd = atoms.bonds
718 self.indices = map(lambda x: (x.atom1._bndIndex_,
719 x.atom2._bndIndex_), bonds)
720 self.sticks.Set(vertices=atoms.coords, faces=self.indices,
721 radii=cradii, materials=colors)
722 self.sticks.Set(instanceMatrices=mat)
723
724 self.outputData(sticks=self.sticks)\n"""
725
726 self.setFunction(code)
727
728
732
733
735 """Extract specific vertices from the SES surface
736
737 Input ports:
738 MSMS: an MSMSobject instance as output by the MSMS calculation node
739 nodes: a TreeNodeSet describing parts of a molecule. This will be
740 expanded to an AtomSet triangles for which at least 'selnum'
741 vertices belong to atoms in this set will be extracted.
742 If ommited, the whole surface is extracted.
743 selnum: number of vertices per triangle that need to belong to atoms
744 in nodes for a triangle to be output.
745 component: MSMS component (i.e. a closed surface) to be extracted.
746 Component 0 is always the external component.
747 ftype: can be any of 'contact', 'reentrant' or 'toric' to select only
748 vertices in this type of faces
749 vtype: can be any of 'inside', 'edge' or 'vertices' to select only
750 vertices inside contact face, on edges or vertices respectively
751
752 OutputPort:
753 vertices: array of X,Y,Z coordinates
754 vnormals: array of nx,ny,nz vertices normal vectors
755 """
756 - def __init__(self, name='gSESVertices', **kw):
757 kw['name'] = name
758 apply( NetworkNode.__init__, (self,), kw )
759
760 self.widgetDescr['selnum'] = {
761 'class':'NEDial', 'size':50,
762 'oneTurn':3, 'min':1, 'max':3, 'lockBMax': 1,
763 'lockMin': 1, 'type':'int',
764 'initialValue':3,
765 'labelGridCfg':{'sticky':'w'},
766 'labelCfg':{'text':'nbVert'},
767 }
768
769 self.widgetDescr['component'] = {
770 'class':'NEDial', 'size':50,
771 'oneTurn':5, 'min':0, 'lockMin':1, 'lockMax':1, 'type':'int',
772 'initialValue':0,
773 'labelGridCfg':{'sticky':'w'},
774 'labelCfg':{'text':'component'},
775 }
776
777 self.widgetDescr['ftype'] = { 'class': 'NEComboBox',
778 'initialValue': 'contact', 'fixedChoices': 1,
779 'entryfield_entry_width':12,
780 'choices': ['contact', 'reentrant', 'toric'],
781 'labelGridCfg':{}, 'master': 'node', 'widgetGridCfg': {},
782 'labelCfg': {'text': 'vtype:'},
783 }
784
785 self.widgetDescr['vtype'] = { 'class': 'NEComboBox',
786 'initialValue': 'inside', 'fixedChoices': 1,
787 'entryfield_entry_width':12,
788 'choices': ['inside', 'edges', 'vertices'],
789 'labelGridCfg':{}, 'master': 'node', 'widgetGridCfg': {},
790 'labelCfg': {'text': 'vtype:'},
791 }
792
793 ip = self.inputPortsDescr
794 ip.append(datatype='MSMSobject', name='MSMS')
795 ip.append(datatype='TreeNodeSet',
796 balloon='Atoms for which triangles are extracted',
797 required=False, name='nodes')
798 ip.append(datatype='int', required=False, name='selnum')
799 ip.append(datatype='int', required=False, name='component')
800 ip.append(datatype='string', required=False, name='ftype')
801 ip.append(datatype='string', required=False, name='vtype')
802
803 op = self.outputPortsDescr
804 op.append(datatype='coordinates3D', name='SAScenters')
805 op.append(datatype='normals3D', name='normals')
806
807
808 code = """def doit(self, msms, nodes=None, selnum=3, component=0, \
809 ftype='contact', vtype='inside'):
810 vf,vi,f = msms.getTriangles()
811 v = {}
812 n = {}
813 if ftype=='contact': faceTest=1
814 elif ftype=='reentrant': faceTest=2
815 elif ftype=='toric': faceTest=3
816 else: raise(ValueError, 'bad face type')
817
818 for fa in f:
819 if fa[3]==faceTest:
820 for i in range(3):
821 v1 = fa[i]
822 vt = vi[v1][0]
823 # pick vertices based on vtype
824 if (vtype=='inside' and vt>0) or \
825 (vtype=='edges' and vt<0) or \
826 (vtype=='vertices' and vt<0):
827 v[v1] = vf[v1][0:3]
828 n[v1] = vf[v1][3:6]
829 self.outputData(SAScenters=v.values())
830 self.outputData(normals=n.values())
831 """
832
833 self.setFunction(code)
834
835
839
840 from Pmv.msmsParser import MSMSParser
842 """based on the MSMSParser object. Reads an MSMS surface
843 Input:
844 filename (string), filename.vert and filename.face will be read
845 Output:
846 vertices: array of vertices
847 normals: array of normals
848 vprop: array of vertex properties (integer values)
849 tri: array of faces
850 triprop: array of face properties (integers)"""
851
852 - def __init__(self, name='Read MSMS', **kw):
853 kw['name'] = name
854 apply( NetworkNode.__init__, (self,), kw )
855
856 fileTypes=[('msms', '*.vert')]
857
858 self.widgetDescr['filename'] = {
859 'class':'NEEntryWithFileBrowser', 'master':'node',
860 'filetypes':fileTypes, 'title':'read molecule', 'width':10,
861 'labelCfg':{'text':'file:'},
862 }
863
864 ip = self.inputPortsDescr
865 ip.append(datatype='string', name='filename')
866
867 op = self.outputPortsDescr
868 op.append(datatype='coordinates3D', name='vertices')
869 op.append(datatype='faceIndices', name='tri')
870 op.append(datatype='normals3D', name='normals')
871 op.append(datatype='int(0,3)', name='vprop')
872 op.append(datatype='int(0,2)', name='triprop')
873
874 code = """def doit(self, filename):
875 if filename and len(filename):
876 msmsParser = MSMSParser()
877 msmsParser.parse(filename, filename[:-4]+'face')
878 self.outputData(
879 vertices=msmsParser.vertices,
880 tri=msmsParser.faces,
881 normals=msmsParser.normals,
882 vprop=msmsParser.vertProp ,
883 triprop=msmsParser.facesProp)"""
884
885 self.setFunction(code)
886
887
888 try:
889 bhtreelibFound = True
890 from bhtree import bhtreelib
891
893 """outputs a list of atoms contributing to the MSMS serface
894
895 Input ports:
896 MSMS: an MSMSobject instance as output by the MSMS calculation node
897 nodes: a TreeNodeSet describing parts of a molecule. This will be
898 expanded to an AtomSet triangles for which at least 'selnum'
899 vertices belong to atoms in this set will be extracted.
900 If ommited, the whole surface is extracted.
901 component: MSMS component (i.e. a closed surface) to be extracted.
902 Component 0 is always the external component.
903 cut0ff: if an atom is less than cutoff away from the surface it will be
904 selected
905
906 OutputPort:
907 surfaceAtoms: AtomSet of atoms close to the surface
908 interiorAtoms: AtomSet of atoms not close to the surface
909 """
910 - def __init__(self, name='SurfaceAtoms', **kw):
911 kw['name'] = name
912 apply( NetworkNode.__init__, (self,), kw )
913
914 self.widgetDescr['component'] = {
915 'class':'NEDial', 'size':50,
916 'oneTurn':5, 'min':0, 'lockMin':1, 'lockMax':1, 'type':'int',
917 'initialValue':0,
918 'labelGridCfg':{'sticky':'w'},
919 'labelCfg':{'text':'component'},
920 }
921 self.widgetDescr['cutoff'] = {
922 'class':'NEDial', 'size':50,
923 'oneTurn':5.0, 'min':0, 'lockMin':1, 'type':'float',
924 'initialValue':0.0,
925 'labelGridCfg':{'sticky':'w'},
926 'labelCfg':{'text':'cutoff'},
927 }
928
929 ip = self.inputPortsDescr
930 ip.append(datatype='MSMSobject', name='MSMS')
931 ip.append(datatype='TreeNodeSet',
932 balloon='Atoms for which triangles are extracted',
933 required=False, name='nodes')
934 ip.append(datatype='int', required=False, name='component')
935 ip.append(datatype='float', required=False, name='cutoff')
936
937 op = self.outputPortsDescr
938 op.append(datatype='AtomSet', name='surfaceAtoms')
939 op.append(datatype='AtomSet', name='interiorAtoms')
940
941
942 code = """def doit(self, msms, nodes=None, component=0, cutoff=0.0):
943 vf,vi,f = msms.getTriangles()
944 from mglutil.util.uniq import uniq
945 atInd = uniq(vi[:,1])
946 atoms = msms.atoms
947 surfats ={}
948 intats = []
949
950 if cutoff is None: cutoff=0.0
951
952 # find surface atoms
953 for n in atInd:
954 surfats[atoms[n-1]]=n-1
955
956 # find complementary set
957 for a in atoms:
958 if surfats.get(a, None) is None:
959 intats.append(a)
960
961 if cutoff!=0.0:
962 import Numeric
963 points = Numeric.array(vf[:,:3], 'f')
964 ids = Numeric.arrayrange(len(points)).astype('i')
965 result = Numeric.zeros( (len(points),) ).astype('i')
966 bht = bhtreelib.TBHTree( points, ids, 10, 10, 9999.0 )
967 for a in intats:
968 nb = bht.ClosePoints( tuple(a.coords), cutoff, result)
969 if nb:
970 print 'atom' , a, 'is close'
971 surfats[a] = True
972 intats.remove(a)
973
974 self.outputData(surfaceAtoms=AtomSet(surfats.keys()))
975 self.outputData(interiorAtoms=AtomSet(intats))
976 """
977
978 self.setFunction(code)
979
980 except:
981 bhtreelibFound = False
982
983
985 """Compute solvent excluded surface using the MSMS library.
986
987 Input ports:
988 atoms: a set of atoms for which to compute a surface
989 radii: optional atomic radii. If they are ommited, we tri to get them
990 from the atoms
991 seedAtoms: a list of up to 3 atoms that are used to position the rolling
992 probe initially. Can be used to compute cavity surfaces.
993 density: floating point value describing the number of vertices per
994 angstrom square used for triangulating the surface
995 probe_radius: floating point value decribing the radius of the rolling
996 probe sphere.
997 allComp: Boolean value triggering the computation of all components
998
999 OutputPort:
1000 MSMS: outputs an MSMSObject
1001
1002 The set of atoms used to calculate the surface are stored in the atoms
1003 attribute of the MSMSobject. By default, the probe_radius and the
1004 density parameters are bound to dials and allComp is bound to a check-
1005 button widget located in the parameter panel.
1006 """
1007 - def __init__(self, name='MSMS', **kw):
1008 kw['name'] = name
1009 apply( NetworkNode.