def test_optimisingOverlays(self): pts1 = np.array([[-0.541357990208,0.840223963793,0.967951435], [-0.507301547591,-0.542664198999,1.171816351], [0.0255987444643,-1.38443421464,0.204678748], [0.556845352422,-0.849058545788,-0.964797836], [0.509212767371,0.526541086859,-1.159201955], [-0.0429973264567,1.40939190877,-0.220446743]]) pts2 = np.array([[-0.452870161944,1.0471304112,0.822046103134], [0.838143143533,0.525685681579,0.978663279603], [1.28692322978,-0.496230348673,0.148563133317], [0.457098160295,-1.03724996618,-0.829129779641], [-0.837847702903,-0.538192702582,-0.964350371592], [-1.29144666876,0.49885692466,-0.155792364821]]) np.testing.assert_array_almost_equal(np.mean(pts1, axis=0), np.mean(pts2, axis=0)) from listMathsSlow import rmsd print rmsd(pts1, pts2) from listMathsSlow import overlay_points_RMSD originalRMSD, qRot = overlay_points_RMSD(pts1, pts2) # print originalRMSD from quaternions import quaternion_rotatn self.assertAlmostEqual(rmsd(pts2, np.array([quaternion_rotatn(x, qRot) for x in pts1])), originalRMSD) from listMathsSlow import optimalZRotation bestRMSDZ, bestQRotZ = optimalZRotation(pts1, pts2) from quaternions import make_quat_rot2 from listMathsSlow import rmsdNoRestriction #check with a grid that optimal Z really is about the best _nPts = 20 gridOptimisedPoints = np.array([rmsd(pts2, np.array([quaternion_rotatn(x, make_quat_rot2(float(i)/float(_nPts) * 2. * np.pi, np.array([0., 0., 1.]))) for x in pts1]) ) for i in xrange(_nPts)]) self.assertTrue(all([x > bestRMSDZ for x in gridOptimisedPoints])) self.assertTrue(bestRMSDZ + 1.e-3 > gridOptimisedPoints[3])
def functionOfOverlay(angle, pts1, pts2, allowRMSDNoRestriction): _quat = make_quat_rot2(angle, np.array([0., 0., 1.])) rotatedPts1 = np.array([quaternion_rotatn(x, _quat) for x in pts1]) if allowRMSDNoRestriction: return rmsdNoRestriction(rotatedPts1, pts2) else: return rmsd(rotatedPts1, pts2)
def boundsShowSurface(self, nanocrystalSize, nanoCell, surfaceCell, angleVariables): ''' estimate bounds for surface during plotting (wont effect calculation) returns surfaceMinA, surfaceMaxA, surfaceMinB, surfaceMaxB (status - run this and visually inspected things, but not extended to general angles, thoroughly tested) ''' cornersNanoCrystal = np.dot( np.array([[nanocrystalSize[0], 0., 0.], [0., nanocrystalSize[1], 0.], [nanocrystalSize[0], nanocrystalSize[1], 0.], [0., 0., 0.]]), nanoCell) #zyz convention -- (after comparing to Stone's book) -- # assert(all([x==0. for x in angleVariables[1:]])) from quaternions import zyz_quaternion, quaternion_rotatn cornersNanoCrystal = np.array([ quaternion_rotatn( x, zyz_quaternion( *map(lambda x: x * np.pi / 180., angleVariables))) for x in cornersNanoCrystal ]) #find bounds in surface cell boundsSurface = np.dot(cornersNanoCrystal, np.linalg.inv(surfaceCell)) boundsSurface = np.vstack( [np.min(boundsSurface, axis=0), np.max(boundsSurface, axis=0)]) return map(int, [ np.floor(boundsSurface[0, 0]), np.ceil(boundsSurface[1, 0]), np.floor(boundsSurface[0, 1]), np.ceil(boundsSurface[1, 1]) ])
def rotateASEAtomsAroundCentroidQuat(self, quat): from quaternions import quaternion_rotatn oldPositions = self.aseAtoms.get_positions() _mean = np.mean(oldPositions, axis=0) oldPositions -= _mean newPositions = [quaternion_rotatn(x, quat) for x in oldPositions] for i in xrange(len(self.aseAtoms)): self.aseAtoms[i].position = newPositions[i] + _mean
def calculateAxisSystem(self, atom1, atom2, atom3): ''' atom1 (int) is (index for) pivot, to atom2 is x, 3 -> y . Make right handed frame ''' from quaternions import make_quat_rot2, quaternion_rotatn internalX = (self.aseAtoms[atom2].position - self.aseAtoms[atom1].position) /\ np.linalg.norm(self.aseAtoms[atom1].position - self.aseAtoms[atom2].position) internalY = (self.aseAtoms[atom3].position - self.aseAtoms[atom1].position) /\ np.linalg.norm(self.aseAtoms[atom3].position - self.aseAtoms[atom1].position) internalZ = np.cross(internalX, internalY) / np.linalg.norm( np.cross(internalX, internalY)) _angle = np.pi / 2. - vecAngle(internalX, internalY) rotationQ = make_quat_rot2(_angle, internalZ) internalY = quaternion_rotatn(internalY, rotationQ) self.axisSystem = np.vstack([internalX, internalY, internalZ]) return self.axisSystem
def makeFilesSingleMolSurface(surfaceCif, nanocrystalCif, surfaceDMA, nanocrystalDMA, nanocrystalAngles=[0., 0., 0.], nanocrystalSize=[1, 1, 1], freezeRotations=False, manuallyRotateNanocrystal=False, rotateAllAngles=False): ''' Writes the files needed to run orient (.in is the input) Also can write other things to visualize components or composite - may be useful as check ''' from analyseClusters import getSpeciesListCIF, Surface, splitASEAtomsToMols, Crystal from orientIO import NanocrystalOnSurfaceInput #from ccdc.io import CrystalReader from ioAndInterfaces import ccdcCrystalToASE from multipoleFile import MultipoleFile, BOHRTOANGSTROM from copy import deepcopy #variables # nanocrystalAngles = [0., 0., 0.] #reading filenames moleculeFilename = 'nanoCryst.punch' #writing filenames cellFilename = 'surface.cell' inputFilename = 'surfaceCalc.in' #other filenams potentialFilename = 'fit4orient.pots' surfaceCrystal = Crystal.fromCif(surfaceCif) nanoCrystal = Crystal.fromCif(nanocrystalCif) #keep only lowest molecule along c axis print "%s mols in nanocrystal %s in surface" % ( len(nanoCrystal.asymmetricMolecules), len(surfaceCrystal.asymmetricMolecules)) nanoCrystal.asymmetricMolecules = [ sorted( nanoCrystal.asymmetricMolecules, key=lambda x: np.dot(x.aseAtoms.get_center_of_mass(scaled=False), nanoCrystal.aseCell[2]))[0] ] print 'MASSIVE HACK - KEEPING BUT A SINGLE MOL' surfaceCrystal.asymmetricMolecules = [ sorted( surfaceCrystal.asymmetricMolecules, key=lambda x: np.dot(x.aseAtoms.get_center_of_mass(scaled=False), surfaceCrystal.aseCell[2]))[0] ] # If doing this, rotate the nanocrystal so that the bottom molecule of surface is same orientation as it if manuallyRotateNanocrystal: from listMathsSlow import overlay_points_RMSD from quaternions import quaternion_rotatn targetAtomPositions = sorted( surfaceCrystal.asymmetricMolecules, key=lambda x: np.dot(x.aseAtoms.get_center_of_mass( scaled=False), surfaceCrystal.aseCell[2] ))[0].aseAtoms.get_positions()[7:13, :] nanoAtoms = nanoCrystal.asymmetricMolecules[0].aseAtoms.get_positions( )[7:13, :] print 'assuming numbering system such that ring of atoms is 2nd ring ->7-12', nanoAtoms.shape, targetAtomPositions.shape if rotateAllAngles: print 'dont do this - rotating all angles prevents coplanar surfaces' exit() _rmsd, _qRot = overlay_points_RMSD( nanoAtoms - np.mean(nanoAtoms, axis=0), targetAtomPositions - np.mean(targetAtomPositions, axis=0)) assert (_rmsd < 0.1) else: #just rotate around z axis - the allowRMSDNoRestriction should not be used- just make sure atomic numbering consistent from listMathsSlow import optimalZRotation _rmsd, _qRot = optimalZRotation( nanoAtoms - np.mean(nanoAtoms, axis=0), targetAtomPositions - np.mean(targetAtomPositions, axis=0), allowRMSDNoRestriction=False) def tempDistPrint(x): print[np.linalg.norm(x[i + 1] - x[i]) for i in range(5)] # tempDistPrint(targetAtomPositions - np.mean(targetAtomPositions, axis=0)) # tempDistPrint(np.array([quaternion_rotatn(x, _qRot) for x in nanoAtoms - np.mean(nanoAtoms, axis=0)])) #rotate nanocrystal atoms and cell nanoCrystal.asymmetricMolecules[0].rotateASEAtomsAroundCentroidQuat( _qRot) nanoCrystal.aseCell = np.array( [quaternion_rotatn(x, _qRot) for x in nanoCrystal.aseCell]) print "%s mols in nanocrystal %s in surface" % ( len(nanoCrystal.asymmetricMolecules), len(surfaceCrystal.asymmetricMolecules)) print "Atoms in nanoCrystal moleucules " + " ".join( [str(len(x.aseAtoms)) for x in nanoCrystal.asymmetricMolecules]) print "Atoms in surface moleucules " + " ".join( [str(len(x.aseAtoms)) for x in surfaceCrystal.asymmetricMolecules]) punchSurface = MultipoleFile(fileName=surfaceDMA) punchNanocrystal = MultipoleFile(fileName=nanocrystalDMA) print "%s atoms in surfaceDMA" % (len(punchSurface.aseAtoms())) print "%s atoms in nanocrystalDMA" % (len(punchNanocrystal.aseAtoms())) # punchSurface.writeFile('dummyPunchSurf.cif') # punchNanocrystal.writeFile('dummyPunchNano.cif') uniqueAtomTypes = list(set(list(surfaceCrystal.uniqueElements()) +\ list(nanoCrystal.uniqueElements()))) #print 'using same atoms for surface and nanocrystal' print 'automatically setting all H bonded to O or N to Hn type' inputHandler = NanocrystalOnSurfaceInput() #displace by c of surfaceCrystal - and add a bit displacementBohr = (surfaceCrystal.aseCell[2] + np.array([0., 0., 3.5])) / BOHRTOANGSTROM variables = [['x1', displacementBohr[0], 'Bohr'], ['y1', displacementBohr[1], 'Bohr'], ['z1', displacementBohr[2], 'Bohr'], ['alpha1', nanocrystalAngles[0], 'Degree'], ['beta1', nanocrystalAngles[1], 'Degree'], ['gamma1', nanocrystalAngles[2], 'Degree']] atomsInCell = [ punchSurface.newPositions(m.aseAtoms.positions) for m in surfaceCrystal.asymmetricMolecules ] atomsInNanocrystal = [ punchNanocrystal.newPositions(m.aseAtoms.positions) for m in nanoCrystal.asymmetricMolecules ] with open(moleculeFilename, 'w') as outf: #acidic hydrogens for im, m in enumerate(atomsInNanocrystal): m.setAtomTypes( dict([(x, 'Hn') for x in nanoCrystal.asymmetricMolecules[im]. indicesAcidicHydrogens()])) outf.write('\n'.join([ x.stringFormat(header='', printTypes=True) for x in atomsInNanocrystal ])) print 'passing one molecule in nanocrystal as only needs cell -- clear up later' aseAtomsNanocrystal = nanoCrystal.asymmetricMolecules[0].aseAtoms with open(cellFilename, 'w') as outf: #acidic hydrogens for im, m in enumerate(atomsInCell): m.setAtomTypes( dict([(x, 'Hn') for x in surfaceCrystal. asymmetricMolecules[im].indicesAcidicHydrogens()])) outf.write( inputHandler.cellAndSitesString(surfaceCrystal.aseCell, atomsInCell)) with open(inputFilename, 'w') as outf: outf.write( inputHandler.inputString(variables=variables, moleculeFilename=moleculeFilename, potentialFile=potentialFilename, cellFile=cellFilename, nanocrystal=aseAtomsNanocrystal, surfaceCrystal=surfaceCrystal, nanocrystalSize=nanocrystalSize, atomTypes=uniqueAtomTypes, freezeRotations=freezeRotations)) # # EVERYTHING BELOW HERE IS FOR MY (DHC) DEBUGGING AND LOOKING AT FILES TO CHECK THINGS - NOT USED # #write temp.xyz with all atoms print 'writing temporary xyz file to temp.xyz - note that this used only by DHC, not ORIENT itself' try: from ase import Atoms except ImportError: print 'Need ASE to print out temp.xyz but not important for ORIENT - ignore this' return # superSurface = deepcopy(surfaceCrystal) # superSurface.filledUnitCellMolecules(superCell=np.array([3,3,2])) # print [x[1] for x in variables[3:6]] nanocrystalSize = [1, 1, 1] print 'hack small nano' surfaceBounds = inputHandler.boundsShowSurface( nanocrystalSize, nanoCrystal.aseCell, surfaceCrystal.aseCell, [x[1] for x in variables[3:6]]) print nanocrystalSize # print len(superSurface.asymmetricMolecules) # totalAtoms = Atoms(symbols = [x.symbol for m in superSurface.asymmetricMolecules for x in m.aseAtoms] +\ # [x.symbol for m in nanoCrystal.asymmetricMolecules for x in m.aseAtoms], # positions = np.vstack([np.array([x.position for m in superSurface.asymmetricMolecules for x in m.aseAtoms]), # displacementBohr * BOHRTOANGSTROM +\ # np.array([x.position for m in nanoCrystal.asymmetricMolecules for x in m.aseAtoms])]) # ) # checkSurfPunch = MultipoleFile() # checkSurfPunch.initFromFile('surface.cell', _fileFormat='punch') # checkSurfPunch.initFromFile('dummySurface.punch', _fileFormat='punch') # checkNanoPunch = MultipoleFile() # checkNanoPunch.initFromFile('nanoCryst.punch') # print checkNanoPunch.atomPositions()[0] # print nanoCrystal.asymmetricMolecules[0].aseAtoms[0].position # print checkSurfPunch.atomPositions()[0]# # print surfaceCrystal.asymmetricMolecules[0].aseAtoms[0].position # tempASE = checkNanoPunch.aseAtoms() # tempASE.write('nanoCheck.xyz') # checkNanoPunch.writeFile('nanoCheck.xyz') # tempASE = checkNanoPunch.aseAtoms() # checkSurfPunch.writeFile('surfCheck.xyz') # exit() # print 'adding big gap' totalAtoms = Atoms(symbols = [x.symbol for m in surfaceCrystal.asymmetricMolecules for x in m.aseAtoms for a in xrange(surfaceBounds[0], surfaceBounds[1] + 1) for b in xrange(surfaceBounds[2], surfaceBounds[3] + 1)] +\ [x.symbol for m in nanoCrystal.asymmetricMolecules for x in m.aseAtoms for a in xrange(0, nanocrystalSize[0]) for b in xrange(0, nanocrystalSize[1])], positions = np.vstack([np.array([x.position + np.dot(np.array([a, b, 1]), surfaceCrystal.aseCell) for m in surfaceCrystal.asymmetricMolecules for x in m.aseAtoms for a in xrange(surfaceBounds[0], surfaceBounds[1] + 1) for b in xrange(surfaceBounds[2], surfaceBounds[3] + 1)]), displacementBohr * BOHRTOANGSTROM +\ # np.array([0., 0., 50.]) + displacementBohr * BOHRTOANGSTROM +\ np.array([x.position + np.dot(np.array([a, b, 1]), nanoCrystal.aseCell) for m in nanoCrystal.asymmetricMolecules for x in m.aseAtoms for a in xrange(0, nanocrystalSize[0]) for b in xrange(0, nanocrystalSize[1])])]) ) # print np.min(np.array([x.position + np.dot(np.array([a, b, 1]), surfaceCrystal.aseCell) # for m in surfaceCrystal.asymmetricMolecules for x in m.aseAtoms # for a in xrange(surfaceBounds[0], surfaceBounds[1] + 1) # for b in xrange(surfaceBounds[2], surfaceBounds[3] + 1)]), axis=0) # print np.max(np.array([x.position + np.dot(np.array([a, b, 1]), surfaceCrystal.aseCell) # for m in surfaceCrystal.asymmetricMolecules for x in m.aseAtoms # for a in xrange(surfaceBounds[0], surfaceBounds[1] + 1) # for b in xrange(surfaceBounds[2], surfaceBounds[3] + 1)]), axis=0) # print np.min( displacementBohr * BOHRTOANGSTROM +\ # np.array([x.position + np.dot(np.array([a, b, 1]), nanoCrystal.aseCell) # for m in nanoCrystal.asymmetricMolecules for x in m.aseAtoms # for a in xrange(0, nanocrystalSize[0]) # for b in xrange(0, nanocrystalSize[1])]), axis=0) # print np.max( displacementBohr * BOHRTOANGSTROM +\ # np.array([x.position + np.dot(np.array([a, b, 1]), nanoCrystal.aseCell) # for m in nanoCrystal.asymmetricMolecules for x in m.aseAtoms # for a in xrange(0, nanocrystalSize[0]) # for b in xrange(0, nanocrystalSize[1])]), axis=0) # print displacementBohr * BOHRTOANGSTROM # print surfaceCrystal.aseCell[2] totalAtoms.write('temp.xyz')