def test_segfault_issue_20(): from os.path import join, dirname from pylada.crystal import Structure, primitive, read from pylada import error sc = read.poscar(join(dirname(__file__), 'issue20.poscar')) assert abs(primitive(sc, tolerance=1e-8).volume - sc.volume) < 1e-8 with raises(error.RuntimeError): primitive(sc, tolerance=1e-5)
def icsd_cif_b(filename): from os.path import basename from numpy import dot, transpose from pylada.crystal import Structure, primitive from . import readCif rdr = readCif.CifReader(0, filename) # buglevel = 0 vaspMap = rdr.getVaspMap() cellBasis = vaspMap['cellBasis'] structure = Structure( transpose(cellBasis), scale=1, name=basename(filename)) usyms = vaspMap['uniqueSyms'] posVecs = vaspMap['posVecs'] # multiplicities = num atoms of each type. mults = [len(x) for x in posVecs] # For each unique type of atom ... for ii in range(len(usyms)): # For each atom of that type ... for jj in range(mults[ii]): atpos = dot(transpose(cellBasis), posVecs[ii][jj]) structure.add_atom(atpos[0], atpos[1], atpos[2], usyms[ii]) prim = primitive(structure) logger.info(" crystal/read: icsd_cif_b: structure: %s" % structure) return prim
def test_primitive(cell): """ Tests whether primitivization works. """ from numpy import abs, dot from numpy.linalg import inv from pylada.crystal import supercell, Structure, are_periodic_images as api, primitive, \ is_primitive lattice = Structure(0.0, 0.5, 0.5, 0.5, 0.0, 0.5, 0.5, 0.5, 0.0, scale=2.0, m=True) \ .add_atom(0, 0, 0, "As") \ .add_atom(0.25, 0.25, 0.25, ['In', 'Ga'], m=True) structure = supercell(lattice, dot(lattice.cell, cell)) assert not is_primitive(structure) structure = primitive(structure, 1e-8) assert is_primitive(structure) assert abs(structure.volume - lattice.volume) < 1e-8 assert len(structure) == len(lattice) assert is_integer(dot(structure.cell, inv(lattice.cell))) assert is_integer(dot(lattice.cell, inv(structure.cell))) invcell = inv(lattice.cell) for atom in structure: assert api(lattice[atom.site].pos, atom.pos, invcell) assert atom.type == lattice[atom.site].type assert getattr(lattice[atom.site], 'm', False) == getattr(atom, 'm', False) assert (getattr(atom, 'm', False) or atom.site == 0)
def test_primitive(cell): """ Tests whether primitivization works. """ from numpy import abs, dot from numpy.linalg import inv from pylada.crystal import supercell, Structure, are_periodic_images as api, primitive, \ is_primitive lattice = Structure(0.0, 0.5, 0.5, 0.5, 0.0, 0.5, 0.5, 0.5, 0.0, scale=2.0, m=True ) \ .add_atom(0, 0, 0, "As") \ .add_atom(0.25, 0.25, 0.25, ['In', 'Ga'], m=True) structure = supercell(lattice, dot(lattice.cell, cell)) assert not is_primitive(structure) structure = primitive(structure, 1e-8) assert is_primitive(structure) assert abs(structure.volume - lattice.volume) < 1e-8 assert len(structure) == len(lattice) assert is_integer(dot(structure.cell, inv(lattice.cell))) assert is_integer(dot(lattice.cell, inv(structure.cell))) invcell = inv(lattice.cell) for atom in structure: assert api(lattice[atom.site].pos, atom.pos, invcell) assert atom.type == lattice[atom.site].type assert getattr(lattice[atom.site], 'm', False) == getattr(atom, 'm', False) assert (getattr(atom, 'm', False) or atom.site == 0)
def get_interstitials(structure, ttol=0.5): """ Function to return unique interstitial sites in the given structure Parameters structure = poscar file (using read.poscar) ttol = tolerance Returns unique_int_list = list of unique intestital sites (cartesian (x,y,z) as list) in the given structure """ s = deepcopy(structure) prim = primitive(s) spg = spglib.get_spacegroup(prim, 0.1) ### Step 1: get unique sites in the primitive of the given structure uniq_sites = get_unique_wyckoff(prim) ### Step 2: get all interstitial sites from Voronoi method ints2 = get_all_interstitials(prim, uniq_sites) ### get interstital sites within primitive cell ints_prim = get_ints_in_prim_cell(prim, ints2) ### Step 3: get unique interstitials after symmetry analysis ints3 = get_unique_ints(prim, ints_prim, ttol=ttol) return ints3
def icsd_cif_b( filename): from os.path import basename from numpy import dot, transpose from pylada.crystal import Structure, primitive from pylada.misc import bugLev from . import readCif rdr = readCif.CifReader( 0, filename) # buglevel = 0 vaspMap = rdr.getVaspMap() cellBasis = vaspMap['cellBasis'] structure = Structure( transpose( cellBasis), scale = 1, name = basename( filename)) usyms = vaspMap['uniqueSyms'] posVecs = vaspMap['posVecs'] # multiplicities = num atoms of each type. mults = [len(x) for x in posVecs] if bugLev >= 5: print " crystal/read: len(usyms): %d usyms: %s" \ % (len( usyms), usyms,) print " crystal/read: len(posVecs): %d" % (len(posVecs),) print " crystal/read: len(mults): %d mults: %s" \ % (len( mults), mults,) # For each unique type of atom ... for ii in range( len( usyms)): if bugLev >= 5: print " crystal/read: icsd_cif_b: ii: ", ii, \ " usym: ", usyms[ii], \ " mult: ", mults[ii], \ " posVecs: ", posVecs[ii] # crystal/read: i: 0 symbol: Mo len position: 2 # For each atom of that type ... for jj in range( mults[ii]): atpos = dot( transpose( cellBasis), posVecs[ii][jj]) if bugLev >= 5: print " jj: ", jj, " pos: ", posVecs[ii][jj] print " atpos: ", atpos # j: 0 pos: [0.3333, 0.6666000000000001, 0.25] # atpos: [ 6.32378655e-16 1.81847148e+00 3.07500000e+00] structure.add_atom( atpos[0], atpos[1], atpos[2], usyms[ii]) if bugLev >= 2: print " crystal/read: icsd_cif_b: structure:\n", structure prim = primitive( structure) if bugLev >= 2: print " crystal/read: icsd_cif_b: primitive structure:\n", prim return prim
def test_incorrect_check_issue(): """ potential cell not discarded despite being singular """ from os.path import join, dirname from pylada.crystal import Structure, primitive, read from pylada import error sc = read.poscar(join(dirname(__file__), 'not_singular_cell.poscar')) # calling primitive used to throw an exception assert abs(primitive(sc, tolerance=1e-8).volume - 47.57971180103934) < 1e-8
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_noop_if_primitive(): from numpy import allclose from pylada.crystal import Structure, primitive, is_primitive original = Structure(4.18742, 2.09371, 2.09376, -1.36476e-06, -3.62642, -1.20883, -1.58443e-05, -1.77396e-05, -10.0923, scale=1, name="icsd_042545.cif")\ .add_atom(4.18743, -2.41762, -1.94751, "Bi")\ .add_atom(4.18746, -2.41763, -8.1448, "Bi")\ .add_atom(2.09376, -1.20883, -10.0923, "Se")\ .add_atom(6.28117, -3.62644, -6.57868, "Se")\ .add_atom(2.09372, -1.20882, -3.51363, "Se") assert is_primitive(original) p = primitive(original) assert allclose(p.cell, original.cell) for a, b in zip(p, original): assert allclose(a.pos, b.pos) assert a.type == b.type
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 mess_supercells(options, A): # generate arbitrary supercells and mess_up() both. # tools should find they are the all the same. from pylada import enum from pylada.crystal import primitive A = primitive(A) # Acells = enum.supercells(A,[2]) # Acells = Acells[2] # print Acells # if len(Acells) == 1: # return # custom to do a better job making hard cells! Acells = [ np.array([[2, 0, 0], [3, 2, 0], [4, 1, 1]]), np.array([[1, 0, 0], [3, 4, 0], [2, 0, 1]]) ] print Acells for idx in range(len(Acells) - 2, len(Acells)): AA = supercell(A, np.dot(A.cell, Acells[idx])) mess_cell(options, AA, "%d" % idx)
def icsd_cif_a(filename): """ Reads lattice from the ICSD \*cif files. It will not work in the case of other \*cif. It is likely to produce wrong output if the site occupations are fractional. If the occupation is > 0.5 it will treat it as 1 and in the case occupation < 0.5 it will treat it as 0 and it will accept all occupation = 0.5 as 1 and create a mess! """ from pylada import logger import re from os.path import basename from numpy.linalg import norm from numpy import array, transpose from numpy import pi, sin, cos, sqrt, dot lines = open(filename, 'r').readlines() logger.info("crystal/read: icsd_cif_a: %s" % filename) sym_big = 0 sym_end = 0 pos_big = 0 pos_end = 0 for l in lines: x = l.split() if len(x) > 0: # CELL if x[0] == '_cell_length_a': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) a = float(x[-1][:index]) if x[0] == '_cell_length_b': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) b = float(x[-1][:index]) if x[0] == '_cell_length_c': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) c = float(x[-1][:index]) if x[0] == '_cell_angle_alpha': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) alpha = float(x[-1][:index]) if x[0] == '_cell_angle_beta': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) beta = float(x[-1][:index]) if x[0] == '_cell_angle_gamma': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) gamma = float(x[-1][:index]) # SYMMETRY OPERATIONS if len(x) > 0 and x[0] == '_symmetry_equiv_pos_as_xyz': sym_big = lines.index(l) if len(x) > 0 and x[0] == '_atom_type_symbol': sym_end = lines.index(l) # WYCKOFF POSITIONS if len(x) > 0 and x[0] == '_atom_site_attached_hydrogens': pos_big = lines.index(l) if len(x) > 0 and x[0] == '_atom_site_B_iso_or_equiv': pos_big = lines.index(l) if len(x) > 0 and x[0] == '_atom_site_U_iso_or_equiv': pos_big = lines.index(l) if len(x) > 0 and x[0] == '_atom_site_0_iso_or_equiv': pos_big = lines.index(l) # if pos_end == 0 and l in ['\n', '\r\n'] and lines.index(l) > pos_big: if pos_end == 0 and pos_big > 0 \ and (l in ['\n', '\r\n'] or l.startswith('#')) \ and lines.index(l) > pos_big: pos_end = lines.index(l) # _symmetry_equiv_pos_* lines are like: # 1 'x, x-y, -z+1/2' logger.debug("crystal/read: icsd_cif_a: sym_big: %s" % sym_big) logger.debug("crystal/read: icsd_cif_a: sym_end: %s" % sym_end) symm_ops = ['(' + x.split()[1][1:] + x.split()[2] + x.split()[3][:-1] + ')' for x in lines[sym_big + 1:sym_end - 1]] logger.debug("crystal/read: icsd_cif_a: symm_ops a: %s" % symm_ops) # ['(x,x-y,-z+1/2)', '(-x+y,y,-z+1/2)', ...] # Insert decimal points after integers symm_ops = [re.sub(r'(\d+)', r'\1.', x) for x in symm_ops] logger.debug("crystal/read: icsd_cif_a: symm_ops b: %s" % symm_ops) # ['(x,x-y,-z+1./2.)', '(-x+y,y,-z+1./2.)', ...] # _atom_site_* lines are like: # Mo1 Mo4+ 2 c 0.3333 0.6667 0.25 1. 0 logger.debug("crystal/read: icsd_cif_a: pos_big: %s" % pos_big) logger.debug("crystal/read: icsd_cif_a: pos_end: %s" % pos_end) wyckoff = [[x.split()[0], [x.split()[4], x.split()[5], x.split()[6]], x.split()[7]] for x in lines[pos_big + 1:pos_end]] logger.debug("crystal/read: icsd_cif_a: wyckoff a: %s" % wyckoff) # [['Mo1', ['0.3333', '0.6667', '0.25'], '1.'], ['S1', ['0.3333', '0.6667', '0.621(4)'], '1.']] wyckoff = [w for w in wyckoff if int(float(w[-1][:4]) + 0.5) != 0] logger.debug("crystal/read: icsd_cif_a: wyckoff b: %s" % wyckoff) # [['Mo1', ['0.3333', '0.6667', '0.25'], '1.'], ['S1', ['0.3333', '0.6667', '0.621(4)'], '1.']] # Setting up a good wyckoff list for w in wyckoff: # Strip trailing numerals from w[0] == 'Mo1' pom = 0 for i in range(len(w[0])): try: int(w[0][i]) if pom == 0: pom = i except: pass w[0] = w[0][:pom] # Strip trailing standard uncertainty, if any, from w[1], ..., w[3] for i in range(3): if '(' in w[1][i]: index = w[1][i].index('(') else: index = len(w[1][i]) w[1][i] = float(w[1][i][:index]) # Delete w[4] del w[-1] ########################################## # List of unique symbols ["Mo", "S"] symbols = list({w[0] for w in wyckoff}) logger.debug("crystal/read: icsd_cif_a: symbols: %s" % symbols) # List of position vectors for each symbol positions = [[] for i in range(len(symbols))] for w in wyckoff: symbol = w[0] x, y, z = w[1][0], w[1][1], w[1][2] logger.debug("symbol: %s x: %s y: %s z: %s" % (symbol, x, y, z)) for i in range(len(symm_ops)): # Set pom = new position based on symmetry transform pom = list(eval(symm_ops[i])) logger.debug("i: %s pom a: %s" % (i, pom)) # [0.3333, -0.3334, 0.25] # Move positions to range [0,1]: for j in range(len(pom)): if pom[j] < 0.: pom[j] = pom[j] + 1. if pom[j] >= 0.999: pom[j] = pom[j] - 1. logger.debug("i: %s pom b: %s" % (i, pom)) # [0.3333, 0.6666, 0.25] # If pom is not in positions[symbol], append pom if not any(norm(array(u) - array(pom)) < 0.01 for u in positions[symbols.index(symbol)]): ix = symbols.index(symbol) positions[ix].append(pom) logger.debug("new positions for %s: %s" % (symbol, repr(positions[ix]))) ################ CELL #################### a1 = a * array([1., 0., 0.]) a2 = b * array([cos(gamma * pi / 180.), sin(gamma * pi / 180.), 0.]) c1 = c * cos(beta * pi / 180.) c2 = c / sin(gamma * pi / 180.) * (-cos(beta * pi / 180.) * cos(gamma * pi / 180.) + cos(alpha * pi / 180.)) a3 = array([c1, c2, sqrt(c**2 - (c1**2 + c2**2))]) cell = array([a1, a2, a3]) logger.debug("crystal/read: icsd_cif_a: a1: %s" % a1) logger.debug("crystal/read: icsd_cif_a: a2: %s" % a2) logger.debug("crystal/read: icsd_cif_a: a3: %s" % a3) ########################################## from pylada.crystal import Structure, primitive logger.debug("crystal/read: icsd_cif_a: cell: %s" % cell) structure = Structure( transpose(cell), scale=1, name=basename(filename)) for i in range(len(symbols)): logger.debug("crystal/read: icsd_cif_a: i: %s symbol: %s len(position): %i" % ( i, symbols[i], len(positions[i]) )) # crystal/read: i: 0 symbol: Mo len position: 2 for j in range(len(positions[i])): atpos = dot(transpose(cell), positions[i][j]) logger.debug("j: %s pos: %s" % (j, positions[i][j])) logger.debug("atpos: " % atpos) # j: 0 pos: [0.3333, 0.6666000000000001, 0.25] # atpos: [ 6.32378655e-16 1.81847148e+00 3.07500000e+00] structure.add_atom(atpos[0], atpos[1], atpos[2], symbols[i]) logger.info("crystal/read: icsd_cif_a: structure: %s" % structure) prim = primitive(structure) logger.info("crystal/read: icsd_cif_a: primitive structure: %s" % prim) return prim
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 icsd_cif_a(filename, make_primitive=True): """ Reads lattice from the ICSD \*cif files. It will not work in the case of other \*cif. It is likely to produce wrong output if the site occupations are fractional. If the occupation is > 0.5 it will treat it as 1 and in the case occupation < 0.5 it will treat it as 0 and it will accept all occupation = 0.5 as 1 and create a mess! """ from pylada import logger import re from copy import deepcopy from os.path import basename from numpy.linalg import norm from numpy import array, transpose from numpy import pi, sin, cos, sqrt, dot lines = open(filename, 'r').readlines() logger.info("crystal/read: icsd_cif_a: %s" % filename) sym_big = 0 sym_end = 0 pos_big = 0 pos_end = 0 for l in lines: x = l.split() if len(x) > 0: # CELL if x[0] == '_cell_length_a': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) a = float(x[-1][:index]) if x[0] == '_cell_length_b': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) b = float(x[-1][:index]) if x[0] == '_cell_length_c': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) c = float(x[-1][:index]) if x[0] == '_cell_angle_alpha': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) alpha = float(x[-1][:index]) if x[0] == '_cell_angle_beta': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) beta = float(x[-1][:index]) if x[0] == '_cell_angle_gamma': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) gamma = float(x[-1][:index]) # SYMMETRY OPERATIONS if len(x) > 0 and x[0] == '_symmetry_equiv_pos_as_xyz': sym_big = lines.index(l) if len(x) > 0 and x[0] == '_atom_type_symbol': sym_end = lines.index(l) # WYCKOFF POSITIONS if len(x) > 0 and x[0] == '_atom_site_attached_hydrogens': pos_big = lines.index(l) if len(x) > 0 and x[0] == '_atom_site_B_iso_or_equiv': pos_big = lines.index(l) if len(x) > 0 and x[0] == '_atom_site_U_iso_or_equiv': pos_big = lines.index(l) if len(x) > 0 and x[0] == '_atom_site_0_iso_or_equiv': pos_big = lines.index(l) # if pos_end == 0 and l in ['\n', '\r\n'] and lines.index(l) > pos_big: if pos_end == 0 and pos_big > 0 \ and (l in ['\n', '\r\n'] or l.startswith('#')) \ and lines.index(l) > pos_big: pos_end = lines.index(l) # _symmetry_equiv_pos_* lines are like: # 1 'x, x-y, -z+1/2' logger.debug("crystal/read: icsd_cif_a: sym_big: %s" % sym_big) logger.debug("crystal/read: icsd_cif_a: sym_end: %s" % sym_end) symm_ops = ['(' + x.split()[1][1:] + x.split()[2] + x.split()[3][:-1] + ')' for x in lines[sym_big + 1:sym_end - 1]] logger.debug("crystal/read: icsd_cif_a: symm_ops a: %s" % symm_ops) # ['(x,x-y,-z+1/2)', '(-x+y,y,-z+1/2)', ...] # Insert decimal points after integers symm_ops = [re.sub(r'(\d+)', r'\1.', x) for x in symm_ops] logger.debug("crystal/read: icsd_cif_a: symm_ops b: %s" % symm_ops) # ['(x,x-y,-z+1./2.)', '(-x+y,y,-z+1./2.)', ...] # _atom_site_* lines are like: # Mo1 Mo4+ 2 c 0.3333 0.6667 0.25 1. 0 logger.debug("crystal/read: icsd_cif_a: pos_big: %s" % pos_big) logger.debug("crystal/read: icsd_cif_a: pos_end: %s" % pos_end) wyckoff = [[x.split()[0], [x.split()[4], x.split()[5], x.split()[6]], x.split()[7]] for x in lines[pos_big + 1:pos_end]] logger.debug("crystal/read: icsd_cif_a: wyckoff a: %s" % wyckoff) # [['Mo1', ['0.3333', '0.6667', '0.25'], '1.'], ['S1', ['0.3333', '0.6667', '0.621(4)'], '1.']] wyckoff = [w for w in wyckoff if int(float(w[-1][:4]) + 0.5) != 0] logger.debug("crystal/read: icsd_cif_a: wyckoff b: %s" % wyckoff) # [['Mo1', ['0.3333', '0.6667', '0.25'], '1.'], ['S1', ['0.3333', '0.6667', '0.621(4)'], '1.']] # Setting up a good wyckoff list for w in wyckoff: # Strip trailing numerals from w[0] == 'Mo1' pom = 0 for i in range(len(w[0])): try: int(w[0][i]) if pom == 0: pom = i except: pass w[0] = w[0][:pom] # Strip trailing standard uncertainty, if any, from w[1], ..., w[3] for i in range(3): if '(' in w[1][i]: index = w[1][i].index('(') else: index = len(w[1][i]) w[1][i] = float(w[1][i][:index]) # Delete w[4] del w[-1] ########################################## # List of unique symbols ["Mo", "S"] symbols = list({w[0] for w in wyckoff}) logger.debug("crystal/read: icsd_cif_a: symbols: %s" % symbols) # List of position vectors for each symbol positions = [[] for i in range(len(symbols))] for w in wyckoff: symbol = w[0] x, y, z = w[1][0], w[1][1], w[1][2] logger.debug("symbol: %s x: %s y: %s z: %s" % (symbol, x, y, z)) for i in range(len(symm_ops)): # Set pom = new position based on symmetry transform pom = list(eval(symm_ops[i])) logger.debug("i: %s pom a: %s" % (i, pom)) # [0.3333, -0.3334, 0.25] # Move positions to range [0,1]: for j in range(len(pom)): if pom[j] < 0.: pom[j] = pom[j] + 1. if pom[j] >= 0.999: pom[j] = pom[j] - 1. logger.debug("i: %s pom b: %s" % (i, pom)) # [0.3333, 0.6666, 0.25] # If pom is not in positions[symbol], append pom if not any(norm(array(u) - array(pom)) < 0.01 for u in positions[symbols.index(symbol)]): ix = symbols.index(symbol) positions[ix].append(pom) logger.debug("new positions for %s: %s" % (symbol, repr(positions[ix]))) ################ CELL #################### a1 = a * array([1., 0., 0.]) a2 = b * array([cos(gamma * pi / 180.), sin(gamma * pi / 180.), 0.]) c1 = c * cos(beta * pi / 180.) c2 = c / sin(gamma * pi / 180.) * (-cos(beta * pi / 180.) * cos(gamma * pi / 180.) + cos(alpha * pi / 180.)) a3 = array([c1, c2, sqrt(c**2 - (c1**2 + c2**2))]) cell = array([a1, a2, a3]) logger.debug("crystal/read: icsd_cif_a: a1: %s" % a1) logger.debug("crystal/read: icsd_cif_a: a2: %s" % a2) logger.debug("crystal/read: icsd_cif_a: a3: %s" % a3) ########################################## from pylada.crystal import Structure, primitive logger.debug("crystal/read: icsd_cif_a: cell: %s" % cell) structure = Structure( transpose(cell), scale=1, name=basename(filename)) for i in range(len(symbols)): logger.debug("crystal/read: icsd_cif_a: i: %s symbol: %s len(position): %i" % ( i, symbols[i], len(positions[i]) )) # crystal/read: i: 0 symbol: Mo len position: 2 for j in range(len(positions[i])): atpos = dot(transpose(cell), positions[i][j]) logger.debug("j: %s pos: %s" % (j, positions[i][j])) logger.debug("atpos: " % atpos) # j: 0 pos: [0.3333, 0.6666000000000001, 0.25] # atpos: [ 6.32378655e-16 1.81847148e+00 3.07500000e+00] structure.add_atom(atpos[0], atpos[1], atpos[2], symbols[i]) logger.info("crystal/read: icsd_cif_a: structure: %s" % structure) if make_primitive: prim = primitive(structure) else: prim = deepcopy(structure) logger.info("crystal/read: icsd_cif_a: primitive structure: %s" % prim) return prim
def icsd_cif_a( filename): """ Reads lattice from the ICSD \*cif files. It will not work in the case of other \*cif. It is likely to produce wrong output if the site occupations are fractional. If the occupation is > 0.5 it will treat it as 1 and in the case occupation < 0.5 it will treat it as 0 and it will accept all occupation = 0.5 as 1 and create a mess! """ import re from os.path import basename from numpy.linalg import norm from numpy import array, transpose from numpy import pi, sin, cos, sqrt, dot from pylada.misc import bugLev lines = open(filename,'r').readlines() if bugLev >= 2: print " crystal/read: icsd_cif_a: filename: ", filename sym_big = 0 sym_end = 0 pos_big = 0 pos_end = 0 for l in lines: x = l.split() if len(x)>0: # CELL if x[0] == '_cell_length_a': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) a = float(x[-1][:index]) if x[0] == '_cell_length_b': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) b = float(x[-1][:index]) if x[0] == '_cell_length_c': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) c = float(x[-1][:index]) if x[0] == '_cell_angle_alpha': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) alpha = float(x[-1][:index]) if x[0] == '_cell_angle_beta': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) beta = float(x[-1][:index]) if x[0] == '_cell_angle_gamma': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) gamma = float(x[-1][:index]) # SYMMETRY OPERATIONS if len(x)>0 and x[0] == '_symmetry_equiv_pos_as_xyz': sym_big = lines.index(l) if len(x)>0 and x[0] == '_atom_type_symbol': sym_end = lines.index(l) # WYCKOFF POSITIONS if len(x)>0 and x[0] == '_atom_site_attached_hydrogens': pos_big = lines.index(l) if len(x)>0 and x[0] == '_atom_site_B_iso_or_equiv': pos_big = lines.index(l) if len(x)>0 and x[0] == '_atom_site_U_iso_or_equiv': pos_big = lines.index(l) if len(x)>0 and x[0] == '_atom_site_0_iso_or_equiv': pos_big = lines.index(l) #if pos_end == 0 and l in ['\n', '\r\n'] and lines.index(l) > pos_big: if pos_end == 0 and pos_big > 0 \ and (l in ['\n', '\r\n'] or l.startswith('#')) \ and lines.index(l) > pos_big: pos_end = lines.index(l) # _symmetry_equiv_pos_* lines are like: # 1 'x, x-y, -z+1/2' if bugLev >= 5: print " crystal/read: icsd_cif_a: sym_big: ", sym_big print " crystal/read: icsd_cif_a: sym_end: ", sym_end symm_ops = [ '(' + x.split()[1][1:] + x.split()[2] + x.split()[3][:-1] + ')'\ for x in lines[sym_big+1:sym_end-1] ] if bugLev >= 5: print " crystal/read: icsd_cif_a: symm_ops a: ", symm_ops # ['(x,x-y,-z+1/2)', '(-x+y,y,-z+1/2)', ...] # Insert decimal points after integers symm_ops = [re.sub(r'(\d+)', r'\1.', x) for x in symm_ops] if bugLev >= 5: print " crystal/read: icsd_cif_a: symm_ops b: ", symm_ops # ['(x,x-y,-z+1./2.)', '(-x+y,y,-z+1./2.)', ...] # _atom_site_* lines are like: # Mo1 Mo4+ 2 c 0.3333 0.6667 0.25 1. 0 if bugLev >= 5: print " crystal/read: icsd_cif_a: pos_big: ", pos_big print " crystal/read: icsd_cif_a: pos_end: ", pos_end wyckoff = [ [x.split()[0],[x.split()[4],x.split()[5],x.split()[6]],x.split()[7]]\ for x in lines[pos_big+1:pos_end] ] if bugLev >= 5: print " crystal/read: icsd_cif_a: wyckoff a: ", wyckoff # [['Mo1', ['0.3333', '0.6667', '0.25'], '1.'], ['S1', ['0.3333', '0.6667', '0.621(4)'], '1.']] wyckoff = [w for w in wyckoff if int(float(w[-1][:4])+0.5) != 0] if bugLev >= 5: print " crystal/read: icsd_cif_a: wyckoff b: ", wyckoff # [['Mo1', ['0.3333', '0.6667', '0.25'], '1.'], ['S1', ['0.3333', '0.6667', '0.621(4)'], '1.']] ############## Setting up a good wyckoff list for w in wyckoff: # Strip trailing numerals from w[0] == 'Mo1' pom = 0 for i in range(len(w[0])): try: int(w[0][i]) if pom ==0: pom=i except: pass w[0] = w[0][:pom] # Strip trailing standard uncertainty, if any, from w[1], ..., w[3] for i in range(3): if '(' in w[1][i]: index = w[1][i].index('(') else: index = len(w[1][i]) w[1][i] = float(w[1][i][:index]) # Delete w[4] del w[-1] ########################################## # List of unique symbols ["Mo", "S"] symbols = list(set([w[0] for w in wyckoff])) if bugLev >= 5: print " crystal/read: icsd_cif_a: symbols: ", symbols # List of position vectors for each symbol positions = [[] for i in range(len(symbols))] for w in wyckoff: symbol = w[0] x,y,z = w[1][0],w[1][1],w[1][2] if bugLev >= 5: print " symbol: ", symbol, " x: ", x, " y: ", y, " z: ", z for i in range(len(symm_ops)): # Set pom = new position based on symmetry transform pom = list(eval(symm_ops[i])) if bugLev >= 5: print " i: ", i, " pom a: ", pom # [0.3333, -0.3334, 0.25] # Move positions to range [0,1]: for j in range(len(pom)): if pom[j] < 0.: pom[j] = pom[j]+1. if pom[j] >= 0.999: pom[j] = pom[j]-1. if bugLev >= 5: print " i: ", i, " pom b: ", pom # [0.3333, 0.6666, 0.25] # If pom is not in positions[symbol], append pom if not any(norm(array(u)-array(pom)) < 0.01 for u in positions[symbols.index(symbol)]): ix = symbols.index(symbol) positions[ix].append(pom) if bugLev >= 5: print " new positions for ", symbol, ": ", positions[ix] ################ CELL #################### a1 = a*array([1.,0.,0.]) a2 = b*array([cos(gamma*pi/180.),sin(gamma*pi/180.),0.]) c1 = c*cos(beta*pi/180.) c2 = c/sin(gamma*pi/180.)*(-cos(beta*pi/180.)*cos(gamma*pi/180.) + cos(alpha*pi/180.)) a3 = array([c1, c2, sqrt(c**2-(c1**2+c2**2))]) cell = array([a1,a2,a3]) if bugLev >= 2: print " crystal/read: icsd_cif_a: a1: ", a1 print " crystal/read: icsd_cif_a: a2: ", a2 print " crystal/read: icsd_cif_a: a3: ", a3 # a1: [ 3.15 0. 0. ] # a2: [-1.575 2.72798002 0. ] # a3: [ 7.53157781e-16 1.30450754e-15 1.23000000e+01] ########################################## from pylada.crystal import Structure, primitive if bugLev >= 2: print " crystal/read: icsd_cif_a: cell: ", cell # [[ 3.15000000e+00 0.00000000e+00 0.00000000e+00] # [ -1.57500000e+00 2.72798002e+00 0.00000000e+00] # [ 7.53157781e-16 1.30450754e-15 1.23000000e+01]] structure = Structure( transpose( cell), scale = 1, name = basename( filename)) for i in range(len(symbols)): if bugLev >= 5: print " crystal/read: icsd_cif_a: i: ", i, \ " symbol: ", symbols[i], \ " len position: ", len(positions[i]) # crystal/read: i: 0 symbol: Mo len position: 2 for j in range(len(positions[i])): atpos = dot( transpose(cell), positions[i][j]) if bugLev >= 5: print " j: ", j, " pos: ", positions[i][j] print " atpos: ", atpos # j: 0 pos: [0.3333, 0.6666000000000001, 0.25] # atpos: [ 6.32378655e-16 1.81847148e+00 3.07500000e+00] structure.add_atom( atpos[0], atpos[1], atpos[2], symbols[i]) if bugLev >= 2: print " crystal/read: icsd_cif_a: structure:\n", structure prim = primitive( structure) if bugLev >= 2: print " crystal/read: icsd_cif_a: primitive structure:\n", prim return prim
def icsd_cif_a(filename, make_primitive=True): """ Reads lattice from the ICSD \*cif files. It will not work in the case of other \*cif. It is likely to produce wrong output if the site occupations are fractional. If the occupation is > 0.5 it will treat it as 1 and in the case occupation < 0.5 it will treat it as 0 and it will accept all occupation = 0.5 as 1 and create a mess! """ import re from copy import deepcopy from os.path import basename from numpy.linalg import norm from numpy import array, transpose from numpy import pi, sin, cos, sqrt, dot from sys import version_info if version_info[0] >= 3: lines = open(filename, 'r', encoding='latin1').readlines() else: lines = open(filename, 'r').readlines() sym_big = 0 sym_end = 0 pos_big = 0 pos_end = 0 for l in lines: x = l.split() if len(x) > 0: # CELL if x[0] == '_cell_length_a': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) a = float(x[-1][:index]) if x[0] == '_cell_length_b': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) b = float(x[-1][:index]) if x[0] == '_cell_length_c': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) c = float(x[-1][:index]) if x[0] == '_cell_angle_alpha': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) alpha = float(x[-1][:index]) if x[0] == '_cell_angle_beta': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) beta = float(x[-1][:index]) if x[0] == '_cell_angle_gamma': if '(' in x[-1]: index = x[-1].index('(') else: index = len(x[-1]) gamma = float(x[-1][:index]) if len(x) > 0 and x[0] == '_symmetry_Int_Tables_number': spg = int(x[1]) # SYMMETRY OPERATIONS if len(x) > 0 and x[0] == '_symmetry_equiv_pos_as_xyz': sym_big = lines.index(l) if len(x) > 0 and x[0] == '_atom_type_symbol': sym_end = lines.index(l) ## FT: reads oxydation states # OXYDATION STATE if len(x) > 0 and x[0] == '_atom_site_label': ox_end = lines.index(l) # WYCKOFF POSITIONS if len(x) > 0 and x[0] == '_atom_site_attached_hydrogens': pos_big = lines.index(l) if len(x) > 0 and x[0] == '_atom_site_B_iso_or_equiv': pos_big = lines.index(l) if len(x) > 0 and x[0] == '_atom_site_U_iso_or_equiv': pos_big = lines.index(l) if len(x) > 0 and x[0] == '_atom_site_0_iso_or_equiv': pos_big = lines.index(l) # if pos_end == 0 and l in ['\n', '\r\n'] and lines.index(l) > pos_big: if pos_end == 0 and pos_big > 0 \ and (l in ['\n', '\r\n'] or l.startswith('#')) \ and lines.index(l) > pos_big: pos_end = lines.index(l) # _symmetry_equiv_pos_* lines are like: # 1 'x, x-y, -z+1/2' symm_ops = [ '(' + x.split()[1][1:] + x.split()[2] + x.split()[3][:-1] + ')' for x in lines[sym_big + 1:sym_end - 1] ] # ['(x,x-y,-z+1/2)', '(-x+y,y,-z+1/2)', ...] # Insert decimal points after integers symm_ops = [re.sub(r'(\d+)', r'\1.', x) for x in symm_ops] # ['(x,x-y,-z+1./2.)', '(-x+y,y,-z+1./2.)', ...] # _atom_site_* lines are like: # Mo1 Mo4+ 2 c 0.3333 0.6667 0.25 1. 0 ## FT: replaced [0] by [1] to take the ion name instead (ex: Mo4+ instead of Mo1) wyckoff = [[ x.split()[1], [x.split()[4], x.split()[5], x.split()[6]], x.split()[7] ] for x in lines[pos_big + 1:pos_end]] # [['Mo1', ['0.3333', '0.6667', '0.25'], '1.'], ['S1', ['0.3333', '0.6667', '0.621(4)'], '1.']] wyckoff = [w for w in wyckoff if int(float(w[-1][:4]) + 0.5) != 0] # [['Mo1', ['0.3333', '0.6667', '0.25'], '1.'], ['S1', ['0.3333', '0.6667', '0.621(4)'], '1.']] ##FT: reading the proper oxidation states oxidation = [[x.split()[0], int(round(float(x.split()[1]), 0))] for x in lines[sym_end + 2:ox_end - 1]] # Setting up a good wyckoff list for w in wyckoff: ## FT: Not stripping anymore to keep different oxydation states different # Strip trailing numerals from w[0] == 'Mo1' # pom = 0 # for i in range(len(w[0])): # try: # int(w[0][i]) # if pom == 0: # pom = i # except: # pass # w[0] = w[0][:pom] # Strip trailing standard uncertainty, if any, from w[1], ..., w[3] for i in range(3): if '(' in w[1][i]: index = w[1][i].index('(') else: index = len(w[1][i]) w[1][i] = float(w[1][i][:index]) # Delete w[4] del w[-1] ########################################## # List of unique symbols ["Mo", "S"] symbols = list({w[0] for w in wyckoff}) # List of position vectors for each symbol positions = [[] for i in range(len(symbols))] for w in wyckoff: symbol = w[0] x, y, z = w[1][0], w[1][1], w[1][2] for i in range(len(symm_ops)): # Set pom = new position based on symmetry transform pom = list(eval(symm_ops[i])) # [0.3333, -0.3334, 0.25] # Move positions to range [0,1]: for j in range(len(pom)): if pom[j] < 0.: pom[j] = pom[j] + 1. if pom[j] >= 0.999: pom[j] = pom[j] - 1. # [0.3333, 0.6666, 0.25] # If pom is not in positions[symbol], append pom if not any( norm(array(u) - array(pom)) < 0.01 for u in positions[symbols.index(symbol)]): ix = symbols.index(symbol) positions[ix].append(pom) ################ CELL #################### a1 = a * array([1., 0., 0.]) a2 = b * array([cos(gamma * pi / 180.), sin(gamma * pi / 180.), 0.]) c1 = c * cos(beta * pi / 180.) c2 = c / sin( gamma * pi / 180.) * (-cos(beta * pi / 180.) * cos(gamma * pi / 180.) + cos(alpha * pi / 180.)) a3 = array([c1, c2, sqrt(c**2 - (c1**2 + c2**2))]) cell = array([a1, a2, a3]) ########################################## from pylada.crystal import Structure, primitive structure = Structure(transpose(cell), scale=1, name=basename(filename), group=spg) for i in range(len(symbols)): # crystal/read: i: 0 symbol: Mo len position: 2 for j in range(len(positions[i])): atpos = dot(transpose(cell), positions[i][j]) # j: 0 pos: [0.3333, 0.6666000000000001, 0.25] # atpos: [ 6.32378655e-16 1.81847148e+00 3.07500000e+00] ## FT: Finds the corresponding oxidation for o in oxidation: if o[0] == symbols[i]: ox = o[1] break structure.add_atom(atpos[0], atpos[1], atpos[2], symbols[i], ox=ox) if make_primitive: prim = primitive(structure) else: prim = deepcopy(structure) return prim