Newer
Older
use crate::error::OpossumError;
use std::fmt::Display;
use std::ops::Range;
use uom::fmt::DisplayStyle::Abbreviation;
use uom::num_traits::Zero;
use uom::si::{f64::Length, length::nanometer};
type Result<T> = std::result::Result<T, OpossumError>;
data: Array1<f64>, // data in 1/meters
lambdas: Array1<f64>, // wavelength in meters
pub fn new(range: Range<Length>, resolution: Length) -> Result<Self> {
if resolution <= Length::zero() {
return Err(OpossumError::Spectrum("resolution must be positive".into()));
}
if range.start >= range.end {
return Err(OpossumError::Spectrum(
"wavelength range must be in ascending order".into(),
));
}
if range.start <= Length::zero() || range.end <= Length::zero() {
return Err(OpossumError::Spectrum(
"wavelength range limits must both be positive".into(),
));
}
let l = Array1::range(
range.start.get::<meter>(),
range.end.get::<meter>(),
resolution.get::<meter>(),
);
let length = l.len();
lambdas: l,
data: Array1::zeros(length),
pub fn set_single_peak(&mut self, wavelength: Length, value: f64) -> Result<()> {
let spectrum_range = self.lambdas.first().unwrap()..self.lambdas.last().unwrap();
if !spectrum_range.contains(&&wavelength.get::<meter>()) {
"wavelength is not in spectrum range".into(),
));
}
return Err(OpossumError::Spectrum("energy must be positive".into()));
}
let wavelength_in_meters = wavelength.get::<meter>();
let idx = self
.lambdas
.clone()
.into_iter()
.position(|w| w >= wavelength_in_meters);
if let Some(idx) = idx {
if idx == 0 {
let delta = self.lambdas.get(1).unwrap() - self.lambdas.get(0).unwrap();
self.data[0] = value / delta;
} else {
let lower_lambda = self.lambdas.get(idx - 1).unwrap();
let upper_lambda = self.lambdas.get(idx).unwrap();
let delta = upper_lambda - lower_lambda;
self.data[idx - 1] =
value * (1.0 - (wavelength_in_meters - lower_lambda) / delta) / delta;
self.data[idx] = value * (wavelength_in_meters - lower_lambda) / delta / delta;
Err(OpossumError::Spectrum("insertion point not found".into()))
pub fn total_energy(&self) -> f64 {
let lambda_deltas: Vec<f64> = self
.lambdas
.windows(2)
.into_iter()
.map(|l| l[1] - l[0])
.collect();
let total_energy = lambda_deltas
.into_iter()
.zip(self.data.iter())
.map(|d| d.0 * *d.1)
.sum();
pub fn scale_vertical(&mut self, factor: f64) -> Result<()> {
if factor < 0.0 {
return Err(OpossumError::Spectrum(
"scaling factor mus be >= 0.0".into(),
));
}
self.data = &self.data * factor;
Ok(())
}
fn enclosing_interval(&self, x_lower: f64, x_upper: f64) -> Vec<Option<usize>> {
.enumerate()
.filter(|x| x.1 < x_upper && x.1 > x_lower)
.map(|x| Some(x.0))
.collect::<Vec<Option<usize>>>();
let mut lower_idx = self.clone().lambdas.into_iter().position(|x| x >= x_lower);
let upper_idx = self.clone().lambdas.into_iter().position(|x| x >= x_upper);
if let Some(l_i) = lower_idx {
if l_i > 0 && self.lambdas[l_i] > x_lower {
lower_idx = Some(l_i - 1);
}
}
if lower_idx == upper_idx {
res = vec![None, None];
} else {
res = vec![lower_idx, upper_idx];
let res_cp = res.clone();
let first = res_cp.first().unwrap();
let last = res_cp.last().unwrap();
if let Some(first) = first {
if *first > 0 {
res.insert(0, Some(first - 1));
}
if let Some(last) = last {
if *last < self.lambdas.len() - 1 {
res.push(Some(last + 1))
}
pub fn resample(&mut self, spectrum: &Spectrum) -> Result<()> {
let _data = spectrum.data.clone();
let _x = spectrum.lambdas.clone();
let max_idx = self.data.len() - 1;
for x in self.data.iter_mut().enumerate().filter(|x| x.0 < max_idx) {
let lower_bound = *self.lambdas.get(x.0).unwrap();
let upper_bound = *self.lambdas.get(x.0 + 1).unwrap();
let interval = spectrum.enclosing_interval(lower_bound, upper_bound);
//println!("bucket: [{},{}[", lower_bound, upper_bound);
let mut bucket_value = 0.0;
if (*src_idx)[0].is_some() && (*src_idx)[1].is_some() {
let source_left = spectrum.lambdas[src_idx[0].unwrap()];
let source_right = spectrum.lambdas[src_idx[1].unwrap()];
//print!(" source: [{},{}] -> ratio: ", source_left, source_right);
let ratio = calc_ratio(lower_bound, upper_bound, source_left, source_right);
//println!("{}", ratio);
bucket_value +=
spectrum.data[src_idx[0].unwrap()] * ratio * (source_right - source_left)
/ (upper_bound - lower_bound);
}
pub fn filter(&mut self, _spectrum: &Spectrum) -> Result<()> {
}
impl Display for Spectrum {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let fmt_length = Length::format_args(nanometer, Abbreviation);
for value in self.data.iter().enumerate() {
fmt_length.with(Length::new::<meter>(self.lambdas[value.0])),
write!(f, "\nTotal energy: {}", self.total_energy())
fn calc_ratio(bucket_left: f64, bucket_right: f64, source_left: f64, source_right: f64) -> f64 {
if bucket_left < source_left && bucket_right > source_left && bucket_right < source_right {
// bucket is left partly outside source
return (bucket_right - source_left) / (source_right - source_left);
}
if bucket_left <= source_left && bucket_right >= source_right {
// bucket contains source
return 1.0;
}
if bucket_left >= source_left && bucket_right <= source_right {
// bucket is part of source
return (bucket_right - bucket_left) / (source_right - source_left);
}
if bucket_left > source_left && bucket_left < source_right && bucket_right > source_right {
// bucket is right partly outside source
return (source_right - bucket_left) / (source_right - source_left);
}
#[cfg(test)]
mod test {
use super::*;
use ndarray::array;
#[test]
fn new() {
let s = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(4.0),
Length::new::<meter>(0.5),
);
assert_eq!(s.is_ok(), true);
assert_eq!(
s.as_ref().unwrap().lambdas,
array![1.0, 1.5, 2.0, 2.5, 3.0, 3.5]
);
assert_eq!(s.unwrap().data, array![0.0, 0.0, 0.0, 0.0, 0.0, 0.0]);
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
}
#[test]
fn new_negative_resolution() {
let s = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(4.0),
Length::new::<meter>(-0.5),
);
assert_eq!(s.is_ok(), false);
}
#[test]
fn new_wrong_range() {
let s = Spectrum::new(
Length::new::<meter>(4.0)..Length::new::<meter>(1.0),
Length::new::<meter>(0.5),
);
assert_eq!(s.is_ok(), false);
}
#[test]
fn new_negative_range() {
let s = Spectrum::new(
Length::new::<meter>(-1.0)..Length::new::<meter>(4.0),
Length::new::<meter>(0.5),
);
assert_eq!(s.is_ok(), false);
}
#[test]
fn set_single_peak() {
let mut s = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(4.0),
s.set_single_peak(Length::new::<meter>(2.0), 1.0).is_ok(),
}
#[test]
fn set_single_peak_interpolated() {
let mut s = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(4.0),
s.set_single_peak(Length::new::<meter>(2.25), 1.0).is_ok(),
assert_eq!(s.data[2], 1.0);
assert_eq!(s.data[3], 1.0);
}
#[test]
fn set_single_peak_lower_bound() {
let mut s = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(4.0),
s.set_single_peak(Length::new::<meter>(1.0), 1.0).is_ok(),
}
#[test]
fn set_single_peak_out_of_limit() {
let mut s = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(4.0),
Length::new::<meter>(1.0),
)
.unwrap();
assert_eq!(
s.set_single_peak(Length::new::<meter>(0.5), 1.0).is_ok(),
s.set_single_peak(Length::new::<meter>(4.0), 1.0).is_ok(),
let mut s = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(4.0),
Length::new::<meter>(1.0),
)
.unwrap();
assert_eq!(
s.set_single_peak(Length::new::<meter>(1.5), -1.0).is_ok(),
false
);
}
#[test]
fn total_energy() {
let mut s = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(4.0),
s.set_single_peak(Length::new::<meter>(2.0), 1.0).unwrap();
assert_eq!(s.total_energy(), 1.0);
fn total_energy2() {
let mut s = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(4.0),
Length::new::<meter>(1.0),
)
.unwrap();
s.set_single_peak(Length::new::<meter>(1.5), 1.0).unwrap();
assert_eq!(s.total_energy(), 1.0);
}
#[test]
fn scale_vertical() {
let mut s = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(5.0),
Length::new::<meter>(1.0),
)
.unwrap();
s.set_single_peak(Length::new::<meter>(2.5), 1.0).unwrap();
assert_eq!(s.scale_vertical(0.5).is_ok(), true);
assert_eq!(s.data, array![0.0, 0.25, 0.25, 0.0]);
}
#[test]
fn scale_vertical_negative() {
let mut s = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(5.0),
Length::new::<meter>(1.0),
)
.unwrap();
s.set_single_peak(Length::new::<meter>(2.5), 1.0).unwrap();
assert_eq!(s.scale_vertical(-0.5).is_ok(), false);
#[test]
fn enclosing_interval() {
let s = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(5.0),
Length::new::<meter>(1.0),
)
.unwrap();
let v = s.enclosing_interval(2.1, 3.9);
assert_eq!(v, vec![Some(1), Some(2), Some(3)]);
}
#[test]
fn enclosing_interval_exact() {
let s = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(5.0),
Length::new::<meter>(1.0),
)
.unwrap();
let v = s.enclosing_interval(2.0, 4.0);
assert_eq!(v, vec![Some(1), Some(2), Some(3)]);
}
#[test]
fn enclosing_interval_upper_bound() {
let s = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(5.0),
Length::new::<meter>(1.0),
)
.unwrap();
let v = s.enclosing_interval(3.0, 5.0);
assert_eq!(v, vec![Some(2), Some(3)]); // ranges are exclusive upper bound !
}
#[test]
fn enclosing_interval_lower_bound() {
let s = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(5.0),
Length::new::<meter>(1.0),
)
.unwrap();
let v = s.enclosing_interval(1.0, 3.0);
assert_eq!(v, vec![Some(0), Some(1), Some(2)]);
}
#[test]
fn enclosing_interval_small_range() {
let s = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(5.0),
Length::new::<meter>(1.0),
)
.unwrap();
let v = s.enclosing_interval(2.6, 2.9);
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
assert_eq!(v, vec![Some(1), Some(2)]);
}
#[test]
fn enclosing_interval_right_outside() {
let s = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(5.0),
Length::new::<meter>(1.0),
)
.unwrap();
let v = s.enclosing_interval(5.1, 7.0);
assert_eq!(v, vec![None, None]);
}
#[test]
fn enclosing_interval_right_partly_outside() {
let s = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(5.0),
Length::new::<meter>(1.0),
)
.unwrap();
let v = s.enclosing_interval(2.1, 7.0);
assert_eq!(v, vec![Some(1), Some(2), Some(3)]);
}
#[test]
fn enclosing_interval_left_outside() {
let s = Spectrum::new(
Length::new::<meter>(5.0)..Length::new::<meter>(10.0),
Length::new::<meter>(1.0),
)
.unwrap();
let v = s.enclosing_interval(1.0, 4.9);
assert_eq!(v, vec![None, None]);
}
#[test]
fn enclosing_interval_left_partly_outside() {
let s = Spectrum::new(
Length::new::<meter>(5.0)..Length::new::<meter>(10.0),
Length::new::<meter>(1.0),
)
.unwrap();
let v = s.enclosing_interval(2.1, 6.5);
assert_eq!(v, vec![Some(0), Some(1), Some(2)]);
#[test]
fn calc_ratio_test() {
assert_eq!(calc_ratio(1.0, 2.0, 3.0, 4.0), 0.0); // bucket completely outside
assert_eq!(calc_ratio(1.0, 4.0, 2.0, 3.0), 1.0); // bucket contains source
assert_eq!(calc_ratio(2.0, 3.0, 0.0, 4.0), 0.25); // bucket is part of source
assert_eq!(calc_ratio(0.0, 1.0, 0.0, 2.0), 0.5); // bucket is part of source (matching left)
assert_eq!(calc_ratio(1.0, 2.0, 0.0, 2.0), 0.5); // bucket is part of source (matching right)
assert_eq!(calc_ratio(0.0, 2.0, 1.0, 3.0), 0.5); // bucket is left outside source
assert_eq!(calc_ratio(0.0, 2.0, 1.0, 2.0), 1.0); // bucket is left outside source (matching)
assert_eq!(calc_ratio(2.0, 4.0, 1.0, 3.0), 0.5); // bucket is right outside source
assert_eq!(calc_ratio(1.0, 4.0, 1.0, 3.0), 1.0); // bucket is right outside source (matching)
assert_eq!(calc_ratio(1.0, 2.0, 1.0, 2.0), 1.0); // bucket matches source
}
#[test]
fn resample() {
let mut s1 = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(5.0),
Length::new::<meter>(1.0),
)
.unwrap();
s1.set_single_peak(Length::new::<meter>(2.0), 1.0).unwrap();
let s2 = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(5.0),
Length::new::<meter>(1.0),
)
.unwrap();
s1.resample(&s2).unwrap();
assert_eq!(s1.data, s2.data);
assert_eq!(s1.total_energy(), s2.total_energy());
let mut s1 = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(5.0),
Length::new::<meter>(0.5),
)
.unwrap();
let mut s2 = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(6.0),
Length::new::<meter>(1.0),
)
.unwrap();
s2.set_single_peak(Length::new::<meter>(2.0), 1.0).unwrap();
s1.resample(&s2).unwrap();
assert_eq!(s1.data, array![0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0]);
assert_eq!(s1.total_energy(), s2.total_energy());
}
#[test]
fn resample_interp2() {
Length::new::<meter>(1.0)..Length::new::<meter>(5.0),
Length::new::<meter>(1.0)..Length::new::<meter>(6.0),
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
Length::new::<meter>(0.5),
)
.unwrap();
s2.set_single_peak(Length::new::<meter>(2.0), 1.0).unwrap();
s1.resample(&s2).unwrap();
assert_eq!(s1.data, array![0.0, 1.0, 0.0, 0.0]);
assert_eq!(s1.total_energy(), s2.total_energy());
}
#[test]
fn resample_right_outside() {
let mut s1 = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(4.0),
Length::new::<meter>(1.0),
)
.unwrap();
let mut s2 = Spectrum::new(
Length::new::<meter>(4.0)..Length::new::<meter>(6.0),
Length::new::<meter>(1.0),
)
.unwrap();
s2.set_single_peak(Length::new::<meter>(4.0), 1.0).unwrap();
s1.resample(&s2).unwrap();
assert_eq!(s1.data, array![0.0, 0.0, 0.0]);
assert_eq!(s1.total_energy(), 0.0);
}
#[test]
fn resample_left_outside() {
let mut s1 = Spectrum::new(
Length::new::<meter>(4.0)..Length::new::<meter>(6.0),
Length::new::<meter>(1.0),
)
.unwrap();
let mut s2 = Spectrum::new(
Length::new::<meter>(1.0)..Length::new::<meter>(4.0),
s2.set_single_peak(Length::new::<meter>(2.0), 1.0).unwrap();
assert_eq!(s1.data, array![0.0, 0.0]);
assert_eq!(s1.total_energy(), 0.0);