def __init__(self, atomlist): # convert the list of atoms into a list of 3d points nat = len(atomlist) pos = XYZ.atomlist2vector(atomlist) points = [] for i in range(0, nat): points.append(pos[3 * i:3 * (i + 1)]) # QHull needs at least 4 point to construct the initial # simplex. if nat < 4: # If there are less than 4 atoms, # we add the center of mass as an additional point masses = AtomicData.atomlist2masses(atomlist) com = MolCo.center_of_mass(masses, pos) points.append(com) if nat < 3: # If there are less than 3 atoms we add # an arbitrary point on the x-axis points.append(com + np.array([0.0005, 0.0, 0.0])) if nat < 2: # If there is only one atom, we add another arbitrary # point on the y-axis points.append(com + np.array([0.0, 0.0005, 0.0])) # We add small random numbers to the input coordinates, so that # we get a 3D convex hull even if the molecule is planar points = np.array(points) + 0.0001 * np.random.rand(len(points), 3) # find the convex hull using the qhull code hull = ConvexHull(points, qhull_options="QbB Qt") # call the constructor of the parent class (MinimalEnclosingBox) super(MoleculeBox, self).__init__(hull)
def _update_visualization(self, atomlist, charges, fragments): """ only change the underlying data but do not recreate visualization. This is faster and avoids jerky animations. """ mlab = self.scene.mlab if self.show_flags["charges"] == True: i = 0 for q,(Z,pos) in zip(charges, atomlist): x,y,z = pos if q < 0.0: color = (0.,0.,1.) elif q > 0.0: color = (1.,0.,0.) else: color = (1.,1.,1.) # color does not work so far txt = "%+2.3f" % q if self.show_flags["labels"] == True: # maybe charges should not overlap with labels txt = "%s" % txt # update label position and text label = self.charge_labels[i] label.remove() label = mlab.text(x,y, txt, z=z, figure=self.scene.mayavi_scene) self.charge_labels[i] = label label.actor.set(text_scale_mode='none', width=0.05, height=0.1) label.property.set(justification='centered', vertical_justification='centered') i += 1 if self.show_flags["frag. charges"] == True: i = 0 for ifrag,(fragment_indeces, fragment_atomlist) in enumerate(fragments): # compute the charges on fragment qfrag = np.sum(charges[fragment_indeces]) print "Fragment charges: %s" % charges[fragment_indeces] # compute the center of the molecule, pos_frag = XYZ.atomlist2vector(fragment_atomlist) masses_frag = AtomicData.atomlist2masses(fragment_atomlist) com = MolCo.center_of_mass(masses_frag, pos_frag) # print "Fragment %d charge = %s" % (ifrag, qfrag) txt = "%+2.3f" % qfrag label = self.frag_charge_labels[i] label.remove() label = mlab.text(com[0],com[1], txt, z=com[2], line_width=0.8, figure=self.scene.mayavi_scene) self.frag_charge_labels[i] = label label.actor.set(text_scale_mode='none', width=0.05, height=0.1) label.property.set(justification='centered', vertical_justification='centered') i += 1 if self.show_flags["charge clouds"] == True: vec = XYZ.atomlist2vector(atomlist) x, y, z = vec[::3], vec[1::3], vec[2::3] s = abs(charges) # The charge clouds represent surfaces of equal charge density around each atoms. # In DFTB the charge fluctuations are modelled by a Gaussian: # F(r) = 1/(2*pi*sA^2)^(3/2) * exp(-r^2/(2*sA^2)) # The radii of the charge clouds are scaled by the charge on the atom: # r = q * r0 # The radius r0 belongs to a charge cloud containing exactly 1 electron, it depends # on the hubbard parameter through sA and on the isoValue: # F(r0) = F(0) * isoValue # r0s = self.charge_cloud_radii.getRadii(atomlist) s *= r0s self.cloud.mlab_source.set(x=x,y=y,z=z,u=s,v=s,w=s, scalars=charges, scale_factor=1.0) # atoms are coloured by their atomic number self.cloud.glyph.color_mode = "color_by_scalar" self.cloud.glyph.glyph_source.glyph_source.center = [0,0,0] self.cloud.module_manager.scalar_lut_manager.lut.table = self.lut self.cloud.module_manager.scalar_lut_manager.data_range = (-1.0, 1.0)
def _create_visualization(self, atomlist, charges, fragments): mlab = self.scene.mlab if self.show_flags["charges"] == True and type(charges) != type(None): self.charge_labels = [] for q,(Z,pos) in zip(charges, atomlist): x,y,z = pos if q < 0.0: color = (0.,0.,1.) elif q > 0.0: color = (1.,0.,0.) else: color = (1.,1.,1.) # color does not work so far txt = "%+2.3f" % q if self.show_flags["labels"] == True: # maybe charges should not overlap with labels txt = "%s" % txt label = mlab.text(x,y, txt, z=z, figure=self.scene.mayavi_scene) label.actor.set(text_scale_mode='none', width=0.05, height=0.1) label.property.set(justification='centered', vertical_justification='centered') self.charge_labels.append( label ) self.shown_charges = True else: self.shown_charges = False if self.show_flags["frag. charges"] == True and type(charges) != type(None): self.frag_charge_labels = [] for ifrag,(fragment_indeces, fragment_atomlist, fragment_box) in enumerate(fragments): # compute the charges on fragment qfrag = np.sum(charges[fragment_indeces]) print "Fragment charges: %s" % charges[fragment_indeces] # compute the center of the molecule, pos_frag = XYZ.atomlist2vector(fragment_atomlist) masses_frag = AtomicData.atomlist2masses(fragment_atomlist) com = MolCo.center_of_mass(masses_frag, pos_frag) # print "Fragment %d charge = %s" % (ifrag, qfrag) txt = "%+2.3f" % qfrag label = mlab.text(com[0],com[1], txt, z=com[2], line_width=0.8, figure=self.scene.mayavi_scene) label.actor.set(text_scale_mode='none', width=0.05, height=0.1) label.property.set(justification='centered', vertical_justification='centered') self.frag_charge_labels.append( label ) self.shown_frag_charges = True else: self.shown_frag_charges = False if self.show_flags["charge clouds"] == True and type(charges) != type(None): self.charge_clouds = [] vec = XYZ.atomlist2vector(atomlist) x, y, z = vec[::3], vec[1::3], vec[2::3] s = abs(charges) # The charge clouds represent surfaces of equal charge density around each atoms. # In DFTB the charge fluctuations are modelled by a Gaussian: # F(r) = 1/(2*pi*sA^2)^(3/2) * exp(-r^2/(2*sA^2)) # The radii of the charge clouds are scaled by the charge on the atom: # r = q * r0 # The radius r0 belongs to a charge cloud containing exactly 1 electron, it depends # on the hubbard parameter through sA and on the isoValue: # F(r0) = F(0) * isoValue # r0s = self.charge_cloud_radii.getRadii(atomlist) s *= r0s cloud = mlab.quiver3d(x,y,z,s,s,s, scalars=charges, mode="sphere", scale_factor=1.0, resolution=20, opacity = 0.4, figure=self.scene.mayavi_scene) # atoms are coloured by their atomic number cloud.glyph.color_mode = "color_by_scalar" cloud.glyph.glyph_source.glyph_source.center = [0,0,0] self.lut = cloud.module_manager.scalar_lut_manager.lut.table.to_array() red = np.array((255.0, 0.0, 0.0)).astype('uint8') blue = np.array((0.0, 0.0, 255.0)).astype('uint8') for i in range(0, 255): if i < 128: color = blue else: color = red self.lut[i,0:3] = color cloud.module_manager.scalar_lut_manager.lut.table = self.lut cloud.module_manager.scalar_lut_manager.data_range = (-1.0, 1.0) self.cloud = cloud self.charge_clouds.append( cloud ) self.shown_charge_clouds = True else: self.shown_charge_clouds = False if self.show_flags["dipole moment"] == True and type(charges) != type(None): self.dipole_vectors = [] # The dipole vector is placed at the center of mass pos = XYZ.atomlist2vector(atomlist) masses = AtomicData.atomlist2masses(atomlist) com = MolCo.center_of_mass(masses, pos) # compute dipole moment from charge distribution dipole = np.zeros(3) for i,q in enumerate(charges): dipole += q * pos[3*i:3*(i+1)] print "Dipole moment D = %s a.u." % dipole # For plotting the dipole vector is converted to Debye dipole *= AtomicData.ebohr_to_debye print "Dipole moment D = %s Debye" % dipole print "Length of dipole moment |D| = %s Debye" % la.norm(dipole) quiver3d = mlab.quiver3d(com[0],com[1],com[2], dipole[0], dipole[1], dipole[2], line_width=5.0, scale_mode='vector', color=(0,0,1), scale_factor=1.0) self.dipole_vectors.append(quiver3d) else: self.shown_dipole_moment = False if self.show_flags["enclosing box"] == True: self.frag_enclosing_boxes = [] for ifrag,(fragment_indeces, fragment_atomlist, fragment_box) in enumerate(fragments): box = fragment_box # plot edges of the enclosing box for edge in box.edges: l = mlab.plot3d(box.vertices[edge,0], box.vertices[edge,1], box.vertices[edge,2], color=(1,0,0), figure=self.scene.mayavi_scene) self.frag_enclosing_boxes.append(l) # plot axes for axis, color in zip(box.axes, [(1.,0.,0.),(0.,1.,0.), (0.,0.,1.)]): ax = mlab.quiver3d(float(box.center[0]), float(box.center[1]), float(box.center[2]), float(axis[0]), float(axis[1]), float(axis[2]), color=color, scale_factor=3.0, mode='arrow', resolution=20, figure=self.scene.mayavi_scene) self.frag_enclosing_boxes.append(ax) self.shown_enclosing_boxes = True else: self.shown_enclosing_boxes = False if self.show_flags["screening charges (COSMO)"] == True: # read parameter for solvent model from command line # or configuration file parser = OptionParserFuncWrapper([ImplicitSolvent.SolventCavity.__init__], "", unknown_options="ignore") # extract only arguments for constructor of solvent cavity (solvent_options, args) = parser.parse_args(ImplicitSolvent.SolventCavity.__init__) # cavity = ImplicitSolvent.SolventCavity(**solvent_options) cavity.constructSAS(atomlist) area = cavity.getSurfaceArea() print "solvent accessible surface area: %8.6f bohr^2 %8.6f Ang^2" % (area, area*AtomicData.bohr_to_angs**2) points = cavity.getSurfacePoints() x,y,z = points[:,0], points[:,1], points[:,2] if type(charges) != type(None): # If there are Mulliken charges we can compute the # induced charges on the surface of the cavity # according to COSMO cavity.constructCOSMO() induced_charges = cavity.getInducedCharges(charges) screening_energy = cavity.getScreeningEnergy(charges) print "screening energy: %10.6f Hartree %10.6f kcal/mol" \ % (screening_energy, screening_energy * AtomicData.hartree_to_kcalmol) # The surface points are colored and scaled # according to their charge # negative -> blue positive -> red points3d = mlab.points3d(x,y,z,induced_charges, colormap="blue-red", mode='2dcross', scale_factor=10) # mode='2dvertex') points3d.glyph.color_mode = "color_by_scalar" else: # points3d = mlab.points3d(x,y,z,color=(0.0,0.0,0.8), mode='2dvertex') self.sas_points.append( points3d ) else: self.shown_solvent_screening_charges = False if self.show_flags["non-adiab. coupling vectors"] == True: vec = XYZ.atomlist2vector(atomlist) x, y, z = vec[::3], vec[1::3], vec[2::3] # assume that charges are transition charges transition_charges = charges # Because we don't know the energy here, the energy difference # in the denominator is set to 1, so only the direction and relative # lengths are correct. nac = NACsApprox.coupling_vector(atomlist, transition_charges, 1.0) # directions of NAC vectors u,v,w = nac[0,:], nac[1,:], nac[2,:] # Add a NAC vector at each atom quiver3d = mlab.quiver3d(x,y,z, u,v,w, line_width=5.0) self.nac_vectors.append(quiver3d) else: self.shown_nacs = False