Exemple #1
0
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 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)
Exemple #3
0
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)
Exemple #5
0
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
Exemple #6
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
Exemple #8
0
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
Exemple #10
0
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
Exemple #11
0
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
Exemple #13
0
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
Exemple #14
0
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)
Exemple #17
0
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
Exemple #20
0
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
Exemple #21
0
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
Exemple #22
0
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