1use crate::fft::fftfreq;
11
12pub fn dipole_kernel(
25 nx: usize, ny: usize, nz: usize,
26 vsx: f64, vsy: f64, vsz: f64,
27 bdir: (f64, f64, f64),
28) -> Vec<f64> {
29 let n_total = nx * ny * nz;
30 let mut d = vec![0.0; n_total];
31
32 let kx = fftfreq(nx, vsx);
34 let ky = fftfreq(ny, vsy);
35 let kz = fftfreq(nz, vsz);
36
37 let (bx, by, bz) = bdir;
39 let bnorm = (bx * bx + by * by + bz * bz).sqrt();
40 let bx = bx / bnorm;
41 let by = by / bnorm;
42 let bz = bz / bnorm;
43
44 let one_third = 1.0 / 3.0;
45
46 for k in 0..nz {
49 let kz_val = kz[k];
50 for j in 0..ny {
51 let ky_val = ky[j];
52 for i in 0..nx {
53 let kx_val = kx[i];
54
55 let k_dot_b = kx_val * bx + ky_val * by + kz_val * bz;
56 let k_squared = kx_val * kx_val + ky_val * ky_val + kz_val * kz_val;
57
58 let idx = i + j * nx + k * nx * ny;
59
60 if k_squared > 1e-20 {
61 d[idx] = one_third - (k_dot_b * k_dot_b) / k_squared;
62 } else {
63 d[idx] = 0.0;
65 }
66 }
67 }
68 }
69
70 d
71}
72
73pub fn dipole_kernel_default(
75 nx: usize, ny: usize, nz: usize,
76 vsx: f64, vsy: f64, vsz: f64,
77) -> Vec<f64> {
78 dipole_kernel(nx, ny, nz, vsx, vsy, vsz, (0.0, 0.0, 1.0))
79}
80
81use crate::fft::fftfreq_f32;
86
87pub fn dipole_kernel_f32(
92 nx: usize, ny: usize, nz: usize,
93 vsx: f32, vsy: f32, vsz: f32,
94 bdir: (f32, f32, f32),
95) -> Vec<f32> {
96 let n_total = nx * ny * nz;
97 let mut d = vec![0.0f32; n_total];
98
99 let kx = fftfreq_f32(nx, vsx);
101 let ky = fftfreq_f32(ny, vsy);
102 let kz = fftfreq_f32(nz, vsz);
103
104 let (bx, by, bz) = bdir;
106 let bnorm = (bx * bx + by * by + bz * bz).sqrt();
107 let bx = bx / bnorm;
108 let by = by / bnorm;
109 let bz = bz / bnorm;
110
111 let one_third = 1.0f32 / 3.0;
112
113 for k in 0..nz {
116 let kz_val = kz[k];
117 for j in 0..ny {
118 let ky_val = ky[j];
119 for i in 0..nx {
120 let kx_val = kx[i];
121
122 let k_dot_b = kx_val * bx + ky_val * by + kz_val * bz;
123 let k_squared = kx_val * kx_val + ky_val * ky_val + kz_val * kz_val;
124
125 let idx = i + j * nx + k * nx * ny;
126
127 if k_squared > 1e-10 {
128 d[idx] = one_third - (k_dot_b * k_dot_b) / k_squared;
129 } else {
130 d[idx] = 0.0;
132 }
133 }
134 }
135 }
136
137 d
138}
139
140pub fn dipole_kernel_default_f32(
142 nx: usize, ny: usize, nz: usize,
143 vsx: f32, vsy: f32, vsz: f32,
144) -> Vec<f32> {
145 dipole_kernel_f32(nx, ny, nz, vsx, vsy, vsz, (0.0, 0.0, 1.0))
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151
152 #[test]
153 fn test_dipole_kernel_dc() {
154 let d = dipole_kernel_default(4, 4, 4, 1.0, 1.0, 1.0);
155 assert!(d[0].abs() < 1e-10, "DC component should be 0, got {}", d[0]);
157 }
158
159 #[test]
160 fn test_dipole_kernel_range() {
161 let d = dipole_kernel_default(8, 8, 8, 1.0, 1.0, 1.0);
162
163 for (i, &val) in d.iter().enumerate() {
165 if i > 0 { assert!(val >= -2.0/3.0 - 1e-10 && val <= 1.0/3.0 + 1e-10,
167 "Dipole value {} out of range at index {}", val, i);
168 }
169 }
170 }
171
172 #[test]
173 fn test_dipole_kernel_symmetry() {
174 let n = 8;
176 let d = dipole_kernel_default(n, n, n, 1.0, 1.0, 1.0);
177
178 for i in 1..n/2 {
180 let i_neg = n - i;
181 for j in 1..n/2 {
182 let j_neg = n - j;
183 for k in 1..n/2 {
184 let k_neg = n - k;
185
186 let idx1 = i * n * n + j * n + k;
187 let idx2 = i_neg * n * n + j_neg * n + k_neg;
188
189 let diff = (d[idx1] - d[idx2]).abs();
190 assert!(diff < 1e-10,
191 "Symmetry broken at ({},{},{}) vs ({},{},{}): {} vs {}",
192 i, j, k, i_neg, j_neg, k_neg, d[idx1], d[idx2]);
193 }
194 }
195 }
196 }
197}