def setUp(self): module_dir = os.path.dirname(os.path.abspath(__file__)) with open( os.path.join(module_dir, "surface_samples.json")) as data_file: surface_properties = json.load(data_file) surface_energies, miller_indices = {}, {} for mpid in surface_properties.keys(): e_surf_list, miller_list = [], [] for surface in surface_properties[mpid]["surfaces"]: e_surf_list.append(surface["surface_energy"]) miller_list.append(surface["miller_index"]) surface_energies[mpid] = e_surf_list miller_indices[mpid] = miller_list # In the case of a high anisotropy material # Nb: mp-8636 latt_Nb = Lattice.cubic(2.992) # In the case of an fcc material # Ir: mp-101 latt_Ir = Lattice.cubic(3.8312) # In the case of a hcp material # Ti: mp-72 latt_Ti = Lattice.hexagonal(4.6000, 2.8200) self.ucell_Nb = Structure(latt_Nb, ["Nb", "Nb", "Nb", "Nb"], [[0, 0, 0], [0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]) self.wulff_Nb = WulffShape(latt_Nb, miller_indices["mp-8636"], surface_energies["mp-8636"]) self.ucell_Ir = Structure(latt_Nb, ["Ir", "Ir", "Ir", "Ir"], [[0, 0, 0], [0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]) self.wulff_Ir = WulffShape(latt_Ir, miller_indices["mp-101"], surface_energies["mp-101"]) self.ucell_Ti = Structure(latt_Ti, ["Ti", "Ti", "Ti"], [[0, 0, 0], [0.333333, 0.666667, 0.5], [0.666667, 0.333333, 0.5]]) self.wulff_Ti = WulffShape(latt_Ti, miller_indices["mp-72"], surface_energies["mp-72"]) self.cube = WulffShape(Lattice.cubic(1), [(1,0,0)], [1]) self.hex_prism = WulffShape(Lattice.hexagonal(2.63, 5.21), [(0,0,1), (1,0,0)], [0.35, 0.53]) self.surface_properties = surface_properties
def setUp(self): module_dir = os.path.dirname(os.path.abspath(__file__)) with open(os.path.join(module_dir, "surface_samples.json")) as data_file: surface_properties = json.load(data_file) surface_energies, miller_indices = {}, {} for mpid in surface_properties.keys(): e_surf_list, miller_list = [], [] for surface in surface_properties[mpid]["surfaces"]: e_surf_list.append(surface["surface_energy"]) miller_list.append(surface["miller_index"]) surface_energies[mpid] = e_surf_list miller_indices[mpid] = miller_list # In the case of a high anisotropy material # Nb: mp-8636 latt_Nb = Lattice.cubic(2.992) # In the case of an fcc material # Ir: mp-101 latt_Ir = Lattice.cubic(3.8312) # In the case of a hcp material # Ti: mp-72 latt_Ti = Lattice.hexagonal(4.6000, 2.8200) self.ucell_Nb = Structure( latt_Nb, ["Nb", "Nb", "Nb", "Nb"], [[0, 0, 0], [0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]], ) self.wulff_Nb = WulffShape(latt_Nb, miller_indices["mp-8636"], surface_energies["mp-8636"]) self.ucell_Ir = Structure( latt_Nb, ["Ir", "Ir", "Ir", "Ir"], [[0, 0, 0], [0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]], ) self.wulff_Ir = WulffShape(latt_Ir, miller_indices["mp-101"], surface_energies["mp-101"]) self.ucell_Ti = Structure( latt_Ti, ["Ti", "Ti", "Ti"], [[0, 0, 0], [0.333333, 0.666667, 0.5], [0.666667, 0.333333, 0.5]], ) self.wulff_Ti = WulffShape(latt_Ti, miller_indices["mp-72"], surface_energies["mp-72"]) self.cube = WulffShape(Lattice.cubic(1), [(1, 0, 0)], [1]) self.hex_prism = WulffShape(Lattice.hexagonal(2.63, 5.21), [(0, 0, 1), (1, 0, 0)], [0.35, 0.53]) self.surface_properties = surface_properties
def wulff(st, miller_list=None, e_surf_list=None, show=0): from pymatgen.core.structure import Structure stpm = st.convert2pymatgen() lat = stpm.lattice recp_lattice = stpm.lattice.reciprocal_lattice_crystallographic recp = Structure(recp_lattice, ["H"], [[0, 0, 0]]) dire = Structure(stpm.lattice, ["H"], [[0, 0, 0]]) print(dire.get_space_group_info()) print(recp.get_space_group_info()) # print(lat) from pymatgen.analysis.wulff import WulffShape WS = WulffShape(lat, miller_list, e_surf_list) # print(dir(WS)) anisotropy = WS.anisotropy weighted_surface_energy = WS.weighted_surface_energy if show: WS.show() return anisotropy, weighted_surface_energy
def get_wulff_shape(self, material_id): """ Constructs a Wulff shape for a material. Args: material_id (str): Materials Project material_id, e.g. 'mp-123'. Returns: pymatgen.analysis.wulff.WulffShape """ from pymatgen.symmetry.analyzer import SpacegroupAnalyzer from pymatgen.analysis.wulff import WulffShape, hkl_tuple_to_str structure = self.get_structure_by_material_id(material_id) surfaces = self.get_surface_data(material_id)["surfaces"] lattice = (SpacegroupAnalyzer( structure).get_conventional_standard_structure().lattice) miller_energy_map = {} for surf in surfaces: miller = tuple(surf["miller_index"]) # Prefer reconstructed surfaces, which have lower surface energies. if (miller not in miller_energy_map) or surf["is_reconstructed"]: miller_energy_map[miller] = surf["surface_energy"] millers, energies = zip(*miller_energy_map.items()) return WulffShape(lattice, millers, energies)
class WulffShapeTest(PymatgenTest): def setUp(self): module_dir = os.path.dirname(os.path.abspath(__file__)) with open(os.path.join(module_dir, "surface_samples.json")) as data_file: surface_properties = json.load(data_file) surface_energies, miller_indices = {}, {} for mpid in surface_properties.keys(): e_surf_list, miller_list = [], [] for surface in surface_properties[mpid]["surfaces"]: e_surf_list.append(surface["surface_energy"]) miller_list.append(surface["miller_index"]) surface_energies[mpid] = e_surf_list miller_indices[mpid] = miller_list # In the case of a high anisotropy material # Nb: mp-8636 latt_Nb = Lattice.cubic(2.992) # In the case of an fcc material # Ir: mp-101 latt_Ir = Lattice.cubic(3.8312) # In the case of a hcp material # Ti: mp-72 latt_Ti = Lattice.hexagonal(4.6000, 2.8200) self.ucell_Nb = Structure( latt_Nb, ["Nb", "Nb", "Nb", "Nb"], [[0, 0, 0], [0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]], ) self.wulff_Nb = WulffShape(latt_Nb, miller_indices["mp-8636"], surface_energies["mp-8636"]) self.ucell_Ir = Structure( latt_Nb, ["Ir", "Ir", "Ir", "Ir"], [[0, 0, 0], [0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]], ) self.wulff_Ir = WulffShape(latt_Ir, miller_indices["mp-101"], surface_energies["mp-101"]) self.ucell_Ti = Structure( latt_Ti, ["Ti", "Ti", "Ti"], [[0, 0, 0], [0.333333, 0.666667, 0.5], [0.666667, 0.333333, 0.5]], ) self.wulff_Ti = WulffShape(latt_Ti, miller_indices["mp-72"], surface_energies["mp-72"]) self.cube = WulffShape(Lattice.cubic(1), [(1, 0, 0)], [1]) self.hex_prism = WulffShape(Lattice.hexagonal(2.63, 5.21), [(0, 0, 1), (1, 0, 0)], [0.35, 0.53]) self.surface_properties = surface_properties @unittest.skipIf("DISPLAY" not in os.environ, "Need display") def test_get_plot(self): # Basic test, not really a unittest. self.wulff_Ti.get_plot() self.wulff_Nb.get_plot() self.wulff_Ir.get_plot() @unittest.skipIf("DISPLAY" not in os.environ, "Need display") def test_get_plotly(self): # Basic test, not really a unittest. self.wulff_Ti.get_plotly() self.wulff_Nb.get_plotly() self.wulff_Ir.get_plotly() def symm_check(self, ucell, wulff_vertices): """ # Checks if the point group of the Wulff shape matches # the point group of its conventional unit cell Args: ucell (string): Unit cell that the Wulff shape is based on. wulff_vertices (list): List of all vertices on the Wulff shape. Use wulff.wulff_pt_list to obtain the list (see wulff_generator.py). return (bool) """ space_group_analyzer = SpacegroupAnalyzer(ucell) symm_ops = space_group_analyzer.get_point_group_operations(cartesian=True) for point in wulff_vertices: for op in symm_ops: symm_point = op.operate(point) if in_coord_list(wulff_vertices, symm_point): continue else: return False return True def consistency_tests(self): # For a set of given values, these tests will # ensure that the general result given by the # algorithm does not change as the code is editted # For fcc Ir, make sure the (111) direction # is the most dominant facet on the Wulff shape fractional_areas = self.wulff_Ir.area_fraction_dict miller_list = [hkl for hkl in fractional_areas.keys()] area_list = [fractional_areas[hkl] for hkl in fractional_areas.keys()] self.assertEqual(miller_list[area_list.index(max(area_list))], (1, 1, 1)) # Overall weighted surface energy of fcc Nb should be # equal to the energy of the (310) surface, ie. fcc Nb # is anisotropic, the (310) surface is so low in energy, # its the only facet that exists in the Wulff shape Nb_area_fraction_dict = self.wulff_Nb.area_fraction_dict for hkl in Nb_area_fraction_dict.keys(): if hkl == (3, 1, 0): self.assertEqual(Nb_area_fraction_dict[hkl], 1) else: self.assertEqual(Nb_area_fraction_dict[hkl], 0) self.assertEqual( self.wulff_Nb.miller_energy_dict[(3, 1, 0)], self.wulff_Nb.weighted_surface_energy, ) def symmetry_test(self): # Maintains that all wulff shapes have the same point # groups as the conventional unit cell they were # derived from. This test should pass for all subsequent # updates of the surface_properties collection check_symmetry_Nb = self.symm_check(self.ucell_Nb, self.wulff_Nb.wulff_pt_list) check_symmetry_Ir = self.symm_check(self.ucell_Ir, self.wulff_Ir.wulff_pt_list) check_symmetry_Ti = self.symm_check(self.ucell_Ti, self.wulff_Ti.wulff_pt_list) self.assertTrue(check_symmetry_Nb) self.assertTrue(check_symmetry_Ir) self.assertTrue(check_symmetry_Ti) def test_get_azimuth_elev(self): # Test out the viewing of the Wulff shape from Miller indices. azim, elev = self.wulff_Ir._get_azimuth_elev((0, 0, 1)) self.assertEqual(azim, 0) self.assertEqual(elev, 90) azim, elev = self.wulff_Ir._get_azimuth_elev((1, 1, 1)) self.assertAlmostEqual(azim, 45) def test_properties(self): # Simple test to check if the values of some # properties are consistent with what we already have wulff_shapes = { "mp-8636": self.wulff_Nb, "mp-72": self.wulff_Ti, "mp-101": self.wulff_Ir, } for mpid in wulff_shapes.keys(): properties = self.surface_properties[mpid] wulff = wulff_shapes[mpid] self.assertEqual( round(wulff.weighted_surface_energy, 3), round(properties["weighted_surface_energy"], 3), ) self.assertEqual(round(wulff.shape_factor, 3), round(properties["shape_factor"], 3)) self.assertEqual(round(wulff.anisotropy, 3), round(properties["surface_anisotropy"], 3)) def test_corner_and_edges(self): # Test if it is returning the correct number of corner and edges self.assertArrayEqual(self.cube.tot_corner_sites, 8) self.assertArrayEqual(self.cube.tot_edges, 12) self.assertArrayEqual(self.hex_prism.tot_corner_sites, 12) self.assertArrayEqual(self.hex_prism.tot_edges, 18)
class WulffShapeTest(PymatgenTest): def setUp(self): module_dir = os.path.dirname(os.path.abspath(__file__)) with open( os.path.join(module_dir, "surface_samples.json")) as data_file: surface_properties = json.load(data_file) surface_energies, miller_indices = {}, {} for mpid in surface_properties.keys(): e_surf_list, miller_list = [], [] for surface in surface_properties[mpid]["surfaces"]: e_surf_list.append(surface["surface_energy"]) miller_list.append(surface["miller_index"]) surface_energies[mpid] = e_surf_list miller_indices[mpid] = miller_list # In the case of a high anisotropy material # Nb: mp-8636 latt_Nb = Lattice.cubic(2.992) # In the case of an fcc material # Ir: mp-101 latt_Ir = Lattice.cubic(3.8312) # In the case of a hcp material # Ti: mp-72 latt_Ti = Lattice.hexagonal(4.6000, 2.8200) self.ucell_Nb = Structure(latt_Nb, ["Nb", "Nb", "Nb", "Nb"], [[0, 0, 0], [0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]) self.wulff_Nb = WulffShape(latt_Nb, miller_indices["mp-8636"], surface_energies["mp-8636"]) self.ucell_Ir = Structure(latt_Nb, ["Ir", "Ir", "Ir", "Ir"], [[0, 0, 0], [0, 0.5, 0.5], [0.5, 0, 0.5], [0.5, 0.5, 0]]) self.wulff_Ir = WulffShape(latt_Ir, miller_indices["mp-101"], surface_energies["mp-101"]) self.ucell_Ti = Structure(latt_Ti, ["Ti", "Ti", "Ti"], [[0, 0, 0], [0.333333, 0.666667, 0.5], [0.666667, 0.333333, 0.5]]) self.wulff_Ti = WulffShape(latt_Ti, miller_indices["mp-72"], surface_energies["mp-72"]) self.surface_properties = surface_properties def test_get_plot(self): # Basic test, not really a unittest. self.wulff_Ti.get_plot() self.wulff_Nb.get_plot() self.wulff_Ir.get_plot() def symm_check(self, ucell, wulff_vertices): """ # Checks if the point group of the Wulff shape matches # the point group of its conventional unit cell Args: ucell (string): Unit cell that the Wulff shape is based on. wulff_vertices (list): List of all vertices on the Wulff shape. Use wulff.wulff_pt_list to obtain the list (see wulff_generator.py). return (bool) """ space_group_analyzer = SpacegroupAnalyzer(ucell) symm_ops = space_group_analyzer.get_point_group_operations( cartesian=True) for point in wulff_vertices: for op in symm_ops: symm_point = op.operate(point) if in_coord_list(wulff_vertices, symm_point): continue else: return False return True def consistency_tests(self): # For a set of given values, these tests will # ensure that the general result given by the # algorithm does not change as the code is editted # For fcc Ir, make sure the (111) direction # is the most dominant facet on the Wulff shape fractional_areas = self.wulff_Ir.area_fraction_dict miller_list = [hkl for hkl in fractional_areas.keys()] area_list = [fractional_areas[hkl] for hkl in fractional_areas.keys()] self.assertEqual(miller_list[area_list.index(max(area_list))], (1, 1, 1)) # Overall weighted surface energy of fcc Nb should be # equal to the energy of the (310) surface, ie. fcc Nb # is anisotropic, the (310) surface is so low in energy, # its the only facet that exists in the Wulff shape Nb_area_fraction_dict = self.wulff_Nb.area_fraction_dict for hkl in Nb_area_fraction_dict.keys(): if hkl == (3, 1, 0): self.assertEqual(Nb_area_fraction_dict[hkl], 1) else: self.assertEqual(Nb_area_fraction_dict[hkl], 0) self.assertEqual(self.wulff_Nb.miller_energy_dict[(3, 1, 0)], self.wulff_Nb.weighted_surface_energy) def symmetry_test(self): # Maintains that all wulff shapes have the same point # groups as the conventional unit cell they were # derived from. This test should pass for all subsequent # updates of the surface_properties collection check_symmetry_Nb = self.symm_check(self.ucell_Nb, self.wulff_Nb.wulff_pt_list) check_symmetry_Ir = self.symm_check(self.ucell_Ir, self.wulff_Ir.wulff_pt_list) check_symmetry_Ti = self.symm_check(self.ucell_Ti, self.wulff_Ti.wulff_pt_list) self.assertTrue(check_symmetry_Nb) self.assertTrue(check_symmetry_Ir) self.assertTrue(check_symmetry_Ti) def test_get_azimuth_elev(self): # Test out the viewing of the Wulff shape from Miller indices. azim, elev = self.wulff_Ir._get_azimuth_elev((0, 0, 1)) self.assertEqual(azim, 0) self.assertEqual(elev, 90) azim, elev = self.wulff_Ir._get_azimuth_elev((1, 1, 1)) self.assertAlmostEqual(azim, 45) def test_properties(self): # Simple test to check if the values of some # properties are consistent with what we already have wulff_shapes = {"mp-8636": self.wulff_Nb, "mp-72": self.wulff_Ti, "mp-101": self.wulff_Ir} for mpid in wulff_shapes.keys(): properties = self.surface_properties[mpid] wulff = wulff_shapes[mpid] self.assertEqual(round(wulff.weighted_surface_energy, 3), round(properties["weighted_surface_energy"], 3)) self.assertEqual(round(wulff.shape_factor, 3), round(properties["shape_factor"], 3)) self.assertEqual(round(wulff.anisotropy, 3), round(properties["surface_anisotropy"], 3))
def Wullff(self, formate, surface_energy, pic_name, directions=(3, 4, 4), x=0.47, y=-16): from pymatgen.core.surface import SlabGenerator, generate_all_slabs, Structure, Lattice # Import the neccesary tools for making a Wulff shape from pymatgen.analysis.wulff import WulffShape from pymatgen.ext.matproj import MPRester from pymatgen.io.cif import CifParser import matplotlib.pyplot as plt import matplotlib.image as mpimg import pandas as pd import os if 'cif' in str(formate): os.chdir(r"D:\Desktop\VASP practical\Cif library") print(os.getcwd()) structure = CifParser(str(pic_name) + '.cif') struct = structure.get_structures()[0] else: mpr = MPRester() mp_id = formate struct = mpr.get_structure_by_material_id(mp_id) surface_energies = surface_energy miller_list = surface_energies.keys() e_surf_list = surface_energies.values() font2 = { 'family': 'Times New Roman', 'fontsize': '40', 'weight': 'bold', } wulffshape = WulffShape(struct.lattice, miller_list, e_surf_list) print(wulffshape.area_fraction_dict) os.chdir(self.dire) dict1 = wulffshape.area_fraction_dict xx = [] yy = [] for key, value in dict1.items(): xx.append(key) yy.append(value) cc = wulffshape.effective_radius bb = wulffshape.volume dd = wulffshape.shape_factor print(wulffshape.effective_radius) res = pd.DataFrame({'Slab': xx, 'area': yy}) #构造原始数据文件 df = res.sort_values(by='area', ascending=True) with open(str(pic_name) + '.txt', 'w') as f: f.write('effective radius:' + str(cc) + ' ' + "volume:" + str(bb) + ' ' + "shape factor:" + str(dd)) print(cc) # os.chdir(r"D:\Desktop\VASP practical\workdir") df.to_excel(str(pic_name) + ".xlsx") #生成Excel文件,并存到指定文件路径下 wulffshape.get_plot(bar_on=True, aspect_ratio=(8, 8), bar_pos=[0, 0.85, 1.1, 0.045], direction=directions) plt.title(str(pic_name), font2, x=x, y=y) plt.savefig(str(pic_name) + ".png", bbox_inches='tight', transparent=True, dpi=600, format='png') plt.show os.chdir(r"D:\Desktop\VASP practical\workdir") print('finished')
(2, 1, 0): 2.3969, (3, 3, 2): 2.0944, (1, 0, 0): 2.2084, (2, 1, 1): 2.2353, (3, 2, 2): 2.1242, (3, 2, 1): 2.3183, (2, 2, 1): 2.1732, (3, 3, 1): 2.2288, (3, 1, 1): 2.3039, (1, 1, 1): 1.9235 } miller_list = surface_energies_Ni.keys() e_surf_list = surface_energies_Ni.values() # We can now construct a Wulff shape with an accuracy up to a max Miller index of 3 wulffshape = WulffShape(Ni.lattice, miller_list, e_surf_list) # Let's get some useful information from our wulffshape object print("shape factor: %.3f, anisotropy: \ %.3f, weighted surface energy: %.3f J/m^2" % (wulffshape.shape_factor, wulffshape.anisotropy, wulffshape.weighted_surface_energy)) # If we want to see what our Wulff shape looks like wulffshape.show() # Lets try something a little more complicated, say LiFePO4 from pymatgen.util.testing import PymatgenTest # Get the LiFePO4 structure LiFePO4 = PymatgenTest.get_structure("LiFePO4") # Let's add some oxidation states to LiFePO4, this will be
'Area': 77.14, 'Energy': -609.24, 'Label': '1 Species' } data = [Adsorbant_1] Surface_111_1 = wulff.calculate_surface_energy(stoich, data, SE, adsorbant, thermochem, 298, 0) #Declare the lattic paramter lattice = Lattice.cubic(5.411) ceo = Structure(lattice, ["Ce", "O"], [[0, 0, 0], [0.25, 0.25, 0.25]]) surface_energies_ceo = { (1, 1, 1): np.amin(Surface_111_1), (1, 1, 0): np.amin(Surface_110_1), (1, 0, 0): np.amin(Surface_100_1) } miller_list = surface_energies_ceo.keys() e_surf_list = surface_energies_ceo.values() # We can now construct a Wulff shape with an accuracy up to a max Miller index of 3 wulffshape = WulffShape(ceo.lattice, miller_list, e_surf_list) # Let's get some useful information from our wulffshape object print("shape factor: %.3f, anisotropy: \ %.3f, weighted surface energy: %.3f J/m^2" % (wulffshape.shape_factor, wulffshape.anisotropy, wulffshape.weighted_surface_energy)) # If we want to see what our Wulff shape looks like wulffshape.show(color_set="RdBu", direction=(1.00, 0.25, 0.25))