Ejemplo n.º 1
0
    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)
Ejemplo n.º 2
0
    def test_get_slabs(self):
        gen = SlabGenerator(self.get_structure("CsCl"), [0, 0, 1], 10, 10)

        #Test orthogonality of some internal variables.
        a, b, c = gen.oriented_unit_cell.lattice.matrix
        self.assertAlmostEqual(np.dot(a, gen._normal), 0)
        self.assertAlmostEqual(np.dot(b, gen._normal), 0)

        self.assertEqual(len(gen.get_slabs()), 1)

        s = self.get_structure("LiFePO4")
        gen = SlabGenerator(s, [0, 0, 1], 10, 10)
        self.assertEqual(len(gen.get_slabs()), 5)

        self.assertEqual(len(gen.get_slabs(bonds={("P", "O"): 3})), 2)

        # There are no slabs in LFP that does not break either P-O or Fe-O
        # bonds for a miller index of [0, 0, 1].
        self.assertEqual(len(gen.get_slabs(
            bonds={("P", "O"): 3, ("Fe", "O"): 3})), 0)

        #If we allow some broken bonds, there are a few slabs.
        self.assertEqual(len(gen.get_slabs(
            bonds={("P", "O"): 3, ("Fe", "O"): 3},
            max_broken_bonds=2)), 2)

        # At this threshold, only the origin and center Li results in
        # clustering. All other sites are non-clustered. So the of
        # slabs is of sites in LiFePO4 unit cell - 2 + 1.
        self.assertEqual(len(gen.get_slabs(tol=1e-4)), 15)

        LiCoO2=Structure.from_file(get_path("icsd_LiCoO2.cif"),
                                          primitive=False)
        gen = SlabGenerator(LiCoO2, [0, 0, 1], 10, 10)
        lco = gen.get_slabs(bonds={("Co", "O"): 3})
        self.assertEqual(len(lco), 1)
        a, b, c = gen.oriented_unit_cell.lattice.matrix
        self.assertAlmostEqual(np.dot(a, gen._normal), 0)
        self.assertAlmostEqual(np.dot(b, gen._normal), 0)

        scc = Structure.from_spacegroup("Pm-3m", Lattice.cubic(3), ["Fe"],
                                        [[0, 0, 0]])
        gen = SlabGenerator(scc, [0, 0, 1], 10, 10)
        slabs = gen.get_slabs()
        self.assertEqual(len(slabs), 1)
        gen = SlabGenerator(scc, [1, 1, 1], 10, 10, max_normal_search=1)
        slabs = gen.get_slabs()
        self.assertEqual(len(slabs), 1)

        # Test whether using units of hkl planes instead of Angstroms for
        # min_slab_size and min_vac_size will give us the same number of atoms
        natoms = []
        for a in [1, 1.4, 2.5, 3.6]:
            s = Structure.from_spacegroup("Im-3m", Lattice.cubic(a), ["Fe"], [[0,0,0]])
            slabgen = SlabGenerator(s, (1,1,1), 10, 10, in_unit_planes=True,
                                    max_normal_search=2)
            natoms.append(len(slabgen.get_slab()))
        n = natoms[0]
        for i in natoms:
            self.assertEqual(n, i)
Ejemplo n.º 3
0
    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()
Ejemplo n.º 4
0
    def test_normal_search(self):
        fcc = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3), ["Fe"],
                                        [[0, 0, 0]])
        for miller in [(1, 0, 0), (1, 1, 0), (1, 1, 1), (2, 1, 1)]:
            gen = SlabGenerator(fcc, miller, 10, 10)
            gen_normal = SlabGenerator(fcc, miller, 10, 10,
                                       max_normal_search=max(miller))
            slab = gen_normal.get_slab()
            self.assertAlmostEqual(slab.lattice.alpha, 90)
            self.assertAlmostEqual(slab.lattice.beta, 90)
            self.assertGreaterEqual(len(gen_normal.oriented_unit_cell),
                                    len(gen.oriented_unit_cell))

        graphite = self.get_structure("Graphite")
        for miller in [(1, 0, 0), (1, 1, 0), (0, 0, 1), (2, 1, 1)]:
            gen = SlabGenerator(graphite, miller, 10, 10)
            gen_normal = SlabGenerator(graphite, miller, 10, 10,
                                       max_normal_search=max(miller))
            self.assertGreaterEqual(len(gen_normal.oriented_unit_cell),
                                    len(gen.oriented_unit_cell))

        sc = Structure(Lattice.hexagonal(3.32, 5.15), ["Sc", "Sc"],
                       [[1/3, 2/3, 0.25], [2/3, 1/3, 0.75]])
        gen = SlabGenerator(sc, (1, 1, 1), 10, 10, max_normal_search=1)
        self.assertAlmostEqual(gen.oriented_unit_cell.lattice.angles[1], 90)
Ejemplo n.º 5
0
    def test_get_slab(self):
        s = self.get_structure("LiFePO4")
        gen = SlabGenerator(s, [0, 0, 1], 10, 10)
        s = gen.get_slab(0.25)
        self.assertAlmostEqual(s.lattice.abc[2], 20.820740000000001)

        fcc = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3), ["Fe"],
                                        [[0, 0, 0]])
        gen = SlabGenerator(fcc, [1, 1, 1], 10, 10)
        slab = gen.get_slab()
        gen = SlabGenerator(fcc, [1, 1, 1], 10, 10, primitive=False)
        slab_non_prim = gen.get_slab()
        self.assertEqual(len(slab), 6)
        self.assertEqual(len(slab_non_prim), len(slab) * 4)

        #Some randomized testing of cell vectors
        for i in range(1, 231):
            i = random.randint(1, 230)
            sg = SpaceGroup.from_int_number(i)
            if sg.crystal_system == "hexagonal" or (sg.crystal_system == \
                    "trigonal" and sg.symbol.endswith("H")):
                latt = Lattice.hexagonal(5, 10)
            else:
                #Cubic lattice is compatible with all other space groups.
                latt = Lattice.cubic(5)
            s = Structure.from_spacegroup(i, latt, ["H"], [[0, 0, 0]])
            miller = (0, 0, 0)
            while miller == (0, 0, 0):
                miller = (random.randint(0, 6), random.randint(0, 6),
                          random.randint(0, 6))
            gen = SlabGenerator(s, miller, 10, 10)
            a, b, c = gen.oriented_unit_cell.lattice.matrix
            self.assertAlmostEqual(np.dot(a, gen._normal), 0)
            self.assertAlmostEqual(np.dot(b, gen._normal), 0)
Ejemplo n.º 6
0
    def setUp(self):
        zno1 = Structure.from_file(get_path("ZnO-wz.cif"), primitive=False)
        zno55 = SlabGenerator(zno1, [1, 0, 0], 5, 5, lll_reduce=False,
                              center_slab=False).get_slab()

        Ti = Structure(Lattice.hexagonal(4.6, 2.82), ["Ti", "Ti", "Ti"],
                       [[0.000000, 0.000000, 0.000000],
                       [0.333333, 0.666667, 0.500000],
                       [0.666667, 0.333333, 0.500000]])

        Ag_fcc = Structure(Lattice.cubic(4.06), ["Ag", "Ag", "Ag", "Ag"],
                           [[0.000000, 0.000000, 0.000000],
                           [0.000000, 0.500000, 0.500000],
                           [0.500000, 0.000000, 0.500000],
                           [0.500000, 0.500000, 0.000000]])

        laue_groups = ["-1", "2/m", "mmm", "4/m",
                       "4/mmm", "-3", "-3m", "6/m",
                       "6/mmm", "m-3", "m-3m"]

        self.ti = Ti
        self.agfcc = Ag_fcc
        self.zno1 = zno1
        self.zno55 = zno55
        self.h = Structure(Lattice.cubic(3), ["H"],
                            [[0, 0, 0]])
        self.libcc = Structure(Lattice.cubic(3.51004), ["Li", "Li"],
                               [[0, 0, 0], [0.5, 0.5, 0.5]])
        self.laue_groups = laue_groups
Ejemplo n.º 7
0
 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))
Ejemplo n.º 8
0
    def get_lattice_quanta(self, convert_to_muC_per_cm2=True, all_in_polar=True):
        """
        Returns the dipole / polarization quanta along a, b, and c for
        all structures.
        """
        lattices = [s.lattice for s in self.structures]
        volumes = np.array([s.lattice.volume for s in self.structures])

        L = len(self.structures)

        e_to_muC = -1.6021766e-13
        cm2_to_A2 = 1e16
        units = 1.0 / np.array(volumes)
        units *= e_to_muC * cm2_to_A2

        # convert polarizations and lattice lengths prior to adjustment
        if convert_to_muC_per_cm2 and not all_in_polar:
            # adjust lattices
            for i in range(L):
                lattice = lattices[i]
                l, a = lattice.lengths_and_angles
                lattices[i] = Lattice.from_lengths_and_angles(
                    np.array(l) * units.ravel()[i], a)
        elif convert_to_muC_per_cm2 and all_in_polar:
            for i in range(L):
                lattice = lattices[-1]
                l, a = lattice.lengths_and_angles
                lattices[i] = Lattice.from_lengths_and_angles(
                    np.array(l) * units.ravel()[-1], a)

        quanta = np.array(
            [np.array(l.lengths_and_angles[0]) for l in lattices])

        return quanta
Ejemplo n.º 9
0
class TestLattice(unittest.TestCase):
    def setUp(self):
        # fixture 
        matrix = [ 100,0,0,0,100,0,0,0,100 ]
        self.lat = Lattice(matrix)
        
        self.assertEqual(self.lat._lengths[0],100.0)
        self.assertEqual(self.lat._lengths[1],100.0)
        self.assertEqual(self.lat._lengths[2],100.0)

        self.assertEqual(self.lat._angles[0],90.0)
        self.assertEqual(self.lat._angles[1],90.0)
        self.assertEqual(self.lat._angles[2],90.0)
        
    def test_changematrix(self):
        matrix = [ 132,0,0,0,127,0,0,0,150 ]
        self.lat.set_matrix(matrix)
        self.assertEqual(self.lat._lengths[0],132.0)
        self.assertEqual(self.lat._lengths[1],127.0)
        self.assertEqual(self.lat._lengths[2],150.0)

        self.assertEqual(self.lat._angles[0],90.0)
        self.assertEqual(self.lat._angles[1],90.0)
        self.assertEqual(self.lat._angles[2],90.0)
        

    def tearDown(self):
        del self.lat 
        self.lat = None
Ejemplo n.º 10
0
    def test_merge_sites(self):
        species = [{'Ag': 0.5}, {'Cl': 0.25}, {'Cl': 0.1},
                   {'Ag': 0.5}, {'F': 0.15}, {'F': 0.1}]
        coords = [[0, 0, 0], [0.5, 0.5, 0.5], [0.5, 0.5, 0.5],
                  [0, 0, 0], [0.5, 0.5, 1.501], [0.5, 0.5, 1.501]]
        s = Structure(Lattice.cubic(1), species, coords)
        s.merge_sites(mode="s")
        self.assertEqual(s[0].specie.symbol, 'Ag')
        self.assertEqual(s[1].species_and_occu,
                         Composition({'Cl': 0.35, 'F': 0.25}))
        self.assertArrayAlmostEqual(s[1].frac_coords, [.5, .5, .5005])

        # Test for TaS2 with spacegroup 166 in 160 setting.
        l = Lattice.from_lengths_and_angles([3.374351, 3.374351, 20.308941],
                                            [90.000000, 90.000000, 120.000000])
        species = ["Ta", "S", "S"]
        coords = [[0.000000, 0.000000, 0.944333], [0.333333, 0.666667, 0.353424],
                  [0.666667, 0.333333, 0.535243]]
        tas2 = Structure.from_spacegroup(160, l, species, coords)
        assert len(tas2) == 13
        tas2.merge_sites(mode="d")
        assert len(tas2) == 9

        l = Lattice.from_lengths_and_angles([3.587776, 3.587776, 19.622793],
                                            [90.000000, 90.000000, 120.000000])
        species = ["Na", "V", "S", "S"]
        coords = [[0.333333, 0.666667, 0.165000], [0.000000, 0.000000, 0.998333],
                  [0.333333, 0.666667, 0.399394], [0.666667, 0.333333, 0.597273]]
        navs2 = Structure.from_spacegroup(160, l, species, coords)
        assert len(navs2) == 18
        navs2.merge_sites(mode="d")
        assert len(navs2) == 12
Ejemplo n.º 11
0
    def setUp(self):

        lattice = Lattice.cubic(3.010)
        frac_coords = [[0.00000, 0.00000, 0.00000],
                       [0.00000, 0.50000, 0.50000],
                       [0.50000, 0.00000, 0.50000],
                       [0.50000, 0.50000, 0.00000],
                       [0.50000, 0.00000, 0.00000],
                       [0.50000, 0.50000, 0.50000],
                       [0.00000, 0.00000, 0.50000],
                       [0.00000, 0.50000, 0.00000]]
        species = ['Mg', 'Mg', 'Mg', 'Mg', 'O', 'O', 'O', 'O']
        self.MgO = Structure(lattice, species, frac_coords)
        self.MgO.add_oxidation_state_by_element({"Mg": 2, "O": -6})

        lattice_Dy = Lattice.hexagonal(3.58, 25.61)
        frac_coords_Dy = [[0.00000, 0.00000, 0.00000],
                          [0.66667, 0.33333, 0.11133],
                          [0.00000, 0.00000, 0.222],
                          [0.66667, 0.33333, 0.33333],
                          [0.33333, 0.66666, 0.44467],
                          [0.66667, 0.33333, 0.55533],
                          [0.33333, 0.66667, 0.66667],
                          [0.00000, 0.00000, 0.778],
                          [0.33333, 0.66667, 0.88867]]
        species_Dy = ['Dy', 'Dy', 'Dy', 'Dy', 'Dy', 'Dy', 'Dy', 'Dy', 'Dy']
        self.Dy = Structure(lattice_Dy, species_Dy, frac_coords_Dy)
Ejemplo n.º 12
0
 def test_get_primitive_structure(self):
     coords = [[0, 0, 0], [0.5, 0.5, 0], [0, 0.5, 0.5], [0.5, 0, 0.5]]
     fcc_ag = Structure(Lattice.cubic(4.09), ["Ag"] * 4, coords)
     self.assertEqual(len(fcc_ag.get_primitive_structure()), 1)
     coords = [[0, 0, 0], [0.5, 0.5, 0.5]]
     bcc_li = Structure(Lattice.cubic(4.09), ["Li"] * 2, coords)
     self.assertEqual(len(bcc_li.get_primitive_structure()), 1)
Ejemplo n.º 13
0
    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)
Ejemplo n.º 14
0
 def test_distance_and_image(self):
     other_site = PeriodicSite("Fe", np.array([1, 1, 1]), self.lattice)
     (distance, image) = self.site.distance_and_image(other_site)
     self.assertAlmostEqual(distance, 6.22494979899, 5)
     self.assertTrue(([-1, -1, -1] == image).all())
     (distance, image) = self.site.distance_and_image(other_site, [1, 0, 0])
     self.assertAlmostEqual(distance, 19.461500456028563, 5)
     # Test that old and new distance algo give the same ans for
     # "standard lattices"
     lattice = Lattice(np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]]))
     site1 = PeriodicSite("Fe", np.array([0.01, 0.02, 0.03]), lattice)
     site2 = PeriodicSite("Fe", np.array([0.99, 0.98, 0.97]), lattice)
     self.assertAlmostEqual(get_distance_and_image_old(site1, site2)[0],
                            site1.distance_and_image(site2)[0])
     lattice = Lattice.from_parameters(1, 0.01, 1, 10, 10, 10)
     site1 = PeriodicSite("Fe", np.array([0.01, 0.02, 0.03]), lattice)
     site2 = PeriodicSite("Fe", np.array([0.99, 0.98, 0.97]), lattice)
     self.assertTrue(get_distance_and_image_old(site1, site2)[0] >
                     site1.distance_and_image(site2)[0])
     site2 = PeriodicSite("Fe", np.random.rand(3), lattice)
     (dist_old, jimage_old) = get_distance_and_image_old(site1, site2)
     (dist_new, jimage_new) = site1.distance_and_image(site2)
     self.assertTrue(dist_old - dist_new > -1e-8,
                     "New distance algo should give smaller answers!")
     self.assertFalse((abs(dist_old - dist_new) < 1e-8) ^
                      (jimage_old == jimage_new).all(),
                      "If old dist == new dist, images must be the same!")
     latt = Lattice.from_parameters(3.0, 3.1, 10.0, 2.96, 2.0, 1.0)
     site = PeriodicSite("Fe", [0.1, 0.1, 0.1], latt)
     site2 = PeriodicSite("Fe", [0.99, 0.99, 0.99], latt)
     (dist, img) = site.distance_and_image(site2)
     self.assertAlmostEqual(dist, 0.15495358379511573)
     self.assertEqual(list(img), [-11, 6, 0])
Ejemplo n.º 15
0
 def __init__(self, structure, scaling_matrix=((1, 0, 0), (0, 1, 0), (0, 0, 1))):
     """
     Create a supercell.
     
     Arguments:
         structure:
             pymatgen.core.structure Structure object.
         scaling_matrix:
             a matrix of transforming the lattice vectors. Defaults to the
             identity matrix. Has to be all integers. e.g.,
             [[2,1,0],[0,3,0],[0,0,1]] generates a new structure with
             lattice vectors a' = 2a + b, b' = 3b, c' = c where a, b, and c
             are the lattice vectors of the original structure. 
     """
     self._original_structure = structure
     old_lattice = structure.lattice
     scale_matrix = np.array(scaling_matrix)
     new_lattice = Lattice(np.dot(scale_matrix, old_lattice.matrix))
     new_species = []
     new_fcoords = []
     def range_vec(i):
         return range(max(scale_matrix[:][:, i]) - min(scale_matrix[:][:, i]))
     for site in structure.sites:
         for (i, j, k) in itertools.product(range_vec(0), range_vec(1), range_vec(2)):
             new_species.append(site.species_and_occu)
             fcoords = site.frac_coords
             coords = old_lattice.get_cartesian_coords(fcoords + np.array([i, j, k]))
             new_fcoords.append(new_lattice.get_fractional_coords(coords))
     self._modified_structure = Structure(new_lattice, new_species, new_fcoords, False)
Ejemplo n.º 16
0
def get_aligned_lattices(slab_sub, slab_2d, max_area=200,
                         max_mismatch=0.05,
                         max_angle_diff=1, r1r2_tol=0.2):
    """
    given the 2 slab structures and the alignment paramters, return
    slab structures with lattices that are aligned with respect to each
    other
    """
    # get the matching substrate and 2D material lattices
    uv_substrate, uv_mat2d = get_matching_lattices(slab_sub, slab_2d,
                                                   max_area=max_area,
                                                   max_mismatch=max_mismatch,
                                                   max_angle_diff=max_angle_diff,
                                                   r1r2_tol=r1r2_tol)
    if not uv_substrate and not uv_mat2d:
        print("no matching u and v, trying adjusting the parameters")
        sys.exit()
    substrate = Structure.from_sites(slab_sub)
    mat2d = Structure.from_sites(slab_2d)
    # map the intial slabs to the newly found matching lattices
    substrate_latt = Lattice(np.array(
            [
                uv_substrate[0][:],
                uv_substrate[1][:],
                substrate.lattice.matrix[2, :]
            ]))
    # to avoid numerical issues with find_mapping
    mat2d_fake_c = mat2d.lattice.matrix[2, :] / np.linalg.norm(mat2d.lattice.matrix[2, :]) * 5.0
    mat2d_latt = Lattice(np.array(
            [
                uv_mat2d[0][:],
                uv_mat2d[1][:],
                mat2d_fake_c
            ]))
    mat2d_latt_fake = Lattice(np.array(
            [
                mat2d.lattice.matrix[0, :],
                mat2d.lattice.matrix[1, :],
                mat2d_fake_c
            ]))
    _, __, scell = substrate.lattice.find_mapping(substrate_latt,
                                                  ltol=0.05,
                                                  atol=1)
    scell[2] = np.array([0, 0, 1])
    substrate.make_supercell(scell)
    _, __, scell = mat2d_latt_fake.find_mapping(mat2d_latt,
                                                ltol=0.05,
                                                atol=1)
    scell[2] = np.array([0, 0, 1])
    mat2d.make_supercell(scell)
    # modify the substrate lattice so that the 2d material can be
    # grafted on top of it
    lmap = Lattice(np.array(
            [
                substrate.lattice.matrix[0, :],
                substrate.lattice.matrix[1, :],
                mat2d.lattice.matrix[2, :]
            ]))
    mat2d.modify_lattice(lmap)
    return substrate, mat2d
Ejemplo n.º 17
0
    def setUp(self):

        l = Lattice.cubic(3.51)
        species = ["Ni"]
        coords = [[0,0,0]]
        self.Ni = Structure.from_spacegroup("Fm-3m", l, species, coords)
        self.Si = Structure.from_spacegroup("Fd-3m", Lattice.cubic(5.430500),
                                            ["Si"], [(0, 0, 0.5)])
Ejemplo n.º 18
0
 def test_mapping_symmetry(self):
     l = Lattice.cubic(1)
     l2 = Lattice.orthorhombic(1.1001, 1, 1)
     self.assertEqual(l.find_mapping(l2, ltol=0.1), None)
     self.assertEqual(l2.find_mapping(l, ltol=0.1), None)
     l2 = Lattice.orthorhombic(1.0999, 1, 1)
     self.assertNotEqual(l2.find_mapping(l, ltol=0.1), None)
     self.assertNotEqual(l.find_mapping(l2, ltol=0.1), None)
Ejemplo n.º 19
0
    def setUp(self):
        zno1 = Structure.from_file(get_path("ZnO-wz.cif"), primitive=False)
        zno55 = SlabGenerator(zno1, [1, 0, 0], 5, 5, lll_reduce=False,
                              center_slab=False).get_slab()

        self.zno1 = zno1
        self.zno55 = zno55
        self.h = Structure(Lattice.cubic(3), ["H"],
                            [[0, 0, 0]])
        self.libcc = Structure(Lattice.cubic(3.51004), ["Li", "Li"],
                               [[0, 0, 0], [0.5, 0.5, 0.5]])
Ejemplo n.º 20
0
    def test_get_points_in_sphere(self):
        # This is a non-niggli representation of a cubic lattice
        latt = Lattice([[1,5,0],[0,1,0],[5,0,1]])
        # evenly spaced points array between 0 and 1
        pts = np.array(list(itertools.product(range(5), repeat=3))) / 5
        pts = latt.get_fractional_coords(pts)

        self.assertEqual(len(latt.get_points_in_sphere(
            pts, [0, 0, 0], 0.20001)), 7)
        self.assertEqual(len(latt.get_points_in_sphere(
            pts, [0.5, 0.5, 0.5], 1.0001)), 552)
Ejemplo n.º 21
0
 def test_to_from_dict(self):
     d = self.tetragonal.to_dict
     t = Lattice.from_dict(d)
     for i in range(3):
         self.assertEqual(t.abc[i], self.tetragonal.abc[i])
         self.assertEqual(t.angles[i], self.tetragonal.angles[i])
     #Make sure old style dicts work.
     del d["matrix"]
     t = Lattice.from_dict(d)
     for i in range(3):
         self.assertEqual(t.abc[i], self.tetragonal.abc[i])
         self.assertEqual(t.angles[i], self.tetragonal.angles[i])
Ejemplo n.º 22
0
 def test_get_vector_along_lattice_directions(self):
    lattice_mat = np.array([[0.5, 0., 0.],
                            [0.5, np.sqrt(3) / 2., 0.],
                            [0., 0., 1.0]])
    lattice = Lattice(lattice_mat)
    cart_coord = np.array([0.5, np.sqrt(3)/4., 0.5])
    latt_coord = np.array([0.25, 0.5, 0.5])
    from_direct = lattice.get_fractional_coords(cart_coord) * lattice.lengths_and_angles[0]
    self.assertArrayAlmostEqual(lattice.get_vector_along_lattice_directions(cart_coord), from_direct)
    self.assertArrayAlmostEqual(lattice.get_vector_along_lattice_directions(cart_coord), latt_coord)
    self.assertArrayEqual(lattice.get_vector_along_lattice_directions(cart_coord).shape, [3,])
    self.assertArrayEqual(lattice.get_vector_along_lattice_directions(cart_coord.reshape([1,3])).shape, [1,3])
Ejemplo n.º 23
0
    def test_find_mapping(self):
        m = np.array([[0.1, 0.2, 0.3], [-0.1, 0.2, 0.7], [0.6, 0.9, 0.2]])
        latt = Lattice(m)

        op = SymmOp.from_origin_axis_angle([0, 0, 0], [2, 3, 3], 35)
        rot = op.rotation_matrix
        scale = np.array([[1, 1, 0], [0, 1, 0], [0, 0, 1]])

        latt2 = Lattice(np.dot(rot, np.dot(scale, m).T).T)
        (latt, rot, scale2) = latt2.find_mapping(latt)
        self.assertAlmostEqual(abs(np.linalg.det(rot)), 1)
        self.assertTrue(np.allclose(scale2, scale) or
                        np.allclose(scale2, -scale))
Ejemplo n.º 24
0
    def setUp(self):
        self.cscl = Structure.from_spacegroup(
            "Pm-3m", Lattice.cubic(4.2), ["Cs", "Cl"], [[0, 0, 0], [0.5, 0.5, 0.5]])

        self.lifepo4 = self.get_structure("LiFePO4")
        self.tei = Structure.from_file(get_path("icsd_TeI.cif"),
                                       primitive=False)
        self.LiCoO2 = Structure.from_file(get_path("icsd_LiCoO2.cif"),
                                          primitive=False)

        self.p1 = Structure(Lattice.from_parameters(3, 4, 5, 31, 43, 50),
                            ["H", "He"], [[0, 0, 0], [0.1, 0.2, 0.3]])
        self.graphite = self.get_structure("Graphite")
Ejemplo n.º 25
0
    def test_get_primitive_structure(self):
        coords = [[0, 0, 0], [0.5, 0.5, 0], [0, 0.5, 0.5], [0.5, 0, 0.5]]
        fcc_ag = IStructure(Lattice.cubic(4.09), ["Ag"] * 4, coords)
        self.assertEqual(len(fcc_ag.get_primitive_structure()), 1)
        coords = [[0, 0, 0], [0.5, 0.5, 0.5]]
        bcc_li = IStructure(Lattice.cubic(4.09), ["Li"] * 2, coords)
        bcc_prim = bcc_li.get_primitive_structure()
        self.assertEqual(len(bcc_prim), 1)
        self.assertAlmostEqual(bcc_prim.lattice.alpha, 109.47122, 3)

        coords = [[0] * 3, [0.5] * 3, [0.25] * 3, [0.26] * 3]
        s = IStructure(Lattice.cubic(4.09), ["Ag"] * 4, coords)
        self.assertEqual(len(s.get_primitive_structure()), 4)
Ejemplo n.º 26
0
    def test_interpolate(self):
        coords = list()
        coords.append([0, 0, 0])
        coords.append([0.75, 0.5, 0.75])
        struct = IStructure(self.lattice, ["Si"] * 2, coords)
        coords2 = list()
        coords2.append([0, 0, 0])
        coords2.append([0.5, 0.5, 0.5])
        struct2 = IStructure(self.struct.lattice, ["Si"] * 2, coords2)
        int_s = struct.interpolate(struct2, 10)
        for s in int_s:
            self.assertIsNotNone(s, "Interpolation Failed!")
            self.assertEqual(int_s[0].lattice, s.lattice)
        self.assertArrayEqual(int_s[1][1].frac_coords, [0.725, 0.5, 0.725])

        badlattice = [[1, 0.00, 0.00], [0, 1, 0.00], [0.00, 0, 1]]
        struct2 = IStructure(badlattice, ["Si"] * 2, coords2)
        self.assertRaises(ValueError, struct.interpolate, struct2)

        coords2 = list()
        coords2.append([0, 0, 0])
        coords2.append([0.5, 0.5, 0.5])
        struct2 = IStructure(self.struct.lattice, ["Si", "Fe"], coords2)
        self.assertRaises(ValueError, struct.interpolate, struct2)

        # Test autosort feature.
        s1 = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3),
                                       ["Fe"], [[0, 0, 0]])
        s1.pop(0)
        s2 = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3),
                                       ["Fe"], [[0, 0, 0]])
        s2.pop(2)
        random.shuffle(s2)

        for s in s1.interpolate(s2, autosort_tol=0.5):
            self.assertArrayAlmostEqual(s1[0].frac_coords, s[0].frac_coords)
            self.assertArrayAlmostEqual(s1[2].frac_coords, s[2].frac_coords)

        # Make sure autosort has no effect on simpler interpolations,
        # and with shuffled sites.
        s1 = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3),
                                       ["Fe"], [[0, 0, 0]])
        s2 = Structure.from_spacegroup("Fm-3m", Lattice.cubic(3),
                                       ["Fe"], [[0, 0, 0]])
        s2[0] = "Fe", [0.01, 0.01, 0.01]
        random.shuffle(s2)

        for s in s1.interpolate(s2, autosort_tol=0.5):
            self.assertArrayAlmostEqual(s1[1].frac_coords, s[1].frac_coords)
            self.assertArrayAlmostEqual(s1[2].frac_coords, s[2].frac_coords)
            self.assertArrayAlmostEqual(s1[3].frac_coords, s[3].frac_coords)
Ejemplo n.º 27
0
    def write_vib_file(self, xyz_file, qpoint, displ, do_real=True, frac_coords=True, scale_matrix=None, max_supercell=None):
        """
        write into the file descriptor xyz_file the positions and displacements of the atoms

        Args:
            xyz_file: file_descriptor
            qpoint: qpoint to be analyzed
            displ: eigendisplacements to be analyzed
            do_real: True if you want to get only real part, False means imaginary part
            frac_coords: True if the eigendisplacements are given in fractional coordinates
            scale_matrix: Scale matrix for supercell
            max_supercell: Maximum size of supercell vectors with respect to primitive cell
        """
        if scale_matrix is None:
            if max_supercell is None:
                raise ValueError("If scale_matrix is not provided, please provide max_supercell !")

            scale_matrix = self.get_smallest_supercell(qpoint, max_supercell=max_supercell)

        old_lattice = self._lattice
        new_lattice = Lattice(np.dot(scale_matrix, old_lattice.matrix))

        tvects = self.get_trans_vect(scale_matrix)

        new_displ = np.zeros(3, dtype=np.float)

        fmtstr = "{{}} {{:.{0}f}} {{:.{0}f}} {{:.{0}f}} {{:.{0}f}} {{:.{0}f}} {{:.{0}f}}\n".format(6)

        for at, site in enumerate(self):
            for t in tvects:
                if do_real:
                    new_displ[:] = np.real(np.exp(2*1j*np.pi*(np.dot(qpoint,t)))*displ[at,:])
                else:
                    new_displ[:] = np.imag(np.exp(2*1j*np.pi*(np.dot(qpoint,t)))*displ[at,:])
                if frac_coords:
                    # Convert to fractional coordinates.
                    new_displ = self.lattice.get_cartesian_coords(new_displ)

                # We don't normalize here !!!
                fcoords = site.frac_coords + t

                coords = old_lattice.get_cartesian_coords(fcoords)

                new_fcoords = new_lattice.get_fractional_coords(coords)

                # New_fcoords -> map into 0 - 1
                new_fcoords = np.mod(new_fcoords, 1)
                coords = new_lattice.get_cartesian_coords(new_fcoords)

                xyz_file.write(fmtstr.format(site.specie, coords[0], coords[1], coords[2], new_displ[0], new_displ[1], new_displ[2]))
Ejemplo n.º 28
0
    def compile_crystal(datarow, flavor='pmg'):
        """
        Helper method for representing the MPDS crystal structures in two flavors:
        either as a Pymatgen Structure object, or as an ASE Atoms object.

        Attention! These two flavors are not compatible, e.g.
        primitive vs. crystallographic cell is defaulted,
        atoms wrapped or non-wrapped into the unit cell etc.

        Note, that the crystal structures are not retrieved by default,
        so one needs to specify the fields while retrieval:
            - cell_abc
            - sg_n
            - setting
            - basis_noneq
            - els_noneq
        e.g. like this: {'S':['cell_abc', 'sg_n', 'setting', 'basis_noneq', 'els_noneq']}
        NB. occupancies are not considered.

        Args:
            datarow: (list) Required data to construct crystal structure:
                [cell_abc, sg_n, setting, basis_noneq, els_noneq]
            flavor: (str) Either "pmg", or "ase"

        Returns:
            - if flavor is pmg, returns Pymatgen Structure object
            - if flavor is ase, returns ASE Atoms object
        """
        if not datarow or not datarow[-1]:
            return None

        cell_abc, sg_n, setting, basis_noneq, els_noneq = \
            datarow[-5], int(datarow[-4]), datarow[-3], datarow[-2], datarow[-1]

        if flavor == 'pmg':
            return Structure.from_spacegroup(
                sg_n,
                Lattice.from_parameters(*cell_abc),
                els_noneq,
                basis_noneq
            )

        elif flavor == 'ase' and use_ase:
            atom_data = []
            setting = 2 if setting == '2' else 1

            for num, i in enumerate(basis_noneq):
                atom_data.append(Atom(els_noneq[num], tuple(i)))

            return crystal(
                atom_data,
                spacegroup=sg_n,
                cellpar=cell_abc,
                primitive_cell=True,
                setting=setting,
                onduplicates='replace'
            )

        else:
            raise APIError("Crystal structure treatment unavailable")
Ejemplo n.º 29
0
 def test_consistency(self):
     """
     when only lengths and angles are given for constructors, the
     internal matrix representation is ambiguous since the lattice rotation
     is not specified.
     This test makes sure that a consistent definition is specified for the
     lattice rotation when using different constructors from lengths angles
     """
     l = [3.840198, 3.84019885, 3.8401976]
     a = [119.99998575, 90, 60.00000728]
     mat1 = Lattice.from_lengths_and_angles(l, a).matrix
     mat2 = Lattice.from_parameters(l[0], l[1], l[2],
                                    a[0], a[1], a[2]).matrix
     for i in range(0, 3):
         for j in range(0, 3):
             self.assertAlmostEqual(mat1[i][j], mat2[i][j], 5)
Ejemplo n.º 30
0
    def test_pbc_shortest_vectors(self):
        fcoords = np.array([[0.3, 0.3, 0.5],
                            [0.1, 0.1, 0.3],
                            [0.9, 0.9, 0.8],
                            [0.1, 0.0, 0.5],
                            [0.9, 0.7, 0.0]])
        lattice = Lattice.from_lengths_and_angles([8, 8, 4],
                                                  [90, 76, 58])
        expected = np.array([[0.000, 3.015, 4.072, 3.519, 3.245],
                             [3.015, 0.000, 3.207, 1.131, 4.453],
                             [4.072, 3.207, 0.000, 2.251, 1.788],
                             [3.519, 1.131, 2.251, 0.000, 3.852]])

        vectors = pbc_shortest_vectors(lattice, fcoords[:-1], fcoords)
        dists = np.sum(vectors**2, axis = -1)**0.5
        self.assertArrayAlmostEqual(dists, expected, 3)

        #now try with small loop threshold
        from pymatgen.util import coord_utils
        prev_threshold = coord_utils.LOOP_THRESHOLD
        coord_utils.LOOP_THRESHOLD = 0

        vectors = pbc_shortest_vectors(lattice, fcoords[:-1], fcoords)
        dists = np.sum(vectors**2, axis = -1)**0.5
        self.assertArrayAlmostEqual(dists, expected, 3)

        coord_utils.LOOP_THRESHOLD = prev_threshold
Ejemplo n.º 31
0
    def get_primitive_standard_structure(self, international_monoclinic=True):
        """
        Gives a structure with a primitive 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

        Returns:
            The structure in a primitive standardized cell
        """
        conv = self.get_conventional_standard_structure(
            international_monoclinic=international_monoclinic)
        lattice = self.get_lattice_type()

        if "P" in self.get_spacegroup_symbol() or lattice == "hexagonal":
            return conv

        if lattice == "rhombohedral":
            # check if the conventional representation is hexagonal or
            # rhombohedral
            lengths, angles = conv.lattice.lengths_and_angles
            if abs(lengths[0] - lengths[2]) < 0.0001:
                transf = np.eye
            else:
                transf = np.array([[-1, 1, 1], [2, 1, 1], [-1, -2, 1]],
                                  dtype=np.float) / 3

        elif "I" in self.get_spacegroup_symbol():
            transf = np.array([[-1, 1, 1], [1, -1, 1], [1, 1, -1]],
                              dtype=np.float) / 2
        elif "F" in self.get_spacegroup_symbol():
            transf = np.array([[0, 1, 1], [1, 0, 1], [1, 1, 0]],
                              dtype=np.float) / 2
        elif "C" in self.get_spacegroup_symbol():
            if self.get_crystal_system() == "monoclinic":
                transf = np.array([[1, 1, 0], [-1, 1, 0], [0, 0, 2]],
                                  dtype=np.float) / 2
            else:
                transf = np.array([[1, -1, 0], [1, 1, 0], [0, 0, 2]],
                                  dtype=np.float) / 2
        else:
            transf = np.eye(3)

        new_sites = []
        latt = Lattice(np.dot(transf, conv.lattice.matrix))
        for s in conv:
            new_s = PeriodicSite(s.specie,
                                 s.coords,
                                 latt,
                                 to_unit_cell=True,
                                 coords_are_cartesian=True,
                                 properties=s.properties)
            if not any(map(new_s.is_periodic_image, new_sites)):
                new_sites.append(new_s)

        if lattice == "rhombohedral":
            prim = Structure.from_sites(new_sites)
            lengths, angles = prim.lattice.lengths_and_angles
            a = lengths[0]
            alpha = math.pi * angles[0] / 180
            new_matrix = [[a * cos(alpha / 2), -a * sin(alpha / 2), 0],
                          [a * cos(alpha / 2), a * sin(alpha / 2), 0],
                          [
                              a * cos(alpha) / cos(alpha / 2), 0,
                              a * math.sqrt(1 - (cos(alpha)**2 /
                                                 (cos(alpha / 2)**2)))
                          ]]
            new_sites = []
            latt = Lattice(new_matrix)
            for s in prim:
                new_s = PeriodicSite(s.specie,
                                     s.frac_coords,
                                     latt,
                                     to_unit_cell=True,
                                     properties=s.properties)
                if not any(map(new_s.is_periodic_image, new_sites)):
                    new_sites.append(new_s)
            return Structure.from_sites(new_sites)

        return Structure.from_sites(new_sites)
Ejemplo n.º 32
0
 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)
Ejemplo n.º 33
0
    parser = argparse.ArgumentParser(description='Generate INCAR')
    parser.add_argument('eps', type=float, help='averaged dielectric constant')
    parser.add_argument('slab_d',
                        type=float,
                        help='corresponding slab thickness (Angstroms)')

    ## read in the above arguments from command line
    args = parser.parse_args()

    ## the bash script already put us in the appropriate subdirectory
    dir_sub = os.getcwd()

    #    lattice = Poscar.from_file(folder1+"POSCAR").structure.lattice
    with open(os.path.join(dir_sub, "defectproperty.json"), 'r') as file:
        defprop = json.loads(file.read())
    lattice = Lattice.from_dict(defprop["lattice"])

    ## STRUCTURE GROUP
    s = structure_grp(lattice)

    ## SLAB GROUP
    ## assume slab is centered in the cell vertically
    slabmin = (lattice.c - args.slab_d) / 2
    slabmax = (lattice.c + args.slab_d) / 2
    s += slab_grp(slabmin, slabmax, eps=args.eps * np.eye(3))

    ## CHARGE GROUP
    posZ = np.mean([def_site[2] for def_site in defprop["defect_site"]])
    s += charge_grp(posZ * lattice.c, defprop["charge"])

    ## ISOLATED GROUP
Ejemplo n.º 34
0
    def from_string(header_str):
        """
        Reads Header string and returns Header object if header was
        generated by pymatgen.

        Args:
            header_str:
                pymatgen generated feff.inp header

        Returns:
            structure object.
        """
        # Checks to see if generated by pymatgen, if not it is impossible to
        # generate structure object so it is not possible to generate header
        # object and routine ends

        lines = tuple(clean_lines(header_str.split("\n"), False))
        comment1 = lines[0]
        feffpmg = comment1.find("pymatgen")

        if feffpmg > 0:
            comment2 = ' '.join(lines[1].split()[2:])

            #This sec section gets information to create structure object

            source = ' '.join(lines[2].split()[2:])
            natoms = int(lines[8].split()[2])
            basis_vec = lines[6].split()

            a = float(basis_vec[2])
            b = float(basis_vec[3])
            c = float(basis_vec[4])

            lengths = [a, b, c]
            basis_ang = lines[7].split()

            alpha = float(basis_ang[2])
            beta = float(basis_ang[3])
            gamma = float(basis_ang[4])
            angles = [alpha, beta, gamma]

            lattice = Lattice.from_lengths_and_angles(lengths, angles)
            atomic_symbols = []

            for i in xrange(9, 9 + natoms):
                atomic_symbols.append(lines[i].split()[2])

            # read the atomic coordinates
            coords = []

            for i in xrange(natoms):
                toks = lines[i + 9].split()
                coords.append([float(s) for s in toks[3:]])

            #Structure object is now generated and Header object returned

            struct_fromfile = Structure(lattice, atomic_symbols, coords, False,
                                        False, False)

            h = Header(struct_fromfile, source, comment2)

            return h
        else:
            return "Header not generated by pymatgen, " \
                   "cannot return header object"
Ejemplo n.º 35
0
    def from_string(data):
        """
        Reads the exciting input from a string
        """

        root = ET.fromstring(data)
        speciesnode = root.find('structure').iter('species')
        elements = []
        positions = []
        vectors = []
        lockxyz = []
        # get title
        title_in = str(root.find('title').text)
        # Read elements and coordinates
        for nodes in speciesnode:
            symbol = nodes.get('speciesfile').split('.')[0]
            if len(symbol.split('_')) == 2:
                symbol = symbol.split('_')[0]
            if Element.is_valid_symbol(symbol):
                # Try to recognize the element symbol
                element = symbol
            else:
                raise NLValueError("Unknown element!")
            natoms = nodes.getiterator('atom')
            for atom in natoms:
                x, y, z = atom.get('coord').split()
                positions.append([float(x), float(y), float(z)])
                elements.append(element)
                # Obtain lockxyz for each atom
                if atom.get('lockxyz') is not None:
                    lxy = []
                    for l in atom.get('lockxyz').split():
                        if l == 'True' or l == 'true':
                            lxyz.append(True)
                        else:
                            lxyz.append(False)
                    lockxyz.append(lxyz)
                else:
                    lockxyz.append([False, False, False])
        #check the atomic positions type
        if 'cartesian' in root.find('structure').attrib.keys():
            if root.find('structure').attrib['cartesian']:
                cartesian = True
                for i in range(len(positions)):
                    for j in range(3):
                        positions[i][
                            j] = positions[i][j] * ExcitingInput.bohr2ang
                print(positions)
        else:
            cartesian = False
        # get the scale attribute
        scale_in = root.find('structure').find('crystal').get('scale')
        if scale_in:
            scale = float(scale_in) * ExcitingInput.bohr2ang
        else:
            scale = ExcitingInput.bohr2ang

    # get the stretch attribute
        stretch_in = root.find('structure').find('crystal').get('stretch')
        if stretch_in:
            stretch = np.array([float(a) for a in stretch_in])
        else:
            stretch = np.array([1.0, 1.0, 1.0])
        # get basis vectors and scale them accordingly
        basisnode = root.find('structure').find('crystal').iter('basevect')
        for vect in basisnode:
            x, y, z = vect.text.split()
            vectors.append([
                float(x) * stretch[0] * scale,
                float(y) * stretch[1] * scale,
                float(z) * stretch[2] * scale
            ])
        # create lattice and structure object
        lattice_in = Lattice(vectors)
        structure_in = Structure(lattice_in,
                                 elements,
                                 positions,
                                 coords_are_cartesian=cartesian)

        return ExcitingInput(structure_in, title_in, lockxyz)
Ejemplo n.º 36
0
class StructureEditor(StructureModifier):
    """
    Editor for adding, removing and changing sites from a structure
    """
    DISTANCE_TOLERANCE = 0.01

    def __init__(self, structure):
        """
        Args:
            structure:
                pymatgen.core.structure Structure object.
        """
        self._original_structure = structure
        self._lattice = structure.lattice
        self._sites = list(structure.sites)

    def add_site_property(self, property_name, values):
        """
        Adds a property to a site.

        Args:
            property_name:
                The name of the property to add.
            values:
                A sequence of values. Must be same length as number of sites.
        """
        if len(values) != len(self._sites):
            raise ValueError("Values must be same length as sites.")
        for i in xrange(len(self._sites)):
            site = self._sites[i]
            props = site.properties
            if not props:
                props = {}
            props[property_name] = values[i]
            self._sites[i] = PeriodicSite(site.species_and_occu,
                                          site.frac_coords,
                                          self._lattice,
                                          properties=props)

    def replace_species(self, species_mapping):
        """
        Swap species in a structure.

        Args:
            species_mapping:
                dict of species to swap. Species can be elements too.
                e.g., {Element("Li"): Element("Na")} performs a Li for Na
                substitution. The second species can be a sp_and_occu dict.
                For example, a site with 0.5 Si that is passed the mapping
                {Element('Si): {Element('Ge'):0.75, Element('C'):0.25} } will
                have .375 Ge and .125 C.
        """
        def mod_site(site):
            new_atom_occu = collections.defaultdict(int)
            for sp, amt in site.species_and_occu.items():
                if sp in species_mapping:
                    if isinstance(species_mapping[sp], (Element, Specie)):
                        new_atom_occu[species_mapping[sp]] += amt
                    elif isinstance(species_mapping[sp], dict):
                        for new_sp, new_amt in species_mapping[sp].items():
                            new_atom_occu[new_sp] += amt * new_amt
                else:
                    new_atom_occu[sp] += amt
            return PeriodicSite(new_atom_occu,
                                site.frac_coords,
                                self._lattice,
                                properties=site.properties)

        self._sites = map(mod_site, self._sites)

    def replace_site(self, index, species_n_occu):
        """
        Replace a single site. Takes either a species or a dict of species and
        occupations.

        Args:
            index:
                The index of the site in the _sites list.
            species:
                A species object.
        """
        self._sites[index] = PeriodicSite(
            species_n_occu,
            self._sites[index].frac_coords,
            self._lattice,
            properties=self._sites[index].properties)

    def remove_species(self, species):
        """
        Remove all occurrences of a species from a structure.

        Args:
            species:
                species to remove.
        """
        new_sites = []
        for site in self._sites:
            new_sp_occu = {
                sp: amt
                for sp, amt in site.species_and_occu.items()
                if sp not in species
            }
            if len(new_sp_occu) > 0:
                new_sites.append(
                    PeriodicSite(new_sp_occu,
                                 site.frac_coords,
                                 self._lattice,
                                 properties=site.properties))
        self._sites = new_sites

    def append_site(self,
                    species,
                    coords,
                    coords_are_cartesian=False,
                    validate_proximity=True):
        """
        Append a site to the structure at the end.

        Args:
            species:
                species of inserted site
            coords:
                coordinates of inserted site
            fractional_coord:
                Whether coordinates are cartesian. Defaults to False.
            validate_proximity:
                Whether to check if inserted site is too close to an existing
                site. Defaults to True.
        """
        self.insert_site(len(self._sites), species, coords,
                         coords_are_cartesian, validate_proximity)

    def insert_site(self,
                    i,
                    species,
                    coords,
                    coords_are_cartesian=False,
                    validate_proximity=True,
                    properties=None):
        """
        Insert a site to the structure.

        Args:
            i:
                index to insert site
            species:
                species of inserted site
            coords:
                coordinates of inserted site
            coords_are_cartesian:
                Whether coordinates are cartesian. Defaults to False.
            validate_proximity:
                Whether to check if inserted site is too close to an existing
                site. Defaults to True.
        """
        if not coords_are_cartesian:
            new_site = PeriodicSite(species,
                                    coords,
                                    self._lattice,
                                    properties=properties)
        else:
            frac_coords = self._lattice.get_fractional_coords(coords)
            new_site = PeriodicSite(species,
                                    frac_coords,
                                    self._lattice,
                                    properties=properties)

        if validate_proximity:
            for site in self._sites:
                if site.distance(new_site) < self.DISTANCE_TOLERANCE:
                    raise ValueError("New site is too close to an existing "
                                     "site!")

        self._sites.insert(i, new_site)

    def delete_site(self, i):
        """
        Delete site at index i.

        Args:
            i:
                index of site to delete.
        """
        del (self._sites[i])

    def delete_sites(self, indices):
        """
        Delete sites with at indices.

        Args:
            indices:
                sequence of indices of sites to delete.
        """
        self._sites = [
            self._sites[i] for i in range(len(self._sites)) if i not in indices
        ]

    def apply_operation(self, symmop):
        """
        Apply a symmetry operation to the structure and return the new
        structure. The lattice is operated by the rotation matrix only.
        Coords are operated in full and then transformed to the new lattice.

        Args:
            symmop:
                Symmetry operation to apply.
        """
        self._lattice = Lattice(
            [symmop.apply_rotation_only(row) for row in self._lattice.matrix])

        def operate_site(site):
            new_cart = symmop.operate(site.coords)
            new_frac = self._lattice.get_fractional_coords(new_cart)
            return PeriodicSite(site.species_and_occu,
                                new_frac,
                                self._lattice,
                                properties=site.properties)

        self._sites = map(operate_site, self._sites)

    def modify_lattice(self, new_lattice):
        """
        Modify the lattice of the structure.  Mainly used for changing the
        basis.

        Args:
            new_lattice:
                New lattice
        """
        self._lattice = new_lattice
        new_sites = []
        for site in self._sites:
            new_sites.append(
                PeriodicSite(site.species_and_occu,
                             site.frac_coords,
                             self._lattice,
                             properties=site.properties))
        self._sites = new_sites

    def apply_strain(self, strain):
        """
        Apply an isotropic strain to the lattice.

        Args:
            strain:
                Amount of strain to apply. E.g., 0.01 means all lattice
                vectors are increased by 1%. This is equivalent to
                calling modify_lattice with a lattice with lattice parameters
                that are 1% larger.
        """
        self.modify_lattice(Lattice(self._lattice.matrix * (1 + strain)))

    def translate_sites(self, indices, vector, frac_coords=True):
        """
        Translate specific sites by some vector, keeping the sites within the
        unit cell.

        Args:
            sites:
                List of site indices on which to perform the translation.
            vector:
                Translation vector for sites.
            frac_coords:
                Boolean stating whether the vector corresponds to fractional or
                cartesian coordinates.
        """
        for i in indices:
            site = self._sites[i]
            if frac_coords:
                fcoords = site.frac_coords + vector
            else:
                fcoords = self._lattice.get_fractional_coords(site.coords +
                                                              vector)
            new_site = PeriodicSite(site.species_and_occu,
                                    fcoords,
                                    self._lattice,
                                    to_unit_cell=True,
                                    coords_are_cartesian=False,
                                    properties=site.properties)
            self._sites[i] = new_site

    def perturb_structure(self, distance=0.1):
        """
        Performs a random perturbation of the sites in a structure to break
        symmetries.

        Args:
            distance:
                distance in angstroms by which to perturb each site.
        """
        def get_rand_vec():
            #deals with zero vectors.
            vector = np.random.randn(3)
            vnorm = np.linalg.norm(vector)
            return vector / vnorm * distance if vnorm != 0 else get_rand_vec()

        for i in range(len(self._sites)):
            self.translate_sites([i], get_rand_vec(), frac_coords=False)

    def add_oxidation_state_by_element(self, oxidation_states):
        """
        Add oxidation states to a structure.

        Args:
            structure:
                pymatgen.core.structure Structure object.
            oxidation_states:
                dict of oxidation states.
                E.g., {"Li":1, "Fe":2, "P":5, "O":-2}
        """
        try:
            for i, site in enumerate(self._sites):
                new_sp = {}
                for el, occu in site.species_and_occu.items():
                    sym = el.symbol
                    new_sp[Specie(sym, oxidation_states[sym])] = occu
                new_site = PeriodicSite(new_sp,
                                        site.frac_coords,
                                        self._lattice,
                                        coords_are_cartesian=False,
                                        properties=site.properties)
                self._sites[i] = new_site

        except KeyError:
            raise ValueError("Oxidation state of all elements must be "
                             "specified in the dictionary.")

    def add_oxidation_state_by_site(self, oxidation_states):
        """
        Add oxidation states to a structure by site.

        Args:
            oxidation_states:
                List of oxidation states.
                E.g., [1, 1, 1, 1, 2, 2, 2, 2, 5, 5, 5, 5, -2, -2, -2, -2]
        """
        try:
            for i, site in enumerate(self._sites):
                new_sp = {}
                for el, occu in site.species_and_occu.items():
                    sym = el.symbol
                    new_sp[Specie(sym, oxidation_states[i])] = occu
                new_site = PeriodicSite(new_sp,
                                        site.frac_coords,
                                        self._lattice,
                                        coords_are_cartesian=False,
                                        properties=site.properties)
                self._sites[i] = new_site

        except IndexError:
            raise ValueError("Oxidation state of all sites must be "
                             "specified in the dictionary.")

    def remove_oxidation_states(self):
        """
        Removes oxidation states from a structure.
        """
        for i, site in enumerate(self._sites):
            new_sp = collections.defaultdict(float)
            for el, occu in site.species_and_occu.items():
                sym = el.symbol
                new_sp[Element(sym)] += occu
            new_site = PeriodicSite(new_sp,
                                    site.frac_coords,
                                    self._lattice,
                                    coords_are_cartesian=False,
                                    properties=site.properties)
            self._sites[i] = new_site

    def to_unit_cell(self, tolerance=0.1):
        """
        Returns all the sites to their position inside the unit cell.
        If there is a site within the tolerance already there, the site is
        deleted instead of moved.
        """
        new_sites = []
        for site in self._sites:
            if not new_sites:
                new_sites.append(site)
                frac_coords = np.array([site.frac_coords])
                continue
            if len(
                    get_points_in_sphere_pbc(self._lattice, frac_coords,
                                             site.coords, tolerance)):
                continue
            frac_coords = np.append(frac_coords, [site.frac_coords % 1],
                                    axis=0)
            new_sites.append(site.to_unit_cell)
        self._sites = new_sites

    @property
    def original_structure(self):
        """
        The original structure.
        """
        return self._original_structure

    @property
    def modified_structure(self):
        coords = [site.frac_coords for site in self._sites]
        species = [site.species_and_occu for site in self._sites]
        props = {}
        if self._sites[0].properties:
            for k in self._sites[0].properties.keys():
                props[k] = [site.properties[k] for site in self._sites]
        return Structure(self._lattice,
                         species,
                         coords,
                         False,
                         site_properties=props)
Ejemplo n.º 37
0
def get_high_accuracy_voronoi_nodes(structure, rad_dict, probe_rad=0.1):
    """
    Analyze the void space in the input structure using high accuracy
    voronoi decomposition.
    Calls Zeo++ for Voronoi decomposition.

    Args:
        structure: pymatgen.core.structure.Structure
        rad_dict (optional): Dictionary of radii of elements in structure.
            If not given, Zeo++ default values are used.
            Note: Zeo++ uses atomic radii of elements.
            For ionic structures, pass rad_dict with ionic radii
        probe_rad (optional): Sampling probe radius in Angstroms.
            Default is 0.1 A

    Returns:
        voronoi nodes as pymatgen.core.structure.Strucutre within the
        unit cell defined by the lattice of input structure
        voronoi face centers as pymatgen.core.structure.Strucutre within the
        unit cell defined by the lattice of input structure
    """

    with ScratchDir('.'):
        name = "temp_zeo1"
        zeo_inp_filename = name + ".cssr"
        ZeoCssr(structure).write_file(zeo_inp_filename)
        rad_flag = True
        rad_file = name + ".rad"
        with open(rad_file, 'w+') as fp:
            for el in rad_dict.keys():
                print("{} {}".format(el, rad_dict[el].real), file=fp)

        atmnet = AtomNetwork.read_from_CSSR(zeo_inp_filename,
                                            rad_flag=rad_flag,
                                            rad_file=rad_file)
        # vornet, vor_edge_centers, vor_face_centers = \
        #        atmnet.perform_voronoi_decomposition()
        red_ha_vornet = \
            prune_voronoi_network_close_node(atmnet)
        # generate_simplified_highaccuracy_voronoi_network(atmnet)
        # get_nearest_largest_diameter_highaccuracy_vornode(atmnet)
        red_ha_vornet.analyze_writeto_XYZ(name, probe_rad, atmnet)
        voro_out_filename = name + '_voro.xyz'
        voro_node_mol = ZeoVoronoiXYZ.from_file(voro_out_filename).molecule

    species = ["X"] * len(voro_node_mol.sites)
    coords = []
    prop = []
    for site in voro_node_mol.sites:
        coords.append(list(site.coords))
        prop.append(site.properties['voronoi_radius'])

    lattice = Lattice.from_lengths_and_angles(structure.lattice.abc,
                                              structure.lattice.angles)
    vor_node_struct = Structure(lattice,
                                species,
                                coords,
                                coords_are_cartesian=True,
                                to_unit_cell=True,
                                site_properties={"voronoi_radius": prop})

    return vor_node_struct
Ejemplo n.º 38
0
    def _get_structure(self, data, primitive):
        """
        Generate structure from part of the cif.
        """
        lengths = [
            str2float(data["_cell_length_" + i]) for i in ["a", "b", "c"]
        ]
        angles = [
            str2float(data["_cell_angle_" + i])
            for i in ["alpha", "beta", "gamma"]
        ]
        lattice = Lattice.from_lengths_and_angles(lengths, angles)
        try:
            sympos = data["_symmetry_equiv_pos_as_xyz"]
        except KeyError:
            try:
                sympos = data["_symmetry_equiv_pos_as_xyz_"]
            except KeyError:
                warnings.warn("No _symmetry_equiv_pos_as_xyz type key found. "
                              "Defaulting to P1.")
                sympos = ['x, y, z']
        self.symmetry_operations = [SymmOp.from_xyz_string(s) for s in sympos]

        def parse_symbol(sym):
            # capitalization conventions are not strictly followed, eg Cu will be CU
            m = re.search("([A-Za-z]*)", sym)
            if m:
                return m.group(1)[:2].capitalize()
            return ""

        try:
            oxi_states = {
                data["_atom_type_symbol"][i]:
                str2float(data["_atom_type_oxidation_number"][i])
                for i in range(len(data["_atom_type_symbol"]))
            }
        except (ValueError, KeyError):
            oxi_states = None

        coord_to_species = OrderedDict()
        for i in range(len(data["_atom_site_label"])):
            symbol = parse_symbol(data["_atom_site_label"][i])

            # make sure symbol was properly parsed from _atom_site_label
            # otherwise get it from _atom_site_type_symbol
            try:
                Element(symbol)
            except KeyError:
                symbol = parse_symbol(data["_atom_site_type_symbol"][i])

            if oxi_states is not None:
                # sometimes the site doesn't have the type_symbol.
                # we then hope the type_symbol can be parsed from the label
                if "_atom_site_type_symbol" in data.data.keys():
                    k = data["_atom_site_type_symbol"][i]
                else:
                    k = symbol
                el = Specie(symbol, oxi_states[k])
            else:
                el = Element(symbol)
            x = str2float(data["_atom_site_fract_x"][i])
            y = str2float(data["_atom_site_fract_y"][i])
            z = str2float(data["_atom_site_fract_z"][i])
            try:
                occu = str2float(data["_atom_site_occupancy"][i])
            except (KeyError, ValueError):
                occu = 1
            if occu > 0:
                coord = (x, y, z)
                if coord not in coord_to_species:
                    coord_to_species[coord] = {el: occu}
                else:
                    coord_to_species[coord][el] = occu

        allspecies = []
        allcoords = []

        for coord, species in coord_to_species.items():
            coords = self._unique_coords(coord)
            allcoords.extend(coords)
            allspecies.extend(len(coords) * [species])

        #rescale occupancies if necessary
        for species in allspecies:
            totaloccu = sum(species.values())
            if 1 < totaloccu <= self._occupancy_tolerance:
                for key, value in six.iteritems(species):
                    species[key] = value / totaloccu

        struct = Structure(lattice, allspecies, allcoords)
        if primitive:
            struct = struct.get_primitive_structure().get_reduced_structure()
        return struct.get_sorted_structure()
Ejemplo n.º 39
0
 def test_get_wigner_seitz_cell(self):
     ws_cell = Lattice([[10, 0, 0], [0, 5, 0], [0, 0, 1]]) \
         .get_wigner_seitz_cell()
     self.assertEqual(6, len(ws_cell))
     for l in ws_cell[3]:
         self.assertEqual([abs(i) for i in l], [5.0, 2.5, 0.5])
Ejemplo n.º 40
0
    def test_get_lll_reduced_lattice(self):
        lattice = Lattice([1.0, 1, 1, -1.0, 0, 2, 3.0, 5, 6])
        reduced_latt = lattice.get_lll_reduced_lattice()

        expected_ans = Lattice(
            np.array([0.0, 1.0, 0.0, 1.0, 0.0, 1.0, -2.0, 0.0, 1.0]).reshape(
                (3, 3)))
        self.assertAlmostEqual(
            np.linalg.det(
                np.linalg.solve(expected_ans.matrix, reduced_latt.matrix)), 1)
        self.assertArrayAlmostEqual(sorted(reduced_latt.abc),
                                    sorted(expected_ans.abc))
        self.assertAlmostEqual(reduced_latt.volume, lattice.volume)
        latt = [
            7.164750, 2.481942, 0.000000, -4.298850, 2.481942, 0.000000,
            0.000000, 0.000000, 14.253000
        ]
        expected_ans = Lattice(
            np.array([
                -4.298850, 2.481942, 0.000000, 2.865900, 4.963884, 0.000000,
                0.000000, 0.000000, 14.253000
            ]))
        reduced_latt = Lattice(latt).get_lll_reduced_lattice()
        self.assertAlmostEqual(
            np.linalg.det(
                np.linalg.solve(expected_ans.matrix, reduced_latt.matrix)), 1)
        self.assertArrayAlmostEqual(sorted(reduced_latt.abc),
                                    sorted(expected_ans.abc))

        expected_ans = Lattice(
            [0.0, 10.0, 10.0, 10.0, 10.0, 0.0, 30.0, -30.0, 40.0])

        lattice = np.array([100., 0., 10., 10., 10., 20., 10., 10., 10.])
        lattice = lattice.reshape(3, 3)
        lattice = Lattice(lattice.T)
        reduced_latt = lattice.get_lll_reduced_lattice()
        self.assertAlmostEqual(
            np.linalg.det(
                np.linalg.solve(expected_ans.matrix, reduced_latt.matrix)), 1)
        self.assertArrayAlmostEqual(sorted(reduced_latt.abc),
                                    sorted(expected_ans.abc))

        random_latt = Lattice(np.random.random((3, 3)))
        if np.linalg.det(random_latt.matrix) > 1e-8:
            reduced_random_latt = random_latt.get_lll_reduced_lattice()
            self.assertAlmostEqual(reduced_random_latt.volume,
                                   random_latt.volume)
Ejemplo n.º 41
0
    def from_files(path_dir):
        """
        get a BoltztrapAnalyzer object from a set of files

        Args:
            path_dir:
                directory where the boltztrap files are

        Returns:
            a BoltztrapAnalyzer object

        """
        t_steps = set()
        m_steps = set()
        gap = None
        doping = []
        data_doping_full = []
        data_doping_hall = []
        with open(os.path.join(path_dir, "boltztrap.condtens"), 'r') as f:
            data_full = []
            for line in f:
                if not line.startswith("#"):
                    t_steps.add(int(float(line.split()[1])))
                    m_steps.add(float(line.split()[0]))
                    data_full.append([float(c) for c in line.split()])

        with open(os.path.join(path_dir, "boltztrap.halltens"), 'r') as f:
            data_hall = []
            for line in f:
                if not line.startswith("#"):
                    data_hall.append([float(c) for c in line.split()])

        data_dos = []
        with open(os.path.join(path_dir, "boltztrap.transdos"), 'r') as f:
            for line in f:
                if not line.startswith(" #"):
                    data_dos.append([
                        Energy(line.split()[0], "Ry").to("eV"),
                        2 * Energy(line.split()[1], "eV").to("Ry")
                    ])

        with open(os.path.join(path_dir, "boltztrap.outputtrans"), 'r') as f:
            warning = False
            step = 0
            for line in f:
                if "WARNING" in line:
                    warning = True
                if line.startswith("VBM"):
                    efermi = Energy(line.split()[1], "Ry").to("eV")

                if step == 2:
                    l_tmp = line.split("-")[1:]
                    doping.extend([-float(d) for d in l_tmp])
                    step = 0

                if step == 1:
                    doping.extend([float(d) for d in line.split()])
                    step = 2

                if line.startswith("Doping levels to be output for") or \
                        line.startswith(" Doping levels to be output for"):
                    step = 1

                if line.startswith("Egap:"):
                    gap = float(line.split()[1])
        if len(doping) != 0:
            with open(os.path.join(path_dir, "fort.26"), 'r') as f:
                for line in f:
                    if not line.startswith("#") and len(line) > 2:
                        data_doping_full.append(
                            [float(c) for c in line.split()])

            with open(os.path.join(path_dir, "fort.27"), 'r') as f:
                for line in f:
                    if not line.startswith("#") and len(line) > 2:

                        data_doping_hall.append(
                            [float(c) for c in line.split()])

        with open(os.path.join(path_dir, "boltztrap.struct"), 'r') as f:
            tokens = f.readlines()
            vol = Lattice([[
                Length(float(tokens[i].split()[j]), "bohr").to("ang")
                for j in range(3)
            ] for i in range(1, 4)]).volume
        return BoltztrapAnalyzer._make_boltztrap_analyzer_from_data(
            data_full, data_hall, data_dos, sorted([t for t in t_steps]),
            sorted([Energy(m, "Ry").to("eV") for m in m_steps]), efermi,
            Energy(gap, "Ry").to("eV"), doping, data_doping_full,
            data_doping_hall, vol, warning)
Ejemplo n.º 42
0
    def from_string(data):
        """
        Reads the exciting input from a string
        """

        root = ET.fromstring(data)
        speciesnode = root.find("structure").iter("species")
        elements = []
        positions = []
        vectors = []
        lockxyz = []
        # get title
        title_in = str(root.find("title").text)
        # Read elements and coordinates
        for nodes in speciesnode:
            symbol = nodes.get("speciesfile").split(".")[0]
            if len(symbol.split("_")) == 2:
                symbol = symbol.split("_")[0]
            if Element.is_valid_symbol(symbol):
                # Try to recognize the element symbol
                element = symbol
            else:
                raise ValueError("Unknown element!")
            for atom in nodes.iter("atom"):
                x, y, z = atom.get("coord").split()
                positions.append([float(x), float(y), float(z)])
                elements.append(element)
                # Obtain lockxyz for each atom
                if atom.get("lockxyz") is not None:
                    lxyz = []
                    for l in atom.get("lockxyz").split():
                        if l in ("True", "true"):
                            lxyz.append(True)
                        else:
                            lxyz.append(False)
                    lockxyz.append(lxyz)
                else:
                    lockxyz.append([False, False, False])
        # check the atomic positions type
        if "cartesian" in root.find("structure").attrib.keys():
            if root.find("structure").attrib["cartesian"]:
                cartesian = True
                for i, p in enumerate(positions):
                    for j in range(3):
                        p[j] = p[j] * ExcitingInput.bohr2ang
                print(positions)
        else:
            cartesian = False
        # get the scale attribute
        scale_in = root.find("structure").find("crystal").get("scale")
        if scale_in:
            scale = float(scale_in) * ExcitingInput.bohr2ang
        else:
            scale = ExcitingInput.bohr2ang
        # get the stretch attribute
        stretch_in = root.find("structure").find("crystal").get("stretch")
        if stretch_in:
            stretch = np.array([float(a) for a in stretch_in])
        else:
            stretch = np.array([1.0, 1.0, 1.0])
        # get basis vectors and scale them accordingly
        basisnode = root.find("structure").find("crystal").iter("basevect")
        for vect in basisnode:
            x, y, z = vect.text.split()
            vectors.append([
                float(x) * stretch[0] * scale,
                float(y) * stretch[1] * scale,
                float(z) * stretch[2] * scale,
            ])
        # create lattice and structure object
        lattice_in = Lattice(vectors)
        structure_in = Structure(lattice_in,
                                 elements,
                                 positions,
                                 coords_are_cartesian=cartesian)

        return ExcitingInput(structure_in, title_in, lockxyz)
Ejemplo n.º 43
0
    def get_relaxed_cell(self, atom_dump_path, data_in_path, element_symbols):
        """
        Parses the relaxed cell from the dump.atom file.

        Returns the relaxed cell as a Cell object.

        Args:
            atom_dump_path: the path (as a string) to the dump.atom file

            in_data_path: the path (as a string) to the in.data file

            element_symbols: a tuple containing the set of chemical symbols of
                all the elements in the compositions space
        """

        # read the dump.atom file as a list of strings
        with open(atom_dump_path, 'r') as atom_dump:
            lines = atom_dump.readlines()

        # get the lattice vectors
        a_data = lines[5].split()
        b_data = lines[6].split()
        c_data = lines[7].split()

        # parse the tilt factors
        xy = float(a_data[2])
        xz = float(b_data[2])
        yz = float(c_data[2])

        # parse the bounds
        xlo_bound = float(a_data[0])
        xhi_bound = float(a_data[1])
        ylo_bound = float(b_data[0])
        yhi_bound = float(b_data[1])
        zlo_bound = float(c_data[0])
        zhi_bound = float(c_data[1])

        # compute xlo, xhi, ylo, yhi, zlo and zhi according to the conversion
        # given by LAMMPS
        # http://lammps.sandia.gov/doc/Section_howto.html#howto-12
        xlo = xlo_bound - min([0.0, xy, xz, xy + xz])
        xhi = xhi_bound - max([0.0, xy, xz, xy + xz])
        ylo = ylo_bound - min(0.0, yz)
        yhi = yhi_bound - max([0.0, yz])
        zlo = zlo_bound
        zhi = zhi_bound

        # construct a Lattice object from the lo's and hi's and tilts
        a = [xhi - xlo, 0.0, 0.0]
        b = [xy, yhi - ylo, 0.0]
        c = [xz, yz, zhi - zlo]
        relaxed_lattice = Lattice([a, b, c])

        # get the number of atoms
        num_atoms = int(lines[3])

        # get the atom types and their Cartesian coordinates
        types = []
        relaxed_cart_coords = []
        for i in range(num_atoms):
            atom_info = lines[9 + i].split()
            types.append(int(atom_info[1]))
            relaxed_cart_coords.append([
                float(atom_info[2]) - xlo,
                float(atom_info[3]) - ylo,
                float(atom_info[4]) - zlo
            ])

        # read the atom types and corresponding atomic masses from in.data
        with open(data_in_path, 'r') as data_in:
            lines = data_in.readlines()
        types_masses = {}
        for i in range(len(lines)):
            if 'Masses' in lines[i]:
                for j in range(len(element_symbols)):
                    types_masses[int(lines[i + j + 2].split()[0])] = float(
                        lines[i + j + 2].split()[1])

        # map the atom types to chemical symbols
        types_symbols = {}
        for symbol in element_symbols:
            for atom_type in types_masses:
                # round the atomic masses to one decimal point for comparison
                if format(float(Element(symbol).atomic_mass),
                          '.1f') == format(types_masses[atom_type], '.1f'):
                    types_symbols[atom_type] = symbol

        # make a list of chemical symbols (one for each site)
        relaxed_symbols = []
        for atom_type in types:
            relaxed_symbols.append(types_symbols[atom_type])

        return Cell(relaxed_lattice,
                    relaxed_symbols,
                    relaxed_cart_coords,
                    coords_are_cartesian=True)
Ejemplo n.º 44
0
    def get_relaxed_structure(self, gout):
        #Find the structure lines
        structure_lines = []
        cell_param_lines = []
        #print gout
        output_lines = gout.split("\n")
        no_lines = len(output_lines)
        i = 0
        # Compute the input lattice parameters
        while i < no_lines:
            line = output_lines[i]
            if "Full cell parameters" in line:
                i += 2
                line = output_lines[i]
                a = float(line.split()[8])
                alpha = float(line.split()[11])
                line = output_lines[i + 1]
                b = float(line.split()[8])
                beta = float(line.split()[11])
                line = output_lines[i + 2]
                c = float(line.split()[8])
                gamma = float(line.split()[11])
                i += 3
                break
            elif "Cell parameters" in line:
                i += 2
                line = output_lines[i]
                a = float(line.split()[2])
                alpha = float(line.split()[5])
                line = output_lines[i + 1]
                b = float(line.split()[2])
                beta = float(line.split()[5])
                line = output_lines[i + 2]
                c = float(line.split()[2])
                gamma = float(line.split()[5])
                i += 3
                break
            else:
                i += 1

        while i < no_lines:
            line = output_lines[i]
            if "Final fractional coordinates of atoms" in line:
                # read the site coordinates in the following lines
                i += 6
                line = output_lines[i]
                while line[0:2] != '--':
                    structure_lines.append(line)
                    i += 1
                    line = output_lines[i]
                    # read the cell parameters
                i += 9
                line = output_lines[i]
                if "Final cell parameters" in line:
                    i += 3
                    for del_i in range(6):
                        line = output_lines[i + del_i]
                        cell_param_lines.append(line)

                break
            else:
                i += 1

        #Process the structure lines
        if structure_lines:
            sp = []
            coords = []
            for line in structure_lines:
                fields = line.split()
                if fields[2] == 'c':
                    sp.append(fields[1])
                    coords.append(list(float(x) for x in fields[3:6]))
        else:
            raise IOError("No structure found")

        if cell_param_lines:
            a = float(cell_param_lines[0].split()[1])
            b = float(cell_param_lines[1].split()[1])
            c = float(cell_param_lines[2].split()[1])
            alpha = float(cell_param_lines[3].split()[1])
            beta = float(cell_param_lines[4].split()[1])
            gamma = float(cell_param_lines[5].split()[1])
        latt = Lattice.from_parameters(a, b, c, alpha, beta, gamma)

        return Structure(latt, sp, coords)
Ejemplo n.º 45
0
    def get_same_branch_polarization_data(self, convert_to_muC_per_cm2=False):
        """
        Get same branch dipole moment (convert_to_muC_per_cm2=False)
        or polarization for given polarization data (convert_to_muC_per_cm2=True).

        Polarization is a lattice vector, meaning it is only defined modulo the
        quantum of polarization:

            P = P_0 + \\sum_i \\frac{n_i e R_i}{\\Omega}

        where n_i is an integer, e is the charge of the electron in microCoulombs,
        R_i is a lattice vector, and \\Omega is the unit cell volume in cm**3
        (giving polarization units of microCoulomb per centimeter**2).

        The quantum of the dipole moment in electron Angstroms (as given by VASP) is:

            \\sum_i n_i e R_i

        where e, the electron charge, is 1 and R_i is a lattice vector, and n_i is an integer.

        Given N polarization calculations in order from nonpolar to polar, this algorithm
        minimizes the distance between adjacent polarization images. To do this, it
        constructs a polarization lattice for each polarization calculation using the
        pymatgen.core.structure class and calls the get_nearest_site method to find the
        image of a given polarization lattice vector that is closest to the previous polarization
        lattice vector image.

        convert_to_muC_per_cm2: convert polarization from electron * Angstroms to
            microCoulomb per centimeter**2
        """

        p_elec, p_ion = self.get_pelecs_and_pions()
        p_tot = p_elec + p_ion
        p_tot = np.array(p_tot)

        lattices = [s.lattice for s in self.structures]
        volumes = np.array([s.lattice.volume for s in self.structures])

        L = len(p_elec)

        # convert polarizations and lattice lengths prior to adjustment
        if convert_to_muC_per_cm2:
            e_to_muC = -1.6021766e-13
            cm2_to_A2 = 1e16
            units = 1.0 / np.array(volumes)
            units *= e_to_muC * cm2_to_A2
            # Convert the total polarization
            p_tot = np.multiply(units.T[:, np.newaxis], p_tot)
            # adjust lattices
            for i in range(L):
                lattice = lattices[i]
                l, a = lattice.lengths_and_angles
                lattices[i] = Lattice.from_lengths_and_angles(
                    np.array(l) * units.ravel()[i], a)

        d_structs = []
        sites = []
        for i in range(L):
            l = lattices[i]
            frac_coord = np.divide(np.array([p_tot[i]]),
                                   np.array([l.a, l.b, l.c]))
            d = PolarizationLattice(l, ["C"], [np.array(frac_coord).ravel()])
            d_structs.append(d)
            site = d[0]
            if i == 0:
                # Adjust nonpolar polarization to be closest to zero.
                # This is compatible with both a polarization of zero or a half quantum.
                prev_site = [0, 0, 0]
            else:
                prev_site = sites[-1].coords
            new_site = d.get_nearest_site(prev_site, site)
            sites.append(new_site[0])

        adjust_pol = []
        for s, d in zip(sites, d_structs):
            l = d.lattice
            adjust_pol.append(
                np.multiply(s.frac_coords, np.array([l.a, l.b, l.c])).ravel())
        adjust_pol = np.array(adjust_pol)

        return adjust_pol
Ejemplo n.º 46
0
    def test_get_slabs(self):
        gen = SlabGenerator(self.get_structure("CsCl"), [0, 0, 1], 10, 10)

        #Test orthogonality of some internal variables.
        a, b, c = gen.oriented_unit_cell.lattice.matrix
        self.assertAlmostEqual(np.dot(a, gen._normal), 0)
        self.assertAlmostEqual(np.dot(b, gen._normal), 0)

        self.assertEqual(len(gen.get_slabs()), 1)

        s = self.get_structure("LiFePO4")
        gen = SlabGenerator(s, [0, 0, 1], 10, 10)
        self.assertEqual(len(gen.get_slabs()), 5)

        self.assertEqual(len(gen.get_slabs(bonds={("P", "O"): 3})), 2)

        # There are no slabs in LFP that does not break either P-O or Fe-O
        # bonds for a miller index of [0, 0, 1].
        self.assertEqual(
            len(gen.get_slabs(bonds={
                ("P", "O"): 3,
                ("Fe", "O"): 3
            })), 0)

        #If we allow some broken bonds, there are a few slabs.
        self.assertEqual(
            len(
                gen.get_slabs(bonds={
                    ("P", "O"): 3,
                    ("Fe", "O"): 3
                },
                              max_broken_bonds=2)), 2)

        # At this threshold, only the origin and center Li results in
        # clustering. All other sites are non-clustered. So the of
        # slabs is of sites in LiFePO4 unit cell - 2 + 1.
        self.assertEqual(len(gen.get_slabs(tol=1e-4)), 15)

        LiCoO2 = Structure.from_file(get_path("icsd_LiCoO2.cif"),
                                     primitive=False)
        gen = SlabGenerator(LiCoO2, [0, 0, 1], 10, 10)
        lco = gen.get_slabs(bonds={("Co", "O"): 3})
        self.assertEqual(len(lco), 1)
        a, b, c = gen.oriented_unit_cell.lattice.matrix
        self.assertAlmostEqual(np.dot(a, gen._normal), 0)
        self.assertAlmostEqual(np.dot(b, gen._normal), 0)

        scc = Structure.from_spacegroup("Pm-3m", Lattice.cubic(3), ["Fe"],
                                        [[0, 0, 0]])
        gen = SlabGenerator(scc, [0, 0, 1], 10, 10)
        slabs = gen.get_slabs()
        self.assertEqual(len(slabs), 1)
        gen = SlabGenerator(scc, [1, 1, 1], 10, 10, max_normal_search=1)
        slabs = gen.get_slabs()
        self.assertEqual(len(slabs), 1)

        # Test whether using units of hkl planes instead of Angstroms for
        # min_slab_size and min_vac_size will give us the same number of atoms
        natoms = []
        for a in [1, 1.4, 2.5, 3.6]:
            s = Structure.from_spacegroup("Im-3m", Lattice.cubic(a), ["Fe"],
                                          [[0, 0, 0]])
            slabgen = SlabGenerator(s, (1, 1, 1),
                                    10,
                                    10,
                                    in_unit_planes=True,
                                    max_normal_search=2)
            natoms.append(len(slabgen.get_slab()))
        n = natoms[0]
        for i in natoms:
            self.assertEqual(n, i)
Ejemplo n.º 47
0
                      from_ase=True)
    substrate.to(fmt='poscar', filename='POSCAR_substrate_initial.vasp')
    mat2d.to(fmt='poscar', filename='POSCAR_mat2d_initial.vasp')

    # get the matching substrate and 2D material lattices
    uv_substrate, uv_mat2d = get_matching_lattices(substrate,
                                                   mat2d,
                                                   max_area=200,
                                                   max_mismatch=0.01,
                                                   max_angle_diff=1,
                                                   r1r2_tol=0.02)

    # map the intial slabs to the newly found matching lattices
    substrate_latt = Lattice(
        np.array([
            uv_substrate[0][:], uv_substrate[1][:],
            substrate.lattice.matrix[2, :]
        ]))
    mat2d_latt = Lattice(
        np.array([uv_mat2d[0][:], uv_mat2d[1][:], mat2d.lattice.matrix[2, :]]))
    _, __, scell = substrate.lattice.find_mapping(substrate_latt,
                                                  ltol=0.01,
                                                  atol=1)
    substrate.make_supercell(scell)
    _, __, scell = mat2d.lattice.find_mapping(mat2d_latt, ltol=0.01, atol=1)
    mat2d.make_supercell(scell)
    substrate.to(fmt='poscar', filename='POSCAR_substrate_matching.vasp')
    mat2d.to(fmt='poscar', filename='POSCAR_mat2d_matching.vasp')

    # modify the substrate lattice so that the 2d material can be
    # grafted on top of it
Ejemplo n.º 48
0
    def _get_structure_from_spcgrp(self):
        if "A" not in self.lattice:
            raise AssertionError()
        if "B" not in self.lattice:
            logging.info("Lattice vector B not given, assume equal to A")
            self.lattice["B"] = self.lattice["A"]
        if "C" not in self.lattice:
            logging.info("Lattice vector C not given, assume equal to A")
            self.lattice["C"] = self.lattice["C"]
        if "ALPHA" not in self.lattice:
            logging.info("Lattice angle ALPHA not given, assume right-angle")
            self.lattice["ALPHA"] = 90
        if "BETA" not in self.lattice:
            logging.info("Lattice angle BETA not given, assume right-angle")
            self.lattice["BETA"] = 90
        if "GAMMA" not in self.lattice:
            try:
                spcgrp_number = int(self.lattice["SPCGRP"])
            except ValueError:
                spcgrp_number = 0
            if 167 < spcgrp_number < 195:
                logging.info("Lattice angle GAMMA not given, "
                             "hexagonal space group, assume 120")
                self.lattice["GAMMA"] = 120
            else:
                logging.info(
                    "Lattice angle GAMMA not given, assume right-angle")
                self.lattice["GAMMA"] = 90

        if self.cartesian:
            logging.info(
                "Warning: Cartesian positions used without explicit lattice vectors"
            )

        if "UNITS" not in self.lattice or self.lattice["UNITS"] is None:
            if self.ignore_units:
                pass
            else:
                for length in ("A", "B", "C"):
                    self.lattice[length] *= _bohr_to_angstrom

        if "ALAT" not in self.lattice:
            raise AssertionError()
        for length in ("A", "B", "C"):
            self.lattice[length] *= self.lattice["ALAT"]

        lattice = Lattice.from_parameters(
            self.lattice["A"],
            self.lattice["B"],
            self.lattice["C"],
            self.lattice["ALPHA"],
            self.lattice["BETA"],
            self.lattice["GAMMA"],
        )

        species, coords = self._get_species_coords()

        return Structure.from_spacegroup(
            self.lattice["SPCGRP"],
            lattice,
            species,
            coords,
            coords_are_cartesian=self.cartesian,
            tol=self.tol,
        )
Ejemplo n.º 49
0
    def __init__(
        self,
        reciprocal_lattice: Lattice,
        original_points: np.ndarray,
        original_dim: np.ndarray,
        extra_points: np.ndarray,
        ir_to_full_idx: Optional[np.ndarray] = None,
        extra_ir_points_idx: Optional[np.ndarray] = None,
        nworkers: int = pdefaults["nworkers"],
    ):
        """

        Add a warning about only using the symmetry options if you are sure your
        extra k-points have been symmetrized

        Args:
            original_points:
            nworkers:
        """
        self._nworkers = nworkers if nworkers != -1 else cpu_count()
        self._final_points = np.concatenate([original_points, extra_points])
        self._reciprocal_lattice = reciprocal_lattice

        if ir_to_full_idx is None:
            ir_to_full_idx = np.arange(
                len(original_points) + len(extra_points))

        if extra_ir_points_idx is None:
            extra_ir_points_idx = np.arange(len(extra_points))

        logger.debug("Initializing periodic Voronoi calculator")
        all_points = np.concatenate((original_points, extra_points))

        logger.debug("  ├── getting supercell k-points")
        supercell_points = get_supercell_points(all_points)
        supercell_idxs = np.arange(supercell_points.shape[0])

        # filter points far from the zone boundary, this will lead to errors for
        # very small meshes < 5x5x5 but we are not interested in those
        mask = ((supercell_points > -0.75) &
                (supercell_points < 0.75)).all(axis=1)
        supercell_points = supercell_points[mask]
        supercell_idxs = supercell_idxs[mask]

        # want points in cartesian space so we can define a regular spherical
        # cutoff even if reciprocal lattice is not cubic. If we used a
        # fractional cutoff, the cutoff regions would not be spherical
        logger.debug("  ├── getting cartesian points")
        cart_points = reciprocal_lattice.get_cartesian_coords(supercell_points)
        cart_extra_points = reciprocal_lattice.get_cartesian_coords(
            extra_points[extra_ir_points_idx])

        # small cutoff is slightly larger than the max regular grid spacing
        # means at least 1 neighbour point will always be included in each
        # direction, need to find cartesian length which covers the longest direction
        # of the mesh
        spacing = 1 / original_dim
        body_diagonal = reciprocal_lattice.get_cartesian_coords(spacing)
        xy = reciprocal_lattice.get_cartesian_coords(
            [spacing[0], spacing[1], 0])
        xz = reciprocal_lattice.get_cartesian_coords(
            [spacing[0], 0, spacing[2]])
        yz = reciprocal_lattice.get_cartesian_coords(
            [0, spacing[1], spacing[2]])

        len_diagonal = np.linalg.norm(body_diagonal)
        len_xy = np.linalg.norm(xy)
        len_xz = np.linalg.norm(xz)
        len_yz = np.linalg.norm(yz)

        small_cutoff = (np.max([len_diagonal, len_xy, len_xz, len_yz]) * 1.6)
        big_cutoff = (small_cutoff * 1.77)

        logger.debug("  ├── initializing ball tree")

        # use BallTree for quickly evaluating which points are within cutoffs
        tree = BallTree(cart_points)

        n_supercell_points = len(supercell_points)

        # big points are those which surround the extra points within the big cutoff
        # (including the extra points themselves)
        logger.debug("  ├── calculating points in big radius")
        big_points_idx = _query_radius_iteratively(tree, n_supercell_points,
                                                   cart_extra_points,
                                                   big_cutoff)

        # Voronoi points are those we actually include in the Voronoi diagram
        self._voronoi_points = cart_points[big_points_idx]

        # small points are the points in all_points (i.e., original + extra points) for
        # which we want to calculate the Voronoi volumes. Outside the small cutoff, the
        # weights will just be the regular grid weight.
        logger.debug("  └── calculating points in small radius")
        small_points_idx = _query_radius_iteratively(tree, n_supercell_points,
                                                     cart_extra_points,
                                                     small_cutoff)

        # get the irreducible small points
        small_points_in_all_points = supercell_idxs[small_points_idx] % len(
            all_points)
        mapping = ir_to_full_idx[small_points_in_all_points]
        unique_mappings, ir_idx = np.unique(mapping, return_index=True)
        small_points_idx = small_points_idx[ir_idx]

        # get a mapping to go from the ir small points to the full BZ.
        groups = groupby(np.arange(len(all_points)), ir_to_full_idx)
        grouped_ir = groups[unique_mappings]
        counts = [len(g) for g in grouped_ir]
        self._expand_ir = np.repeat(np.arange(len(ir_idx)), counts)

        # get the indices of the expanded ir_small_points in all_points
        self._volume_in_final_idx = np.concatenate(grouped_ir)

        # get the indices of ir_small_points_idx (i.e., the points for which we will
        # calculate the volume) in voronoi_points
        self._volume_points_idx = _get_loc(big_points_idx, small_points_idx)

        # Prepopulate the final volumes array. By default, each point has the
        # volume of the original mesh. Note: at this point, the extra points
        # will have zero volume. This will array will be updated by
        # compute_volumes
        self._volume = reciprocal_lattice.volume
        self._final_volumes = np.full(len(all_points),
                                      1 / len(original_points))
        self._final_volumes[len(original_points):] = 0
        self._final_volumes[self._volume_in_final_idx] = 0
Ejemplo n.º 50
0
 def av_lat(l1, l2):
     params = (np.array(l1.lengths_and_angles) +
               np.array(l2.lengths_and_angles)) / 2
     return Lattice.from_lengths_and_angles(*params)
Ejemplo n.º 51
0
    def from_string(string):
        """
        Reads an PWInput object from a string.

        Args:
            string (str): PWInput string

        Returns:
            PWInput object
        """
        lines = list(clean_lines(string.splitlines()))

        def input_mode(line):
            if line[0] == "&":
                return ("sections", line[1:].lower())
            if "ATOMIC_SPECIES" in line:
                return ("pseudo",)
            if "K_POINTS" in line:
                return "kpoints", line.split("{")[1][:-1]
            if "CELL_PARAMETERS" in line or "ATOMIC_POSITIONS" in line:
                return "structure", line.split("{")[1][:-1]
            if line == "/":
                return None
            return mode

        sections = {
            "control": {},
            "system": {},
            "electrons": {},
            "ions": {},
            "cell": {},
        }
        pseudo = {}
        pseudo_index = 0
        lattice = []
        species = []
        coords = []
        structure = None
        site_properties = {"pseudo": []}
        mode = None
        for line in lines:
            mode = input_mode(line)
            if mode is None:
                pass
            elif mode[0] == "sections":
                section = mode[1]
                m = re.match(r"(\w+)\(?(\d*?)\)?\s*=\s*(.*)", line)
                if m:
                    key = m.group(1).strip()
                    key_ = m.group(2).strip()
                    val = m.group(3).strip()
                    if key_ != "":
                        if sections[section].get(key, None) is None:
                            val_ = [0.0] * 20  # MAX NTYP DEFINITION
                            val_[int(key_) - 1] = PWInput.proc_val(key, val)
                            sections[section][key] = val_

                            site_properties[key] = []
                        else:
                            sections[section][key][int(key_) - 1] = PWInput.proc_val(key, val)
                    else:
                        sections[section][key] = PWInput.proc_val(key, val)

            elif mode[0] == "pseudo":
                m = re.match(r"(\w+)\s+(\d*.\d*)\s+(.*)", line)
                if m:
                    pseudo[m.group(1).strip()] = {}
                    pseudo[m.group(1).strip()]["index"] = pseudo_index
                    pseudo[m.group(1).strip()]["pseudopot"] = m.group(3).strip()
                    pseudo_index += 1
            elif mode[0] == "kpoints":
                m = re.match(r"(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)", line)
                if m:
                    kpoints_grid = (int(m.group(1)), int(m.group(2)), int(m.group(3)))
                    kpoints_shift = (int(m.group(4)), int(m.group(5)), int(m.group(6)))
                else:
                    kpoints_mode = mode[1]
            elif mode[0] == "structure":
                m_l = re.match(r"(-?\d+\.?\d*)\s+(-?\d+\.?\d*)\s+(-?\d+\.?\d*)", line)
                m_p = re.match(r"(\w+)\s+(-?\d+\.\d*)\s+(-?\d+\.?\d*)\s+(-?\d+\.?\d*)", line)
                if m_l:
                    lattice += [
                        float(m_l.group(1)),
                        float(m_l.group(2)),
                        float(m_l.group(3)),
                    ]
                elif m_p:
                    site_properties["pseudo"].append(pseudo[m_p.group(1)]["pseudopot"])
                    species += [pseudo[m_p.group(1)]["pseudopot"].split(".")[0]]
                    coords += [[float(m_p.group(2)), float(m_p.group(3)), float(m_p.group(4))]]

                    for k, v in site_properties.items():
                        if k != "pseudo":
                            site_properties[k].append(sections["system"][k][pseudo[m_p.group(1)]["index"]])
                if mode[1] == "angstrom":
                    coords_are_cartesian = True
                elif mode[1] == "crystal":
                    coords_are_cartesian = False

        structure = Structure(
            Lattice(lattice),
            species,
            coords,
            coords_are_cartesian=coords_are_cartesian,
            site_properties=site_properties,
        )
        return PWInput(
            structure=structure,
            control=sections["control"],
            system=sections["system"],
            electrons=sections["electrons"],
            ions=sections["ions"],
            cell=sections["cell"],
            kpoints_mode=kpoints_mode,
            kpoints_grid=kpoints_grid,
            kpoints_shift=kpoints_shift,
        )
Ejemplo n.º 52
0
    def convert_to_ieee(self, structure):
        """
        Given a structure associated with a tensor, attempts a
        calculation of the tensor in IEEE format according to
        the 1987 IEEE standards.

        Args:
            structure (Structure): a structure associated with the
                tensor to be converted to the IEEE standard
        """
        def get_uvec(vec):
            """ Gets a unit vector parallel to input vector"""
            return vec / np.linalg.norm(vec)

        # Check conventional setting:
        sga = SpacegroupAnalyzer(structure)
        dataset = sga.get_symmetry_dataset()
        trans_mat = dataset['transformation_matrix']
        conv_latt = Lattice(
            np.transpose(
                np.dot(np.transpose(structure.lattice.matrix),
                       np.linalg.inv(trans_mat))))
        xtal_sys = sga.get_crystal_system()

        vecs = conv_latt.matrix
        lengths = np.array(conv_latt.abc)
        angles = np.array(conv_latt.angles)
        a = b = c = None
        rotation = np.zeros((3, 3))

        # IEEE rules: a,b,c || x1,x2,x3
        if xtal_sys == "cubic":
            rotation = [vecs[i] / lengths[i] for i in range(3)]

        # IEEE rules: a=b in length; c,a || x3, x1
        elif xtal_sys == "tetragonal":
            rotation = np.array([
                vec / mag
                for (mag,
                     vec) in sorted(zip(lengths, vecs), key=lambda x: x[0])
            ])
            if abs(lengths[2] - lengths[1]) < abs(lengths[1] - lengths[0]):
                rotation[0], rotation[2] = rotation[2], rotation[0].copy()
            rotation[1] = get_uvec(np.cross(rotation[2], rotation[0]))

        # IEEE rules: c<a<b; c,a || x3,x1
        elif xtal_sys == "orthorhombic":
            rotation = [vec / mag for (mag, vec) in sorted(zip(lengths, vecs))]
            rotation = np.roll(rotation, 2, axis=0)

        # IEEE rules: c,a || x3,x1, c is threefold axis
        # Note this also includes rhombohedral crystal systems
        elif xtal_sys in ("trigonal", "hexagonal"):
            # find threefold axis:
            tf_index = np.argmin(abs(angles - 120.))
            non_tf_mask = np.logical_not(angles == angles[tf_index])
            rotation[2] = get_uvec(vecs[tf_index])
            rotation[0] = get_uvec(vecs[non_tf_mask][0])
            rotation[1] = get_uvec(np.cross(rotation[2], rotation[0]))

        # IEEE rules: b,c || x2,x3; alpha=beta=90, c<a
        elif xtal_sys == "monoclinic":
            # Find unique axis
            u_index = np.argmax(abs(angles - 90.))
            n_umask = np.logical_not(angles == angles[u_index])
            rotation[1] = get_uvec(vecs[u_index])
            # Shorter of remaining lattice vectors for c axis
            c = [
                vec / mag
                for (mag, vec) in sorted(zip(lengths[n_umask], vecs[n_umask]))
            ][0]
            rotation[2] = np.array(c)
            rotation[0] = np.cross(rotation[1], rotation[2])

        # IEEE rules: c || x3
        elif xtal_sys == "triclinic":
            rotation = [vec / mag for (mag, vec) in sorted(zip(lengths, vecs))]
            rotation = np.roll(rotation, 2, axis=0)
            rotation[1] = get_uvec(np.cross(rotation[2], rotation[1]))
            rotation[0] = np.cross(rotation[1], rotation[2])

        return self.rotate(rotation)
Ejemplo n.º 53
0
from crystal_toolkit.settings import SETTINGS
from crystal_toolkit.helpers.layouts import H1, H3, Container

# import for this example
from pymatgen.ext.matproj import MPRester
from pymatgen.analysis.phase_diagram import PhaseDiagram
from pymatgen.analysis.diffraction.xrd import XRDCalculator

# create Dash app as normal
app = dash.Dash(assets_folder=SETTINGS.ASSETS_PATH)

# create our crystal structure using pymatgen
from pymatgen.core.structure import Structure
from pymatgen.core.lattice import Lattice

structure = Structure(Lattice.cubic(4.2), ["Na", "K"],
                      [[0, 0, 0], [0.5, 0.5, 0.5]])

xrd_component = ctc.XRayDiffractionComponent(initial_structure=structure)

# example layout to demonstrate capabilities of component
my_layout = Container([
    H1("XRDComponent Example"),
    H3("Generated from Structure object"),
    xrd_component.layout(),
])

# as explained in "preamble" section in documentation
ctc.register_crystal_toolkit(app=app, layout=my_layout)

# allow app to be run using "python structure.py"
Ejemplo n.º 54
0
 def setUp(self):
     self.silicon = Structure(
         Lattice.from_lengths_and_angles([5.47, 5.47, 5.47],
                                         [90.0, 90.0, 90.0]),
         ["Si", "Si", "Si", "Si", "Si", "Si", "Si", "Si"],
         [[0.000000, 0.000000, 0.500000], [0.750000, 0.750000, 0.750000],
          [0.000000, 0.500000, 1.000000], [0.750000, 0.250000, 0.250000],
          [0.500000, 0.000000, 1.000000], [0.250000, 0.750000, 0.250000],
          [0.500000, 0.500000, 0.500000], [0.250000, 0.250000, 0.750000]],
         validate_proximity=False,
         to_unit_cell=False,
         coords_are_cartesian=False,
         site_properties=None)
     self.smi = StructureMotifInterstitial(
         self.silicon,
         "Si",
         motif_types=["tetrahedral", "octahedral"],
         op_threshs=[0.3, 0.5],
         dl=0.4,
         doverlap=1.0,
         facmaxdl=1.51)
     self.diamond = Structure(
         Lattice([[2.189, 0, 1.264], [0.73, 2.064, 1.264], [0, 0, 2.528]]),
         ["C0+", "C0+"], [[2.554, 1.806, 4.423], [0.365, 0.258, 0.632]],
         validate_proximity=False,
         to_unit_cell=False,
         coords_are_cartesian=True,
         site_properties=None)
     self.nacl = Structure(Lattice([[3.485, 0,
                                     2.012], [1.162, 3.286, 2.012],
                                    [0, 0, 4.025]]), ["Na1+", "Cl1-"],
                           [[0, 0, 0], [2.324, 1.643, 4.025]],
                           validate_proximity=False,
                           to_unit_cell=False,
                           coords_are_cartesian=True,
                           site_properties=None)
     self.cscl = Structure(Lattice([[4.209, 0, 0], [0, 4.209, 0],
                                    [0, 0, 4.209]]), ["Cl1-", "Cs1+"],
                           [[2.105, 2.105, 2.105], [0, 0, 0]],
                           validate_proximity=False,
                           to_unit_cell=False,
                           coords_are_cartesian=True,
                           site_properties=None)
     self.square_pyramid = Structure(Lattice([[100, 0, 0], [0, 100, 0],
                                              [0, 0, 100]]),
                                     ["C", "C", "C", "C", "C", "C"],
                                     [[0, 0, 0], [1, 0, 0], [-1, 0, 0],
                                      [0, 1, 0], [0, -1, 0], [0, 0, 1]],
                                     validate_proximity=False,
                                     to_unit_cell=False,
                                     coords_are_cartesian=True,
                                     site_properties=None)
     self.trigonal_bipyramid = Structure(
         Lattice([[100, 0, 0], [0, 100, 0],
                  [0, 0, 100]]), ["P", "Cl", "Cl", "Cl", "Cl", "Cl"],
         [[0, 0, 0], [0, 0, 2.14], [0, 2.02, 0], [1.74937, -1.01, 0],
          [-1.74937, -1.01, 0], [0, 0, -2.14]],
         validate_proximity=False,
         to_unit_cell=False,
         coords_are_cartesian=True,
         site_properties=None)
Ejemplo n.º 55
0
    if not os.path.exists(file_dir):
        os.makedirs(file_dir)
    else:
        print(f'{file_dir} already exists')

# INCAR
# creating Incar object
    incar = Incar(incar_dict)
    # writing INCAR in designated path
    incar.write_file(file_dir + 'INCAR')

    # POSCAR

    a = a_min + interval * i

    new_lattice = Lattice([[a, 0, 0], [0, a, 0], [0, 0, a]])
    # creating new structure
    struct.lattice = new_lattice
    #     new_struct_dict = struct.as_dict()
    #     print(new_struct_dict['lattice']['a'])
    poscar = Poscar(struct)
    poscar.write_file(file_dir + 'POSCAR')

    # KPOINTS

    k = 6
    kpoints = Kpoints.gamma_automatic(kpts=(k, k, k), shift=(0.0, 0.0, 0.0))

    kpoints.write_file(file_dir + 'KPOINTS')

    # POTCAR
Ejemplo n.º 56
0
def read_data(data=None,ff=None):
    pot_file=open(ff,"r")
    lines = pot_file.read().splitlines()
    symb=[]
    count=0
    from pymatgen.core.periodic_table import Element
    for i, line in enumerate(lines):
        if "pair_coeff" in line.split():
               sp=line.split()
               #print "spsplit",sp,os.getcwd()
               for el in sp:
                   try:
                    if Element(el):
                     #if el=='M':
                   #    el='Mo'
                   #count=count+1
                   #if count >4:
                      symb.append(Element(el))
                   except:
                      pass
    f=open(data,"r")
    lines = f.read().splitlines()
    for i, line in enumerate(lines):
        if "atoms" in line.split():
             natoms=int(line.split()[0])
        if "types" in line.split():
             #print line
             ntypes=int(line.split()[0])
        if "xlo" in line.split():
             xlo=float(line.split()[0])
             xhi=float(line.split()[1])
        if "ylo" in line.split():
             ylo=float(line.split()[0])
             yhi=float(line.split()[1])
        if "zlo" in line.split():
             zlo=float(line.split()[0])
             zhi=float(line.split()[1])
        if "xy" in line.split():
             xy=float(line.split()[0])
             xz=float(line.split()[1])
             yz=float(line.split()[2])
    if len(symb) != ntypes:
        print ("Something wrong in atom type assignment",len(symb),ntypes)
        sys.exit()
    lat=Lattice([[ xhi-xlo,0.0,0.0],[xy,yhi-ylo,0.0],[xz,yz,zhi-zlo]])
    typ= [] #np.empty((natoms),dtype="S20")
    x=np.zeros((natoms))
    y=np.zeros((natoms))
    z=np.zeros((natoms))
    q=np.zeros((natoms))
    coords = list()
    for i, line in enumerate(lines):
        if "Atoms" in line.split():
           for j in range(0,natoms):
                 typ.append(symb[int(((lines[i+j+2]).split()[1]))-1])
                 q[j]=(lines[i+j+2]).split()[2]
                 x[j]=(lines[i+j+2]).split()[3]
                 y[j]=(lines[i+j+2]).split()[4]
                 z[j]=(lines[i+j+2]).split()[5]
                 coords.append([x[j],y[j],z[j]])
    f.close()
    #print "info",len(typ),len(coords)
    pot_file.close()
    struct=Structure(lat,typ,coords,coords_are_cartesian=True)
    finder = SpacegroupAnalyzer(struct)
    num=finder.get_space_group_symbol()
    return struct
Ejemplo n.º 57
0
    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()
Ejemplo n.º 58
0
 def test_tune_for_gamma(self):
     lattice = Lattice([[4.692882, -8.12831, 0.0], [4.692882, 8.12831, 0.0],
                        [0.0, 0.0, 10.03391]])
     epsilon = 10.0 * np.identity(3)
     gamma = tune_for_gamma(lattice, epsilon)
     self.assertAlmostEqual(gamma, 0.19357221)
Ejemplo n.º 59
0
    def get_ieee_rotation(structure, refine_rotation=True):
        """
        Given a structure associated with a tensor, determines
        the rotation matrix for IEEE conversion according to
        the 1987 IEEE standards.

        Args:
            structure (Structure): a structure associated with the
                tensor to be converted to the IEEE standard
            refine_rotation (bool): whether to refine the rotation
                using SquareTensor.refine_rotation
        """
        # Check conventional setting:
        sga = SpacegroupAnalyzer(structure)
        dataset = sga.get_symmetry_dataset()
        trans_mat = dataset["transformation_matrix"]
        conv_latt = Lattice(
            np.transpose(
                np.dot(np.transpose(structure.lattice.matrix),
                       np.linalg.inv(trans_mat))))
        xtal_sys = sga.get_crystal_system()

        vecs = conv_latt.matrix
        lengths = np.array(conv_latt.abc)
        angles = np.array(conv_latt.angles)
        rotation = np.zeros((3, 3))

        # IEEE rules: a,b,c || x1,x2,x3
        if xtal_sys == "cubic":
            rotation = [vecs[i] / lengths[i] for i in range(3)]

        # IEEE rules: a=b in length; c,a || x3, x1
        elif xtal_sys == "tetragonal":
            rotation = np.array([
                vec / mag
                for (mag,
                     vec) in sorted(zip(lengths, vecs), key=lambda x: x[0])
            ])
            if abs(lengths[2] - lengths[1]) < abs(lengths[1] - lengths[0]):
                rotation[0], rotation[2] = rotation[2], rotation[0].copy()
            rotation[1] = get_uvec(np.cross(rotation[2], rotation[0]))

        # IEEE rules: c<a<b; c,a || x3,x1
        elif xtal_sys == "orthorhombic":
            rotation = [vec / mag for (mag, vec) in sorted(zip(lengths, vecs))]
            rotation = np.roll(rotation, 2, axis=0)

        # IEEE rules: c,a || x3,x1, c is threefold axis
        # Note this also includes rhombohedral crystal systems
        elif xtal_sys in ("trigonal", "hexagonal"):
            # find threefold axis:
            tf_index = np.argmin(abs(angles - 120.0))
            non_tf_mask = np.logical_not(angles == angles[tf_index])
            rotation[2] = get_uvec(vecs[tf_index])
            rotation[0] = get_uvec(vecs[non_tf_mask][0])
            rotation[1] = get_uvec(np.cross(rotation[2], rotation[0]))

        # IEEE rules: b,c || x2,x3; alpha=beta=90, c<a
        elif xtal_sys == "monoclinic":
            # Find unique axis
            u_index = np.argmax(abs(angles - 90.0))
            n_umask = np.logical_not(angles == angles[u_index])
            rotation[1] = get_uvec(vecs[u_index])
            # Shorter of remaining lattice vectors for c axis
            c = [
                vec / mag
                for (mag, vec) in sorted(zip(lengths[n_umask], vecs[n_umask]))
            ][0]
            rotation[2] = np.array(c)
            rotation[0] = np.cross(rotation[1], rotation[2])

        # IEEE rules: c || x3, x2 normal to ac plane
        elif xtal_sys == "triclinic":
            rotation = [vec / mag for (mag, vec) in sorted(zip(lengths, vecs))]
            rotation[1] = get_uvec(np.cross(rotation[2], rotation[0]))
            rotation[0] = np.cross(rotation[1], rotation[2])

        rotation = SquareTensor(rotation)
        if refine_rotation:
            rotation = rotation.refine_rotation()

        return rotation
Ejemplo n.º 60
0
def get_voronoi_nodes(structure, rad_dict=None, probe_rad=0.1):
    """
    Analyze the void space in the input structure using voronoi decomposition
    Calls Zeo++ for Voronoi decomposition.

    Args:
        structure: pymatgen.core.structure.Structure
        rad_dict (optional): Dictionary of radii of elements in structure.
            If not given, Zeo++ default values are used.
            Note: Zeo++ uses atomic radii of elements.
            For ionic structures, pass rad_dict with ionic radii
        probe_rad (optional): Sampling probe radius in Angstroms. Default is
            0.1 A

    Returns:
        voronoi nodes as pymatgen.core.structure.Strucutre within the
        unit cell defined by the lattice of input structure
        voronoi face centers as pymatgen.core.structure.Strucutre within the
        unit cell defined by the lattice of input structure
    """

    with ScratchDir("."):
        name = "temp_zeo1"
        zeo_inp_filename = name + ".cssr"
        ZeoCssr(structure).write_file(zeo_inp_filename)
        rad_file = None
        rad_flag = False

        if rad_dict:
            rad_file = name + ".rad"
            rad_flag = True
            with open(rad_file, "w+") as fp:
                for el in rad_dict.keys():
                    fp.write(f"{el} {rad_dict[el].real}\n")

        atmnet = AtomNetwork.read_from_CSSR(zeo_inp_filename, rad_flag=rad_flag, rad_file=rad_file)
        (
            vornet,
            vor_edge_centers,
            vor_face_centers,
        ) = atmnet.perform_voronoi_decomposition()
        vornet.analyze_writeto_XYZ(name, probe_rad, atmnet)
        voro_out_filename = name + "_voro.xyz"
        voro_node_mol = ZeoVoronoiXYZ.from_file(voro_out_filename).molecule

    species = ["X"] * len(voro_node_mol.sites)
    coords = []
    prop = []
    for site in voro_node_mol.sites:
        coords.append(list(site.coords))
        prop.append(site.properties["voronoi_radius"])

    lattice = Lattice.from_parameters(*structure.lattice.parameters)
    vor_node_struct = Structure(
        lattice,
        species,
        coords,
        coords_are_cartesian=True,
        to_unit_cell=True,
        site_properties={"voronoi_radius": prop},
    )

    # PMG-Zeo c<->a transformation for voronoi face centers
    rot_face_centers = [(center[1], center[2], center[0]) for center in vor_face_centers]
    rot_edge_centers = [(center[1], center[2], center[0]) for center in vor_edge_centers]

    species = ["X"] * len(rot_face_centers)
    prop = [0.0] * len(rot_face_centers)  # Vor radius not evaluated for fc
    vor_facecenter_struct = Structure(
        lattice,
        species,
        rot_face_centers,
        coords_are_cartesian=True,
        to_unit_cell=True,
        site_properties={"voronoi_radius": prop},
    )

    species = ["X"] * len(rot_edge_centers)
    prop = [0.0] * len(rot_edge_centers)  # Vor radius not evaluated for fc
    vor_edgecenter_struct = Structure(
        lattice,
        species,
        rot_edge_centers,
        coords_are_cartesian=True,
        to_unit_cell=True,
        site_properties={"voronoi_radius": prop},
    )

    return vor_node_struct, vor_edgecenter_struct, vor_facecenter_struct