def test_from_magnetic_spacegroup(self): # AFM MnF s1 = Structure.from_magnetic_spacegroup("P4_2'/mnm'", Lattice.tetragonal(4.87, 3.30), ["Mn", "F"], [[0, 0, 0], [0.30, 0.30, 0.00]], {'magmom': [4, 0]}) self.assertEqual(s1.formula, "Mn2 F4") self.assertEqual(sum(map(float, s1.site_properties['magmom'])), 0) self.assertEqual(max(map(float, s1.site_properties['magmom'])), 4) self.assertEqual(min(map(float, s1.site_properties['magmom'])), -4) # AFM LaMnO3, ordered on (001) planes s2 = Structure.from_magnetic_spacegroup("Pn'ma'", Lattice.orthorhombic(5.75, 7.66, 5.53), ["La", "Mn", "O", "O"], [[0.05, 0.25, 0.99], [0.00, 0.00, 0.50], [0.48, 0.25, 0.08], [0.31, 0.04, 0.72]], {'magmom': [0, Magmom([4, 0, 0]), 0, 0]}) self.assertEqual(s2.formula, "La4 Mn4 O12") self.assertEqual(sum(map(float, s2.site_properties['magmom'])), 0) self.assertEqual(max(map(float, s2.site_properties['magmom'])), 4) self.assertEqual(min(map(float, s2.site_properties['magmom'])), -4)
def test_lattice_2_lmpbox(self): matrix = np.diag(np.random.randint(5, 14, size=(3,))) + np.random.rand(3, 3) * 0.2 - 0.1 init_latt = Lattice(matrix) frac_coords = np.random.rand(10, 3) init_structure = Structure(init_latt, ["H"] * 10, frac_coords) origin = np.random.rand(3) * 10 - 5 box, symmop = lattice_2_lmpbox(lattice=init_latt, origin=origin) boxed_latt = box.to_lattice() np.testing.assert_array_almost_equal(init_latt.abc, boxed_latt.abc) np.testing.assert_array_almost_equal(init_latt.angles, boxed_latt.angles) cart_coords = symmop.operate_multi(init_structure.cart_coords) - origin boxed_structure = Structure(boxed_latt, ["H"] * 10, cart_coords, coords_are_cartesian=True) np.testing.assert_array_almost_equal(boxed_structure.frac_coords, frac_coords) tetra_latt = Lattice.tetragonal(5, 5) tetra_box, _ = lattice_2_lmpbox(tetra_latt) self.assertIsNone(tetra_box.tilt) ortho_latt = Lattice.orthorhombic(5, 5, 5) ortho_box, _ = lattice_2_lmpbox(ortho_latt) self.assertIsNone(ortho_box.tilt) rot_tetra_latt = Lattice([[5, 0, 0], [0, 2, 2], [0, -2, 2]]) _, rotop = lattice_2_lmpbox(rot_tetra_latt) np.testing.assert_array_almost_equal( rotop.rotation_matrix, [ [1, 0, 0], [0, 2 ** 0.5 / 2, 2 ** 0.5 / 2], [0, -(2 ** 0.5) / 2, 2 ** 0.5 / 2], ], )
def test_is_compatible(self): cubic = Lattice.cubic(1) hexagonal = Lattice.hexagonal(1, 2) rhom = Lattice.rhombohedral(3, 80) tet = Lattice.tetragonal(1, 2) ortho = Lattice.orthorhombic(1, 2, 3) msg = MagneticSpaceGroup("Fm-3m") self.assertTrue(msg.is_compatible(cubic)) self.assertFalse(msg.is_compatible(hexagonal)) msg = MagneticSpaceGroup("Pnma") self.assertTrue(msg.is_compatible(cubic)) self.assertTrue(msg.is_compatible(tet)) self.assertTrue(msg.is_compatible(ortho)) self.assertFalse(msg.is_compatible(rhom)) self.assertFalse(msg.is_compatible(hexagonal)) msg = MagneticSpaceGroup("P2/c") self.assertTrue(msg.is_compatible(cubic)) self.assertTrue(msg.is_compatible(tet)) self.assertTrue(msg.is_compatible(ortho)) self.assertFalse(msg.is_compatible(rhom)) self.assertFalse(msg.is_compatible(hexagonal)) msg = MagneticSpaceGroup("P-1") self.assertTrue(msg.is_compatible(cubic)) self.assertTrue(msg.is_compatible(tet)) self.assertTrue(msg.is_compatible(ortho)) self.assertTrue(msg.is_compatible(rhom)) self.assertTrue(msg.is_compatible(hexagonal))
def test_get_xrd_data(self): a = 4.209 latt = Lattice.cubic(a) structure = Structure(latt, ["Cs", "Cl"], [[0, 0, 0], [0.5, 0.5, 0.5]]) c = XRDCalculator() data = c.get_xrd_data(structure, two_theta_range=(0, 90)) #Check the first two peaks self.assertAlmostEqual(data[0][0], 21.107738329639844) self.assertAlmostEqual(data[0][1], 36.483184003748946) self.assertEqual(data[0][2], {(1, 0, 0): 6}) self.assertAlmostEqual(data[0][3], 4.2089999999999996) self.assertAlmostEqual(data[1][0], 30.024695921112777) self.assertAlmostEqual(data[1][1], 100) self.assertEqual(data[1][2], {(1, 1, 0): 12}) self.assertAlmostEqual(data[1][3], 2.976212442014178) s = read_structure(os.path.join(test_dir, "LiFePO4.cif")) data = c.get_xrd_data(s, two_theta_range=(0, 90)) self.assertAlmostEqual(data[1][0], 17.03504233621785) self.assertAlmostEqual(data[1][1], 50.400928948337075) s = read_structure(os.path.join(test_dir, "Li10GeP2S12.cif")) data = c.get_xrd_data(s, two_theta_range=(0, 90)) self.assertAlmostEqual(data[1][0], 14.058274883353876) self.assertAlmostEqual(data[1][1], 4.4111123641667671) # Test a hexagonal structure. s = read_structure(os.path.join(test_dir, "Graphite.cif"), primitive=False) data = c.get_xrd_data(s, two_theta_range=(0, 90)) self.assertAlmostEqual(data[0][0], 7.929279053132362) self.assertAlmostEqual(data[0][1], 100) self.assertAlmostEqual(len(list(data[0][2].keys())[0]), 4) #Add test case with different lengths of coefficients. #Also test d_hkl. coords = [[0.25, 0.25, 0.173], [0.75, 0.75, 0.827], [0.75, 0.25, 0], [0.25, 0.75, 0], [0.25, 0.25, 0.676], [0.75, 0.75, 0.324]] sp = ["Si", "Si", "Ru", "Ru", "Pr", "Pr"] s = Structure(Lattice.tetragonal(4.192, 6.88), sp, coords) data = c.get_xrd_data(s) self.assertAlmostEqual(data[0][0], 12.86727341476735) self.assertAlmostEqual(data[0][1], 31.448239816769796) self.assertAlmostEqual(data[0][3], 6.88) self.assertEqual(len(data), 42) data = c.get_xrd_data(s, two_theta_range=[0, 60]) self.assertEqual(len(data), 18) #Test with and without Debye-Waller factor tungsten = Structure(Lattice.cubic(3.1653), ["W"] * 2, [[0, 0, 0], [0.5, 0.5, 0.5]]) data = c.get_xrd_data(tungsten, scaled=False) self.assertAlmostEqual(data[0][0], 40.294828554672264) self.assertAlmostEqual(data[0][1], 2414237.5633093244) self.assertAlmostEqual(data[0][3], 2.2382050944897789) c = XRDCalculator(debye_waller_factors={"W": 0.1526}) data = c.get_xrd_data(tungsten, scaled=False) self.assertAlmostEqual(data[0][0], 40.294828554672264) self.assertAlmostEqual(data[0][1], 2377745.2296686019) self.assertAlmostEqual(data[0][3], 2.2382050944897789)
def test_primitive_structure_volume_check(self): l = Lattice.tetragonal(10, 30) coords = [[0.5, 0.8, 0], [0.5, 0.2, 0], [0.5, 0.8, 0.333], [0.5, 0.5, 0.333], [0.5, 0.5, 0.666], [0.5, 0.2, 0.666]] s = IStructure(l, ["Ag"] * 6, coords) sprim = s.get_primitive_structure(tolerance=0.1) self.assertEqual(len(sprim), 6)
def test_from_spacegroup(self): s1 = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3), ["Li", "O"], [[0.25, 0.25, 0.25], [0, 0, 0]]) self.assertEqual(s1.formula, "Li8 O4") s2 = Structure.from_spacegroup(225, Lattice.cubic(3), ["Li", "O"], [[0.25, 0.25, 0.25], [0, 0, 0]]) self.assertEqual(s1, s2) s2 = Structure.from_spacegroup(225, Lattice.cubic(3), ["Li", "O"], [[0.25, 0.25, 0.25], [0, 0, 0]], site_properties={"charge": [1, -2]}) self.assertEqual(sum(s2.site_properties["charge"]), 0) s = Structure.from_spacegroup("Pm-3m", Lattice.cubic(3), ["Cs", "Cl"], [[0, 0, 0], [0.5, 0.5, 0.5]]) self.assertEqual(s.formula, "Cs1 Cl1") self.assertRaises(ValueError, Structure.from_spacegroup, "Pm-3m", Lattice.tetragonal(1, 3), ["Cs", "Cl"], [[0, 0, 0], [0.5, 0.5, 0.5]]) self.assertRaises(ValueError, Structure.from_spacegroup, "Pm-3m", Lattice.cubic(3), ["Cs"], [[0, 0, 0], [0.5, 0.5, 0.5]])
def test_get_pattern(self): s = self.get_structure("CsCl") c = XRDCalculator() xrd = c.get_pattern(s, two_theta_range=(0, 90)) self.assertTrue(xrd.to_json()) # Test MSONAble property # Check the first two peaks self.assertAlmostEqual(xrd.x[0], 21.107738329639844) self.assertAlmostEqual(xrd.y[0], 36.483184003748946) self.assertEqual(xrd.hkls[0], [{'hkl': (1, 0, 0), 'multiplicity': 6}]) self.assertAlmostEqual(xrd.d_hkls[0], 4.2089999999999996) self.assertAlmostEqual(xrd.x[1], 30.024695921112777) self.assertAlmostEqual(xrd.y[1], 100) self.assertEqual(xrd.hkls[1], [{"hkl": (1, 1, 0), "multiplicity": 12}]) self.assertAlmostEqual(xrd.d_hkls[1], 2.976212442014178) s = self.get_structure("LiFePO4") xrd = c.get_pattern(s, two_theta_range=(0, 90)) self.assertAlmostEqual(xrd.x[1], 17.03504233621785) self.assertAlmostEqual(xrd.y[1], 50.400928948337075) s = self.get_structure("Li10GeP2S12") xrd = c.get_pattern(s, two_theta_range=(0, 90)) self.assertAlmostEqual(xrd.x[1], 14.058274883353876) self.assertAlmostEqual(xrd.y[1], 4.4111123641667671) # Test a hexagonal structure. s = self.get_structure("Graphite") xrd = c.get_pattern(s, two_theta_range=(0, 90)) self.assertAlmostEqual(xrd.x[0], 26.21057350859598) self.assertAlmostEqual(xrd.y[0], 100) self.assertAlmostEqual(len(xrd.hkls[0][0]["hkl"]), 4) # Add test case with different lengths of coefficients. # Also test d_hkl. coords = [[0.25, 0.25, 0.173], [0.75, 0.75, 0.827], [0.75, 0.25, 0], [0.25, 0.75, 0], [0.25, 0.25, 0.676], [0.75, 0.75, 0.324]] sp = ["Si", "Si", "Ru", "Ru", "Pr", "Pr"] s = Structure(Lattice.tetragonal(4.192, 6.88), sp, coords) xrd = c.get_pattern(s) self.assertAlmostEqual(xrd.x[0], 12.86727341476735) self.assertAlmostEqual(xrd.y[0], 31.448239816769796) self.assertAlmostEqual(xrd.d_hkls[0], 6.88) self.assertEqual(len(xrd), 42) xrd = c.get_pattern(s, two_theta_range=[0, 60]) self.assertEqual(len(xrd), 18) # Test with and without Debye-Waller factor tungsten = Structure(Lattice.cubic(3.1653), ["W"] * 2, [[0, 0, 0], [0.5, 0.5, 0.5]]) xrd = c.get_pattern(tungsten, scaled=False) self.assertAlmostEqual(xrd.x[0], 40.294828554672264) self.assertAlmostEqual(xrd.y[0], 2414237.5633093244) self.assertAlmostEqual(xrd.d_hkls[0], 2.2382050944897789) c = XRDCalculator(debye_waller_factors={"W": 0.1526}) xrd = c.get_pattern(tungsten, scaled=False) self.assertAlmostEqual(xrd.x[0], 40.294828554672264) self.assertAlmostEqual(xrd.y[0], 2377745.2296686019) self.assertAlmostEqual(xrd.d_hkls[0], 2.2382050944897789) c.get_plot(tungsten).show()
def test_kpath_generation(self): triclinic = [1, 2] monoclinic = range(3, 16) orthorhombic = range(16, 75) tetragonal = range(75, 143) rhombohedral = range(143, 168) hexagonal = range(168, 195) cubic = range(195, 231) species = ['K', 'La', 'Ti'] coords = [[.345, 5, .77298], [.1345, 5.1, .77298], [.7, .8, .9]] for i in range(230): sg_num = i + 1 if sg_num in triclinic: lattice = Lattice([[3.0233057319441246, 0, 0], [0, 7.9850357844548681, 0], [0, 0, 8.1136762279561818]]) elif sg_num in monoclinic: lattice = Lattice.monoclinic(2, 9, 1, 99) elif sg_num in orthorhombic: lattice = Lattice.orthorhombic(2, 9, 1) elif sg_num in tetragonal: lattice = Lattice.tetragonal(2, 9) elif sg_num in rhombohedral: lattice = Lattice.hexagonal(2, 95) elif sg_num in hexagonal: lattice = Lattice.hexagonal(2, 9) elif sg_num in cubic: lattice = Lattice.cubic(2) struct = Structure.from_spacegroup(sg_num, lattice, species, coords) kpath = HighSymmKpath(struct) # Throws error if something doesn't work, causing test to fail. struct_file_path = os.path.join(test_dir_structs, 'ICSD_170.cif') struct = Structure.from_file(struct_file_path) hkp = HighSymmKpath(struct) self.assertEqual(hkp.name, 'MCLC5')
def test_selling_dist(self): # verification process described here: https://github.com/materialsproject/pymatgen/pull/1888#issuecomment-818072164 np.testing.assert_(Lattice.selling_dist(Lattice.cubic(5), Lattice.cubic(5)) == 0) hex_lattice = Lattice.hexagonal(5, 8) triclinic_lattice = Lattice.from_parameters(4, 10, 11, 100, 110, 80) np.testing.assert_allclose(Lattice.selling_dist(hex_lattice, triclinic_lattice), 76, rtol=0.1) np.testing.assert_allclose( Lattice.selling_dist(Lattice.tetragonal(10, 12), Lattice.tetragonal(10.1, 11.9)), 3.7, rtol=0.1, ) np.testing.assert_allclose( Lattice.selling_dist(Lattice.cubic(5), Lattice.from_parameters(8, 10, 12, 80, 90, 95)), 115.6, rtol=0.1, )
def test_kpath_generation(self): triclinic = [1, 2] monoclinic = range(3, 16) orthorhombic = range(16, 75) tetragonal = range(75, 143) rhombohedral = range(143, 168) hexagonal = range(168, 195) cubic = range(195, 231) species = ['K', 'La', 'Ti'] coords = [[.345, 5, .77298], [.1345, 5.1, .77298], [.7, .8, .9]] for i in range(230): sg_num = i + 1 if sg_num in triclinic: lattice = Lattice([[3.0233057319441246,0,0], [0,7.9850357844548681,0], [0,0,8.1136762279561818]]) elif sg_num in monoclinic: lattice = Lattice.monoclinic(2, 9, 1, 99) elif sg_num in orthorhombic: lattice = Lattice.orthorhombic(2, 9, 1) elif sg_num in tetragonal: lattice = Lattice.tetragonal(2, 9) elif sg_num in rhombohedral: lattice = Lattice.hexagonal(2, 95) elif sg_num in hexagonal: lattice = Lattice.hexagonal(2, 9) elif sg_num in cubic: lattice = Lattice.cubic(2) struct = Structure.from_spacegroup(sg_num, lattice, species, coords) kpath = HighSymmKpath(struct) #Throws error if something doesn't work, causing test to fail.
def test_is_compatible(self): cubic = Lattice.cubic(1) hexagonal = Lattice.hexagonal(1, 2) rhom = Lattice.rhombohedral(3, 80) tet = Lattice.tetragonal(1, 2) ortho = Lattice.orthorhombic(1, 2, 3) sg = SpaceGroup("Fm-3m") self.assertTrue(sg.is_compatible(cubic)) self.assertFalse(sg.is_compatible(hexagonal)) sg = SpaceGroup("R-3mH") self.assertFalse(sg.is_compatible(cubic)) self.assertTrue(sg.is_compatible(hexagonal)) sg = SpaceGroup("R-3m") self.assertTrue(sg.is_compatible(cubic)) self.assertTrue(sg.is_compatible(rhom)) self.assertFalse(sg.is_compatible(hexagonal)) sg = SpaceGroup("Pnma") self.assertTrue(sg.is_compatible(cubic)) self.assertTrue(sg.is_compatible(tet)) self.assertTrue(sg.is_compatible(ortho)) self.assertFalse(sg.is_compatible(rhom)) self.assertFalse(sg.is_compatible(hexagonal)) sg = SpaceGroup("P12/c1") self.assertTrue(sg.is_compatible(cubic)) self.assertTrue(sg.is_compatible(tet)) self.assertTrue(sg.is_compatible(ortho)) self.assertFalse(sg.is_compatible(rhom)) self.assertFalse(sg.is_compatible(hexagonal)) sg = SpaceGroup("P-1") self.assertTrue(sg.is_compatible(cubic)) self.assertTrue(sg.is_compatible(tet)) self.assertTrue(sg.is_compatible(ortho)) self.assertTrue(sg.is_compatible(rhom)) self.assertTrue(sg.is_compatible(hexagonal))
def test_kpath_generation(self): triclinic = [1, 2] monoclinic = range(3, 16) orthorhombic = range(16, 75) tetragonal = range(75, 143) rhombohedral = range(143, 168) hexagonal = range(168, 195) cubic = range(195, 231) species = ["K", "La", "Ti"] coords = [[0.345, 5, 0.77298], [0.1345, 5.1, 0.77298], [0.7, 0.8, 0.9]] for i in range(230): sg_num = i + 1 if sg_num in triclinic: lattice = Lattice( [[3.0233057319441246, 1, 0], [0, 7.9850357844548681, 1], [0, 1.2, 8.1136762279561818]] ) elif sg_num in monoclinic: lattice = Lattice.monoclinic(2, 9, 1, 99) elif sg_num in orthorhombic: lattice = Lattice.orthorhombic(2, 9, 1) elif sg_num in tetragonal: lattice = Lattice.tetragonal(2, 9) elif sg_num in rhombohedral: lattice = Lattice.hexagonal(2, 95) elif sg_num in hexagonal: lattice = Lattice.hexagonal(2, 9) elif sg_num in cubic: lattice = Lattice.cubic(2) struct = Structure.from_spacegroup(sg_num, lattice, species, coords) kpath = KPathSeek(struct) # Throws error if something doesn't work, causing test to fail.
def test_sulfide_type(self): # NaS2 -> polysulfide latt = Lattice.tetragonal(9.59650, 11.78850) species = ["Na"] * 2 + ["S"] * 2 coords = [ [0.00000, 0.00000, 0.17000], [0.27600, 0.25000, 0.12500], [0.03400, 0.25000, 0.29600], [0.14700, 0.11600, 0.40000], ] struct = Structure.from_spacegroup(122, latt, species, coords) self.assertEqual(sulfide_type(struct), "polysulfide") # NaCl type NaS -> sulfide latt = Lattice.cubic(5.75) species = ["Na", "S"] coords = [[0.00000, 0.00000, 0.00000], [0.50000, 0.50000, 0.50000]] struct = Structure.from_spacegroup(225, latt, species, coords) self.assertEqual(sulfide_type(struct), "sulfide") # Na2S2O3 -> None (sulfate) latt = Lattice.monoclinic(6.40100, 8.10000, 8.47400, 96.8800) species = ["Na"] * 2 + ["S"] * 2 + ["O"] * 3 coords = [ [0.29706, 0.62396, 0.08575], [0.37673, 0.30411, 0.45416], [0.52324, 0.10651, 0.21126], [0.29660, -0.04671, 0.26607], [0.17577, 0.03720, 0.38049], [0.38604, -0.20144, 0.33624], [0.16248, -0.08546, 0.11608], ] struct = Structure.from_spacegroup(14, latt, species, coords) self.assertEqual(sulfide_type(struct), None) # Na3PS3O -> sulfide latt = Lattice.orthorhombic(9.51050, 11.54630, 5.93230) species = ["Na"] * 2 + ["S"] * 2 + ["P", "O"] coords = [ [0.19920, 0.11580, 0.24950], [0.00000, 0.36840, 0.29380], [0.32210, 0.36730, 0.22530], [0.50000, 0.11910, 0.27210], [0.50000, 0.29400, 0.35500], [0.50000, 0.30300, 0.61140], ] struct = Structure.from_spacegroup(36, latt, species, coords) self.assertEqual(sulfide_type(struct), "sulfide") # test for unphysical cells struct.scale_lattice(struct.volume * 10) self.assertEqual(sulfide_type(struct), "sulfide")
def setUp(self): self.lattice = Lattice.cubic(10.0) self.cubic = self.lattice self.tetragonal = Lattice.tetragonal(10, 20) self.orthorhombic = Lattice.orthorhombic(10, 20, 30) self.monoclinic = Lattice.monoclinic(10, 20, 30, 66) self.hexagonal = Lattice.hexagonal(10, 20) self.rhombohedral = Lattice.rhombohedral(10, 77) family_names = ["cubic", "tetragonal", "orthorhombic", "monoclinic", "hexagonal", "rhombohedral"] self.families = {} for name in family_names: self.families[name] = getattr(self, name)
def test_selling_vector(self): a1 = 10 np.testing.assert_array_almost_equal( Lattice.cubic(a1).selling_vector.round(4), np.array([0, 0, 0, -(a1**2), -(a1**2), -(a1**2)]), ) a2, c2 = 5, 8 np.testing.assert_array_almost_equal( Lattice.tetragonal(a2, c2).selling_vector.round(4), np.array([0, 0, 0, -(a2**2), -(a2**2), -(c2**2)]), ) a3, b3, c3 = 4, 6, 7 np.testing.assert_array_almost_equal( Lattice.orthorhombic(a3, b3, c3).selling_vector.round(4), np.array([0, 0, 0, -(a3**2), -(b3**2), -(c3**2)]), )
def test_is_compatible(self): cubic = Lattice.cubic(1) hexagonal = Lattice.hexagonal(1, 2) rhom = Lattice.rhombohedral(3, 80) tet = Lattice.tetragonal(1, 2) ortho = Lattice.orthorhombic(1, 2, 3) sg = SpaceGroup("Fm-3m") self.assertTrue(sg.is_compatible(cubic)) self.assertFalse(sg.is_compatible(hexagonal)) sg = SpaceGroup("R-3m:H") self.assertFalse(sg.is_compatible(cubic)) self.assertTrue(sg.is_compatible(hexagonal)) sg = SpaceGroup("R-3m:R") self.assertTrue(sg.is_compatible(cubic)) self.assertTrue(sg.is_compatible(rhom)) self.assertFalse(sg.is_compatible(hexagonal)) sg = SpaceGroup("Pnma") self.assertTrue(sg.is_compatible(cubic)) self.assertTrue(sg.is_compatible(tet)) self.assertTrue(sg.is_compatible(ortho)) self.assertFalse(sg.is_compatible(rhom)) self.assertFalse(sg.is_compatible(hexagonal)) sg = SpaceGroup("P12/c1") self.assertTrue(sg.is_compatible(cubic)) self.assertTrue(sg.is_compatible(tet)) self.assertTrue(sg.is_compatible(ortho)) self.assertFalse(sg.is_compatible(rhom)) self.assertFalse(sg.is_compatible(hexagonal)) sg = SpaceGroup("P-1") self.assertTrue(sg.is_compatible(cubic)) self.assertTrue(sg.is_compatible(tet)) self.assertTrue(sg.is_compatible(ortho)) self.assertTrue(sg.is_compatible(rhom)) self.assertTrue(sg.is_compatible(hexagonal)) sg = SpaceGroup("Pmmn:2") self.assertTrue(sg.is_compatible(cubic)) self.assertTrue(sg.is_compatible(tet)) self.assertTrue(sg.is_compatible(ortho)) self.assertFalse(sg.is_compatible(rhom)) self.assertFalse(sg.is_compatible(hexagonal)) sg = SpaceGroup.from_int_number(165) self.assertFalse(sg.is_compatible(cubic)) self.assertFalse(sg.is_compatible(tet)) self.assertFalse(sg.is_compatible(ortho)) self.assertFalse(sg.is_compatible(rhom)) self.assertTrue(sg.is_compatible(hexagonal))
def test_from_spacegroup(self): s1 = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3), ["Li", "O"], [[0.25, 0.25, 0.25], [0, 0, 0]]) self.assertEqual(s1.formula, "Li8 O4") s2 = Structure.from_spacegroup(225, Lattice.cubic(3), ["Li", "O"], [[0.25, 0.25, 0.25], [0, 0, 0]]) self.assertEqual(s1, s2) s2 = Structure.from_spacegroup(225, Lattice.cubic(3), ["Li", "O"], [[0.25, 0.25, 0.25], [0, 0, 0]], site_properties={"charge": [1, -2]}) self.assertEqual(sum(s2.site_properties["charge"]), 0) s = Structure.from_spacegroup("Pm-3m", Lattice.cubic(3), ["Cs", "Cl"], [[0, 0, 0], [0.5, 0.5, 0.5]]) self.assertEqual(s.formula, "Cs1 Cl1") self.assertRaises(ValueError, Structure.from_spacegroup, "Pm-3m", Lattice.tetragonal(1, 3), ["Cs", "Cl"], [[0, 0, 0], [0.5, 0.5, 0.5]])
def test_from_magnetic_spacegroup(self): # AFM MnF s1 = Structure.from_magnetic_spacegroup( "P4_2'/mnm'", Lattice.tetragonal(4.87, 3.30), ["Mn", "F"], [[0, 0, 0], [0.30, 0.30, 0.00]], {'magmom': [4, 0]}) self.assertEqual(s1.formula, "Mn2 F4") self.assertEqual(sum(map(float, s1.site_properties['magmom'])), 0) self.assertEqual(max(map(float, s1.site_properties['magmom'])), 4) self.assertEqual(min(map(float, s1.site_properties['magmom'])), -4) # AFM LaMnO3, ordered on (001) planes s2 = Structure.from_magnetic_spacegroup( "Pn'ma'", Lattice.orthorhombic(5.75, 7.66, 5.53), ["La", "Mn", "O", "O"], [[0.05, 0.25, 0.99], [0.00, 0.00, 0.50], [0.48, 0.25, 0.08], [0.31, 0.04, 0.72]], {'magmom': [0, Magmom([4, 0, 0]), 0, 0]}) self.assertEqual(s2.formula, "La4 Mn4 O12") self.assertEqual(sum(map(float, s2.site_properties['magmom'])), 0) self.assertEqual(max(map(float, s2.site_properties['magmom'])), 4) self.assertEqual(min(map(float, s2.site_properties['magmom'])), -4)
def get_conventional_standard_structure( self, international_monoclinic=True): """ Gives a structure with a conventional cell according to certain standards. The standards are defined in Setyawan, W., & Curtarolo, S. (2010). High-throughput electronic band structure calculations: Challenges and tools. Computational Materials Science, 49(2), 299-312. doi:10.1016/j.commatsci.2010.05.010 They basically enforce as much as possible norm(a1)<norm(a2)<norm(a3) Returns: The structure in a conventional standardized cell """ tol = 1e-5 struct = self.get_refined_structure() latt = struct.lattice latt_type = self.get_lattice_type() sorted_lengths = sorted(latt.abc) sorted_dic = sorted([{'vec': latt.matrix[i], 'length': latt.abc[i], 'orig_index': i} for i in [0, 1, 2]], key=lambda k: k['length']) if latt_type in ("orthorhombic", "cubic"): #you want to keep the c axis where it is #to keep the C- settings transf = np.zeros(shape=(3, 3)) if self.get_spacegroup_symbol().startswith("C"): transf[2] = [0, 0, 1] a, b = sorted(latt.abc[:2]) sorted_dic = sorted([{'vec': latt.matrix[i], 'length': latt.abc[i], 'orig_index': i} for i in [0, 1]], key=lambda k: k['length']) for i in range(2): transf[i][sorted_dic[i]['orig_index']] = 1 c = latt.abc[2] else: for i in range(len(sorted_dic)): transf[i][sorted_dic[i]['orig_index']] = 1 a, b, c = sorted_lengths latt = Lattice.orthorhombic(a, b, c) elif latt_type == "tetragonal": #find the "a" vectors #it is basically the vector repeated two times transf = np.zeros(shape=(3, 3)) a, b, c = sorted_lengths for d in range(len(sorted_dic)): transf[d][sorted_dic[d]['orig_index']] = 1 if abs(b - c) < tol: a, c = c, a transf = np.dot([[0, 0, 1], [0, 1, 0], [1, 0, 0]], transf) latt = Lattice.tetragonal(a, c) elif latt_type in ("hexagonal", "rhombohedral"): #for the conventional cell representation, #we allways show the rhombohedral lattices as hexagonal #check first if we have the refined structure shows a rhombohedral #cell #if so, make a supercell a, b, c = latt.abc if np.all(np.abs([a - b, c - b, a - c]) < 0.001): struct.make_supercell(((1, -1, 0), (0, 1, -1), (1, 1, 1))) a, b, c = sorted(struct.lattice.abc) if abs(b - c) < 0.001: a, c = c, a new_matrix = [[a / 2, -a * math.sqrt(3) / 2, 0], [a / 2, a * math.sqrt(3) / 2, 0], [0, 0, c]] latt = Lattice(new_matrix) transf = np.eye(3, 3) elif latt_type == "monoclinic": #you want to keep the c axis where it is #to keep the C- settings if self.get_spacegroup().int_symbol.startswith("C"): transf = np.zeros(shape=(3, 3)) transf[2] = [0, 0, 1] sorted_dic = sorted([{'vec': latt.matrix[i], 'length': latt.abc[i], 'orig_index': i} for i in [0, 1]], key=lambda k: k['length']) a = sorted_dic[0]['length'] b = sorted_dic[1]['length'] c = latt.abc[2] new_matrix = None for t in itertools.permutations(list(range(2)), 2): m = latt.matrix landang = Lattice( [m[t[0]], m[t[1]], m[2]]).lengths_and_angles if landang[1][0] > 90: #if the angle is > 90 we invert a and b to get #an angle < 90 landang = Lattice( [-m[t[0]], -m[t[1]], m[2]]).lengths_and_angles transf = np.zeros(shape=(3, 3)) transf[0][t[0]] = -1 transf[1][t[1]] = -1 transf[2][2] = 1 a, b, c = landang[0] alpha = math.pi * landang[1][0] / 180 new_matrix = [[a, 0, 0], [0, b, 0], [0, c * cos(alpha), c * sin(alpha)]] continue elif landang[1][0] < 90: transf = np.zeros(shape=(3, 3)) transf[0][t[0]] = 1 transf[1][t[1]] = 1 transf[2][2] = 1 a, b, c = landang[0] alpha = math.pi * landang[1][0] / 180 new_matrix = [[a, 0, 0], [0, b, 0], [0, c * cos(alpha), c * sin(alpha)]] if new_matrix is None: #this if is to treat the case #where alpha==90 (but we still have a monoclinic sg new_matrix = [[a, 0, 0], [0, b, 0], [0, 0, c]] transf = np.zeros(shape=(3, 3)) for c in range(len(sorted_dic)): transf[c][sorted_dic[c]['orig_index']] = 1 #if not C-setting else: #try all permutations of the axis #keep the ones with the non-90 angle=alpha #and b<c new_matrix = None for t in itertools.permutations(list(range(3)), 3): m = latt.matrix landang = Lattice( [m[t[0]], m[t[1]], m[t[2]]]).lengths_and_angles if landang[1][0] > 90 and landang[0][1] < landang[0][2]: landang = Lattice( [-m[t[0]], -m[t[1]], m[t[2]]]).lengths_and_angles transf = np.zeros(shape=(3, 3)) transf[0][t[0]] = -1 transf[1][t[1]] = -1 transf[2][t[2]] = 1 a, b, c = landang[0] alpha = math.pi * landang[1][0] / 180 new_matrix = [[a, 0, 0], [0, b, 0], [0, c * cos(alpha), c * sin(alpha)]] continue elif landang[1][0] < 90 and landang[0][1] < landang[0][2]: transf = np.zeros(shape=(3, 3)) transf[0][t[0]] = 1 transf[1][t[1]] = 1 transf[2][t[2]] = 1 a, b, c = landang[0] alpha = math.pi * landang[1][0] / 180 new_matrix = [[a, 0, 0], [0, b, 0], [0, c * cos(alpha), c * sin(alpha)]] if new_matrix is None: #this if is to treat the case #where alpha==90 (but we still have a monoclinic sg new_matrix = [[sorted_lengths[0], 0, 0], [0, sorted_lengths[1], 0], [0, 0, sorted_lengths[2]]] transf = np.zeros(shape=(3, 3)) for c in range(len(sorted_dic)): transf[c][sorted_dic[c]['orig_index']] = 1 if international_monoclinic: # The above code makes alpha the non-right angle. # The following will convert to proper international convention # that beta is the non-right angle. op = [[0, 1, 0], [1, 0, 0], [0, 0, -1]] transf = np.dot(op, transf) new_matrix = np.dot(op, new_matrix) beta = Lattice(new_matrix).beta if beta < 90: op = [[-1, 0, 0], [0, -1, 0], [0, 0, 1]] transf = np.dot(op, transf) new_matrix = np.dot(op, new_matrix) latt = Lattice(new_matrix) elif latt_type == "triclinic": #we use a LLL Minkowski-like reduction for the triclinic cells struct = struct.get_reduced_structure("LLL") a, b, c = latt.lengths_and_angles[0] alpha, beta, gamma = [math.pi * i / 180 for i in latt.lengths_and_angles[1]] new_matrix = None test_matrix = [[a, 0, 0], [b * cos(gamma), b * sin(gamma), 0.0], [c * cos(beta), c * (cos(alpha) - cos(beta) * cos(gamma)) / sin(gamma), c * math.sqrt(sin(gamma) ** 2 - cos(alpha) ** 2 - cos(beta) ** 2 + 2 * cos(alpha) * cos(beta) * cos(gamma)) / sin(gamma)]] def is_all_acute_or_obtuse(m): recp_angles = np.array(Lattice(m).reciprocal_lattice.angles) return np.all(recp_angles <= 90) or np.all(recp_angles > 90) if is_all_acute_or_obtuse(test_matrix): transf = np.eye(3) new_matrix = test_matrix test_matrix = [[-a, 0, 0], [b * cos(gamma), b * sin(gamma), 0.0], [-c * cos(beta), -c * (cos(alpha) - cos(beta) * cos(gamma)) / sin(gamma), -c * math.sqrt(sin(gamma) ** 2 - cos(alpha) ** 2 - cos(beta) ** 2 + 2 * cos(alpha) * cos(beta) * cos(gamma)) / sin(gamma)]] if is_all_acute_or_obtuse(test_matrix): transf = [[-1, 0, 0], [0, 1, 0], [0, 0, -1]] new_matrix = test_matrix test_matrix = [[-a, 0, 0], [-b * cos(gamma), -b * sin(gamma), 0.0], [c * cos(beta), c * (cos(alpha) - cos(beta) * cos(gamma)) / sin(gamma), c * math.sqrt(sin(gamma) ** 2 - cos(alpha) ** 2 - cos(beta) ** 2 + 2 * cos(alpha) * cos(beta) * cos(gamma)) / sin(gamma)]] if is_all_acute_or_obtuse(test_matrix): transf = [[-1, 0, 0], [0, -1, 0], [0, 0, 1]] new_matrix = test_matrix test_matrix = [[a, 0, 0], [-b * cos(gamma), -b * sin(gamma), 0.0], [-c * cos(beta), -c * (cos(alpha) - cos(beta) * cos(gamma)) / sin(gamma), -c * math.sqrt(sin(gamma) ** 2 - cos(alpha) ** 2 - cos(beta) ** 2 + 2 * cos(alpha) * cos(beta) * cos(gamma)) / sin(gamma)]] if is_all_acute_or_obtuse(test_matrix): transf = [[1, 0, 0], [0, -1, 0], [0, 0, -1]] new_matrix = test_matrix latt = Lattice(new_matrix) new_coords = np.dot(transf, np.transpose(struct.frac_coords)).T new_struct = Structure(latt, struct.species_and_occu, new_coords, site_properties=struct.site_properties, to_unit_cell=True) return new_struct.get_sorted_structure()
def setUp(self): self.lattice = Lattice.cubic(10.0) self.tetragonal = Lattice.tetragonal(10, 20)
def get_conventional_standard_structure(self, international_monoclinic=True): """ Gives a structure with a conventional cell according to certain standards. The standards are defined in Setyawan, W., & Curtarolo, S. (2010). High-throughput electronic band structure calculations: Challenges and tools. Computational Materials Science, 49(2), 299-312. doi:10.1016/j.commatsci.2010.05.010 They basically enforce as much as possible norm(a1)<norm(a2)<norm(a3) Returns: The structure in a conventional standardized cell """ tol = 1e-5 struct = self.get_refined_structure() latt = struct.lattice latt_type = self.get_lattice_type() sorted_lengths = sorted(latt.abc) sorted_dic = sorted([{ 'vec': latt.matrix[i], 'length': latt.abc[i], 'orig_index': i } for i in [0, 1, 2]], key=lambda k: k['length']) if latt_type in ("orthorhombic", "cubic"): #you want to keep the c axis where it is #to keep the C- settings transf = np.zeros(shape=(3, 3)) if self.get_spacegroup_symbol().startswith("C"): transf[2] = [0, 0, 1] a, b = sorted(latt.abc[:2]) sorted_dic = sorted([{ 'vec': latt.matrix[i], 'length': latt.abc[i], 'orig_index': i } for i in [0, 1]], key=lambda k: k['length']) for i in range(2): transf[i][sorted_dic[i]['orig_index']] = 1 c = latt.abc[2] else: for i in range(len(sorted_dic)): transf[i][sorted_dic[i]['orig_index']] = 1 a, b, c = sorted_lengths latt = Lattice.orthorhombic(a, b, c) elif latt_type == "tetragonal": #find the "a" vectors #it is basically the vector repeated two times transf = np.zeros(shape=(3, 3)) a, b, c = sorted_lengths for d in range(len(sorted_dic)): transf[d][sorted_dic[d]['orig_index']] = 1 if abs(b - c) < tol: a, c = c, a transf = np.dot([[0, 0, 1], [0, 1, 0], [1, 0, 0]], transf) latt = Lattice.tetragonal(a, c) elif latt_type in ("hexagonal", "rhombohedral"): #for the conventional cell representation, #we allways show the rhombohedral lattices as hexagonal #check first if we have the refined structure shows a rhombohedral #cell #if so, make a supercell a, b, c = latt.abc if np.all(np.abs([a - b, c - b, a - c]) < 0.001): struct.make_supercell(((1, -1, 0), (0, 1, -1), (1, 1, 1))) a, b, c = sorted(struct.lattice.abc) if abs(b - c) < 0.001: a, c = c, a new_matrix = [[a / 2, -a * math.sqrt(3) / 2, 0], [a / 2, a * math.sqrt(3) / 2, 0], [0, 0, c]] latt = Lattice(new_matrix) transf = np.eye(3, 3) elif latt_type == "monoclinic": #you want to keep the c axis where it is #to keep the C- settings if self.get_spacegroup().int_symbol.startswith("C"): transf = np.zeros(shape=(3, 3)) transf[2] = [0, 0, 1] sorted_dic = sorted([{ 'vec': latt.matrix[i], 'length': latt.abc[i], 'orig_index': i } for i in [0, 1]], key=lambda k: k['length']) a = sorted_dic[0]['length'] b = sorted_dic[1]['length'] c = latt.abc[2] new_matrix = None for t in itertools.permutations(list(range(2)), 2): m = latt.matrix landang = Lattice([m[t[0]], m[t[1]], m[2]]).lengths_and_angles if landang[1][0] > 90: #if the angle is > 90 we invert a and b to get #an angle < 90 landang = Lattice([-m[t[0]], -m[t[1]], m[2]]).lengths_and_angles transf = np.zeros(shape=(3, 3)) transf[0][t[0]] = -1 transf[1][t[1]] = -1 transf[2][2] = 1 a, b, c = landang[0] alpha = math.pi * landang[1][0] / 180 new_matrix = [[a, 0, 0], [0, b, 0], [0, c * cos(alpha), c * sin(alpha)]] continue elif landang[1][0] < 90: transf = np.zeros(shape=(3, 3)) transf[0][t[0]] = 1 transf[1][t[1]] = 1 transf[2][2] = 1 a, b, c = landang[0] alpha = math.pi * landang[1][0] / 180 new_matrix = [[a, 0, 0], [0, b, 0], [0, c * cos(alpha), c * sin(alpha)]] if new_matrix is None: #this if is to treat the case #where alpha==90 (but we still have a monoclinic sg new_matrix = [[a, 0, 0], [0, b, 0], [0, 0, c]] transf = np.zeros(shape=(3, 3)) for c in range(len(sorted_dic)): transf[c][sorted_dic[c]['orig_index']] = 1 #if not C-setting else: #try all permutations of the axis #keep the ones with the non-90 angle=alpha #and b<c new_matrix = None for t in itertools.permutations(list(range(3)), 3): m = latt.matrix landang = Lattice([m[t[0]], m[t[1]], m[t[2]]]).lengths_and_angles if landang[1][0] > 90 and landang[0][1] < landang[0][2]: landang = Lattice([-m[t[0]], -m[t[1]], m[t[2]]]).lengths_and_angles transf = np.zeros(shape=(3, 3)) transf[0][t[0]] = -1 transf[1][t[1]] = -1 transf[2][t[2]] = 1 a, b, c = landang[0] alpha = math.pi * landang[1][0] / 180 new_matrix = [[a, 0, 0], [0, b, 0], [0, c * cos(alpha), c * sin(alpha)]] continue elif landang[1][0] < 90 and landang[0][1] < landang[0][2]: transf = np.zeros(shape=(3, 3)) transf[0][t[0]] = 1 transf[1][t[1]] = 1 transf[2][t[2]] = 1 a, b, c = landang[0] alpha = math.pi * landang[1][0] / 180 new_matrix = [[a, 0, 0], [0, b, 0], [0, c * cos(alpha), c * sin(alpha)]] if new_matrix is None: #this if is to treat the case #where alpha==90 (but we still have a monoclinic sg new_matrix = [[sorted_lengths[0], 0, 0], [0, sorted_lengths[1], 0], [0, 0, sorted_lengths[2]]] transf = np.zeros(shape=(3, 3)) for c in range(len(sorted_dic)): transf[c][sorted_dic[c]['orig_index']] = 1 if international_monoclinic: # The above code makes alpha the non-right angle. # The following will convert to proper international convention # that beta is the non-right angle. op = [[0, 1, 0], [1, 0, 0], [0, 0, -1]] transf = np.dot(op, transf) new_matrix = np.dot(op, new_matrix) beta = Lattice(new_matrix).beta if beta < 90: op = [[-1, 0, 0], [0, -1, 0], [0, 0, 1]] transf = np.dot(op, transf) new_matrix = np.dot(op, new_matrix) latt = Lattice(new_matrix) elif latt_type == "triclinic": #we use a LLL Minkowski-like reduction for the triclinic cells struct = struct.get_reduced_structure("LLL") a, b, c = latt.lengths_and_angles[0] alpha, beta, gamma = [ math.pi * i / 180 for i in latt.lengths_and_angles[1] ] new_matrix = None test_matrix = [ [a, 0, 0], [b * cos(gamma), b * sin(gamma), 0.0], [ c * cos(beta), c * (cos(alpha) - cos(beta) * cos(gamma)) / sin(gamma), c * math.sqrt( sin(gamma)**2 - cos(alpha)**2 - cos(beta)**2 + 2 * cos(alpha) * cos(beta) * cos(gamma)) / sin(gamma) ] ] def is_all_acute_or_obtuse(m): recp_angles = np.array(Lattice(m).reciprocal_lattice.angles) return np.all(recp_angles <= 90) or np.all(recp_angles > 90) if is_all_acute_or_obtuse(test_matrix): transf = np.eye(3) new_matrix = test_matrix test_matrix = [ [-a, 0, 0], [b * cos(gamma), b * sin(gamma), 0.0], [ -c * cos(beta), -c * (cos(alpha) - cos(beta) * cos(gamma)) / sin(gamma), -c * math.sqrt( sin(gamma)**2 - cos(alpha)**2 - cos(beta)**2 + 2 * cos(alpha) * cos(beta) * cos(gamma)) / sin(gamma) ] ] if is_all_acute_or_obtuse(test_matrix): transf = [[-1, 0, 0], [0, 1, 0], [0, 0, -1]] new_matrix = test_matrix test_matrix = [ [-a, 0, 0], [-b * cos(gamma), -b * sin(gamma), 0.0], [ c * cos(beta), c * (cos(alpha) - cos(beta) * cos(gamma)) / sin(gamma), c * math.sqrt( sin(gamma)**2 - cos(alpha)**2 - cos(beta)**2 + 2 * cos(alpha) * cos(beta) * cos(gamma)) / sin(gamma) ] ] if is_all_acute_or_obtuse(test_matrix): transf = [[-1, 0, 0], [0, -1, 0], [0, 0, 1]] new_matrix = test_matrix test_matrix = [ [a, 0, 0], [-b * cos(gamma), -b * sin(gamma), 0.0], [ -c * cos(beta), -c * (cos(alpha) - cos(beta) * cos(gamma)) / sin(gamma), -c * math.sqrt( sin(gamma)**2 - cos(alpha)**2 - cos(beta)**2 + 2 * cos(alpha) * cos(beta) * cos(gamma)) / sin(gamma) ] ] if is_all_acute_or_obtuse(test_matrix): transf = [[1, 0, 0], [0, -1, 0], [0, 0, -1]] new_matrix = test_matrix latt = Lattice(new_matrix) new_coords = np.dot(transf, np.transpose(struct.frac_coords)).T new_struct = Structure(latt, struct.species_and_occu, new_coords, site_properties=struct.site_properties, to_unit_cell=True) return new_struct.get_sorted_structure()