def __init__( self, *args, **kargs ): if len( args ) == 6: a,b,c, alpha, beta, gamma = args if kargs.get( 'indeg', False ): self.rep = Reper.from_abc( a,b,c, alpha * math.pi / 180.0, beta * math.pi / 180.0, gamma * math.pi / 180.0 ) else: self.rep = Reper.from_abc( a,b,c, alpha, beta, gamma ) elif len( args ) == 9: self.rep = Reper( Vec( *args[0:3] ), Vec( *args[3:6] ), Vec( *args[6:9] ) ) else: assert type( args[ 0 ] ) is Reper self.rep = args[ 0 ] self.atoms = {} self.decart = kargs.get( 'decart', False ) ## by default assume that all coordinates of atoms in fractional coordinates
def from_abc(klass, a, b, c, alpha, beta, gamma): """ Create 3 vectors from six parameters """ sin_gamma = sin(gamma) cos_gamma = cos(gamma) tan_gamma = tan(gamma) cos_beta = cos(beta) cos_alpha = cos(alpha) v1 = Vec(a, 0, 0) v2 = Vec(b * cos_gamma, b * sin_gamma, 0) v3 = Vec( c * cos_beta, -(c * cos_beta / tan_gamma) + c * cos_alpha / sin_gamma, sqrt(c**2 - (c * cos_beta)**2 - (-(c * cos_beta / tan_gamma) + c * cos_alpha / sin_gamma)**2)) return Reper(v1, v2, v3)
def norm(self): """ Reduce & make all zelling args negative. """ vs = [self.v1, self.v2, self.v3, (self.v1 + self.v2 + self.v3) * -1] f = True while f: f = False for i, v1 in enumerate(vs): for j, v2 in enumerate(vs): if i != j and v1 * v2 > 0.0001: for k in xrange(4): if k != i and k != j: vs[k] += v1 ## add v1 to all, except v1 and v2 vs[i] *= -1 ## v1 --> -v1 v1 *= -1 ## also correct cycle var f = True return Reper(vs[0], vs[1], vs[2])
def minimize(self): """ Reduce reper to 3 minimal vector. """ vs = [self.v1, self.v2, self.v3] f = True while f: f = False vts = [t for t in vs] abc = ( (i,j,k) for i in xrange( -1, 2 )\ for j in xrange( -1, 2 )\ for k in xrange( -1, 2 ) ) for a, b, c in abc: vnew = a * vs[0] + b * vs[1] + c * vs[2] for i, v in enumerate(vts): if v.vlen2() > vnew.vlen2(): vo, vts[i] = vts[i], vnew if abs(vts[0] * vts[1].vcross(vts[2])) > 0.0001: f = True else: vts[i] = vo #print vs, vts, f vs = vts return Reper(*vs)
def to_min( self ): decart = self.decart self.to_decart() ## transform coordinates to decart basis rep = self.rep ## alias near = {} ext = self * 1 ## extend in all directions by 1 mps = min( self.atoms.values(), key = lambda l: len( l ) ) ## "minimal points" ## this is minimal group of points mps = list( mps ) ## convert set() to ordered list() #print "minimal points = ", mps eqv_dr = set() ## all possible translations wich translate each point of ## this ucell (self) to extended ucell (ext) #print '--[1]-- find eqv_dr' ## for each pair from mps... for i in xrange( len(mps) ): for j in xrange( i+1, len(mps) ): dr = mps[i] - mps[j] ## vec to translate uct = self + dr ## translated ucell eqv = True ## translate equiv or not for k,vs in uct: ## for each groups of atoms... (k = group name, vs = list of positions) if not vs.issubset( ext.atoms[ k ] ): ## some points not intersected with others eqv = False break if eqv: eqv_dr.add( dr ) eqv_dr.update( list( rep ) ) ## add vectors from original reper #print "possible translations = ", eqv_dr ## sort vectors by its lengs eqv_dr = list( eqv_dr ) eqv_dr.sort( key = lambda v: v.vlen() ) #DEBUG DRAW #for v in eqv_dr: # drawgl( v, style="line", color=(1,0,0) ) #print '--[2]-- find v1,v2,v3' ## take first vector for v1 in eqv_dr: v2, v3 = None, None ## take second shorterst wich is not lie on one line with v1 v1n = v1.norm() for v in eqv_dr: if not v1n == v.norm(): #print v1n, v.norm() v2 = v break ## take third shortest wich is not coplanar with v1 and v2 vc = v1.vcross( v2 ) for v in eqv_dr: if abs( vc * v ) > 0.05: v3 = v break if v2 and v3: ## good solution founded nrep = Reper( v1,v2,v3 ) ## new reper break #DEBUG DRAW #for v in nrep: # drawgl( v, style="line", color=(0,1,0) ) #print '--[3]-- reduce ucell' uc = UCell( nrep, decart = True ) ## all atoms in decart coordinate system ## reduce basis for k,vs in self.atoms.iteritems(): vsn = nrep.dec2frac( vs ) vsn = map( lambda v: v.z2o(), vsn ) vsn = set( vsn ) vsn = nrep.frac2dec( vsn ) uc.add( k, vsn ) if not decart: self.to_fract() ## restore state of ucell uc.to_fract() ## convert answer to the same coordinate system return uc
class UCell( object ): def __init__( self, *args, **kargs ): if len( args ) == 6: a,b,c, alpha, beta, gamma = args if kargs.get( 'indeg', False ): self.rep = Reper.from_abc( a,b,c, alpha * math.pi / 180.0, beta * math.pi / 180.0, gamma * math.pi / 180.0 ) else: self.rep = Reper.from_abc( a,b,c, alpha, beta, gamma ) elif len( args ) == 9: self.rep = Reper( Vec( *args[0:3] ), Vec( *args[3:6] ), Vec( *args[6:9] ) ) else: assert type( args[ 0 ] ) is Reper self.rep = args[ 0 ] self.atoms = {} self.decart = kargs.get( 'decart', False ) ## by default assume that all coordinates of atoms in fractional coordinates def add( self, name, x ): """ Add a group of named points (atoms) to unit cell. Points mus be in decart (not fractional) coordinate system. """ if name not in self.atoms: self.atoms[ name ] = set() if type( x ) in ( list, set, tuple ): self.atoms[ name ].update( set( x ) ) elif type( x ) is Vec: self.atoms[ name ].add( x ) else: raise Exception, "type of argument must be list,set,tuple, or single Vec" def __getitem__( self, n ): if n in self.atoms: return self.atoms[ n ] else: res = [] self.atoms[ n ] = res return res def __setitem__( self, n, v ): self.atoms[ n ] = v def __iter__( self ): return self.atoms.iteritems() def to_fract( self ): """ convert all coordinates: decart --> fractional """ if self.decart: for n, vs in self: self[ n ] = self.rep.dec2frac( vs ) self.decart = False def to_decart( self ): """ convert all coordinates: fractional --> decart """ if not self.decart: for n, vs in self: self[ n ] = self.rep.frac2dec( vs ) self.decart = True def __mul__( self, o ): """ Multiply ucell with number and space group """ if type( o ) is int: decart = self.decart self.to_decart() ns = [ (i,j,k) for i in xrange( -o, o+1 )\ for j in xrange( -o, o+1 )\ for k in xrange( -o, o+1 ) ] uc = UCell( self.rep, decart = True ) for k,vs in self.atoms.iteritems(): toadd = set() for t in ns: vt = self.rep * t for v in vs: ## for each vector with name "k" toadd.add( v + vt ) ## translate v by reper in each directions uc.add( k, toadd ) ## add extended points with name "k" if not decart: uc.to_fract() return uc elif type( o ) == SpGrp: uc = UCell( self.rep ) for n, ats in self: tmp = set() for a in ats: for v in o * a: ## multiply each atom position with space group tmp.add( v ) ## ... and then add it uc[ n ] = list( tmp ) ## transform to list and save return uc def __add__( self, obj ): """ Translate ucell by obj """ if type( obj ) is Vec: nc = UCell( self.rep ) for k,vs in self.atoms.iteritems(): vs = map( lambda v: v + obj, vs ) ## translate vectors nc.add( k, vs ) return nc elif type( obj ) is UCell: nc = UCell( self.rep ) nc.atoms = dict( self.atoms ) ## make copy of self atoms for k,v in obj.atoms.iteritems(): nc.atoms[ k ] = nc.atoms.get( k, [] ) + v ## add extended set of atoms return nc def __repr__( self ): rep = self.rep a,b,c = rep[0].vlen(), rep[1].vlen(), rep[2].vlen() gam = math.acos( rep[0].norm() * rep[1].norm() ) * 180 / math.pi bet = math.acos( rep[0].norm() * rep[2].norm() ) * 180 / math.pi alf = math.acos( rep[1].norm() * rep[2].norm() ) * 180 / math.pi return "UCell( a,b,c = (%s, %s, %s) angles = (%s, %s, %s) atoms = '%s' )" % ( a,b,c, alf, bet, gam, map( lambda t: "%s(%s)" % (t[0], len( t[1] )), self ) )