1use std::collections::HashMap;
2
3use anyhow::{anyhow, Result};
4use serde::{Deserialize, Serialize};
5
6#[derive(Serialize, Deserialize, Debug)]
8pub struct Store {
9 inner: HashMap<String, Material>,
10}
11
12#[derive(Serialize, Deserialize, Debug)]
14pub struct Material {
15 pub shelf: String,
16 pub book: String,
17 pub page: String,
18 pub comments: String,
19 pub references: String,
20 pub data: Vec<DispersionData>,
21 pub shelf_divider: Option<String>,
24 pub book_divider: Option<String>,
28}
29
30#[derive(Debug)]
31enum DataType {
32 Real,
33 Imaginary,
34 Both,
35}
36
37#[derive(Serialize, Deserialize, Debug)]
39pub enum DispersionData {
40 TabulatedK {
41 data: Vec<[f64; 2]>,
42 },
43 TabulatedN {
44 data: Vec<[f64; 2]>,
45 },
46 TabulatedNK {
47 data: Vec<[f64; 3]>,
48 },
49
50 Formula1 {
52 wavelength_range: [f64; 2],
53 c: Vec<f64>,
54 },
55
56 Formula2 {
58 wavelength_range: [f64; 2],
59 c: Vec<f64>,
60 },
61
62 Formula3 {
64 wavelength_range: [f64; 2],
65 c: Vec<f64>,
66 },
67
68 Formula4 {
70 wavelength_range: [f64; 2],
71 c: Vec<f64>,
72 },
73
74 Formula5 {
76 wavelength_range: [f64; 2],
77 c: Vec<f64>,
78 },
79
80 Formula6 {
82 wavelength_range: [f64; 2],
83 c: Vec<f64>,
84 },
85
86 Formula7 {
88 wavelength_range: [f64; 2],
89 c: Vec<f64>,
90 },
91
92 Formula8 {
94 wavelength_range: [f64; 2],
95 c: Vec<f64>,
96 },
97
98 Formula9 {
100 wavelength_range: [f64; 2],
101 c: Vec<f64>,
102 },
103}
104
105impl Store {
106 pub fn new(database: HashMap<String, Material>) -> Self {
107 Store { inner: database }
108 }
109
110 pub fn get(&self, key: &str) -> Option<&Material> {
118 self.inner.get(key)
119 }
120
121 pub fn insert(&mut self, key: String, material: Material) {
127 self.inner.insert(key, material);
128 }
129
130 pub fn keys(&self) -> impl Iterator<Item = &String> {
132 self.inner.keys()
133 }
134
135 pub fn retain(&mut self, predicate: impl FnMut(&String, &mut Material) -> bool) {
141 self.inner.retain(predicate);
142 }
143
144 pub fn remove(&mut self, key: &str) -> Option<Material> {
149 self.inner.remove(key)
150 }
151
152 pub fn remove_many(&mut self, keys: &[String]) {
157 for key in keys {
158 self.inner.remove(key);
159 }
160 }
161}
162
163impl Default for Store {
164 fn default() -> Self {
165 let database = HashMap::new();
166 Self::new(database)
167 }
168}
169
170impl Material {
171 pub fn n(&self, wavelength: f64) -> Result<f64> {
186 let data = self
187 .data
188 .iter()
189 .find(|d| matches!(d.data_type(), DataType::Real | DataType::Both));
190 let (n, _) = match data {
191 Some(data) => data.interpolate(wavelength)?,
192 None => return Err(anyhow!("No real data found for item.")),
193 };
194 Ok(n)
195 }
196
197 pub fn k(&self, wavelength: f64) -> Result<Option<f64>> {
211 let data = self
212 .data
213 .iter()
214 .find(|d| matches!(d.data_type(), DataType::Imaginary | DataType::Both));
215 match data {
216 Some(data) => Ok(data.interpolate(wavelength)?.1),
217 None => Ok(None),
218 }
219 }
220}
221
222impl DispersionData {
223 pub fn interpolate(&self, wavelength: f64) -> Result<(f64, Option<f64>)> {
237 let n: f64 = match &self {
238 Self::Formula1 {
239 wavelength_range,
240 c,
241 } => {
242 if wavelength < wavelength_range[0] || wavelength > wavelength_range[1] {
244 return Err(anyhow!(
245 "The requested wavelength is outside the range of the available dispersion data."
246 ));
247 }
248 let mut sum = 0.0;
249 for i in (1..c.len()).step_by(2) {
250 sum += c[i] * wavelength.powi(2) / (wavelength.powi(2) - c[i + 1].powi(2));
251 }
252 (1.0 + c[0] + sum).sqrt()
253 }
254 Self::Formula2 {
255 wavelength_range,
256 c,
257 } => {
258 if wavelength < wavelength_range[0] || wavelength > wavelength_range[1] {
260 return Err(anyhow!(
261 "The requested wavelength is outside the range of the available dispersion data."
262 ));
263 }
264 let mut sum = 0.0;
265 for i in (1..c.len()).step_by(2) {
266 sum += c[i] * wavelength.powi(2) / (wavelength.powi(2) - c[i + 1]);
267 }
268 (1.0 + c[0] + sum).sqrt()
269 }
270 Self::Formula3 {
271 wavelength_range,
272 c,
273 } => {
274 if wavelength < wavelength_range[0] || wavelength > wavelength_range[1] {
276 return Err(anyhow!(
277 "The requested wavelength is outside the range of the available dispersion data."
278 ));
279 }
280
281 let mut sum = 0.0;
282 for i in (1..c.len()).step_by(2) {
283 sum += c[i] * wavelength.powf(c[i + 1]);
284 }
285 (c[0] + sum).sqrt()
286 }
287 Self::Formula4 {
288 wavelength_range,
289 c,
290 } => {
291 if wavelength < wavelength_range[0] || wavelength > wavelength_range[1] {
293 return Err(anyhow!(
294 "The requested wavelength is outside the range of the available dispersion data."
295 ));
296 }
297
298 let mut sum = 0.0;
299 for i in (1..c.len()).step_by(4) {
300 if i <= 9 {
302 sum += c[i] * wavelength.powf(c[i + 1])
303 / (wavelength.powi(2) - c[i + 2].powf(c[i + 3]));
304 } else {
305 sum += c[i] * wavelength.powf(c[i + 1]);
306 }
307 }
308 (c[0] + sum).sqrt()
309 }
310 Self::Formula5 {
311 wavelength_range,
312 c,
313 } => {
314 if wavelength < wavelength_range[0] || wavelength > wavelength_range[1] {
316 return Err(anyhow!(
317 "The requested wavelength is outside the range of the available dispersion data."
318 ));
319 }
320
321 let mut sum = 0.0;
322 for i in (1..c.len()).step_by(2) {
323 sum += c[i] * wavelength.powf(c[i + 1]);
324 }
325 c[0] + sum
326 }
327 Self::Formula6 {
328 wavelength_range,
329 c,
330 } => {
331 if wavelength < wavelength_range[0] || wavelength > wavelength_range[1] {
333 return Err(anyhow!(
334 "The requested wavelength is outside the range of the available dispersion data."
335 ));
336 }
337
338 let mut sum = 0.0;
339 for i in (1..c.len()).step_by(2) {
340 sum += c[i] / (c[i + 1] - wavelength.powi(-2));
341 }
342 1.0 + c[0] + sum
343 }
344 Self::Formula7 {
345 wavelength_range,
346 c,
347 } => {
348 if wavelength < wavelength_range[0] || wavelength > wavelength_range[1] {
350 return Err(anyhow!(
351 "The requested wavelength is outside the range of the available dispersion data."
352 ));
353 }
354 let mut sum = 0.0;
355 for i in (3..c.len()).step_by(2) {
356 sum += c[i] * wavelength.powi(i as i32 - 1);
357 }
358 c[0] + c[1] / (wavelength.powi(2) - 0.028)
359 + c[2] / (wavelength.powi(2) - 0.028).powi(2)
360 + sum
361 }
362 Self::Formula8 {
363 wavelength_range,
364 c,
365 } => {
366 if wavelength < wavelength_range[0] || wavelength > wavelength_range[1] {
368 return Err(anyhow!(
369 "The requested wavelength is outside the range of the available dispersion data."
370 ));
371 }
372
373 let sum = c[0]
374 + c[1] * wavelength.powi(2) / (wavelength.powi(2) - c[2])
375 + c[3] * wavelength.powi(2);
376 println!("sum: {}", sum);
377 ((2.0 * sum + 1.0) / (1.0 - sum)).sqrt()
378 }
379 Self::Formula9 {
380 wavelength_range,
381 c,
382 } => {
383 if wavelength < wavelength_range[0] || wavelength > wavelength_range[1] {
385 return Err(anyhow!(
386 "The requested wavelength is outside the range of the available dispersion data."
387 ));
388 }
389
390 (c[0]
391 + c[1] / (wavelength.powi(2) - c[2])
392 + c[3] * (wavelength - c[4]) / ((wavelength - c[4]).powi(2) + c[5]))
393 .sqrt()
394 }
395 _ => {
396 return Err(anyhow!("Tabulated dispersion data are not implemented."));
397 }
398 };
399
400 Ok((n, None))
401 }
402
403 fn data_type(&self) -> DataType {
419 match self {
420 self::DispersionData::TabulatedK { data: _ } => DataType::Imaginary,
421 self::DispersionData::TabulatedN { data: _ } => DataType::Real,
422 self::DispersionData::TabulatedNK { data: _ } => DataType::Both,
423 _ => DataType::Real,
424 }
425 }
426}
427
428#[cfg(test)]
429mod test {
430 use super::*;
431 use approx::assert_abs_diff_eq;
432
433 fn test_material(data: Vec<DispersionData>) -> Material {
434 Material {
435 shelf: "main".to_string(),
436 book: "Ag (Silver)".to_string(),
437 page: "Johnson and Christy 1972".to_string(),
438 comments: "".to_string(),
439 references: "".to_string(),
440 data,
441 shelf_divider: None,
442 book_divider: None,
443 }
444 }
445
446 #[test]
447 fn test_store_get_existing() {
448 let mut store = Store::default();
449 store.insert("main:Ag:Johnson".to_string(), test_material(vec![]));
450 assert!(store.get("main:Ag:Johnson").is_some());
451 }
452
453 #[test]
454 fn test_store_get_missing() {
455 let store = Store::default();
456 assert!(store.get("main:Ag:Johnson").is_none());
457 }
458
459 #[test]
460 fn test_store_insert_and_keys() {
461 let mut store = Store::default();
462 store.insert("main:Ag:Johnson".to_string(), test_material(vec![]));
463 store.insert("main:Ag:Choi".to_string(), test_material(vec![]));
464 let mut keys: Vec<&String> = store.keys().collect();
465 keys.sort();
466 assert_eq!(keys.len(), 2);
467 assert!(keys.contains(&&"main:Ag:Johnson".to_string()));
468 assert!(keys.contains(&&"main:Ag:Choi".to_string()));
469 }
470
471 #[test]
472 fn test_store_remove() {
473 let mut store = Store::default();
474 store.insert("main:Ag:Johnson".to_string(), test_material(vec![]));
475 store.remove("main:Ag:Johnson");
476 assert!(store.get("main:Ag:Johnson").is_none());
477 }
478
479 #[test]
480 fn test_store_remove_many() {
481 let mut store = Store::default();
482 store.insert("a".to_string(), test_material(vec![]));
483 store.insert("b".to_string(), test_material(vec![]));
484 store.insert("c".to_string(), test_material(vec![]));
485 store.remove_many(&["a".to_string(), "b".to_string()]);
486 assert!(store.get("a").is_none());
487 assert!(store.get("b").is_none());
488 assert!(store.get("c").is_some());
489 }
490
491 #[test]
492 fn test_store_retain() {
493 let mut store = Store::default();
494 store.insert("main:Ag:Johnson".to_string(), test_material(vec![]));
495 store.insert("main:Ag:Choi".to_string(), test_material(vec![]));
496 store.insert("glass:Schott:BK7".to_string(), test_material(vec![]));
497 store.retain(|key, _| key.starts_with("main:"));
498 assert!(store.get("main:Ag:Johnson").is_some());
499 assert!(store.get("main:Ag:Choi").is_some());
500 assert!(store.get("glass:Schott:BK7").is_none());
501 }
502
503 #[test]
504 fn test_material_n_with_formula() {
505 let data = DispersionData::Formula2 {
507 wavelength_range: [0.3, 2.5],
508 c: vec![
509 0.0,
510 1.03961212,
511 0.00600069867,
512 0.231792344,
513 0.0200179144,
514 1.01046945,
515 103.560653,
516 ],
517 };
518 let material = test_material(vec![data]);
519 let n = material.n(0.5876).unwrap();
520 assert_abs_diff_eq!(n, 1.51680, epsilon = 1e-5);
521 }
522
523 #[test]
524 fn test_material_k_no_imaginary_data() {
525 let data = DispersionData::Formula2 {
526 wavelength_range: [0.3, 2.5],
527 c: vec![
528 0.0,
529 1.03961212,
530 0.00600069867,
531 0.231792344,
532 0.0200179144,
533 1.01046945,
534 103.560653,
535 ],
536 };
537 let material = test_material(vec![data]);
538 let k = material.k(0.5876).unwrap();
539 assert!(k.is_none());
540 }
541
542 #[test]
543 fn test_material_n_no_real_data() {
544 let data = DispersionData::TabulatedK {
545 data: vec![[0.5, 0.1], [0.6, 0.2]],
546 };
547 let material = test_material(vec![data]);
548 assert!(material.n(0.55).is_err());
549 }
550
551 #[test]
552 fn test_interpolate_wavelength_out_of_range() {
553 let data = DispersionData::Formula2 {
554 wavelength_range: [0.3, 2.5],
555 c: vec![
556 0.0,
557 1.03961212,
558 0.00600069867,
559 0.231792344,
560 0.0200179144,
561 1.01046945,
562 103.560653,
563 ],
564 };
565 let err = data.interpolate(0.1).unwrap_err();
566 assert!(err.to_string().contains("outside the range"));
567 }
568
569 #[test]
570 fn test_interpolate_formula_1() {
571 let data = DispersionData::Formula1 {
573 wavelength_range: [0.210, 0.757],
574 c: vec![0.0, 0.496, 0.071, 0.190, 0.134],
575 };
576 let (n, k) = data.interpolate(0.5876).unwrap();
577 assert_abs_diff_eq!(n, 1.3053, epsilon = 1e-4);
578 assert!(k.is_none());
579 }
580
581 #[test]
582 fn test_interpolate_formula_2() {
583 let data = DispersionData::Formula2 {
585 wavelength_range: [0.3, 2.5],
586 c: vec![
587 0.0,
588 1.03961212,
589 0.00600069867,
590 0.231792344,
591 0.0200179144,
592 1.01046945,
593 103.560653,
594 ],
595 };
596
597 let (n, k) = data.interpolate(0.5876).unwrap();
598 assert_abs_diff_eq!(n, 1.51680, epsilon = 1e-5);
599 assert!(k.is_none());
600 }
601
602 #[test]
603 fn test_interpolate_formula_3() {
604 let data = DispersionData::Formula3 {
606 wavelength_range: [0.365, 0.9],
607 c: vec![
608 2.730459,
609 -0.01063385,
610 2.0,
611 0.01942756,
612 -2.0,
613 0.0008209873,
614 -4.0,
615 -5.210457e-05,
616 -6.0,
617 4.447534e-06,
618 -8.0,
619 ],
620 };
621 let (n, k) = data.interpolate(0.5876).unwrap();
622 assert_abs_diff_eq!(n, 1.6700, epsilon = 1e-4);
623 assert!(k.is_none());
624 }
625
626 #[test]
627 fn test_interpolate_formula_4() {
628 let data = DispersionData::Formula4 {
630 wavelength_range: [0.3, 1.06],
631 c: vec![2.1823, 0.0125, 0.0, 0.0300, 1.0, 0.0, 0.0, 0.0, 1.0],
632 };
633 let (n, k) = data.interpolate(0.5876).unwrap();
634 assert_abs_diff_eq!(n, 1.4906, epsilon = 1e-4);
635 assert!(k.is_none());
636 }
637
638 #[test]
639 fn test_interpolate_formula_5() {
640 let data = DispersionData::Formula5 {
642 wavelength_range: [0.31, 1.55],
643 c: vec![1.502787, 455872.4E-8, -2.0, 9.844856E-5, -4.0],
644 };
645 let (n, k) = data.interpolate(0.5876).unwrap();
646 assert_abs_diff_eq!(n, 1.5168, epsilon = 1e-4);
647 assert!(k.is_none());
648 }
649
650 #[test]
651 fn test_interpolate_formula_6() {
652 let data = DispersionData::Formula6 {
654 wavelength_range: [0.168, 1.6945],
655 c: vec![0.0, 0.0148956, 180.7, 0.0049037, 92.0],
656 };
657 let (n, k) = data.interpolate(0.5876).unwrap();
658 assert_abs_diff_eq!(n, 1.00013881, epsilon = 1e-8);
659 assert!(k.is_none());
660 }
661
662 #[test]
663 fn test_interpolate_formula_7() {
664 let data = DispersionData::Formula7 {
666 wavelength_range: [2.4373, 25.0],
667 c: vec![3.41983, 0.159906, -0.123109, 1.26878E-6, -1.95104E-9],
668 };
669 let (n, k) = data.interpolate(2.4373).unwrap();
670 assert_abs_diff_eq!(n, 3.4434, epsilon = 1e-4);
671 assert!(k.is_none());
672 }
673
674 #[test]
675 fn test_interpolate_formula_8() {
676 let data = DispersionData::Formula8 {
678 wavelength_range: [0.43, 0.66],
679 c: vec![0.47856, 0.07858, 0.08277, -0.00881],
680 };
681 let (n, k) = data.interpolate(0.5876).unwrap();
682 assert_abs_diff_eq!(n, 2.2636, epsilon = 1e-4);
683 assert!(k.is_none());
684 }
685
686 #[test]
687 fn test_interpolate_formula_9() {
688 let data = DispersionData::Formula9 {
690 wavelength_range: [0.3, 1.06],
691 c: vec![2.51527, 0.0240, 0.0300, 0.020, 1.52, 0.8771],
692 };
693 let (n, k) = data.interpolate(0.5876).unwrap();
694 assert_abs_diff_eq!(n, 1.6065, epsilon = 1e-4);
695 assert!(k.is_none());
696 }
697}