예제 #1
0
def setup_grid(iscomplex=False, halfN=(2, 2, 2)):
    """Create a grid object for interpolating."""
    rlat = s.Reciprocal((1, 1, 1), (90, 90, 90))
    bz = s.BrillouinZone(rlat)
    max_volume = rlat.volume/(8*np.prod(halfN))
    if iscomplex:
        bzg = s.BZTrellisQcc(bz, max_volume)
    else:
        bzg = s.BZTrellisQdd(bz, max_volume)
    return bzg
예제 #2
0
 def test_b_isinside_hexagonal(self):
     d, r = make_dr(3, 3, 9, np.pi / 2, np.pi / 2, np.pi * 2 / 3)
     bz = s.BrillouinZone(r)
     # Q = (np.random.rand(1000, 3)-0.5) * 2 # this is a uniform distribution over [-1, 1)
     x = np.linspace(-1, 1, 100)
     X, Y, Z = np.meshgrid(x, x, 0)
     Q = np.stack((X.flatten(), Y.flatten(), Z.flatten()), axis=-1)
     Qin = bz.isinside(Q)
     B = r.B
     X = np.stack([np.matmul(B, v) for v in Q[Qin, :]])
예제 #3
0
    def test_a_init_hexagonal(self):
        # instantiate a hexagonal lattice and its reciprocal lattice, still hexagonal
        d, r = make_dr(3, 3, 9, np.pi / 2, np.pi / 2, np.pi * 2 / 3)
        # creating a BrillouinZone objects requires that we pass the reciprocal
        # lattice object, and passing a Direct object is a TypeError:
        with self.assertRaises(TypeError):
            s.BrillouinZone(d)
        # its first Brillouin zone is a cube in reciprocal space
        bz = s.BrillouinZone(r)
        # with eight faces, defined by: (̄100),(̄110),(0̄10),(001),(001),(010),(1̄10),(100)
        p = bz.points
        self.assertEqual(p.ndim, 2)
        self.assertEqual(p.shape[0],
                         8)  # and there is one for each of the eight faces
        self.assertEqual(p.shape[1], 3)  # the face vectors are 3-vectors
        n = bz.normals
        self.assertEqual(n.ndim, 2)
        self.assertEqual(n.shape[0],
                         8)  # and there is one for each of the six faces
        self.assertEqual(n.shape[1], 3)  # the face normals are 3-vectors
        n_dot_p = np.array(
            [np.dot(x / norm(x), y / norm(y)) for x, y in zip(n, p)])
        self.assertTrue(np.allclose(n_dot_p, 1.))

        expected = np.array([[-1, 0, 0], [-1, 1, 0], [0, -1, 0], [0, 0, -1],
                             [0, 0, 1], [0, 1, 0], [1, -1, 0], [1, 0, 0]])
        self.assertTrue(vector_lists_match(p, expected / 2))
        n_compare = np.array([x / np.max(np.abs(x)) for x in n])
        self.assertTrue(vector_lists_match(n_compare, expected))
        self.assertTrue(np.all(bz.isinside(expected / 2)))
        #
        # the vertices of the first Brillouin zone are the 12 corners of the hexagonal-prism:
        expected = np.array([[-4, 2, -3], [-2, -2, -3], [-4, 2, 3], [
            -2, -2, 3
        ], [-2, 4, -3], [-2, 4, 3], [2, -4, -3], [2, -4, 3], [2, 2, -3],
                             [4, -2, -3], [2, 2, 3], [4, -2, 3]]) / 6
        verts = bz.vertices
        self.assertEqual(verts.ndim, 2)
        self.assertEqual(verts.shape[0], 12)
        self.assertEqual(verts.shape[1], 3)
        self.assertTrue(vector_lists_match(verts, expected))
        self.assertTrue(np.all(bz.isinside(expected)))
예제 #4
0
 def test_c_moveinto_hexagonal(self):
     d, r = make_dr(3, 3, 9, np.pi / 2, np.pi / 2, np.pi * 2 / 3)
     bz = s.BrillouinZone(r)
     Q = (np.random.rand(100, 3) -
          0.5) * 10  # this is a uniform distribution over [-5, 5)
     if np.all(bz.isinside(Q)):  # this is vanishingly-unlikely
         Q += 100.0
     self.assertFalse(np.all(bz.isinside(Q)))
     (q, tau) = bz.moveinto(Q)
     self.assertTrue(np.all(bz.isinside(q)))
     self.assertAlmostEqual(np.abs(Q - q - tau).sum(), 0)
예제 #5
0
    def test_a_init_unit_cube(self):
        # instantiate a cubic lattice with unit-length vectors
        # and its reciprocal lattice, still cubic with 2π-length vectors
        d, r = make_dr(1, 1, 1)
        # creating a BrillouinZone objects requires that we pass the reciprocal
        # lattice object, and passing a Direct object is a TypeError:
        with self.assertRaises(TypeError):
            s.BrillouinZone(d)
        # its first Brillouin zone is a cube in reciprocal space
        bz = s.BrillouinZone(r)
        # with six faces, defined by: (00̄1),(0̄10),(̄100),(100),(010),(001)
        p = bz.points
        self.assertEqual(p.ndim, 2)
        self.assertEqual(p.shape[0],
                         6)  # and there is one for each of the six faces
        self.assertEqual(p.shape[1], 3)  # the face vectors are 3-vectors
        n = bz.normals
        self.assertEqual(n.ndim, 2)
        self.assertEqual(n.shape[0],
                         6)  # and there is one for each of the six faces
        self.assertEqual(n.shape[1], 3)  # the face normals are 3-vectors
        n_dot_p = np.array(
            [np.dot(x / norm(x), y / norm(y)) for x, y in zip(n, p)])
        self.assertTrue((n_dot_p == 1.0).all())

        expected = np.array([[-1, 0, 0], [0, -1, 0], [0, 0, -1], [0, 0, 1],
                             [0, 1, 0], [1, 0, 0]])
        self.assertTrue(vector_lists_match(p, expected / 2))
        self.assertTrue(
            vector_lists_match(np.array([x / norm(x) for x in n]), expected))
        #
        # the vertices of the first Brillouin zone are the 8 corners of the 1/2-unit cube:
        expected = np.array([[-1, -1, -1], [-1, -1, 1], [-1, 1, -1], [
            -1, 1, 1
        ], [1, -1, -1], [1, -1, 1], [1, 1, -1], [1, 1, 1]]) / 2
        verts = bz.vertices
        self.assertEqual(verts.ndim, 2)
        self.assertEqual(verts.shape[0], 8)
        self.assertEqual(verts.shape[1], 3)
        self.assertTrue(vector_lists_match(verts, expected))
예제 #6
0
 def test_i_iron_self_consistency(self):
     """Test with data as iron spinwaves, but test only *at* grid points."""
     d = s.Direct((2.87, 2.87, 2.87), np.pi/2*np.array((1, 1, 1)), "Im-3m")
     r = d.star
     bz = s.BrillouinZone(r) # constructs an irreducible Bz by default
     bzg = s.BZTrellisQdc(bz, 0.125)
     Q = bzg.rlu
     bzg.fill(fe_dispersion(Q), (1,), bzg.rlu, (0,3))
     # The irreducible interpolation must be used here
     # since the Brillouin zone is an *irreducible* Brillouin zone!!
     intres, _ = bzg.ir_interpolate_at(Q, False, False)
     antres = fe_dispersion(Q)
     self.assertTrue(np.isclose(np.squeeze(intres), antres).all())
예제 #7
0
    def test_aflow_crystaldatabase(self):
        tested = 0
        failed = 0
        errored = 0
        failed_afl = []
        failed_ratio = []
        errored_afl = []
        errored_arg = []
        hall_groups_passed = np.zeros(530, dtype='int')
        hall_groups_failed = np.zeros(530, dtype='int')
        for afl in get_aflow_lattices():
            # afl == [hall_number, basis_vector_lengths, basis_vector_angles, Hall_symbol]
            dlat = s.Direct(afl[1], afl[2], afl[3])
            tested += 1
            try:
                bz = s.BrillouinZone(dlat.star)
                vol_bz = bz.polyhedron.volume
                vol_ir = bz.ir_polyhedron.volume
                if not np.isclose(vol_ir,
                                  vol_bz / s.PointSymmetry(afl[0]).size):
                    failed += 1
                    failed_afl.append(afl)
                    failed_ratio.append(vol_ir / vol_bz *
                                        s.PointSymmetry(afl[0]).size)
                    hall_groups_failed[afl[0] - 1] += 1
                else:
                    hall_groups_passed[afl[0] - 1] += 1
            except Exception as err:
                errored += 1
                errored_afl.append(afl)
                errored_arg.append(err.args)
        if failed > 0:
            print("\nFailed to find correct irreducible Brillouin zone for",
                  failed, "out of", tested, "lattices")
            for file, rat in zip(failed_afl, failed_ratio):
                print(file, rat)
        if errored > 0:
            print("\nException raised for", errored, "out of", tested,
                  "lattices")
            for file, arg in zip(errored_afl, errored_arg):
                print(file, arg)
        print("\nHall groups passed (total =", hall_groups_passed.sum(), "of",
              tested, "tested)")
        encoded_hgp = [n2chr(x) for x in hall_groups_passed]
        for x in [encoded_hgp[i * 53:(i + 1) * 53] for i in range(10)]:
            print(''.join(x))

        self.assertTrue(failed == 0)
        self.assertTrue(errored == 0)
예제 #8
0
 def test_b_isinside_unit_cube(self):
     d, r = make_dr(1, 1, 1)
     bz = s.BrillouinZone(r)
     # the first Brillouin zone of this unit-cube is bounded by
     # {(00̄1),(0̄10),(̄100),(100),(010),(001)}/2
     face_centres = np.array([[-1, 0, 0], [0, -1, 0], [0, 0, -1], [0, 0, 1],
                              [0, 1, 0], [1, 0, 0]],
                             dtype='double') / 2
     self.assertTrue(np.all(bz.isinside(face_centres)))
     # including the vertices (since they are the corners of the zone)
     corners = np.array([[-1, -1, -1], [-1, -1, 1], [-1, 1, -1], [-1, 1, 1],
                         [1, -1, -1], [1, -1, 1], [1, 1, -1], [1, 1, 1]],
                        dtype='double') / 2
     self.assertTrue(np.all(bz.isinside(corners)))
     # so all points with h, k, and l in the range [-0.5, 0.5] are in the zone
     Q = np.random.rand(
         100, 3
     ) - 0.5  # this is a uniform distribution over [-0.5, 0.5) -- close enough
     self.assertTrue(np.all(bz.isinside(Q)))
     self.assertFalse(np.all(bz.isinside(Q + 5)))
예제 #9
0
    def test_d_all_hallgroups(self):
        tested = 0
        failed = 0
        errored = 0
        failed_spg = []
        failed_ptg = []
        failed_lat = []
        failed_ratio = []
        errored_spg = []
        errored_ptg = []
        errored_lat = []
        errored_arg = []
        print()
        for i in range(1, 531):
            spacegroup = s.Spacegroup(i)
            pointgroup = s.Pointgroup(spacegroup.pointgroup_number)
            a = 5
            b = 5
            c = 5
            al = np.pi / 2
            be = np.pi / 2
            ga = np.pi / 2
            # nothing to do for cubic spacegroups
            if 'hexa' in pointgroup.holohedry:
                ga = 2 * np.pi / 3
            elif 'trig' in pointgroup.holohedry:
                if 'R' in spacegroup.choice:
                    al = be = ga = np.pi / 3
                else:  # 'H' setting or normally hexagonal
                    c = 10
                    ga = 2 * np.pi / 3
            elif 'tetr' in pointgroup.holohedry:
                c = 10
            elif 'orth' in pointgroup.holohedry:
                axperm = spacegroup.choice.replace('-', '')
                if 'cab' in axperm:
                    c = 5
                    a = 10
                    b = 15
                elif 'cba' in axperm:
                    c = 5
                    b = 10
                    a = 15
                elif 'bca' in axperm:
                    b = 5
                    c = 10
                    a = 15
                elif 'bac' in axperm:
                    b = 5
                    a = 10
                    c = 15
                elif 'acb' in axperm:
                    a = 5
                    c = 10
                    b = 15
                else:
                    a = 5
                    b = 10
                    c = 15
            elif 'mono' in pointgroup.holohedry:
                # continue # skip all monoclinic pointgroups for now
                if 'a' in spacegroup.choice:
                    a = 10
                    al = np.pi / 180 * (91 + 19 * np.random.rand())
                elif 'b' in spacegroup.choice:
                    b = 10
                    be = np.pi / 180 * (91 + 19 * np.random.rand())
                elif 'c' in spacegroup.choice:
                    c = 10
                    ga = np.pi / 180 * (91 + 19 * np.random.rand())
                else:
                    print("Monoclinic without 'a', 'b', or 'c' choice?? ",
                          spacegroup.choice)
                    continue
            elif 'tric' in pointgroup.holohedry:
                a = 5
                b = 10
                c = 15
                al = np.pi / 3 * (1 + np.random.rand())
                be = np.pi / 3 * (1 + np.random.rand())
                ga = np.pi / 3 * (1 + np.random.rand())

            dlat, rlat = make_dr(a, b, c, al, be, ga, i)

            # print("Hall ", i, " ", dlat)
            # print(spacegroup,pointgroup)
            try:
                bz = s.BrillouinZone(rlat)
                vol_bz = bz.polyhedron.volume
                vol_ir = bz.ir_polyhedron.volume
                tested += 1
                if not np.isclose(vol_ir, vol_bz / s.PointSymmetry(i).size):
                    # print(dlat,": ",vol_ir," != ",vol_bz/s.PointSymmetry(i).size)
                    failed += 1
                    failed_spg.append(spacegroup)
                    failed_ptg.append(pointgroup)
                    failed_lat.append(dlat)
                    failed_ratio.append(vol_ir / vol_bz *
                                        s.PointSymmetry(i).size)
            except Exception as err:
                errored += 1
                errored_spg.append(spacegroup)
                errored_ptg.append(pointgroup)
                errored_lat.append(dlat)
                errored_arg.append(err.args)

        if failed > 0:
            print("\nFailed to find irreducible Brillouin zone for", failed,
                  "out of", tested, "(max 530) Hall groups")
            for spg, ptg, lat, rat in zip(failed_spg, failed_ptg, failed_lat,
                                          failed_ratio):
                print(spg, ptg, lat, rat)
        if errored > 0:
            print("\nException raised for", errored, "out of", tested,
                  "(max 530) Hall Groups")
            for spg, ptg, lat, arg in zip(errored_spg, errored_ptg,
                                          errored_lat, errored_arg):
                print(arg)
                print(spg, ptg, lat)
        self.assertTrue(errored == 0)
예제 #10
0
    def test_nacl(self):
        # load numpy arrays from the compressed binary pack
        nacl = getLoad(np.load,'test_5_gamma.npz')
        # construct the NaCl direct lattice
        basis_vec = nacl['basis_vectors']
        atom_pos = nacl['atom_positions']
        atom_idx = nacl['atom_index']
        dlat = s.Direct(basis_vec, atom_pos, atom_idx, 'P1')
        dlat.spacegroup = s.Symmetry(nacl['spacegroup_mat'],nacl['spacegroup_vec'])
        # use it to construct an irreducible Brillouin zone
        bz = s.BrillouinZone(dlat.star)
        # and use that to produce a hybrid interpolation grid
        # with parameters stored in the binary pack
        max_volume = float(nacl['grid_max_volume'])
        always_triangulate = bool(nacl['grid_always_triangulate'])
        grid = s.BZTrellisQdc(bz, max_volume, always_triangulate)

        # verify the stored grid points to ensure we have the same irreducible
        # wedge and grid
        self.assertTrue(np.allclose(grid.rlu, nacl['grid_rlu']))
        # insert the Euphonic-derived eigenvalues and eigenvectors for the grid
        # points, which are used in the interpolation
        grid.fill(
            nacl['grid_values'], nacl['grid_values_elements'], nacl['grid_values_weights'],
            nacl['grid_vectors'], nacl['grid_vectors_elements'], nacl['grid_vectors_weights'],
            bool(nacl['grid_sort']))

        # the fourth grid point is inside of the irreducible volume, which
        # ensures we avoid degeneracies
        q_ir = grid.rlu[4:5]
        # find all symmetry equivalent q within the first Brillouin zone by
        # applying each pointgroup operator to q_ir to find q_nu = R^T_nu q_ir
        q_nu = np.einsum('xji,aj->xi', dlat.pointgroup.W, q_ir)
        
        # The std::sort algorithm does not provide the same pointgroup sorting
        # on all systems, but there should be a permutation mapping:
        perm = np.array([np.squeeze(np.argwhere(np.all(np.isclose(q_nu,x),axis=1))) for x in nacl['q_nu']])
        # The permutation must be complete and unique
        self.assertTrue(np.unique(perm).size == q_nu.shape[0])
        # And the permuted q_nu must match the stored q_nu
        q_nu = q_nu[perm]
        self.assertTrue(np.allclose(q_nu, nacl['q_nu']))      
        
        # Use the grid to interpolate at each q_nu:
        br_val, br_vec = grid.ir_interpolate_at(q_nu)
        br_val = np.squeeze(br_val)
        
        # verify that q_ir does not have degeneracies:
        self.assertFalse(np.any(np.isclose(np.diff(br_val, axis=1), 0.)))
        # and that the 'interpolated' eigenvalues are identical for all q_nu
        self.assertTrue(np.allclose(np.diff(br_val, axis=0), 0.))
        # plus that the interpolated eigenvalues match the store Euphonic eigenvalues
        self.assertTrue(np.allclose(br_val, nacl['euphonic_values']))
        
        # convert the eigenvalues into the same cartesian coordinate system
        # used by Euphonic
        br_vec = np.einsum('ba,ijkb->ijka', basis_vec, br_vec)
        # load the Euphonic calculated eigenvectors
        eu_vec = nacl['euphonic_vectors']
        # The 'interpolated' eigenvectors and the Euphonic eigenvectors should
        # only be equivalent up to an overall phase factor, so find it:
        antiphase = np.exp(-1J*np.angle(np.einsum('qmij,qmij->qm', np.conj(eu_vec), br_vec)))
        # and remove the phase from the interpolated eigenvectors
        br_vec = np.einsum('ab,abij->abij', antiphase, br_vec)
        # now all eigenvectors must match
        self.assertTrue(np.allclose(br_vec, eu_vec))