def test_phase(self): a, b, c, alpha, beta, gamma = 5, 6, 7, np.pi / 2, np.pi / 3, np.pi / 4 inv_constants = crystal.reciprocal(a, b, c, alpha, beta, gamma) a_, b_, c_, alpha_, beta_, gamma_ = inv_constants A = crystal.cartesian(a, b, c, alpha, beta, gamma) B = crystal.cartesian(a_, b_, c_, alpha_, beta_, gamma_) R = crystal.cartesian_rotation(a, b, c, alpha, beta, gamma) nu, nv, nw = 5, 3, 4 Rx, Ry, Rz = space.cell(nu, nv, nw, A) atm = np.array(['Fe', 'Co']) u, v, w = np.array([0, 0.2]), np.array([0, 0.3]), np.array([0, 0.4]) ux, uy, uz = crystal.transform(u, v, w, A) rx, ry, rz, atms = space.real(ux, uy, uz, Rx, Ry, Rz, atm) h, k, l = np.array([-7, -3]), np.array([2, 2]), np.array([2, 5]) Qh, Qk, Ql = crystal.vector(h, k, l, B) Qx, Qy, Qz = crystal.transform(Qh, Qk, Ql, R) phase_factor = scattering.phase(Qx, Qy, Qz, rx, ry, rz) np.testing.assert_array_almost_equal(phase_factor, 1 + 0j)
def test_dipole_dipole_interaction_potential(self): nu, nv, nw, n_atm = 3, 4, 5, 2 n = nu * nv * nw * n_atm M = 2 Sx = np.random.random((nu, nv, nw, n_atm, M)) Sy = np.random.random((nu, nv, nw, n_atm, M)) Sz = np.random.random((nu, nv, nw, n_atm, M)) u = np.array([0.2, 0.3]) v = np.array([0.5, 0.4]) w = np.array([0.7, 0.2]) atm = np.array(['', '']) a, b, c, alpha, beta, gamma = 5, 6, 7, np.pi / 2, np.pi / 2, 2 * np.pi / 3 A = crystal.cartesian(a, b, c, alpha, beta, gamma) B = crystal.cartesian(*crystal.reciprocal(a, b, c, alpha, beta, gamma)) R = crystal.cartesian_rotation(a, b, c, alpha, beta, gamma) Rx, Ry, Rz = space.cell(nu, nv, nw, A) ux, uy, uz = crystal.transform(u, v, w, A) rx, ry, rz, atms = space.real(ux, uy, uz, Rx, Ry, Rz, atm) i, j = np.triu_indices(n) Qijm = np.zeros((n, n, 6)) Qijkl = np.zeros((n, n, 3, 3)) Q = interaction.dipole_dipole_matrix(rx, ry, rz, nu, nv, nw, n_atm, A, B, R) Qijm[i, j, :] = Q Qijm[j, i, :] = Qijm[i, j, :] Qijkl[:, :, 0, 0] = Qijm[:, :, 0] Qijkl[:, :, 1, 1] = Qijm[:, :, 1] Qijkl[:, :, 2, 2] = Qijm[:, :, 2] Qijkl[:, :, 1, 2] = Qijkl[:, :, 2, 1] = Qijm[:, :, 3] Qijkl[:, :, 0, 2] = Qijkl[:, :, 2, 0] = Qijm[:, :, 4] Qijkl[:, :, 0, 1] = Qijkl[:, :, 1, 0] = Qijm[:, :, 5] p0 = simulation.dipole_dipole_interaction_potential(Sx, Sy, Sz, Q) Sx, Sy, Sz = Sx.reshape(n, M), Sy.reshape(n, M), Sz.reshape(n, M) S = np.column_stack((Sx, Sy, Sz)).reshape(-1, 3, M) p = np.einsum('ijkl,jlm->ijklm', Qijkl, S).sum(axis=1) np.testing.assert_array_almost_equal(p, p0)
def real_space_coordinate_transform(self, u, v, w, atm, A, nu, nv, nw): ux, uy, uz = crystal.transform(u, v, w, A) ix, iy, iz = space.cell(nu, nv, nw, A) rx, ry, rz, atms = space.real(ux, uy, uz, ix, iy, iz, atm) return ux, uy, uz, rx, ry, rz, atms
def reciprocal_space_coordinate_transform(self, h, k, l, B, R): Qh, Qk, Ql = crystal.vector(h, k, l, B) Qx, Qy, Qz = crystal.transform(Qh, Qk, Ql, R) Qx_norm, Qy_norm, Qz_norm, Q = space.unit(Qx, Qy, Qz) return Qx, Qy, Qz, Qx_norm, Qy_norm, Qz_norm, Q
def spatial_wavevector(nu, nv, nw, n_atm, B, R): mu = (nu+1)//2 mv = (nv+1)//2 mw = (nw+1)//2 ku = 2*np.pi*np.arange(mu)/nu kv = 2*np.pi*np.concatenate((np.arange(mv),np.arange(-mv+1,0)))/nv kw = 2*np.pi*np.concatenate((np.arange(mw),np.arange(-mw+1,0)))/nw ku, kv, kw = np.meshgrid(ku, kv, kw, indexing='ij') ku, kv, kw = ku.flatten(), kv.flatten(), kw.flatten() ku, kv, kw = np.delete(ku, 0), np.delete(kv, 0), np.delete(kw, 0) Gx, Gy, Gz = crystal.transform(ku, kv, kw, B) Gx, Gy, Gz = crystal.transform(Gx, Gy, Gz, R) return Gx, Gy, Gz
def test_transform(self): u, v, w = np.array([1, 1, 0.]), np.array([-1, 1, 2.]), np.array([2, -2, 2.]) u /= np.linalg.norm(u) v /= np.linalg.norm(v) w /= np.linalg.norm(w) R = np.stack((u, v, w)).T x = np.array([3.5, 4, -1.2]) y = crystal.transform(x[0], x[1], x[2], R) np.testing.assert_array_almost_equal(y, np.dot(R, x))
def test_intensity(self): a, b, c, alpha, beta, gamma = 5, 6, 7, np.pi / 2, np.pi / 3, np.pi / 4 inv_constants = crystal.reciprocal(a, b, c, alpha, beta, gamma) a_, b_, c_, alpha_, beta_, gamma_ = inv_constants h_range, nh = [-1, 1], 5 k_range, nk = [0, 2], 11 l_range, nl = [-1, 0], 5 nu, nv, nw, n_atm = 2, 5, 4, 2 u = np.array([0.2, 0.1]) v = np.array([0.3, 0.4]) w = np.array([0.4, 0.5]) atm = np.array(['Fe', 'Mn']) occupancy = np.array([0.75, 0.5]) U11 = np.array([0.5, 0.3]) U22 = np.array([0.6, 0.4]) U33 = np.array([0.4, 0.6]) U23 = np.array([0.05, -0.03]) U13 = np.array([-0.04, 0.02]) U12 = np.array([0.03, -0.02]) T = space.debye_waller(h_range, k_range, l_range, nh, nk, nl, U11, U22, U33, U23, U13, U12, a_, b_, c_) A = crystal.cartesian(a, b, c, alpha, beta, gamma) B = crystal.cartesian(a_, b_, c_, alpha_, beta_, gamma_) R = crystal.cartesian_rotation(a, b, c, alpha, beta, gamma) A_r = occupational.composition(nu, nv, nw, n_atm, value=occupancy) index_parameters = space.mapping(h_range, k_range, l_range, nh, nk, nl, nu, nv, nw) h, k, l, H, K, L, indices, inverses, operators = index_parameters Qh, Qk, Ql = crystal.vector(h, k, l, B) Qx, Qy, Qz = crystal.transform(Qh, Qk, Ql, R) Qx_norm, Qy_norm, Qz_norm, Q = space.unit(Qx, Qy, Qz) ux, uy, uz = crystal.transform(u, v, w, A) ix, iy, iz = space.cell(nu, nv, nw, A) rx, ry, rz, atms = space.real(ux, uy, uz, ix, iy, iz, atm) phase_factor = scattering.phase(Qx, Qy, Qz, ux, uy, uz) scattering_length = scattering.length(atm, Q.size) A_k, i_dft = occupational.transform(A_r, H, K, L, nu, nv, nw, n_atm) factors = space.prefactors(scattering_length, phase_factor, occupancy) factors *= T I = occupational.intensity(A_k, i_dft, factors) n_hkl = Q.size n_xyz = nu * nv * nw * n_atm i, j = np.triu_indices(n_xyz, 1) k, l = np.mod(i, n_atm), np.mod(j, n_atm) m = np.arange(n_xyz) n = np.mod(m, n_atm) rx_ij = rx[j] - rx[i] ry_ij = ry[j] - ry[i] rz_ij = rz[j] - rz[i] bc = scattering.length(atm, 1) T = T.reshape(n_hkl, n_atm) A_i, A_j, A_m = A_r[i], A_r[j], A_r[m] c_k, c_l, c_n = occupancy[k], occupancy[l], occupancy[n] b_k, b_l, b_n = bc[k], bc[l], bc[n] T_k, T_l, T_n = T[:, k], T[:, l], T[:, n] I_ref = ((c_n**2*(b_n*b_n.conj()).real*A_m**2*T_n**2).sum(axis=1)\ + 2*(c_k*c_l*(b_k*b_l.conj()).real*A_i*A_j*T_k*T_l*\ np.cos(Qx[:,np.newaxis]*rx_ij+\ Qy[:,np.newaxis]*ry_ij+\ Qz[:,np.newaxis]*rz_ij)).sum(axis=1))/n_xyz np.testing.assert_array_almost_equal(I, I_ref)
def test_structure(self): a, b, c, alpha, beta, gamma = 5, 6, 7, np.pi / 2, np.pi / 3, np.pi / 4 inv_constants = crystal.reciprocal(a, b, c, alpha, beta, gamma) a_, b_, c_, alpha_, beta_, gamma_ = inv_constants h_range, nh = [-1, 1], 5 k_range, nk = [0, 2], 11 l_range, nl = [-1, 0], 5 nu, nv, nw, n_atm = 2, 5, 4, 2 u = np.array([0.2, 0.1]) v = np.array([0.3, 0.4]) w = np.array([0.4, 0.5]) atm = np.array(['Fe', 'Mn']) occupancy = np.array([0.75, 0.5]) U11 = np.array([0.5, 0.3]) U22 = np.array([0.6, 0.4]) U33 = np.array([0.4, 0.6]) U23 = np.array([0.05, -0.03]) U13 = np.array([-0.04, 0.02]) U12 = np.array([0.03, -0.02]) T = space.debye_waller(h_range, k_range, l_range, nh, nk, nl, U11, U22, U33, U23, U13, U12, a_, b_, c_) A = crystal.cartesian(a, b, c, alpha, beta, gamma) B = crystal.cartesian(a_, b_, c_, alpha_, beta_, gamma_) R = crystal.cartesian_rotation(a, b, c, alpha, beta, gamma) A_r = occupational.composition(nu, nv, nw, n_atm, value=occupancy) index_parameters = space.mapping(h_range, k_range, l_range, nh, nk, nl, nu, nv, nw) h, k, l, H, K, L, indices, inverses, operators = index_parameters Qh, Qk, Ql = crystal.vector(h, k, l, B) Qx, Qy, Qz = crystal.transform(Qh, Qk, Ql, R) Qx_norm, Qy_norm, Qz_norm, Q = space.unit(Qx, Qy, Qz) ux, uy, uz = crystal.transform(u, v, w, A) ix, iy, iz = space.cell(nu, nv, nw, A) rx, ry, rz, atms = space.real(ux, uy, uz, ix, iy, iz, atm) phase_factor = scattering.phase(Qx, Qy, Qz, ux, uy, uz) scattering_length = scattering.length(atm, Q.size) A_k, i_dft = occupational.transform(A_r, H, K, L, nu, nv, nw, n_atm) factors = space.prefactors(scattering_length, phase_factor, occupancy) factors *= T F, prod = occupational.structure(A_k, i_dft, factors) n_hkl = Q.size n_xyz = nu * nv * nw * n_atm m = np.arange(n_xyz) n = np.mod(m, n_atm) rx_m = rx[m] ry_m = ry[m] rz_m = rz[m] bc = scattering.length(atm, 1) T = T.reshape(n_hkl, n_atm) A_m = A_r[m] c_n = occupancy[n] b_n = bc[n] T_n = T[:, n] prod_ref = (c_n*b_n*A_m*T_n*np.exp(1j*(Qx[:,np.newaxis]*rx_m+\ Qy[:,np.newaxis]*ry_m+\ Qz[:,np.newaxis]*rz_m))) F_ref = prod_ref.sum(axis=1) prod_ref = prod_ref.reshape(n_hkl, nu * nv * nw, n_atm).sum(axis=1).flatten() np.testing.assert_array_almost_equal(F, F_ref) np.testing.assert_array_almost_equal(prod, prod_ref)
def test_heisenberg_cluster(self): np.random.seed(13) nu, nv, nw = 3, 4, 5 n_atm = 2 atm = np.array(['Fe3+', 'Fe3+']) u, v, w = np.array([0, 0.5]), np.array([0, 0.5]), np.array([0, 0.5]) a, b, c, alpha, beta, gamma = 5, 5, 5, np.pi / 2, np.pi / 2, np.pi / 2 A = crystal.cartesian(a, b, c, alpha, beta, gamma) ux, uy, uz = crystal.transform(u, v, w, A) pair_dict = crystal.pairs(u, v, w, atm, A, extend=True) img_ind_i, img_ind_j, img_ind_k, atm_ind = [], [], [], [] du, dv, dw = [], [], [] for i in pair_dict.keys(): for pair in pair_dict[i].keys(): pair_no, atm_j = pair ind_i, ind_j, ind_k, atm_a = [], [], [], [] for j, img in zip(*pair_dict[i][pair]): ind_i.append(img[0]) ind_j.append(img[1]) ind_k.append(img[2]) atm_a.append(j) du.append(u[j] - u[i] + img[0]) dv.append(v[j] - v[i] + img[1]) dw.append(w[j] - w[i] + img[2]) img_ind_i.append(ind_i) img_ind_j.append(ind_j) img_ind_k.append(ind_k) atm_ind.append(atm_a) img_ind_i = np.array(img_ind_i, dtype=np.int_) img_ind_j = np.array(img_ind_j, dtype=np.int_) img_ind_k = np.array(img_ind_k, dtype=np.int_) atm_ind = np.array(atm_ind, dtype=np.int_) du, dv, dw = np.array(du), np.array(dv), np.array(dw) dx, dy, dz = crystal.transform(du, dv, dw, A) dx = dx.reshape(atm_ind.shape) dy = dy.reshape(atm_ind.shape) dz = dz.reshape(atm_ind.shape) d = np.sqrt(dx**2 + dy**2 + dz**2) n_pair = atm_ind.shape[1] dist = np.round(d / 1e-2).astype(int) _, inv_ind = np.unique(dist, return_inverse=True) pair_ind = np.arange(n_pair + 1, dtype=np.int_)[inv_ind].reshape(n_atm, n_pair) mask = d < 0.99 * np.sqrt(2 * a**2) n_pair = mask.sum() // n_atm dx = dx[mask].reshape(n_atm, n_pair) dy = dy[mask].reshape(n_atm, n_pair) dz = dz[mask].reshape(n_atm, n_pair) pair_ind = pair_ind[mask].reshape(n_atm, n_pair) img_ind_i = img_ind_i[mask].reshape(n_atm, n_pair) img_ind_j = img_ind_j[mask].reshape(n_atm, n_pair) img_ind_k = img_ind_k[mask].reshape(n_atm, n_pair) atm_ind = atm_ind[mask].reshape(n_atm, n_pair) d_xyz = np.stack((dx, dy, dz)) inv_d_xyz = -d_xyz pair_inv = np.zeros((n_atm, n_pair), dtype=np.int_) for i in range(n_atm): for p in range(n_pair): for q in range(n_pair): if (np.allclose(d_xyz[:, i, p], inv_d_xyz[:, atm_ind[i, p], q])): pair_inv[i, p] = q pair_ij = np.zeros((n_atm, n_pair), dtype=np.intc) J = np.zeros((n_pair, 3, 3)) K = np.zeros((n_atm, 3, 3)) g = np.zeros((n_atm, 3, 3)) B = np.zeros(3) Jx, Jy, Jz = -3, -2, -1 Kx, Ky, Kz = -1, -3, -2 gx, gy, gz = 2, 3, 4 Bx, By, Bz = 1, 2, 3 J[:n_pair, :, :] = np.array([[Jx, 0, 0], [0, Jy, 0], [0, 0, Jz]], dtype=float) K[:n_atm, :, :] = np.array([[Kx, 0, 0], [0, Ky, 0], [0, 0, Kz]], dtype=float) g[:n_atm, :, :] = np.array([[gx, 0, 0], [0, gy, 0], [0, 0, gz]], dtype=float) B[:] = Bx, By, Bz n = nu * nv * nw * n_atm Q = np.random.random((n * (n + 1) // 2, 6)) * 0 ix, iy, iz = space.cell(nu, nv, nw, A) rx, ry, rz, ion = space.real(ux, uy, uz, ix, iy, iz, atm) M, N = 2, 10 T0, T1 = 0.01, 5 T_range = np.logspace(np.log2(T0), np.log2(T1), M, base=2) kB = 0.08617 theta = 2 * np.pi * np.random.rand(nu, nv, nw, n_atm, M) phi = np.arccos(1 - 2 * np.random.rand(nu, nv, nw, n_atm, M)) Sx = np.sin(phi) * np.cos(theta) Sy = np.sin(phi) * np.sin(theta) Sz = np.cos(phi) E, T_range = simulation.heisenberg_cluster(Sx, Sy, Sz, J, K, g, B, Q, atm_ind, img_ind_i, img_ind_j, img_ind_k, pair_ind, pair_inv, pair_ij, T_range, kB, N) E_ref = simulation.magnetic_energy(Sx, Sy, Sz, J, K, g, B, atm_ind, img_ind_i, img_ind_j, img_ind_k, pair_ind, pair_ij) V_ref = simulation.dipole_dipole_interaction_energy(Sx, Sy, Sz, Q) E0 = E_ref.sum(axis=(0, 1, 2, 3, 4)) + V_ref.sum(axis=(0, 1, 2)) np.testing.assert_array_almost_equal(E, E0)
def test_displacive(self): a, b, c, alpha, beta, gamma = 5, 6, 7, np.pi / 2, np.pi / 3, np.pi / 4 inv_constants = crystal.reciprocal(a, b, c, alpha, beta, gamma) a_, b_, c_, alpha_, beta_, gamma_ = inv_constants h_range, nh = [-1, 1], 5 k_range, nk = [0, 2], 11 l_range, nl = [-1, 0], 5 nu, nv, nw, n_atm = 2, 5, 4, 2 u = np.array([0.2, 0.1]) v = np.array([0.3, 0.4]) w = np.array([0.4, 0.5]) atm = np.array(['Fe', 'Mn']) occupancy = np.array([0.75, 0.5]) U11 = np.array([0.5, 0.3]) U22 = np.array([0.6, 0.4]) U33 = np.array([0.4, 0.6]) U23 = np.array([0.05, -0.03]) U13 = np.array([-0.04, 0.02]) U12 = np.array([0.03, -0.02]) twins = np.eye(3).reshape(1, 3, 3) variants = np.array([1.0]) W = np.eye(3) A = crystal.cartesian(a, b, c, alpha, beta, gamma) B = crystal.cartesian(a_, b_, c_, alpha_, beta_, gamma_) R = crystal.cartesian_rotation(a, b, c, alpha, beta, gamma) U = np.row_stack((U11, U22, U33, U23, U13, U12)) Ux, Uy, Uz = displacive.expansion(nu, nv, nw, n_atm, value=U) index_parameters = space.mapping(h_range, k_range, l_range, nh, nk, nl, nu, nv, nw) h, k, l, H, K, L, indices, inverses, operators = index_parameters Qh, Qk, Ql = crystal.vector(h, k, l, B) Qx, Qy, Qz = crystal.transform(Qh, Qk, Ql, R) Qx_norm, Qy_norm, Qz_norm, Q = space.unit(Qx, Qy, Qz) ux, uy, uz = crystal.transform(u, v, w, A) ix, iy, iz = space.cell(nu, nv, nw, A) rx, ry, rz, atms = space.real(ux, uy, uz, ix, iy, iz, atm) phase_factor = scattering.phase(Qx, Qy, Qz, ux, uy, uz) scattering_length = scattering.length(atm, Q.size) p = 3 coeffs = displacive.coefficients(p) H_nuc, K_nuc, L_nuc, cond = space.condition(H, K, L, nu, nv, nw, centering='P') U_r = displacive.products(Ux, Uy, Uz, p) Q_k = displacive.products(Qx, Qy, Qz, p) U_k, i_dft = displacive.transform(U_r, H, K, L, nu, nv, nw, n_atm) factors = space.prefactors(scattering_length, phase_factor, occupancy) I_ref = displacive.intensity(U_k, Q_k, coeffs, cond, p, i_dft, factors) reduced_params = space.reduced(h_range, k_range, l_range, nh, nk, nl, nu, nv, nw) indices, reverses, symops, Nu, Nv, Nw = reduced_params symop = symmetry.laue_id(symops) centering = 1 even, odd = displacive.indices(p) I = monocrystal.displacive(U_r, coeffs, occupancy, ux, uy, uz, atm, h_range, k_range, l_range, indices, symop, W, B, R, twins, variants, nh, nk, nl, nu, nv, nw, Nu, Nv, Nw, p, even, centering) np.testing.assert_array_almost_equal(I, I_ref)
def test_intensity(self): a, b, c, alpha, beta, gamma = 5, 6, 7, np.pi / 2, np.pi / 3, np.pi / 4 inv_constants = crystal.reciprocal(a, b, c, alpha, beta, gamma) a_, b_, c_, alpha_, beta_, gamma_ = inv_constants h_range, nh = [-1, 1], 5 k_range, nk = [0, 2], 11 l_range, nl = [-1, 0], 5 nu, nv, nw, n_atm = 2, 5, 4, 2 u = np.array([0.2, 0.1]) v = np.array([0.3, 0.4]) w = np.array([0.4, 0.5]) atm = np.array(['Fe', 'Mn']) occupancy = np.array([0.75, 0.5]) U11 = np.array([0.5, 0.3]) U22 = np.array([0.6, 0.4]) U33 = np.array([0.4, 0.6]) U23 = np.array([0.05, -0.03]) U13 = np.array([-0.04, 0.02]) U12 = np.array([0.03, -0.02]) A = crystal.cartesian(a, b, c, alpha, beta, gamma) B = crystal.cartesian(a_, b_, c_, alpha_, beta_, gamma_) R = crystal.cartesian_rotation(a, b, c, alpha, beta, gamma) U = np.row_stack((U11, U22, U33, U23, U13, U12)) Ux, Uy, Uz = displacive.expansion(nu, nv, nw, n_atm, value=U) index_parameters = space.mapping(h_range, k_range, l_range, nh, nk, nl, nu, nv, nw) h, k, l, H, K, L, indices, inverses, operators = index_parameters Qh, Qk, Ql = crystal.vector(h, k, l, B) Qx, Qy, Qz = crystal.transform(Qh, Qk, Ql, R) Qx_norm, Qy_norm, Qz_norm, Q = space.unit(Qx, Qy, Qz) ux, uy, uz = crystal.transform(u, v, w, A) ix, iy, iz = space.cell(nu, nv, nw, A) rx, ry, rz, atms = space.real(ux, uy, uz, ix, iy, iz, atm) phase_factor = scattering.phase(Qx, Qy, Qz, ux, uy, uz) scattering_length = scattering.length(atm, Q.size) p = 3 coeffs = displacive.coefficients(p) H_nuc, K_nuc, L_nuc, cond = space.condition(H, K, L, nu, nv, nw, centering='P') U_r = displacive.products(Ux, Uy, Uz, p) Q_k = displacive.products(Qx, Qy, Qz, p) U_k, i_dft = displacive.transform(U_r, H, K, L, nu, nv, nw, n_atm) factors = space.prefactors(scattering_length, phase_factor, occupancy) # I = displacive.intensity(U_k, Q_k, coeffs, cond, p, i_dft, factors) I, F_nuc = displacive.intensity(U_k, Q_k, coeffs, cond, p, i_dft, factors, subtract=False) n_hkl = Q.size n_xyz = nu * nv * nw * n_atm i, j = np.triu_indices(n_xyz, 1) k, l = np.mod(i, n_atm), np.mod(j, n_atm) m = np.arange(n_xyz) n = np.mod(m, n_atm) rx_ij = rx[j] - rx[i] ry_ij = ry[j] - ry[i] rz_ij = rz[j] - rz[i] bc = scattering.length(atm, 1) U_r = U_r.reshape(coeffs.shape[0], n_xyz) Q_k = Q_k.reshape(coeffs.shape[0], n_hkl) U_i, U_j, U_m = U_r[:, i], U_r[:, j], U_r[:, m] c_k, c_l, c_n = occupancy[k], occupancy[l], occupancy[n] b_k, b_l, b_n = bc[k], bc[l], bc[n] exp_iQ_dot_U_m = np.dot(coeffs * U_m.T, Q_k).T exp_iQ_dot_U_i = np.dot(coeffs * U_i.T, Q_k).T exp_iQ_dot_U_j = np.dot(coeffs * U_j.T, Q_k).T I_ref = ((c_n**2*(b_n*b_n.conj()).real*\ (exp_iQ_dot_U_m*exp_iQ_dot_U_m.conj()).real).sum(axis=1)\ + 2*(c_k*c_l*(b_k*b_l.conj()).real* ((exp_iQ_dot_U_i*exp_iQ_dot_U_j.conj()*\ np.cos(Qx[:,np.newaxis]*rx_ij+\ Qy[:,np.newaxis]*ry_ij+\ Qz[:,np.newaxis]*rz_ij)).real+ (exp_iQ_dot_U_i*exp_iQ_dot_U_j.conj()*\ np.sin(Qx[:,np.newaxis]*rx_ij+\ Qy[:,np.newaxis]*ry_ij+\ Qz[:,np.newaxis]*rz_ij)).imag)).sum(axis=1))/n_xyz np.testing.assert_array_almost_equal(I, I_ref)
def test_structure(self): a, b, c, alpha, beta, gamma = 5, 6, 7, np.pi / 2, np.pi / 3, np.pi / 4 inv_constants = crystal.reciprocal(a, b, c, alpha, beta, gamma) a_, b_, c_, alpha_, beta_, gamma_ = inv_constants h_range, nh = [-1, 1], 5 k_range, nk = [0, 2], 11 l_range, nl = [-1, 0], 5 nu, nv, nw, n_atm = 2, 5, 4, 2 u = np.array([0.2, 0.1]) v = np.array([0.3, 0.4]) w = np.array([0.4, 0.5]) atm = np.array(['Fe3+', 'Mn3+']) occupancy = np.array([0.75, 0.5]) U11 = np.array([0.5, 0.3]) U22 = np.array([0.6, 0.4]) U33 = np.array([0.4, 0.6]) U23 = np.array([0.05, -0.03]) U13 = np.array([-0.04, 0.02]) U12 = np.array([0.03, -0.02]) T = space.debye_waller(h_range, k_range, l_range, nh, nk, nl, U11, U22, U33, U23, U13, U12, a_, b_, c_) A = crystal.cartesian(a, b, c, alpha, beta, gamma) B = crystal.cartesian(a_, b_, c_, alpha_, beta_, gamma_) R = crystal.cartesian_rotation(a, b, c, alpha, beta, gamma) Sx, Sy, Sz = magnetic.spin(nu, nv, nw, n_atm) index_parameters = space.mapping(h_range, k_range, l_range, nh, nk, nl, nu, nv, nw) h, k, l, H, K, L, indices, inverses, operators = index_parameters Qh, Qk, Ql = crystal.vector(h, k, l, B) Qx, Qy, Qz = crystal.transform(Qh, Qk, Ql, R) Qx_norm, Qy_norm, Qz_norm, Q = space.unit(Qx, Qy, Qz) ux, uy, uz = crystal.transform(u, v, w, A) ix, iy, iz = space.cell(nu, nv, nw, A) rx, ry, rz, atms = space.real(ux, uy, uz, ix, iy, iz, atm) phase_factor = scattering.phase(Qx, Qy, Qz, ux, uy, uz) form_factor = magnetic.form(Q, atm, g=2) Sx_k, Sy_k, Sz_k, i_dft = magnetic.transform(Sx, Sy, Sz, H, K, L, nu, nv, nw, n_atm) factors = space.prefactors(form_factor, phase_factor, occupancy) factors *= T Fx, Fy, Fz, \ prod_x, prod_y, prod_z = magnetic.structure(Qx_norm, Qy_norm, Qz_norm, Sx_k, Sy_k, Sz_k, i_dft, factors) n_hkl = Q.size n_xyz = nu * nv * nw * n_atm m = np.arange(n_xyz) n = np.mod(m, n_atm) rx_m = rx[m] ry_m = ry[m] rz_m = rz[m] mf = form_factor.reshape(n_hkl, n_atm) T = T.reshape(n_hkl, n_atm) Sx_m = Sx[m] Sy_m = Sy[m] Sz_m = Sz[m] c_n = occupancy[n] f_n = mf[:, n] T_n = T[:, n] prod_x_ref = (c_n*f_n*Sx_m*T_n*np.exp(1j*(Qx[:,np.newaxis]*rx_m+\ Qy[:,np.newaxis]*ry_m+\ Qz[:,np.newaxis]*rz_m))) prod_y_ref = (c_n*f_n*Sy_m*T_n*np.exp(1j*(Qx[:,np.newaxis]*rx_m+\ Qy[:,np.newaxis]*ry_m+\ Qz[:,np.newaxis]*rz_m))) prod_z_ref = (c_n*f_n*Sz_m*T_n*np.exp(1j*(Qx[:,np.newaxis]*rx_m+\ Qy[:,np.newaxis]*ry_m+\ Qz[:,np.newaxis]*rz_m))) Fx_ref = prod_x_ref.sum(axis=1) Fy_ref = prod_y_ref.sum(axis=1) Fz_ref = prod_z_ref.sum(axis=1) prod_x_ref = prod_x_ref.reshape(n_hkl, nu * nv * nw, n_atm).sum(axis=1) prod_y_ref = prod_y_ref.reshape(n_hkl, nu * nv * nw, n_atm).sum(axis=1) prod_z_ref = prod_z_ref.reshape(n_hkl, nu * nv * nw, n_atm).sum(axis=1) prod_x_ref = prod_x_ref.flatten() prod_y_ref = prod_y_ref.flatten() prod_z_ref = prod_z_ref.flatten() np.testing.assert_array_almost_equal(Fx, Fx_ref) np.testing.assert_array_almost_equal(Fy, Fy_ref) np.testing.assert_array_almost_equal(Fz, Fz_ref) np.testing.assert_array_almost_equal(prod_x, prod_x_ref) np.testing.assert_array_almost_equal(prod_y, prod_y_ref) np.testing.assert_array_almost_equal(prod_z, prod_z_ref)
def test_intensity(self): a, b, c, alpha, beta, gamma = 5, 6, 7, np.pi / 2, np.pi / 3, np.pi / 4 inv_constants = crystal.reciprocal(a, b, c, alpha, beta, gamma) a_, b_, c_, alpha_, beta_, gamma_ = inv_constants h_range, nh = [-1, 1], 5 k_range, nk = [0, 2], 11 l_range, nl = [-1, 0], 5 nu, nv, nw, n_atm = 2, 5, 4, 2 u = np.array([0.2, 0.1]) v = np.array([0.3, 0.4]) w = np.array([0.4, 0.5]) atm = np.array(['Fe3+', 'Mn3+']) occupancy = np.array([0.75, 0.5]) U11 = np.array([0.5, 0.3]) U22 = np.array([0.6, 0.4]) U33 = np.array([0.4, 0.6]) U23 = np.array([0.05, -0.03]) U13 = np.array([-0.04, 0.02]) U12 = np.array([0.03, -0.02]) T = space.debye_waller(h_range, k_range, l_range, nh, nk, nl, U11, U22, U33, U23, U13, U12, a_, b_, c_) A = crystal.cartesian(a, b, c, alpha, beta, gamma) B = crystal.cartesian(a_, b_, c_, alpha_, beta_, gamma_) R = crystal.cartesian_rotation(a, b, c, alpha, beta, gamma) Sx, Sy, Sz = magnetic.spin(nu, nv, nw, n_atm) index_parameters = space.mapping(h_range, k_range, l_range, nh, nk, nl, nu, nv, nw) h, k, l, H, K, L, indices, inverses, operators = index_parameters Qh, Qk, Ql = crystal.vector(h, k, l, B) Qx, Qy, Qz = crystal.transform(Qh, Qk, Ql, R) Qx_norm, Qy_norm, Qz_norm, Q = space.unit(Qx, Qy, Qz) ux, uy, uz = crystal.transform(u, v, w, A) ix, iy, iz = space.cell(nu, nv, nw, A) rx, ry, rz, atms = space.real(ux, uy, uz, ix, iy, iz, atm) phase_factor = scattering.phase(Qx, Qy, Qz, ux, uy, uz) form_factor = magnetic.form(Q, atm, g=2) Sx_k, Sy_k, Sz_k, i_dft = magnetic.transform(Sx, Sy, Sz, H, K, L, nu, nv, nw, n_atm) factors = space.prefactors(form_factor, phase_factor, occupancy) factors *= T I = magnetic.intensity(Qx_norm, Qy_norm, Qz_norm, \ Sx_k, Sy_k, Sz_k, i_dft, factors) n_hkl = Q.size n_xyz = nu * nv * nw * n_atm i, j = np.triu_indices(n_xyz, 1) k, l = np.mod(i, n_atm), np.mod(j, n_atm) m = np.arange(n_xyz) n = np.mod(m, n_atm) rx_ij = rx[j] - rx[i] ry_ij = ry[j] - ry[i] rz_ij = rz[j] - rz[i] mf = form_factor.reshape(n_hkl, n_atm) T = T.reshape(n_hkl, n_atm) Sx_i, Sx_j, Sx_m = Sx[i], Sx[j], Sx[m] Sy_i, Sy_j, Sy_m = Sy[i], Sy[j], Sy[m] Sz_i, Sz_j, Sz_m = Sz[i], Sz[j], Sz[m] c_k, c_l, c_n = occupancy[k], occupancy[l], occupancy[n] f_k, f_l, f_n = mf[:, k], mf[:, l], mf[:, n] T_k, T_l, T_n = T[:, k], T[:, l], T[:, n] Q_norm_dot_S_i = Qx_norm[:,np.newaxis]*Sx_i\ + Qy_norm[:,np.newaxis]*Sy_i\ + Qz_norm[:,np.newaxis]*Sz_i Q_norm_dot_S_j = Qx_norm[:,np.newaxis]*Sx_j\ + Qy_norm[:,np.newaxis]*Sy_j\ + Qz_norm[:,np.newaxis]*Sz_j Q_norm_dot_S_m = Qx_norm[:,np.newaxis]*Sx_m\ + Qy_norm[:,np.newaxis]*Sy_m\ + Qz_norm[:,np.newaxis]*Sz_m Sx_perp_i = Sx_i - (Q_norm_dot_S_i) * Qx_norm[:, np.newaxis] Sx_perp_j = Sx_j - (Q_norm_dot_S_j) * Qx_norm[:, np.newaxis] Sx_perp_m = Sx_m - (Q_norm_dot_S_m) * Qx_norm[:, np.newaxis] Sy_perp_i = Sy_i - (Q_norm_dot_S_i) * Qy_norm[:, np.newaxis] Sy_perp_j = Sy_j - (Q_norm_dot_S_j) * Qy_norm[:, np.newaxis] Sy_perp_m = Sy_m - (Q_norm_dot_S_m) * Qy_norm[:, np.newaxis] Sz_perp_i = Sz_i - (Q_norm_dot_S_i) * Qz_norm[:, np.newaxis] Sz_perp_j = Sz_j - (Q_norm_dot_S_j) * Qz_norm[:, np.newaxis] Sz_perp_m = Sz_m - (Q_norm_dot_S_m) * Qz_norm[:, np.newaxis] I_ref = ((c_n**2*(f_n*f_n.conj()).real*T_n**2*\ (Sx_perp_m**2+Sy_perp_m**2+Sz_perp_m**2)).sum(axis=1)\ + 2*(c_k*c_l*(f_k*f_l.conj()).real*T_k*T_l*\ (Sx_perp_i*Sx_perp_j+Sy_perp_i*Sy_perp_j+Sz_perp_i*Sz_perp_j)*\ np.cos(Qx[:,np.newaxis]*rx_ij+\ Qy[:,np.newaxis]*ry_ij+\ Qz[:,np.newaxis]*rz_ij)).sum(axis=1))/n_xyz np.testing.assert_array_almost_equal(I, I_ref)
def atom_pairs_distance(rx, ry, rz, nu, nv, nw, n_atm, A, tol=1e-3): A_inv = np.linalg.inv(A) mu = (nu+1)//2 mv = (nv+1)//2 mw = (nw+1)//2 m_uvw = mu*mv*mw n_uvw = nu*nv*nw c_uvw = np.arange(n_uvw, dtype=int) cu, cv, cw = np.unravel_index(c_uvw, (nu,nv,nw)) i_lat, j_lat = np.triu_indices(m_uvw, k=1) iu, iv, iw = np.unravel_index(i_lat, (mu,mv,mw)) ju, jv, jw = np.unravel_index(j_lat, (mu,mv,mw)) iu = np.mod(iu+cu[:,None], nu).flatten() iv = np.mod(iv+cv[:,None], nv).flatten() iw = np.mod(iw+cw[:,None], nw).flatten() ju = np.mod(ju+cu[:,None], nu).flatten() jv = np.mod(jv+cv[:,None], nv).flatten() jw = np.mod(jw+cw[:,None], nw).flatten() i_lat = np.ravel_multi_index((iu,iv,iw), (nu,nv,nw)) j_lat = np.ravel_multi_index((ju,jv,jw), (nu,nv,nw)) pairs = np.stack((i_lat,j_lat)).reshape(2,n_uvw*m_uvw*(m_uvw-1)//2) i_lat, j_lat = np.unique(np.sort(pairs, axis=0), axis=1) # --- iu, iv, iw = np.unravel_index(i_lat, (nu,nv,nw)) ju, jv, jw = np.unravel_index(j_lat, (nu,nv,nw)) du, dv, dw = ju-iu, jv-iv, jw-iw distance = np.stack((du,dv,dw)) distance = np.stack((distance.T,-distance.T)).T sort = np.lexsort(distance, axis=1)[:,0] n_pairs = sort.size distance = distance.reshape(3,2*n_pairs)[:,sort+2*np.arange(n_pairs)] metric = np.vstack(distance).T _, index, inverse = np.unique(metric, return_index=True, return_inverse=True, axis=0) i_lat = np.ravel_multi_index((iu[index],iv[index],iw[index]), (nu,nv,nw)) j_lat = np.ravel_multi_index((ju[index],jv[index],jw[index]), (nu,nv,nw)) # --- i_atm, j_atm = np.triu_indices(n_atm, k=1) ux = rx.reshape(nu,nv,nw,n_atm)[0,0,0,:] uy = ry.reshape(nu,nv,nw,n_atm)[0,0,0,:] uz = rz.reshape(nu,nv,nw,n_atm)[0,0,0,:] dx = ux[j_atm]-ux[i_atm] dy = uy[j_atm]-uy[i_atm] dz = uz[j_atm]-uz[i_atm] distance = np.stack((dx,dy,dz)) distance = np.stack((distance.T,-distance.T)).T sort = np.lexsort(distance, axis=1)[:,0] n_pairs = sort.size distance = distance.reshape(3,2*n_pairs)[:,sort+2*np.arange(n_pairs)] metric = np.vstack(np.round(distance/tol,0)).astype(int).T _, ind, inv = np.unique(metric, return_index=True, return_inverse=True, axis=0) i_atm, j_atm = i_atm[ind], j_atm[ind] i_atms = np.concatenate((i_atm,j_atm)) j_atms = np.concatenate((j_atm,i_atm)) i_atms = np.concatenate((i_atms,np.arange(n_atm))) j_atms = np.concatenate((j_atms,np.arange(n_atm))) i = np.ravel_multi_index((i_lat,i_atms[:,None]), (n_uvw,n_atm)).flatten() j = np.ravel_multi_index((j_lat,j_atms[:,None]), (n_uvw,n_atm)).flatten() ic = np.ravel_multi_index((0,i_atm[:,None]), (n_uvw,n_atm)).flatten() jc = np.ravel_multi_index((0,j_atm[:,None]), (n_uvw,n_atm)).flatten() i, j = np.concatenate((ic,i)), np.concatenate((jc,j)) # --- dx, dy, dz = rx[j]-rx[i], ry[j]-ry[i], rz[j]-rz[i] du, dv, dw = crystal.transform(dx, dy, dz, A_inv) du[du < -mu] += nu dv[dv < -mv] += nv dw[dw < -mw] += nw du[du > mu] -= nu dv[dv > mv] -= nv dw[dw > mw] -= nw dx, dy, dz = crystal.transform(du, dv, dw, A) i_atm, j_atm = np.triu_indices(n_atm, k=1) i_atms = np.concatenate((i_atm,j_atm)) j_atms = np.concatenate((j_atm,i_atm)) i_atms = np.concatenate((i_atms,np.arange(n_atm))) j_atms = np.concatenate((j_atms,np.arange(n_atm))) i_lat = np.ravel_multi_index((iu,iv,iw), (nu,nv,nw)) j_lat = np.ravel_multi_index((ju,jv,jw), (nu,nv,nw)) i = np.ravel_multi_index((i_lat,i_atms[:,None]), (n_uvw,n_atm)).flatten() j = np.ravel_multi_index((j_lat,j_atms[:,None]), (n_uvw,n_atm)).flatten() ic = np.ravel_multi_index((c_uvw,i_atm[:,None]), (n_uvw,n_atm)).flatten() jc = np.ravel_multi_index((c_uvw,j_atm[:,None]), (n_uvw,n_atm)).flatten() i, j = np.concatenate((ic,i)), np.concatenate((jc,j)) l, m = ind.size, index.size k = np.concatenate((inv,l+inv,2*l+np.arange(n_atm))) p = (np.arange(n_uvw)*0+inv[:,None]).flatten() q = l+(inverse+m*k[:,None]).flatten() inverse = np.concatenate((p,q)) return dx, dy, dz, i, j, inverse
def test_structure(self): a, b, c, alpha, beta, gamma = 5, 6, 7, np.pi / 2, np.pi / 3, np.pi / 4 inv_constants = crystal.reciprocal(a, b, c, alpha, beta, gamma) a_, b_, c_, alpha_, beta_, gamma_ = inv_constants h_range, nh = [-1, 1], 5 k_range, nk = [0, 2], 11 l_range, nl = [-1, 0], 5 nu, nv, nw, n_atm = 2, 5, 4, 2 u = np.array([0.2, 0.1]) v = np.array([0.3, 0.4]) w = np.array([0.4, 0.5]) atm = np.array(['Fe', 'Mn']) occupancy = np.array([0.75, 0.5]) U11 = np.array([0.5, 0.3]) U22 = np.array([0.6, 0.4]) U33 = np.array([0.4, 0.6]) U23 = np.array([0.05, -0.03]) U13 = np.array([-0.04, 0.02]) U12 = np.array([0.03, -0.02]) A = crystal.cartesian(a, b, c, alpha, beta, gamma) B = crystal.cartesian(a_, b_, c_, alpha_, beta_, gamma_) R = crystal.cartesian_rotation(a, b, c, alpha, beta, gamma) U = np.row_stack((U11, U22, U33, U23, U13, U12)) Ux, Uy, Uz = displacive.expansion(nu, nv, nw, n_atm, value=U) index_parameters = space.mapping(h_range, k_range, l_range, nh, nk, nl, nu, nv, nw) h, k, l, H, K, L, indices, inverses, operators = index_parameters Qh, Qk, Ql = crystal.vector(h, k, l, B) Qx, Qy, Qz = crystal.transform(Qh, Qk, Ql, R) Qx_norm, Qy_norm, Qz_norm, Q = space.unit(Qx, Qy, Qz) ux, uy, uz = crystal.transform(u, v, w, A) ix, iy, iz = space.cell(nu, nv, nw, A) rx, ry, rz, atms = space.real(ux, uy, uz, ix, iy, iz, atm) phase_factor = scattering.phase(Qx, Qy, Qz, ux, uy, uz) scattering_length = scattering.length(atm, Q.size) p = 3 coeffs = displacive.coefficients(p) H_nuc, K_nuc, L_nuc, cond = space.condition(H, K, L, nu, nv, nw) U_r = displacive.products(Ux, Uy, Uz, p) Q_k = displacive.products(Qx, Qy, Qz, p) U_k, i_dft = displacive.transform(U_r, H, K, L, nu, nv, nw, n_atm) factors = space.prefactors(scattering_length, phase_factor, occupancy) F, F_nuc, \ prod, prod_nuc, \ V_k, V_k_nuc, \ even, bragg = displacive.structure(U_k, Q_k, coeffs, cond, p, i_dft, factors) n_hkl = Q.size n_xyz = nu * nv * nw * n_atm m = np.arange(n_xyz) n = np.mod(m, n_atm) rx_m = rx[m] ry_m = ry[m] rz_m = rz[m] bc = scattering.length(atm, 1) U_r = U_r.reshape(coeffs.shape[0], n_xyz) Q_k = Q_k.reshape(coeffs.shape[0], n_hkl) U_m = U_r[:, m] c_n = occupancy[n] b_n = bc[n] exp_iQ_dot_U_m = np.dot(coeffs * U_m.T, Q_k).T prod_ref = c_n*b_n*exp_iQ_dot_U_m*np.exp(1j*(Qx[:,np.newaxis]*rx_m+\ Qy[:,np.newaxis]*ry_m+\ Qz[:,np.newaxis]*rz_m)) F_ref = prod_ref.sum(axis=1) prod_ref = prod_ref.reshape(n_hkl, nu * nv * nw, n_atm).sum(axis=1).flatten() np.testing.assert_array_almost_equal(F, F_ref) np.testing.assert_array_almost_equal(prod, prod_ref) cos_iQ_dot_U_m = np.dot((coeffs * U_m.T)[:, even], Q_k[even, :]).T prod_nuc_ref = c_n*b_n*cos_iQ_dot_U_m*\ np.exp(1j*(Qx[:,np.newaxis]*rx_m+\ Qy[:,np.newaxis]*ry_m+\ Qz[:,np.newaxis]*rz_m)) F_nuc_ref = prod_nuc_ref.sum(axis=1)[cond] prod_nuc_ref = prod_nuc_ref.reshape(n_hkl, nu * nv * nw, n_atm).sum(axis=1)[cond].flatten() np.testing.assert_array_almost_equal(F_nuc, F_nuc_ref) np.testing.assert_array_almost_equal(prod_nuc, prod_nuc_ref) factors = (c_n * b_n * cos_iQ_dot_U_m).flatten() F_nuc_ref = space.bragg(Qx, Qy, Qz, rx, ry, rz, factors, cond) np.testing.assert_array_almost_equal(F_nuc, F_nuc_ref)
def test_magnetic(self): a, b, c, alpha, beta, gamma = 5, 6, 7, np.pi / 2, np.pi / 3, np.pi / 4 inv_constants = crystal.reciprocal(a, b, c, alpha, beta, gamma) a_, b_, c_, alpha_, beta_, gamma_ = inv_constants h_range, nh = [-1, 1], 5 k_range, nk = [0, 2], 11 l_range, nl = [-1, 0], 5 nu, nv, nw, n_atm = 2, 5, 4, 2 u = np.array([0.2, 0.1]) v = np.array([0.3, 0.4]) w = np.array([0.4, 0.5]) atm = np.array(['Fe3+', 'Mn3+']) occupancy = np.array([0.75, 0.5]) g = np.array([2., 2.]) U11 = np.array([0.5, 0.3]) U22 = np.array([0.6, 0.4]) U33 = np.array([0.4, 0.6]) U23 = np.array([0.05, -0.03]) U13 = np.array([-0.04, 0.02]) U12 = np.array([0.03, -0.02]) twins = np.eye(3).reshape(1, 3, 3) variants = np.array([1.0]) W = np.eye(3) T = space.debye_waller(h_range, k_range, l_range, nh, nk, nl, U11, U22, U33, U23, U13, U12, a_, b_, c_) A = crystal.cartesian(a, b, c, alpha, beta, gamma) B = crystal.cartesian(a_, b_, c_, alpha_, beta_, gamma_) R = crystal.cartesian_rotation(a, b, c, alpha, beta, gamma) D = crystal.cartesian_displacement(a, b, c, alpha, beta, gamma) Sx, Sy, Sz = magnetic.spin(nu, nv, nw, n_atm) index_parameters = space.mapping(h_range, k_range, l_range, nh, nk, nl, nu, nv, nw) h, k, l, H, K, L, indices, inverses, operators = index_parameters Qh, Qk, Ql = crystal.vector(h, k, l, B) Qx, Qy, Qz = crystal.transform(Qh, Qk, Ql, R) Qx_norm, Qy_norm, Qz_norm, Q = space.unit(Qx, Qy, Qz) ux, uy, uz = crystal.transform(u, v, w, A) ix, iy, iz = space.cell(nu, nv, nw, A) rx, ry, rz, atms = space.real(ux, uy, uz, ix, iy, iz, atm) phase_factor = scattering.phase(Qx, Qy, Qz, ux, uy, uz) form_factor = magnetic.form(Q, atm, g=g) Sx_k, Sy_k, Sz_k, i_dft = magnetic.transform(Sx, Sy, Sz, H, K, L, nu, nv, nw, n_atm) factors = space.prefactors(form_factor, phase_factor, occupancy) factors *= T I_ref = magnetic.intensity(Qx_norm, Qy_norm, Qz_norm, Sx_k, Sy_k, Sz_k, i_dft, factors) reduced_params = space.reduced(h_range, k_range, l_range, nh, nk, nl, nu, nv, nw) indices, reverses, symops, Nu, Nv, Nw = reduced_params symop = symmetry.laue_id(symops) I = monocrystal.magnetic(Sx, Sy, Sz, occupancy, U11, U22, U33, U23, U13, U12, ux, uy, uz, atm, h_range, k_range, l_range, indices, symop, W, B, R, D, twins, variants, nh, nk, nl, nu, nv, nw, Nu, Nv, Nw, g) np.testing.assert_array_almost_equal(I, I_ref)
def test_charge_charge_matrix(self): a = b = c = 4.1 alpha = beta = gamma = np.pi / 2 inv_constants = crystal.reciprocal(a, b, c, alpha, beta, gamma) a_, b_, c_, alpha_, beta_, gamma_ = inv_constants A = crystal.cartesian(a, b, c, alpha, beta, gamma) R = crystal.cartesian_rotation(a, b, c, alpha, beta, gamma) B = crystal.cartesian(a_, b_, c_, alpha_, beta_, gamma_) nu, nv, nw, n_atm = 4, 4, 4, 2 n = nu * nv * nw * n_atm atm = np.array(['Cs', 'Cl']) u = np.array([0.0, 0.5]) v = np.array([0.0, 0.5]) w = np.array([0.0, 0.5]) Rx, Ry, Rz = space.cell(nu, nv, nw, A) ux, uy, uz = crystal.transform(u, v, w, A) rx, ry, rz, atms = space.real(ux, uy, uz, Rx, Ry, Rz, atm) z = np.zeros(n) i, j = np.triu_indices(n) Qij = np.zeros((n, n)) Qij[i, j] = interaction.charge_charge_matrix(rx, ry, rz, nu, nv, nw, n_atm, A, B, R) Qij[j, i] = Qij[i, j] z[atms == 'Cs'] = +1 z[atms == 'Cl'] = -1 np.testing.assert_array_almost_equal(Qij, Qij.T) phi = np.dot(Qij, z) * a * np.sqrt(3) / 2 np.testing.assert_array_almost_equal(phi[atms == 'Cs'], -1.762675, 2) np.testing.assert_array_almost_equal(phi[atms == 'Cl'], +1.762675, 2) a = b = c = 5.7 alpha = beta = gamma = np.pi / 2 inv_constants = crystal.reciprocal(a, b, c, alpha, beta, gamma) a_, b_, c_, alpha_, beta_, gamma_ = inv_constants A = crystal.cartesian(a, b, c, alpha, beta, gamma) R = crystal.cartesian_rotation(a, b, c, alpha, beta, gamma) B = crystal.cartesian(a_, b_, c_, alpha_, beta_, gamma_) nu, nv, nw, n_atm = 4, 4, 4, 8 n = nu * nv * nw * n_atm atm = np.array(['Na', 'Na', 'Na', 'Na', 'Cl', 'Cl', 'Cl', 'Cl']) u = np.array([0.0, 0.0, 0.5, 0.5, 0.5, 0.5, 0.0, 0.0]) v = np.array([0.0, 0.5, 0.0, 0.5, 0.0, 0.5, 0.0, 0.5]) w = np.array([0.0, 0.5, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0]) Rx, Ry, Rz = space.cell(nu, nv, nw, A) ux, uy, uz = crystal.transform(u, v, w, A) rx, ry, rz, atms = space.real(ux, uy, uz, Rx, Ry, Rz, atm) z = np.zeros(n) i, j = np.triu_indices(n) Qij = np.zeros((n, n)) Qij[i, j] = interaction.charge_charge_matrix(rx, ry, rz, nu, nv, nw, n_atm, A, B, R) Qij[j, i] = Qij[i, j] z[atms == 'Na'] = +1 z[atms == 'Cl'] = -1 phi = np.dot(Qij, z) * a / 2 np.testing.assert_array_almost_equal(phi[atms == 'Na'], -1.747565, 2) np.testing.assert_array_almost_equal(phi[atms == 'Cl'], +1.747565, 2) a = b = 3.819 c = 6.246 alpha = beta = np.pi / 2 gamma = 2 * np.pi / 3 inv_constants = crystal.reciprocal(a, b, c, alpha, beta, gamma) a_, b_, c_, alpha_, beta_, gamma_ = inv_constants A = crystal.cartesian(a, b, c, alpha, beta, gamma) R = crystal.cartesian_rotation(a, b, c, alpha, beta, gamma) B = crystal.cartesian(a_, b_, c_, alpha_, beta_, gamma_) nu, nv, nw, n_atm = 6, 6, 6, 4 n = nu * nv * nw * n_atm atm = np.array(['Zn', 'Zn', 'S', 'S']) u = np.array([2 / 3, 1 / 3, 2 / 3, 1 / 3]) v = np.array([1 / 3, 2 / 3, 1 / 3, 2 / 3]) w = np.array([0.0, 0.5, 0.625, 0.125]) Rx, Ry, Rz = space.cell(nu, nv, nw, A) ux, uy, uz = crystal.transform(u, v, w, A) rx, ry, rz, atms = space.real(ux, uy, uz, Rx, Ry, Rz, atm) z = np.zeros(n) i, j = np.triu_indices(n) Qij = np.zeros((n, n)) Qij[i, j] = interaction.charge_charge_matrix(rx, ry, rz, nu, nv, nw, n_atm, A, B, R) Qij[j, i] = Qij[i, j] z[atms == 'Zn'] = +2 z[atms == 'S'] = -2 phi = np.dot(Qij, z) * c * 0.375 np.testing.assert_array_almost_equal(phi[atms == 'Zn'], -3.28146, 2) np.testing.assert_array_almost_equal(phi[atms == 'S'], +3.28146, 2) a = b = c = 5.52 alpha = beta = gamma = np.pi / 2 inv_constants = crystal.reciprocal(a, b, c, alpha, beta, gamma) a_, b_, c_, alpha_, beta_, gamma_ = inv_constants A = crystal.cartesian(a, b, c, alpha, beta, gamma) R = crystal.cartesian_rotation(a, b, c, alpha, beta, gamma) B = crystal.cartesian(a_, b_, c_, alpha_, beta_, gamma_) nu, nv, nw, n_atm = 5, 5, 5, 12 n = nu * nv * nw * n_atm atm = np.array( ['Ca', 'Ca', 'Ca', 'Ca', 'F', 'F', 'F', 'F', 'F', 'F', 'F', 'F']) u = np.array( [0, 0, 0.5, 0.5, 0.25, 0.25, 0.25, 0.25, 0.75, 0.75, 0.75, 0.75]) v = np.array( [0, 0.5, 0, 0.5, 0.75, 0.25, 0.25, 0.75, 0.75, 0.25, 0.25, 0.75]) w = np.array( [0, 0.5, 0.5, 0, 0.75, 0.75, 0.25, 0.25, 0.25, 0.25, 0.75, 0.75]) Rx, Ry, Rz = space.cell(nu, nv, nw, A) ux, uy, uz = crystal.transform(u, v, w, A) rx, ry, rz, atms = space.real(ux, uy, uz, Rx, Ry, Rz, atm) z = np.zeros(n) i, j = np.triu_indices(n) Qij = np.zeros((n, n)) Qij[i, j] = interaction.charge_charge_matrix(rx, ry, rz, nu, nv, nw, n_atm, A, B, R) Qij[j, i] = Qij[i, j] z[atms == 'Ca'] = +2 z[atms == 'F'] = -1 phi = np.dot(Qij, z) * a * 0.25 * np.sqrt(3) np.testing.assert_array_almost_equal(phi[atms == 'Ca'], -3.276110, 2) np.testing.assert_array_almost_equal(phi[atms == 'F'], +1.762675, 2)
def test_structural(self): a, b, c, alpha, beta, gamma = 5, 6, 7, np.pi / 2, np.pi / 3, np.pi / 4 inv_constants = crystal.reciprocal(a, b, c, alpha, beta, gamma) a_, b_, c_, alpha_, beta_, gamma_ = inv_constants h_range, nh = [-5, 5], 11 k_range, nk = [-5, 5], 11 l_range, nl = [-5, 5], 11 nu, nv, nw, n_atm = 5, 5, 5, 3 u = np.array([0.2, 0.1, 0.3]) v = np.array([0.3, 0.4, 0.2]) w = np.array([0.4, 0.5, 0.1]) atm = np.array(['Fe', 'Mn', 'Co']) occupancy = np.array([0.75, 0.5, 0.6]) U11 = np.array([0.5, 0.3, 0.1]) U22 = np.array([0.6, 0.4, 0.3]) U33 = np.array([0.4, 0.6, 0.2]) U23 = np.array([0.05, -0.03, -0.01]) U13 = np.array([-0.04, 0.02, 0.02]) U12 = np.array([0.03, -0.02, 0.01]) twins = np.eye(3).reshape(1, 3, 3) variants = np.array([1.0]) W = np.eye(3) A = crystal.cartesian(a, b, c, alpha, beta, gamma) B = crystal.cartesian(a_, b_, c_, alpha_, beta_, gamma_) R = crystal.cartesian_rotation(a, b, c, alpha, beta, gamma) D = crystal.cartesian_displacement(a, b, c, alpha, beta, gamma) ux, uy, uz = crystal.transform(u, v, w, A) ix, iy, iz = space.cell(nu, nv, nw, A) rx, ry, rz, atms = space.real(ux, uy, uz, ix, iy, iz, atm) reduced_params = space.reduced(h_range, k_range, l_range, nh, nk, nl, nu, nv, nw) indices, reverses, symops, Nu, Nv, Nw = reduced_params symop = symmetry.laue_id(symops) I = monocrystal.structural(occupancy, U11, U22, U33, U23, U13, U12, ux, uy, uz, atm, h_range, k_range, l_range, indices, symop, W, B, R, D, twins, variants, nh, nk, nl, nu, nv, nw, Nu, Nv, Nw) hmin, hmax = h_range kmin, kmax = k_range lmin, lmax = l_range h, k, l = np.meshgrid(np.arange(hmin, hmax + 1), np.arange(kmin, kmax + 1), np.arange(lmin, lmax + 1), indexing='ij') h, k, l = h.flatten(), k.flatten(), l.flatten() Qh, Qk, Ql = crystal.vector(h, k, l, B) Q = np.sqrt(Qh**2 + Qk**2 + Ql**2) n_hkl = Q.size phase_factor = np.exp(2j * np.pi * (h[:, np.newaxis] * u + k[:, np.newaxis] * v + l[:, np.newaxis] * w)) scattering_power = scattering.length(atm, n_hkl).reshape(n_hkl, n_atm) T = np.exp(-2 * np.pi**2 * (U11 * (h * a_)[:, np.newaxis]**2 + U22 * (k * b_)[:, np.newaxis]**2 + U33 * (l * c_)[:, np.newaxis]**2 + U23 * (k * l * b_ * c_ * 2)[:, np.newaxis] + U13 * (h * l * a_ * c_ * 2)[:, np.newaxis] + U12 * (h * k * a_ * b_ * 2)[:, np.newaxis])) factors = scattering_power * occupancy * T * phase_factor F = factors.sum(axis=1) I_ref = np.abs(F)**2 / (nu * nv * nw * n_atm) np.testing.assert_array_almost_equal(I, I_ref)
def test_charge_dipole_matrix(self): a = b = c = 5.4187 alpha = beta = gamma = np.pi / 2 inv_constants = crystal.reciprocal(a, b, c, alpha, beta, gamma) a_, b_, c_, alpha_, beta_, gamma_ = inv_constants A = crystal.cartesian(a, b, c, alpha, beta, gamma) R = crystal.cartesian_rotation(a, b, c, alpha, beta, gamma) B = crystal.cartesian(a_, b_, c_, alpha_, beta_, gamma_) nu, nv, nw, n_atm = 5, 5, 5, 12 n = nu * nv * nw * n_atm atm = np.array( ['S', 'S', 'S', 'S', 'S', 'S', 'S', 'S', 'Fe', 'Fe', 'Fe', 'Fe']) x = 0.385 u = np.array([ x, 1 - x, 0.5 - x, 0.5 + x, x, 1 - x, 0.5 + x, 0.5 - x, 0.0, 0.0, 0.5, 0.5 ]) v = np.array([ x, 1 - x, 0.5 + x, 0.5 - x, 0.5 - x, 0.5 + x, x, 1 - x, 0.0, 0.5, 0.0, 0.5 ]) w = np.array([ x, 1 - x, x, 1 - x, 0.5 + x, 0.5 - x, 0.5 - x, 0.5 + x, 0.0, 0.5, 0.5, 0.0 ]) Rx, Ry, Rz = space.cell(nu, nv, nw, A) ux, uy, uz = crystal.transform(u, v, w, A) rx, ry, rz, atms = space.real(ux, uy, uz, Rx, Ry, Rz, atm) z = np.zeros(n) px = np.zeros(n) py = np.zeros(n) pz = np.zeros(n) i, j = np.triu_indices(n) Qijk = np.zeros((n, n, 3)) Qijk[i, j, :] = interaction.charge_dipole_matrix(rx, ry, rz, nu, nv, nw, n_atm, A, B, R) Qijk[j, i, :] = Qijk[i, j, :] z[atms == 'S'] = -1 z[atms == 'Fe'] = +2 px.reshape(nu, nv, nw, n_atm)[:, :, :, [0, 3, 4, 6]] = +np.sqrt(3) / 3 px.reshape(nu, nv, nw, n_atm)[:, :, :, [1, 2, 5, 7]] = -np.sqrt(3) / 3 py.reshape(nu, nv, nw, n_atm)[:, :, :, [0, 2, 5, 6]] = +np.sqrt(3) / 3 py.reshape(nu, nv, nw, n_atm)[:, :, :, [1, 3, 4, 7]] = -np.sqrt(3) / 3 pz.reshape(nu, nv, nw, n_atm)[:, :, :, [0, 2, 4, 7]] = +np.sqrt(3) / 3 pz.reshape(nu, nv, nw, n_atm)[:, :, :, [1, 3, 5, 6]] = -np.sqrt(3) / 3 p = np.column_stack((px, py, pz)) am = np.array([-1.957, -7.458]) ad = np.array([-1.184, -2.898]) bm = 2.632 bd = -2.561 np.testing.assert_array_almost_equal(Qijk, np.swapaxes(Qijk, 0, 1)) E = -np.einsum('i,i->...', z, np.einsum('ijk,jk->i', Qijk, p)) / n self.assertAlmostEqual(E, -2 * (bm / a**2 + bd / a**3) / a, places=1) Qij = np.zeros((n, n)) Qij[i, j] = interaction.charge_charge_matrix(rx, ry, rz, nu, nv, nw, n_atm, A, B, R) Qij[j, i] = Qij[i, j] E = np.dot(z, np.dot(Qij, z)) / n self.assertAlmostEqual(E, (2 * am[0] + am[1]) / a * 2 / 3, places=2) Qijm = np.zeros((n, n, 6)) Qijkl = np.zeros((n, n, 3, 3)) Qijm[i, j, :] = interaction.dipole_dipole_matrix(rx, ry, rz, nu, nv, nw, n_atm, A, B, R) Qijm[j, i, :] = Qijm[i, j, :] Qijkl[:, :, 0, 0] = Qijm[:, :, 0] Qijkl[:, :, 1, 1] = Qijm[:, :, 1] Qijkl[:, :, 2, 2] = Qijm[:, :, 2] Qijkl[:, :, 1, 2] = Qijkl[:, :, 2, 1] = Qijm[:, :, 3] Qijkl[:, :, 0, 2] = Qijkl[:, :, 2, 0] = Qijm[:, :, 4] Qijkl[:, :, 0, 1] = Qijkl[:, :, 1, 0] = Qijm[:, :, 5] E = np.einsum('ik,ik->...', p, np.einsum('ijkl,jl->ik', Qijkl, p)) / n self.assertAlmostEqual(E, -(2 * ad[0] + ad[1]) / a**3 * 2 / 3, places=2)
def test_disordered(self): folder = os.path.abspath(os.path.join(directory, '..', 'data')) uc_dict = crystal.unitcell(folder=folder, filename='CaTiOSiO4.cif', tol=1e-4) u = uc_dict['u'] v = uc_dict['v'] w = uc_dict['w'] occ = uc_dict['occupancy'] disp = uc_dict['displacement'] mom = uc_dict['moment'] atm = uc_dict['atom'] n_atm = uc_dict['n_atom'] nu, nv, nw = 2, 3, 4 constants = crystal.parameters(folder=folder, filename='CaTiOSiO4.cif') A = crystal.cartesian(*constants) C = crystal.cartesian_moment(*constants) D = crystal.cartesian_displacement(*constants) ux, uy, uz = crystal.transform(u, v, w, A) ix, iy, iz = space.cell(nu, nv, nw, A) rx, ry, rz, atms = space.real(ux, uy, uz, ix, iy, iz, atm) U11, U22, U33, U23, U13, U12 = disp.T U = np.row_stack(displacive.cartesian(U11, U22, U33, U23, U13, U12, D)) Ux, Uy, Uz = displacive.expansion(nu, nv, nw, n_atm, U) A_r = occupational.composition(nu, nv, nw, n_atm, occ) delta = (occ * (1 + A_r.reshape(nu, nv, nw, n_atm))).flatten() mu1, mu2, mu3 = mom.T mu = np.row_stack(magnetic.cartesian(mu1, mu2, mu3, C)) Sx, Sy, Sz = magnetic.spin(nu, nv, nw, n_atm, mu) crystal.disordered(delta, Ux, Uy, Uz, Sx, Sy, Sz, rx, ry, rz, nu, nv, nw, atm, A, folder + '/disordered_CaTiOSiO4.cif', folder=folder, filename='CaTiOSiO4.cif', ulim=[0, nu], vlim=[0, nv], wlim=[0, nw]) UC_dict = crystal.unitcell(folder=folder, filename='disordered_CaTiOSiO4.cif', tol=1e-4) Atm = UC_dict['atom'] np.testing.assert_array_equal(atms == Atm, True) os.remove(folder + '/disordered_CaTiOSiO4.cif') uc_dict = crystal.unitcell(folder=folder, filename='CuMnO2.mcif', tol=1e-4) u = uc_dict['u'] v = uc_dict['v'] w = uc_dict['w'] occ = uc_dict['occupancy'] disp = uc_dict['displacement'] mom = uc_dict['moment'] atm = uc_dict['atom'] n_atm = uc_dict['n_atom'] nu, nv, nw = 2, 3, 4 constants = crystal.parameters(folder=folder, filename='CuMnO2.mcif') A = crystal.cartesian(*constants) C = crystal.cartesian_moment(*constants) D = crystal.cartesian_displacement(*constants) ux, uy, uz = crystal.transform(u, v, w, A) ix, iy, iz = space.cell(nu, nv, nw, A) rx, ry, rz, atms = space.real(ux, uy, uz, ix, iy, iz, atm) Uiso = disp.flatten() Ux, Uy, Uz = displacive.expansion(nu, nv, nw, n_atm, Uiso) A_r = occupational.composition(nu, nv, nw, n_atm, occ) delta = (occ * (1 + A_r.reshape(nu, nv, nw, n_atm))).flatten() mu1, mu2, mu3 = mom.T mu = np.row_stack(magnetic.cartesian(mu1, mu2, mu3, C)) Sx, Sy, Sz = magnetic.spin(nu, nv, nw, n_atm, mu) crystal.disordered(delta, Ux, Uy, Uz, Sx, Sy, Sz, rx, ry, rz, nu, nv, nw, atm, A, folder + '/disordered_CuMnO2.mcif', folder=folder, filename='CuMnO2.mcif', ulim=[0, nu], vlim=[0, nv], wlim=[0, nw]) UC_dict = crystal.unitcell(folder=folder, filename='disordered_CuMnO2.mcif', tol=1e-4) Atm = UC_dict['atom'] np.testing.assert_array_equal(atms == Atm, True) os.remove(folder + '/disordered_CuMnO2.mcif')
def test_dipole_dipole_matrix(self): a = b = c = 4.04 alpha = beta = gamma = np.pi / 2 inv_constants = crystal.reciprocal(a, b, c, alpha, beta, gamma) a_, b_, c_, alpha_, beta_, gamma_ = inv_constants A = crystal.cartesian(a, b, c, alpha, beta, gamma) R = crystal.cartesian_rotation(a, b, c, alpha, beta, gamma) B = crystal.cartesian(a_, b_, c_, alpha_, beta_, gamma_) nu, nv, nw, n_atm = 8, 8, 16, 1 n = nu * nv * nw * n_atm atm = np.array(['Ti']) u = np.array([0.5]) v = np.array([0.5]) w = np.array([0.5]) Rx, Ry, Rz = space.cell(nu, nv, nw, A) ux, uy, uz = crystal.transform(u, v, w, A) rx, ry, rz, atms = space.real(ux, uy, uz, Rx, Ry, Rz, atm) px = np.zeros(n) py = np.zeros(n) pz = np.zeros(n) i, j = np.triu_indices(n) Qijm = np.zeros((n, n, 6)) Qijkl = np.zeros((n, n, 3, 3)) Qijm[i, j, :] = interaction.dipole_dipole_matrix(rx, ry, rz, nu, nv, nw, n_atm, A, B, R) Qijm[j, i, :] = Qijm[i, j, :] Qijkl[:, :, 0, 0] = Qijm[:, :, 0] Qijkl[:, :, 1, 1] = Qijm[:, :, 1] Qijkl[:, :, 2, 2] = Qijm[:, :, 2] Qijkl[:, :, 1, 2] = Qijkl[:, :, 2, 1] = Qijm[:, :, 3] Qijkl[:, :, 0, 2] = Qijkl[:, :, 2, 0] = Qijm[:, :, 4] Qijkl[:, :, 0, 1] = Qijkl[:, :, 1, 0] = Qijm[:, :, 5] np.testing.assert_array_almost_equal(Qijkl, np.swapaxes(Qijkl, 0, 1)) np.testing.assert_array_almost_equal(Qijkl, np.swapaxes(Qijkl, 2, 3)) px.reshape(nu, nv, nw, n_atm)[:, :, :, :] = 0 py.reshape(nu, nv, nw, n_atm)[:, :, :, :] = 0 pz.reshape(nu, nv, nw, n_atm)[:, :, :, :] = 1 p = np.column_stack((px, py, pz)) E = np.einsum('ijkl,jl->ik', Qijkl, p) * a**3 / 2 np.testing.assert_array_almost_equal(E[..., 2], -2.09440, 2) px.reshape(nu, nv, nw, n_atm)[:, :, :, :] = 0 py.reshape(nu, nv, nw, n_atm)[:, :, :, :] = 0 pz.reshape(nu, nv, nw, n_atm)[:, :, 0::2, :] = +1 pz.reshape(nu, nv, nw, n_atm)[:, :, 1::2, :] = -1 p = np.column_stack((px, py, pz)) E = np.einsum('ijkl,jl->ik', Qijkl, p)[..., 2] * a**3 / 2 np.testing.assert_array_almost_equal(E[..., 0::2], +4.84372, 2) np.testing.assert_array_almost_equal(E[..., 1::2], -4.84372, 2) px.reshape(nu, nv, nw, n_atm)[:, :, 0::2, :] = +1 px.reshape(nu, nv, nw, n_atm)[:, :, 1::2, :] = -1 py.reshape(nu, nv, nw, n_atm)[:, :, :, :] = 0 pz.reshape(nu, nv, nw, n_atm)[:, :, :, :] = 0 p = np.column_stack((px, py, pz)) E = np.einsum('ijkl,jl->ik', Qijkl, p)[..., 0] * a**3 / 2 np.testing.assert_array_almost_equal(E[0::2], -2.42186, 2) np.testing.assert_array_almost_equal(E[1::2], +2.42186, 2) px.reshape(nu, nv, nw, n_atm)[:, :, :, :] = 0 py.reshape(nu, nv, nw, n_atm)[:, :, :, :] = 0 pz.reshape(nu, nv, nw, n_atm)[0::2, 0::2, :, :] = +1 pz.reshape(nu, nv, nw, n_atm)[1::2, 1::2, :, :] = +1 pz.reshape(nu, nv, nw, n_atm)[0::2, 1::2, :, :] = -1 pz.reshape(nu, nv, nw, n_atm)[1::2, 0::2, :, :] = -1 p = np.column_stack((px, py, pz)) E = np.einsum('ijkl,jl->ik', Qijkl, p)[..., 2] * a**3 / 2 E = E.reshape(nu, nv, nw, n_atm) np.testing.assert_array_almost_equal(E[0::2, 0::2, :, :], -2.67679, 2) np.testing.assert_array_almost_equal(E[1::2, 1::2, :, :], -2.67679, 2) np.testing.assert_array_almost_equal(E[0::2, 1::2, :, :], +2.67679, 2) np.testing.assert_array_almost_equal(E[1::2, 0::2, :, :], +2.67679, 2) px.reshape(nu, nv, nw, n_atm)[0::2, 0::2, :, :] = +1 px.reshape(nu, nv, nw, n_atm)[1::2, 1::2, :, :] = +1 px.reshape(nu, nv, nw, n_atm)[0::2, 1::2, :, :] = -1 px.reshape(nu, nv, nw, n_atm)[1::2, 0::2, :, :] = -1 py.reshape(nu, nv, nw, n_atm)[:, :, :, :] = 0 pz.reshape(nu, nv, nw, n_atm)[:, :, :, :] = 0 p = np.column_stack((px, py, pz)) E = np.einsum('ijkl,jl->ik', Qijkl, p)[..., 0] * a**3 / 2 E = E.reshape(nu, nv, nw, n_atm) np.testing.assert_array_almost_equal(E[0::2, 0::2, :, :], +1.33839, 2) np.testing.assert_array_almost_equal(E[1::2, 1::2, :, :], +1.33839, 2) np.testing.assert_array_almost_equal(E[0::2, 1::2, :, :], -1.33839, 2) np.testing.assert_array_almost_equal(E[1::2, 0::2, :, :], -1.33839, 2) px.reshape(nu, nv, nw, n_atm)[:, :, :, :] = 0 py.reshape(nu, nv, nw, n_atm)[:, :, :, :] = 0 pz.reshape(nu, nv, nw, n_atm)[0::2, 0::2, 0::2, :] = +1 pz.reshape(nu, nv, nw, n_atm)[1::2, 1::2, 0::2, :] = +1 pz.reshape(nu, nv, nw, n_atm)[0::2, 1::2, 1::2, :] = +1 pz.reshape(nu, nv, nw, n_atm)[1::2, 0::2, 1::2, :] = +1 pz.reshape(nu, nv, nw, n_atm)[1::2, 1::2, 1::2, :] = -1 pz.reshape(nu, nv, nw, n_atm)[1::2, 0::2, 0::2, :] = -1 pz.reshape(nu, nv, nw, n_atm)[0::2, 1::2, 0::2, :] = -1 pz.reshape(nu, nv, nw, n_atm)[0::2, 0::2, 1::2, :] = -1 p = np.column_stack((px, py, pz)) E = np.einsum('ijkl,jl->ik', Qijkl, p) * a**3 / 2 np.testing.assert_array_almost_equal(E, 0, decimal=2) a = c = 2 * 4.04 b = 4.04 alpha = beta = gamma = np.pi / 2 inv_constants = crystal.reciprocal(a, b, c, alpha, beta, gamma) a_, b_, c_, alpha_, beta_, gamma_ = inv_constants A = crystal.cartesian(a, b, c, alpha, beta, gamma) R = crystal.cartesian_rotation(a, b, c, alpha, beta, gamma) B = crystal.cartesian(a_, b_, c_, alpha_, beta_, gamma_) nu, nv, nw, n_atm = 8, 8, 8, 2 n = nu * nv * nw * n_atm atm = np.array(['Ti', 'Ti']) u = np.array([0.0, 0.5]) v = np.array([0.0, 0.0]) w = np.array([0.0, 0.5]) Rx, Ry, Rz = space.cell(nu, nv, nw, A) ux, uy, uz = crystal.transform(u, v, w, A) rx, ry, rz, atms = space.real(ux, uy, uz, Rx, Ry, Rz, atm) px = np.zeros(n) py = np.zeros(n) pz = np.zeros(n) i, j = np.triu_indices(n) Qijm = np.zeros((n, n, 6)) Qijkl = np.zeros((n, n, 3, 3)) Qijm[i, j, :] = interaction.dipole_dipole_matrix(rx, ry, rz, nu, nv, nw, n_atm, A, B, R) Qijm[j, i, :] = Qijm[i, j, :] Qijkl[:, :, 0, 0] = Qijm[:, :, 0] Qijkl[:, :, 1, 1] = Qijm[:, :, 1] Qijkl[:, :, 2, 2] = Qijm[:, :, 2] Qijkl[:, :, 1, 2] = Qijkl[:, :, 2, 1] = Qijm[:, :, 3] Qijkl[:, :, 0, 2] = Qijkl[:, :, 2, 0] = Qijm[:, :, 4] Qijkl[:, :, 0, 1] = Qijkl[:, :, 1, 0] = Qijm[:, :, 5] px.reshape(nu, nv, nw, n_atm)[0::2, :, 0::2, :] = -np.sqrt(2) / 2 px.reshape(nu, nv, nw, n_atm)[1::2, :, 1::2, :] = -np.sqrt(2) / 2 px.reshape(nu, nv, nw, n_atm)[0::2, :, 1::2, :] = +np.sqrt(2) / 2 px.reshape(nu, nv, nw, n_atm)[1::2, :, 0::2, :] = +np.sqrt(2) / 2 py.reshape(nu, nv, nw, n_atm)[:, :, :, :] = 0 pz.reshape(nu, nv, nw, n_atm)[0::2, :, 0::2, :] = +np.sqrt(2) / 2 pz.reshape(nu, nv, nw, n_atm)[1::2, :, 1::2, :] = +np.sqrt(2) / 2 pz.reshape(nu, nv, nw, n_atm)[0::2, :, 1::2, :] = -np.sqrt(2) / 2 pz.reshape(nu, nv, nw, n_atm)[1::2, :, 0::2, :] = -np.sqrt(2) / 2 p = np.column_stack((px, py, pz)) E = np.einsum('ijkl,jl->ik', Qijkl, p) * a**3 / 16 E = np.sqrt(np.sum(E**2, axis=1)) np.testing.assert_array_almost_equal(E, 2.93226, 1)
def test_pairs(self): folder = os.path.abspath(os.path.join(directory, '..', 'data')) uc_dict = crystal.unitcell(folder=folder, filename='CaTiOSiO4.cif') u = uc_dict['u'] v = uc_dict['v'] w = uc_dict['w'] atms = uc_dict['atom'] constants = crystal.parameters(folder=folder, filename='CaTiOSiO4.cif') A = crystal.cartesian(*constants) pair_dict = crystal.pairs(u, v, w, atms, A) pairs = [] coordination = [] for i in pair_dict.keys(): atm = atms[i] if (atm != 'O'): pair_num, pair_atm = list(pair_dict[i])[0] coord_num = len(pair_dict[i][(pair_num, pair_atm)][0]) pairs.append('_'.join((atm, pair_atm))) coordination.append(coord_num) pairs = np.array(pairs) coordination = np.array(coordination) mask = pairs == 'Ca_O' np.testing.assert_array_equal(coordination[mask], 7) mask = pairs == 'Ti_O' np.testing.assert_array_equal(coordination[mask], 6) mask = pairs == 'Si_O' np.testing.assert_array_equal(coordination[mask], 4) uc_dict = crystal.unitcell(folder=folder, filename='Tb2Ir3Ga9.cif') u = uc_dict['u'] v = uc_dict['v'] w = uc_dict['w'] atms = uc_dict['atom'] constants = crystal.parameters(folder=folder, filename='Tb2Ir3Ga9.cif') A = crystal.cartesian(*constants) pair_dict = crystal.pairs(u, v, w, atms, A) pairs = [] for i in pair_dict.keys(): atm = atms[i] if (atm == 'Tb'): for pair in pair_dict[i].keys(): if pair[1] == 'Tb': for ind in pair_dict[i][pair][0]: pairs.append([i, ind]) pairs = np.unique(pairs) self.assertEqual(pairs.size, (atms == 'Tb').sum()) pair_dict = crystal.pairs(u, v, w, atms, A, extend=True) for i in pair_dict.keys(): d_ref = 0 for pair in pair_dict[i].keys(): j, atm_j = pair for j, img in zip(*pair_dict[i][pair]): du = u[j] - u[i] + img[0] dv = v[j] - v[i] + img[1] dw = w[j] - w[i] + img[2] dx, dy, dz = crystal.transform(du, dv, dw, A) d = np.sqrt(dx**2 + dy**2 + dz**2) self.assertGreaterEqual(np.round(d, 6), np.round(d_ref, 6)) self.assertEqual(atms[j], atm_j) d_ref = d
def test_magnetic_energy(self): nu, nv, nw = 3, 4, 5 n_atm = 2 atm = np.array(['Fe3+', 'Fe3+']) u, v, w = np.array([0, 0.5]), np.array([0, 0.5]), np.array([0, 0.5]) a, b, c, alpha, beta, gamma = 5, 5, 5, np.pi / 2, np.pi / 2, np.pi / 2 A = crystal.cartesian(a, b, c, alpha, beta, gamma) ux, uy, uz = crystal.transform(u, v, w, A) pair_dict = crystal.pairs(u, v, w, atm, A, extend=True) img_ind_i, img_ind_j, img_ind_k, atm_ind = [], [], [], [] du, dv, dw = [], [], [] for i in pair_dict.keys(): for pair in pair_dict[i].keys(): pair_no, atm_j = pair ind_i, ind_j, ind_k, atm_a = [], [], [], [] for j, img in zip(*pair_dict[i][pair]): ind_i.append(img[0]) ind_j.append(img[1]) ind_k.append(img[2]) atm_a.append(j) du.append(u[j] - u[i] + img[0]) dv.append(v[j] - v[i] + img[1]) dw.append(w[j] - w[i] + img[2]) img_ind_i.append(ind_i) img_ind_j.append(ind_j) img_ind_k.append(ind_k) atm_ind.append(atm_a) img_ind_i = np.array(img_ind_i, dtype=np.int_) img_ind_j = np.array(img_ind_j, dtype=np.int_) img_ind_k = np.array(img_ind_k, dtype=np.int_) atm_ind = np.array(atm_ind, dtype=np.int_) du, dv, dw = np.array(du), np.array(dv), np.array(dw) dx, dy, dz = crystal.transform(du, dv, dw, A) dx = dx.reshape(atm_ind.shape) dy = dy.reshape(atm_ind.shape) dz = dz.reshape(atm_ind.shape) d = np.sqrt(dx**2 + dy**2 + dz**2) n_pair = atm_ind.shape[1] dist = np.round(d / 1e-2).astype(int) _, inv_ind = np.unique(dist, return_inverse=True) pair_ind = np.arange(n_pair + 1, dtype=np.int_)[inv_ind].reshape(n_atm, n_pair) mask = d < 0.99 * np.sqrt(2 * a**2) n_pair = mask.sum() // n_atm dx = dx[mask].reshape(n_atm, n_pair) dy = dy[mask].reshape(n_atm, n_pair) dz = dz[mask].reshape(n_atm, n_pair) pair_ind = pair_ind[mask].reshape(n_atm, n_pair) img_ind_i = img_ind_i[mask].reshape(n_atm, n_pair) img_ind_j = img_ind_j[mask].reshape(n_atm, n_pair) img_ind_k = img_ind_k[mask].reshape(n_atm, n_pair) atm_ind = atm_ind[mask].reshape(n_atm, n_pair) d_xyz = np.stack((dx, dy, dz)) inv_d_xyz = -d_xyz pair_inv = np.zeros((n_atm, n_pair), dtype=np.int_) for i in range(n_atm): for p in range(n_pair): for q in range(n_pair): if (np.allclose(d_xyz[:, i, p], inv_d_xyz[:, atm_ind[i, p], q])): pair_inv[i, p] = q pair_ij = np.zeros((n_atm, n_pair), dtype=np.intc) J = np.zeros((n_pair, 3, 3)) K = np.zeros((n_atm, 3, 3)) g = np.zeros((n_atm, 3, 3)) B = np.zeros(3) Jx, Jy, Jz = -3, -2, -1 Kx, Ky, Kz = -1, -3, -2 gx, gy, gz = 2, 3, 4 Bx, By, Bz = 1, 2, 3 J[:n_pair, :, :] = np.array([[Jx, 0, 0], [0, Jy, 0], [0, 0, Jz]], dtype=float) K[:n_atm, :, :] = np.array([[Kx, 0, 0], [0, Ky, 0], [0, 0, Kz]], dtype=float) g[:n_atm, :, :] = np.array([[gx, 0, 0], [0, gy, 0], [0, 0, gz]], dtype=float) B[:] = Bx, By, Bz ix, iy, iz = space.cell(nu, nv, nw, A) rx, ry, rz, ion = space.real(ux, uy, uz, ix, iy, iz, atm) n = nu * nv * nw * n_atm M = 3 Sx = np.zeros((nu, nv, nw, n_atm, M)) Sy = np.zeros((nu, nv, nw, n_atm, M)) Sz = np.zeros((nu, nv, nw, n_atm, M)) Sx[..., 0], Sy[..., 0], Sz[..., 0] = 1, 0, 0 Sx[..., 1], Sy[..., 1], Sz[..., 1] = 0, 1, 0 Sx[..., 2], Sy[..., 2], Sz[..., 2] = 0, 0, 1 E = simulation.magnetic_energy(Sx, Sy, Sz, J, K, g, B, atm_ind, img_ind_i, img_ind_j, img_ind_k, pair_ind, pair_ij) self.assertAlmostEqual(E[..., 0].sum(), -(0.5 * Jx * n_pair + Kx + Bx * gx) * n) self.assertAlmostEqual(E[..., 1].sum(), -(0.5 * Jy * n_pair + Ky + By * gy) * n) self.assertAlmostEqual(E[..., 2].sum(), -(0.5 * Jz * n_pair + Kz + Bz * gz) * n)