1use nalgebra::DVector;
2use ndarray::*;
3use num_dual::DualNum;
4use std::f64::consts::{FRAC_PI_3, PI};
6use std::ops::Mul;
7
8#[derive(Clone)]
10pub struct WeightFunction<T> {
11 pub prefactor: DVector<T>,
13 pub kernel_radius: DVector<T>,
15 pub shape: WeightFunctionShape,
17}
18
19impl<T: DualNum<f64> + Copy> WeightFunction<T> {
20 pub fn new_unscaled(kernel_radius: DVector<T>, shape: WeightFunctionShape) -> Self {
22 Self {
23 prefactor: DVector::from_element(kernel_radius.len(), T::one()),
24 kernel_radius,
25 shape,
26 }
27 }
28
29 pub fn new_scaled(kernel_radius: DVector<T>, shape: WeightFunctionShape) -> Self {
31 let unscaled = Self::new_unscaled(kernel_radius, shape);
32 let weight_constants = unscaled.scalar_weight_constants(T::zero());
33 let weight_constants = DVector::from(weight_constants.to_vec());
34 Self {
35 prefactor: weight_constants.map(|w| w.recip()),
36 kernel_radius: unscaled.kernel_radius,
37 shape,
38 }
39 }
40
41 pub(crate) fn fft_scalar_weight_functions<D: Dimension, T2: DualNum<f64>>(
44 &self,
45 k_abs: &Array<T2, D>,
46 lanczos: &Option<Array<T2, D>>,
47 ) -> Array<T, D::Larger>
48 where
49 T: Mul<T2, Output = T>,
50 D::Larger: Dimension<Smaller = D>,
51 {
52 let mut d = vec![self.prefactor.len()];
54 k_abs.shape().iter().for_each(|&x| d.push(x));
55 let mut w = Array::zeros(d.into_dimension())
56 .into_dimensionality()
57 .unwrap();
58
59 for i in 0..self.prefactor.len() {
61 let radius = self.kernel_radius[i];
62 let p = self.prefactor[i];
63 let rik = k_abs.mapv(|k| radius * k);
64 let mut w_i = w.index_axis_mut(Axis(0), i);
65 w_i.assign(&match self.shape {
66 WeightFunctionShape::Theta => rik.mapv(|rik| {
67 (rik.sph_j0() + rik.sph_j2()) * 4.0 * FRAC_PI_3 * radius.powi(3) * p
68 }),
69 WeightFunctionShape::Delta => {
70 rik.mapv(|rik| rik.sph_j0() * 4.0 * PI * radius.powi(2) * p)
71 }
72 WeightFunctionShape::KR1 => {
73 rik.mapv(|rik| (rik.sph_j0() + rik.cos()) * 0.5 * radius * p)
74 }
75 WeightFunctionShape::KR0 => rik.mapv(|rik| (rik * rik.sin() * 0.5 + rik.cos()) * p),
76 WeightFunctionShape::DeltaVec => unreachable!(),
77 });
78
79 if let Some(l) = lanczos {
81 let w_i_l = &w_i * l;
82 w_i.assign(&w_i_l);
83 }
84 }
85
86 w
88 }
89
90 pub(crate) fn fft_vector_weight_functions<D: Dimension, T2: DualNum<f64>>(
94 &self,
95 k_abs: &Array<T2, D>,
96 k: &Array<T2, D::Larger>,
97 lanczos: &Option<Array<T2, D>>,
98 ) -> Array<T, <D::Larger as Dimension>::Larger>
99 where
100 D::Larger: Dimension<Smaller = D>,
101 <D::Larger as Dimension>::Larger: Dimension<Smaller = D::Larger>,
102 T: Mul<T2, Output = T>,
103 {
104 let mut d = vec![k.shape()[0], self.prefactor.len()];
106 k.shape().iter().skip(1).for_each(|&x| d.push(x));
107 let mut w = Array::zeros(d.into_dimension())
108 .into_dimensionality()
109 .unwrap();
110
111 for (k_x, mut w_x) in k.outer_iter().zip(w.outer_iter_mut()) {
113 for i in 0..self.prefactor.len() {
115 let radius = self.kernel_radius[i];
116 let p = self.prefactor[i];
117 let rik = k_abs.mapv(|k| radius * k);
118 let mut w_i = w_x.index_axis_mut(Axis(0), i);
119 w_i.assign(&match self.shape {
120 WeightFunctionShape::DeltaVec => {
121 &rik.mapv(|rik| {
122 (rik.sph_j0() + rik.sph_j2()) * (radius.powi(3) * 4.0 * FRAC_PI_3 * p)
123 }) * &k_x
124 }
125 _ => unreachable!(),
126 });
127
128 if let Some(l) = lanczos {
130 let w_i_l = &w_i * l;
131 w_i.assign(&w_i_l)
132 }
133 }
134 }
135 w
137 }
138
139 pub fn scalar_weight_constants(&self, k: T) -> Array1<T> {
142 let k = arr0(k);
143 self.fft_scalar_weight_functions(&k, &None)
144 }
145
146 pub fn vector_weight_constants(&self, k: T) -> Array1<T> {
149 let k_abs = arr0(k);
150 let k = arr1(&[k]);
151 self.fft_vector_weight_functions(&k_abs, &k, &None)
152 .index_axis_move(Axis(0), 0)
153 }
154}
155
156#[derive(Clone, Copy, PartialEq, Eq, Debug)]
158pub enum WeightFunctionShape {
159 Theta,
161 Delta,
163 KR0,
165 KR1,
167 DeltaVec,
169}
170
171pub struct WeightFunctionInfo<T> {
173 pub(crate) component_index: DVector<usize>,
175 pub(crate) local_density: bool,
177 pub(crate) scalar_component_weighted_densities: Vec<WeightFunction<T>>,
179 pub(crate) vector_component_weighted_densities: Vec<WeightFunction<T>>,
181 pub(crate) scalar_fmt_weighted_densities: Vec<WeightFunction<T>>,
183 pub(crate) vector_fmt_weighted_densities: Vec<WeightFunction<T>>,
185}
186
187impl<T> WeightFunctionInfo<T> {
188 pub fn n_weighted_densities(&self, dimensions: usize) -> usize {
191 let segments = self.component_index.len();
192 (if self.local_density { segments } else { 0 })
193 + self.scalar_component_weighted_densities.len() * segments
194 + self.vector_component_weighted_densities.len() * segments * dimensions
195 + self.scalar_fmt_weighted_densities.len()
196 + self.vector_fmt_weighted_densities.len() * dimensions
197 }
198}
199
200impl<T> WeightFunctionInfo<T> {
201 pub fn new(component_index: DVector<usize>, local_density: bool) -> Self {
203 Self {
204 component_index,
205 local_density,
206 scalar_component_weighted_densities: Vec::new(),
207 vector_component_weighted_densities: Vec::new(),
208 scalar_fmt_weighted_densities: Vec::new(),
209 vector_fmt_weighted_densities: Vec::new(),
210 }
211 }
212
213 pub fn add(mut self, weight_function: WeightFunction<T>, fmt: bool) -> Self {
216 let segments = self.component_index.len();
217
218 if segments != weight_function.kernel_radius.len() {
220 panic!(
221 "Number of segments is fixed to {}; `kernel_radius` has {} entries.",
222 segments,
223 weight_function.kernel_radius.len()
224 );
225 }
226
227 if segments != weight_function.prefactor.len() {
229 panic!(
230 "Number of segments is fixed to {}; `prefactor` has {} entries.",
231 segments,
232 weight_function.prefactor.len()
233 );
234 }
235
236 match (fmt, weight_function.shape) {
238 (false, WeightFunctionShape::DeltaVec) => self
240 .vector_component_weighted_densities
241 .push(weight_function),
242 (false, _) => self
244 .scalar_component_weighted_densities
245 .push(weight_function),
246 (true, WeightFunctionShape::DeltaVec) => {
248 self.vector_fmt_weighted_densities.push(weight_function)
249 }
250 (true, _) => self.scalar_fmt_weighted_densities.push(weight_function),
252 };
253
254 self
256 }
257
258 pub fn extend(mut self, weight_functions: Vec<WeightFunction<T>>, fmt: bool) -> Self {
260 for wf in weight_functions {
262 self = self.add(wf, fmt);
263 }
264
265 self
267 }
268
269 pub fn as_slice(&self) -> [&Vec<WeightFunction<T>>; 4] {
271 [
272 &self.scalar_component_weighted_densities,
273 &self.vector_component_weighted_densities,
274 &self.scalar_fmt_weighted_densities,
275 &self.vector_fmt_weighted_densities,
276 ]
277 }
278}
279
280impl<T: DualNum<f64> + Copy> WeightFunctionInfo<T> {
281 pub fn weight_constants(&self, k: T, dimensions: usize) -> Array2<T> {
283 let segments = self.component_index.len();
284 let n_wd = self.n_weighted_densities(dimensions);
285 let mut weight_constants = Array::zeros([n_wd, segments]);
286 let mut j = 0;
287 if self.local_density {
288 weight_constants
289 .slice_mut(s![j..j + segments, ..])
290 .into_diag()
291 .assign(&Array::ones(segments));
292 j += segments;
293 }
294 for w in &self.scalar_component_weighted_densities {
295 weight_constants
296 .slice_mut(s![j..j + segments, ..])
297 .into_diag()
298 .assign(&w.scalar_weight_constants(k));
299 j += segments;
300 }
301 if dimensions == 1 {
302 for w in &self.vector_component_weighted_densities {
303 weight_constants
304 .slice_mut(s![j..j + segments, ..])
305 .into_diag()
306 .assign(&w.vector_weight_constants(k));
307 j += segments;
308 }
309 }
310 for w in &self.scalar_fmt_weighted_densities {
311 weight_constants
312 .slice_mut(s![j, ..])
313 .assign(&w.scalar_weight_constants(k));
314 j += 1;
315 }
316 if dimensions == 1 {
317 for w in &self.vector_fmt_weighted_densities {
318 weight_constants
319 .slice_mut(s![j, ..])
320 .assign(&w.vector_weight_constants(k));
321 j += 1;
322 }
323 }
324 weight_constants
325 }
326}