1+ # ## FOURIER TRANSFORMS ###
2+
3+ # TODO : the same function could be used for both DenseOperator and FHIaimsDenseOperator
4+ # (except for the data I would need to use SH conversion)
5+ # TODO : modify to enable float conversions
6+ function fourier_transform (in_operator:: BSparseOperator , kpoints, phases, :: Type{DenseOperator} ; float = Float64)
7+ in_metadata = get_metadata (in_operator)
8+ in_sparsity = get_sparsity (in_operator)
9+ in_atoms = get_atoms (in_operator)
10+ in_basisset = get_basisset (in_operator)
11+ in_spins = get_spins (in_operator)
12+ in_kind = get_kind (in_operator)
13+
14+ # Convert metadata
15+ out_sparsity = convert_sparsity (in_metadata, RecipDenseSparsity, hermitian = in_sparsity. hermitian)
16+ out_metadata_list = [
17+ RecipDenseMetadata (in_atoms, out_sparsity, in_basisset, in_spins, SVector {3} (kpoint))
18+ for kpoint in kpoints
19+ ]
20+
21+ # Initialize out_operator with zeros
22+ out_operator_list = [
23+ build_operator (DenseOperator, in_kind, out_metadata; uninit = false , value = zero (Complex{float}))
24+ for out_metadata in out_metadata_list
25+ ]
26+
27+ # Perform Fourier transform
28+ for (out_operator, phases_k) in zip (out_operator_list, phases)
29+ fourier_transform_data! (out_operator, in_operator, phases_k)
30+ end
31+
32+ return length (out_operator_list) == 1 ? first (out_operator_list) : out_operator_list
33+ end
34+
35+ # TODO : write another method but with shifts (or alternatively write the method with shifts
36+ # but force the compiler to remove shifts for matching SH convention)
37+ function fourier_transform_data! (out_operator:: DenseOperator , in_operator:: BSparseOperator , phases_k)
38+ in_keydata = get_keydata (in_operator)
39+ in_sparsity = get_sparsity (in_operator)
40+ out_data = get_data (out_operator)
41+ out_basisset = get_basisset (out_operator)
42+ out_float = get_float (out_operator)
43+
44+ ilocal2iglobal = get_ilocal2iglobal (in_sparsity)
45+ atom2offset = get_atom2offset (out_basisset)
46+
47+ for ((iat, jat), in_block) in pairs (in_keydata)
48+
49+ phases_kij = phases_k[ilocal2iglobal[(iat, jat)]]
50+ atom2offset_i = atom2offset[iat]
51+ atom2offset_j = atom2offset[jat]
52+
53+ for jb in axes (in_block, 2 )
54+ jb_dense = jb + atom2offset_j
55+ for ib in axes (in_block, 1 )
56+ ib_dense = ib + atom2offset_i
57+
58+ tmp = zero (out_float)
59+ @inbounds for iR in axes (in_block, 3 )
60+ tmp += in_block[ib, jb, iR] * phases_kij[iR]
61+ end
62+
63+ out_data[ib_dense, jb_dense] = tmp
64+ end
65+ end
66+
67+ # @tullio tmp[ib,jb] := in_block[ib,jb,R] * phases_kij[R]
68+ # out_data[atom2basis[iat], atom2basis[jat]] = tmp
69+ end
70+
71+ # TODO : I could use Hermitian array instead, probably directly in build stage
72+ if in_sparsity. hermitian
73+ for jb_dense in 1 : size (out_data, 1 )
74+ for ib_dense in 1 : jb_dense
75+ out_data[jb_dense, ib_dense] = conj (out_data[ib_dense, jb_dense])
76+ end
77+ end
78+ end
79+ end
80+
81+ # Only a contribution to out_operator because in_operator contains only a single k-point
82+ function inv_fourier_transform_data! (out_operator:: BSparseOperator , in_operator:: DenseOperator , phases_k, weight)
83+ out_basisset = get_basisset (out_operator)
84+ out_keydata = get_keydata (out_operator)
85+ out_sparsity = get_sparsity (out_operator)
86+ in_data = get_data (in_operator)
87+
88+ ilocal2iglobal = get_ilocal2iglobal (out_sparsity)
89+ atom2offset = get_atom2offset (out_basisset)
90+
91+ for ((iat, jat), out_block) in pairs (out_keydata)
92+
93+ # Assuming phases is a vector here (i.e. for a single k-point)
94+ inv_phases_kij = conj (phases_k[ilocal2iglobal[(iat, jat)]])
95+ atom2offset_i = atom2offset[iat]
96+ atom2offset_j = atom2offset[jat]
97+
98+ for iR in axes (out_block, 3 )
99+ for jb in axes (out_block, 2 )
100+ jb_dense = jb + atom2offset_j
101+ for ib in axes (out_block, 1 )
102+ ib_dense = ib + atom2offset_i
103+ out_block[ib, jb, iR] += weight * real (in_data[ib_dense, jb_dense] * inv_phases_kij[iR])
104+ end
105+ end
106+ end
107+ end
108+
109+ end
0 commit comments