def test_zb(): from numpy import all, abs, dot from pylada.crystal import space_group, transform, binary structure = binary.zinc_blende() ops = space_group(structure) assert len(ops) == 24 for op in ops: assert op.shape == (4, 3) other = transform(structure, op) assert all(abs(dot(op[:3], structure.cell) - other.cell) < 1e-8) for a, atom in zip(structure, other): assert all(abs(dot(op[:3], a.pos) + op[3] - atom.pos) < 1e-8) assert a.type == atom.type for atom in structure: atom.type = ['A', 'B'] ops = space_group(structure) assert len(ops) == 48 for op in ops: assert op.shape == (4, 3) other = transform(structure, op) assert all(abs(dot(op[:3], structure.cell) - other.cell) < 1e-8) for a, atom in zip(structure, other): assert all(abs(dot(op[:3], a.pos) + op[3] - atom.pos) < 1e-8) assert a.type == atom.type
def get_symmetries0(A, mode=0): """ call space_group to get all symmetries of structure A. mode 0 -> return them all, totally unfiltered optionally (mode 1) reduce to unique sets of eigenvalues, b/c others are in some sense equivalent optionally (mode 2) combine by unique eval sets (return list of lists) """ import scipy.linalg as spl sA = space_group(A) print "unfiltered symmetries:" for s in sA: print s print spl.eig(s[0:3, 0:3]) print "done unfiltered symmetries" if (mode == 0): return sA if (mode == 1): sunique = [s[0]] else: sunique = [[s[0]]] for i in range(1, len(sA)): s = s[i] found = False for p in sunique: if eq_sym2(s, p): found = True if (mode == 2): p.append(s) break # out of "for p" if mode == 1 and not found: sunique.append(s) return sunique
def symmetrically_inequivalent_sites(lattice, type): # Haowei, not tested, but seldomly used in practice """ Yields sites occupied by type which are inequivalent according to symmetry operations. When creating a vacancy on, say, "O", or a substitution of "Al" by "Mg", there may be more than one site which qualifies. We want to iterate over those sites which are inequivalent only, so that only the minimum number of operations are performed. :note: lattice sites can be defined as occupiable by more than one atomic type\: lattice.site.type[i] = ["Al", "Mg"]. These sites will be counted if type in lattice.site.type, where type is the input parameter. :Parameters: lattice : `pylada.crystal.Lattice` Lattice for which to find equivalent sites. type : str Atomic specie for which to find inequivalent sites. :return: indices of inequivalent sites. """ from numpy import dot from numpy.linalg import inv, norm from pylada.crystal import into_cell, space_group, primitive # all sites with occupation "type". sites = [site for site in lattice if type in site.type] site_indices = [i for i,site in enumerate(lattice) if type in site.type] # inverse cell. invcell = inv(lattice.cell) # loop over all site with type occupation. i = 0 while i < len(sites): # iterates over symmetry operations. for op in space_group(primitive(lattice)): pos = dot(op[:3], site.pos) + op[3] # finds index of transformed position, using translation quivalents. for t, other in enumerate(sites): if norm(into_cell(pos, lattice.cell, invcell)) < 1e-12: print t break # removes equivalent site and index from lists if necessary if t != i and t < len(sites): sites.pop(t) site_indices.pop(t) i += 1 return site_indices
def symmetrically_inequivalent_sites(lattice, type): """ Yields sites occupied by type which are inequivalent according to symmetry operations. When creating a vacancy on, say, "O", or a substitution of "Al" by "Mg", there may be more than one site which qualifies. We want to iterate over those sites which are inequivalent only, so that only the minimum number of operations are performed. :note: lattice sites can be defined as occupiable by more than one atomic type: lattice.site.type[i] = ["Al", "Mg"]. These sites will be counted if type in lattice.site.type, where type is the input parameter. :Parameters: lattice : `pylada.crystal.Lattice` Lattice for which to find equivalent sites. type : str Atomic specie for which to find inequivalent sites. :return: indices of inequivalent sites. """ from numpy import dot from numpy.linalg import inv, norm from pylada.crystal import into_cell, space_group, primitive # all sites with occupation "type". sites = [site for site in lattice if type in site.type] site_indices = [i for i, site in enumerate(lattice) if type in site.type] # inverse cell. invcell = inv(lattice.cell) # loop over all site with type occupation. i = 0 while i < len(sites): # iterates over symmetry operations. for op in space_group(primitive(lattice)): pos = dot(op[:3], site.pos) + op[3] # finds index of transformed position, using translation quivalents. for t, other in enumerate(sites): if norm(into_cell(pos, lattice.cell, invcell)) < 1e-12: print(t) break # removes equivalent site and index from lists if necessary if t != i and t < len(sites): sites.pop(t) site_indices.pop(t) i += 1 return site_indices
def test_firstisid(cell): """ Assumption is made in Transforms.transformations """ from numpy import abs, all, identity from pylada.crystal import binary, supercell, space_group lattice = binary.zinc_blende() lattice[0].type = ['Si', 'Ge'] lattice[1].type = ['Si', 'Ge', 'C'] # create random structure structure = supercell(lattice, cell) while len(structure) > 1: structure.pop(-1) assert len(structure) == 1 sg = space_group(structure)[0] assert all(abs(sg[:3] - identity(3, dtype='float64')) < 1e-8) assert all(abs(sg[3]) < 1e-8)
def test_rotations(): from numpy import all, dot, zeros from numpy.linalg import inv from pylada.crystal import binary, supercell, HFTransform, space_group, \ which_site from pylada.enum import Transforms lattice = binary.zinc_blende() lattice[0].type = ['Si', 'Ge'] lattice[1].type = ['Si', 'Ge', 'C'] sg = space_group(lattice) invcell = inv(lattice.cell) def get_cells(n): for i in xrange(1, n): yield [[i, 0, 0], [0, 0.5, 0.5], [0, -0.5, 0.5]] for i in xrange(1, n): yield [[i, 0, 0], [0, i, 0], [0, 0, 1]] for i in xrange(1, n): yield [[i, 0, 0], [0, i, 0], [0, 0, i]] yield dot(lattice.cell, [[1, 0, 0], [0, 1, 0], [0, 0, 2]]) for cell in get_cells(8): # create random structure structure = supercell(lattice, cell) hft = HFTransform(lattice, structure) # these are all the translations transforms = Transforms(lattice) permutations = transforms.transformations(hft) assert permutations.shape == (len(sg) - 1, len(structure)) operations = transforms.invariant_ops(structure) assert any(operations) # compute each translation and gets decorations for index, (op, isgood) in enumerate(zip(sg[1:], operations)): if not isgood: continue # Create rotation and figure out its index permutation = zeros(len(structure), dtype='int') - 1 for atom in structure: pos = dot(op[:3], atom.pos) + op[3] newsite = which_site(pos, lattice, invcell) i = hft.index(atom.pos - lattice[atom.site].pos, atom.site) j = hft.index(pos - lattice[newsite].pos, newsite) permutation[i] = j assert all(permutation == permutations[index])
def __init__(self, lattice, spins=None): from pylada.crystal import Structure, space_group from pylada.error import ValueError super(Cluster, self).__init__() if not isinstance(lattice, Structure): raise ValueError( "Lattice should be an instance of " \ "pylada.crystal.Structure.") self.lattice = lattice """ Lattice back-bone on which this cluster exists. Should be an instance of :py:class:`~pylada.crystal.cppwrappers.Structure` or derived. """ self.spins = spins """ Holds array of spins. """ self.spacegroup = getattr(lattice, 'spacegroup', space_group(self.lattice)) """ Symmetry operations. """
def get_symmetries(A, mode=0): import scipy.linalg as spl sA = space_group(A) allsyms = [] if (mode == 0): for s in sA: sym = Symmetry(s) allsyms.append(sym) return allsyms if (mode == 1): allsyms.append(Symmetry(sA[0])) else: allsyms.append([Symmetry(sA[0])]) for i in range(1, len(sA)): s = sA[i] sym = Symmetry(s) found = False for p in allsyms: if (mode == 1): pcomp = p else: pcomp = p[0] if eq_evals(sym, pcomp): found = True if (mode == 2): p.append(sym) break # out of "for p" if not found: if (mode == 1): allsyms.append(sym) else: allsyms.append([sym]) print "found syms:" for eqsymlist in allsyms: print eqsymlist[0].evals # for s in eqsymlist: # print s.evects # print return allsyms
def test_fcc(): """ Test fcc space-group """ from numpy import all, abs, dot from pylada.crystal import space_group, Structure, transform structure = Structure([[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]], m=True).add_atom(0, 0, 0, "Si", m=True) ops = space_group(structure) assert len(ops) == 48 for op in ops: assert op.shape == (4, 3) assert all(abs(op[3, :]) < 1e-8) other = transform(structure, op) assert all(abs(dot(op[:3], structure.cell) - other.cell) < 1e-8) assert getattr(other, 'm', False) for a, atom in zip(structure, other): assert all(abs(dot(op[:3], a.pos) + op[3] - atom.pos) < 1e-8) assert a.type == atom.type assert getattr(atom, 'm', False)
def test_fcc(): """ Test fcc space-group """ from numpy import all, abs, dot from pylada.crystal import space_group, Structure, transform structure = Structure([[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]], m=True).add_atom(0, 0, 0, "Si", m=True) ops = space_group(structure) assert len(ops) == 48 for op in ops: assert op.shape == (4, 3) assert all(abs(op[3,:]) < 1e-8) other = transform(structure, op) assert all(abs(dot(op[:3], structure.cell) - other.cell) < 1e-8) assert getattr(other, 'm', False) for a, atom in zip(structure, other): assert all(abs(dot(op[:3], a.pos) + op[3] - atom.pos) < 1e-8) assert a.type == atom.type assert getattr(atom, 'm', False)
def mess_cell(options, A, tag): # generated some test poscars from A (B ignored) from copy import deepcopy save_struct(options, A, "%s.orig" % tag) # 1) shift 1 atom, save AA = deepcopy(A) AA[1].pos = AA[1].pos + np.array([0.1, 0.1, 0.1]) save_struct(options, AA, "%s.shift_one" % tag) # 2) pick a nontrivial symmetry (of lattice), apply to A, save AA = deepcopy(A) AA.clear() AA.add_atom(0, 0, 0, "Au") asym = space_group(primitive(AA)) sym = asym[1] # print sym AA = transform_cell(sym[0:3, :], A) save_struct(options, AA, "%s.sym" % tag) # 3) shift whole cell AA = deepcopy(A) offset = [1.2, 2.7, 0] for i in range(len(AA)): AA[i].pos = into_cell(AA[i].pos + offset, AA.cell) save_struct(options, AA, "%s.shift_all" % tag) # 4) rotate whole cell from util import rot_euler T = rot_euler(122, 27, -55) AA = transform_cell(T, A) save_struct(options, AA, "%s.rot" % tag) # 5) rotate AND shift offset = [0.5, 0.11, 0.2] for i in range(len(AA)): AA[i].pos = into_cell(AA[i].pos + offset, AA.cell) save_struct(options, AA, "%s.rotshift" % tag)
def test_rotations(cell): from numpy import all, dot, zeros from numpy.linalg import inv from pylada.crystal import binary, supercell, HFTransform, space_group, \ which_site from pylada.decorations import Transforms lattice = binary.zinc_blende() lattice[0].type = ['Si', 'Ge'] lattice[1].type = ['Si', 'Ge', 'C'] sg = space_group(lattice) invcell = inv(lattice.cell) # create random structure structure = supercell(lattice, cell) hft = HFTransform(lattice, structure) # these are all the translations transforms = Transforms(lattice) permutations = transforms.transformations(hft) assert permutations.shape == (len(sg) - 1, len(structure)) operations = transforms.invariant_ops(structure) assert any(operations) # compute each translation and gets decorations for index, (op, isgood) in enumerate(zip(sg[1:], operations)): if not isgood: continue # Create rotation and figure out its index permutation = zeros(len(structure), dtype='int') - 1 for atom in structure: pos = dot(op[:3], atom.pos) + op[3] newsite = which_site(pos, lattice, invcell) i = hft.index(atom.pos - lattice[atom.site].pos, atom.site) j = hft.index(pos - lattice[newsite].pos, newsite) permutation[i] = j assert all(permutation == permutations[index])
def raw_anim(A, B, options): # just animate between A and B, straight up! ### this option is under development savedir = os.getcwd() os.chdir(options.trajdir) structure = pcread.poscar(options.A) structure = pcread.poscar(options.B) print "saving starting anim" Bpath = deepcopy(B) tag = "Bpath0" write_xyz(options, Bpath, tag, options.output_tiles) fout = file("%s.tcl" % tag, "w") write_struct(fout, Bpath, "%s.xyz" % tag, 0, center=False, bonds=True, bond_len=options.bond_len) fout.close() # now write frames dt = 1.0 / (options.frames - 1) t = 0 iter = 0 eps = 1e-6 curpos = [] while t <= 1 + eps: Bpath = deepcopy(B) Bpath.cell = t * A.cell + (1.0 - t) * B.cell for i in range(len(apos)): pos = t * A[i].pos[i] + (1.0 - t) * B[i].pos[i] if (iter == 0): Bpath[i].pos = into_cell( pos, Bpath.cell) # then make sure it's _in_ the unit cell curpos.append(Bpath[i].pos) else: Bpath[i].pos = closest_to(pos, Bpath.cell, curpos[i]) curpos[i] = Bpath[i].pos if (iter == 0): ## testing/bug fixing from pylada.crystal import space_group, primitive from pylada.math import gruber Btest = primitive(Bpath) g = gruber(Btest.cell) print "src has cell:" print Btest.cell # print g Btest = supercell(Btest, g) spacegroup = space_group(Btest) sg = spglib.get_spacegroup(Btest, symprec=1e-4, angle_tolerance=2.0) print "src has %d syms and sg %s" % (len(spacegroup), str(sg)) Bstart = deepcopy(Bpath) sg = spglib.get_spacegroup(Bpath, symprec=1e-4, angle_tolerance=2.0) # sg = spglib.get_spacegroup(Bpath, symprec=1e-1, angle_tolerance=10.0) ### debugging print t, sg, tag if (iter == options.frames - 1): ## testing/bug fixing from pylada.crystal import space_group, primitive from pylada.math import gruber Btest = primitive(Bpath) g = gruber(Btest.cell) print "target has cell:" print Btest.cell # print g Btest = supercell(Btest, g) spacegroup = space_group(Btest) sg = spglib.get_spacegroup(Btest, symprec=1e-4, angle_tolerance=2.0) print "target has %d syms and sg %s" % (len(spacegroup), str(sg)) Bend = deepcopy(Bpath) tag = "traj.%d" % iter write_xyz(options, Bpath, tag, options.output_tiles) fout = file("%s.tcl" % tag, "w") write_struct(fout, Bpath, "%s.xyz" % tag, 0, center=False, bonds=True, bond_len=options.bond_len) fout.close() # write poscar we can analyze later # bigB = supercell(Bpath, np.dot(eye2,Bpath.cell)) # for writing a big poscar with open("%s.POSCAR" % tag, "w") as f: pcwrite.poscar(Bpath, f, vasp5=True) t += dt iter += 1 os.chdir(savedir) if (options.verbose > 2): write_tcl(options, Bend, Bstart, pairs[1], "pairs") # some special work to verify we really arrived at B: # Borig = pcread.poscar(options.A) # M = np.dot(Borig.cell, npl.inv(Bpath.cell)) # Bfinal = transform_cell(M,Bpath) # bigB = supercell(Bfinal, np.dot(eye2,Bfinal.cell)) ## this is a special "doubling" test # with open("final.POSCAR", "w") as f: pcwrite.poscar(bigB, f, vasp5=True) # with open("final.POSCAR", "w") as f: pcwrite.poscar(Bfinal, f, vasp5=True) # sg = spglib.get_spacegroup(Bfinal, symprec=1e-4, angle_tolerance=2.0) ## this is "B in A coords" # print "spacegroup of final structure: ", sg sg = spglib.get_spacegroup(B, symprec=1e-4, angle_tolerance=2.0) print "spacegroup of initial structure (B, [Bflip in code]) ", sg sg = spglib.get_spacegroup(A, symprec=1e-4, angle_tolerance=2.0) print "spacegroup of target structure (A) ", sg
def make_anim(A, B, Tm, shift, pairs, options): # combined view of the unit call and atom transforms # A is target, B is src, after src has been rotated and its unit cell axes permuted so that they # "most align" with those of A. Then transform is just two parts: first is unit cell Tform "Tm" # next is mapping in pairs ### no longer true: which is expressed in 3N-dim space as bigA. from copy import deepcopy from util import write_struct, write_xyz, transform_cell, write_tcl if options.verbose > 1: print "Exploring minimal path we have discovered..." # the results come out a little convoluated b/c of all the steps, so here we gather the # actual start and finish positions. details = False print B.cell print "maps to" print A.cell print "with internal atom shift" print shift print "and atom idx pairing" ppidx = pairs[0] ppos = pairs[1] ainv = npl.inv(A.cell) apos = [] bpos = [] for i in range(len(ppidx)): p = ppidx[i] q = ppos[i] print p, q ##, into_cell(np.dot(B.cell, np.dot(ainv, q[4])), B.cell) apos.append(q[3]) # target atom position bpos.append(q[4]) # src atom position if (options.verbose > 2): print "and A is just" print A.cell for a in A: print a.pos, into_cell(a.pos, A.cell) print "and B is just" print B.cell for b in B: print b.pos, into_cell(b.pos, B.cell) if (not os.path.exists(options.trajdir)): os.mkdir(options.trajdir) savedir = os.getcwd() os.chdir(options.trajdir) if (options.verbose > 1): print "saving starting anim" Bpath = deepcopy(B) tag = "Bpath0" write_xyz(options, Bpath, tag, options.output_tiles) fout = file("%s.tcl" % tag, "w") write_struct(fout, Bpath, "%s.xyz" % tag, 0, center=False, bonds=True, bond_len=options.bond_len) fout.close() # now write frames eye2 = 2.0 * np.identity(3) # for writing a big cell if we want dt = 1.0 / (options.frames - 1) t = 0 iter = 0 eps = 1e-6 curpos = [] while t <= 1 + eps: Bpath = deepcopy(B) Bpath.cell = t * A.cell + (1.0 - t) * B.cell for i in range(len(apos)): p = t * apos[i] + (1.0 - t) * bpos[ i] # this is an abs position, but in A's frame of reference (both apos and bpos are created with # B.cell transformed to A.cell. Here we are mapping to cells in between original B.cell and A.cell) # Note apos and bpos are not taken directly from A, B input cells but are part of the "pairing" data c = np.dot(ainv, p) # so get the coords pos = np.dot(Bpath.cell, c) # and express it w.r.t. evolving Bpath frame if (iter == 0): Bpath[i].pos = into_cell( pos, Bpath.cell) # then make sure it's _in_ the unit cell curpos.append(Bpath[i].pos) else: Bpath[i].pos = closest_to(pos, Bpath.cell, curpos[i]) curpos[i] = Bpath[i].pos if (iter == 0): ## testing/bug fixing Bstart = deepcopy(Bpath) if (options.verbose > 2): from pylada.crystal import space_group, primitive from pylada.math import gruber Btest = primitive(Bpath) g = gruber(Btest.cell) print "src has primitive cell:" print Btest.cell # print g Btest = supercell(Btest, g) spacegroup = space_group(Btest) sg = spglib.get_spacegroup(Btest, symprec=1e-4, angle_tolerance=2.0) print "src has %d syms and sg %s" % (len(spacegroup), str(sg)) sg = spglib.get_spacegroup(Bpath, symprec=1e-4, angle_tolerance=2.0) # sg = spglib.get_spacegroup(Bpath, symprec=1e-1, angle_tolerance=10.0) ### debugging if (options.verbose > 1): print t, sg, tag if (iter == options.frames - 1): ## testing/bug fixing Bend = deepcopy(Bpath) if (options.verbose > 2): from pylada.crystal import space_group, primitive from pylada.math import gruber Btest = primitive(Bpath) g = gruber(Btest.cell) print "target has primitive cell:" print Btest.cell # print g Btest = supercell(Btest, g) spacegroup = space_group(Btest) sg = spglib.get_spacegroup(Btest, symprec=1e-4, angle_tolerance=2.0) print "target has %d syms and sg %s" % (len(spacegroup), str(sg)) tag = "traj.%d" % iter write_xyz(options, Bpath, tag, options.output_tiles) fout = file("%s.tcl" % tag, "w") write_struct(fout, Bpath, "%s.xyz" % tag, 0, center=False, bonds=True, bond_len=options.bond_len) fout.close() # write poscar we can analyze later # bigB = supercell(Bpath, np.dot(eye2,Bpath.cell)) # for writing a big poscar with open("%s.POSCAR" % tag, "w") as f: pcwrite.poscar(Bpath, f, vasp5=True) t += dt iter += 1 os.chdir(savedir) if (options.verbose > 2): write_tcl(options, Bend, Bstart, pairs[1], "pairs") # some special work to verify we really arrived at B: # Borig = pcread.poscar(options.A) # M = np.dot(Borig.cell, npl.inv(Bpath.cell)) # Bfinal = transform_cell(M,Bpath) # bigB = supercell(Bfinal, np.dot(eye2,Bfinal.cell)) ## this is a special "doubling" test # with open("final.POSCAR", "w") as f: pcwrite.poscar(bigB, f, vasp5=True) # with open("final.POSCAR", "w") as f: pcwrite.poscar(Bfinal, f, vasp5=True) # sg = spglib.get_spacegroup(Bfinal, symprec=1e-4, angle_tolerance=2.0) ## this is "B in A coords" # print "spacegroup of final structure: ", sg sg = spglib.get_spacegroup(B, symprec=1e-4, angle_tolerance=2.0) if (options.verbose > 0): print "spacegroup of initial structure (B, [Bflip in code]) ", sg sg = spglib.get_spacegroup(A, symprec=1e-4, angle_tolerance=2.0) if (options.verbose > 1): print "spacegroup of target structure (A) ", sg
def test_b5(u): """ Test b5 space-group """ from random import random, randint from numpy import all, abs, dot, pi from numpy.linalg import inv, norm from numpy.random import random_sample from pylada.crystal import space_group, Structure, transform from pylada.crystal import which_site x, y = u, 0.25 - u structure = Structure([[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]) \ .add_atom(5.000000e-01, 5.000000e-01, 5.000000e-01, "A") \ .add_atom(5.000000e-01, 2.500000e-01, 2.500000e-01, "A") \ .add_atom(2.500000e-01, 5.000000e-01, 2.500000e-01, "A") \ .add_atom(2.500000e-01, 2.500000e-01, 5.000000e-01, "A") \ .add_atom(8.750000e-01, 8.750000e-01, 8.750000e-01, "B") \ .add_atom(1.250000e-01, 1.250000e-01, 1.250000e-01, "B") \ .add_atom( x, x, x, "X") \ .add_atom( x, y, y, "X") \ .add_atom( y, x, y, "X") \ .add_atom( y, y, x, "X") \ .add_atom( -x, -x, -x, "X") \ .add_atom( -x, -y, -y, "X") \ .add_atom( -y, -x, -y, "X") \ .add_atom(-y, -y, -x, "X") ops = space_group(structure) assert len(ops) == 48 invcell = inv(structure.cell) for op in ops: assert op.shape == (4, 3) other = transform(structure, op) assert all(abs(dot(op[:3], structure.cell) - other.cell) < 1e-8) for a, atom in zip(structure, other): assert all(abs(dot(op[:3], a.pos) + op[3] - atom.pos) < 1e-8) assert a.type == atom.type sites = [] for i, atom in enumerate(structure): pos = dot(op[:3], atom.pos) + op[3] j = which_site(pos, structure, invcell) assert j != -1 sites.append(j) assert len(set(sites)) == len(structure) structure[0], structure[-1] = structure[-1], structure[0] ops = space_group(structure) assert len(ops) == 48 for op in ops: assert op.shape == (4, 3) other = transform(structure, op) assert all(abs(dot(op[:3], structure.cell) - other.cell) < 1e-8) for a, atom in zip(structure, other): assert all(abs(dot(op[:3], a.pos) + op[3] - atom.pos) < 1e-8) assert a.type == atom.type sites = [] for i, atom in enumerate(structure): pos = dot(op[:3], atom.pos) + op[3] j = which_site(pos, structure, invcell) assert j != -1, (i, atom, op) sites.append(j) assert len(set(sites)) == len(structure) # try random rotation, translations, atom swap structure[0], structure[-1] = structure[-1], structure[0] for u in range(10): axis = random_sample((3,)) axis /= norm(axis) rotation = rotation_matrix(pi * random(), axis) translation = random_sample((3,)) other = transform(structure, rotation, translation) for u in range(10): l, m = randint(0, len(structure) - 1), randint(0, len(structure) - 1) a, b = other[l], other[m] other[l], other[m] = b, a invcell = inv(other.cell) ops = space_group(other) for z, op in enumerate(ops): assert op.shape == (4, 3) other2 = transform(other, op) assert all(abs(dot(op[:3], other.cell) - other2.cell) < 1e-8) for a, atom in zip(other, other2): assert all(abs(dot(op[:3], a.pos) + op[3] - atom.pos) < 1e-8) assert a.type == atom.type sites = [] for i, atom in enumerate(other): pos = dot(op[:3], atom.pos) + op[3] j = which_site(pos, other, invcell) if j == -1: print((i, z)) print(atom) print(op) print(pos) print(other) raise Exception() sites.append(j) assert len(set(sites)) == len(other)
def test_b5(u): """ Test b5 space-group """ from random import random, randint from numpy import all, abs, dot, pi from numpy.linalg import inv, norm from numpy.random import random_sample from pylada.crystal import space_group, Structure, transform from pylada.crystal import which_site x, y = u, 0.25 - u structure = Structure([[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]) \ .add_atom(5.000000e-01, 5.000000e-01, 5.000000e-01, "A") \ .add_atom(5.000000e-01, 2.500000e-01, 2.500000e-01, "A") \ .add_atom(2.500000e-01, 5.000000e-01, 2.500000e-01, "A") \ .add_atom(2.500000e-01, 2.500000e-01, 5.000000e-01, "A") \ .add_atom(8.750000e-01, 8.750000e-01, 8.750000e-01, "B") \ .add_atom(1.250000e-01, 1.250000e-01, 1.250000e-01, "B") \ .add_atom( x, x, x, "X") \ .add_atom( x, y, y, "X") \ .add_atom( y, x, y, "X") \ .add_atom( y, y, x, "X") \ .add_atom( -x, -x, -x, "X") \ .add_atom( -x, -y, -y, "X") \ .add_atom( -y, -x, -y, "X") \ .add_atom(-y, -y, -x, "X") ops = space_group(structure) assert len(ops) == 48 invcell = inv(structure.cell) for op in ops: assert op.shape == (4, 3) other = transform(structure, op) assert all(abs(dot(op[:3], structure.cell) - other.cell) < 1e-8) for a, atom in zip(structure, other): assert all(abs(dot(op[:3], a.pos) + op[3] - atom.pos) < 1e-8) assert a.type == atom.type sites = [] for i, atom in enumerate(structure): pos = dot(op[:3], atom.pos) + op[3] j = which_site(pos, structure, invcell) assert j != -1 sites.append(j) assert len(set(sites)) == len(structure) structure[0], structure[-1] = structure[-1], structure[0] ops = space_group(structure) assert len(ops) == 48 for op in ops: assert op.shape == (4, 3) other = transform(structure, op) assert all(abs(dot(op[:3], structure.cell) - other.cell) < 1e-8) for a, atom in zip(structure, other): assert all(abs(dot(op[:3], a.pos) + op[3] - atom.pos) < 1e-8) assert a.type == atom.type sites = [] for i, atom in enumerate(structure): pos = dot(op[:3], atom.pos) + op[3] j = which_site(pos, structure, invcell) assert j != -1, (i, atom, op) sites.append(j) assert len(set(sites)) == len(structure) # try random rotation, translations, atom swap structure[0], structure[-1] = structure[-1], structure[0] for u in range(10): axis = random_sample((3, )) axis /= norm(axis) rotation = rotation_matrix(pi * random(), axis) translation = random_sample((3, )) other = transform(structure, rotation, translation) for u in range(10): l, m = randint(0, len(structure) - 1), randint(0, len(structure) - 1) a, b = other[l], other[m] other[l], other[m] = b, a invcell = inv(other.cell) ops = space_group(other) for z, op in enumerate(ops): assert op.shape == (4, 3) other2 = transform(other, op) assert all(abs(dot(op[:3], other.cell) - other2.cell) < 1e-8) for a, atom in zip(other, other2): assert all(abs(dot(op[:3], a.pos) + op[3] - atom.pos) < 1e-8) assert a.type == atom.type sites = [] for i, atom in enumerate(other): pos = dot(op[:3], atom.pos) + op[3] j = which_site(pos, other, invcell) if j == -1: print((i, z)) print(atom) print(op) print(pos) print(other) raise Exception() sites.append(j) assert len(set(sites)) == len(other)