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
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, :]])
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)))
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)
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))
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())
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)
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)))
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)
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))