Exemplo n.º 1
0
    def test_symmetrization(self):

        # Restricted to primitive_elemental materials due to the risk of
        # broken stoichiometry. For compound materials, use is_polar()

        # Get all slabs for P6/mmm Ti and Fm-3m Ag up to index of 2

        all_Ti_slabs = generate_all_slabs(
            self.ti,
            2,
            10,
            10,
            bonds=None,
            tol=1e-3,
            max_broken_bonds=0,
            lll_reduce=False,
            center_slab=False,
            primitive=True,
            max_normal_search=2,
            symmetrize=True,
        )

        all_Ag_fcc_slabs = generate_all_slabs(
            self.agfcc,
            2,
            10,
            10,
            bonds=None,
            tol=1e-3,
            max_broken_bonds=0,
            lll_reduce=False,
            center_slab=False,
            primitive=True,
            max_normal_search=2,
            symmetrize=True,
        )

        all_slabs = [all_Ti_slabs, all_Ag_fcc_slabs]

        for i, slabs in enumerate(all_slabs):

            assymetric_count = 0
            symmetric_count = 0

            for i, slab in enumerate(slabs):
                sg = SpacegroupAnalyzer(slab)

                # Check if a slab is symmetric
                if not sg.is_laue():
                    assymetric_count += 1
                else:
                    symmetric_count += 1

            # Check if slabs are all symmetric
            self.assertEqual(assymetric_count, 0)
            self.assertEqual(symmetric_count, len(slabs))
Exemplo n.º 2
0
    def from_bulk_and_miller(cls, structure, miller_index, min_slab_size=5.0,
                             min_vacuum_size=10.0, max_normal_search=None, 
                             center_slab = True, selective_dynamics=False,
                             undercoord_threshold = 0.09):
        """
        This method constructs the adsorbate site finder from a bulk 
        structure and a miller index, which allows the surface sites 
        to be determined from the difference in bulk and slab coordination, 
        as opposed to the height threshold.
        
        Args:
            structure (Structure): structure from which slab
                input to the ASF is constructed
            miller_index (3-tuple or list): miller index to be used
            min_slab_size (float): min slab size for slab generation
            min_vacuum_size (float): min vacuum size for slab generation
            max_normal_search (int): max normal search for slab generation
            center_slab (bool): whether to center slab in slab generation
            selective dynamics (bool): whether to assign surface sites
                to selective dynamics
            undercoord_threshold (float): threshold of "undercoordation"
                to use for the assignment of surface sites.  Default is
                0.1, for which surface sites will be designated if they
                are 10% less coordinated than their bulk counterpart
        """
        # TODO: for some reason this works poorly with primitive cells
        vcf_bulk = VoronoiCoordFinder(structure)
        bulk_coords = [len(vcf_bulk.get_coordinated_sites(n))
                       for n in range(len(structure))]
        struct = structure.copy(site_properties = {'bulk_coordinations':bulk_coords})
        slabs = generate_all_slabs(struct, max_index=max(miller_index), 
                                   min_slab_size=min_slab_size,
                                   min_vacuum_size=min_vacuum_size,
                                   max_normal_search = max_normal_search,
                                   center_slab = center_slab)

        slab_dict = {slab.miller_index:slab for slab in slabs}
        
        if miller_index not in slab_dict:
            raise ValueError("Miller index not in slab dict")

        this_slab = slab_dict[miller_index]

        vcf_surface = VoronoiCoordFinder(this_slab, allow_pathological=True)
        surf_props = []
        this_mi_vec = get_mi_vec(this_slab.miller_index)
        mi_mags = [np.dot(this_mi_vec, site.coords) for site in this_slab]
        average_mi_mag = np.average(mi_mags)
        for n, site in enumerate(this_slab):
            bulk_coord = this_slab.site_properties['bulk_coordinations'][n]
            slab_coord = len(vcf_surface.get_coordinated_sites(n))
            mi_mag = np.dot(this_mi_vec, site.coords)
            undercoord = (bulk_coord - slab_coord)/bulk_coord
            if undercoord > undercoord_threshold and mi_mag > average_mi_mag:
                surf_props += ['surface']
            else:
                surf_props += ['subsurface']
        new_site_properties = {'surface_properties':surf_props}
        new_slab = this_slab.copy(site_properties=new_site_properties)
        return cls(new_slab, selective_dynamics)
Exemplo n.º 3
0
    def test_get_symmetric_sites(self):

        # Check to see if we get an equivalent site on one
        # surface if we add a new site to the other surface

        all_Ti_slabs = generate_all_slabs(self.ti,
                                          2,
                                          10,
                                          10,
                                          bonds=None,
                                          tol=1e-3,
                                          max_broken_bonds=0,
                                          lll_reduce=False,
                                          center_slab=False,
                                          primitive=True,
                                          max_normal_search=2,
                                          symmetrize=True)

        for slab in all_Ti_slabs:
            sorted_sites = sorted(slab, key=lambda site: site.frac_coords[2])
            site = sorted_sites[-1]
            point = site.frac_coords
            point[2] = point[2] + 0.1
            point2 = slab.get_symmetric_site(point)
            slab.append("O", point)
            slab.append("O", point2)

            # Check if slab is all symmetric
            sg = SpacegroupAnalyzer(slab)
            self.assertTrue(sg.is_laue())
    def test_input_sets(self):
        # Test bulk
        bulk_set = MPSurfaceSet(self.struct_ir, bulk=True)
        self.assertFalse(bulk_set.auto_dipole)
        self.assertIsNone(bulk_set.incar.get('LDIPOL'))
        self.assertIsNone(bulk_set.incar.get('LVTOT'))

        # Test slab
        slab_set = MPSurfaceSet(self.slab_100)
        self.assertTrue(slab_set.auto_dipole)
        self.assertTrue(slab_set.incar.get('LDIPOL'))
        self.assertTrue(slab_set.incar.get('LVTOT'))
        banio3_slab = generate_all_slabs(
            PymatgenTest.get_structure('BaNiO3'), 1, 7.0, 20.0)[0]
        banio3_slab_set = MPSurfaceSet(banio3_slab)
        self.assertTrue(banio3_slab_set.incar['LDAU'], True)

        # Test adsorbates
        fe_ads = self.wf_1.fws[-1].tasks[-1]['additional_fields']['slab'].copy()
        fe_ads.replace_species({'H': 'O', "Ir": "Fe"})
        fe_ads_set = MPSurfaceSet(fe_ads)
        self.assertFalse(fe_ads_set.incar['LDAU'])

        # Test interaction of adsorbates and LDAU
        banio3_ads = banio3_slab.copy()
        banio3_ads.add_adsorbate_atom([-1], 'O', 0.5)
        banio3_ads.add_site_property('surface_properties', ['surface'] * len(
            banio3_slab) + ['adsorbate'])
        banio3_ads_set = MPSurfaceSet(banio3_ads)
        self.assertTrue(banio3_ads_set.incar['LDAU'])
        banio3_ads_set_noldau = MPSurfaceSet(
            banio3_ads, user_incar_settings={'LDAU': False})
        self.assertFalse(banio3_ads_set_noldau.incar['LDAU'])
    def test_wf_functions(self):
        # Test slab trans params generator
        for slab in self.slabs:
            trans_params = get_slab_trans_params(slab)
            trans = SlabTransformation(**trans_params)
            new_slab = trans.apply_transformation(slab.oriented_unit_cell)
            self.assertTrue(np.allclose(new_slab.cart_coords, slab.cart_coords))
            self.assertTrue(np.allclose(new_slab.lattice.matrix,
                                        slab.lattice.matrix))

        # Try something a bit more complicated
        formulas = ['Si', 'Sn', 'SrTiO3', 'Li2O']
        structs = [PymatgenTest.get_structure(s) for s in formulas]
        for struct in structs:
            slabs = generate_all_slabs(struct, max_index=2, min_slab_size=10,
                                       min_vacuum_size=20)
            for slab in slabs:
                trans_params = get_slab_trans_params(slab)
                trans = SlabTransformation(**trans_params)
                new_slab = trans.apply_transformation(slab.oriented_unit_cell)
                old_coords = np.around(slab.frac_coords, 10) % 1
                new_coords = np.around(new_slab.frac_coords, 10) % 1
                self.assertTrue(np.allclose(old_coords, new_coords))
                self.assertTrue(np.allclose(new_slab.lattice.matrix,
                                            slab.lattice.matrix))
Exemplo n.º 6
0
    def test_as_dict(self):
        slabs = generate_all_slabs(
            self.ti,
            1,
            10,
            10,
            bonds=None,
            tol=1e-3,
            max_broken_bonds=0,
            lll_reduce=False,
            center_slab=False,
            primitive=True,
        )
        slab = slabs[0]
        s = json.dumps(slab.as_dict())
        d = json.loads(s)
        self.assertEqual(slab, Slab.from_dict(d))

        # test initialising with a list scale_factor
        slab = Slab(
            self.zno55.lattice,
            self.zno55.species,
            self.zno55.frac_coords,
            self.zno55.miller_index,
            self.zno55.oriented_unit_cell,
            0,
            self.zno55.scale_factor.tolist(),
        )
        s = json.dumps(slab.as_dict())
        d = json.loads(s)
        self.assertEqual(slab, Slab.from_dict(d))
Exemplo n.º 7
0
    def test_wf_functions(self):
        # Test slab trans params generator
        for slab in self.slabs:
            trans_params = get_slab_trans_params(slab)
            trans = SlabTransformation(**trans_params)
            new_slab = trans.apply_transformation(slab.oriented_unit_cell)
            self.assertTrue(np.allclose(new_slab.cart_coords,
                                        slab.cart_coords))
            self.assertTrue(
                np.allclose(new_slab.lattice.matrix, slab.lattice.matrix))

        # Try something a bit more complicated
        formulas = ['Si', 'Sn', 'SrTiO3', 'Li2O']
        structs = [PymatgenTest.get_structure(s) for s in formulas]
        for struct in structs:
            slabs = generate_all_slabs(struct,
                                       max_index=2,
                                       min_slab_size=10,
                                       min_vacuum_size=20)
            for slab in slabs:
                trans_params = get_slab_trans_params(slab)
                trans = SlabTransformation(**trans_params)
                new_slab = trans.apply_transformation(slab.oriented_unit_cell)
                old_coords = np.around(slab.frac_coords, 10) % 1
                new_coords = np.around(new_slab.frac_coords, 10) % 1
                self.assertTrue(np.allclose(old_coords, new_coords))
                self.assertTrue(
                    np.allclose(new_slab.lattice.matrix, slab.lattice.matrix))
Exemplo n.º 8
0
    def test_input_sets(self):
        # Test bulk
        bulk_set = MPSurfaceSet(self.struct_ir, bulk=True)
        self.assertFalse(bulk_set.auto_dipole)
        self.assertIsNone(bulk_set.incar.get('LDIPOL'))
        self.assertIsNone(bulk_set.incar.get('LVTOT'))

        # Test slab
        slab_set = MPSurfaceSet(self.slab_100)
        self.assertTrue(slab_set.auto_dipole)
        self.assertTrue(slab_set.incar.get('LDIPOL'))
        self.assertTrue(slab_set.incar.get('LVTOT'))
        banio3_slab = generate_all_slabs(PymatgenTest.get_structure('BaNiO3'),
                                         1, 7.0, 20.0)[0]
        banio3_slab_set = MPSurfaceSet(banio3_slab)
        self.assertTrue(banio3_slab_set.incar['LDAU'], True)

        # Test adsorbates
        fe_ads = self.wf_1.fws[-1].tasks[-1]['additional_fields']['slab'].copy(
        )
        fe_ads.replace_species({'H': 'O', "Ir": "Fe"})
        fe_ads_set = MPSurfaceSet(fe_ads)
        self.assertFalse(fe_ads_set.incar['LDAU'])

        # Test interaction of adsorbates and LDAU
        banio3_ads = banio3_slab.copy()
        banio3_ads.add_adsorbate_atom([-1], 'O', 0.5)
        banio3_ads.add_site_property('surface_properties',
                                     ['surface'] * len(banio3_slab) +
                                     ['adsorbate'])
        banio3_ads_set = MPSurfaceSet(banio3_ads)
        self.assertTrue(banio3_ads_set.incar['LDAU'])
        banio3_ads_set_noldau = MPSurfaceSet(
            banio3_ads, user_incar_settings={'LDAU': False})
        self.assertFalse(banio3_ads_set_noldau.incar['LDAU'])
Exemplo n.º 9
0
    def test_nonstoichiometric_symmetrized_slab(self):
        # For the (111) halite slab, sometimes a nonstoichiometric
        # system is preferred over the stoichiometric Tasker 2.
        slabgen = SlabGenerator(self.MgO, (1, 1, 1),
                                10,
                                10,
                                max_normal_search=1)
        slabs = slabgen.get_slabs(symmetrize=True)

        # We should end up with two terminations, one with
        # an Mg rich surface and another O rich surface
        self.assertEqual(len(slabs), 2)
        for slab in slabs:
            self.assertTrue(slab.is_symmetric())

        # For a low symmetry primitive_elemental system such as
        # R-3m, there should be some nonsymmetric slabs
        # without using nonstoichiometric_symmetrized_slab
        slabs = generate_all_slabs(self.Dy,
                                   1,
                                   30,
                                   30,
                                   center_slab=True,
                                   symmetrize=True)
        for s in slabs:
            self.assertTrue(s.is_symmetric())
            self.assertGreater(len(s), len(self.Dy))
Exemplo n.º 10
0
 def proc_adsorb(cryst, mol, data):
     if data['method'] == 1:
         asf_slab = AdsorbateSiteFinder(cryst)
         ads_sites = asf_slab.find_adsorption_sites()
         ads_structs = asf_slab.generate_adsorption_structures(
             mol, repeat=data['repeat'])
         for i in range(len(ads_structs)):
             ads_struct = ads_structs[i]
             try:
                 miller_str = [str(j) for j in cryst.miller_index]
             except:
                 miller_str = ['adsorb']
             filename = '_'.join(miller_str) + '-' + str(i) + '.vasp'
             ads_struct.to(filename=filename, fmt='POSCAR')
     else:
         slabs = generate_all_slabs(cryst,
                                    max_index=data['max_index'],
                                    min_slab_size=data['min_slab'],
                                    min_vacuum_size=data['min_vacum'],
                                    lll_reduce=True)
         for slab in slabs:
             asf_slab = AdsorbateSiteFinder(slab)
             ads_sites = asf_slab.find_adsorption_sites()
             ads_structs = asf_slab.generate_adsorption_structures(
                 mol, repeat=data['repeat'])
             for i in range(len(ads_structs)):
                 ads_struct = ads_structs[i]
                 miller_str = [str(j) for j in slab.miller_index]
                 filename = 'adsorb' + '_'.join(miller_str) + '-' + str(
                     i) + '.vasp'
                 ads_struct.to(filename=filename, fmt='POSCAR')
Exemplo n.º 11
0
    def test_generate_all_slabs(self):

        slabs = generate_all_slabs(self.cscl, 1, 10, 10)
        # Only three possible slabs, one each in (100), (110) and (111).
        self.assertEqual(len(slabs), 3)

        # make sure it generates reconstructions
        slabs = generate_all_slabs(self.Fe, 1, 10, 10, include_reconstructions=True)

        # Four possible slabs, (100), (110), (111) and the zigzag (100).
        self.assertEqual(len(slabs), 4)

        slabs = generate_all_slabs(self.cscl, 1, 10, 10, bonds={("Cs", "Cl"): 4})
        # No slabs if we don't allow broken Cs-Cl
        self.assertEqual(len(slabs), 0)

        slabs = generate_all_slabs(self.cscl, 1, 10, 10, bonds={("Cs", "Cl"): 4}, max_broken_bonds=100)
        self.assertEqual(len(slabs), 3)

        slabs2 = generate_all_slabs(self.lifepo4, 1, 10, 10, bonds={("P", "O"): 3, ("Fe", "O"): 3})
        self.assertEqual(len(slabs2), 0)

        # There should be only one possible stable surfaces, all of which are
        # in the (001) oriented unit cell
        slabs3 = generate_all_slabs(self.LiCoO2, 1, 10, 10, bonds={("Co", "O"): 3})
        self.assertEqual(len(slabs3), 1)
        mill = (0, 0, 1)
        for s in slabs3:
            self.assertEqual(s.miller_index, mill)

        slabs1 = generate_all_slabs(self.lifepo4, 1, 10, 10, tol=0.1, bonds={("P", "O"): 3})
        self.assertEqual(len(slabs1), 4)

        # Now we test this out for repair_broken_bonds()
        slabs1_repair = generate_all_slabs(self.lifepo4, 1, 10, 10, tol=0.1, bonds={("P", "O"): 3}, repair=True)
        self.assertGreater(len(slabs1_repair), len(slabs1))

        # Lets see if there are no broken PO4 polyhedrons
        miller_list = get_symmetrically_distinct_miller_indices(self.lifepo4, 1)
        all_miller_list = []
        for slab in slabs1_repair:
            hkl = tuple(slab.miller_index)
            if hkl not in all_miller_list:
                all_miller_list.append(hkl)
            broken = []
            for site in slab:
                if site.species_string == "P":
                    neighbors = slab.get_neighbors(site, 3)
                    cn = 0
                    for nn in neighbors:
                        cn += 1 if nn[0].species_string == "O" else 0
                    broken.append(cn != 4)
            self.assertFalse(any(broken))

        # check if we were able to produce at least one
        # termination for each distinct Miller _index
        self.assertEqual(len(miller_list), len(all_miller_list))
Exemplo n.º 12
0
def generate_all_slab(structs,fnames,max_index,min_slab_size,min_vac_size):
    for struct,fname in zip(structs,fnames):
        slabs=generate_all_slabs(struct,max_index=max_index,min_slab_size=min_slab_size,min_vacuum_size=min_vac_size,lll_reduce=True)
        for slab_struct in slabs:
            slab_struct.sort()
            miller_str=[str(i) for i in slab_struct.miller_index]
            filename='_'.join(miller_str)+"_"+fname+'.vasp'
            slab_struct.to(filename=filename,fmt='POSCAR')
Exemplo n.º 13
0
 def test_as_dict(self):
     slabs = generate_all_slabs(self.ti, 1, 10, 10, bonds=None,
                                tol=1e-3, max_broken_bonds=0, lll_reduce=False, center_slab=False,
                                primitive=True)
     slab = slabs[0]
     s = json.dumps(slab.as_dict())
     d = json.loads(s)
     self.assertEqual(slab, Slab.from_dict(d))
Exemplo n.º 14
0
    def setUp(self):
        super(TestAdsorptionWorkflow, self).setUp()

        self.struct_ir = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3.875728), ["Ir"], [[0, 0, 0]])
        sgp = {"max_index": 1, "min_slab_size": 7.0, "min_vacuum_size": 20.0}
        slabs = generate_all_slabs(self.struct_ir, **sgp)
        slabs = [slab for slab in slabs if slab.miller_index==(1, 0, 0)]
        sgp.pop("max_index")
        self.wf_1 = get_wf_surface(slabs, [Molecule("H", [[0, 0, 0]])], self.struct_ir, sgp,
                                  db_file=os.path.join(db_dir, "db.json"))
Exemplo n.º 15
0
 def setUp(self):
     self.structure = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3.5),
                                             ["Ni"], [[0, 0, 0]])
     slabs = generate_all_slabs(self.structure, max_index=2,
                                min_slab_size=6.0, min_vacuum_size=15.0,
                                max_normal_search=1, center_slab=True)
     self.slab_dict = {''.join([str(i) for i in slab.miller_index]):
                       slab for slab in slabs}
     self.asf_211 = AdsorbateSiteFinder(self.slab_dict["211"])
     self.asf_100 = AdsorbateSiteFinder(self.slab_dict["100"])
     self.asf_111 = AdsorbateSiteFinder(self.slab_dict["111"])
     self.asf_110 = AdsorbateSiteFinder(self.slab_dict["110"])
Exemplo n.º 16
0
    def setUp(self):
        super(TestAdsorptionWorkflow, self).setUp()

        self.struct_ir = Structure.from_spacegroup(
            "Fm-3m", Lattice.cubic(3.875728), ["Ir"], [[0, 0, 0]])
        sgp = {"max_index": 1, "min_slab_size": 7.0, "min_vacuum_size": 20.0}
        self.slabs = generate_all_slabs(self.struct_ir, **sgp)
        self.slab_100 = [slab for slab in self.slabs
                         if slab.miller_index==(1, 0, 0)][0]
        self.wf_1 = get_wf_slab(self.slab_100, True,
                                [Molecule("H", [[0, 0, 0]])],
                               db_file=os.path.join(db_dir, "db.json"))
Exemplo n.º 17
0
    def test_generate_all_slabs(self):

        slabs = generate_all_slabs(self.cscl, 1, 10, 10)

        # Only three possible slabs, one each in (100), (110) and (111).
        self.assertEqual(len(slabs), 3)

        slabs = generate_all_slabs(self.cscl, 1, 10, 10,
                                   bonds={("Cs", "Cl"): 4})
        # No slabs if we don't allow broken Cs-Cl
        self.assertEqual(len(slabs), 0)

        slabs = generate_all_slabs(self.cscl, 1, 10, 10,
                                   bonds={("Cs", "Cl"): 4},
                                   max_broken_bonds=100)
        self.assertEqual(len(slabs), 3)

        slabs1 = generate_all_slabs(self.lifepo4, 1, 10, 10, tol=0.1,
                                    bonds={("P", "O"): 3})
        self.assertEqual(len(slabs1), 4)

        slabs2 = generate_all_slabs(self.lifepo4, 1, 10, 10,
                                    bonds={("P", "O"): 3, ("Fe", "O"): 3})
        self.assertEqual(len(slabs2), 0)

        # There should be only one possible stable surfaces, all of which are
        # in the (001) oriented unit cell
        slabs3 = generate_all_slabs(self.LiCoO2, 1, 10, 10,
                                    bonds={("Co", "O"): 3})
        self.assertEqual(len(slabs3), 1)
        mill = (0, 0, 1)
        for s in slabs3:
            self.assertEqual(s.miller_index, mill)
Exemplo n.º 18
0
    def test_generate_all_slabs(self):

        slabs = generate_all_slabs(self.cscl, 1, 10, 10)

        # Only three possible slabs, one each in (100), (110) and (111).
        self.assertEqual(len(slabs), 3)

        slabs = generate_all_slabs(self.cscl, 1, 10, 10,
                                   bonds={("Cs", "Cl"): 4})
        # No slabs if we don't allow broken Cs-Cl
        self.assertEqual(len(slabs), 0)

        slabs = generate_all_slabs(self.cscl, 1, 10, 10,
                                   bonds={("Cs", "Cl"): 4},
                                   max_broken_bonds=100)
        self.assertEqual(len(slabs), 3)

        slabs1 = generate_all_slabs(self.lifepo4, 1, 10, 10, tol=0.1,
                                    bonds={("P", "O"): 3})
        self.assertEqual(len(slabs1), 4)

        slabs2 = generate_all_slabs(self.lifepo4, 1, 10, 10,
                                    bonds={("P", "O"): 3, ("Fe", "O"): 3})
        self.assertEqual(len(slabs2), 0)

        # There should be only one possible stable surfaces, all of which are
        # in the (001) oriented unit cell
        slabs3 = generate_all_slabs(self.LiCoO2, 1, 10, 10,
                                    bonds={("Co", "O"): 3})
        self.assertEqual(len(slabs3), 1)
        mill = (0, 0, 1)
        for s in slabs3:
            self.assertEqual(s.miller_index, mill)
Exemplo n.º 19
0
    def test_symmetrization(self):

        # Restricted to primitive_elemental materials due to the risk of
        # broken stoichiometry. For compound materials, use is_polar()

        # Get all slabs for P6/mmm Ti and Fm-3m Ag up to index of 2

        all_Ti_slabs = generate_all_slabs(self.ti, 2, 10, 10, bonds=None,
                                          tol=1e-3, max_broken_bonds=0,
                                          lll_reduce=False, center_slab=False,
                                          primitive=True, max_normal_search=2,
                                          symmetrize=True)

        all_Ag_fcc_slabs = generate_all_slabs(self.agfcc, 2, 10, 10, bonds=None,
                                              tol=1e-3, max_broken_bonds=0,
                                              lll_reduce=False,
                                              center_slab=False,
                                              primitive=True,
                                              max_normal_search=2,
                                              symmetrize=True)

        all_slabs = [all_Ti_slabs, all_Ag_fcc_slabs]

        for i, slabs in enumerate(all_slabs):

            assymetric_count = 0
            symmetric_count = 0

            for i, slab in enumerate(slabs):
                sg = SpacegroupAnalyzer(slab)

                # Check if a slab is symmetric
                if not sg.is_laue():
                    assymetric_count += 1
                else:
                    symmetric_count += 1

            # Check if slabs are all symmetric
            self.assertEqual(assymetric_count, 0)
            self.assertEqual(symmetric_count, len(slabs))
Exemplo n.º 20
0
def get_wfs_all_slabs(bulk_structure,
                      include_bulk_opt=False,
                      adsorbates=None,
                      max_index=1,
                      slab_gen_params=None,
                      ads_structures_params=None,
                      ads_site_finder_params=None,
                      vasp_cmd="vasp",
                      db_file=None,
                      add_molecules_in_box=False,
                      user_incar_settings=None):
    """
    Convenience constructor that allows a user to construct a workflow
    that finds all adsorption configurations (or slabs) for a given
    max miller index.

    Args:
        bulk_structure (Structure): bulk structure from which to construct slabs
        include_bulk_opt (bool): whether to include bulk optimization
            of oriented unit cells
        adsorbates ([Molecule]): adsorbates to place on surfaces
        max_index (int): max miller index
        slab_gen_params (dict): dictionary of kwargs for generate_all_slabs
        ads_structures_params (dict): dictionary of kwargs for generating
            of adsorption structures via AdsorptionSiteFinder
        vasp_cmd (str): vasp command
        db_file (str): location of db file
        add_molecules_in_box (bool): whether to add molecules in a box
            for the entire workflow

    Returns:
        list of slab-specific Workflows
    """
    # TODO: these could be more well-thought out defaults
    sgp = slab_gen_params or {"min_slab_size": 7.0, "min_vacuum_size": 20.0}
    slabs = generate_all_slabs(bulk_structure, max_index=max_index, **sgp)
    wfs = []
    for slab in slabs:
        slab_wf = get_wf_slab(slab,
                              include_bulk_opt,
                              adsorbates,
                              ads_structures_params,
                              ads_site_finder_params,
                              vasp_cmd,
                              db_file,
                              user_incar_settings=user_incar_settings)
        wfs.append(slab_wf)

    if add_molecules_in_box:
        wfs.append(
            get_wf_molecules(adsorbates, db_file=db_file, vasp_cmd=vasp_cmd))
    return wfs
Exemplo n.º 21
0
    def test_oriented_unit_cell(self):

        # Check to see if we get the fully reduced oriented unit
        # cell. This will also ensure that the constrain_latt
        # parameter for get_primitive_structure is working properly

        def surface_area(s):
            m = s.lattice.matrix
            return np.linalg.norm(np.cross(m[0], m[1]))

        all_slabs = generate_all_slabs(self.agfcc, 3, 10, 10, max_normal_search=3)
        for slab in all_slabs:
            ouc = slab.oriented_unit_cell
            self.assertAlmostEqual(surface_area(slab), surface_area(ouc))
            self.assertGreaterEqual(len(slab), len(ouc))
Exemplo n.º 22
0
 def generate_all_slab(in_str):
     tmp_list = in_str.split('|')
     max_index = int(tmp_list[0])
     min_slab_size = float(tmp_list[1])
     min_vac_size = float(tmp_list[2])
     slabs = generate_all_slabs(struct,
                                max_index=max_index,
                                min_slab_size=min_slab_size,
                                min_vacuum_size=min_vac_size,
                                lll_reduce=True)
     for slab_struct in slabs:
         slab_struct.sort()
         miller_str = [str(i) for i in slab_struct.miller_index]
         filename = '_'.join(miller_str) + '.vasp'
         slab_struct.to(filename=filename, fmt='POSCAR')
Exemplo n.º 23
0
    def test_oriented_unit_cell(self):

        # Check to see if we get the fully reduced oriented unit
        # cell. This will also ensure that the constrain_latt
        # parameter for get_primitive_structure is working properly

        def surface_area(s):
            m = s.lattice.matrix
            return np.linalg.norm(np.cross(m[0], m[1]))

        all_slabs = generate_all_slabs(self.agfcc, 2, 10, 10, max_normal_search=3)
        for slab in all_slabs:
            ouc = slab.oriented_unit_cell
            self.assertAlmostEqual(surface_area(slab), surface_area(ouc))
            self.assertGreaterEqual(len(slab), len(ouc))
Exemplo n.º 24
0
    def setUp(self):
        super().setUp()

        self.struct_ir = Structure.from_spacegroup(
            "Fm-3m", Lattice.cubic(3.875728), ["Ir"], [[0, 0, 0]]
        )
        sgp = {"max_index": 1, "min_slab_size": 7.0, "min_vacuum_size": 20.0}
        self.slabs = generate_all_slabs(self.struct_ir, **sgp)
        self.slab_100 = [slab for slab in self.slabs if slab.miller_index == (1, 0, 0)][
            0
        ]
        self.wf_1 = get_wf_slab(
            self.slab_100,
            True,
            [Molecule("H", [[0, 0, 0]])],
            db_file=os.path.join(db_dir, "db.json"),
        )
Exemplo n.º 25
0
 def setUp(self):
     self.structure = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3.5),
                                                ["Ni"], [[0, 0, 0]])
     slabs = generate_all_slabs(self.structure,
                                max_index=2,
                                min_slab_size=6.0,
                                min_vacuum_size=15.0,
                                max_normal_search=1,
                                center_slab=True)
     self.slab_dict = {
         ''.join([str(i) for i in slab.miller_index]): slab
         for slab in slabs
     }
     self.asf_211 = AdsorbateSiteFinder(self.slab_dict["211"])
     self.asf_100 = AdsorbateSiteFinder(self.slab_dict["100"])
     self.asf_111 = AdsorbateSiteFinder(self.slab_dict["111"])
     self.asf_110 = AdsorbateSiteFinder(self.slab_dict["110"])
Exemplo n.º 26
0
    def __init__(self,
                 struct,
                 plane=(1, 0, 0),
                 slab_depth=4,
                 vacuum_size=15.0,
                 max_normal_search=5,
                 symmetrize=True,
                 cut=True):

        if cut == True:

            mi = max(plane)
            slabs = generate_all_slabs(struct,
                                       min_slab_size=slab_depth,
                                       min_vacuum_size=vacuum_size,
                                       max_index=mi,
                                       in_unit_planes=True,
                                       center_slab=True,
                                       symmetrize=symmetrize,
                                       max_normal_search=max_normal_search)

            slab = [slab for slab in slabs if slab.miller_index == plane][0]

            ase_atoms = AseAtomsAdaptor.get_atoms(slab)

            self.blank_slab_pym = slab
            self.blank_slab_ase = ase_atoms
            self.minimal_unit_cell = ase_atoms.get_cell().T
            self.duplications = (1, 1, 1)
            self.plane = plane

        else:

            slab = struct
            ase_atoms = AseAtomsAdaptor.get_atoms(slab)

            self.blank_slab_pym = slab
            self.blank_slab_ase = ase_atoms
            self.minimal_unit_cell = ase_atoms.get_cell().T
            self.duplications = (1, 1, 1)
            self.plane = plane
    def enumerate_surfaces(self,
                           max_index,
                           slab_depth,
                           vacuum_size,
                           plane='all',
                           replicate=(1, 1, 1),
                           max_normal_search=5,
                           symmetrize=True,
                           tol=0.1):

        surface_dict = {}
        max_index = int(max_index)
        slabs = generate_all_slabs(self.struct,
                                   min_slab_size=slab_depth,
                                   min_vacuum_size=vacuum_size,
                                   max_index=max_index,
                                   in_unit_planes=True,
                                   center_slab=True,
                                   symmetrize=symmetrize,
                                   max_normal_search=max_normal_search,
                                   tol=tol)

        print 'Surfaces enumerated.'

        if plane != 'all':
            slabs = [slab for slab in slabs if slab.miller_index == plane]

        count = 0
        for slab in slabs:
            count += 1
            r1, r2, r3 = replicate
            P = np.array([[r1, 0, 0], [0, r2, 0], [0, 0, r3]])
            ase_slab = AseAtomsAdaptor.get_atoms(slab)
            ase_slab = make_supercell(ase_slab, P)
            formula = ase_slab.get_chemical_formula()
            plane = slab.miller_index
            plane_string = ''.join(map(str, plane))
            surface_dict[formula + '_' + plane_string + '_' +
                         str(count)] = ase_slab

        self.surface_dict = surface_dict
Exemplo n.º 28
0
    def test_nonstoichiometric_symmetrized_slab(self):
        # For the (111) halite slab, sometimes a nonstoichiometric
        # system is preferred over the stoichiometric Tasker 2.
        slabgen = SlabGenerator(self.MgO, (1, 1, 1), 10, 10,
                                max_normal_search=1)
        slabs = slabgen.get_slabs(symmetrize=True)

        # We should end up with two terminations, one with
        # an Mg rich surface and another O rich surface
        self.assertEqual(len(slabs), 2)
        for slab in slabs:
            self.assertTrue(slab.is_symmetric())

        # For a low symmetry primitive_elemental system such as
        # R-3m, there should be some nonsymmetric slabs
        # without using nonstoichiometric_symmetrized_slab
        slabs = generate_all_slabs(self.Dy, 1, 30, 30,
                                   center_slab=True, symmetrize=True)
        for s in slabs:
            self.assertTrue(s.is_symmetric())
            self.assertGreater(len(s), len(self.Dy))
def slab_enumeration(bulk_structure, bulk):
    all_slabs = generate_all_slabs(bulk_structure,
                                   2,
                                   10,
                                   20,
                                   bonds=None,
                                   tol=0.1,
                                   ftol=0.1,
                                   max_broken_bonds=0,
                                   lll_reduce=False,
                                   center_slab=False,
                                   primitive=True,
                                   max_normal_search=None,
                                   symmetrize=False,
                                   repair=False,
                                   include_reconstructions=False,
                                   in_unit_planes=False)
    return [{
        'slab': slab,
        'bulk_structure': bulk_structure,
        'bulk': bulk
    } for slab in all_slabs]
Exemplo n.º 30
0
def get_wfs_all_slabs(bulk_structure, include_bulk_opt=False,
                      adsorbates=None, max_index=1, slab_gen_params=None,
                      ads_structures_params=None, vasp_cmd="vasp",
                      db_file=None, add_molecules_in_box=False):
    """
    Convenience constructor that allows a user to construct a workflow
    that finds all adsorption configurations (or slabs) for a given
    max miller index.

    Args:
        bulk_structure (Structure): bulk structure from which to construct slabs
        include_bulk_opt (bool): whether to include bulk optimization
            of oriented unit cells
        adsorbates ([Molecule]): adsorbates to place on surfaces
        max_index (int): max miller index
        slab_gen_params (dict): dictionary of kwargs for generate_all_slabs
        ads_structures_params (dict): dictionary of kwargs for generating
            of adsorption structures via AdsorptionSiteFinder
        vasp_cmd (str): vasp command
        db_file (str): location of db file
        add_molecules_in_box (bool): whether to add molecules in a box
            for the entire workflow

    Returns:
        list of slab-specific Workflows
    """
    # TODO: these could be more well-thought out defaults
    sgp = slab_gen_params or {"min_slab_size": 7.0, "min_vacuum_size": 20.0}
    slabs = generate_all_slabs(bulk_structure, max_index=max_index, **sgp)
    wfs = []
    for slab in slabs:
        slab_wf = get_wf_slab(slab, include_bulk_opt, adsorbates,
                              ads_structures_params, vasp_cmd, db_file)
        wfs.append(slab_wf)

    if add_molecules_in_box:
        wfs.append(get_wf_molecules(adsorbates, db_file=db_file,
                                    vasp_cmd=vasp_cmd))
    return wfs
Exemplo n.º 31
0
def get_wf_surface_all_slabs(bulk_structure,
                             molecules,
                             max_index=1,
                             slab_gen_params=None,
                             **kwargs):
    """
    Convenience constructor that allows a user to construct a workflow
    that finds all adsorption configurations (or slabs) for a given
    max miller index.

    Args:
        bulk_structure (Structure): bulk structure from which to construct slabs
        molecules (list of Molecules): adsorbates to place on surfaces
        max_index (int): max miller index
        slab_gen_params (dict): dictionary of kwargs for generate_all_slabs

    Returns:
        Workflow
    """
    sgp = slab_gen_params or {"min_slab_size": 7.0, "min_vacuum_size": 20.0}
    slabs = generate_all_slabs(bulk_structure, max_index=max_index, **sgp)
    return get_wf_surface(slabs, molecules, bulk_structure, sgp, **kwargs)
Exemplo n.º 32
0
def create_all_slabs_buggy(initial_structure,
                           miller_index,
                           min_slab_size_ang,
                           min_vacuum_size=0,
                           bonds=None,
                           tol=1e-3,
                           max_broken_bonds=0,
                           lll_reduce=False,
                           center_slab=False,
                           primitive=False,
                           max_normal_search=None,
                           symmetrize=False):  #, reorient_lattice=True):
    """
    wraps the pymatgen function generate_all_slabs with some useful extras
    returns a dictionary of structures
    """
    aiida_strucs = {}
    pymat_struc = initial_structure.get_pymatgen_structure()
    # currently the pymatgen method is buggy... no coordinates in x,y....
    all_slabs = generate_all_slabs(
        pymat_struc,
        miller_index,
        min_slab_size_ang,
        min_vacuum_size,
        bonds=bonds,
        tol=tol,
        max_broken_bonds=max_broken_bonds,
        lll_reduce=lll_reduce,
        center_slab=center_slab,
        primitive=primitive,
        max_normal_search=max_normal_search,
        symmetrize=symmetrize)  #, reorient_lattice=reorient_lattice)
    for slab in all_slabs:
        print slab
        #slab2 = #slab.get_orthogonal_c_slab()
        film_struc = StructureData(pymatgen_structure=slab2)
        film_struc.pbc = (True, True, False)
        aiida_strucs[slab.miller_index] = film_struc
    return aiida_strucs
Exemplo n.º 33
0
    def test_get_symmetric_sites(self):

        # Check to see if we get an equivalent site on one
        # surface if we add a new site to the other surface

        all_Ti_slabs = generate_all_slabs(self.ti, 2, 10, 10, bonds=None,
                                          tol=1e-3, max_broken_bonds=0,
                                          lll_reduce=False, center_slab=False,
                                          primitive=True, max_normal_search=2,
                                          symmetrize=True)

        for slab in all_Ti_slabs:
            sorted_sites = sorted(slab, key=lambda site: site.frac_coords[2])
            site = sorted_sites[-1]
            point = np.array(site.frac_coords)
            point[2] = point[2] + 0.1
            point2 = slab.get_symmetric_site(point)
            slab.append("O", point)
            slab.append("O", point2)

            # Check if slab is all symmetric
            sg = SpacegroupAnalyzer(slab)
            self.assertTrue(sg.is_laue())
Exemplo n.º 34
0
    def test_input_sets(self):
        # Test bulk
        bulk_set = MPSurfaceSet(self.struct_ir, bulk=True)
        self.assertFalse(bulk_set.auto_dipole)
        self.assertIsNone(bulk_set.incar.get("LDIPOL"))
        self.assertIsNone(bulk_set.incar.get("LVTOT"))

        # Test slab
        slab_set = MPSurfaceSet(self.slab_100)
        self.assertTrue(slab_set.auto_dipole)
        self.assertTrue(slab_set.incar.get("LDIPOL"))
        self.assertTrue(slab_set.incar.get("LVTOT"))
        banio3_slab = generate_all_slabs(
            PymatgenTest.get_structure("BaNiO3"), 1, 7.0, 20.0
        )[0]
        banio3_slab_set = MPSurfaceSet(banio3_slab)
        self.assertTrue(banio3_slab_set.incar["LDAU"], True)

        # Test adsorbates
        fe_ads = self.wf_1.fws[-1].tasks[-1]["additional_fields"]["slab"].copy()
        fe_ads.replace_species({"H": "O", "Ir": "Fe"})
        fe_ads_set = MPSurfaceSet(fe_ads)
        self.assertFalse(fe_ads_set.incar["LDAU"])

        # Test interaction of adsorbates and LDAU
        banio3_ads = banio3_slab.copy()
        banio3_ads.add_adsorbate_atom([-1], "O", 0.5)
        banio3_ads.add_site_property(
            "surface_properties", ["surface"] * len(banio3_slab) + ["adsorbate"]
        )
        banio3_ads_set = MPSurfaceSet(banio3_ads)
        self.assertTrue(banio3_ads_set.incar["LDAU"])
        banio3_ads_set_noldau = MPSurfaceSet(
            banio3_ads, user_incar_settings={"LDAU": False}
        )
        self.assertFalse(banio3_ads_set_noldau.incar["LDAU"])
Exemplo n.º 35
0
def make_lammps(jdata,
                conf_dir,
                max_miller=2,
                static=False,
                relax_box=False,
                task_type='wrong-task'):
    kspacing = jdata['vasp_params']['kspacing']
    fp_params = jdata['lammps_params']
    model_dir = fp_params['model_dir']
    type_map = fp_params['type_map']
    model_dir = os.path.abspath(model_dir)
    model_name = fp_params['model_name']
    if not model_name and task_type == 'deepmd':
        models = glob.glob(os.path.join(model_dir, '*pb'))
        model_name = [os.path.basename(ii) for ii in models]
        assert len(model_name) > 0, "No deepmd model in the model_dir"
    else:
        models = [os.path.join(model_dir, ii) for ii in model_name]

    model_param = {
        'model_name': fp_params['model_name'],
        'param_type': fp_params['model_param_type']
    }

    ntypes = len(type_map)

    min_slab_size = jdata['min_slab_size']
    min_vacuum_size = jdata['min_vacuum_size']

    # get equi poscar
    # conf_path = os.path.abspath(conf_dir)
    # conf_poscar = os.path.join(conf_path, 'POSCAR')
    equi_path = re.sub('confs', global_equi_name, conf_dir)
    equi_path = os.path.join(equi_path, 'vasp-k%.2f' % kspacing)
    equi_path = os.path.abspath(equi_path)
    equi_contcar = os.path.join(equi_path, 'CONTCAR')
    assert os.path.exists(
        equi_contcar), "Please compute the equilibrium state using vasp first"
    task_path = re.sub('confs', global_task_name, conf_dir)
    task_path = os.path.abspath(task_path)
    if static:
        task_path = os.path.join(task_path, task_type + '-static')
    else:
        task_path = os.path.join(task_path, task_type)
    os.makedirs(task_path, exist_ok=True)
    cwd = os.getcwd()
    os.chdir(task_path)
    if os.path.isfile('POSCAR'):
        os.remove('POSCAR')
    os.symlink(os.path.relpath(equi_contcar), 'POSCAR')
    os.chdir(cwd)
    task_poscar = os.path.join(task_path, 'POSCAR')
    # gen strcture
    ss = Structure.from_file(task_poscar)
    # gen slabs
    all_slabs = generate_all_slabs(ss, max_miller, min_slab_size,
                                   min_vacuum_size)
    # make lammps.in
    if task_type == 'deepmd':
        if static:
            fc = lammps.make_lammps_eval('conf.lmp', ntypes,
                                         lammps.inter_deepmd, model_name)
        else:
            fc = lammps.make_lammps_equi('conf.lmp',
                                         ntypes,
                                         lammps.inter_deepmd,
                                         model_name,
                                         change_box=relax_box)
    elif task_type == 'meam':
        if static:
            fc = lammps.make_lammps_eval('conf.lmp', ntypes, lammps.inter_meam,
                                         model_param)
        else:
            fc = lammps.make_lammps_equi('conf.lmp',
                                         ntypes,
                                         lammps.inter_meam,
                                         model_param,
                                         change_box=relax_box)
    f_lammps_in = os.path.join(task_path, 'lammps.in')
    with open(f_lammps_in, 'w') as fp:
        fp.write(fc)
    cwd = os.getcwd()

    if task_type == 'deepmd':
        os.chdir(task_path)
        for ii in model_name:
            if os.path.exists(ii):
                os.remove(ii)
        for (ii, jj) in zip(models, model_name):
            os.symlink(os.path.relpath(ii), jj)
        share_models = glob.glob(os.path.join(task_path, '*pb'))
    else:
        share_models = models

    for ii in range(len(all_slabs)):
        slab = all_slabs[ii]
        miller_str = "m%d.%d.%dm" % (
            slab.miller_index[0], slab.miller_index[1], slab.miller_index[2])
        # make dir
        struct_path = os.path.join(task_path,
                                   'struct-%03d-%s' % (ii, miller_str))
        os.makedirs(struct_path, exist_ok=True)
        os.chdir(struct_path)
        for jj in ['conf.lmp', 'lammps.in'] + model_name:
            if os.path.isfile(jj):
                os.remove(jj)
        print("# %03d generate " % ii, struct_path,
              " \t %d atoms" % len(slab.sites))
        # make conf
        slab.to('POSCAR', 'POSCAR')
        vasp.regulate_poscar('POSCAR', 'POSCAR')
        lammps.cvt_lammps_conf('POSCAR', 'conf.lmp')
        ptypes = vasp.get_poscar_types('POSCAR')
        lammps.apply_type_map('conf.lmp', type_map, ptypes)
        # record miller
        np.savetxt('miller.out', slab.miller_index, fmt='%d')
        # link lammps.in
        os.symlink(os.path.relpath(f_lammps_in), 'lammps.in')
        # link models
        for (ii, jj) in zip(share_models, model_name):
            os.symlink(os.path.relpath(ii), jj)
    cwd = os.getcwd()
Exemplo n.º 36
0
    def from_bulk_and_miller(cls,
                             structure,
                             miller_index,
                             min_slab_size=8.0,
                             min_vacuum_size=10.0,
                             max_normal_search=None,
                             center_slab=True,
                             selective_dynamics=False,
                             undercoord_threshold=0.09):
        """
        This method constructs the adsorbate site finder from a bulk
        structure and a miller index, which allows the surface sites
        to be determined from the difference in bulk and slab coordination,
        as opposed to the height threshold.

        Args:
            structure (Structure): structure from which slab
                input to the ASF is constructed
            miller_index (3-tuple or list): miller index to be used
            min_slab_size (float): min slab size for slab generation
            min_vacuum_size (float): min vacuum size for slab generation
            max_normal_search (int): max normal search for slab generation
            center_slab (bool): whether to center slab in slab generation
            selective dynamics (bool): whether to assign surface sites
                to selective dynamics
            undercoord_threshold (float): threshold of "undercoordation"
                to use for the assignment of surface sites.  Default is
                0.1, for which surface sites will be designated if they
                are 10% less coordinated than their bulk counterpart
        """
        # TODO: for some reason this works poorly with primitive cells
        #       may want to switch the coordination algorithm eventually
        vnn_bulk = VoronoiNN(tol=0.05)
        bulk_coords = [
            len(vnn_bulk.get_nn(structure, n)) for n in range(len(structure))
        ]
        struct = structure.copy(
            site_properties={'bulk_coordinations': bulk_coords})
        slabs = generate_all_slabs(struct,
                                   max_index=max(miller_index),
                                   min_slab_size=min_slab_size,
                                   min_vacuum_size=min_vacuum_size,
                                   max_normal_search=max_normal_search,
                                   center_slab=center_slab)

        slab_dict = {slab.miller_index: slab for slab in slabs}

        if miller_index not in slab_dict:
            raise ValueError("Miller index not in slab dict")

        this_slab = slab_dict[miller_index]

        vnn_surface = VoronoiNN(tol=0.05, allow_pathological=True)

        surf_props, undercoords = [], []
        this_mi_vec = get_mi_vec(this_slab)
        mi_mags = [np.dot(this_mi_vec, site.coords) for site in this_slab]
        average_mi_mag = np.average(mi_mags)
        for n, site in enumerate(this_slab):
            bulk_coord = this_slab.site_properties['bulk_coordinations'][n]
            slab_coord = len(vnn_surface.get_nn(this_slab, n))
            mi_mag = np.dot(this_mi_vec, site.coords)
            undercoord = (bulk_coord - slab_coord) / bulk_coord
            undercoords += [undercoord]
            if undercoord > undercoord_threshold and mi_mag > average_mi_mag:
                surf_props += ['surface']
            else:
                surf_props += ['subsurface']
        new_site_properties = {
            'surface_properties': surf_props,
            'undercoords': undercoords
        }
        new_slab = this_slab.copy(site_properties=new_site_properties)
        return cls(new_slab, selective_dynamics)
Exemplo n.º 37
0
def get_all_slabs(structure=None,
                  max_index=None,
                  thicknesses=None,
                  vacuums=None,
                  make_fols=False,
                  make_input_files=False,
                  max_size=500,
                  ox_states=None,
                  is_symmetric=True,
                  lll_reduce=True,
                  center_slab=True,
                  config_dict=PBEsol_slab_config,
                  potcar_functional='PBE',
                  update_incar=None,
                  update_potcar=None,
                  update_kpoints=None,
                  **kwargs):
    """
    Generates all unique slabs with specified maximum Miller index, minimum slab
    and vacuum thicknesses. It includes all combinations for multiple zero
    dipole symmetric terminations for the same Miller index. Note that using
    this method of slab generation will results in different slab index numbers
    as in the `get_one_hkl_slabs` - the slabs identified are the same, the index
    varies based on the position in the list of generated slabs.

    Args:
        structure: filename of structure file, takes all pymatgen-supported formats.
        max_index (int): maximum Miller index to be considered
        thicknesses (list): minimum size of the slab in angstroms.
        vacuums (list): minimum size of the vacuum in angstroms.
        make_fols (bool): makes folders containing POSCARs; default=False
        make_input_files (bool): makes INCAR, POTCAR and KPOINTS files in each
        of the folders; if True but make_fols is False it will make the folders 
        regardless; default=False.
        max_size (int): the maximum number of atoms in the slab for the size
        warning; default=500.
        ox_states (list or dict): add oxidation states either by sites
        i.e. [3, 2, 2, 1, -2, -2, -2, -2] or by element i.e. {'Fe': 3, 'O':-2};
        default=None which adds oxidation states by guess
        is_symmetric (bool): whether or not the slabs cleaved should have 
        inversion symmetry. Needs to be False for slabs cleaved from a
        non-centrosymmetric bulk; default=True
        lll_reduce (bool): whether or not the slabs will be orthogonalized;
        default=True.
        center_slab (bool): position of the slab in the unit cell, if True the
        slab is centered with equal amounts of vacuum above and below;
        default=True
        config_dict (dict): specifies the dictionary used for generation of
        input files; default=PBEsol_slab_config
        potcar_functional (str): The functional used for POTCAR generation;
        default='PBE'
        update_incar (dict): overrides default INCAR settings; default=None
        update_kpoints (dict or kpoints object): overrides default kpoints
        settings, if supplied as dict should be as {'reciprocal_density': 100};
        default=None
        update_potcar (dict): overrides default POTCAR settings; default=None

    Returns:
        POSCAR_hkl_slab_vac_index.vasp or hkl/slab_vac_index folders with
        POSCARs or hkl/slab_vac_index with all input files

    """
    # Check all neccessary input parameters are present
    if not any([structure, max_index, thicknesses, vacuums]):
        raise ValueError('One or more of the required arguments (structure, '
                         'max_index, thicknesses, vacuums) were not supplied.')

    # Import bulk relaxed structure, add oxidation states for slab dipole
    # calculations
    struc = Structure.from_file(structure)
    bulk_name = struc.formula.replace(" ", "")
    struc = oxidation_states(struc, ox_states=ox_states)

    # Iterate through vacuums and thicknessses
    provisional = []
    for vacuum in vacuums:
        for thickness in thicknesses:
            all_slabs = generate_all_slabs(struc,
                                           max_index=max_index,
                                           min_slab_size=thickness,
                                           min_vacuum_size=vacuum,
                                           lll_reduce=lll_reduce,
                                           center_slab=center_slab,
                                           **kwargs)

            for i, slab in enumerate(all_slabs):
                # Get all the zero-dipole slabs with inversion symmetry
                if is_symmetric:
                    if (slab.is_polar() == False) and (slab.is_symmetric()
                                                       == True):
                        provisional.append({
                            'hkl':
                            ''.join(map(str, slab.miller_index)),
                            'slab_t':
                            thickness,
                            'vac_t':
                            vacuum,
                            's_index':
                            i,
                            'slab':
                            slab
                        })

                # Get all the zero-dipole slabs wihtout inversion symmetry
                else:
                    if not slab.is_polar():
                        provisional.append({
                            'hkl':
                            ''.join(map(str, slab.miller_index)),
                            'slab_t':
                            thickness,
                            'vac_t':
                            vacuum,
                            's_index':
                            i,
                            'slab':
                            slab
                        })

    # Iterate though provisional slabs to extract the unique slabs
    unique_list, unique_list_of_dicts, repeat, large = ([] for i in range(4))

    for slab in provisional:
        if slab['slab'] not in unique_list:
            unique_list.append(slab['slab'])
            unique_list_of_dicts.append(slab)
            # For large slab size warning
            atoms = len(slab['slab'].atomic_numbers)
            if atoms > max_size:
                large.append('{}_{}_{}_{}'.format(slab['hkl'], slab['slab_t'],
                                                  slab['vac_t'],
                                                  slab['s_index']))

        # For repeat slabs warning
        else:
            repeat.append('{}_{}_{}_{}'.format(slab['hkl'], slab['slab_t'],
                                               slab['vac_t'], slab['s_index']))

    # Warnings for large and repeated slabs
        if repeat:
            warnings.formatwarning = custom_formatwarning
            warnings.warn(
                'Not all combinations of hkl or slab/vac thicknesses '
                'were generated because of repeat structures. '
                'The repeat slabs are: ' + ', '.join(map(str, repeat)))

        if large:
            warnings.formatwarning = custom_formatwarning
            warnings.warn('Some generated slabs exceed the max size specified.'
                          ' Slabs that exceed the max size are: ' +
                          ', '.join(map(str, large)))

    # Makes folders hkl/slab_vac_index, if only make_input_files is true the
    # folders will also be made automatically.
    if make_fols or make_input_files:
        for slab in unique_list_of_dicts:
            os.makedirs(os.path.join(
                os.getcwd(),
                r'{}/{}_{}_{}'.format(slab['hkl'], slab['slab_t'],
                                      slab['vac_t'], slab['s_index'])),
                        exist_ok=True)

            # Makes all VASP input files (KPOINTS, POTCAR, INCAR) based on the
            # config dictionary
            if make_input_files:
                vis = DictSet(structure=slab['slab'],
                              config_dict=config_dict,
                              potcar_functional=potcar_functional,
                              user_incar_settings=update_incar,
                              user_potcar_settings=update_potcar,
                              user_kpoints_settings=update_kpoints,
                              **kwargs)
                vis.write_input(
                    os.path.join(
                        os.getcwd(),
                        r'{}/{}_{}_{}'.format(slab['hkl'], slab['slab_t'],
                                              slab['vac_t'], slab['s_index'])))

            # Makes the folders with POSCARs
            else:
                slab['slab'].to(fmt='poscar',
                                filename=r'{}/{}_{}_{}/POSCAR'.format(
                                    slab['hkl'], slab['slab_t'], slab['vac_t'],
                                    slab['s_index']))

    # Omits folders, makes POSCAR_hkl_slab_vac_index files in folder bulk_name
    else:
        os.makedirs(os.path.join(os.getcwd(), r'{}'.format(bulk_name)),
                    exist_ok=True)
        for slab in unique_list_of_dicts:
            slab['slab'].to(fmt='poscar',
                            filename='{}/POSCAR_{}_{}_{}_{}.vasp'.format(
                                bulk_name, slab['hkl'], slab['slab_t'],
                                slab['vac_t'], slab['s_index']))
Exemplo n.º 38
0
# terminated in two different locations along the vector of the Miller index. For a
# fcc structure such as Ni however, there should only be one way to cut a (111) slab.
all_slabs = slabgen.get_slabs()
print("The Ni(111) slab only has %s termination." %
      (len(all_slabs)))  #len返回容器中的项目数
lattice = Lattice.cubic(5.46873)
Si = Structure(lattice, ["Si", "Si", "Si", "Si", "Si", "Si", "Si", "Si"],
               [[0.00000, 0.00000, 0.50000], [0.75000, 0.75000, 0.75000],
                [0.00000, 0.50000, 0.00000], [0.75000, 0.25000, 0.25000],
                [0.50000, 0.00000, 0.00000], [0.25000, 0.75000, 0.25000],
                [0.50000, 0.50000, 0.50000], [0.25000, 0.25000, 0.75000]])

slabgen = SlabGenerator(Si, (1, 1, 1), 10, 10)
print("Notice now there are actually now %s terminations that can be \
generated in the (111) direction for diamond Si" % (len(slabgen.get_slabs())))
all_slabs = generate_all_slabs(Si, 3, 10, 10)
print("%s unique slab structures have been found for a max Miller index of 3" %
      (len(all_slabs)))
# What are the Miller indices of these slabs?(晶面的米勒指数是用来表达晶面在晶体上之方向的一组无公约数的整数,他们的具体数值等于该晶面在结晶轴上所截截距的倒数比)
for slab in all_slabs:
    print(slab.miller_index)
print(
    "Notice some Miller indices are repeated. Again, this is due to there being more than one termination"
)
# Now let's assume that we then calculated the surface energies for these slabs

# Surface energy values in J/m^2
surface_energies_Ni = {
    (3, 2, 0): 2.3869,
    (1, 1, 0): 2.2862,
    (3, 1, 0): 2.3964,
Exemplo n.º 39
0
    def make_confs(self,
                   path_to_work,
                   path_to_equi,
                   refine=False):
        path_to_work = os.path.abspath(path_to_work)
        if os.path.exists(path_to_work):
            dlog.warning('%s already exists' % path_to_work)
        else:
            os.makedirs(path_to_work)
        path_to_equi = os.path.abspath(path_to_equi)

        if 'start_confs_path' in self.parameter and os.path.exists(self.parameter['start_confs_path']):
            init_path_list = glob.glob(os.path.join(self.parameter['start_confs_path'], '*'))
            struct_init_name_list = []
            for ii in init_path_list:
                struct_init_name_list.append(ii.split('/')[-1])
            struct_output_name = path_to_work.split('/')[-2]
            assert struct_output_name in struct_init_name_list
            path_to_equi = os.path.abspath(os.path.join(self.parameter['start_confs_path'],
                                                        struct_output_name, 'relaxation', 'relax_task'))

        task_list = []
        cwd = os.getcwd()

        if self.reprod:
            print('surface reproduce starts')
            if 'init_data_path' not in self.parameter:
                raise RuntimeError("please provide the initial data path to reproduce")
            init_data_path = os.path.abspath(self.parameter['init_data_path'])
            task_list = make_repro(init_data_path, self.init_from_suffix,
                                   path_to_work, self.parameter.get('reprod_last_frame', True))
            os.chdir(cwd)

        else:
            if refine:
                print('surface refine starts')
                task_list = make_refine(self.parameter['init_from_suffix'],
                                        self.parameter['output_suffix'],
                                        path_to_work)
                os.chdir(cwd)
                # record miller
                init_from_path = re.sub(self.parameter['output_suffix'][::-1],
                                        self.parameter['init_from_suffix'][::-1],
                                        path_to_work[::-1], count=1)[::-1]
                task_list_basename = list(map(os.path.basename, task_list))

                for ii in task_list_basename:
                    init_from_task = os.path.join(init_from_path, ii)
                    output_task = os.path.join(path_to_work, ii)
                    os.chdir(output_task)
                    if os.path.isfile('miller.json'):
                        os.remove('miller.json')
                    if os.path.islink('miller.json'):
                        os.remove('miller.json')
                    os.symlink(os.path.relpath(os.path.join(init_from_task, 'miller.json')), 'miller.json')
                os.chdir(cwd)

            else:
                equi_contcar = os.path.join(path_to_equi, 'CONTCAR')
                if not os.path.exists(equi_contcar):
                    raise RuntimeError("please do relaxation first")
                ptypes = vasp.get_poscar_types(equi_contcar)
                # gen structure
                ss = Structure.from_file(equi_contcar)
                # gen slabs
                all_slabs = generate_all_slabs(ss, self.miller, self.min_slab_size, self.min_vacuum_size)

                os.chdir(path_to_work)
                if os.path.isfile('POSCAR'):
                    os.remove('POSCAR')
                if os.path.islink('POSCAR'):
                    os.remove('POSCAR')
                os.symlink(os.path.relpath(equi_contcar), 'POSCAR')
                #           task_poscar = os.path.join(output, 'POSCAR')
                for ii in range(len(all_slabs)):
                    output_task = os.path.join(path_to_work, 'task.%06d' % ii)
                    os.makedirs(output_task, exist_ok=True)
                    os.chdir(output_task)
                    for jj in ['INCAR', 'POTCAR', 'POSCAR', 'conf.lmp', 'in.lammps']:
                        if os.path.exists(jj):
                            os.remove(jj)
                    task_list.append(output_task)
                    print("# %03d generate " % ii, output_task, " \t %d atoms" % len(all_slabs[ii].sites))
                    # make confs
                    all_slabs[ii].to('POSCAR', 'POSCAR.tmp')
                    vasp.regulate_poscar('POSCAR.tmp', 'POSCAR')
                    vasp.sort_poscar('POSCAR', 'POSCAR', ptypes)
                    vasp.perturb_xz('POSCAR', 'POSCAR', self.pert_xz)
                    # record miller
                    dumpfn(all_slabs[ii].miller_index, 'miller.json')
                os.chdir(cwd)

        return task_list
Exemplo n.º 40
0
def make_deepmd_lammps(jdata, conf_dir, max_miller = 2, static = False, relax_box = False, task_name = 'wrong-task') :
    fp_params = jdata['vasp_params']
    kspacing = fp_params['kspacing']
    deepmd_model_dir = jdata['deepmd_model_dir']
    deepmd_type_map = jdata['deepmd_type_map']
    ntypes = len(deepmd_type_map)    
    deepmd_model_dir = os.path.abspath(deepmd_model_dir)
    deepmd_models = glob.glob(os.path.join(deepmd_model_dir, '*pb'))
    deepmd_models_name = [os.path.basename(ii) for ii in deepmd_models]

    min_slab_size = jdata['min_slab_size']
    min_vacuum_size = jdata['min_vacuum_size']

    # get equi poscar
    # conf_path = os.path.abspath(conf_dir)
    # conf_poscar = os.path.join(conf_path, 'POSCAR')
    equi_path = re.sub('confs', global_equi_name, conf_dir)
    equi_path = os.path.join(equi_path, 'vasp-k%.2f' % kspacing)
    equi_path = os.path.abspath(equi_path)    
    equi_contcar = os.path.join(equi_path, 'CONTCAR')    
    task_path = re.sub('confs', global_task_name, conf_dir)
    task_path = os.path.abspath(task_path)
    task_path = os.path.join(task_path, task_name)
    os.makedirs(task_path, exist_ok=True)
    cwd = os.getcwd()
    os.chdir(task_path)
    if os.path.isfile('POSCAR') :
        os.remove('POSCAR')
    os.symlink(os.path.relpath(equi_contcar), 'POSCAR')
    os.chdir(cwd)
    task_poscar = os.path.join(task_path, 'POSCAR')
    # gen strcture
    ss = Structure.from_file(task_poscar)
    # gen slabs
    all_slabs = generate_all_slabs(ss, max_miller, min_slab_size, min_vacuum_size)
    # make lammps.in
    if static :
        fc = lammps.make_lammps_eval('conf.lmp', 
                                     ntypes, 
                                     lammps.inter_deepmd,
                                     deepmd_models_name)
    else :
        fc = lammps.make_lammps_equi('conf.lmp', 
                                     ntypes, 
                                     lammps.inter_deepmd,
                                     deepmd_models_name, 
                                     change_box = relax_box)
    f_lammps_in = os.path.join(task_path, 'lammps.in')
    with open(f_lammps_in, 'w') as fp :
        fp.write(fc)
    cwd = os.getcwd()
    for ii in range(len(all_slabs)) :
        slab = all_slabs[ii]
        miller_str = "m%d.%d.%dm" % (slab.miller_index[0], slab.miller_index[1], slab.miller_index[2])
        # make dir
        struct_path = os.path.join(task_path, 'struct-%03d-%s' % (ii, miller_str))
        os.makedirs(struct_path, exist_ok=True)
        os.chdir(struct_path)
        for jj in ['conf.lmp', 'lammps.in'] + deepmd_models_name :
            if os.path.isfile(jj):
                os.remove(jj)
        print("# %03d generate " % ii, struct_path, " \t %d atoms" % len(slab.sites))
        # make conf
        slab.to('POSCAR', 'POSCAR')
        vasp.regulate_poscar('POSCAR', 'POSCAR')
        lammps.cvt_lammps_conf('POSCAR', 'conf.lmp')
        ptypes = vasp.get_poscar_types('POSCAR')
        lammps.apply_type_map('conf.lmp', deepmd_type_map, ptypes)    
        # record miller
        np.savetxt('miller.out', slab.miller_index, fmt='%d')
        # link lammps.in
        os.symlink(os.path.relpath(f_lammps_in), 'lammps.in')
        # link models
        for (ii,jj) in zip(deepmd_models, deepmd_models_name) :
            os.symlink(os.path.relpath(ii), jj)
    cwd = os.getcwd()
Exemplo n.º 41
0
def make_vasp(jdata, conf_dir, max_miller=2, relax_box=False, static=False):
    fp_params = jdata['vasp_params']
    ecut = fp_params['ecut']
    ediff = fp_params['ediff']
    npar = fp_params['npar']
    kpar = fp_params['kpar']
    kspacing = fp_params['kspacing']
    kgamma = fp_params['kgamma']
    min_slab_size = jdata['min_slab_size']
    min_vacuum_size = jdata['min_vacuum_size']
    pert_xz = jdata['pert_xz']

    # get conf poscar
    # conf_path = os.path.abspath(conf_dir)
    # conf_poscar = os.path.join(conf_path, 'POSCAR')
    equi_path = re.sub('confs', global_equi_name, conf_dir)
    equi_path = os.path.join(equi_path, 'vasp-k%.2f' % kspacing)
    equi_path = os.path.abspath(equi_path)
    equi_contcar = os.path.join(equi_path, 'CONTCAR')
    assert os.path.exists(
        equi_contcar), "Please compute the equilibrium state using vasp first"
    task_path = re.sub('confs', global_task_name, conf_dir)
    task_path = os.path.abspath(task_path)
    if static:
        task_path = os.path.join(task_path, 'vasp-static-k%.2f' % kspacing)
    else:
        task_path = os.path.join(task_path, 'vasp-k%.2f' % kspacing)
    os.makedirs(task_path, exist_ok=True)
    cwd = os.getcwd()
    os.chdir(task_path)
    if os.path.isfile('POSCAR'):
        os.remove('POSCAR')
    os.symlink(os.path.relpath(equi_contcar), 'POSCAR')
    os.chdir(cwd)
    task_poscar = os.path.join(task_path, 'POSCAR')
    ptypes = vasp.get_poscar_types(task_poscar)
    # gen strcture
    ss = Structure.from_file(task_poscar)
    # gen slabs
    all_slabs = generate_all_slabs(ss, max_miller, min_slab_size,
                                   min_vacuum_size)
    # gen incar
    if static:
        fc = vasp.make_vasp_static_incar(ecut,
                                         ediff,
                                         npar=npar,
                                         kpar=kpar,
                                         kspacing=kspacing,
                                         kgamma=kgamma)
    else:
        fc = vasp.make_vasp_relax_incar(ecut,
                                        ediff,
                                        True,
                                        relax_box,
                                        False,
                                        npar=npar,
                                        kpar=kpar,
                                        kspacing=kspacing,
                                        kgamma=kgamma)
    with open(os.path.join(task_path, 'INCAR'), 'w') as fp:
        fp.write(fc)
    # gen potcar
    with open(task_poscar, 'r') as fp:
        lines = fp.read().split('\n')
        ele_list = lines[5].split()
    potcar_map = jdata['potcar_map']
    potcar_list = []
    for ii in ele_list:
        assert os.path.exists(os.path.abspath(
            potcar_map[ii])), "No POTCAR in the potcar_map of %s" % (ii)
        potcar_list.append(os.path.abspath(potcar_map[ii]))
    with open(os.path.join(task_path, 'POTCAR'), 'w') as outfile:
        for fname in potcar_list:
            with open(fname) as infile:
                outfile.write(infile.read())
    # gen tasks
    cwd = os.getcwd()
    for ii in range(len(all_slabs)):
        slab = all_slabs[ii]
        miller_str = "m%d.%d.%dm" % (
            slab.miller_index[0], slab.miller_index[1], slab.miller_index[2])
        # make dir
        struct_path = os.path.join(task_path,
                                   'struct-%03d-%s' % (ii, miller_str))
        os.makedirs(struct_path, exist_ok=True)
        os.chdir(struct_path)
        for jj in ['POSCAR', 'POTCAR', 'INCAR']:
            if os.path.isfile(jj):
                os.remove(jj)
        print("# %03d generate " % ii, struct_path,
              " \t %d atoms" % len(slab.sites))
        # make conf
        slab.to('POSCAR', 'POSCAR.tmp')
        vasp.regulate_poscar('POSCAR.tmp', 'POSCAR')
        vasp.sort_poscar('POSCAR', 'POSCAR', ptypes)
        vasp.perturb_xz('POSCAR', 'POSCAR', pert_xz)
        # record miller
        np.savetxt('miller.out', slab.miller_index, fmt='%d')
        # link incar, potcar, kpoints
        os.symlink(os.path.relpath(os.path.join(task_path, 'INCAR')), 'INCAR')
        os.symlink(os.path.relpath(os.path.join(task_path, 'POTCAR')),
                   'POTCAR')
    cwd = os.getcwd()
Exemplo n.º 42
0
    def test_generate_all_slabs(self):

        slabs = generate_all_slabs(self.cscl, 1, 10, 10)
        # Only three possible slabs, one each in (100), (110) and (111).
        self.assertEqual(len(slabs), 3)

        # make sure it generates reconstructions
        slabs = generate_all_slabs(self.Fe, 1, 10, 10,
                                   include_reconstructions=True)

        # Four possible slabs, (100), (110), (111) and the zigzag (100).
        self.assertEqual(len(slabs), 4)

        slabs = generate_all_slabs(self.cscl, 1, 10, 10,
                                   bonds={("Cs", "Cl"): 4})
        # No slabs if we don't allow broken Cs-Cl
        self.assertEqual(len(slabs), 0)

        slabs = generate_all_slabs(self.cscl, 1, 10, 10,
                                   bonds={("Cs", "Cl"): 4},
                                   max_broken_bonds=100)
        self.assertEqual(len(slabs), 3)

        slabs2 = generate_all_slabs(self.lifepo4, 1, 10, 10,
                                    bonds={("P", "O"): 3, ("Fe", "O"): 3})
        self.assertEqual(len(slabs2), 0)

        # There should be only one possible stable surfaces, all of which are
        # in the (001) oriented unit cell
        slabs3 = generate_all_slabs(self.LiCoO2, 1, 10, 10,
                                    bonds={("Co", "O"): 3})
        self.assertEqual(len(slabs3), 1)
        mill = (0, 0, 1)
        for s in slabs3:
            self.assertEqual(s.miller_index, mill)

        slabs1 = generate_all_slabs(self.lifepo4, 1, 10, 10, tol=0.1,
                                    bonds={("P", "O"): 3})
        self.assertEqual(len(slabs1), 4)

        # Now we test this out for repair_broken_bonds()
        slabs1_repair = generate_all_slabs(self.lifepo4, 1, 10, 10, tol=0.1,
                                           bonds={("P", "O"): 3}, repair=True)
        self.assertGreater(len(slabs1_repair), len(slabs1))

        # Lets see if there are no broken PO4 polyhedrons
        miller_list = get_symmetrically_distinct_miller_indices(self.lifepo4, 1)
        all_miller_list = []
        for slab in slabs1_repair:
            hkl = tuple(slab.miller_index)
            if hkl not in all_miller_list:
                all_miller_list.append(hkl)
            broken = []
            for site in slab:
                if site.species_string == "P":
                    neighbors = slab.get_neighbors(site, 3)
                    cn = 0
                    for nn in neighbors:
                        cn += 1 if nn[0].species_string == "O" else 0
                    broken.append(cn != 4)
            self.assertFalse(any(broken))

        # check if we were able to produce at least one
        # termination for each distinct Miller _index
        self.assertEqual(len(miller_list), len(all_miller_list))