def test_coordination_shells(): from random import random from numpy import array from numpy.random import randint, random from pylada.crystal import supercell, Structure lattice = Structure([[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]) \ .add_atom(0, 0, 0, "Si") \ .add_atom(0.25, 0.25, 0.25, "Ge") for atom in lattice: check(lattice, atom) structure = supercell(lattice, [[1, 1, 0], [-5, 2, 0], [0, 0, 1]]) for index in randint(len(structure), size=4): check(structure, structure[index]) for atom in lattice: atom.pos += random(3) * 1e-4 - 5e-5 for atom in lattice: check(lattice, atom, 1e-2) for atom in structure: atom.pos += random(3) * 1e-4 - 5e-5 for index in randint(len(structure), size=4): check(structure, structure[index], 1e-2) check_against_neighbors(structure, 1e-8) lattice = Structure([[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]) \ .add_atom(0, 0, 0, "Si") check_against_neighbors(structure, 1e-8) lattice = Structure([[-0.5, 0.5, 0.5], [0.5, -0.5, 0.5], [0.5, 0.5, -0.5]]) \ .add_atom(0, 0, 0, "Si") check_against_neighbors(structure, 1e-8)
def mix_atoms(s1, s2, roll=True): """ Randomly mix cell and atoms from parents. Mating operation on two parent structures s1 and s2. Done by mixing randomly atoms from s1 and s2 into the cell coming from one of them. Returns two offspring structures. """ from random import choice from itertools import chain from numpy.linalg import det from numpy import dot, abs from ..crystal import Structure # swap structures randomly. if choice([True, False]): s1, s2 = s2, s1 # chem. symbols and scaled positions of the two parents sc_pos1 = zip([atom.type for atom in s1], fractional_pos(s1, roll)) sc_pos2 = zip([atom.type for atom in s2], fractional_pos(s2, roll)) result = Structure(s1.cell) for pos, type in chain(sc_pos1, sc_pos2): if choice([True, False]): result.add_atom(*dot(result.cell, pot), type=type) result.scale = s1.scale * (float(len(result)) / float(len(s1)))**(1./3.) return result
def str_template(name, scale, cell): from pylada.crystal import Structure structure = Structure() structure.name = name structure.scale = scale structure.cell = cell return structure
def test_system(): from pylada.crystal import Structure from pylada.vasp import Vasp a = Vasp() b = Structure() assert a.system is None assert a._input['system'].keyword == 'system' assert a._input['system'].output_map(vasp=a, structure=b) is None a.system = 'system' assert a.system == 'system' assert 'system' in a._input['system'].output_map(vasp=a, structure=b) assert a._input['system'].output_map(vasp=a, structure=b)['system'] == 'system' b.name = 'hello' assert 'system' in a._input['system'].output_map(vasp=a, structure=b) assert a._input['system'].output_map( vasp=a, structure=b)['system'] == 'system: hello' a.system = None assert a.system is None assert 'system' in a._input['system'].output_map(vasp=a, structure=b) assert a._input['system'].output_map(vasp=a, structure=b)['system'] == 'hello' a.system = None assert a.system is None assert 'system' in a._input['system'].output_map(vasp=a, structure=b) assert a._input['system'].output_map(vasp=a, structure=b)['system'] == 'hello'
def test_ewald(): """ Ewald Rydberg test """ from numpy import all, abs, sqrt from pylada.crystal import Structure from pylada.ewald import ewald from quantities import angstrom, a0, Ry structure = Structure([[1, 0, 0], [0, 1, 0], [0, 0, 1] ], scale=50 ) \ .add_atom(0, 0, 0, 'A', charge=1e0) \ .add_atom(float(a0.rescale(angstrom) / 50.0), 0, 0, 'A', charge=-1e0) result = ewald(structure, cutoff=80) assert abs(result.energy + 2e0 * Ry) < 1e-3 assert all(abs(abs(result[0].force) - [2e0, 0, 0] * Ry / a0) < 1e-3) assert all(abs(abs(result[1].force) - [2e0, 0, 0] * Ry / a0) < 1e-3) a = float(a0.rescale(angstrom) / 50.0) / sqrt(2.) structure = Structure([[1, 0, 0], [0, 1, 0], [0, 0, 1] ], scale=50 ) \ .add_atom(0, 0, 0, 'A', charge=1e0) \ .add_atom(0, a, a, 'A', charge=-1e0) result = ewald(structure, cutoff=80) assert abs(result.energy + 2e0 * Ry) < 1e-3 assert all( abs(abs(result[0].force) - [0, 2. / sqrt(2), 2. / sqrt(2)] * Ry / a0) < 1e-3) assert all( abs(abs(result[1].force) - [0, 2. / sqrt(2), 2. / sqrt(2)] * Ry / a0) < 1e-3)
def test_system(): from pylada.crystal import Structure from pylada.vasp import Vasp a = Vasp() b = Structure() assert a.system is None assert a._input['system'].keyword == 'system' assert a._input['system'].output_map(vasp=a, structure=b) is None a.system = 'system' assert a.system == 'system' assert 'system' in a._input['system'].output_map(vasp=a, structure=b) assert a._input['system'].output_map(vasp=a, structure=b)['system'] == 'system' b.name = 'hello' assert 'system' in a._input['system'].output_map(vasp=a, structure=b) assert a._input['system'].output_map(vasp=a, structure=b)['system'] == 'system: hello' a.system = None assert a.system is None assert 'system' in a._input['system'].output_map(vasp=a, structure=b) assert a._input['system'].output_map(vasp=a, structure=b)['system'] == 'hello' a.system = None assert a.system is None assert 'system' in a._input['system'].output_map(vasp=a, structure=b) assert a._input['system'].output_map(vasp=a, structure=b)['system'] == 'hello'
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 mix_poscars(s1,s2, roll=True): """ Crossover operations where atoms are from one and cell from other. Mating operation on two parent structures s1 and s2. Done on scaled atomic positions (cubic systems) by interchanging their cells and scaled positions. Returns two offspring structures each with the same number of atoms as the parent from which the atoms are inhereted. """ from random import choice from numpy import dot from numpy.linalg import det from ..crystal import Structure # swap structures randomly. if choice([True, False]): s1, s2 = s2, s1 # chem. symbols and scaled positions of the two parents sc_pos2 = zip([atom.type for atom in s2],fractional_pos(s2, roll)) # cell from s1 result = Structure(s1.cell, scal=s1.scale) # atoms from s2 for type, pos in sc_pos2: result.add_atom(*dot(result.cell, pos), type=type) result.scale = s1.scale * (float(len(result)) / float(len(s1)))**(1./3.) return result
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 pmg_to_pyl(pmg : Structure): from pylada.crystal import Structure as Pyl_Structure from pylada.crystal import Atom pyl = Pyl_Structure(np.transpose(pmg.lattice.matrix)) for i in range(len(pmg)): if pmg.site_properties: kwargs = {x: pmg.site_properties[x][i] for x in pmg.site_properties} else: kwargs = {} coords = pmg[i].coords specie = str(pmg[i].specie) pyl_atom = Atom(coords[0], coords[1], coords[2], specie, **kwargs) pyl.add_atom(pyl_atom) return pyl
def test_ediff(): from pickle import loads, dumps from pylada.crystal import Structure, supercell from pylada.vasp.incar._params import Ediff, Ediffg structure = Structure([[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]])\ .add_atom(0, 0, 0, 'Si')\ .add_atom(0.25, 0.25, 0.25, 'Si') assert Ediff(None).incar_string(structure=structure) is None a = loads(dumps(Ediff(1e-4))).incar_string(structure=structure).split() assert a[0] == 'EDIFF' and a[1] == '=' and abs(float(a[2]) - 1e-4 * 2.) < 1e-8 a = loads(dumps(Ediff(-1e-4))).incar_string(structure=structure).split() assert a[0] == 'EDIFF' and a[1] == '=' and abs(float(a[2]) - 1e-4) < 1e-8 and float(a[2]) > 0e0 assert Ediffg(None).incar_string(structure=structure) is None a = loads(dumps(Ediffg(1e-4))).incar_string(structure=structure).split() assert a[0] == 'EDIFFG' and a[1] == '=' and abs(float(a[2]) - 1e-4 * 2.) < 1e-8 a = loads(dumps(Ediffg(-1e-4))).incar_string(structure=structure).split() assert a[0] == 'EDIFFG' and a[1] == '=' and abs(float(a[2]) + 1e-4) < 1e-8 structure = supercell(structure, [[1, 0, 0], [0, 1, 0], [0, 0, 1]]) a = loads(dumps(Ediff(1e-4))).incar_string(structure=structure).split() assert a[0] == 'EDIFF' and a[1] == '=' and abs(float(a[2]) - 1e-4 * 8.) < 1e-8 a = loads(dumps(Ediff(-1e-4))).incar_string(structure=structure).split() assert a[0] == 'EDIFF' and a[1] == '=' and abs(float(a[2]) - 1e-4) < 1e-8 and float(a[2]) > 0e0 a = loads(dumps(Ediffg(1e-4))).incar_string(structure=structure).split() assert a[0] == 'EDIFFG' and a[1] == '=' and abs(float(a[2]) - 1e-4 * 8.) < 1e-8 a = loads(dumps(Ediffg(-1e-4))).incar_string(structure=structure).split() assert a[0] == 'EDIFFG' and a[1] == '=' and abs(float(a[2]) + 1e-4) < 1e-8
def test_encut(EncutClass): from pickle import loads, dumps from collections import namedtuple from pylada.crystal import Structure, supercell from quantities import eV, hartree Vasp = namedtuple('Vasp', ['species']) Specie = namedtuple('Specie', ['enmax']) vasp = Vasp({'Si': Specie(1. * eV), 'Ge': Specie(10.), 'C': Specie(100. * eV)}) name = EncutClass.__name__.upper() structure = Structure([[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]])\ .add_atom(0, 0, 0, 'Si')\ .add_atom(0.25, 0.25, 0.25, 'Ge') assert EncutClass(None).incar_string(vasp=vasp, structure=structure) is None a = loads(dumps(EncutClass(50))).incar_string(vasp=vasp, structure=structure).split() assert a[0] == name and a[1] == '=' and abs(float(a[2]) - 50) < 1e-8 a = loads(dumps(EncutClass(1.0))).incar_string(vasp=vasp, structure=structure).split() assert a[0] == name and a[1] == '=' and abs(float(a[2]) - 10.) < 1e-8 structure[0].type = 'C' a = loads(dumps(EncutClass(2.0))).incar_string(vasp=vasp, structure=structure).split() assert a[0] == name and a[1] == '=' and abs(float(a[2]) - 2. * 100.) < 1e-8 a = loads(dumps(EncutClass(50. * eV))).incar_string(vasp=vasp, structure=structure).split() assert a[0] == name and a[1] == '=' and abs(float(a[2]) - 50.) < 1e-8 a = loads(dumps(EncutClass((50. * eV).rescale(hartree))) ).incar_string(vasp=vasp, structure=structure).split() assert a[0] == name and a[1] == '=' and abs(float(a[2]) - 50.) < 1e-8 assert EncutClass(-50).incar_string(vasp=vasp, structure=structure) is None assert EncutClass(-50 * eV).incar_string(vasp=vasp, structure=structure) is None
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(path): from shutil import rmtree from tempfile import mkdtemp from pylada.crystal import Structure from pylada.vasp import Vasp from epirelax import epitaxial from pylada import default_comm structure = Structure([[0, 0.5, 0.5],[0.5, 0, 0.5], [0.5, 0.5, 0]], scale=5.55, name='has a name')\ .add_atom(0,0,0, "Si")\ .add_atom(0.25,0.25,0.25, "Si") vasp = Vasp() vasp.kpoints = "Automatic generation\n0\nMonkhorst\n2 2 2\n0 0 0" vasp.prec = "accurate" vasp.ediff = 1e-5 vasp.encut = 1.4 vasp.ismear = "fermi" vasp.sigma = 0.01 vasp.relaxation = "volume" vasp.add_specie = "Si", "{0}/pseudos/Si".format(path) directory = mkdtemp() try: result = epitaxial(vasp, structure, outdir=directory, epiconv=1e-4, comm=default_comm) assert result.success finally: rmtree(directory) pass
def __init__(self, vasp, dft, gw, outdir="nlep_fit", comm=None, units=None): from os import makedirs from os.path import exists from shutil import rmtree from boost.mpi import world from pylada.crystal import Structure self.gw = gw self.dft = dft self.gw.comm = comm self.dft.comm = comm # since comm has changed, makes sure there are no issues with caching self.gw.uncache() self.dft.uncache() self.vasp = vasp self.system = Structure(dft.structure) self._nbcalls = 0 self.outdir = outdir self.comm = comm if comm != None else world self.units = units if units != None else 1e0 self.use_syscall = True if self.comm.rank == 0 and exists(self.outdir): rmtree(self.outdir) makedirs(self.outdir) self.comm.barrier()
def s28(): """ s28 lattice """ from pylada.crystal import Structure return Structure( 7.342, -3.671, 0,\ 0, 6.35836, 0,\ 0, 0, 7.218,\ scale=1, name='s28' )\ .add_atom(1.55419, 2.47041, 1.8045, 'A')\ .add_atom(4.42546, 0.110763, 1.8045, 'A')\ .add_atom(5.03334, 3.77718, 1.8045, 'A')\ .add_atom(1.36234, 2.58118, 5.4135, 'A')\ .add_atom(-2.11681, 3.88795, 5.4135, 'A')\ .add_atom(0.754464, 6.2476, 5.4135, 'A')\ .add_atom(-3.671e-08, 4.23891, 0.241153, 'B')\ .add_atom(-3.671e-08, 4.23891, 3.36785, 'B')\ .add_atom(3.671, 2.11945, 3.85015, 'B')\ .add_atom(3.671, 2.11945, 6.97685, 'B')\ .add_atom(0, 0, 1.8045, 'B')\ .add_atom(0, 0, 5.4135, 'B')\ .add_atom(2.67983, 4.6416, 0, 'X')\ .add_atom(1.98234, 0, 0, 'X')\ .add_atom(-0.99117, 1.71676, 0, 'X')\ .add_atom(2.67983, 4.6416, 3.609, 'X')\ .add_atom(1.98234, 0, 3.609, 'X')\ .add_atom(-0.99117, 1.71676, 3.609, 'X')
def s18(): """ s18 lattice """ from pylada.crystal import Structure return Structure( 9.296, -4.648, 0,\ 0, 8.05057, 0,\ 0, 0, 7.346,\ scale=1.1, name='s18' )\ .add_atom(4.648, 5.24897, 4.99528, 'A')\ .add_atom(2.42626, 1.4008, 4.99528, 'A')\ .add_atom(6.86974, 1.4008, 4.99528, 'A')\ .add_atom(0, 2.8016, 1.32228, 'A')\ .add_atom(2.22174, 6.64977, 1.32228, 'A')\ .add_atom(-2.22174, 6.64977, 1.32228, 'A')\ .add_atom(4.64846, 2.68326, 2.29195, 'A')\ .add_atom(-0.0004648, 5.36732, 5.96495, 'A')\ .add_atom(0, 7.76075, 3.673, 'B')\ .add_atom(-2.07301, 4.1702, 3.673, 'B')\ .add_atom(2.07301, 4.1702, 3.673, 'B')\ .add_atom(4.648, 0.289821, 0, 'B')\ .add_atom(6.72101, 3.88038, 0, 'B')\ .add_atom(2.57499, 3.88038, 0, 'B')\ .add_atom(0, 0, 3.673, 'B')\ .add_atom(0, 0, 0, 'B')\ .add_atom(4.648, 5.31338, 1.89527, 'X')\ .add_atom(2.37048, 1.3686, 1.89527, 'X')\ .add_atom(6.92552, 1.3686, 1.89527, 'X')\ .add_atom(0, 2.73719, 5.56827, 'X')\ .add_atom(2.27752, 6.68197, 5.56827, 'X')\ .add_atom(-2.27752, 6.68197, 5.56827, 'X')\ .add_atom(4.64846, 2.68326, 5.28177, 'X')\ .add_atom(-0.0004648, 5.36732, 1.60877, 'X')
def test_b5(u): """ Test b5 space-group and equivalents """ from numpy import dot from numpy.random import randint from pylada.crystal import Structure, which_site x, y = u, 0.25 - u lattice = 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") assert which_site(lattice[6].pos + [0.5, -0.5, 2], lattice) == 6 for i, atom in enumerate(lattice): assert which_site(atom.pos, lattice) == i for j in xrange(10): newpos = dot(lattice.cell, randint(10, size=(3, )) - 5) assert which_site(atom.pos + newpos, lattice) == i, (atom.pos, newpos, i)
def s26(): """ s26 lattice """ from pylada.crystal import Structure return Structure( 6.997, 0, 3.4985,\ 0, 10.83, 5.415,\ 0, 0, 3.1435,\ scale=1.1, name='s26' )\ .add_atom(5.24775, 8.65967, 1.86347, 'A')\ .add_atom(8.74625, 13.0003, 1.86347, 'A')\ .add_atom(5.24775, 13.2202, 1.70189, 'A')\ .add_atom(8.74625, 8.43982, 1.70189, 'A')\ .add_atom(8.74625, 5.43774, 2.62671, 'A')\ .add_atom(5.24775, 5.39226, 2.62671, 'A')\ .add_atom(3.70491, 6.75359, 0.75444, 'B')\ .add_atom(3.29209, 4.07641, 0.75444, 'B')\ .add_atom(6.79059, 6.75359, 0.75444, 'B')\ .add_atom(7.20341, 4.07641, 0.75444, 'B')\ .add_atom(3.4985, 10.83, 1.57238, 'B')\ .add_atom(6.997, 10.83, 1.57238, 'B')\ .add_atom(7.03898, 14.431, 3.11395, 'X')\ .add_atom(6.95502, 7.22903, 3.11395, 'X')\ .add_atom(10.4535, 14.431, 3.11395, 'X')\ .add_atom(3.54048, 7.22903, 3.11395, 'X')\ .add_atom(1.74925, 5.689, 0.0345785, 'X')\ .add_atom(5.24775, 5.141, 0.0345785, 'X')
def s13(): """ s13 lattice """ from pylada.crystal import Structure return Structure( 14.462, 7.231, -2.12564,\ 0, 2.785, 0,\ 0, 0, 9.23657,\ scale=1.1, name='s13' )\ .add_atom(12.2089, 1.30839, 5.17802, 'A')\ .add_atom(13.5267, 1.30839, 8.67683, 'A')\ .add_atom(7.35851, 1.47661, 4.05855, 'A')\ .add_atom(6.04068, 1.47661, 0.559736, 'A')\ .add_atom(9.78368, 1.3925, 4.61828, 'A')\ .add_atom(3.6155, 1.3925, 0, 'A')\ .add_atom(10.1658, 1.31619, 8.0469, 'B')\ .add_atom(15.5698, 1.31619, 5.80795, 'B')\ .add_atom(9.40159, 1.46881, 1.18967, 'B')\ .add_atom(3.9976, 1.46881, 3.42861, 'B')\ .add_atom(5.63677, 1.22429, 6.92742, 'B')\ .add_atom(13.9306, 1.56071, 2.30914, 'B')\ .add_atom(10.8767, 2.56777, 5.62507, 'X')\ .add_atom(14.8588, 2.56777, 8.22978, 'X')\ .add_atom(8.69066, 0.21723, 3.6115, 'X')\ .add_atom(4.70852, 0.21723, 1.00679, 'X')\ .add_atom(-1.06282, 0, 4.61828, 'X')\ .add_atom(0, 0, 0, 'X')
def s41(): """ s41 lattice """ from pylada.crystal import Structure return Structure( 5.81, 0, 0,\ 0, 5.96, 0,\ 0, 0, 11.71,\ scale=1.2, name='s41' )\ .add_atom(5.03146, 1.79992, 0.74944, 'A')\ .add_atom(2.12646, 1.18008, 10.9606, 'A')\ .add_atom(0.77854, 4.77992, 5.10556, 'A')\ .add_atom(3.68354, 4.16008, 6.60444, 'A')\ .add_atom(0.77854, 4.16008, 10.9606, 'A')\ .add_atom(3.68354, 4.77992, 0.74944, 'A')\ .add_atom(5.03146, 1.18008, 6.60444, 'A')\ .add_atom(2.12646, 1.79992, 5.10556, 'A')\ .add_atom(3.59058, 0.298, 3.7472, 'B')\ .add_atom(0.68558, 2.682, 7.9628, 'B')\ .add_atom(2.21942, 3.278, 2.1078, 'B')\ .add_atom(5.12442, 5.662, 9.6022, 'B')\ .add_atom(2.21942, 5.662, 7.9628, 'B')\ .add_atom(5.12442, 3.278, 3.7472, 'B')\ .add_atom(3.59058, 2.682, 9.6022, 'B')\ .add_atom(0.68558, 0.298, 2.1078, 'B')\ .add_atom(2.98634, 1.03108, 1.33494, 'X')\ .add_atom(0.08134, 1.94892, 10.3751, 'X')\ .add_atom(2.82366, 4.01108, 4.52006, 'X')\ .add_atom(5.72866, 4.92892, 7.18994, 'X')\ .add_atom(2.82366, 4.92892, 10.3751, 'X')\ .add_atom(5.72866, 4.01108, 1.33494, 'X')\ .add_atom(2.98634, 1.94892, 7.18994, 'X')\ .add_atom(0.08134, 1.03108, 4.52006, 'X')
def s29(): """ s29 lattice """ from pylada.crystal import Structure return Structure( 4.308, 0, 0,\ 0, 13.912, 0,\ 0, 0, 7.431,\ scale=1.1, name='s29' )\ .add_atom(3.231, 8.93276, 6.85658, 'A')\ .add_atom(3.231, 11.9352, 6.85658, 'A')\ .add_atom(1.077, 4.97924, 0.574416, 'A')\ .add_atom(1.077, 1.97676, 0.574416, 'A')\ .add_atom(3.231, 1.32039, 4.32023, 'A')\ .add_atom(3.231, 5.63561, 4.32023, 'A')\ .add_atom(1.077, 12.5916, 3.11077, 'A')\ .add_atom(1.077, 8.27639, 3.11077, 'A')\ .add_atom(3.231, 6.97603, 1.55828, 'B')\ .add_atom(3.231, 13.892, 1.55828, 'B')\ .add_atom(1.077, 6.93597, 5.87272, 'B')\ .add_atom(1.077, 0.0200333, 5.87272, 'B')\ .add_atom(3.231, 3.478, 2.16688, 'B')\ .add_atom(1.077, 10.434, 5.26412, 'B')\ .add_atom(3.231, 10.434, 2.22484, 'B')\ .add_atom(1.077, 3.478, 5.20616, 'B')\ .add_atom(3.231, 8.39728, 4.39321, 'X')\ .add_atom(3.231, 12.4707, 4.39321, 'X')\ .add_atom(1.077, 5.51472, 3.03779, 'X')\ .add_atom(1.077, 1.44128, 3.03779, 'X')\ .add_atom(3.231, 2.18001, 6.76072, 'X')\ .add_atom(3.231, 4.77599, 6.76072, 'X')\ .add_atom(1.077, 11.732, 0.670276, 'X')\ .add_atom(1.077, 9.13601, 0.670276, 'X')
def test(path): from os import makedirs from os.path import exists from shutil import rmtree from tempfile import mkdtemp from pylada.crystal import Structure from pylada.vasp import Vasp from pylada import default_comm structure = Structure([[0, 0.5, 0.5],[0.5, 0, 0.5], [0.5, 0.5, 0]], scale=5.43, name='has a name')\ .add_atom(0,0,0, "Si")\ .add_atom(0.25,0.25,0.25, "Si") vasp = Vasp() vasp.kpoints = "Automatic generation\n0\nMonkhorst\n2 2 2\n0 0 0" vasp.prec = "accurate" vasp.ediff = 1e-5 vasp.encut = 1 vasp.ismear = "fermi" vasp.sigma = 0.01 vasp.relaxation = "volume" vasp.add_specie = "Si", "{0}/pseudos/Si".format(path) directory = mkdtemp() if directory == '/tmp/test' or directory == '/tmp/test/': if exists(directory): rmtree(directory) makedirs(directory) try: result = vasp(structure, outdir=directory, comm=default_comm) assert result.success finally: if directory != '/tmp/test' and directory != '/tmp/test/': rmtree(directory)
def test(tmpdir, path): from numpy import abs from pylada.crystal import Structure from pylada.vasp import Vasp from pylada.vasp.relax import epitaxial from pylada import default_comm structure = Structure([[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]], scale=5.55, name='has a name')\ .add_atom(0, 0, 0, "Si")\ .add_atom(0.25, 0.25, 0.25, "Si") vasp = Vasp() vasp.kpoints = "Automatic generation\n0\nMonkhorst\n2 2 2\n0 0 0" vasp.prec = "accurate" vasp.ediff = 1e-5 vasp.encut = 1.4 vasp.ismear = "fermi" vasp.sigma = 0.01 vasp.relaxation = "volume" vasp.add_specie = "Si", "{0}/pseudos/Si".format(path) result = epitaxial(vasp, structure, outdir=str(tmpdir), epiconv=1e-5, comm=default_comm) assert result.success assert abs(result.stress[2, 2]) < 1.0
def test_nelect(): from os.path import dirname from pickle import loads, dumps from pylada.vasp import Vasp from pylada.crystal import Structure structure = Structure([[0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]], scale=5.43, name='has a name')\ .add_atom(0, 0, 0, "Si")\ .add_atom(0.25, 0.25, 0.25, "Si") a = Vasp() a.add_specie = "Si", "{0}/pseudos/Si".format(dirname(__file__)) assert a.extraelectron is None assert a._input['extraelectron'].output_map() is None assert a._input['nelect'].output_map() is None a.extraelectron = 0 assert a.extraelectron == 0 assert a.nelect is None assert a._input['extraelectron'].output_map() is None assert a._input['nelect'].output_map() is None a.extraelectron = 1 assert a.extraelectron == 1 assert a.nelect is None assert 'nelect' in a._input['extraelectron'].output_map( vasp=a, structure=structure) assert abs( float(a._input['extraelectron'].output_map(vasp=a, structure=structure) ['nelect']) - 9.0) < 1e-8 assert a._input['nelect'].output_map() is None a.nelect = 1 a.extraelectron = -1 assert a.extraelectron == -1 assert a.nelect is None assert 'nelect' in a._input['extraelectron'].output_map( vasp=a, structure=structure) assert abs( float(a._input['extraelectron'].output_map(vasp=a, structure=structure) ['nelect']) - 7.0) < 1e-8 assert a._input['nelect'].output_map() is None o = a._input['extraelectron'] d = {'ExtraElectron': o.__class__} assert repr(eval(repr(o), d)) == repr(o) assert abs( float( eval(repr(o), d).output_map(vasp=a, structure=structure)['nelect']) - 7.0) < 1e-8 assert repr(loads(dumps(o))) == repr(o) a.nelect = 8 assert a.nelect == 8 assert a.extraelectron is None assert 'nelect' in a._input['nelect'].output_map() assert abs(float(a._input['nelect'].output_map()['nelect']) - 8.0) < 1e-8 assert a._input['extraelectron'].output_map() is None o = a._input['nelect'] d = {'NElect': o.__class__} assert repr(eval(repr(o), d)) == repr(o) assert abs(float(eval(repr(o), d).output_map()['nelect']) - 8.0) < 1e-8 assert repr(loads(dumps(o))) == repr(o)
def bcc(): """ Creates a BCC lattice with a single site. """ from pylada.crystal import Structure return Structure(-0.5, 0.5, 0.5, 0.5, -0.5, 0.5, 0.5, 0.5, -0.5, scale=1, name='bcc' )\ .add_atom(0, 0, 0, 'A')
def fcc(): """ Creates an FCC lattice with a single site. """ from pylada.crystal import Structure return Structure( 0, 0.5, 0.5,\ 0.5, 0, 0.5,\ 0.5, 0.5, 0,\ scale=1, name='fcc' )\ .add_atom(0, 0, 0, 'A')
def test(path): from shutil import rmtree from tempfile import mkdtemp from os.path import join from quantities import eV from pylada.vasp import Vasp, read_incar from pylada.crystal import Structure structure = Structure([[0, 0.5, 0.5],[0.5, 0, 0.5], [0.5, 0.5, 0]], scale=5.43, name='has a name')\ .add_atom(0,0,0, "Si")\ .add_atom(0.25,0.25,0.25, "Si") vasp = Vasp() vasp.kpoints = "Automatic generation\n0\nMonkhorst\n2 2 2\n0 0 0" vasp.precision = "accurate" vasp.ediff = 1e-5 vasp.encut = 1 vasp.ismear = "metal" vasp.sigma = 0.06 vasp.relaxation = "volume" vasp.add_specie = "Si", "{0}/pseudos/Si".format(path) directory = mkdtemp() try: vasp.write_incar(path=join(directory, 'INCAR'), structure=structure) other = read_incar(join(directory, 'INCAR')) assert abs(other.ediff - 1e-5) < 1e-8 assert abs(other.encut - 245.345) < 1e-8 assert abs(other.sigma - 0.06 * eV) < 1e-8 assert other.ibrion == 2 assert other.icharg == 'atomic' assert other.isif == 7 assert other.ismear == 'metal' assert other.istart == 'scratch' assert other.lcharg == False assert other.nsw == 50 assert other.relaxation == 'volume' assert other.system == 'has a name' with open(join(directory, 'INCAR'), 'a') as file: file.write('\nSOMETHing = 0.5\n') other = read_incar(join(directory, 'INCAR')) assert abs(other.ediff - 1e-5) < 1e-8 assert abs(other.encut - 245.345) < 1e-8 assert abs(other.sigma - 0.06 * eV) < 1e-8 assert other.ibrion == 2 assert other.icharg == 'atomic' assert other.isif == 7 assert other.ismear == 'metal' assert other.istart == 'scratch' assert other.lcharg == False assert other.nsw == 50 assert other.relaxation == 'volume' assert other.system == 'has a name' assert 'something' in other._input assert isinstance(other.something, float) assert abs(other.something - 0.5) < 1e-8 finally: rmtree(directory) pass
def rock_salt(): """ rock_salt lattice """ from pylada.crystal import Structure return Structure(1, 0, 0, 0, 1, 0, 0, 0, 1, scale=1, name='Rock-Salt' )\ .add_atom(0, 0, 0, 'A')\ .add_atom(0.5, 0.5, 0.5, 'B')
def zinc_blende(): """ zinc_blende lattice """ from pylada.crystal import Structure return Structure(0, 0.5, 0.5, 0.5, 0, 0.5, 0.5, 0.5, 0, scale=1, name='Zinc-Blende' )\ .add_atom(0, 0, 0, 'A')\ .add_atom(0.25, 0.25, 0.25, 'B')
def crystal(file='fort.34'): """ Reads CRYSTAL's external format. """ from numpy import array, abs, zeros, any, dot from numpy.linalg import inv from ..crystal import which_site from ..misc import RelativePath from ..error import IOError from ..periodic_table import find as find_specie from . import Structure if isinstance(file, str): if file.find('\n') == -1: with open(RelativePath(file).path, 'r') as file: return crystal(file) else: file = file.splitlines().__iter__() # read first line try: line = file.next() except StopIteration: raise IOError('Premature end of stream.') else: dimensionality, centering, type = [int(u) for u in line.split()[:3]] # read cell try: cell = array( [file.next().split()[:3] for i in xrange(3)], dtype='float64' ).T except StopIteration: raise IOError('Premature end of stream.') result = Structure( cell=cell, centering=centering, dimensionality=dimensionality, type=type, scale=1e0 ) # read symmetry operators result.spacegroup = [] try: N = int(file.next()) except StopIteration: raise IOError('Premature end of stream.') for i in xrange(N): try: op = array( [file.next().split()[:3] for j in xrange(4)], dtype='float64' ) except StopIteration: raise IOError('Premature end of stream.') else: op[:3] = op[:3].copy().T result.spacegroup.append(op) result.spacegroup = array(result.spacegroup) # read atoms. try: N = int(file.next()) except StopIteration: raise IOError('Premature end of stream.') for i in xrange(N): try: line = file.next().split() except StopIteration: raise IOError('Premature end of stream.') else: type, pos = int(line[0]), array(line[1:4], dtype='float64') if type < 100: type = find_specie(atomic_number=type).symbol result.add_atom(pos=pos, type=type, asymmetric=True) # Adds symmetrically equivalent structures. identity = zeros((4, 3), dtype='float64') for i in xrange(3): identity[i, i] == 1 symops = [u for u in result.spacegroup if any(abs(u - identity) > 1e-8)] invcell = inv(result.cell) for atom in [u for u in result]: for op in symops: pos = dot(op[:3], atom.pos) + op[3] if which_site(pos, result, invcell=invcell) == -1: result.add_atom(pos=pos, type=atom.type, asymmetric=False) return result
def s1(): """ s1 lattice """ from pylada.crystal import Structure return Structure( 0, 3.1, 3.1,\ 3.1, 0, 3.1,\ 3.1, 3.1, 0,\ scale=1, name='s1' )\ .add_atom(3.1, 3.1, 3.1, 'A')\ .add_atom(0, 0, 0, 'B')\ .add_atom(1.55, 1.55, 1.55, 'X')
def test_lattice_is_primitive(): from pylada.crystal import Structure, 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) assert is_primitive(lattice)
def s17(): """ s17 lattice """ from pylada.crystal import Structure return Structure( 4.244, -2.122, 0,\ 0, 3.67541, 0,\ 0, 0, 4.563,\ scale=1, name='s17' )\ .add_atom(0, 0, 0, 'A')\ .add_atom(2.122, 1.22514, 2.2815, 'B')\ .add_atom(-2.122e-08, 2.45027, 2.2815, 'X')
def wurtzite(): """ wurtzite lattice """ from pylada.crystal import Structure return Structure(0.5, 0.5, 0, -0.866025, 0.866025, 0, 0, 0, 1, scale=1, name='Wurtzite' )\ .add_atom(0.5, 0.288675, 0, 'A')\ .add_atom(0.5, -0.288675, 0.5, 'A')\ .add_atom(0.5, 0.288675, 0.25, 'B')\ .add_atom(0.5, -0.288675, 0.75, 'B')
def get_madelungenergy(latt_vec_array, charge, epsilon, cutoff): """ Function returns leading first order correction term, i.e., screened Madelung-like lattice energy of point charge Reference: M. Leslie and M. J. Gillan, J. Phys. C: Solid State Phys. 18 (1985) 973 Parameters defect = pylada.vasp.Extract object charge = charge of point defect. Default 1e0 elementary charge epsilon = dimensionless relative permittivity, SKW: isotropic average of dielectric constant cutoff = Ewald cutoff parameter Returns Madelung (electrostatic) energy in eV Note: 1. Units in this function are either handled by the module Quantities, or\ defaults to Angstrom and elementary charges 2. Function is adopted from Haowei Peng's version in pylada.defects modules """ ewald_cutoff = cutoff * Ry cell_scale = 1.0 # SKW: In notebook workflow cell parameters are converted to Cartesians and units of Angstroms # SKW: Create point charge in pylada.crystal.structure class (used for charge model) # http://pylada.github.io/pylada/userguide/crystal.html struc = Structure() struc.cell = latt_vec_array struc.scale = cell_scale struc.add_atom(0., 0., 0., "P", charge=charge) #Anuj_05/22/18: added "cutoff" in ewald syntax result = ewald(struc, cutoff=ewald_cutoff).energy / epsilon return -1 * result.rescale(eV)
def first_order_charge_correction(structure, charge=None, epsilon=1e0, cutoff=20.0, **kwargs): """ First order charge correction of +1 charge in given supercell. Units in this function are either handled by the module Quantities, or defaults to Angstroems and elementary charges. :Parameters: structure : `pylada.crystal.Structure` Defect supercell, with cartesian positions in angstrom. charge Charge of the point-defect. Defaults to 1e0 elementary charge. If no units are attached, expects units of elementary charges. epsilon dimensionless relative permittivity. cutoff Ewald cutoff parameter. :return: Electrostatic energy in eV. """ from quantities import elementary_charge, eV from pylada.crystal import Structure from pylada.physics import Ry from pylada.ewald import ewald if charge is None: charge = 1 elif charge == 0: return 0e0 * eV if hasattr(charge, "units"): charge = float(charge.rescale(elementary_charge)) ewald_cutoff = cutoff * Ry struc = Structure() struc.cell = structure.cell struc.scale = structure.scale struc.add_atom(0e0, 0, 0, "A", charge=charge) result = ewald(struc, ewald_cutoff).energy / epsilon return -result.rescale(eV)
def cut_and_splice(s1, s2, roll=True): """ Cut-n-splice GSGO crossover operation Mating operation on two parent structures s1 and s2. Done on scaled atomic positions (cubic systems) by cutting them in half and mixing their upper and lower parts. """ from random import choice, random from numpy import dot, abs from numpy.linalg import det from pylada.crystal import Structure # swap structures randomly. if choice([True, False]): s1, s2 = s2, s1 # chem. symbols and scaled positions of the two parents sc_pos1 = zip([atom.type for atom in s1],fractional_pos(s1, roll=True)) sc_pos2 = zip([atom.type for atom in s2],fractional_pos(s2, roll=True)) result = Structure(s1.cell, scale=s1.scale) # choose random positions of split-plane xsep = 0.5 - (random() * 0.45 + 0.15) # choose direction of split-plane randomly from cell-vectors. direction = choice(range(3)) for type, pos in sc_pos1: if pos[direction] >= xsep: result.add_atom(*dot(result.cell, pos), type=type) for type, pos in sc_pos2: if pos[direction] < xsep: result.add_atom(*dot(result.cell, pos), type=type) result.scale = s1.scale * (float(len(result)) / float(len(s1)))**(1./3.) return result
############################### from sys import exit from shutil import rmtree from os.path import exists, join from math import ceil, sqrt from numpy import dot, array, matrix from numpy.linalg import norm from boost.mpi import world from pylada.vff import Vff from pylada.crystal import Structure, Lattice, fill_structure from pylada.escan import read_input input = read_input("input.py") structure = Structure() structure.set_cell = (4, 0, 0.5),\ (0, 1, 0),\ (0, 0, 0.5) structure = fill_structure(structure.cell) for i, atom in enumerate(structure.atoms): atom.type = "Si" if i < len(structure.atoms)/2 else "Ge" result_str = Structure() result_str.scale = 5.450000e+00 result_str.set_cell = (4.068890e+00, -4.235770e-18, 5.083297e-01),\ (-1.694308e-17, 1.016103e+00, 2.238072e-18),\ (-2.252168e-03, 8.711913e-18, 5.083297e-01) result_str.weight = 1.000000e+00 result_str.name = ""
# Structure definition. from pylada.crystal import Structure from pylada.crystal.defects import third_order_charge_correction from quantities import eV structure = Structure() structure.name = 'Ga2CdO4: b5' structure.scale = 1.0 structure.energy = -75.497933000000003 structure.weight = 1.0 structure.set_cell = (-0.0001445, 4.3538020, 4.3537935),\ (4.3538700, -0.0001445, 4.3538615),\ (4.3538020, 4.3537935, -0.0001445) structure.add_atoms = [(7.61911540668, 7.61923876219, 7.61912846850), 'Cd'],\ [(1.08833559332, 1.08834823781, 1.08832253150), 'Cd'],\ [(4.35372550000, 4.35379350000, 4.35372550000), 'Ga'],\ [(4.35379775000, 2.17685850000, 2.17682450000), 'Ga'],\ [(2.17682450000, 4.35386575000, 2.17682875000), 'Ga'],\ [(2.17682875000, 2.17686275000, 4.35379775000), 'Ga'],\ [(2.32881212361, 2.32884849688, 2.32881647755), 'O'],\ [(2.32887187256, 4.20174404476, 4.20169148188), 'O'],\ [(4.20168277385, 2.32891695560, 4.20168347161), 'O'],\ [(4.20168782554, 4.20174474241, 2.32887622633), 'O'],\ [(6.37863887654, 6.37873414925, 6.37863016865), 'O'],\ [(6.37857477364, 4.50584295539, 4.50575516433), 'O'],\ [(4.50576822615, 6.37867004441, 4.50576752839), 'O'],\ [(4.50576317445, 4.50584225759, 6.37857477367), 'O'] # this is converged to less than 1meV third = third_order_charge_correction(structure, epsilon=10.0, n=20) assert abs(third - 0.11708438633232088*eV) < 1e-12
# <http://www.gnu.org/licenses/>. ############################### from pylada.crystal import Structure from pylada.pcm import Clj, bond_name from pylada.physics import a0, Ry from quantities import angstrom, eV, hartree clj = Clj() """ Point charge + r^12 + r^6 model. """ clj.ewald_cutoff = 80 * Ry clj.charges["A"] = -1.0 clj.charges["B"] = 1.0 structure = Structure() structure.set_cell = (1,0,0),\ (0,1,0),\ (0,0,1) structure.scale = 50 structure.add_atom = (0,0,0), "A" structure.add_atom = (a0.rescale(angstrom)/structure.scale,0,0), "B" print clj.ewald(structure).energy, hartree.rescale(eV) from pylada.crystal.A2BX4 import b5 from pylada.crystal import fill_structure from numpy import array clj.ewald_cutoff = 20 * Ry lattice = b5()
def make_surface(structure=None, miller=None, nlayers=5, vacuum=15, acc=5): """Returns a slab from the 3D structure Takes a structure and makes a slab defined by the miller indices with nlayers number of layers and vacuum defining the size of the vacuum thickness. Variable acc determines the number of loops used to get the direct lattice vectors perpendicular and parallel to miller. For high index surfaces use larger acc value .. warning: (1) cell is always set such that miller is alogn z-axes (2) nlayers and vacuum are always along z-axes. :param structure: LaDa structure :param miller: 3x1 float64 array Miller indices defining the slab :param nlayers: integer Number of layers in the slab :param vacuum: real Vacuum thicness in angstroms :param acc: integer number of loops for finding the cell vectors of the slab structure """ direct_cell = transpose(structure.cell) reciprocal_cell = 2 * pi * transpose(inv(direct_cell)) orthogonal = [] # lattice vectors orthogonal to miller for n1 in arange(-acc, acc + 1): for n2 in arange(-acc, acc + 1): for n3 in arange(-acc, acc + 1): pom = array([n1, n2, n3]) if dot(pom, miller) == 0 and dot(pom, pom) != 0: orthogonal.append(array([n1, n2, n3])) # chose the shortest parallel and set it to be a3 lattice vector norm_orthogonal = [sqrt(dot(dot(x, direct_cell), dot(x, direct_cell))) for x in orthogonal] a1 = orthogonal[norm_orthogonal.index(min(norm_orthogonal))] # chose the shortest orthogonal to miller and not colinear with a1 and set it as a2 in_plane = [] for x in orthogonal: if dot(x, x) > 1e-3: v = cross(dot(x, direct_cell), dot(a1, direct_cell)) v = sqrt(dot(v, v)) if v > 1e-3: in_plane.append(x) norm_in_plane = [sqrt(dot(dot(x, direct_cell), dot(x, direct_cell))) for x in in_plane] a2 = in_plane[norm_in_plane.index(min(norm_in_plane))] a1 = dot(a1, direct_cell) a2 = dot(a2, direct_cell) # new cartesian axes z-along miller, x-along a1, and y-to define the right-hand orientation e1 = a1 / sqrt(dot(a1, a1)) e2 = a2 - dot(e1, a2) * e1 e2 = e2 / sqrt(dot(e2, e2)) e3 = cross(e1, e2) # find vectors parallel to miller and set the shortest to be a3 parallel = [] for n1 in arange(-acc, acc + 1): for n2 in arange(-acc, acc + 1): for n3 in arange(-acc, acc + 1): pom = dot(array([n1, n2, n3]), direct_cell) if sqrt(dot(pom, pom)) - dot(e3, pom) < 1e-8 and sqrt(dot(pom, pom)) > 1e-3: parallel.append(pom) # if there are no lattice vectors parallel to miller if len(parallel) == 0: for n1 in arange(-acc, acc + 1): for n2 in arange(-acc, acc + 1): for n3 in arange(-acc, acc + 1): pom = dot(array([n1, n2, n3]), direct_cell) if dot(e3, pom) > 1e-3: parallel.append(pom) parallel = [x for x in parallel if sqrt( dot(x - dot(e1, x) * e1 - dot(e2, x) * e2, x - dot(e1, x) * e1 - dot(e2, x) * e2)) > 1e-3] norm_parallel = [sqrt(dot(x, x)) for x in parallel] assert len(norm_parallel) != 0, "Increase acc, found no lattice vectors parallel to (hkl)" a3 = parallel[norm_parallel.index(min(norm_parallel))] # making a structure in the new unit cell - defined by the a1,a2,a3 new_direct_cell = array([a1, a2, a3]) assert abs(det(new_direct_cell)) > 1e-5, "Something is wrong your volume is equal to zero" # make sure determinant is positive if det(new_direct_cell) < 0.: new_direct_cell = array([-a1, a2, a3]) #structure = fill_structure(transpose(new_direct_cell),structure.to_lattice()) structure = supercell(lattice=structure, supercell=transpose(new_direct_cell)) # transformation matrix to new coordinates x' = dot(m,x) m = array([e1, e2, e3]) # seting output structure out_structure = Structure() out_structure.scale = structure.scale out_structure.cell = transpose(dot(new_direct_cell, transpose(m))) for atom in structure: p = dot(m, atom.pos) out_structure.add_atom(p[0], p[1], p[2], atom.type) # repaeting to get nlayers and vacuum repeat_cell = dot(out_structure.cell, array([[1., 0., 0.], [0., 1., 0.], [0., 0., nlayers]])) out_structure = supercell(lattice=out_structure, supercell=repeat_cell) # checking whether there are atoms close to the cell faces and putting them back to zero for i in range(len(out_structure)): scaled_pos = dot(out_structure[i].pos, inv(transpose(out_structure.cell))) for j in range(3): if abs(scaled_pos[j] - 1.) < 1e-5: scaled_pos[j] = 0. out_structure[i].pos = dot(scaled_pos, transpose(out_structure.cell)) # adding vaccum to the cell out_structure.cell = out_structure.cell + \ array([[0., 0., 0.], [0., 0., 0.], [0., 0., float(vacuum) / float(out_structure.scale)]]) # translating atoms so that center of the slab and the center of the cell along z-axes coincide max_z = max([x.pos[2] for x in out_structure]) min_z = min([x.pos[2] for x in out_structure]) center_atoms = 0.5 * (max_z + min_z) center_cell = 0.5 * out_structure.cell[2][2] for i in range(len(out_structure)): out_structure[i].pos = out_structure[i].pos + array([0., 0., center_cell - center_atoms]) # exporting the final structure return out_structure
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 test_istruc(): from collections import namedtuple from pickle import loads, dumps from os import remove from os.path import join, exists from shutil import rmtree from tempfile import mkdtemp from pylada.vasp.files import POSCAR, CONTCAR from pylada.vasp import Vasp from pylada.crystal import Structure, read, specieset, write from pylada.error import ValueError structure = Structure([[0, 0.5, 0.5],[0.5, 0, 0.5], [0.5, 0.5, 0]], scale=5.43, name='has a name')\ .add_atom(0,0,0, "Si")\ .add_atom(0.25,0.25,0.25, "Si") Extract = namedtuple("Extract", ['directory', 'success', 'structure']) a = Vasp() o = a._input['istruc'] d = {'IStruc': o.__class__} directory = mkdtemp() try: assert a.istruc == 'auto' assert o.output_map(vasp=a, outdir=directory, structure=structure) is None assert eval(repr(o), d).value == 'auto' assert loads(dumps(o)).value == 'auto' assert exists(join(directory, POSCAR)) remove(join(directory, POSCAR)) # check reading from outcar but only on success. a.restart = Extract(directory, False, structure.copy()) a.restart.structure[1].pos[0] += 0.02 assert a.istruc == 'auto' assert o.output_map(vasp=a, outdir=directory, structure=structure) is None assert exists(join(directory, POSCAR)) other = read.poscar(join(directory, POSCAR), types=specieset(structure)) assert abs(other[1].pos[0] - 0.25) < 1e-8 assert abs(other[1].pos[0] - 0.27) > 1e-8 # check reading from outcar but only on success. a.restart = Extract(directory, True, structure.copy()) a.restart.structure[1].pos[0] += 0.02 assert a.istruc == 'auto' assert o.output_map(vasp=a, outdir=directory, structure=structure) is None assert exists(join(directory, POSCAR)) other = read.poscar(join(directory, POSCAR), types=specieset(structure)) assert abs(other[1].pos[0] - 0.25) > 1e-8 assert abs(other[1].pos[0] - 0.27) < 1e-8 # Now check CONTCAR write.poscar(structure, join(directory, CONTCAR)) assert a.istruc == 'auto' assert o.output_map(vasp=a, outdir=directory, structure=structure) is None assert exists(join(directory, POSCAR)) other = read.poscar(join(directory, POSCAR), types=specieset(structure)) assert abs(other[1].pos[0] - 0.25) < 1e-8 assert abs(other[1].pos[0] - 0.27) > 1e-8 # Check some failure modes. write.poscar(structure, join(directory, CONTCAR)) structure[0].type = 'Ge' a.restart = None try: o.output_map(vasp=a, outdir=directory, structure=structure) except ValueError: pass else: raise Exception() structure[0].type = 'Si' structure.add_atom(0.25,0,0, 'Si') try: o.output_map(vasp=a, outdir=directory, structure=structure) except ValueError: pass else: raise Exception() finally: rmtree(directory)
vff.lattice.set_types = ("In", "Ga"), ("As",) vff.lattice.scale = 6.5 vff.add_bond = "In", "As", (2.62332, 21.6739, -112.0, 150.0) vff.add_bond = "Ga", "As", (2.44795, 32.1530, -105.0, 150.0) vff.add_angle = "As", "Ga", "As", ("tet", -4.099, 9.3703) vff.add_angle = "Ga", "As", "Ga", ("tet", -4.099, 9.3703) vff.add_angle = "In", "As", "In", ("tet", -5.753, 5.7599) vff.add_angle = "As", "In", "As", ("tet", -5.753, 5.7599) vff.add_angle = "Ga", "As", "In", (-0.35016, -4.926, 7.5651) vff.minimizer.verbose = True vff.minimizer.type = "gsl_bfgs2" vff.minimizer.itermax = 1 vff.minimizer.tolerance = 1e-5 vff.minimizer.uncertainties = 1e-3 structure = Structure() # structure.set_cell = (00.0, 0.5, 0.5),\ # (0.50, 0.0, 0.5),\ # (0.50, 0.5, 0.0) # structure.add_atoms = ((0.00, 0.00, 0.00), "In"),\ # ((0.25, 0.25, 0.25), "As") structure.set_cell = (10.0, 0.5, 0.5),\ (0.00, 0.0, 0.5),\ (0.00, 0.5, 0.0) structure.add_atoms = ((0.00, 0.00, 0.00), "Ga"),\ ((0.25, 0.25, 0.25), "As"),\ ((1.00, 0.00, 0.00), "Ga"),\ ((1.25, 0.25, 0.25), "As"),\ ((2.00, 0.00, 0.00), "In"),\ ((2.25, 0.25, 0.25), "As"),\ ((3.00, 0.00, 0.00), "In"),\
def test_ingaas(): from numpy import abs, all, dot, array from quantities import eV, angstrom from pylada.crystal import Structure vff = functional() structure = Structure( 10.0, 0.5, 0.5, 0.00, 0.0, 0.5, 0.00, 0.5, 0.0, scale=6.5 )\ .add_atom(pos=(0.00, 0.00, 0.00), type="Ga") \ .add_atom(pos=(0.25, 0.25, 0.25), type="As") \ .add_atom(pos=(1.00, 0.00, 0.00), type="Ga") \ .add_atom(pos=(1.25, 0.25, 0.25), type="As") \ .add_atom(pos=(2.00, 0.00, 0.00), type="In") \ .add_atom(pos=(2.25, 0.25, 0.25), type="As") \ .add_atom(pos=(3.00, 0.00, 0.00), type="In") \ .add_atom(pos=(3.25, 0.25, 0.25), type="As") \ .add_atom(pos=(4.00, 0.00, 0.00), type="Ga") \ .add_atom(pos=(4.25, 0.25, 0.25), type="As") \ .add_atom(pos=(5.00, 0.00, 0.00), type="In") \ .add_atom(pos=(5.25, 0.25, 0.25), type="As") \ .add_atom(pos=(6.00, 0.00, 0.00), type="In") \ .add_atom(pos=(6.25, 0.25, 0.25), type="As") \ .add_atom(pos=(7.00, 0.00, 0.00), type="Ga") \ .add_atom(pos=(7.25, 0.25, 0.25), type="As") \ .add_atom(pos=(8.00, 0.00, 0.00), type="Ga") \ .add_atom(pos=(8.25, 0.25, 0.25), type="As") \ .add_atom(pos=(9.00, 0.00, 0.00), type="Ga") \ .add_atom(pos=(9.25, 0.25, 0.25), type="As") epsilon = array([[1e0, 0.1, 0], [0.1, 1e0, 0], [0, 0, 1e0]]) structure.cell = dot(epsilon, structure.cell) for atom in structure: atom.pos = dot(epsilon, atom.pos) out = vff._pyeval(structure) assert abs(out.energy - 12.7962141476*eV) < 1e-8 assert abs(vff.energy(structure) - 12.7962141476*eV) < 1e-8 stress = array([[ 0.07050804, 0.04862879, 0.00025269], [ 0.04862879, 0.07050804, -0.00025269], [ 0.00025269, -0.00025269, 0.06073765]]) * eV/angstrom**3 assert all(abs(out.stress - stress) < 1e-6) assert all(abs(vff.jacobian(structure)[0] - stress) < 1e-6) gradients = array([u.gradient for u in out]) check_gradients = array( [[ 9.15933995e-16, -5.96744876e-15, 1.12373933e+00], [ 2.22044605e-16, -9.43689571e-15, -1.12373933e+00], [ 6.19921859e-02, -6.19921859e-02, 1.00514227e+00], [ -5.99520433e-15, 2.19269047e-15, -1.12373933e+00], [ 7.66544525e-02, -7.66544525e-02, 1.02138259e+00], [ 3.82409256e-01, -3.82409256e-01, -1.65478066e+00], [ -1.89756333e-02, 1.89756333e-02, 1.15847492e+00], [ -4.16333634e-17, 8.81239526e-16, -1.09205526e+00], [ -7.54665596e-02, 7.54665596e-02, 1.14107325e+00], [ -3.64621516e-01, 3.64621516e-01, -5.74094841e-01], [ 7.66544525e-02, -7.66544525e-02, 1.02138259e+00], [ 3.82409256e-01, -3.82409256e-01, -1.65478066e+00], [ -1.89756333e-02, 1.89756333e-02, 1.15847492e+00], [ -1.19973476e-14, -2.32591724e-14, -1.09205526e+00], [ -1.37458746e-01, 1.37458746e-01, 1.25967031e+00], [ -3.64621516e-01, 3.64621516e-01, -5.74094841e-01], [ -7.77156117e-16, -3.10862447e-15, 1.12373933e+00], [ 8.29891711e-15, 0.00000000e+00, -1.12373933e+00], [ 3.33066907e-15, 7.16093851e-15, 1.12373933e+00], [ -4.44089210e-16, 7.85482790e-15, -1.12373933e+00]]) assert all(abs(gradients - check_gradients) < 1e-8) assert all(abs(vff.jacobian(structure)[1].magnitude - check_gradients) < 1e-8)
def castep(file): """ Tries to read a castep structure file. """ from numpy import array, dot from ..periodic_table import find as find_specie from ..error import IOError, NotImplementedError, input as InputError from ..misc import RelativePath from . import Structure if isinstance(file, str): if file.find('\n') == -1: with open(RelativePath(file).path, 'r') as file: return castep(file) else: file = file.splitlines() file = [l for l in file] def parse_input(input): """ Retrieves blocks from CASTEP input file. """ current_block = None result = {} for line in file: if '#' in line: line = line[:line.find('#')] if current_block is not None: if line.split()[0].lower() == '%endblock': current_block = None continue result[current_block] += line elif len(line.split()) == 0: continue elif len(line.split()[0]) == 0: continue elif line.split()[0].lower() == '%block': name = line.split()[1].lower().replace('.', '').replace('_', '') if name in result: raise InputError('Found two {0} blocks in input.'.format(name)) result[name] = "" current_block = name else: name = line.split()[0].lower().replace('.', '').replace('_', '') if name[-1] in ['=' or ':']: name = name[:-1] if name in result: raise InputError('Found two {0} tags in input.'.format(name)) data = line.split()[1:] if len(data) == 0: result[name] = None; continue if data[0] in [':', '=']: data = data[1:] result[name] = ' '.join(data) return result def parse_units(line): from quantities import a0, meter, centimeter, millimeter, angstrom, emass, \ amu, second, millisecond, microsecond, nanosecond, \ picosecond, femtosecond, elementary_charge, coulomb,\ hartree, eV, meV, Ry, joule, cal, erg, hertz, \ megahertz, gigahertz, tera, kelvin, newton, dyne, \ h_bar, UnitQuantity, pascal, megapascal, gigapascal,\ bar, atm, milli, mol auv = UnitQuantity('auv', a0*Ry/h_bar) # velocity units = { 'a0': a0, 'bohr': a0, 'm': meter, 'cm': centimeter, 'mm': millimeter, 'ang': angstrom, 'me': emass, 'amu': amu, 's': second, 'ms': millisecond, 'mus': microsecond, 'ns': nanosecond, 'ps': picosecond, 'fs': femtosecond, 'e': elementary_charge, 'c': coulomb, 'hartree': hartree, 'ha': hartree, 'mha': 1e-3*hartree, 'ev': eV, 'mev': meV, 'ry': Ry, 'mry': 1e-3*Ry, 'kj': 1e3*joule, 'mol': mol, 'kcal': 1e3*cal, 'j': joule, 'erg': erg, 'hz': hertz, 'mhz': megahertz, 'ghz': gigahertz, 'thz': tera*hertz, 'k': kelvin, 'n': newton, 'dyne': dyne, 'auv': auv, 'pa': pascal, 'mpa': megapascal, 'gpa': gigapascal, 'atm': atm, 'bar': bar, 'atm': atm, 'mbar': milli*bar } line = line.replace('cm-1', '1/cm') return eval(line, units) input = parse_input(file) if 'latticecart' in input: data = input['latticecart'].splitlines() if len(data) == 4: units = parse_units(data[0]) data = data[1:] else: units = 1 cell = array([l.split() for l in data], dtype='float64') elif 'latticeabc' in input: raise NotImplementedError('Cannot read lattice in ABC format yet.') else: raise InputError('Could not find lattice block in input.') # create structure result = Structure(cell, scale=units) # now look for position block. units = None if 'positionsfrac' in input: posdata, isfrac = input['positionsfrac'].splitlines(), True elif 'positionsabs' in input: posdata, isfrac = input['positionsabs'].splitlines(), False try: units = parse_units(posdata[0]) except: units = None else: posdata = posdata[1:] else: raise InputError('Could not find position block in input.') # and parse it for line in posdata: line = line.split() if len(line) < 2: raise IOError( 'Wrong file format: line with less ' \ 'than two items in positions block.') pos = array(line[1:4], dtype='float64') if isfrac: pos = dot(result.cell, pos) try: dummy = int(line[0]) except: type = line[0] else: type = find_specie(atomic_number=dummy).symbol result.add_atom(pos=pos, type=type) if len(line) == 5: result[-1].magmom = float(line[4]) return result
def poscar(path="POSCAR", types=None): """ Tries to read a VASP POSCAR file. :param path: Path to the POSCAR file. Can also be an object with file-like behavior. :type path: str or file object :param types: Species in the POSCAR. :type types: None or sequence of str :return: `pylada.crystal.Structure` instance. """ import re from os.path import join, exists, isdir from copy import deepcopy from numpy import array, dot, transpose from numpy.linalg import det from quantities import angstrom from . import Structure # if types is not none, converts to a list of strings. if types is not None: if isinstance(types, str): types = [types] # can't see another way of doing this... elif not hasattr(types, "__iter__"): types = [str(types)] # single lone vasp.specie.Specie else: types = [str(s) for s in types] if path is None: path = "POSCAR" if not hasattr(path, 'read'): assert exists(path), IOError("Could not find path %s." % (path)) if isdir(path): assert exists(join(path, "POSCAR")), IOError("Could not find POSCAR in %s." % (path)) path = join(path, "POSCAR") result = Structure() poscar = path if hasattr(path, "read") else open(path, 'r') try: # gets name of structure result.name = poscar.readline().strip() if len(result.name) > 0: if result.name[0] == "#": result.name = result.name[1:].strip() # reads scale scale = float(poscar.readline().split()[0]) # gets cell vectors. cell = [] for i in range(3): line = poscar.readline() assert len(line.split()) >= 3,\ RuntimeError("Could not read column vector from poscar: %s." % (line)) cell.append( [float(f) for f in line.split()[:3]] ) result.cell = transpose(array(cell)) vol = det(cell) if scale < 1.E-8 : scale = abs(scale/vol) **(1.0/3) result.scale = scale * angstrom # checks for vasp 5 input. is_vasp_5 = True line = poscar.readline().split() for i in line: if not re.match(r"[A-Z][a-z]?", i): is_vasp_5 = False break if is_vasp_5: text_types = deepcopy(line) if types is not None and not set(text_types).issubset(set(types)): raise RuntimeError( "Unknown species in poscar: {0} not in {1}."\ .format(text_types, types) ) types = text_types line = poscar.readline().split() assert types is not None, RuntimeError("No atomic species given in POSCAR or input.") # checks/reads for number of each specie assert len(types) >= len(line), RuntimeError("Too many atomic species in POSCAR.") nb_atoms = [int(u) for u in line] # Check whether selective dynamics, cartesian, or direct. first_char = poscar.readline().strip().lower()[0] selective_dynamics = False if first_char == 's': selective_dynamics = True first_char = poscar.readline().strip().lower()[0] # Checks whether cartesian or direct. is_direct = first_char not in ['c', 'k'] # reads atoms. for n, specie in zip(nb_atoms, types): for i in range(n): line = poscar.readline().split() pos = array([float(u) for u in line[:3]], dtype="float64") if is_direct: pos = dot(result.cell, pos) result.add_atom(pos=pos, type=specie) if selective_dynamics: for which, freeze in zip(line[3:], ['x', 'y', 'z']): if which.lower()[0] == 't': result[-1].freeze = getattr(result[-1], 'freeze', '') + freeze finally: poscar.close() return result
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