Exemple #1
0
def randomize_biatom_13(atoms, type_a, type_b, ratio):
    """ replace randomly by clusters of 13 atoms
     to acheive target conc """
    n_A = 0
    n_B = 0
    for atom in atoms:
        if atom.symbol == type_a:
            n_A += 1
        elif atom.symbol == type_b:
            n_B += 1
        else:
            raise Exception('Extra chemical element %s!'%atom.symbol)
    #print n_A, n_B
    N = len(atoms)
    nl = NeighborList([1.5]*N, self_interaction=False, bothways=True)  # 2*1.5=3 Angstr. radius
    nl.build(atoms)
    #print "conc",  n_A *1.0 / N
    r = random.Random()
    while n_A < ratio*N:  # add A atoms randomly
        index = r.randint(0, N-1)
        if (atoms[index].symbol != type_a):
            #print "changing atom #"+str(index)+" to "+type_a
            #if (r.randint(0, 1000) < 500):
            atoms[index].symbol = type_a
            n_A += 1
            indeces, offsets = nl.get_neighbors(index)
            for ia in indeces :
                if (atoms[ia].symbol != type_a)&(n_A < ratio*N):
                    atoms[ia].symbol = type_a
                    n_A += 1
    return atoms
Exemple #2
0
def get_localEnv(frame, centerIdx, cutoff, onlyDict=False):
    '''
    Get the local atomic environment around an atom in an atomic frame.

    :param frame: ase or quippy Atoms object
    :param centerIdx: int
    Index of the local environment center.
    :param cutoff: float
    Cutoff radius of the local environment.
    :return: ase Atoms object
    Local atomic environment. The center atom is in first position.
    '''
    import ase.atoms
    import quippy.atoms
    from ase.neighborlist import NeighborList
    from ase import Atoms as aseAtoms
    if isinstance(frame, quippy.atoms.Atoms):
        atoms = qp2ase(frame)
    elif isinstance(frame, ase.atoms.Atoms):
        atoms = frame
    else:
        raise ValueError

    n = len(atoms.get_atomic_numbers())
    nl = NeighborList([
        cutoff / 2.,
    ] * n,
                      skin=0.,
                      sorted=False,
                      self_interaction=False,
                      bothways=True)
    nl.build(atoms)

    cell = atoms.get_cell()
    pbc = atoms.get_pbc()
    pos = atoms.get_positions()
    positions = [
        pos[centerIdx],
    ]
    zList = atoms.get_atomic_numbers()
    numbers = [
        zList[centerIdx],
    ]

    indices, offsets = nl.get_neighbors(centerIdx)

    # print offsets,len(atom.get_atomic_numbers())
    for i, offset in zip(indices, offsets):
        positions.append(pos[i] + np.dot(offset, cell))
        numbers.append(zList[i])

    atomsParam = dict(numbers=numbers, cell=cell, positions=positions, pbc=pbc)

    if onlyDict:
        return atomsParam
    else:
        return aseAtoms(**atomsParam)
Exemple #3
0
def hop_shuffle(atoms, A, B, count=10, R=3.0):
    """
    Shuffle atoms in given structure
    by swapping atom types within first coordination shell

    Parameters
    ----------
    atoms: ase.Atoms
        ase Atoms object, containing atomic cluster.
    A, B: string
        symbols of atoms to swap
    count: integer
        number of shuffles
    R: float
        radius of coordination shell, were atoms will be swapped

    Returns
    -------
        Function returns ASE atoms object whith
        shuffled atoms
    """
    n_atoms = len(atoms)
    nswaps = 0
    neiblist = NeighborList( [ R ] * n_atoms,
                             self_interaction=False,
                             bothways=True )
    neiblist.build( atoms )
    rnd = random.Random()
    while nswaps < count:
        i = rnd.randint(0, n_atoms-1)
        indeces, offsets = neiblist.get_neighbors( i )
        if (atoms[i].symbol == B):
            candidates = []
            for ii in indeces:
                if atoms[ii].symbol == A:
                    candidates.append( ii )
            if len(candidates) > 0:
                j = random.choice(candidates)
                atoms[i].symbol = A
                atoms[j].symbol = B
                nswaps += 1
                neiblist.build( atoms )
        elif (atoms[i].symbol == B):
            candidates = []
            for ii in indeces:
                if atoms[ii].symbol == A:
                    candidates.append( ii )
            if len(candidates) > 0:
                j = random.choice(candidates)
                atoms[i].symbol = B
                atoms[j].symbol = A
                nswaps += 1
                neiblist.build( atoms )
    return atoms
Exemple #4
0
def load_oqmd_data(num_dist_basis, dtype=float, cutoff=2.0, filter_query=None):
    """ Returns tuple (Z, D, y, num_species)
    where
        Z is a matrix, each row in Z corresponds to a molecule and the elements
        are atomic numbers
        D is a 4D tensor giving the Gaussian feature expansion of distances
            between atoms
        y is the energy of each molecule in kcal/mol
        num_species is the integer number of different atomic species in Z """
    import ase.db
    con = ase.db.connect("oqmd_all_entries.db")
    sel = filter_query

    # Create sorted list of species
    all_species = sorted(
        list(
            reduce(lambda x, y: x.union(set(y.numbers)), con.select(sel),
                   set())))
    # Insert dummy species 0
    all_species.insert(0, 0)
    # Create mapping from species to species index
    species_map = dict([(x, i) for i, x in enumerate(all_species)])

    # Find max number of atoms
    max_atoms = 0
    num_molecules = 0
    for row in con.select(sel):
        num_molecules += 1
        atoms = row.toatoms()
        max_atoms = max(max_atoms, len(atoms))

    # Gaussian basis function parameters
    mu_min = -1
    mu_max = 2 * cutoff + 1
    step = (mu_max - mu_min) / num_dist_basis
    sigma = step
    print "Number of molecules %d, max atoms %d" % (num_molecules, max_atoms)
    k = np.arange(num_dist_basis)
    D = np.zeros((num_molecules, max_atoms, max_atoms, num_dist_basis),
                 dtype=dtype)
    Z = np.zeros((num_molecules, max_atoms), dtype=np.int8)
    y = np.zeros(num_molecules, dtype=dtype)

    for i_mol, row in enumerate(con.select(sel)):
        print "feature expansion %10d/%10d\r" % (i_mol + 1, num_molecules),
        atoms = row.toatoms()
        y[i_mol] = row.total_energy
        Z[i_mol, 0:len(row.numbers)] = map(lambda x: species_map[x],
                                           row.numbers)
        assert np.all(atoms.get_atomic_numbers() == row.numbers)
        neighborhood = NeighborList([cutoff] * len(atoms),
                                    self_interaction=False,
                                    bothways=False)
        neighborhood.build(atoms)
        for ii in range(len(atoms)):
            neighbor_indices, offset = neighborhood.get_neighbors(ii)
            for jj, offs in zip(neighbor_indices, offset):
                ii_pos = atoms.positions[ii]
                jj_pos = atoms.positions[jj] + np.dot(offs, atoms.get_cell())
                dist = np.linalg.norm(ii_pos - jj_pos)
                d_expand = np.exp(-((dist - (mu_min + k * step))**2.0) /
                                  (2.0 * sigma**2.0))
                D[i_mol, ii, jj, :] += d_expand
                if jj != ii:
                    D[i_mol, jj, ii, :] += d_expand
    return Z, D, y, len(all_species)
Exemple #5
0
    def monoatomic(self, R1=3, calc_energy=False):
        r"""This routine analyzes atomic structure
        by the calculation of coordination numbers
        in cluster with only one type of atom.

        Parameters
        ----------
        R1: float
            First coordination shell will icnlude all atoms
            with distance less then R1 [Angstrom].
            Default value is 3.
        calc_energy: bool
            Flag used for calculation of potential energy with EMT
            calculator.  The default value is False, so that
            energy is not calculated.

        Returns
        -------
        N: int
            number of atoms in cluster
        R: float
            radius of the cluster
        CN: float
            average coord number
        E: float
            potential energy, -1 if calc_energy is False
        Ncore:
            number of atoms in core region (number of atoms with
            all 12 neighbors)
        CNshell:
            average coordination number for surface atoms only

        Notes
        -----
            The radius of the cluster is roughly determined as
            maximum the distance from the center to most distant atom
            in the cluster.

        Example
        --------
        >>> atoms = FaceCenteredCubic('Ag',
          [(1, 0, 0), (1, 1, 0), (1, 1, 1)], [7,8,7], 4.09)
        >>> qsar = QSAR(atoms)
        >>> qsar.monoatomic(R1=3.0)
        >>> print "average CN is ", qsar.CN
        """
        self.chems = ['*'] # any element. For now used for report only

        N = len(self.atoms)
        nl = NeighborList( [0.5 * R1] * N, self_interaction=False, bothways=True )
        nl.build(self.atoms)
        CN = 0
        Ncore = 0
        Nshell = 0
        CNshell = 0  # average CN of surface atoms
        for i in xrange(0, N):
            indeces, offsets = nl.get_neighbors(i)
            CN += len(indeces)
            if len(indeces) < 12:
                Nshell += 1
                CNshell += len(indeces)
            else:
                Ncore += 1
        CN = CN * 1.0 / N
        CNshell = CNshell * 1.0 / Nshell
        #atoms.center()
        R = self.atoms.positions.max() / 2.0
        if calc_energy:
            #from asap3 import EMT
            from ase.calculators.emt import EMT
            atoms.set_calculator(EMT())
            E = atoms.get_potential_energy()
        else:
            E = -1
        #return N, R, CN, E, Ncore, CNshell
        self.N = N #TODO: use array property CNs
        self.R = R
        self.CN = CN
        self.CNs = np.array([[CN]])
        self.E = E
        self.Ncore = Ncore
        self.CNshell = CNshell
Exemple #6
0
    def biatomic(self, A, B, R1=3.0, calc_energy=False):
        r"""This routine analyzes atomic structure
        by the calculation of coordination numbers
        in cluster with atoms of two types (A and B).

        Parameters
        ----------
        A: string
            atom type, like 'Ag', 'Pt', etc.
        B: string
            atom type, like 'Ag', 'Pt', etc.
        R1: float
            First coordination shell will icnlude all atoms
            with distance less then R1 [Angstrom].
            Default value is 3.
        calc_energy: bool
            Flag used for calculation of potential energy with EMT
            calculator.  The default value is False, so that
            energy is not calculated.

        Returns
        -------
        N: int
            number of atoms in cluster
        nA:
            number of atoms of type A
        R: float
            radius of the cluster
        CN_AA: float
            average number of atoms A around atom A
        CN_AB: float
            average number of atoms A around atom B
        CN_BB: float
            average number of atoms B around atom B
        CN_BA: float
            average number of atoms B around atom A
        etha: float
            parameter of local ordering, -1 < etha < 1.
            Returns 999 if concentration of one of the
            component is too low.
        E: float
            potential energy
        NAcore:
            number of A atoms in core
        NBcore:
            number of B atoms in core
        CNshellAA:
            average CN of A-A for surface atoms only
        CNshellAB:
            average CN of A-B for surface atoms only
        CNshellBB:
            average CN of B-B for surface atoms only
        CNshellBA:
            average CN of B-A for surface atoms only

        Notes
        -----
            The radius of the cluster is roughly determined as
            maximum the distance from the center to most distant atom
            in the cluster.

        Example
        --------
        >>> atoms = FaceCenteredCubic('Ag',
          [(1, 0, 0), (1, 1, 0), (1, 1, 1)], [7,8,7], 4.09)
        >>> atoms = CoreShellFCC(atoms, 'Pt', 'Ag', 0.6, 4.09)
        >>> [N, nA, R, CN_AA, CN_AB, CN_BB, CN_BA, etha] =
          biatomic(atoms, 'Pt', 'Ag')
        >>> print "Short range order parameter: ", etha
        """
        self.chems = [A, B] # for now used for report only

        N = len(self.atoms)
        nA = 0
        nB = 0
        for element in self.atoms.get_chemical_symbols():
            if element == A:
                nA += 1
            elif element == B:
                nB += 1
            else:
                raise Exception('Extra element ' + element)
        if (nA + nB != N):
            raise Exception('Number of A (' + str(nA) + ') ' +
              'and B (' + str(nB) + ') artoms mismatch!')
        nl = NeighborList([0.5 * R1] * N, self_interaction=False, bothways=True)
        nl.build(self.atoms)

        # initialize counters:
        CN_AA = 0    # averaged total coord. numbers
        CN_AB = 0
        CN_BB = 0
        CN_BA = 0
        NAcore = 0  # number of atoms in core region
        NBcore = 0
        CNshellAA = 0  # average coord. numbers for surface atoms
        CNshellAB = 0
        CNshellBB = 0
        CNshellBA = 0
        for iatom in xrange(0, N):
            #print "central atom index:", iatom, " kind: ", self.atoms[iatom].symbol
            indeces, offsets = nl.get_neighbors(iatom)
            if self.atoms[iatom].symbol == B:
                CN_BB_temp = 0
                CN_BA_temp = 0
                for ii in indeces:
                    #print "neighbor atom index:", ii, " kind: ", self.atoms[ii].symbol
                    if self.atoms[ii].symbol == B:
                        CN_BB_temp += 1
                    elif  self.atoms[ii].symbol == A:
                        CN_BA_temp += 1
                    else:
                        print("Warning: unknown atom type %s. It will not be counted!"%self.atoms[ii].symbol)
                CN_BB += CN_BB_temp
                CN_BA += CN_BA_temp
                if len(indeces) < 12:
                    # SHELL
                    CNshellBB += CN_BB_temp
                    CNshellBA += CN_BA_temp
                else:
                    # CORE
                    NBcore += 1
            elif self.atoms[iatom].symbol == A:
                CN_AA_temp = 0
                CN_AB_temp = 0
                for i in indeces:
                    #print "neighbor atom index:", i, " kind: ", self.atoms[i].symbol
                    if self.atoms[i].symbol == A:
                        CN_AA_temp += 1
                    elif self.atoms[i].symbol == B:
                        CN_AB_temp += 1
                    else:
                        print("Warning: unknown atom type %s. It will not be counted!"%self.atoms[i].symbol)
                CN_AA += CN_AA_temp
                CN_AB += CN_AB_temp
                if len(indeces) < 12:
                    # SHELL
                    CNshellAA += CN_AA_temp
                    CNshellAB += CN_AB_temp
                else:
                    # CORE
                    NAcore += 1
            else:
                #raise Exception("Un")
                print("Warning: unknown atom type %s. It will not be counted!"%self.atoms[iatom].symbol)
        # averaging:
        CN_AA = CN_AA * 1.0 / nA
        CN_AB = CN_AB * 1.0 / nA
        CN_BB = CN_BB * 1.0 / nB
        CN_BA = CN_BA * 1.0 / nB
        znam = (nA - NAcore)
        if znam > 0.0001:
            CNshellAA = CNshellAA * 1.0 / znam
            CNshellAB = CNshellAB * 1.0 / znam
        else:
            CNshellAA = 0
            CNshellAB = 0
        znam = (nB - NBcore)
        if znam > 0.0001:
            CNshellBB = CNshellBB * 1.0 / znam
            CNshellBA = CNshellBA * 1.0 / znam
        else:
            CNshellBB = 0
            CNshellBA = 0

        # calc concentrations:
        concB = nB * 1.0 / N
        znam = concB * (CN_AA + CN_AB)
        if znam < 0.0001:
            #print "WARNING! Too low B concentration: ",concB
            etha = 999
        else:
            etha = 1 - CN_AB / znam
        R = self.atoms.positions.max() / 2.0
        if calc_energy:
            #from asap3 import EMT
            from ase.calculators.emt import EMT
            self.atoms.set_calculator(EMT())
            E = self.atoms.get_potential_energy()
        else:
            E = -1
        #return N, nA, R, CN_AA, CN_AB, CN_BB, CN_BA, etha, E, NAcore, \
        #  NBcore, CNshellAA, CNshellAB, CNshellBB, CNshellBA
        self.N = N
        self.nA = nA
        self.R = R
        self.CN_AA = CN_AA  #TODO: use only arrays of CNs
        self.CN_AB = CN_AB
        self.CN_BB = CN_BB
        self.CN_BA = CN_BA
        self.CNs = np.array([ [CN_AA, CN_AB], [CN_BA, CN_BB] ])
        self.etha = etha
        self.E = E
        self.NAcore = NAcore
        self.NBcore = NBcore
        self.CNshellAA = CNshellAA
        self.CNshellAB = CNshellAB
        self.CNshellBB = CNshellBB
        self.CNshellBA = CNshellBA
Exemple #7
0
def CoreShellCN(atoms, type_a, type_b, ratio, R_min = 1.5, CN_max=12, n_depth=-1):
    r"""This routine generates cluster with ideal core-shell architecture,
    so that atoms of type_a are placed on the surface
    and atoms of type_b are forming the core of nanoparticle.
    The 'surface' of nanoparticle is defined as atoms
    with unfinished first coordination shell.
      Used algorithm without need for explicit knowledge of
    far coordination shells parameters, as it was required in CoreShellFCC(..)

    Parameters
    ----------
    atoms: ase.Atoms
        ase Atoms object, containing atomic cluster.
    type_a: string
        Symbol of chemical element to be placed on the shell.
    type_b: string
        Symbol of chemical element to be placed in the core.
    ratio: float
        Guards the number of shell atoms, type_a:type_b = ratio:(1-ratio)
    R_min: float
        Typical bond length. Neighboring atoms within this value are counted
        as coordination numbers.
        Default is 3.0.
    CN_max: float
        Maximum possible coordination number (bulk coordination number).
        Default is 12.
    n_depth: int
        Number of layers of the shell formed by atoms ratio.
        Default value -1 is ignored and n_depth is calculated according
        ratio. If n_depth is set then value of ratio is ignored.

    Returns
    -------
        Function returns ASE atoms object which
        contains bimetallic core-shell cluster

    Example
    --------
    >>> atoms = FaceCenteredCubic('Ag',
      [(1, 0, 0), (1, 1, 0), (1, 1, 1)], [7,8,7], 4.09)
    >>> atoms = CoreShellCN(atoms, 'Pt', 'Ag', 0.5)
    >>> view(atoms)
    """
    # 0 < ratio < 1
    target_x = ratio
    if n_depth != -1:
        target_x = 1

    n_atoms = len(atoms)
    n_a = (np.array(atoms.get_chemical_symbols()) == type_a).sum()
    #n_b = (atoms.get_chemical_symbols() == type_b).sum()
    #print n_a

    n_shell = 0  # depth of the shell
    while (n_a < n_atoms * target_x):
        n_shell += 1
        print ("shell: ", n_shell)

        if (n_depth != -1)and(n_shell > n_depth):
            break

        neiblist = NeighborList( [ R_min ] * n_atoms,
          self_interaction=False, bothways=True )
        neiblist.build( atoms )

        for i in xrange( n_atoms ):
            indeces, offsets = neiblist.get_neighbors(i)
            if (atoms[i].symbol == type_b):
                CN_temp = 0
                for ii in indeces:
                    if atoms[ii].symbol == type_b:
                        CN_temp += 1
                #print "CN_temp: ", CN_temp
                if (CN_temp < CN_max):
                    # coord shell is not full, swap type to type_a!
                    atoms[i].tag = n_shell # not swap yet, but mark

        # swap atom types now. Stop if target ratio achieved
        for atom in atoms:
            if (atom.tag > 0)&(atom.symbol == type_b):
                if n_a < n_atoms * target_x:
                    atom.symbol = type_a
                    n_a += 1
                    #print "n_A: ", n_a
    # end while

    # check number of atoms
    checkn_a = 0
    for element in atoms.get_chemical_symbols():
        if element == type_a:
            checkn_a += 1
    #print "Should be equal: ", n_a, checkn_a
    assert n_a == checkn_a
    return atoms
Exemple #8
0
def CoreShellFCC(atoms, type_a, type_b, ratio, a_cell, n_depth=-1):
    r"""This routine generates cluster with ideal core-shell architecture,
    so that atoms of type_a are placed on the surface
    and atoms of type_b are forming the core of nanoparticle.
    The 'surface' of nanoparticle is defined as atoms
    with unfinished coordination shell.

    Parameters
    ----------
    atoms: ase.Atoms
        ase Atoms object, containing atomic cluster.
    type_a: string
        Symbol of chemical element to be placed on the shell.
    type_b: string
        Symbol of chemical element to be placed in the core.
    ratio: float
        Guards the number of shell atoms, type_a:type_b = ratio:(1-ratio)
    a_cell: float
        Parameter of FCC cell, in Angstrom.
        Required for calculation of neighbor distances in for infinite
        crystal.
    n_depth: int
        Number of layers of the shell formed by atoms ratio.
        Default value -1 is ignored and n_depth is calculated according
        ratio. If n_depth is set then value of ratio is ignored.

    Returns
    -------
        Function returns ASE atoms object which
        contains bimetallic core-shell cluster

    Notes
    -----
        The criterion of the atom beeing on the surface is incompletnes
        of it's coordination shell. For the most outer atoms the first
        coordination shell will be incomplete (coordination number
        is less then 12 for FCC), for the second layer --
        second coordination shell( CN1 + CN2 < 12 + 6) and so on.
        In this algorithm each layer is tagged by the number
        ('depth'), take care if used with other routines
        dealing with tags (add_adsorbate etc).

        First, atoms with unfinished first shell are replaced
        by atoms type_a, then second, and so on.
        The last depth surface layer is replaced by random
        to maintain given ratio value.

    Example
    --------
    >>> atoms = FaceCenteredCubic('Ag',
      [(1, 0, 0), (1, 1, 0), (1, 1, 1)], [7,8,7], 4.09)
    >>> atoms = CoreShellFCC(atoms, 'Pt', 'Ag', 0.6, 4.09)
    >>> view(atoms)
    """
    # 0 < ratio < 1
    target_x = ratio
    if n_depth != -1:
        target_x = 1  # needed to label all needed layeres

    def fill_by_tag(atoms, chems, tag):
        """Replaces all atoms within selected layer"""
        for i in xrange(0, len(atoms)):
            if atoms[i].tag == tag:
                chems[i] = type_a
        return
    # coord numbers for FCC:
    coord_nums = [1, 12, 6, 24, 12, 24, 8, 48, 6, 36, 24, 24, 24, 72, 48,
    12, 48, 30, 72, 24]
    # coordination radii obtained from this array as R = sqrt(coord_radii)*a/2
    coord_radii = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 30,
    32, 34, 36, 38, 40]

    ## generate FCC cluster ##
    #atoms = FaceCenteredCubic(type_b, surfaces, layers, a_cell)
    n_atoms = len(atoms)
    ## tag layers ##
    positions = [0]  # number of positions in layer
    n_tag = 0    # number of tags to check if there is enought layers
    n_shell = 0  # depth of the shell
    while (n_tag < n_atoms * target_x):
        n_shell += 1
        if (n_depth != -1)and(n_shell > n_depth):
            break
        neiblist = NeighborList(
          [
            a_cell / 2.0 * sqrt(coord_radii[n_shell]) / 2.0 + 0.0001
          ] * n_atoms,
          self_interaction=False, bothways=True
        )
        neiblist.build(atoms)
        for i in xrange(0, n_atoms):
            indeces, offsets = neiblist.get_neighbors(i)
            if (atoms[i].tag == 0):
                if (len(indeces) < sum(coord_nums[1:n_shell + 1])):
                    # coord shell is not full -> atom is on surface!
                    atoms[i].tag = n_shell
                    n_tag += 1
        # save the count of positions at each layer:
        positions.append(n_tag - sum(positions[0:n_shell]))
    ## populate layers ##
    chems = atoms.get_chemical_symbols()
    n_type_a = 0  # number of changes B -> A
    if (n_tag < n_atoms * target_x)and(n_depth == -1):
        # raise exception?
        return None
    else:
        n_filled = n_shell - 1  # number of totally filled layers
        ilayer = 1
        while (ilayer < n_filled + 1):
            fill_by_tag(atoms, chems, ilayer)
            n_type_a += positions[ilayer]
            ilayer += 1
        while (n_type_a < n_atoms * target_x)and(n_depth == -1):
            i = random.randint(0, n_atoms - 1)
            if (atoms[i].tag == n_shell):
                if (chems[i] == type_b):
                    chems[i] = type_a
                    n_type_a += 1
    atoms.set_chemical_symbols(chems)
    ## check number of atoms ##
    checkn_a = 0
    for element in chems:
        if element == type_a:
            checkn_a += 1
    assert n_type_a == checkn_a
    return atoms