def __init__(
            self,
            initial_structure: Structure,
            task: Task,
            kpt_mode: Optional[KpointsMode] = None,  # None for default setting
            kpt_density: Optional[float] = None,  # in Å
            gamma_centered: Optional[bool] = None,  # Vasp definition
            only_even_num_kpts: bool = False,  # If ceil kpt numbers to be even.
            num_kpt_factor: Optional[int] = None,  # Set NKRED to this as well.
            band_ref_dist: float = defaults.band_mesh_distance,
            symprec: float = defaults.symmetry_length_tolerance,
            angle_tolerance: float = defaults.symmetry_angle_tolerance,
            is_magnetization: bool = False):  # Whether the system is magnetic.

        self._initial_structure = initial_structure.copy()
        self._task = task
        self._kpt_density = kpt_density or defaults.kpoint_density
        self._is_magnetization = is_magnetization
        self._num_kpt_factor = num_kpt_factor or self._task.default_kpt_factor
        self._symmetrizer = \
            StructureSymmetrizer(structure=self._initial_structure,
                                 symprec=symprec,
                                 angle_tolerance=angle_tolerance,
                                 band_mesh_distance=band_ref_dist,
                                 time_reversal=(not self._is_magnetization))
        # overwrite options fixed by other options
        self._gamma_centered = task.requisite_gamma_centered or gamma_centered
        self._adjust_only_even_num_kpts(only_even_num_kpts)
        self._adjust_kpt_mode(kpt_mode)
Ejemplo n.º 2
0
def is_equivalent(structure : Structure, atoms_1 : tuple, atoms_2 : tuple , eps=0.05):
    """

    Find Vacancy Strucutres for diffusion into and out of the specified atom_i site.

    :param structure: Structure
        Structure to calculate diffusion pathways
    :param atom_i: int
        Atom to get diffion path from
    :return: [ Structure ]
    """

    # To Find Pathway, look for voronoi edges
    structure = structure.copy() # type: Structure

    coords = get_midpoint(structure, atoms_1[0], atoms_1[1])
    structure.append('H', coords)
    coords = get_midpoint(structure, atoms_2[0], atoms_2[1])
    structure.append('H', coords)

    dist_1 = structure.get_neighbors(structure[-2], 3)
    dist_2 = structure.get_neighbors(structure[-1], 3)
    dist_1.sort(key=lambda x: x[1])
    dist_2.sort(key=lambda x: x[1])
    for (site_a, site_b) in zip(dist_1, dist_2):
        if abs(site_a[1] - site_b[1]) > eps:
            return False
        elif site_a[0].specie != site_b[0].specie:
            return False
    return True
Ejemplo n.º 3
0
    def merge_clashing(s: Structure, tolerance_factor: float = 0.9):
        """
        Naive method for 'merging' clashing sites.
        In case it the two sites are not the same elements, priority will be given to the heavier one.

        Args:
            s:

        Returns:

        """
        crystal = s.copy()
        duplicates = get_duplicates_dynamic_threshold(s, tolerance_factor)
        logger.debug('found %s clashing sites', duplicates)
        deleted = []
        for duplicate in duplicates:
            if (not duplicate[0] in deleted) and (not duplicate[1] in deleted):
                element0 = crystal[duplicate[0]].specie.number
                element1 = crystal[duplicate[1]].specie.number
            if element0 == element1:
                deleted.append(duplicate[1])
            elif element0 > element1:
                deleted.append(duplicate[1])
            elif element0 < element1:
                deleted.append(duplicate[0])
        crystal.remove_sites(deleted)
        return crystal
Ejemplo n.º 4
0
def poscar_from_sitelist( configs, labels, sitelists, structure, subset = None ):
    """
    Uses pymatgen Structure.to() method to generates POSCAR files for a set of 
    configurations within a parent structure.
    Args:
        configs   (list):               site configurations
        labels    (list):               atom labels to output
        sitelist  (list):               list of sites in fractional coords
        structure (pymatgen Structure): Parent structure
        subset    (Optional [list]):    list of atom indices to output 
    """

    if subset:
        species_clean = [ spec for i,spec in enumerate( structure.species ) if i not in subset ]
        species_config = [ spec for i,spec in enumerate( structure.species ) if i in subset ]
        frac_coords_clean = [ coord for i, coord in enumerate( structure.frac_coords ) if i not in subset ]
        clean_structure = Structure( structure.lattice, species_clean, frac_coords_clean )

    else:
        clean_structure = Structure( structure.lattice, [], [] )
        species_config = structure.species

    for idx, config in enumerate( configs, start=1 ):
       structure_config = clean_structure.copy()
       for label in labels:
           for pos in config.position( label ):
               for sitelist in sitelists:
                   structure_config.append( species_config[ pos ], sitelist[ pos ] )
       structure_config.to( filename="POSCAR_{}.vasp".format( idx ) )
Ejemplo n.º 5
0
def agnr_edge_types(cell: Structure, atom_list=None, combinations=None):

    #To check whether the graphene structure is broken if there is an atom with only 1 bond
    def broken_structure(cell: Structure):
        all_bonds = calculate_bond_list(structure=cell)
        for atoms in all_bonds:
            if len(atoms) == 1:
                return True
        return False

    #Get all the bonds
    all_bonds = calculate_bond_list(structure=cell)

    #Get the atoms with only 2 bonds
    atoms_2bonds = []
    atoms_2bonds_index = []
    for index, atoms in enumerate(all_bonds):
        if len(atoms) == 2:
            atoms_2bonds.append(atoms)
            atoms_2bonds_index.append(index)

    #Store the combinations of atoms indices list
    if combinations == None:
        combinations = set()

    if atom_list is None:
        atom_list = [i for i in range(len(all_bonds))]

    #Store all possible combinations of the cell with the removed atoms in a list
    structures = []
    for atoms in atoms_2bonds:
        for bonds in atoms:
            #Go through the atoms with only 2 bonds and check if its neighbor has 2 bonds.
            if bonds[4] in atoms_2bonds_index:
                atom_list_temp = atom_list.copy()

                #Remove the atoms from indices list
                if bonds[3] > bonds[4]:
                    del atom_list_temp[bonds[3]]
                    del atom_list_temp[bonds[4]]
                else:
                    del atom_list_temp[bonds[4]]
                    del atom_list_temp[bonds[3]]

                #Remove the atoms from structure
                cell_temp = cell.copy()
                cell_temp.remove_sites([bonds[3], bonds[4]])

                #Check whether there are duplicates and if the structure is broken
                if tuple(atom_list_temp
                         ) not in combinations and broken_structure(
                             cell_temp) == False:
                    combinations.add(tuple(atom_list_temp))
                    structures.append(cell_temp)
                    structures.extend(
                        edge_types(cell_temp, atom_list_temp, combinations))

    return structures
Ejemplo n.º 6
0
def remove_random_carbon(structure: Structure) -> Structure:
    structure = structure.copy()

    carbon_indices = [
        idx for idx, s in enumerate(structure.species) if s == Element.C
    ]
    to_remove = np.random.randint(len(carbon_indices))

    structure.remove_sites([to_remove])
    return structure
Ejemplo n.º 7
0
    def __init__(self,
                 structure: Structure,
                 symprec: float = defaults.symmetry_length_tolerance,
                 angle_tolerance: float = defaults.symmetry_angle_tolerance,
                 time_reversal: bool = True,
                 band_mesh_distance: float = defaults.band_mesh_distance):
        """Get full information of seekpath band path.

        Note: site properties such as magmom are removed.

        The structures of aP (SG:1, 2), mC (5, 8, 9, 12, 15) and
        oA (38, 39, 40, 41) can be different between spglib and seekpath.
        see Y. Hinuma et al. Comput. Mater. Sci. 128 (2017) 140–184
        -- spglib mC
         6.048759 -3.479491 0.000000
         6.048759  3.479491 0.000000
        -4.030758  0.000000 6.044512
        -- seekpath mC
         6.048759  3.479491  0.000000
        -6.048759  3.479491  0.000000
        -4.030758  0.000000  6.044512
        """
        self.structure = structure.copy()
        if structure.site_properties:
            logger.warning(
                f"Site property {structure.site_properties.keys()} "
                f"removed in primitive and conventional structures.")
        self.symprec = symprec
        self.angle_tolerance = angle_tolerance
        self.time_reversal = time_reversal
        self.ref_distance = band_mesh_distance
        lattice_matrix = structure.lattice.matrix
        positions = structure.frac_coords.tolist()
        atomic_numbers = [i.specie.number for i in structure.sites]
        self.cell = (lattice_matrix, positions, atomic_numbers)
        # evaluated lazily
        self._spglib_sym_data = None
        self._conventional = None
        self._primitive = None
        self._second_primitive = None
        self._seekpath_data = None
        self._band_primitive = None
        self._irreducible_kpoints = None
Ejemplo n.º 8
0
    def check_unbound(s: Structure,
                      whitelist: list = ['H'],
                      threshold: float = 2.5,
                      mode='naive') -> bool:
        """
        This uses the fact that unbound solvent is often in pores
        and more distant from all other atoms. So far this test focusses on water.

        Args:
            s (pymatgen structure object): structure to be checked
            whitelist (list): elements that are not considered in the check (they are basically removed from the
                structure)
            mode (str): checking mode. If 'naive' then a simple distance based check is used and a atom is detected
                as unbound it there is no other atom within the threshold distance.

        Returns:

        """

        crystal = s.copy()

        if whitelist:
            crystal.remove_species(whitelist)

        if mode == 'naive':
            for atom in crystal:
                neighbors = crystal.get_neighbors(atom, threshold)
                if len(neighbors) == 0:
                    return True
            return False

        if mode == 'graph':
            nn_strategy = JmolNN()
            sgraph = StructureGraph.with_local_env_strategy(
                crystal, nn_strategy)
            molecules = get_subgraphs_as_molecules_all(sgraph)

            if len(molecules) > 0:
                return True
            else:
                return False
Ejemplo n.º 9
0
    def remove_unbound_solvent(structure: Structure) -> Structure:
        """
        Constructs a structure graph and removes unbound solvent molecules
        if they are in a hardcoded composition list.

        Args:
            structure (pymatgen structure object=:

        Returns:

        """
        crystal = structure.copy()
        molecules_solvent = ['H2 O1', 'H3 O1', 'C2 H6 O S', 'O1']
        nn_strategy = JmolNN()
        sgraph = StructureGraph.with_local_env_strategy(crystal, nn_strategy)
        molecules = get_subgraphs_as_molecules_all(sgraph)
        cart_coordinates = crystal.cart_coords
        indices = []
        for molecule in molecules:
            print(str(molecule.composition))
            if molecule.formula in molecules_solvent:
                for coord in [
                        site.as_dict()['xyz'] for site in molecule.sites
                ]:
                    if (coord[0] < crystal.lattice.a) and (
                            coord[1] < crystal.lattice.b) and (
                                coord[2] < crystal.lattice.c):
                        print(coord)
                        indices.append(
                            np.where(
                                np.prod(
                                    np.isclose(cart_coordinates - coord, 0),
                                    axis=1) == 1)[0][0])

        print(indices)
        crystal.remove_sites(indices)

        return crystal
class Test2D(unittest.TestCase):
    def setUp(self):

        ## simple toy structure for preliminary testing

        self.a0 = 3.0
        self.c = 20.0

        self.structure = Structure(
            Lattice.from_parameters(a=self.a0,
                                    b=self.a0,
                                    c=self.c,
                                    alpha=90,
                                    beta=90,
                                    gamma=90), ["O", "O"],
            [[0.0, 0.0, 0.1], [0.5, 0.5, 0.3]])

        self.nvecs = [3, 3, 1]
        self.vacuum = 20
        self.q = 0
        self.structure.make_supercell(self.nvecs)
        self.structure_bulk = self.structure.copy()

        self.initdef_list = []
        self.initdef_list.append(
            {"def1": {
                "type": "vac",
                "species": "O",
                "index": 0
            }})
        self.initdef_list.append({
            "def1": {
                "type": "sub",
                "index": -1,
                "species": "O",
                "species_new": "N"
            }
        })
        self.initdef_list.append({
            "def1": {
                "type": "sub",
                "index": -1,
                "species": "O",
                "species_new": "N"
            },
            "def2": {
                "type": "vac",
                "index": -1,
                "species": "O",
                "index_offset_n1n2": -1
            }
        })
        self.initdef_list.append({
            "def1": {
                "type": "ad",
                "index": [-1],
                "species": ["O"],
                "species_new": "N",
                "shift_z": "3.0"
            }
        })
        self.initdef_list.append({
            "def1": {
                "type": "int",
                "index": [0, 0, 0, 0],
                "index_offset_n1": [0, 1, 0, 0],
                "index_offset_n2": [0, 0, 0, 2],
                "index_offset_n1n2": [0, 0, 1, 1],
                "species": 4 * ["O"],
                "species_new": "N"
            }
        })
        self.create_defects()

        self.siteinds_list = [[0], [17], [17, 8], [[17]], [[0, 3, 9, 11]]]
        self.defcoords_list = [[[0.0, 0.0, 0.1]], [[0.833333, 0.833333, 0.3]],
                               [[0.833333, 0.833333, 0.3],
                                [0.666667, 0.666667, 0.1]],
                               [[0.833333, 0.833333, 0.45]],
                               [[0.166667, 0.0, 0.2]]]
        self.natoms_list = [17, 18, 17, 19, 19]

    def create_defects(self):

        ## create defects

        self.defects_list = []

        for initdef in self.initdef_list:

            ## initialize defect object
            defect = gen_defect_supercells.Defect(self.structure_bulk,
                                                  self.structure.copy(),
                                                  self.nvecs, self.vacuum,
                                                  self.q)

            ## set the defect info (type, site, species) for each defect
            for d in initdef:
                initdef[d]["index_offset_n1"] = initdef[d].get(
                    "index_offset_n1", 0)
                initdef[d]["index_offset_n2"] = initdef[d].get(
                    "index_offset_n2", 0)
                initdef[d]["index_offset_n1n2"] = initdef[d].get(
                    "index_offset_n1n2", 0)
                defect_site, siteinds = defect.get_defect_site(initdef[d])
                defect.add_defect_info(initdef[d], defect_site)

            ## create defect(s)
            defect.remove_atom()
            defect.replace_atom()
            defect.add_atom()
            self.defects_list.append(defect)

    def test_get_site_index(self):

        ## test get_site_index returns the correct absolute site indices

        ## initialize dummy "defect" object
        defect = gen_defect_supercells.Defect(self.structure_bulk,
                                              self.structure.copy(),
                                              self.nvecs, self.vacuum, self.q)

        for initdef, siteinds in zip(self.initdef_list, self.siteinds_list):
            for d, siteind_ref in zip(initdef, siteinds):

                if initdef[d]["type"][0] == "v" or initdef[d]["type"][0] == "s":
                    siteind = defect.get_site_ind(
                        initdef[d]["index"], initdef[d]["species"],
                        initdef[d]["index_offset_n1"],
                        initdef[d]["index_offset_n2"],
                        initdef[d]["index_offset_n1n2"])
                    self.assertEqual(siteind, siteind_ref)

                if initdef[d]["type"][0] == "a" or initdef[d]["type"][0] == "i":
                    for siteindi_ref,ind,sp,offset_n1,offset_n2,offset_n1n2 \
                        in zip(siteind_ref,
                               initdef[d]["index"],
                               initdef[d]["species"],
                               initdef[d]["index_offset_n1"],
                               initdef[d]["index_offset_n2"],
                               initdef[d]["index_offset_n1n2"]):
                        siteind = defect.get_site_ind(ind, sp, offset_n1,
                                                      offset_n2, offset_n1n2)
                        self.assertEqual(siteind, siteindi_ref)

    def test_defcoords(self):

        ## test that the defect(s) are created at the correct position
        ## essentially tests the get_defect_site function

        for defects, defcoords_ref in zip(self.defects_list,
                                          self.defcoords_list):
            for defect_site, defcoord_ref in zip(defects.defect_site,
                                                 defcoords_ref):
                defcoord = defect_site.lattice.get_fractional_coords(
                    defect_site.coords)
                #                defcoord = defect_site.frac_coords
                for j in range(3):
                    self.assertAlmostEqual(defcoord[j] % 1 % 1,
                                           defcoord_ref[j] % 1 % 1,
                                           places=6)

    def test_natoms(self):

        ## test that the defect creation results in the correct number of atoms

        for defect, natoms in zip(self.defects_list, self.natoms_list):
            self.assertEqual(defect.structure.num_sites, natoms)
Ejemplo n.º 11
0
Archivo: fss.py Proyecto: uw-cmg/MAST
    return nfinal

# Potential issues:
#	--Poor accounting of internal periodicity  
#		-definition of relative term can be ambiguous depending on structure
#	--Assumes defect structure and primordial structures are aligned

if __name__ == "__main__":
    #Sanity checks	
    #Test 1 - Final size cannot be constructed directly from primordial structure
    ssize=[5,5,5]
    fsize=[9,5,5]
    psize=[2,5,5]
    felat = Lattice.cubic(2.87)
    primf = Structure(felat,["Fe","Fe"],[[0,0,0],[0.5,0.5,0.5]])
    primordialstructure = primf.copy()
    primordialstructure.make_supercell(psize)
    f = primf.copy()
    f.make_supercell(fsize)
    write_structure(primordialstructure,'POSCAR_prim1')
    final = finite_size_scale('POSCAR_Fdefect', ssize, 'POSCAR_prim1', fsize, psize)
    write_structure(f,'POSCAR_F1expected')
    write_structure(final,'POSCAR_F1')
    
    #Test 2 - Test in all 3 dimensions
    ssize=[5,5,5]
    fsize=[8,8,8]
    psize=[1,1,1]
    primordialstructure = primf.copy()
    primordialstructure.make_supercell(psize)
    f = primf.copy()
Ejemplo n.º 12
0
    def test_supercell_subsets(self):
        sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5,
                              primitive_cell=False, scale=True,
                              attempt_supercell=True, allow_subset=True,
                              supercell_size='volume')
        sm_no_s = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5,
                                   primitive_cell=False, scale=True,
                                   attempt_supercell=True, allow_subset=False,
                                   supercell_size='volume')
        l = Lattice.orthorhombic(1, 2, 3)
        s1 = Structure(l, ['Ag', 'Si', 'Si'],
                       [[.7, .4, .5], [0, 0, 0.1], [0, 0, 0.2]])
        s1.make_supercell([2, 1, 1])
        s2 = Structure(l, ['Si', 'Si', 'Ag'],
                       [[0, 0.1, -0.95], [0, 0.1, 0], [-.7, .5, .375]])

        shuffle = [0, 2, 1, 3, 4, 5]
        s1 = Structure.from_sites([s1[i] for i in shuffle])

        # test when s1 is exact supercell of s2
        result = sm.get_s2_like_s1(s1, s2)
        for a, b in zip(s1, result):
            self.assertTrue(a.distance(b) < 0.08)
            self.assertEqual(a.species, b.species)

        self.assertTrue(sm.fit(s1, s2))
        self.assertTrue(sm.fit(s2, s1))
        self.assertTrue(sm_no_s.fit(s1, s2))
        self.assertTrue(sm_no_s.fit(s2, s1))

        rms = (0.048604032430991401, 0.059527539448807391)
        self.assertTrue(np.allclose(sm.get_rms_dist(s1, s2), rms))
        self.assertTrue(np.allclose(sm.get_rms_dist(s2, s1), rms))

        # test when the supercell is a subset of s2
        subset_supercell = s1.copy()
        del subset_supercell[0]
        result = sm.get_s2_like_s1(subset_supercell, s2)
        self.assertEqual(len(result), 6)
        for a, b in zip(subset_supercell, result):
            self.assertTrue(a.distance(b) < 0.08)
            self.assertEqual(a.species, b.species)

        self.assertTrue(sm.fit(subset_supercell, s2))
        self.assertTrue(sm.fit(s2, subset_supercell))
        self.assertFalse(sm_no_s.fit(subset_supercell, s2))
        self.assertFalse(sm_no_s.fit(s2, subset_supercell))

        rms = (0.053243049896333279, 0.059527539448807336)
        self.assertTrue(np.allclose(sm.get_rms_dist(subset_supercell, s2), rms))
        self.assertTrue(np.allclose(sm.get_rms_dist(s2, subset_supercell), rms))

        # test when s2 (once made a supercell) is a subset of s1
        s2_missing_site = s2.copy()
        del s2_missing_site[1]
        result = sm.get_s2_like_s1(s1, s2_missing_site)
        for a, b in zip((s1[i] for i in (0, 2, 4, 5)), result):
            self.assertTrue(a.distance(b) < 0.08)
            self.assertEqual(a.species, b.species)

        self.assertTrue(sm.fit(s1, s2_missing_site))
        self.assertTrue(sm.fit(s2_missing_site, s1))
        self.assertFalse(sm_no_s.fit(s1, s2_missing_site))
        self.assertFalse(sm_no_s.fit(s2_missing_site, s1))

        rms = (0.029763769724403633, 0.029763769724403987)
        self.assertTrue(np.allclose(sm.get_rms_dist(s1, s2_missing_site), rms))
        self.assertTrue(np.allclose(sm.get_rms_dist(s2_missing_site, s1), rms))
Ejemplo n.º 13
0
def zgnr_edge_types(cell: Structure, atom_list=None, combinations=None):
    def recurse_bonds(current_bonds: np.array,
                      all_bonds: np.array,
                      previous_atom=-1,
                      traverse_atoms=None):
        ''' 
        Get a set of atoms that can be traversed by starting from a atom

        Return a dictionary, traverse_atoms, in which the keys are the atoms traversed and the 
        values are a set of periodic direction to move to that atom
        '''

        if traverse_atoms == None:
            traverse_atoms = dict()

        for bonds in current_bonds:
            # Do not move back to the previous atom
            # Do not move in the negative periodic direction
            if bonds[4] != previous_atom and bonds[0] != -1:
                current_atom = bonds[3]
                neighbor_atom = bonds[4]

                # Create the key if the atom has not been traversed
                if neighbor_atom not in traverse_atoms:
                    traverse_atoms[neighbor_atom] = {bonds[0]}
                    recurse_bonds(all_bonds[neighbor_atom], all_bonds,
                                  current_atom, traverse_atoms)
                # Add the new periodic direction. The values can only be 0 or 1
                else:
                    traverse_atoms[neighbor_atom].add(bonds[0])

        return traverse_atoms

    def broken_structure(cell: Structure):
        ''' 
        Check whether the graphene structure is broken if there is an atom with only 1 bond or 
        if the structure is no longer periodic
        '''
        try:
            all_bonds = calculate_bond_list(structure=cell)
        except IndexError:
            return True

        for atoms in all_bonds:
            if atoms.shape[0] == 1:
                return True

        # Get the list of atoms traversed by starting from atom 0
        atom0 = all_bonds[0][0][3]
        traverse_atoms = recurse_bonds(all_bonds[0], all_bonds)
        traverse_path = set()
        for i in traverse_atoms.values():
            traverse_path.update(i)

        # If there is no path moving back to atom 0 or if no positive periodic direction is
        # taken, then the structure is broken
        if atom0 not in traverse_atoms.keys() or 1 not in traverse_path \
            or len(traverse_atoms.keys()) != len(all_bonds):
            return True

        return False

    all_bonds = calculate_bond_list(structure=cell)

    # Get the atoms with only 2 bonds
    atoms_2bonds_index = set()
    for index, atoms in enumerate(all_bonds):
        if atoms.shape[0] == 2:
            atoms_2bonds_index.add(index)

    # Store the set of atoms of the structures for search for duplicates
    if combinations == None:
        combinations = set()

    if atom_list is None:
        atom_list = [i for i in range(len(all_bonds))]

    # Store all possible structures with the removed atoms in a list
    structures = []

    for atoms in all_bonds:
        bond1 = atoms[0]
        bond2 = atoms[1]
        bond1_in_2bonds = bond1[4] in atoms_2bonds_index
        bond2_in_2bonds = bond2[4] in atoms_2bonds_index

        cell_temp = cell.copy()
        atom_list_temp = atom_list.copy()

        # Remove the atoms from both the atom_list and the structure
        bond_list = None
        if bond1_in_2bonds and bond2_in_2bonds:
            bond_list = [bond1[3], bond1[4], bond2[4]]
            cell_temp.remove_sites([bond1[3], bond1[4], bond2[4]])
        elif atoms.shape[0] == 3:
            bond3 = atoms[2]
            bond3_in_2bonds = bond3[4] in atoms_2bonds_index

            if bond1_in_2bonds and bond3_in_2bonds:
                bond_list = [bond1[3], bond1[4], bond3[4]]
                cell_temp.remove_sites([bond1[3], bond1[4], bond3[4]])
            elif bond2_in_2bonds and bond3_in_2bonds:
                bond_list = [bond2[3], bond2[4], bond3[4]]
                cell_temp.remove_sites([bond2[3], bond2[4], bond3[4]])
        elif bond1[3] in atoms_2bonds_index and bond1_in_2bonds:
            del atom_list_temp[max([bond1[3], bond1[4]])]
            del atom_list_temp[min([bond1[3], bond1[4]])]
            cell_temp.remove_sites([bond1[3], bond1[4]])
        elif bond1[3] in atoms_2bonds_index and bond2_in_2bonds:
            del atom_list_temp[max([bond1[3], bond2[4]])]
            del atom_list_temp[min([bond1[3], bond2[4]])]
            cell_temp.remove_sites([bond1[3], bond2[4]])

        if bond_list != None:
            del atom_list_temp[max(bond_list)]
            bond_list.remove(max(bond_list))
            del atom_list_temp[max(bond_list)]
            del atom_list_temp[min(bond_list)]

        # Check whether there are duplicates and if the structure is broken
        if cell_temp != cell and atom_list_temp != atom_list:
            if broken_structure(cell_temp) == False and tuple(
                    atom_list_temp) not in combinations:
                combinations.add(tuple(atom_list_temp))
                structures.append(cell_temp)
                structures.extend(
                    edge_types(cell_temp, atom_list_temp, combinations))

    return structures
Ejemplo n.º 14
0
def get_interstitial_diffusion_pathways_from_cell(structure : Structure, interstitial_atom : str, vis=False,
                                                  get_midpoints=False, dummy='He', min_dist=0.5, weight_cutoff=0.0001,
                                                  is_interstitial_structure=False):
    """

    Find Vacancy Strucutres for diffusion into and out of the specified atom_i site.

    :param structure: Structure
        Structure to calculate diffusion pathways
    :param atom_i: int
        Atom to get diffion path from
    :return: [ Structure ]
    """

    vnn = VoronoiNN(targets=[interstitial_atom])
    # To Find Pathway, look for voronoi edges
    if not is_interstitial_structure:
        orig_structure = structure.copy()
        structure = structure.copy() # type: Structure
        interstitial_structure = structure.copy()
        interstitial_structure.DISTANCE_TOLERANCE = 0.01

        if vis:
            Poscar(structure).write_file(vis)
            open_in_VESTA(vis)
        inter_gen = list(VoronoiInterstitialGenerator(orig_structure, interstitial_atom))
        if vis:
            print(len(inter_gen))
        for interstitial in inter_gen:
            sat_structure = None
            for dist_tol in [0.2, 0.15, 0.1, 0.05, 0.01, 0.001]:
                try:
                    sat_structure = create_saturated_interstitial_structure(interstitial, dist_tol=dist_tol) # type: Structure
                    break
                except ValueError:
                    continue
                except TypeError:
                    continue
            if not sat_structure:
                continue
            sat_structure.remove_site_property('velocities')
            if vis:
                Poscar(sat_structure).write_file(vis)
                open_in_VESTA(vis)
                time.sleep(0.5)
            for site in sat_structure: # type: PeriodicSite
                if site.specie == interstitial_atom:
                    try:
                        interstitial_structure.append(site.specie, site.coords, coords_are_cartesian=True, validate_proximity=True)
                    except StructureError:
                        pass

        # combined_structure.merge_sites(mode='delete')
        interstitial_structure.remove_site_property('velocities')
        if vis:
            Poscar(interstitial_structure).write_file(vis)
            open_in_VESTA(vis)
    else:
        interstitial_structure = structure.copy()

    # edges = vnn.get_nn_info(structure, atom_i)
    # base_coords = structure[atom_i].coords
    pathway_structure = interstitial_structure.copy() # type: Structure
    pathway_structure.DISTANCE_TOLERANCE = 0.01
    # Add H for all other diffusion atoms, so symmetry is preserved
    for i in get_atom_i(interstitial_structure, interstitial_atom):
        sym_edges = vnn.get_nn_info(interstitial_structure, i)
        base = pathway_structure[i] # type: PeriodicSite
        for edge in sym_edges:
            dest = edge['site']
            if base.distance(dest, jimage=edge['image']) > min_dist and edge['weight'] > weight_cutoff:
                coords = (base.coords + dest.coords) / 2
                try:
                    neighbors = [i, edge['site_index']]
                    # neighbors.sort()
                    pathway_structure.append(dummy, coords, True, validate_proximity=True, properties={'neighbors': neighbors, 'image' : edge['image']})
                except StructureError:
                    pass
                except ValueError:
                    pass
    if vis:
        Poscar(pathway_structure).write_file(vis)
        open_in_VESTA(vis)



    # Remove symmetrically equivalent pathways:
    # sga = SpacegroupAnalyzer(pathway_structure, 0.1, angle_tolerance=10)
    # ss = sga.get_symmetrized_structure()
    return interstitial_structure, pathway_structure
Ejemplo n.º 15
0
def get_vacancy_diffusion_pathways_from_cell(structure : Structure, atom_i : int, vis=False, get_midpoints=False):
    """

    Find Vacancy Strucutres for diffusion into and out of the specified atom_i site.

    :param structure: Structure
        Structure to calculate diffusion pathways
    :param atom_i: int
        Atom to get diffion path from
    :return: [ Structure ]
    """

    # To Find Pathway, look for voronoi edges
    orig_structure = structure.copy()
    structure = structure.copy() # type: Structure
    target_atom = structure[atom_i].specie
    vnn = VoronoiNN(targets=[target_atom])
    edges = vnn.get_nn_info(structure, atom_i)
    base_coords = structure[atom_i].coords

    # Add H in middle of the discovered pathways.  Use symmetry analysis to elminate equivlent H and therfore
    # equivalent pathways
    site_dir = {}
    for edge in edges:
        coords = np.round((base_coords + edge['site'].coords)/2,3)
        structure.append('H', coords, True)
       # site_dir[tuple(np.round(coords))] = structure.index(edge['site']) # Use Tuple for indexing dict, need to round
        site_dir[tuple(np.round(coords))] =  [list(x) for x in np.round(structure.frac_coords % 1,2) ].index(list(np.round(edge['site'].frac_coords % 1, 2))) # Use Tuple for indexing dict, need to round
    # Add H for all other diffusion atoms, so symmetry is preserved
    for i in get_atom_i(orig_structure, target_atom):
        sym_edges = vnn.get_nn_info(orig_structure, i)
        base_coords = structure[i].coords
        for edge in sym_edges:
            coords = (base_coords + edge['site'].coords) / 2
            try:
                structure.append('H', coords, True, True)
            except:
                pass

    # Remove symmetrically equivalent pathways:
    sga = SpacegroupAnalyzer(structure, 0.5, angle_tolerance=20)
    ss = sga.get_symmetrized_structure()

    final_structure = structure.copy()
    indices = []
    for i in range(len(orig_structure), len(orig_structure)+len(edges)): # get all 'original' edge sites
        sites = ss.find_equivalent_sites(ss[i])
        new_indices = [ss.index(site) for site in sites if ss.index(site) < len(orig_structure) + len(edges)] # Check if symmetrically equivalent to other original edge sites
        new_indices.remove(i)
        if i not in indices: # Don't duplicate effort
            indices = indices + new_indices
            indices.sort()
    indices = indices + list(range(len(orig_structure)+len(edges), len(final_structure)))
    final_structure.remove_sites(indices)
    diffusion_elements = [ site_dir[tuple(np.round(h.coords))] for h in final_structure[len(orig_structure):] ]
    if vis:
        view(final_structure, 'VESTA')
        print(diffusion_elements)

    if get_midpoints:
        centers = [h.frac_coords for h in final_structure[len(orig_structure):]]
        return (diffusion_elements, centers)


    return diffusion_elements
Ejemplo n.º 16
0
    def remove_disorder(structure: Structure,
                        distance: float = 0.3) -> Structure:
        """
        Merges sites within distance that are likely due to structural disorder.

        Inspired by the pymatgen merge function
            - we assume that the site properties of the clustered species are all
              the same
            - we assume that we can replace the disorder with an averaged position

        Args:
            structure (pymatgen Structure object):
            distance (float): distance threshold for the merging operation

        Returns:
            structure object with merged sites
        """

        crystal = structure.copy()
        d = crystal.distance_matrix

        indices_to_dump = []
        to_append = defaultdict(list)
        symbol_indices_dict = get_symbol_indices(crystal)
        symbol_set = symbol_indices_dict.keys()

        for symbol in symbol_set:
            sub_matrix = d[
                symbol_indices_dict[symbol], :][:, symbol_indices_dict[symbol]]
            # perform hierarchical clustering, get flat array of indices
            clusters = fcluster(
                linkage(squareform(sub_matrix)), distance, 'distance')

            symbol_indices = symbol_indices_dict[symbol]
            species_coord = [crystal[i].frac_coords for i in symbol_indices]

            for c in np.unique(clusters):
                inds = np.where(clusters == c)[0]
                indices_to_dump.append([symbol_indices[i] for i in inds])
                coords = [species_coord[i] for i in inds]

                if len(coords) == 1:
                    average_coord = np.concatenate(coords).ravel().tolist()
                else:
                    average_coord = np.mean(coords, axis=0).tolist()

                to_append[symbol].append(average_coord)

        indices_to_dump = list(
            set(np.concatenate(indices_to_dump).ravel().tolist()))

        crystal.remove_sites(indices_to_dump)
        to_append = dict(to_append)

        for symbol in to_append.keys():
            for coord in to_append[symbol]:
                crystal.append(
                    symbol,
                    coord,
                    validate_proximity=False,
                )

        return crystal
class Test2D_WSe2(Test2D):
    def setUp(self):

        ## hexagonal WSe2 unitcell

        self.a0 = 3.287596
        self.c = 23.360843

        self.structure = Structure(
            Lattice.from_parameters(a=self.a0,
                                    b=self.a0,
                                    c=self.c,
                                    alpha=90,
                                    beta=90,
                                    gamma=120), ["W", "Se", "Se"],
            [[0.0, 0.0, 0.5], [0.333333, 0.666667, 0.571091],
             [0.333333, 0.666667, 0.428909]])

        self.nvecs = [4, 4, 1]
        self.vacuum = 20
        self.q = 0
        self.structure.make_supercell(self.nvecs)
        self.structure_bulk = self.structure.copy()

        self.initdef_list = []
        self.initdef_list.append(
            {"def1": {
                "type": "vac",
                "species": "Se",
                "index": 0
            }})
        self.initdef_list.append({
            "def1": {
                "type": "sub",
                "index": -1,
                "species": "W",
                "species_new": "Re"
            }
        })
        self.initdef_list.append({
            "def1": {
                "type": "sub",
                "index": -1,
                "species": "W",
                "species_new": "Re"
            },
            "def2": {
                "type": "vac",
                "index": -1,
                "species": "Se",
                "index_offset_n1n2": -1
            }
        })
        self.initdef_list.append({
            "def1": {
                "type": "ad-W",
                "index": [-1],
                "species": ["W"],
                "species_new": "Re",
                "shift_z": "3.36"
            }
        })
        self.initdef_list.append({
            "def1": {
                "type": "int-hex",
                "index": [0, 1, 0],
                "index_offset_n1": [1, 1, 0],
                "species": 3 * ["W"],
                "species_new": "Re"
            }
        })
        self.create_defects()

        self.siteinds_list = [[16], [15], [15, 31], [[15]], [[4, 5, 0]]]
        self.defcoords_list = [[[0.083333, 0.166667, 0.571091]],
                               [[0.75, 0.75, 0.5]],
                               [[0.75, 0.75, 0.5],
                                [0.833333, 0.916667, 0.571091]],
                               [[0.75, 0.75, 0.643830]],
                               [[0.166667, 0.083333, 0.5]]]
        self.natoms_list = [47, 48, 47, 49, 49]