mod calculate;
mod map;
#[cfg(test)]
mod tests;
pub use map::{WaveletMap, WaveletPreparation, WaveletRegionValue};
use serde::{Deserialize, Serialize};
use std::ops::RangeInclusive;
#[derive(Debug, Clone, PartialEq, thiserror::Error)]
pub enum WaveletError {
#[error("Wavelet: {0}")]
Invalid(String),
#[error("Wavelet calculation cancelled")]
Cancelled,
#[error("Wavelet preparation: {0}")]
Preparation(String),
}
type Result<T> = std::result::Result<T, WaveletError>;
fn invalid(message: impl std::fmt::Display) -> WaveletError {
WaveletError::Invalid(message.to_string())
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Serialize, Deserialize)]
pub enum WaveletWindow {
#[default]
None,
Cosine {
width: f64,
},
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Wavelet {
pub k_range: [f64; 2],
pub kweight: u8,
pub order: usize,
pub kstep: f64,
pub rmax: f64,
pub rstep: Option<f64>,
pub radii: Option<Vec<f64>>,
pub nfft: Option<usize>,
pub window: WaveletWindow,
}
impl Wavelet {
pub fn new(k_range: RangeInclusive<f64>) -> Self {
Self {
k_range: [*k_range.start(), *k_range.end()],
kweight: 2,
order: 100,
kstep: 0.05,
rmax: 6.,
rstep: None,
radii: None,
nfft: None,
window: WaveletWindow::None,
}
}
pub fn kweight(mut self, value: u8) -> Self {
self.kweight = value;
self
}
pub fn order(mut self, value: usize) -> Self {
self.order = value;
self
}
pub fn kstep(mut self, value: f64) -> Self {
self.kstep = value;
self
}
pub fn rmax(mut self, value: f64) -> Self {
self.rmax = value;
self.radii = None;
self
}
pub fn rstep(mut self, value: f64) -> Self {
self.rstep = Some(value);
self.radii = None;
self
}
pub fn radii(mut self, values: Vec<f64>) -> Self {
self.radii = Some(values);
self
}
pub fn nfft(mut self, value: usize) -> Self {
self.nfft = Some(value);
self
}
pub fn taper(mut self, width: f64) -> Self {
self.window = WaveletWindow::Cosine { width };
self
}
pub fn calculate(&self, k: &[f64], chi: &[f64]) -> Result<WaveletMap> {
self.calculate_with_cancel(k, chi, || false)
}
pub fn calculate_with_cancel(
&self,
k: &[f64],
chi: &[f64],
cancelled: impl Fn() -> bool,
) -> Result<WaveletMap> {
calculate::calculate(self, k, chi, &cancelled)
}
pub fn estimate(&self, k: &[f64]) -> Result<WaveletSize> {
Ok(calculate::layout(self, k)?.size)
}
}
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct WaveletSize {
pub k_points: usize,
pub r_points: usize,
pub nfft: usize,
pub cells: usize,
pub bytes: usize,
}
impl crate::Spectrum {
pub fn wavelet(&self, settings: &Wavelet) -> Result<WaveletMap> {
self.wavelet_with_cancel(settings, || false)
}
pub fn wavelet_with_cancel(
&self,
settings: &Wavelet,
cancelled: impl Fn() -> bool,
) -> Result<WaveletMap> {
self.ensure_exafs_allowed()
.map_err(|e| WaveletError::Preparation(e.to_string()))?;
if cancelled() {
return Err(WaveletError::Cancelled);
}
let mut prepared;
let spectrum = if self.k().is_some() && self.chi().is_some() {
self
} else {
prepared = self.clone();
prepared
.calc_background()
.map_err(|e| WaveletError::Preparation(e.to_string()))?;
&prepared
};
let mut map = settings.calculate_with_cancel(
spectrum.k().ok_or_else(|| invalid("missing prepared k"))?,
spectrum
.chi()
.ok_or_else(|| invalid("missing prepared chi"))?,
cancelled,
)?;
map.data.preparation = Some(WaveletPreparation::from_spectrum(spectrum));
Ok(map)
}
}