use nalgebra::DVector;
use ndarray::*;
use num_dual::DualNum;
use std::f64::consts::{FRAC_PI_3, PI};
use std::ops::Mul;
#[derive(Clone)]
pub struct WeightFunction<T> {
pub prefactor: DVector<T>,
pub kernel_radius: DVector<T>,
pub shape: WeightFunctionShape,
}
impl<T: DualNum<f64> + Copy> WeightFunction<T> {
pub fn new_unscaled(kernel_radius: DVector<T>, shape: WeightFunctionShape) -> Self {
Self {
prefactor: DVector::from_element(kernel_radius.len(), T::one()),
kernel_radius,
shape,
}
}
pub fn new_scaled(kernel_radius: DVector<T>, shape: WeightFunctionShape) -> Self {
let unscaled = Self::new_unscaled(kernel_radius, shape);
let weight_constants = unscaled.scalar_weight_constants(T::zero());
let weight_constants = DVector::from(weight_constants.to_vec());
Self {
prefactor: weight_constants.map(|w| w.recip()),
kernel_radius: unscaled.kernel_radius,
shape,
}
}
pub(crate) fn fft_scalar_weight_functions<D: Dimension, T2: DualNum<f64>>(
&self,
k_abs: &Array<T2, D>,
lanczos: &Option<Array<T2, D>>,
) -> Array<T, D::Larger>
where
T: Mul<T2, Output = T>,
D::Larger: Dimension<Smaller = D>,
{
let mut d = vec![self.prefactor.len()];
k_abs.shape().iter().for_each(|&x| d.push(x));
let mut w = Array::zeros(d.into_dimension())
.into_dimensionality()
.unwrap();
for i in 0..self.prefactor.len() {
let radius = self.kernel_radius[i];
let p = self.prefactor[i];
let rik = k_abs.mapv(|k| radius * k);
let mut w_i = w.index_axis_mut(Axis(0), i);
w_i.assign(&match self.shape {
WeightFunctionShape::Theta => rik.mapv(|rik| {
(rik.sph_j0() + rik.sph_j2()) * 4.0 * FRAC_PI_3 * radius.powi(3) * p
}),
WeightFunctionShape::Delta => {
rik.mapv(|rik| rik.sph_j0() * 4.0 * PI * radius.powi(2) * p)
}
WeightFunctionShape::KR1 => {
rik.mapv(|rik| (rik.sph_j0() + rik.cos()) * 0.5 * radius * p)
}
WeightFunctionShape::KR0 => rik.mapv(|rik| (rik * rik.sin() * 0.5 + rik.cos()) * p),
WeightFunctionShape::DeltaVec => unreachable!(),
});
if let Some(l) = lanczos {
let w_i_l = &w_i * l;
w_i.assign(&w_i_l);
}
}
w
}
pub(crate) fn fft_vector_weight_functions<D: Dimension, T2: DualNum<f64>>(
&self,
k_abs: &Array<T2, D>,
k: &Array<T2, D::Larger>,
lanczos: &Option<Array<T2, D>>,
) -> Array<T, <D::Larger as Dimension>::Larger>
where
D::Larger: Dimension<Smaller = D>,
<D::Larger as Dimension>::Larger: Dimension<Smaller = D::Larger>,
T: Mul<T2, Output = T>,
{
let mut d = vec![k.shape()[0], self.prefactor.len()];
k.shape().iter().skip(1).for_each(|&x| d.push(x));
let mut w = Array::zeros(d.into_dimension())
.into_dimensionality()
.unwrap();
for (k_x, mut w_x) in k.outer_iter().zip(w.outer_iter_mut()) {
for i in 0..self.prefactor.len() {
let radius = self.kernel_radius[i];
let p = self.prefactor[i];
let rik = k_abs.mapv(|k| radius * k);
let mut w_i = w_x.index_axis_mut(Axis(0), i);
w_i.assign(&match self.shape {
WeightFunctionShape::DeltaVec => {
&rik.mapv(|rik| {
(rik.sph_j0() + rik.sph_j2()) * (radius.powi(3) * 4.0 * FRAC_PI_3 * p)
}) * &k_x
}
_ => unreachable!(),
});
if let Some(l) = lanczos {
let w_i_l = &w_i * l;
w_i.assign(&w_i_l)
}
}
}
w
}
pub fn scalar_weight_constants(&self, k: T) -> Array1<T> {
let k = arr0(k);
self.fft_scalar_weight_functions(&k, &None)
}
pub fn vector_weight_constants(&self, k: T) -> Array1<T> {
let k_abs = arr0(k);
let k = arr1(&[k]);
self.fft_vector_weight_functions(&k_abs, &k, &None)
.index_axis_move(Axis(0), 0)
}
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub enum WeightFunctionShape {
Theta,
Delta,
KR0,
KR1,
DeltaVec,
}
pub struct WeightFunctionInfo<T> {
pub(crate) component_index: DVector<usize>,
pub(crate) local_density: bool,
pub(crate) scalar_component_weighted_densities: Vec<WeightFunction<T>>,
pub(crate) vector_component_weighted_densities: Vec<WeightFunction<T>>,
pub(crate) scalar_fmt_weighted_densities: Vec<WeightFunction<T>>,
pub(crate) vector_fmt_weighted_densities: Vec<WeightFunction<T>>,
}
impl<T> WeightFunctionInfo<T> {
pub fn n_weighted_densities(&self, dimensions: usize) -> usize {
let segments = self.component_index.len();
(if self.local_density { segments } else { 0 })
+ self.scalar_component_weighted_densities.len() * segments
+ self.vector_component_weighted_densities.len() * segments * dimensions
+ self.scalar_fmt_weighted_densities.len()
+ self.vector_fmt_weighted_densities.len() * dimensions
}
}
impl<T> WeightFunctionInfo<T> {
pub fn new(component_index: DVector<usize>, local_density: bool) -> Self {
Self {
component_index,
local_density,
scalar_component_weighted_densities: Vec::new(),
vector_component_weighted_densities: Vec::new(),
scalar_fmt_weighted_densities: Vec::new(),
vector_fmt_weighted_densities: Vec::new(),
}
}
pub fn add(mut self, weight_function: WeightFunction<T>, fmt: bool) -> Self {
let segments = self.component_index.len();
if segments != weight_function.kernel_radius.len() {
panic!(
"Number of segments is fixed to {}; `kernel_radius` has {} entries.",
segments,
weight_function.kernel_radius.len()
);
}
if segments != weight_function.prefactor.len() {
panic!(
"Number of segments is fixed to {}; `prefactor` has {} entries.",
segments,
weight_function.prefactor.len()
);
}
match (fmt, weight_function.shape) {
(false, WeightFunctionShape::DeltaVec) => self
.vector_component_weighted_densities
.push(weight_function),
(false, _) => self
.scalar_component_weighted_densities
.push(weight_function),
(true, WeightFunctionShape::DeltaVec) => {
self.vector_fmt_weighted_densities.push(weight_function)
}
(true, _) => self.scalar_fmt_weighted_densities.push(weight_function),
};
self
}
pub fn extend(mut self, weight_functions: Vec<WeightFunction<T>>, fmt: bool) -> Self {
for wf in weight_functions {
self = self.add(wf, fmt);
}
self
}
pub fn as_slice(&self) -> [&Vec<WeightFunction<T>>; 4] {
[
&self.scalar_component_weighted_densities,
&self.vector_component_weighted_densities,
&self.scalar_fmt_weighted_densities,
&self.vector_fmt_weighted_densities,
]
}
}
impl<T: DualNum<f64> + Copy> WeightFunctionInfo<T> {
pub fn weight_constants(&self, k: T, dimensions: usize) -> Array2<T> {
let segments = self.component_index.len();
let n_wd = self.n_weighted_densities(dimensions);
let mut weight_constants = Array::zeros([n_wd, segments]);
let mut j = 0;
if self.local_density {
weight_constants
.slice_mut(s![j..j + segments, ..])
.into_diag()
.assign(&Array::ones(segments));
j += segments;
}
for w in &self.scalar_component_weighted_densities {
weight_constants
.slice_mut(s![j..j + segments, ..])
.into_diag()
.assign(&w.scalar_weight_constants(k));
j += segments;
}
if dimensions == 1 {
for w in &self.vector_component_weighted_densities {
weight_constants
.slice_mut(s![j..j + segments, ..])
.into_diag()
.assign(&w.vector_weight_constants(k));
j += segments;
}
}
for w in &self.scalar_fmt_weighted_densities {
weight_constants
.slice_mut(s![j, ..])
.assign(&w.scalar_weight_constants(k));
j += 1;
}
if dimensions == 1 {
for w in &self.vector_fmt_weighted_densities {
weight_constants
.slice_mut(s![j, ..])
.assign(&w.vector_weight_constants(k));
j += 1;
}
}
weight_constants
}
}