def test_ordered_primitive_to_disordered_supercell(self): sm_atoms = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=False, scale=True, attempt_supercell=True, allow_subset=True, supercell_size = 'num_atoms', comparator=OrderDisorderElementComparator()) sm_sites = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=False, scale=True, attempt_supercell=True, allow_subset=True, supercell_size = 'num_sites', comparator=OrderDisorderElementComparator()) lp = Lattice.orthorhombic(10, 20, 30) pcoords = [[0, 0, 0], [0.5, 0.5, 0.5]] ls = Lattice.orthorhombic(20,20,30) scoords = [[0, 0, 0], [0.5, 0, 0], [0.25, 0.5, 0.5], [0.75, 0.5, 0.5]] s1 = Structure(lp, ['Na', 'Cl'], pcoords) s2 = Structure(ls, [{'Na':0.5}, {'Na':0.5}, {'Cl':0.5}, {'Cl':0.5}], scoords) self.assertTrue(sm_sites.fit(s1, s2)) self.assertFalse(sm_atoms.fit(s1, s2))
def test_disordered_primitive_to_ordered_supercell(self): sm_atoms = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=False, scale=True, attempt_supercell=True, allow_subset=True, supercell_size = 'num_atoms', comparator=OrderDisorderElementComparator()) sm_sites = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=False, scale=True, attempt_supercell=True, allow_subset=True, supercell_size = 'num_sites', comparator=OrderDisorderElementComparator()) lp = Lattice.orthorhombic(10, 20, 30) pcoords = [[0, 0, 0], [0.5, 0.5, 0.5]] ls = Lattice.orthorhombic(20,20,30) scoords = [[0, 0, 0], [0.75, 0.5, 0.5]] prim = Structure(lp, [{'Na':0.5}, {'Cl':0.5}], pcoords) supercell = Structure(ls, ['Na', 'Cl'], scoords) supercell.make_supercell([[-1,1,0],[0,1,1],[1,0,0]]) self.assertFalse(sm_sites.fit(prim, supercell)) self.assertTrue(sm_atoms.fit(prim, supercell)) self.assertRaises(ValueError, sm_atoms.get_s2_like_s1, prim, supercell) self.assertEqual(len(sm_atoms.get_s2_like_s1(supercell, prim)), 4)
def test_get_supercell_size(self): l = Lattice.cubic(1) l2 = Lattice.cubic(0.9) s1 = Structure(l, ['Mg', 'Cu', 'Ag', 'Cu', 'Ag'], [[0]*3]*5) s2 = Structure(l2, ['Cu', 'Cu', 'Ag'], [[0]*3]*3) sm = StructureMatcher(supercell_size='volume') self.assertEqual(sm._get_supercell_size(s1, s2), (1, True)) self.assertEqual(sm._get_supercell_size(s2, s1), (1, True)) sm = StructureMatcher(supercell_size='num_sites') self.assertEqual(sm._get_supercell_size(s1, s2), (2, False)) self.assertEqual(sm._get_supercell_size(s2, s1), (2, True)) sm = StructureMatcher(supercell_size='Ag') self.assertEqual(sm._get_supercell_size(s1, s2), (2, False)) self.assertEqual(sm._get_supercell_size(s2, s1), (2, True)) sm = StructureMatcher(supercell_size='wfieoh') self.assertRaises(ValueError, sm._get_supercell_size, s1, s2)
def test_fit(self): """ Take two known matched structures 1) Ensure match 2) Ensure match after translation and rotations 3) Ensure no-match after large site translation 4) Ensure match after site shuffling """ sm = StructureMatcher() self.assertTrue(sm.fit(self.struct_list[0], self.struct_list[1])) # Test rotational/translational invariance op = SymmOp.from_axis_angle_and_translation([0, 0, 1], 30, False, np.array([0.4, 0.7, 0.9])) self.struct_list[1].apply_operation(op) self.assertTrue(sm.fit(self.struct_list[0], self.struct_list[1])) #Test failure under large atomic translation self.struct_list[1].translate_sites([0], [.4, .4, .2], frac_coords=True) self.assertFalse(sm.fit(self.struct_list[0], self.struct_list[1])) self.struct_list[1].translate_sites([0], [-.4, -.4, -.2], frac_coords=True) # random.shuffle(editor._sites) self.assertTrue(sm.fit(self.struct_list[0], self.struct_list[1])) #Test FrameworkComporator sm2 = StructureMatcher(comparator=FrameworkComparator()) lfp = self.get_structure("LiFePO4") nfp = self.get_structure("NaFePO4") self.assertTrue(sm2.fit(lfp, nfp)) self.assertFalse(sm.fit(lfp, nfp)) #Test anonymous fit. self.assertEqual(sm.fit_anonymous(lfp, nfp), True) self.assertAlmostEqual(sm.get_rms_anonymous(lfp, nfp)[0], 0.060895871160262717) #Test partial occupancies. s1 = Structure(Lattice.cubic(3), [{"Fe": 0.5}, {"Fe": 0.5}, {"Fe": 0.5}, {"Fe": 0.5}], [[0, 0, 0], [0.25, 0.25, 0.25], [0.5, 0.5, 0.5], [0.75, 0.75, 0.75]]) s2 = Structure(Lattice.cubic(3), [{"Fe": 0.25}, {"Fe": 0.5}, {"Fe": 0.5}, {"Fe": 0.75}], [[0, 0, 0], [0.25, 0.25, 0.25], [0.5, 0.5, 0.5], [0.75, 0.75, 0.75]]) self.assertFalse(sm.fit(s1, s2)) self.assertFalse(sm.fit(s2, s1)) s2 = Structure(Lattice.cubic(3), [{"Mn": 0.5}, {"Mn": 0.5}, {"Mn": 0.5}, {"Mn": 0.5}], [[0, 0, 0], [0.25, 0.25, 0.25], [0.5, 0.5, 0.5], [0.75, 0.75, 0.75]]) self.assertEqual(sm.fit_anonymous(s1, s2), True) self.assertAlmostEqual(sm.get_rms_anonymous(s1, s2)[0], 0)
def setUp(self): c1 = [[0.5] * 3, [0.9] * 3] c2 = [[0.5] * 3, [0.9, 0.1, 0.1]] s1 = Structure(Lattice.cubic(5), ['Si', 'Si'], c1) s2 = Structure(Lattice.cubic(5), ['Si', 'Si'], c2) structs = [] for s in s1.interpolate(s2, 3, pbc=True): structs.append(Structure.from_sites(s.sites, to_unit_cell=True)) self.structures = structs self.vis = MITNEBSet(self.structures)
def test_get_lattices(self): sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=True, scale=True, attempt_supercell=False) l1 = Lattice.from_lengths_and_angles([1, 2.1, 1.9], [90, 89, 91]) l2 = Lattice.from_lengths_and_angles([1.1, 2, 2], [89, 91, 90]) s1 = Structure(l1, [], []) s2 = Structure(l2, [], []) lattices = list(sm._get_lattices(s=s1, target_lattice=s2.lattice)) self.assertEqual(len(lattices), 16) l3 = Lattice.from_lengths_and_angles([1.1, 2, 20], [89, 91, 90]) s3 = Structure(l3, [], []) lattices = list(sm._get_lattices(s=s1, target_lattice=s3.lattice)) self.assertEqual(len(lattices), 0)
def pair(self, element_a, element_b, potential, separations): max_r = np.max(separations) lattice = Lattice.from_parameters(10*max_r, 10*max_r, 10*max_r, 90, 90, 90) if self.calculator_type == 'lammps': kwargs = {'lammps_set': load_lammps_set('static')} elif self.calculator_type == 'lammps_cython': kwargs = {'lammps_additional_commands': ['run 0']} async def calculate(): futures = [] for sep in separations: coord_a = (lattice.a*0.5-(sep/2), lattice.b*0.5, lattice.c*0.5) coord_b = (lattice.a*0.5+(sep/2), lattice.b*0.5, lattice.c*0.5) structure = Structure( lattice, [element_a, element_b], [coord_a, coord_b], coords_are_cartesian=True) futures.append(await self.calculator.submit( structure, potential, properties={'energy'}, **kwargs)) return await asyncio.gather(*futures) results = self._run_async_func(calculate()) return np.array([r['results']['energy'] for r in results])
def test_subset(self): sm = StructureMatcher( ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=False, scale=True, attempt_supercell=False, allow_subset=True, ) l = Lattice.orthorhombic(10, 20, 30) s1 = Structure(l, ["Si", "Si", "Ag"], [[0, 0, 0.1], [0, 0, 0.2], [0.7, 0.4, 0.5]]) s2 = Structure(l, ["Si", "Ag"], [[0, 0.1, 0], [-0.7, 0.5, 0.4]]) result = sm.get_s2_like_s1(s1, s2) self.assertEqual(len(find_in_coord_list_pbc(result.frac_coords, [0, 0, 0.1])), 1) self.assertEqual(len(find_in_coord_list_pbc(result.frac_coords, [0.7, 0.4, 0.5])), 1) # test with fewer species in s2 s1 = Structure(l, ["Si", "Ag", "Si"], [[0, 0, 0.1], [0, 0, 0.2], [0.7, 0.4, 0.5]]) s2 = Structure(l, ["Si", "Si"], [[0, 0.1, 0], [-0.7, 0.5, 0.4]]) result = sm.get_s2_like_s1(s1, s2) mindists = np.min(s1.lattice.get_all_distances(s1.frac_coords, result.frac_coords), axis=0) self.assertLess(np.max(mindists), 1e-6) self.assertEqual(len(find_in_coord_list_pbc(result.frac_coords, [0, 0, 0.1])), 1) self.assertEqual(len(find_in_coord_list_pbc(result.frac_coords, [0.7, 0.4, 0.5])), 1) # test with not enough sites in s1 # test with fewer species in s2 s1 = Structure(l, ["Si", "Ag", "Cl"], [[0, 0, 0.1], [0, 0, 0.2], [0.7, 0.4, 0.5]]) s2 = Structure(l, ["Si", "Si"], [[0, 0.1, 0], [-0.7, 0.5, 0.4]]) self.assertEqual(sm.get_s2_like_s1(s1, s2), None)
def test_get_supercell_matrix(self): sm = StructureMatcher(ltol=0.1, stol=0.3, angle_tol=2, primitive_cell=False, scale=True, attempt_supercell=True) l = Lattice.orthorhombic(1, 2, 3) s1 = Structure(l, ['Si', 'Si', 'Ag'], [[0,0,0.1],[0,0,0.2],[.7,.4,.5]]) s1.make_supercell([2,1,1]) s2 = Structure(l, ['Si', 'Si', 'Ag'], [[0,0.1,0],[0,0.1,-0.95],[-.7,.5,.375]]) result = sm.get_supercell_matrix(s1, s2) self.assertTrue((result == [[-2,0,0],[0,1,0],[0,0,1]]).all()) s1 = Structure(l, ['Si', 'Si', 'Ag'], [[0,0,0.1],[0,0,0.2],[.7,.4,.5]]) s1.make_supercell([[1, -1, 0],[0, 0, -1],[0, 1, 0]]) s2 = Structure(l, ['Si', 'Si', 'Ag'], [[0,0.1,0],[0,0.1,-0.95],[-.7,.5,.375]]) result = sm.get_supercell_matrix(s1, s2) self.assertTrue((result == [[-1,-1,0],[0,0,-1],[0,1,0]]).all()) #test when the supercell is a subset sm = StructureMatcher(ltol=0.1, stol=0.3, angle_tol=2, primitive_cell=False, scale=True, attempt_supercell=True, allow_subset=True) del s1[0] result = sm.get_supercell_matrix(s1, s2) self.assertTrue((result == [[-1,-1,0],[0,0,-1],[0,1,0]]).all())
def test_writestring(self): # Test for the string export of s atructure into the exciting input xml schema input_string = ( '<input xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ' 'xsi:noNamespaceSchemaLocation="http://xml.exciting-code.org/excitinginput' '.xsd">\n <title>Na4 Cl4</title>\n <structure speciespath="./">\n ' '<crystal scale="1.8897543768634038">\n <basevect> 5.62000000' ' 0.00000000 0.00000000</basevect>\n <basevect> ' '0.00000000 5.62000000 0.00000000</basevect>\n ' '<basevect> 0.00000000 0.00000000 5.62000000</basevect>' '\n </crystal>\n <species speciesfile="Na.xml">\n <atom coord=' '" 0.00000000 0.00000000 0.00000000" />\n <atom coor' 'd=" 0.50000000 0.50000000 0.00000000" />\n <atom co' 'ord=" 0.50000000 0.00000000 0.50000000" />\n <atom ' 'coord=" 0.00000000 0.50000000 0.50000000" />\n </spec' 'ies>\n <species speciesfile="Cl.xml">\n <atom coord=" 0.5000' '0000 0.00000000 0.00000000" />\n <atom coord=" 0.00' '000000 0.50000000 0.00000000" />\n <atom coord=" 0.' '00000000 0.00000000 0.50000000" />\n <atom coord=" ' '0.50000000 0.50000000 0.50000000" />\n </species>\n </str' 'ucture>\n</input>\n') lattice = Lattice.cubic('5.62') structure = Structure(lattice, ['Na', 'Na', 'Na', 'Na', 'Cl', 'Cl', 'Cl', 'Cl'], [[0, 0, 0], [0.5, 0.5, 0.0], [0.5, 0.0, 0.5], [0.0, 0.5, 0.5], [0.5, 0.0, 0.0], [0.0, 0.5, 0.0], [0.0, 0.0, 0.5], [0.5, 0.5, 0.5]]) excin = ExcitingInput(structure) for l1, l2 in zip(input_string.split("\n"), excin.write_string('unchanged').split("\n")): if not l1.strip().startswith("<crystal scale"): self.assertEqual(l1, l2)
def test_get_mapping(self): sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=False, scale=True, attempt_supercell=False, allow_subset = True) l = Lattice.orthorhombic(1, 2, 3) s1 = Structure(l, ['Ag', 'Si', 'Si'], [[.7,.4,.5],[0,0,0.1],[0,0,0.2]]) s1.make_supercell([2,1,1]) s2 = Structure(l, ['Si', 'Si', 'Ag'], [[0,0.1,-0.95],[0,0.1,0],[-.7,.5,.375]]) shuffle = [2,0,1,3,5,4] s1 = Structure.from_sites([s1[i] for i in shuffle]) #test the mapping s2.make_supercell([2,1,1]) #equal sizes for i, x in enumerate(sm.get_mapping(s1, s2)): self.assertEqual(s1[x].species_and_occu, s2[i].species_and_occu) del s1[0] #s1 is subset of s2 for i, x in enumerate(sm.get_mapping(s2, s1)): self.assertEqual(s1[i].species_and_occu, s2[x].species_and_occu) #s2 is smaller than s1 del s2[0] del s2[1] self.assertRaises(ValueError, sm.get_mapping, s2, s1)
def test_out_of_cell_s2_like_s1(self): l = Lattice.cubic(5) s1 = Structure(l, ["Si", "Ag", "Si"], [[0, 0, -0.02], [0, 0, 0.001], [0.7, 0.4, 0.5]]) s2 = Structure(l, ["Si", "Ag", "Si"], [[0, 0, 0.98], [0, 0, 0.99], [0.7, 0.4, 0.5]]) new_s2 = StructureMatcher(primitive_cell=False).get_s2_like_s1(s1, s2) dists = np.sum((s1.cart_coords - new_s2.cart_coords) ** 2, axis=-1) ** 0.5 self.assertLess(np.max(dists), 0.1)
def test_get_supercells(self): sm = StructureMatcher(comparator=ElementComparator()) l = Lattice.cubic(1) l2 = Lattice.cubic(0.5) s1 = Structure(l, ['Mg', 'Cu', 'Ag', 'Cu'], [[0]*3]*4) s2 = Structure(l2, ['Cu', 'Cu', 'Ag'], [[0]*3]*3) scs = list(sm._get_supercells(s1, s2, 8, False)) for x in scs: self.assertAlmostEqual(abs(np.linalg.det(x[3])), 8) self.assertEqual(len(x[0]), 4) self.assertEqual(len(x[1]), 24) self.assertEqual(len(scs), 48) scs = list(sm._get_supercells(s2, s1, 8, True)) for x in scs: self.assertAlmostEqual(abs(np.linalg.det(x[3])), 8) self.assertEqual(len(x[0]), 24) self.assertEqual(len(x[1]), 4) self.assertEqual(len(scs), 48)
def test_get_s2_large_s2(self): sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=False, scale=False, attempt_supercell=True, allow_subset=False, supercell_size='volume') l = Lattice.orthorhombic(1, 2, 3) s1 = Structure(l, ['Ag', 'Si', 'Si'], [[.7,.4,.5],[0,0,0.1],[0,0,0.2]]) l2 = Lattice.orthorhombic(1.01, 2.01, 3.01) s2 = Structure(l2, ['Si', 'Si', 'Ag'], [[0,0.1,-0.95],[0,0.1,0],[-.7,.5,.375]]) s2.make_supercell([[0,-1,0],[1,0,0],[0,0,1]]) result = sm.get_s2_like_s1(s1, s2) for x,y in zip(s1, result): self.assertLess(x.distance(y), 0.08)
def test_get_supercell_size(self): l = Lattice.cubic(1) l2 = Lattice.cubic(0.9) s1 = Structure(l, ["Mg", "Cu", "Ag", "Cu", "Ag"], [[0] * 3] * 5) s2 = Structure(l2, ["Cu", "Cu", "Ag"], [[0] * 3] * 3) sm = StructureMatcher(supercell_size="volume") self.assertEqual(sm._get_supercell_size(s1, s2), (1, True)) self.assertEqual(sm._get_supercell_size(s2, s1), (1, True)) sm = StructureMatcher(supercell_size="num_sites") self.assertEqual(sm._get_supercell_size(s1, s2), (2, False)) self.assertEqual(sm._get_supercell_size(s2, s1), (2, True)) sm = StructureMatcher(supercell_size="Ag") self.assertEqual(sm._get_supercell_size(s1, s2), (2, False)) self.assertEqual(sm._get_supercell_size(s2, s1), (2, True)) sm = StructureMatcher(supercell_size="wfieoh") self.assertRaises(ValueError, sm._get_supercell_size, s1, s2)
def test_nelect(self): coords = [[0] * 3, [0.5] * 3, [0.75] * 3] lattice = Lattice.cubic(4) s = Structure(lattice, ['Si', 'Si', 'Fe'], coords) self.assertAlmostEqual(MITRelaxSet(s).nelect, 16) # Check that it works even when oxidation states are present. Was a bug # previously. s = Structure(lattice, ['Si4+', 'Si4+', 'Fe2+'], coords) self.assertAlmostEqual(MITRelaxSet(s).nelect, 16) self.assertAlmostEqual(MPRelaxSet(s).nelect, 22)
def test_get_mask(self): sm = StructureMatcher(comparator=ElementComparator()) l = Lattice.cubic(1) s1 = Structure(l, ['Mg', 'Cu', 'Ag', 'Cu'], [[0]*3]*4) s2 = Structure(l, ['Cu', 'Cu', 'Ag'], [[0]*3]*3) result = [[True, False, True, False], [True, False, True, False], [True, True, False, True]] m, inds, i = sm._get_mask(s1, s2, 1, True) self.assertTrue(np.all(m == result)) self.assertTrue(i == 2) self.assertEqual(inds, [2]) #test supercell with match result = [[1, 1, 0, 0, 1, 1, 0, 0], [1, 1, 0, 0, 1, 1, 0, 0], [1, 1, 1, 1, 0, 0, 1, 1]] m, inds, i = sm._get_mask(s1, s2, 2, True) self.assertTrue(np.all(m == result)) self.assertTrue(i == 2) self.assertTrue(np.allclose(inds, np.array([4]))) #test supercell without match result = [[1, 1, 1, 1, 1, 1], [0, 0, 0, 0, 1, 1], [1, 1, 1, 1, 0, 0], [0, 0, 0, 0, 1, 1]] m, inds, i = sm._get_mask(s2, s1, 2, True) self.assertTrue(np.all(m == result)) self.assertTrue(i == 0) self.assertTrue(np.allclose(inds, np.array([]))) #test s2_supercell result = [[1, 1, 1], [1, 1, 1], [0, 0, 1], [0, 0, 1], [1, 1, 0], [1, 1, 0], [0, 0, 1], [0, 0, 1]] m, inds, i = sm._get_mask(s2, s1, 2, False) self.assertTrue(np.all(m == result)) self.assertTrue(i == 0) self.assertTrue(np.allclose(inds, np.array([]))) #test for multiple translation indices s1 = Structure(l, ['Cu', 'Ag', 'Cu', 'Ag', 'Ag'], [[0]*3]*5) s2 = Structure(l, ['Ag', 'Cu', 'Ag'], [[0]*3]*3) result = [[1, 0, 1, 0, 0], [0, 1, 0, 1, 1], [1, 0, 1, 0, 0]] m, inds, i = sm._get_mask(s1, s2, 1, True) self.assertTrue(np.all(m == result)) self.assertTrue(i == 1) self.assertTrue(np.allclose(inds, [0, 2]))
def test_metal_check(self): structure = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3), ["Cu"], [[0, 0, 0]]) with warnings.catch_warnings(record=True) as w: # Cause all warnings to always be triggered. warnings.simplefilter("always") # Trigger a warning. vis = MITRelaxSet(structure) incar = vis.incar # Verify some things self.assertIn("ISMEAR", str(w[-1].message))
def test_get_supercell_size(self): l = Lattice.cubic(1) l2 = Lattice.cubic(0.9) s1 = Structure(l, ['Mg', 'Cu', 'Ag', 'Cu', 'Ag'], [[0]*3]*5) s2 = Structure(l2, ['Cu', 'Cu', 'Ag'], [[0]*3]*3) sm = StructureMatcher(supercell_size='volume') result = sm._get_supercell_size(s1, s2) self.assertEqual(result[0], 1) self.assertEqual(result[1], True) result = sm._get_supercell_size(s2, s1) self.assertEqual(result[0], 1) self.assertEqual(result[1], True) sm = StructureMatcher(supercell_size='num_sites') result = sm._get_supercell_size(s1, s2) self.assertEqual(result[0], 2) self.assertEqual(result[1], False) result = sm._get_supercell_size(s2, s1) self.assertEqual(result[0], 2) self.assertEqual(result[1], True)
def test_rms_vs_minimax(self): # This tests that structures with adjusted RMS less than stol, but minimax # greater than stol are treated properly sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=False) l = Lattice.orthorhombic(1, 2, 12) sp = ["Si", "Si", "Al"] s1 = Structure(l, sp, [[0.5, 0, 0], [0, 0, 0], [0, 0, 0.5]]) s2 = Structure(l, sp, [[0.5, 0, 0], [0, 0, 0], [0, 0, 0.6]]) self.assertArrayAlmostEqual(sm.get_rms_dist(s1, s2), (0.32 ** 0.5 / 2, 0.4)) self.assertEqual(sm.fit(s1, s2), False) self.assertEqual(sm.fit_anonymous(s1, s2), False) self.assertEqual(sm.get_mapping(s1, s2), None)
def test_find_match2(self): sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=True, scale=True, attempt_supercell=False) l = Lattice.orthorhombic(1, 2, 3) s1 = Structure(l, ["Si", "Si"], [[0, 0, 0.1], [0, 0, 0.2]]) s2 = Structure(l, ["Si", "Si"], [[0, 0.1, 0], [0, 0.1, -0.95]]) s1, s2, fu, s1_supercell = sm._preprocess(s1, s2, False) match = sm._strict_match(s1, s2, fu, s1_supercell=False, use_rms=True, break_on_match=False) scale_matrix = match[2] s2.make_supercell(scale_matrix) s2.translate_sites(range(len(s2)), match[3]) self.assertAlmostEqual(np.sum(s2.frac_coords), 0.3) self.assertAlmostEqual(np.sum(s2.frac_coords[:, :2]), 0)
def test_find_match1(self): sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=True, scale=True, attempt_supercell=False) l = Lattice.orthorhombic(1, 2, 3) s1 = Structure(l, ["Si", "Si", "Ag"], [[0, 0, 0.1], [0, 0, 0.2], [0.7, 0.4, 0.5]]) s2 = Structure(l, ["Si", "Si", "Ag"], [[0, 0.1, 0], [0, 0.1, -0.95], [0.7, 0.5, 0.375]]) s1, s2, fu, s1_supercell = sm._preprocess(s1, s2, False) match = sm._strict_match(s1, s2, fu, s1_supercell=True, use_rms=True, break_on_match=False) scale_matrix = match[2] s2.make_supercell(scale_matrix) fc = s2.frac_coords + match[3] fc -= np.round(fc) self.assertAlmostEqual(np.sum(fc), 0.9) self.assertAlmostEqual(np.sum(fc[:, :2]), 0.1) cart_dist = np.sum(match[1] * (l.volume / 3) ** (1 / 3)) self.assertAlmostEqual(cart_dist, 0.15)
def test_find_match2(self): sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=True, scale=True, attempt_supercell=False) l = Lattice.orthorhombic(1, 2, 3) s1 = Structure(l, ['Si', 'Si'], [[0,0,0.1],[0,0,0.2]]) s2 = Structure(l, ['Si', 'Si'], [[0,0.1,0],[0,0.1,-0.95]]) match = sm._find_match(s1, s2, break_on_match = False, use_rms = True, niggli = False) scale_matrix = np.round(np.dot(match[2].matrix, s2.lattice.inv_matrix)).astype('int') s2.make_supercell(scale_matrix) fc = s2.frac_coords + match[3] fc -= np.round(fc) self.assertAlmostEqual(np.sum(fc), 0.3) self.assertAlmostEqual(np.sum(fc[:,:2]), 0)
def test_get_s2_like_s1(self): sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=False, scale=True, attempt_supercell=True) l = Lattice.orthorhombic(1, 2, 3) s1 = Structure(l, ['Si', 'Si', 'Ag'], [[0,0,0.1],[0,0,0.2],[.7,.4,.5]]) s1.make_supercell([2,1,1]) s2 = Structure(l, ['Si', 'Si', 'Ag'], [[0,0.1,0],[0,0.1,-0.95],[-.7,.5,.375]]) result = sm.get_s2_like_s1(s1, s2) self.assertEqual(len(find_in_coord_list_pbc(result.frac_coords, [0.35,0.4,0.5])), 1) self.assertEqual(len(find_in_coord_list_pbc(result.frac_coords, [0,0,0.125])), 1) self.assertEqual(len(find_in_coord_list_pbc(result.frac_coords, [0,0,0.175])), 1)
def setUp(self): parser = CifParser(os.path.join(test_dir, 'Fe.cif')) self.Fe = parser.get_structures()[0] parser = CifParser(os.path.join(test_dir, 'LiFePO4.cif')) self.LiFePO4 = parser.get_structures()[0] parser = CifParser(os.path.join(test_dir, 'Fe3O4.cif')) self.Fe3O4 = parser.get_structures()[0] parser = CifParser(os.path.join(test_dir, 'magnetic.ncl.example.GdB4.mcif')) self.GdB4 = parser.get_structures()[0] parser = CifParser(os.path.join(test_dir, 'magnetic.example.NiO.mcif')) self.NiO_expt = parser.get_structures()[0] latt = Lattice.cubic(4.17) species = ["Ni", "O"] coords = [[0, 0, 0], [0.5, 0.5, 0.5]] self.NiO = Structure.from_spacegroup(225, latt, species, coords) latt = Lattice([[2.085, 2.085, 0.0], [0.0, -2.085, -2.085], [-2.085, 2.085, -4.17]]) species = ["Ni", "Ni", "O", "O"] coords = [[0.5, 0, 0.5], [0, 0, 0], [0.25, 0.5, 0.25], [0.75, 0.5, 0.75]] self.NiO_AFM_111 = Structure(latt, species, coords, site_properties={'magmom': [-5, 5, 0, 0]}) latt = Lattice([[2.085, 2.085, 0], [0, 0, -4.17], [-2.085, 2.085, 0]]) species = ["Ni", "Ni", "O", "O"] coords = [[0.5, 0.5, 0.5], [0, 0, 0], [0, 0.5, 0], [0.5, 0, 0.5]] self.NiO_AFM_001 = Structure(latt, species, coords, site_properties={'magmom': [-5, 5, 0, 0]})
def test_cart_dists(self): sm = StructureMatcher() l = Lattice.orthorhombic(1, 2, 3) s1 = np.array([[0.13, 0.25, 0.37], [0.1, 0.2, 0.3]]) s2 = np.array([[0.11, 0.22, 0.33]]) s3 = np.array([[0.1, 0.2, 0.3], [0.11, 0.2, 0.3]]) s4 = np.array([[0.1, 0.2, 0.3], [0.1, 0.6, 0.7]]) mask = np.array([[False, False]]) mask2 = np.array([[False, True]]) mask3 = np.array([[False, False], [False, False]]) mask4 = np.array([[False, True], [False, True]]) n1 = (len(s1) / l.volume) ** (1/3) n2 = (len(s2) / l.volume) ** (1/3) self.assertRaises(ValueError, sm._cart_dists, s2, s1, l, mask.T, n2) self.assertRaises(ValueError, sm._cart_dists, s1, s2, l, mask.T, n1) d, ft, s = sm._cart_dists(s1, s2, l, mask, n1) self.assertTrue(np.allclose(d, [0])) self.assertTrue(np.allclose(ft, [-0.01, -0.02, -0.03])) self.assertTrue(np.allclose(s, [1])) #check that masking best value works d, ft, s = sm._cart_dists(s1, s2, l, mask2, n1) self.assertTrue(np.allclose(d, [0])) self.assertTrue(np.allclose(ft, [0.02, 0.03, 0.04])) self.assertTrue(np.allclose(s, [0])) #check that averaging of translation is done properly d, ft, s = sm._cart_dists(s1, s3, l, mask3, n1) self.assertTrue(np.allclose(d, [0.08093341]*2)) self.assertTrue(np.allclose(ft, [0.01, 0.025, 0.035])) self.assertTrue(np.allclose(s, [1, 0])) #check distances are large when mask allows no 'real' mapping d, ft, s = sm._cart_dists(s1, s4, l, mask4, n1) self.assertTrue(np.min(d) > 1e8) self.assertTrue(np.min(ft) > 1e8)
def test_subset(self): sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=False, scale=True, attempt_supercell=False, allow_subset=True) l = Lattice.orthorhombic(10, 20, 30) s1 = Structure(l, ['Si', 'Si', 'Ag'], [[0,0,0.1],[0,0,0.2],[.7,.4,.5]]) s2 = Structure(l, ['Si', 'Ag'], [[0,0.1,0],[-.7,.5,.4]]) result = sm.get_s2_like_s1(s1, s2) self.assertEqual(len(find_in_coord_list_pbc(result.frac_coords, [0,0,0.1])), 1) self.assertEqual(len(find_in_coord_list_pbc(result.frac_coords, [0.7,0.4,0.5])), 1) #test with fewer species in s2 s1 = Structure(l, ['Si', 'Ag', 'Si'], [[0,0,0.1],[0,0,0.2],[.7,.4,.5]]) s2 = Structure(l, ['Si', 'Si'], [[0,0.1,0],[-.7,.5,.4]]) result = sm.get_s2_like_s1(s1, s2) self.assertEqual(len(find_in_coord_list_pbc(result.frac_coords, [0,0,0.1])), 1) self.assertEqual(len(find_in_coord_list_pbc(result.frac_coords, [0.7,0.4,0.5])), 1) #test with not enough sites in s1 #test with fewer species in s2 s1 = Structure(l, ['Si', 'Ag', 'Cl'], [[0,0,0.1],[0,0,0.2],[.7,.4,.5]]) s2 = Structure(l, ['Si', 'Si'], [[0,0.1,0],[-.7,.5,.4]]) self.assertEqual(sm.get_s2_like_s1(s1, s2), None)
def simple_cubic(): lattice = Lattice.cubic(1.0) coords = [[0.0, 0.0, 0.0]] return IStructure(lattice=lattice, species=["H"], coords=coords)
def monoclinic(): lattice = Lattice.monoclinic(3, 4, 5, 100) coords = [[0.0, 0.0, 0.0]] return IStructure(lattice=lattice, species=["H"], coords=coords)
def test_supercell_subsets(self): sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=False, scale=True, attempt_supercell=True, allow_subset=True, supercell_size='volume') sm_no_s = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=False, scale=True, attempt_supercell=True, allow_subset=False, supercell_size='volume') l = Lattice.orthorhombic(1, 2, 3) s1 = Structure(l, ['Ag', 'Si', 'Si'], [[.7,.4,.5],[0,0,0.1],[0,0,0.2]]) s1.make_supercell([2,1,1]) s2 = Structure(l, ['Si', 'Si', 'Ag'], [[0,0.1,-0.95],[0,0.1,0],[-.7,.5,.375]]) shuffle = [0,2,1,3,4,5] s1 = Structure.from_sites([s1[i] for i in shuffle]) #test when s1 is exact supercell of s2 result = sm.get_s2_like_s1(s1, s2) for a, b in zip(s1, result): self.assertTrue(a.distance(b) < 0.08) self.assertEqual(a.species_and_occu, b.species_and_occu) self.assertTrue(sm.fit(s1, s2)) self.assertTrue(sm.fit(s2, s1)) self.assertTrue(sm_no_s.fit(s1, s2)) self.assertTrue(sm_no_s.fit(s2, s1)) rms = (0.048604032430991401, 0.059527539448807391) self.assertTrue(np.allclose(sm.get_rms_dist(s1, s2), rms)) self.assertTrue(np.allclose(sm.get_rms_dist(s2, s1), rms)) #test when the supercell is a subset of s2 subset_supercell = s1.copy() del subset_supercell[0] result = sm.get_s2_like_s1(subset_supercell, s2) self.assertEqual(len(result), 6) for a, b in zip(subset_supercell, result): self.assertTrue(a.distance(b) < 0.08) self.assertEqual(a.species_and_occu, b.species_and_occu) self.assertTrue(sm.fit(subset_supercell, s2)) self.assertTrue(sm.fit(s2, subset_supercell)) self.assertFalse(sm_no_s.fit(subset_supercell, s2)) self.assertFalse(sm_no_s.fit(s2, subset_supercell)) rms = (0.053243049896333279, 0.059527539448807336) self.assertTrue(np.allclose(sm.get_rms_dist(subset_supercell, s2), rms)) self.assertTrue(np.allclose(sm.get_rms_dist(s2, subset_supercell), rms)) #test when s2 (once made a supercell) is a subset of s1 s2_missing_site = s2.copy() del s2_missing_site[1] result = sm.get_s2_like_s1(s1, s2_missing_site) for a, b in zip((s1[i] for i in (0, 2, 4, 5)), result): self.assertTrue(a.distance(b) < 0.08) self.assertEqual(a.species_and_occu, b.species_and_occu) self.assertTrue(sm.fit(s1, s2_missing_site)) self.assertTrue(sm.fit(s2_missing_site, s1)) self.assertFalse(sm_no_s.fit(s1, s2_missing_site)) self.assertFalse(sm_no_s.fit(s2_missing_site, s1)) rms = (0.029763769724403633, 0.029763769724403987) self.assertTrue(np.allclose(sm.get_rms_dist(s1, s2_missing_site), rms)) self.assertTrue(np.allclose(sm.get_rms_dist(s2_missing_site, s1), rms))
def test_fit(self): """ Take two known matched structures 1) Ensure match 2) Ensure match after translation and rotations 3) Ensure no-match after large site translation 4) Ensure match after site shuffling """ sm = StructureMatcher() self.assertTrue(sm.fit(self.struct_list[0], self.struct_list[1])) # Test rotational/translational invariance op = SymmOp.from_axis_angle_and_translation([0, 0, 1], 30, False, np.array([0.4, 0.7, 0.9])) self.struct_list[1].apply_operation(op) self.assertTrue(sm.fit(self.struct_list[0], self.struct_list[1])) #Test failure under large atomic translation self.struct_list[1].translate_sites([0], [.4, .4, .2], frac_coords=True) self.assertFalse(sm.fit(self.struct_list[0], self.struct_list[1])) self.struct_list[1].translate_sites([0], [-.4, -.4, -.2], frac_coords=True) # random.shuffle(editor._sites) self.assertTrue(sm.fit(self.struct_list[0], self.struct_list[1])) #Test FrameworkComporator sm2 = StructureMatcher(comparator=FrameworkComparator()) lfp = self.get_structure("LiFePO4") nfp = self.get_structure("NaFePO4") self.assertTrue(sm2.fit(lfp, nfp)) self.assertFalse(sm.fit(lfp, nfp)) #Test anonymous fit. self.assertEqual(sm.fit_anonymous(lfp, nfp), True) self.assertAlmostEqual( sm.get_rms_anonymous(lfp, nfp)[0], 0.060895871160262717) #Test partial occupancies. s1 = Structure(Lattice.cubic(3), [{ "Fe": 0.5 }, { "Fe": 0.5 }, { "Fe": 0.5 }, { "Fe": 0.5 }], [[0, 0, 0], [0.25, 0.25, 0.25], [0.5, 0.5, 0.5], [0.75, 0.75, 0.75]]) s2 = Structure(Lattice.cubic(3), [{ "Fe": 0.25 }, { "Fe": 0.5 }, { "Fe": 0.5 }, { "Fe": 0.75 }], [[0, 0, 0], [0.25, 0.25, 0.25], [0.5, 0.5, 0.5], [0.75, 0.75, 0.75]]) self.assertFalse(sm.fit(s1, s2)) self.assertFalse(sm.fit(s2, s1)) s2 = Structure(Lattice.cubic(3), [{ "Mn": 0.5 }, { "Mn": 0.5 }, { "Mn": 0.5 }, { "Mn": 0.5 }], [[0, 0, 0], [0.25, 0.25, 0.25], [0.5, 0.5, 0.5], [0.75, 0.75, 0.75]]) self.assertEqual(sm.fit_anonymous(s1, s2), True) self.assertAlmostEqual(sm.get_rms_anonymous(s1, s2)[0], 0)
if scan: wf_name += " - SCAN" wf = Workflow(fws, name=wf_name) wf = add_additional_fields_to_taskdocs(wf, {"wf_meta": self.wf_meta}) tag = "magnetic_orderings group: >>{}<<".format(self.uuid) wf = add_tags(wf, [tag, ordered_structure_origins]) return wf if __name__ == "__main__": # for trying workflows from fireworks import LaunchPad latt = Lattice.cubic(4.17) species = ["Ni", "O"] coords = [[0.00000, 0.00000, 0.00000], [0.50000, 0.50000, 0.50000]] NiO = Structure.from_spacegroup(225, latt, species, coords) wf_deformation = get_wf_magnetic_deformation(NiO) wf_orderings = MagneticOrderingsWF(NiO).get_wf() lpad = LaunchPad.auto_load() lpad.add_wf(wf_orderings) lpad.add_wf(wf_deformation)
def test_relative_to_crystal_axes(self): lattice = Lattice.from_parameters(5, 10, 5, 90, 110, 90) moment = [1, 0, 2] magmom = Magmom.from_moment_relative_to_crystal_axes(moment, lattice) self.assertTrue(np.allclose(magmom.moment, [0.93969262, 0.0, 1.65797986])) self.assertTrue(np.allclose(magmom.get_moment_relative_to_crystal_axes(lattice), moment))
def test_make_band_edge_orbital_infos(mocker): mock_procar = mocker.Mock(spec=Procar, autospec=True) mock_vasprun = mocker.Mock(spec=Vasprun, autospec=True) mock_str_info = mocker.Mock(spec=DefectStructureInfo, autospec=True) mock_vasprun.actual_kpoints = [[0.0, 0.0, 0.0]] mock_vasprun.actual_kpoints_weights = [1.0] mock_vasprun.final_structure = Structure(Lattice.cubic(1), species=["H", "H", "He"], coords=[[0] * 3] * 3) mock_vasprun.eigenvalues = { Spin.up: np.array([ [ [-3.01, 1.], [-2.90, 1.], # vbm [8.01, 0.] ], [[-3.01, 1.], [-2.99, 1.], [7.90, 0.]] ]), # cbm # shift one band for testing magnetization Spin.down: np.array([[[-2.99, 1.], [7.99, 0.], [10.00, 0.]], [[-3.01, 1.], [8.00, 0.], [10.00, 0.]]]) } mock_vasprun.efermi = 20.0 # s: 1, p: 3, d: 5, f: 7 = 16 orbitals mock_procar.data = { Spin.up: np.array([ [ [ [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], # 1st kpt, 0th band, 1st atom [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], # 1st kpt, 0th band, 2nd atom [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] ], # 1st kpt, 0th band, 3rd atom [ [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], # 1st kpt, 1st band, 1st atom [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] ], [[1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]] ], [ [ [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], # 2nd kpt, 0th band, 1st atom [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] ], [[1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]], [[1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]] ] ]), Spin.down: np.array([ [ [ [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], # 1st kpt, 0th band, 1st atom [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], # 1st kpt, 0th band, 2nd atom [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] ], # 1st kpt, 0th band, 3rd atom [ [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], # 1st kpt, 1st band, 1st atom [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] ], [[1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]] ], [ [ [1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], # 2nd kpt, 0th band, 1st atom [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0] ], [[1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]], [[1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]] ] ]), } mock_str_info.neighbor_atom_indices = [0] actual = make_band_edge_orbital_infos(mock_procar, mock_vasprun, vbm=0.0, cbm=5.0, str_info=mock_str_info) expected = BandEdgeOrbitalInfos( orbital_infos=[[[ OrbitalInfo(energy=-2.9, orbitals={ "H": [1.0, 0.0, 0.0, 0.0], "He": [0.0, 0.0, 0.0, 0.0] }, occupation=1.0, participation_ratio=1.0), OrbitalInfo(energy=8.01, orbitals={ "H": [1.0, 0.0, 0.0, 0.0], "He": [0.0, 0.0, 0.0, 0.0] }, occupation=0.0, participation_ratio=1.0) ], [ OrbitalInfo(energy=-2.99, orbitals={ "H": [1.0, 0.0, 0.0, 0.0], "He": [0.0, 0.0, 0.0, 0.0] }, occupation=1.0, participation_ratio=1.0), OrbitalInfo(energy=7.90, orbitals={ "H": [1.0, 0.0, 0.0, 0.0], "He": [0.0, 0.0, 0.0, 0.0] }, occupation=0.0, participation_ratio=1.0) ]], [[ OrbitalInfo(energy=7.99, orbitals={ "H": [1.0, 0.0, 0.0, 0.0], "He": [0.0, 0.0, 0.0, 0.0] }, occupation=0.0, participation_ratio=1.0), OrbitalInfo(energy=10.00, orbitals={ "H": [1.0, 0.0, 0.0, 0.0], "He": [0.0, 0.0, 0.0, 0.0] }, occupation=0.0, participation_ratio=1.0) ], [ OrbitalInfo(energy=8.00, orbitals={ "H": [1.0, 0.0, 0.0, 0.0], "He": [0.0, 0.0, 0.0, 0.0] }, occupation=0.0, participation_ratio=1.0), OrbitalInfo(energy=10.00, orbitals={ "H": [1.0, 0.0, 0.0, 0.0], "He": [0.0, 0.0, 0.0, 0.0] }, occupation=0.0, participation_ratio=1.0) ]]], kpt_coords=[(0.0, 0.0, 0.0)], kpt_weights=[1.0], lowest_band_index=1, fermi_level=20.0) assert_dataclass_almost_equal(actual, expected)
def get_face_centered_cubic(): latt = Lattice(np.array([[0, 1, 1], [1, 0, 1], [1, 1, 0]])) struct = Structure(latt, ["Al"], [[0, 0, 0]]) return struct
def get_body_centered_cubic(): latt = Lattice(np.array([[-1, 1, 1], [1, -1, 1], [1, 1, -1]])) struct = Structure(latt, ["Fe"], [[0, 0, 0]]) return struct
def from_slabs( cls, substrate_slab: Slab, film_slab: Slab, in_plane_offset: Tuple[float, float] = (0, 0), gap: float = 1.6, vacuum_over_film: float = 0.0, interface_properties: Dict = {}, center_slab: bool = True, ) -> Interface: """ Makes an interface structure by merging a substrate and film slabs The film a- and b-vectors will be forced to be the substrate slab's a- and b-vectors. For now, it's suggested to use a factory method that will ensure the appropriate interface structure is already met. Args: sub_slab: slab for the substrate film_slab: slab for the film in_plane_offset: fractional shift in plane for the film with respect to the substrate gap: gap between substrate and film in Angstroms vacuum_over_film: vacuum space above the film in Angstroms structure_properties: dictionary of misc properties for this structure center_slab: center the slab """ # Ensure c-axis is orthogonal to a/b plane if isinstance(substrate_slab, Slab): substrate_slab = substrate_slab.get_orthogonal_c_slab() if isinstance(film_slab, Slab): film_slab = film_slab.get_orthogonal_c_slab() assert np.allclose(film_slab.lattice.alpha, 90, 0.1) assert np.allclose(film_slab.lattice.beta, 90, 0.1) assert np.allclose(substrate_slab.lattice.alpha, 90, 0.1) assert np.allclose(substrate_slab.lattice.beta, 90, 0.1) # Ensure sub is right-handed # IE sub has surface facing "up" sub_vecs = substrate_slab.lattice.matrix.copy() if np.dot(np.cross(*sub_vecs[:2]), sub_vecs[2]) < 0: sub_vecs[2] *= -1.0 substrate_slab.lattice = Lattice(sub_vecs) # Find the limits of C-coords sub_coords = substrate_slab.frac_coords film_coords = film_slab.frac_coords sub_min_c = np.min(sub_coords[:, 2]) * substrate_slab.lattice.c sub_max_c = np.max(sub_coords[:, 2]) * substrate_slab.lattice.c film_min_c = np.min(film_coords[:, 2]) * film_slab.lattice.c film_max_c = np.max(film_coords[:, 2]) * film_slab.lattice.c min_height = np.abs(film_max_c - film_min_c) + np.abs(sub_max_c - sub_min_c) # construct new lattice abc = substrate_slab.lattice.abc[:2] + (min_height + gap + vacuum_over_film, ) angles = substrate_slab.lattice.angles lattice = Lattice.from_parameters(*abc, *angles) # Get the species species = substrate_slab.species + film_slab.species # Get the coords # Shift substrate to bottom in new lattice sub_coords = np.subtract(sub_coords, [0, 0, np.min(sub_coords[:, 2])]) sub_coords[:, 2] *= substrate_slab.lattice.c / lattice.c # Flip the film over film_coords[:, 2] *= -1.0 film_coords[:, 2] *= film_slab.lattice.c / lattice.c # Shift the film coords to right over the substrate + gap film_coords = np.subtract(film_coords, [0, 0, np.min(film_coords[:, 2])]) film_coords = np.add( film_coords, [0, 0, gap / lattice.c + np.max(sub_coords[:, 2])]) # Build coords coords = np.concatenate([sub_coords, film_coords]) # Shift coords to center if center_slab: coords = np.add(coords, [0, 0, 0.5 - np.average(coords[:, 2])]) # Only merge site properties in both slabs site_properties = {} site_props_in_both = set(substrate_slab.site_properties.keys()) & set( film_slab.site_properties.keys()) for key in site_props_in_both: site_properties[key] = [ *substrate_slab.site_properties[key], *film_slab.site_properties[key], ] site_properties["interface_label"] = ["substrate"] * len( substrate_slab) + ["film"] * len(film_slab) iface = cls( lattice=lattice, species=species, coords=coords, to_unit_cell=False, coords_are_cartesian=False, site_properties=site_properties, validate_proximity=False, in_plane_offset=in_plane_offset, gap=gap, vacuum_over_film=vacuum_over_film, interface_properties=interface_properties, ) iface.sort() return iface
def test_get_incar(self): incar = self.mpset.incar self.assertEqual(incar['LDAUU'], [5.3, 0, 0]) self.assertAlmostEqual(incar['EDIFF'], 0.0012) incar = self.mitset.incar self.assertEqual(incar['LDAUU'], [4.0, 0, 0]) self.assertAlmostEqual(incar['EDIFF'], 1e-5) si = 14 coords = list() coords.append(np.array([0, 0, 0])) coords.append(np.array([0.75, 0.5, 0.75])) # Silicon structure for testing. latt = Lattice(np.array([[3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603]])) struct = Structure(latt, [si, si], coords) incar = MPRelaxSet(struct).incar self.assertNotIn("LDAU", incar) coords = list() coords.append([0, 0, 0]) coords.append([0.75, 0.5, 0.75]) lattice = Lattice([[3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603]]) struct = Structure(lattice, ["Fe", "Mn"], coords) incar = MPRelaxSet(struct).incar self.assertNotIn('LDAU', incar) # check fluorides struct = Structure(lattice, ["Fe", "F"], coords) incar = MPRelaxSet(struct).incar self.assertEqual(incar['LDAUU'], [5.3, 0]) self.assertEqual(incar['MAGMOM'], [5, 0.6]) struct = Structure(lattice, ["Fe", "F"], coords) incar = MITRelaxSet(struct).incar self.assertEqual(incar['LDAUU'], [4.0, 0]) # Make sure this works with species. struct = Structure(lattice, ["Fe2+", "O2-"], coords) incar = MPRelaxSet(struct).incar self.assertEqual(incar['LDAUU'], [5.3, 0]) struct = Structure(lattice, ["Fe", "Mn"], coords, site_properties={'magmom': (5.2, -4.5)}) incar = MPRelaxSet(struct).incar self.assertEqual(incar['MAGMOM'], [-4.5, 5.2]) incar = MITRelaxSet(struct, sort_structure=False).incar self.assertEqual(incar['MAGMOM'], [5.2, -4.5]) struct = Structure(lattice, [Specie("Fe", 2, {'spin': 4.1}), "Mn"], coords) incar = MPRelaxSet(struct).incar self.assertEqual(incar['MAGMOM'], [5, 4.1]) struct = Structure(lattice, ["Mn3+", "Mn4+"], coords) incar = MITRelaxSet(struct).incar self.assertEqual(incar['MAGMOM'], [4, 3]) userset = MPRelaxSet(struct, user_incar_settings={ 'MAGMOM': {"Fe": 10, "S": -5, "Mn3+": 100}} ) self.assertEqual(userset.incar['MAGMOM'], [100, 0.6]) # sulfide vs sulfate test coords = list() coords.append([0, 0, 0]) coords.append([0.75, 0.5, 0.75]) coords.append([0.25, 0.5, 0]) struct = Structure(lattice, ["Fe", "Fe", "S"], coords) incar = MITRelaxSet(struct).incar self.assertEqual(incar['LDAUU'], [1.9, 0]) # Make sure Matproject sulfides are ok. self.assertNotIn('LDAUU', MPRelaxSet(struct).incar) struct = Structure(lattice, ["Fe", "S", "O"], coords) incar = MITRelaxSet(struct).incar self.assertEqual(incar['LDAUU'], [4.0, 0, 0]) # Make sure Matproject sulfates are ok. self.assertEqual(MPRelaxSet(struct).incar['LDAUU'], [5.3, 0, 0]) # test for default LDAUU value userset_ldauu_fallback = MPRelaxSet(struct, user_incar_settings={ 'LDAUU': {'Fe': 5.0, 'S': 0}} ) self.assertEqual(userset_ldauu_fallback.incar['LDAUU'], [5.0, 0, 0]) # Expected to be oxide (O is the most electronegative atom) s = Structure(lattice, ["Fe", "O", "S"], coords) incar = MITRelaxSet(s).incar self.assertEqual(incar["LDAUU"], [4.0, 0, 0]) # Expected to be chloride (Cl is the most electronegative atom) s = Structure(lattice, ["Fe", "Cl", "S"], coords) incar = MITRelaxSet(s, user_incar_settings={"LDAU": True}).incar self.assertFalse("LDAUU" in incar) # LDAU = False # User set a compound to be sulfide by specifing values of "LDAUL" etc. s = Structure(lattice, ["Fe", "Cl", "S"], coords) incar = MITRelaxSet(s, user_incar_settings={"LDAU": True, "LDAUL": {"Fe": 3}, "LDAUU": {"Fe": 1.8}}).incar self.assertEqual(incar["LDAUL"], [3.0, 0, 0]) self.assertEqual(incar["LDAUU"], [1.8, 0, 0]) # test that van-der-Waals parameters are parsed correctly incar = MITRelaxSet(struct, vdw='optB86b').incar self.assertEqual(incar['GGA'], 'Mk') self.assertEqual(incar['LUSE_VDW'], True) self.assertEqual(incar['PARAM1'], 0.1234) # Test that NELECT is updated when a charge is present si = 14 coords = list() coords.append(np.array([0, 0, 0])) coords.append(np.array([0.75, 0.5, 0.75])) # Silicon structure for testing. latt = Lattice(np.array([[3.8401979337, 0.00, 0.00], [1.9200989668, 3.3257101909, 0.00], [0.00, -2.2171384943, 3.1355090603]])) struct = Structure(latt, [si, si], coords, charge=1) mpr = MPRelaxSet(struct, use_structure_charge=True) self.assertEqual(mpr.incar["NELECT"], 7, "NELECT not properly set for nonzero charge") #test that NELECT does not get set when use_structure_charge = False mpr = MPRelaxSet(struct, use_structure_charge=False) self.assertFalse("NELECT" in mpr.incar.keys(), "NELECT should not be set when " "use_structure_charge is False")
def cubic_supercell(): lattice = Lattice.cubic(10) coords = [ [0.00, 0.00, 0.00], [0.00, 0.00, 0.50], [0.00, 0.50, 0.00], [0.00, 0.50, 0.50], [0.50, 0.00, 0.00], [0.50, 0.00, 0.50], [0.50, 0.50, 0.00], [0.50, 0.50, 0.50], [0.00, 0.25, 0.25], [0.00, 0.25, 0.75], [0.00, 0.75, 0.25], [0.00, 0.75, 0.75], [0.50, 0.25, 0.25], [0.50, 0.25, 0.75], [0.50, 0.75, 0.25], [0.50, 0.75, 0.75], [0.25, 0.00, 0.25], [0.25, 0.00, 0.75], [0.25, 0.50, 0.25], [0.25, 0.50, 0.75], [0.75, 0.00, 0.25], [0.75, 0.00, 0.75], [0.75, 0.50, 0.25], [0.75, 0.50, 0.75], [0.25, 0.25, 0.00], [0.25, 0.25, 0.50], [0.25, 0.75, 0.00], [0.25, 0.75, 0.50], [0.75, 0.25, 0.00], [0.75, 0.25, 0.50], [0.75, 0.75, 0.00], [0.75, 0.75, 0.50], [0.25, 0.00, 0.00], [0.25, 0.00, 0.50], [0.25, 0.50, 0.00], [0.25, 0.50, 0.50], [0.75, 0.00, 0.00], [0.75, 0.00, 0.50], [0.75, 0.50, 0.00], [0.75, 0.50, 0.50], [0.25, 0.25, 0.25], [0.25, 0.25, 0.75], [0.25, 0.75, 0.25], [0.25, 0.75, 0.75], [0.75, 0.25, 0.25], [0.75, 0.25, 0.75], [0.75, 0.75, 0.25], [0.75, 0.75, 0.75], [0.00, 0.00, 0.25], [0.00, 0.00, 0.75], [0.00, 0.50, 0.25], [0.00, 0.50, 0.75], [0.50, 0.00, 0.25], [0.50, 0.00, 0.75], [0.50, 0.50, 0.25], [0.50, 0.50, 0.75], [0.00, 0.25, 0.00], [0.00, 0.25, 0.50], [0.00, 0.75, 0.00], [0.00, 0.75, 0.50], [0.50, 0.25, 0.00], [0.50, 0.25, 0.50], [0.50, 0.75, 0.00], [0.50, 0.75, 0.50], ] return Structure(lattice=lattice, species=["H"] * 32 + ["He"] * 32, coords=coords)
def test_kumagai(self): gamma = 0.19357221 prec = 28 lattice = Lattice([[4.692882, -8.12831, 0.0], [4.692882, 8.12831, 0.0], [0.0, 0.0, 10.03391]]) # note that real/recip vector generation is not dependent on epsilon g_vecs, _, r_vecs, _ = generate_R_and_G_vecs(gamma, prec, lattice, 80.0 * np.identity(3)) # test real space summation (bigger for large epsilon) kc_high_diel = KumagaiCorrection(80.0 * np.identity(3), gamma=gamma) real_sum = kc_high_diel.get_real_summation(gamma, r_vecs[0]) self.assertAlmostEqual(real_sum, 0.00843104) # test recip space summation (bigger for small epsilon) kc_low_diel = KumagaiCorrection(0.1 * np.identity(3), gamma=gamma) recip_sum = kc_low_diel.get_recip_summation(gamma, g_vecs[0], lattice.volume) self.assertAlmostEqual(recip_sum, 0.31117099) # test self interaction si_corr = kc_low_diel.get_self_interaction(gamma) self.assertAlmostEqual(si_corr, -0.54965249) # test potenital shift interaction correction ps_corr = kc_low_diel.get_potential_shift(gamma, lattice.volume) self.assertAlmostEqual(ps_corr, -0.00871593) # """Test Defect Entry approach to correction """ bulk_struc = Poscar.from_file( os.path.join(PymatgenTest.TEST_FILES_DIR, "defect", "CONTCAR_bulk")).structure bulk_out = Outcar( os.path.join(PymatgenTest.TEST_FILES_DIR, "defect", "OUTCAR_bulk.gz")) defect_out = Outcar( os.path.join(PymatgenTest.TEST_FILES_DIR, "defect", "OUTCAR_vac_Ga_-3.gz")) epsilon = 18.118 * np.identity(3) vac = Vacancy(bulk_struc, bulk_struc.sites[0], charge=-3) defect_structure = vac.generate_defect_structure() defect_frac_coords = [0.0, 0.0, 0.0] parameters = { "bulk_atomic_site_averages": bulk_out.electrostatic_potential, "defect_atomic_site_averages": defect_out.electrostatic_potential, "site_matching_indices": [[ind, ind - 1] for ind in range(len(bulk_struc))], "initial_defect_structure": defect_structure, "defect_frac_sc_coords": defect_frac_coords, } dentry = DefectEntry(vac, 0.0, parameters=parameters) kc = KumagaiCorrection(epsilon) kcorr = kc.get_correction(dentry) self.assertAlmostEqual(kcorr["kumagai_electrostatic"], 0.88236299) self.assertAlmostEqual(kcorr["kumagai_potential_alignment"], 2.09704862) # test ES correction high_diel_es_corr = kc_high_diel.perform_es_corr( gamma, prec, lattice, -3.0) self.assertAlmostEqual(high_diel_es_corr, 0.25176240) low_diel_es_corr = kc_low_diel.perform_es_corr(gamma, prec, lattice, -3.0) self.assertAlmostEqual(low_diel_es_corr, 201.28810966) # test pot correction site_list = [] for bs_ind, ds_ind in dentry.parameters["site_matching_indices"]: Vqb = -(defect_out.electrostatic_potential[ds_ind] - bulk_out.electrostatic_potential[bs_ind]) site_list.append([defect_structure[ds_ind], Vqb]) sampling_radius = dentry.parameters["kumagai_meta"]["sampling_radius"] gamma = dentry.parameters["kumagai_meta"]["gamma"] q = -3 g_vecs, _, r_vecs, _ = generate_R_and_G_vecs(gamma, 28, defect_structure.lattice, np.identity(3)) high_diel_pot_corr = kc_high_diel.perform_pot_corr( defect_structure, defect_frac_coords, site_list, sampling_radius, q, r_vecs[0], g_vecs[0], gamma, ) self.assertAlmostEqual(high_diel_pot_corr, 2.35840716) low_diel_pot_corr = kc_low_diel.perform_pot_corr( defect_structure, defect_frac_coords, site_list, sampling_radius, q, r_vecs[0], g_vecs[0], gamma, ) self.assertAlmostEqual(low_diel_pot_corr, -58.83598095) # test the kumagai plotter kcp = kc.plot() self.assertTrue(kcp) # check that uncertainty metadata exists self.assertAlmostEqual( set(kc.metadata["pot_corr_uncertainty_md"].keys()), set(["number_sampled", "stats"]), )
def setUp(self): self.sc = Structure(Lattice([[3.52, 0, 0], [0, 3.52, 0], [0, 0, 3.52]]), ["Al"], [[0, 0, 0]], validate_proximity=False, to_unit_cell=False, coords_are_cartesian=False) self.target = torch.Tensor([1, 0, 1])
def parchg(): struc = Structure(lattice=Lattice.cubic(10), species=["H"], coords=[[0]*3]) data = {"total": np.array([[[0, 0], [0, 2000.0]], [[0, 0], [0, 0]]]), "diff": np.array([[[0, 0], [0, 1000.0]], [[0, 0], [0, 0]]])} # spin-up: 1.5, spin-down: 0.5 return Chgcar(struc, data=data)
def __mul__(self, scaling_matrix): """ Replicates the graph, creating a supercell, intelligently joining together edges that lie on periodic boundaries. In principle, any operations on the expanded graph could also be done on the original graph, but a larger graph can be easier to visualize and reason about. :param scaling_matrix: same as Structure.__mul__ :return: """ # Developer note: a different approach was also trialed, using # a simple Graph (instead of MultiDiGraph), with node indices # representing both site index and periodic image. Here, the # number of nodes != number of sites in the Structure. This # approach has many benefits, but made it more difficult to # keep the graph in sync with its corresponding Structure. # Broadly, it would be easier to multiply the Structure # *before* generating the StructureGraph, but this isn't # possible when generating the graph using critic2 from # charge density. # Multiplication works by looking for the expected position # of an image node, and seeing if that node exists in the # supercell. If it does, the edge is updated. This is more # computationally expensive than just keeping track of the # which new lattice images present, but should hopefully be # easier to extend to a general 3x3 scaling matrix. # code adapted from Structure.__mul__ scale_matrix = np.array(scaling_matrix, np.int16) if scale_matrix.shape != (3, 3): scale_matrix = np.array(scale_matrix * np.eye(3), np.int16) else: # TODO: test __mul__ with full 3x3 scaling matrices raise NotImplementedError( 'Not tested with 3x3 scaling matrices yet.') new_lattice = Lattice( np.dot(scale_matrix, self.structure.lattice.matrix)) f_lat = lattice_points_in_supercell(scale_matrix) c_lat = new_lattice.get_cartesian_coords(f_lat) new_sites = [] new_graphs = [] for v in c_lat: # create a map of nodes from original graph to its image mapping = { n: n + len(new_sites) for n in range(len(self.structure)) } for idx, site in enumerate(self.structure): s = PeriodicSite(site.species_and_occu, site.coords + v, new_lattice, properties=site.properties, coords_are_cartesian=True, to_unit_cell=False) new_sites.append(s) new_graphs.append(nx.relabel_nodes(self.graph, mapping, copy=True)) new_structure = Structure.from_sites(new_sites) # merge all graphs into one big graph new_g = nx.MultiDiGraph() for new_graph in new_graphs: new_g = nx.union(new_g, new_graph) edges_to_remove = [] # tuple of (u, v, k) edges_to_add = [] # tuple of (u, v, attr_dict) # list of new edges inside supercell # for duplicate checking edges_inside_supercell = [{u, v} for u, v, d in new_g.edges(data=True) if d['to_jimage'] == (0, 0, 0)] new_periodic_images = [] orig_lattice = self.structure.lattice # use k-d tree to match given position to an # existing Site in Structure kd_tree = KDTree(new_structure.cart_coords) # tolerance in Å for sites to be considered equal # this could probably be a lot smaller tol = 0.05 for u, v, k, d in new_g.edges(keys=True, data=True): to_jimage = d['to_jimage'] # for node v # reduce unnecessary checking if to_jimage != (0, 0, 0): # get index in original site n_u = u % len(self.structure) n_v = v % len(self.structure) # get fractional co-ordinates of where atoms defined # by edge are expected to be, relative to original # lattice (keeping original lattice has # significant benefits) v_image_frac = np.add(self.structure[n_v].frac_coords, to_jimage) u_frac = self.structure[n_u].frac_coords # using the position of node u as a reference, # get relative Cartesian co-ordinates of where # atoms defined by edge are expected to be v_image_cart = orig_lattice.get_cartesian_coords(v_image_frac) u_cart = orig_lattice.get_cartesian_coords(u_frac) v_rel = np.subtract(v_image_cart, u_cart) # now retrieve position of node v in # new supercell, and get absolute Cartesian # co-ordinates of where atoms defined by edge # are expected to be v_expec = new_structure[u].coords + v_rel # now search in new structure for these atoms # query returns (distance, index) v_present = kd_tree.query(v_expec) v_present = v_present[1] if v_present[0] <= tol else None # check if image sites now present in supercell # and if so, delete old edge that went through # periodic boundary if v_present is not None: new_u = u new_v = v_present new_d = d.copy() # node now inside supercell new_d['to_jimage'] = (0, 0, 0) edges_to_remove.append((u, v, k)) # make sure we don't try to add duplicate edges # will remove two edges for everyone one we add if {new_u, new_v} not in edges_inside_supercell: # normalize direction if new_v < new_u: new_u, new_v = new_v, new_u edges_inside_supercell.append({new_u, new_v}) edges_to_add.append((new_u, new_v, new_d)) else: # want to find new_v such that we have # full periodic boundary conditions # so that nodes on one side of supercell # are connected to nodes on opposite side v_expec_frac = new_structure.lattice.get_fractional_coords( v_expec) # find new to_jimage # use np.around to fix issues with finite precision leading to incorrect image v_expec_image = np.around(v_expec_frac, decimals=3) v_expec_image = v_expec_image - v_expec_image % 1 v_expec_frac = np.subtract(v_expec_frac, v_expec_image) v_expec = new_structure.lattice.get_cartesian_coords( v_expec_frac) v_present = kd_tree.query(v_expec) v_present = v_present[1] if v_present[0] <= tol else None if v_present is not None: new_u = u new_v = v_present new_d = d.copy() new_to_jimage = tuple(map(int, v_expec_image)) # normalize direction if new_v < new_u: new_u, new_v = new_v, new_u new_to_jimage = tuple( np.multiply(-1, d['to_jimage']).astype(int)) new_d['to_jimage'] = new_to_jimage edges_to_remove.append((u, v, k)) if (new_u, new_v, new_to_jimage) not in new_periodic_images: edges_to_add.append((new_u, new_v, new_d)) new_periodic_images.append( (new_u, new_v, new_to_jimage)) logger.debug("Removing {} edges, adding {} new edges.".format( len(edges_to_remove), len(edges_to_add))) # add/delete marked edges for edges_to_remove in edges_to_remove: new_g.remove_edge(*edges_to_remove) for (u, v, d) in edges_to_add: new_g.add_edge(u, v, **d) # return new instance of StructureGraph with supercell d = { "@module": self.__class__.__module__, "@class": self.__class__.__name__, "structure": new_structure.as_dict(), "graphs": json_graph.adjacency_data(new_g) } sg = StructureGraph.from_dict(d) return sg
def bcc(): lattice = Lattice.cubic(1.0) coords = [[0.0, 0.0, 0.0], [0.5, 0.5, 0.5]] return IStructure(lattice=lattice, species=["H"] * 2, coords=coords)
def c_centered_monoclinic(): lattice = Lattice.monoclinic(3, 4, 5, 100) coords = [[0.0, 0.0, 0.0], [0.5, 0.5, 0.0]] return IStructure(lattice=lattice, species=["H", "H"], coords=coords)
def a_centered_orthorhombic(): lattice = Lattice([[1, 0, 0], [0, 2, 3], [0, -2, 3]]) coords = [[0.5, 0.8, 0.8], [0.0, 0.3, 0.0], [0.0, 0.0, 0.3]] return IStructure(lattice=lattice, species=["H"] * 3, coords=coords)
def test_supercell_subsets(self): sm = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=False, scale=True, attempt_supercell=True, allow_subset=True, supercell_size='volume') sm_no_s = StructureMatcher(ltol=0.2, stol=0.3, angle_tol=5, primitive_cell=False, scale=True, attempt_supercell=True, allow_subset=False, supercell_size='volume') l = Lattice.orthorhombic(1, 2, 3) s1 = Structure(l, ['Ag', 'Si', 'Si'], [[.7, .4, .5], [0, 0, 0.1], [0, 0, 0.2]]) s1.make_supercell([2, 1, 1]) s2 = Structure(l, ['Si', 'Si', 'Ag'], [[0, 0.1, -0.95], [0, 0.1, 0], [-.7, .5, .375]]) shuffle = [0, 2, 1, 3, 4, 5] s1 = Structure.from_sites([s1[i] for i in shuffle]) #test when s1 is exact supercell of s2 result = sm.get_s2_like_s1(s1, s2) for a, b in zip(s1, result): self.assertTrue(a.distance(b) < 0.08) self.assertEqual(a.species_and_occu, b.species_and_occu) self.assertTrue(sm.fit(s1, s2)) self.assertTrue(sm.fit(s2, s1)) self.assertTrue(sm_no_s.fit(s1, s2)) self.assertTrue(sm_no_s.fit(s2, s1)) rms = (0.048604032430991401, 0.059527539448807391) self.assertTrue(np.allclose(sm.get_rms_dist(s1, s2), rms)) self.assertTrue(np.allclose(sm.get_rms_dist(s2, s1), rms)) #test when the supercell is a subset of s2 subset_supercell = s1.copy() del subset_supercell[0] result = sm.get_s2_like_s1(subset_supercell, s2) self.assertEqual(len(result), 6) for a, b in zip(subset_supercell, result): self.assertTrue(a.distance(b) < 0.08) self.assertEqual(a.species_and_occu, b.species_and_occu) self.assertTrue(sm.fit(subset_supercell, s2)) self.assertTrue(sm.fit(s2, subset_supercell)) self.assertFalse(sm_no_s.fit(subset_supercell, s2)) self.assertFalse(sm_no_s.fit(s2, subset_supercell)) rms = (0.053243049896333279, 0.059527539448807336) self.assertTrue(np.allclose(sm.get_rms_dist(subset_supercell, s2), rms)) self.assertTrue(np.allclose(sm.get_rms_dist(s2, subset_supercell), rms)) #test when s2 (once made a supercell) is a subset of s1 s2_missing_site = s2.copy() del s2_missing_site[1] result = sm.get_s2_like_s1(s1, s2_missing_site) for a, b in zip((s1[i] for i in (0, 2, 4, 5)), result): self.assertTrue(a.distance(b) < 0.08) self.assertEqual(a.species_and_occu, b.species_and_occu) self.assertTrue(sm.fit(s1, s2_missing_site)) self.assertTrue(sm.fit(s2_missing_site, s1)) self.assertFalse(sm_no_s.fit(s1, s2_missing_site)) self.assertFalse(sm_no_s.fit(s2_missing_site, s1)) rms = (0.029763769724403633, 0.029763769724403987) self.assertTrue(np.allclose(sm.get_rms_dist(s1, s2_missing_site), rms)) self.assertTrue(np.allclose(sm.get_rms_dist(s2_missing_site, s1), rms))
def rhombohedral(): lattice = Lattice.rhombohedral(a=1, alpha=45) coords = [[0.0, 0.0, 0.0]] return IStructure(lattice=lattice, species=["H"], coords=coords)
def read_cfgs(self, filename, predict=False): """ Read the configuration file. Args: filename (str): The configuration file to be read. """ type_convert = {'R': np.float32, 'I': np.int, 'S': np.str} data_pool = [] with zopen(filename, 'rt') as f: lines = f.read() repl = re.compile('AT ') lines = repl.sub('', string=lines) block_pattern = re.compile( r'(\n[0-9]+\n|^[0-9]+\n)(.+?)(?=\n[0-9]+\n|$)', re.S) lattice_pattern = re.compile(r'Lattice="(.+)"') # energy_pattern = re.compile('dft_energy=(-?[0-9]+.[0-9]+)', re.I) energy_pattern = re.compile( r'(?<=\S{3}\s|dft_)energy=(-?[0-9]+.[0-9]+)') # stress_pattern = re.compile('dft_virial={(.+)}') stress_pattern = re.compile(r'dft_virial=({|)(.+?)(}|) \S.*') properties_pattern = re.compile(r'properties=(\S+)', re.I) # position_pattern = re.compile('\n(.+)', re.S) position_pattern = re.compile('\n(.+?)(?=\nE.*|\n\n.*|$)', re.S) # formatify = lambda string: [float(s) for s in string.split()] for (size, block) in block_pattern.findall(lines): d = {'outputs': {}} size = int(size) lattice_str = lattice_pattern.findall(block)[0] lattice = Lattice( list(map(lambda s: float(s), lattice_str.split()))) energy_str = energy_pattern.findall(block)[-1] energy = float(energy_str) stress_str = stress_pattern.findall(block)[0][1] virial_stress = np.array( list(map(lambda s: float(s), stress_str.split()))) virial_stress = [virial_stress[i] for i in [0, 4, 8, 1, 5, 6]] properties = properties_pattern.findall(block)[0].split(":") labels_columns = OrderedDict() labels = defaultdict() for i in range(0, len(properties), 3): labels_columns[properties[i]] = [ int(properties[i + 2]), properties[i + 1] ] position_str = position_pattern.findall(block)[0].split('\n') position = np.array([p.split() for p in position_str]) column_index = 0 for key in labels_columns: num_columns, dtype = labels_columns[key] labels[key] = position[:, column_index:column_index + num_columns].astype(type_convert[dtype]) column_index += num_columns struct = Structure(lattice=lattice, species=labels['species'].ravel(), coords=labels['pos'], coords_are_cartesian=True) if predict: forces = labels['force'] else: forces = labels['dft_force'] d['structure'] = struct.as_dict() d['outputs']['energy'] = energy assert size == struct.num_sites d['num_atoms'] = size d['outputs']['forces'] = forces d['outputs']['virial_stress'] = virial_stress data_pool.append(d) _, df = convert_docs(docs=data_pool) return data_pool, df
def get_ph_bs_symm_line_from_dict(bands_dict, has_nac=False, labels_dict=None): """ Creates a pymatgen PhononBandStructure object from the dictionary extracted by the band.yaml file produced by phonopy. The labels will be extracted from the dictionary, if present. If the 'eigenvector' key is found the eigendisplacements will be calculated according to the formula:: exp(2*pi*i*(frac_coords \\dot q) / sqrt(mass) * v and added to the object. Args: bands_dict: the dictionary extracted from the band.yaml file has_nac: True if the data have been obtained with the option --nac option. Default False. labels_dict: dict that links a qpoint in frac coords to a label. Its value will replace the data contained in the band.yaml. """ structure = get_structure_from_dict(bands_dict) qpts = [] frequencies = [] eigendisplacements = [] phonopy_labels_dict = {} for p in bands_dict['phonon']: q = p['q-position'] qpts.append(q) bands = [] eig_q = [] for b in p['band']: bands.append(b['frequency']) if 'eigenvector' in b: eig_b = [] for i, eig_a in enumerate(b['eigenvector']): v = np.zeros(3, np.complex) for x in range(3): v[x] = eig_a[x][0] + eig_a[x][1] * 1j eig_b.append( eigvec_to_eigdispl( v, q, structure[i].frac_coords, structure.site_properties['phonopy_masses'][i])) eig_q.append(eig_b) frequencies.append(bands) if 'label' in p: phonopy_labels_dict[p['label']] = p['q-position'] if eig_q: eigendisplacements.append(eig_q) qpts = np.array(qpts) # transpose to match the convention in PhononBandStructure frequencies = np.transpose(frequencies) if eigendisplacements: eigendisplacements = np.transpose(eigendisplacements, (1, 0, 2, 3)) rec_latt = Lattice(bands_dict['reciprocal_lattice']) labels_dict = labels_dict or phonopy_labels_dict ph_bs = PhononBandStructureSymmLine(qpts, frequencies, rec_latt, has_nac=has_nac, labels_dict=labels_dict, structure=structure, eigendisplacements=eigendisplacements) return ph_bs
def elongated_tetragonal(): lattice = Lattice.tetragonal(a=1, c=3 * sqrt(2)) coords = [[0.0, 0.0, 0.0]] return IStructure(lattice=lattice, species=["H"], coords=coords)
""" Dummy test systems """ from pymatgen.core import Composition, Structure, Lattice, Molecule from ._data_conversion import to_array from ._inspect import get_param_types DUMMY_OBJECTS = { 'str': 'H2O', 'composition': Composition('H2O'), 'structure': Structure(Lattice.cubic(3.167), ['Mo', 'Mo'], [[0, 0, 0], [0.5, 0.5, 0.5]]), 'molecule': Molecule(['C', 'O'], [[0, 0, 0], [1, 0, 0]]) } def get_describer_dummy_obj(instance): """ For a describers, get a dummy object for transform_one. This relies on the type hint. Args: instance (BaseDescriber): describers instance """ obj_type = getattr(instance, "describer_type", None) if obj_type is not None: return DUMMY_OBJECTS[obj_type.lower()] arg_types = get_param_types(instance.transform_one) arg_type = list(arg_types.values())[0]
def simple_cubic_2x1x1(): lattice = Lattice.orthorhombic(2.0, 1.0, 1.0) coords = [[0.0, 0.0, 0.0], [0.5, 0.0, 0.0]] return IStructure(lattice=lattice, species=["H", "H"], coords=coords)
def setUp(self): self.maxDiff = None # trivial example, simple square lattice for testing structure = Structure(Lattice.tetragonal(5.0, 50.0), ["H"], [[0, 0, 0]]) self.square_sg = StructureGraph.with_empty_graph(structure, edge_weight_name="", edge_weight_units="") self.square_sg.add_edge(0, 0, from_jimage=(0, 0, 0), to_jimage=(1, 0, 0)) self.square_sg.add_edge(0, 0, from_jimage=(0, 0, 0), to_jimage=(-1, 0, 0)) self.square_sg.add_edge(0, 0, from_jimage=(0, 0, 0), to_jimage=(0, 1, 0)) self.square_sg.add_edge(0, 0, from_jimage=(0, 0, 0), to_jimage=(0, -1, 0)) # TODO: decorating still fails because the structure graph gives a CN of 8 for this square lattice # self.square_sg.decorate_structure_with_ce_info() # body-centered square lattice for testing structure = Structure(Lattice.tetragonal(5.0, 50.0), ["H", "He"], [[0, 0, 0], [0.5, 0.5, 0.5]]) self.bc_square_sg = StructureGraph.with_empty_graph( structure, edge_weight_name="", edge_weight_units="") self.bc_square_sg.add_edge(0, 0, from_jimage=(0, 0, 0), to_jimage=(1, 0, 0)) self.bc_square_sg.add_edge(0, 0, from_jimage=(0, 0, 0), to_jimage=(-1, 0, 0)) self.bc_square_sg.add_edge(0, 0, from_jimage=(0, 0, 0), to_jimage=(0, 1, 0)) self.bc_square_sg.add_edge(0, 0, from_jimage=(0, 0, 0), to_jimage=(0, -1, 0)) self.bc_square_sg.add_edge(0, 1, from_jimage=(0, 0, 0), to_jimage=(0, 0, 0)) self.bc_square_sg.add_edge(0, 1, from_jimage=(0, 0, 0), to_jimage=(-1, 0, 0)) self.bc_square_sg.add_edge(0, 1, from_jimage=(0, 0, 0), to_jimage=(-1, -1, 0)) self.bc_square_sg.add_edge(0, 1, from_jimage=(0, 0, 0), to_jimage=(0, -1, 0)) # body-centered square lattice for testing # directions reversed, should be equivalent to as bc_square structure = Structure(Lattice.tetragonal(5.0, 50.0), ["H", "He"], [[0, 0, 0], [0.5, 0.5, 0.5]]) self.bc_square_sg_r = StructureGraph.with_empty_graph( structure, edge_weight_name="", edge_weight_units="") self.bc_square_sg_r.add_edge(0, 0, from_jimage=(0, 0, 0), to_jimage=(1, 0, 0)) self.bc_square_sg_r.add_edge(0, 0, from_jimage=(0, 0, 0), to_jimage=(-1, 0, 0)) self.bc_square_sg_r.add_edge(0, 0, from_jimage=(0, 0, 0), to_jimage=(0, 1, 0)) self.bc_square_sg_r.add_edge(0, 0, from_jimage=(0, 0, 0), to_jimage=(0, -1, 0)) self.bc_square_sg_r.add_edge(0, 1, from_jimage=(0, 0, 0), to_jimage=(0, 0, 0)) self.bc_square_sg_r.add_edge(1, 0, from_jimage=(-1, 0, 0), to_jimage=(0, 0, 0)) self.bc_square_sg_r.add_edge(1, 0, from_jimage=(-1, -1, 0), to_jimage=(0, 0, 0)) self.bc_square_sg_r.add_edge(1, 0, from_jimage=(0, -1, 0), to_jimage=(0, 0, 0)) # MoS2 example, structure graph obtained from critic2 # (not ground state, from mp-1023924, single layer) stdout_file = os.path.join( PymatgenTest.TEST_FILES_DIR, "critic2/MoS2_critic2_stdout.txt", ) with open(stdout_file) as f: reference_stdout = f.read() self.structure = Structure.from_file( os.path.join( PymatgenTest.TEST_FILES_DIR, "critic2/MoS2.cif", )) c2o = Critic2Analysis(self.structure, reference_stdout) self.mos2_sg = c2o.structure_graph(include_critical_points=False) latt = Lattice.cubic(4.17) species = ["Ni", "O"] coords = [[0, 0, 0], [0.5, 0.5, 0.5]] self.NiO = Structure.from_spacegroup(225, latt, species, coords).get_primitive_structure() # BCC example. self.bcc = Structure(Lattice.cubic(5.0), ["He", "He"], [[0, 0, 0], [0.5, 0.5, 0.5]]) warnings.simplefilter("ignore")
sph = np.array(np.divmod(np.ravel(pf_grid), pf_grid.shape[1])).T sph = sph * pf.res sph[:, 0] = np.where(sph[:, 0] == 0, 0.004363323, sph[:, 0]) #1/4deg tilt off center to avoid issues #convert to xyz xyz_pf = np.zeros((sph.shape[0], 3)) xyz_pf[:, 0] = np.sin(sph[:, 0]) * np.cos(sph[:, 1]) xyz_pf[:, 1] = np.sin(sph[:, 0]) * np.sin(sph[:, 1]) xyz_pf[:, 2] = np.cos(sph[:, 0]) od = bunge(cellSize, crystalSym, sampleSym) """ refl weights """ a = 4.046 #Angstrom latt = Lattice.cubic(a) structure = Structure(latt, ["Al", "Al", "Al", "Al"], [[0, 0, 0], [0.5, 0.5, 0], [0, 0.5, 0.5], [0.5, 0, 0.5]]) rad = 'CuKa' refl_wgt = calc_XRDreflWeights(structure, hkls, rad='CuKa') refl_wgt = {0: 1.0, 1: 1.0, 2: 1.0} omega = np.radians(np.arange(0, 365, 5)) """ symmetry after """ hkls = normalize(np.array(hkls)) symOps = genSym(crystalSym) symOps = np.unique(np.swapaxes(symOps, 2, 0), axis=0) """ only use proper rotations """
def structure(): test_latt = Lattice.cubic(3.0) test_struc = Structure(lattice=test_latt, species=["Fe"], coords=[[0, 0, 0]]) return test_struc
def find_lattice(lattice_points): return Lattice(find_basis(lattice_points)).get_niggli_reduced_lattice()
def tetra_close_to_cubic(): lattice = Lattice.tetragonal(1.001 * 10 / sqrt(2), 10) coords = [[0.0, 0.0, 0.0]] return IStructure(lattice=lattice, species=["H"], coords=coords)