pub fn sobolev_s2_truncated_coefficients(lmax: usize, m: usize) -> Vec<f64> {
let four_pi = 4.0 * std::f64::consts::PI;
let mut coeffs = vec![0.0_f64; lmax + 1];
let mi = m as i32;
for ell in 1..=lmax {
let l = ell as f64;
let eigen = (l * (l + 1.0)).powi(mi);
coeffs[ell] = (2.0 * l + 1.0) / (four_pi * eigen);
}
coeffs
}
pub fn pseudo_s2_truncated_coefficients(lmax: usize, m: usize) -> Vec<f64> {
let four_pi = 4.0 * std::f64::consts::PI;
let mut coeffs = vec![0.0_f64; lmax + 1];
for ell in 1..=lmax {
let l = ell as f64;
let mut denom = 1.0_f64;
for k in 1..=(m + 1) {
denom *= l + k as f64;
}
coeffs[ell] = 2.0 / (four_pi * denom);
}
coeffs
}
#[inline]
pub fn sphere_truncated_spectral_eval(cos_gamma: f64, coeffs: &[f64]) -> f64 {
let t = cos_gamma.clamp(-1.0, 1.0);
let lmax = coeffs.len().saturating_sub(1);
if lmax == 0 {
return coeffs.first().copied().unwrap_or(0.0);
}
let mut p_prev = 1.0_f64; let mut p_curr = t; let mut acc = coeffs[0] * p_prev + coeffs[1] * p_curr;
for ell in 1..lmax {
let lf = ell as f64;
let p_next = ((2.0 * lf + 1.0) * t * p_curr - lf * p_prev) / (lf + 1.0);
acc += coeffs[ell + 1] * p_next;
p_prev = p_curr;
p_curr = p_next;
}
acc
}
pub(crate) fn sphere_truncated_spectral_derivative_eval(cos_gamma: f64, coeffs: &[f64]) -> f64 {
const POLE_LIMIT_THRESHOLD: f64 = 1.0e-10;
let x = cos_gamma.clamp(-1.0, 1.0);
let lmax = coeffs.len().saturating_sub(1);
if lmax == 0 {
return 0.0;
}
if x.abs() > 1.0 - POLE_LIMIT_THRESHOLD {
let pole_neg = x.is_sign_negative();
let mut acc = 0.0_f64;
for ell in 1..=lmax {
let lf = ell as f64;
let sign = if pole_neg && ell % 2 == 0 { -1.0 } else { 1.0 };
let p_prime = 0.5 * lf * (lf + 1.0) * sign;
acc += coeffs[ell] * p_prime;
}
return acc;
}
let one_minus_x2 = (1.0 - x * x).max(f64::EPSILON);
let mut p_prev = 1.0_f64; let mut p_curr = x; let mut acc = 0.0_f64;
let mut ell = 1usize;
loop {
let lf = ell as f64;
let p_prime = lf * (p_prev - x * p_curr) / one_minus_x2;
acc += coeffs[ell] * p_prime;
if ell >= lmax {
break;
}
let p_next = ((2.0 * lf + 1.0) * x * p_curr - lf * p_prev) / (lf + 1.0);
p_prev = p_curr;
p_curr = p_next;
ell += 1;
}
acc
}