class NanoscaleStabilityTest(PymatgenTest): def setUp(self): # Load all entries La_hcp_entry_dict = get_entry_dict( os.path.join(get_path(""), "La_hcp_entries.txt")) La_fcc_entry_dict = get_entry_dict( os.path.join(get_path(""), "La_fcc_entries.txt")) with open(os.path.join(get_path(""), 'ucell_entries.txt')) as ucell_entries: ucell_entries = json.loads(ucell_entries.read()) La_hcp_ucell_entry = ComputedStructureEntry.from_dict( ucell_entries["La_hcp"]) La_fcc_ucell_entry = ComputedStructureEntry.from_dict( ucell_entries["La_fcc"]) # Set up the NanoscaleStabilityClass self.La_hcp_analyzer = SurfaceEnergyPlotter(La_hcp_entry_dict, La_hcp_ucell_entry) self.La_fcc_analyzer = SurfaceEnergyPlotter(La_fcc_entry_dict, La_fcc_ucell_entry) self.nanoscale_stability = NanoscaleStability( [self.La_fcc_analyzer, self.La_hcp_analyzer]) def test_stability_at_r(self): # Check that we have a different polymorph that is # stable below or above the equilibrium particle size r = self.nanoscale_stability.solve_equilibrium_point( self.La_hcp_analyzer, self.La_fcc_analyzer) * 10 # hcp phase of La particle should be the stable # polymorph above the equilibrium radius hcp_wulff = self.La_hcp_analyzer.wulff_from_chempot() bulk = self.La_hcp_analyzer.ucell_entry ghcp, rhcp = self.nanoscale_stability.wulff_gform_and_r( hcp_wulff, bulk, r + 10, from_sphere_area=True) fcc_wulff = self.La_fcc_analyzer.wulff_from_chempot() bulk = self.La_fcc_analyzer.ucell_entry gfcc, rfcc = self.nanoscale_stability.wulff_gform_and_r( fcc_wulff, bulk, r + 10, from_sphere_area=True) self.assertGreater(gfcc, ghcp) # fcc phase of La particle should be the stable # polymorph below the equilibrium radius hcp_wulff = self.La_hcp_analyzer.wulff_from_chempot() bulk = self.La_hcp_analyzer.ucell_entry ghcp, rhcp = self.nanoscale_stability.wulff_gform_and_r( hcp_wulff, bulk, r - 10, from_sphere_area=True) fcc_wulff = self.La_fcc_analyzer.wulff_from_chempot() bulk = self.La_fcc_analyzer.ucell_entry gfcc, rfcc = self.nanoscale_stability.wulff_gform_and_r( fcc_wulff, bulk, r - 10, from_sphere_area=True) self.assertLess(gfcc, ghcp)
class NanoscaleStabilityTest(PymatgenTest): def setUp(self): # Load all entries La_hcp_entry_dict = get_entry_dict(os.path.join(get_path(""), "La_hcp_entries.txt")) La_fcc_entry_dict = get_entry_dict(os.path.join(get_path(""), "La_fcc_entries.txt")) with open(os.path.join(get_path(""), 'ucell_entries.txt')) as ucell_entries: ucell_entries = json.loads(ucell_entries.read()) La_hcp_ucell_entry = ComputedStructureEntry.from_dict(ucell_entries["La_hcp"]) La_fcc_ucell_entry = ComputedStructureEntry.from_dict(ucell_entries["La_fcc"]) # Set up the NanoscaleStabilityClass self.La_hcp_analyzer = SurfaceEnergyPlotter(La_hcp_entry_dict, La_hcp_ucell_entry) self.La_fcc_analyzer = SurfaceEnergyPlotter(La_fcc_entry_dict, La_fcc_ucell_entry) self.nanoscale_stability = NanoscaleStability([self.La_fcc_analyzer, self.La_hcp_analyzer]) def test_stability_at_r(self): # Check that we have a different polymorph that is # stable below or above the equilibrium particle size r = self.nanoscale_stability.solve_equilibrium_point(self.La_hcp_analyzer, self.La_fcc_analyzer)*10 # hcp phase of La particle should be the stable # polymorph above the equilibrium radius hcp_wulff = self.La_hcp_analyzer.wulff_from_chempot() bulk = self.La_hcp_analyzer.ucell_entry ghcp, rhcp = self.nanoscale_stability.wulff_gform_and_r(hcp_wulff, bulk, r+10, from_sphere_area=True) fcc_wulff = self.La_fcc_analyzer.wulff_from_chempot() bulk = self.La_fcc_analyzer.ucell_entry gfcc, rfcc = self.nanoscale_stability.wulff_gform_and_r(fcc_wulff, bulk, r+10, from_sphere_area=True) self.assertGreater(gfcc, ghcp) # fcc phase of La particle should be the stable # polymorph below the equilibrium radius hcp_wulff = self.La_hcp_analyzer.wulff_from_chempot() bulk = self.La_hcp_analyzer.ucell_entry ghcp, rhcp = self.nanoscale_stability.wulff_gform_and_r(hcp_wulff, bulk, r-10, from_sphere_area=True) fcc_wulff = self.La_fcc_analyzer.wulff_from_chempot() bulk = self.La_fcc_analyzer.ucell_entry gfcc, rfcc = self.nanoscale_stability.wulff_gform_and_r(fcc_wulff, bulk, r-10, from_sphere_area=True) self.assertLess(gfcc, ghcp)
class SurfaceEnergyPlotterTest(PymatgenTest): def setUp(self): entry_dict = get_entry_dict(os.path.join(get_path(""), "Cu_entries.txt")) self.Cu_entry_dict = entry_dict with open(os.path.join(get_path(""), 'ucell_entries.txt')) as ucell_entries: ucell_entries = json.loads(ucell_entries.read()) self.Cu_ucell_entry = ComputedStructureEntry.from_dict(ucell_entries["Cu"]) self.Cu_analyzer = SurfaceEnergyPlotter(entry_dict, self.Cu_ucell_entry) self.metals_O_entry_dict = load_O_adsorption() ucell_entry = ComputedStructureEntry.from_dict(ucell_entries["Pt"]) self.Pt_analyzer = SurfaceEnergyPlotter(self.metals_O_entry_dict["Pt"], ucell_entry) ucell_entry = ComputedStructureEntry.from_dict(ucell_entries["Ni"]) self.Ni_analyzer = SurfaceEnergyPlotter(self.metals_O_entry_dict["Ni"], ucell_entry) ucell_entry = ComputedStructureEntry.from_dict(ucell_entries["Rh"]) self.Rh_analyzer = SurfaceEnergyPlotter(self.metals_O_entry_dict["Rh"], ucell_entry) self.Oads_analyzer_dict = {"Pt": self.Pt_analyzer, "Ni": self.Ni_analyzer, "Rh": self.Rh_analyzer} def test_get_stable_entry_at_u(self): for el in self.Oads_analyzer_dict.keys(): plotter = self.Oads_analyzer_dict[el] for hkl in plotter.all_slab_entries.keys(): # Test that the surface energy is clean for specific range of chempot entry1, gamma1 = \ plotter.get_stable_entry_at_u(hkl, delu_dict={Symbol("delu_O"): -7}) entry2, gamma2 = \ plotter.get_stable_entry_at_u(hkl, delu_dict={Symbol("delu_O"): -6}) self.assertEqual(gamma1, gamma2) self.assertEqual(entry1.label, entry2.label) # Now test that for a high chempot, adsorption # occurs and gamma is not equal to clean gamma entry3, gamma3 = \ plotter.get_stable_entry_at_u(hkl, delu_dict={Symbol("delu_O"): -1}) self.assertNotEqual(entry3.label, entry2.label) self.assertNotEqual(gamma3, gamma2) # For any chempot greater than -6, surface energy should vary # but the configuration should remain the same entry4, gamma4 = \ plotter.get_stable_entry_at_u(hkl, delu_dict={Symbol("delu_O"): 0}) self.assertEqual(entry3.label, entry4.label) self.assertNotEqual(gamma3, gamma4) def test_wulff_from_chempot(self): # Test if it generates a Wulff shape, test if # all the facets for Cu wulff shape are inside. Cu_wulff = self.Cu_analyzer.wulff_from_chempot() area_frac_dict = Cu_wulff.area_fraction_dict facets_hkl = [(1, 1, 1), (3, 3, 1), (3, 1, 0), (1, 0, 0), (3, 1, 1), (2, 1, 0), (2, 2, 1)] for hkl in area_frac_dict.keys(): if hkl in facets_hkl: self.assertNotEqual(area_frac_dict[hkl], 0) else: self.assertEqual(area_frac_dict[hkl], 0) for el in self.Oads_analyzer_dict.keys(): # Test WulffShape for adsorbed surfaces analyzer = self.Oads_analyzer_dict[el] # chempot = analyzer.max_adsorption_chempot_range(0) wulff = analyzer.wulff_from_chempot(delu_default=-6) se = wulff.weighted_surface_energy # Test if a different Wulff shape is generated # for Ni when adsorption comes into play wulff_neg7 = self.Oads_analyzer_dict["Ni"].wulff_from_chempot(delu_default=-7) wulff_neg6 = self.Oads_analyzer_dict["Ni"].wulff_from_chempot(delu_default=-6) self.assertEqual(wulff_neg7.weighted_surface_energy, wulff_neg6.weighted_surface_energy) wulff_neg55 = self.Oads_analyzer_dict["Ni"].wulff_from_chempot(delu_default=-5.5) self.assertNotEqual(wulff_neg55.weighted_surface_energy, wulff_neg6.weighted_surface_energy) wulff_neg525 = self.Oads_analyzer_dict["Ni"].wulff_from_chempot(delu_default=-5.25) self.assertNotEqual(wulff_neg55.weighted_surface_energy, wulff_neg525.weighted_surface_energy) def test_color_palette_dict(self): for el in self.metals_O_entry_dict.keys(): analyzer = self.Oads_analyzer_dict[el] color_dict = analyzer.color_palette_dict() for hkl in self.metals_O_entry_dict[el].keys(): for clean in self.metals_O_entry_dict[el][hkl].keys(): color = color_dict[clean] for ads in self.metals_O_entry_dict[el][hkl][clean]: color = color_dict[ads] def test_get_surface_equilibrium(self): # For clean stoichiometric system, the two equations should # be parallel because the surface energy is a constant. Then # get_surface_equilibrium should return None clean111_entry = list(self.Cu_entry_dict[(1, 1, 1)].keys())[0] clean100_entry = list(self.Cu_entry_dict[(1, 0, 0)].keys())[0] soln = self.Cu_analyzer.get_surface_equilibrium([clean111_entry, clean100_entry]) self.assertFalse(soln) # For adsorbed system, we should find one intercept Pt_entries = self.metals_O_entry_dict["Pt"] clean = list(Pt_entries[(1, 1, 1)].keys())[0] ads = Pt_entries[(1, 1, 1)][clean][0] Pt_analyzer = self.Oads_analyzer_dict["Pt"] soln = Pt_analyzer.get_surface_equilibrium([clean, ads]) self.assertNotEqual(list(soln.values())[0], list(soln.values())[1]) # Check if the number of parameters for adsorption are correct self.assertEqual((Symbol("delu_O"), Symbol("gamma")), tuple(soln.keys())) # Adsorbed systems have a b2=(-1*Nads) / (Nsurfs * Aads) se = ads.surface_energy(Pt_analyzer.ucell_entry, Pt_analyzer.ref_entries) self.assertAlmostEqual(se.as_coefficients_dict()[Symbol("delu_O")], -1 / (2 * ads.surface_area)) def test_stable_u_range_dict(self): for el in self.Oads_analyzer_dict.keys(): analyzer = self.Oads_analyzer_dict[el] stable_u_range = analyzer.stable_u_range_dict([-1, 0], Symbol("delu_O"), no_doped=False) all_u = [] for entry in stable_u_range.keys(): all_u.extend(stable_u_range[entry]) self.assertGreater(len(all_u), 1) def test_entry_dict_from_list(self): # Plug in a list of entries to see if it works all_Pt_slab_entries = [] Pt_entries = self.Pt_analyzer.all_slab_entries for hkl in Pt_entries.keys(): for clean in Pt_entries[hkl].keys(): all_Pt_slab_entries.append(clean) all_Pt_slab_entries.extend(Pt_entries[hkl][clean]) a = SurfaceEnergyPlotter(all_Pt_slab_entries, self.Pt_analyzer.ucell_entry) self.assertEqual(type(a).__name__, "SurfaceEnergyPlotter")
class SurfaceEnergyPlotterTest(PymatgenTest): def setUp(self): entry_dict = get_entry_dict(os.path.join(get_path(""), "Cu_entries.txt")) self.Cu_entry_dict = entry_dict with open(os.path.join(get_path(""), 'ucell_entries.txt')) as ucell_entries: ucell_entries = json.loads(ucell_entries.read()) self.Cu_ucell_entry = ComputedStructureEntry.from_dict(ucell_entries["Cu"]) self.Cu_analyzer = SurfaceEnergyPlotter(entry_dict, self.Cu_ucell_entry) self.metals_O_entry_dict = load_O_adsorption() ucell_entry = ComputedStructureEntry.from_dict(ucell_entries["Pt"]) self.Pt_analyzer = SurfaceEnergyPlotter(self.metals_O_entry_dict["Pt"], ucell_entry) ucell_entry = ComputedStructureEntry.from_dict(ucell_entries["Ni"]) self.Ni_analyzer = SurfaceEnergyPlotter(self.metals_O_entry_dict["Ni"], ucell_entry) ucell_entry = ComputedStructureEntry.from_dict(ucell_entries["Rh"]) self.Rh_analyzer = SurfaceEnergyPlotter(self.metals_O_entry_dict["Rh"], ucell_entry) self.Oads_analyzer_dict = {"Pt": self.Pt_analyzer, "Ni": self.Ni_analyzer, "Rh": self.Rh_analyzer} def test_get_stable_entry_at_u(self): for el in self.Oads_analyzer_dict.keys(): plotter = self.Oads_analyzer_dict[el] for hkl in plotter.all_slab_entries.keys(): # Test that the surface energy is clean for specific range of chempot entry1, gamma1 = \ plotter.get_stable_entry_at_u(hkl, delu_dict={Symbol("delu_O"): -7}) entry2, gamma2 = \ plotter.get_stable_entry_at_u(hkl, delu_dict={Symbol("delu_O"): -6}) self.assertEqual(gamma1, gamma2) self.assertEqual(entry1.label, entry2.label) # Now test that for a high chempot, adsorption # occurs and gamma is not equal to clean gamma entry3, gamma3 = \ plotter.get_stable_entry_at_u(hkl, delu_dict={Symbol("delu_O"): -1}) self.assertNotEqual(entry3.label, entry2.label) self.assertNotEqual(gamma3, gamma2) # For any chempot greater than -6, surface energy should vary # but the configuration should remain the same entry4, gamma4 = \ plotter.get_stable_entry_at_u(hkl, delu_dict={Symbol("delu_O"): 0}) self.assertEqual(entry3.label, entry4.label) self.assertNotEqual(gamma3, gamma4) def test_wulff_from_chempot(self): # Test if it generates a Wulff shape, test if # all the facets for Cu wulff shape are inside. Cu_wulff = self.Cu_analyzer.wulff_from_chempot() area_frac_dict = Cu_wulff.area_fraction_dict facets_hkl = [(1,1,1), (3,3,1), (3,1,0), (1,0,0), (3,1,1), (2,1,0), (2,2,1)] for hkl in area_frac_dict.keys(): if hkl in facets_hkl: self.assertNotEqual(area_frac_dict[hkl], 0) else: self.assertEqual(area_frac_dict[hkl], 0) for el in self.Oads_analyzer_dict.keys(): # Test WulffShape for adsorbed surfaces analyzer = self.Oads_analyzer_dict[el] # chempot = analyzer.max_adsorption_chempot_range(0) wulff = analyzer.wulff_from_chempot(delu_default=-6) se = wulff.weighted_surface_energy # Test if a different Wulff shape is generated # for Ni when adsorption comes into play wulff_neg7 = self.Oads_analyzer_dict["Ni"].wulff_from_chempot(delu_default=-7) wulff_neg6 = self.Oads_analyzer_dict["Ni"].wulff_from_chempot(delu_default=-6) self.assertEqual(wulff_neg7.weighted_surface_energy, wulff_neg6.weighted_surface_energy) wulff_neg55 = self.Oads_analyzer_dict["Ni"].wulff_from_chempot(delu_default=-5.5) self.assertNotEqual(wulff_neg55.weighted_surface_energy, wulff_neg6.weighted_surface_energy) wulff_neg525 = self.Oads_analyzer_dict["Ni"].wulff_from_chempot(delu_default=-5.25) self.assertNotEqual(wulff_neg55.weighted_surface_energy, wulff_neg525.weighted_surface_energy) def test_color_palette_dict(self): for el in self.metals_O_entry_dict.keys(): analyzer = self.Oads_analyzer_dict[el] color_dict = analyzer.color_palette_dict() for hkl in self.metals_O_entry_dict[el].keys(): for clean in self.metals_O_entry_dict[el][hkl].keys(): color = color_dict[clean] for ads in self.metals_O_entry_dict[el][hkl][clean]: color = color_dict[ads] def test_get_surface_equilibrium(self): # For clean stoichiometric system, the two equations should # be parallel because the surface energy is a constant. Then # get_surface_equilibrium should return None clean111_entry = list(self.Cu_entry_dict[(1, 1, 1)].keys())[0] clean100_entry = list(self.Cu_entry_dict[(1, 0, 0)].keys())[0] soln = self.Cu_analyzer.get_surface_equilibrium([clean111_entry, clean100_entry]) self.assertFalse(soln) # For adsorbed system, we should find one intercept Pt_entries = self.metals_O_entry_dict["Pt"] clean = list(Pt_entries[(1, 1, 1)].keys())[0] ads = Pt_entries[(1, 1, 1)][clean][0] Pt_analyzer = self.Oads_analyzer_dict["Pt"] soln = Pt_analyzer.get_surface_equilibrium([clean, ads]) self.assertNotEqual(list(soln.values())[0], list(soln.values())[1]) # Check if the number of parameters for adsorption are correct self.assertEqual((Symbol("delu_O"), Symbol("gamma")), tuple(soln.keys())) # Adsorbed systems have a b2=(-1*Nads) / (Nsurfs * Aads) se = ads.surface_energy(Pt_analyzer.ucell_entry, Pt_analyzer.ref_entries) self.assertAlmostEqual(se.as_coefficients_dict()[Symbol("delu_O")], -1 / (2 * ads.surface_area)) def test_stable_u_range_dict(self): for el in self.Oads_analyzer_dict.keys(): analyzer = self.Oads_analyzer_dict[el] stable_u_range = analyzer.stable_u_range_dict([-1,0], Symbol("delu_O"), no_doped=False) all_u = [] for entry in stable_u_range.keys(): all_u.extend(stable_u_range[entry]) self.assertGreater(len(all_u), 1) def test_entry_dict_from_list(self): # Plug in a list of entries to see if it works all_Pt_slab_entries = [] Pt_entries = self.Pt_analyzer.all_slab_entries for hkl in Pt_entries.keys(): for clean in Pt_entries[hkl].keys(): all_Pt_slab_entries.append(clean) all_Pt_slab_entries.extend(Pt_entries[hkl][clean]) a = SurfaceEnergyPlotter(all_Pt_slab_entries, self.Pt_analyzer.ucell_entry) self.assertEqual(type(a).__name__, "SurfaceEnergyPlotter")