1use crate::axis::Axis;
13use crate::error::{Error, Result};
14use crate::waveform::Waveform;
15use core::fmt;
16use core::ops::{Index, IndexMut};
17
18mod convert;
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
22pub enum Face {
23 XMin,
25 XMax,
27 YMin,
29 YMax,
31 ZMin,
33 ZMax,
35}
36
37impl Face {
38 pub const ALL: [Face; 6] = [
41 Face::XMin,
42 Face::XMax,
43 Face::YMin,
44 Face::YMax,
45 Face::ZMin,
46 Face::ZMax,
47 ];
48
49 pub const fn index(self) -> usize {
51 self as usize
52 }
53
54 pub const fn from_index(i: usize) -> Option<Face> {
56 if i < 6 { Some(Face::ALL[i]) } else { None }
57 }
58
59 pub const fn new(axis: Axis, max: bool) -> Face {
61 Face::ALL[2 * axis.index() + max as usize]
62 }
63
64 pub const fn axis(self) -> Axis {
66 match self {
67 Face::XMin | Face::XMax => Axis::X,
68 Face::YMin | Face::YMax => Axis::Y,
69 Face::ZMin | Face::ZMax => Axis::Z,
70 }
71 }
72
73 pub const fn is_max(self) -> bool {
75 self.index() % 2 == 1
76 }
77
78 pub const fn opposite(self) -> Face {
80 Face::new(self.axis(), !self.is_max())
81 }
82
83 pub const fn name(self) -> &'static str {
85 match self {
86 Face::XMin => "xmin",
87 Face::XMax => "xmax",
88 Face::YMin => "ymin",
89 Face::YMax => "ymax",
90 Face::ZMin => "zmin",
91 Face::ZMax => "zmax",
92 }
93 }
94}
95
96impl fmt::Display for Face {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 f.write_str(self.name())
99 }
100}
101
102#[derive(Debug, Clone, Copy, PartialEq, Default)]
110pub struct Mur {
111 pub phase_velocity: Option<f64>,
114}
115
116impl Mur {
117 pub const fn new() -> Self {
119 Mur {
120 phase_velocity: None,
121 }
122 }
123
124 pub const fn with_phase_velocity(v: f64) -> Self {
126 Mur {
127 phase_velocity: Some(v),
128 }
129 }
130
131 pub fn velocity(&self) -> f64 {
133 self.phase_velocity.unwrap_or(crate::constants::C0)
134 }
135
136 pub fn validate(&self) -> Result<()> {
138 match self.phase_velocity {
139 Some(v) if !(v.is_finite() && v > 0.0) => Err(Error::Invalid(format!(
140 "Mur boundary: phase velocity {v} must be finite and > 0"
141 ))),
142 _ => Ok(()),
143 }
144 }
145}
146
147#[derive(Debug, Clone, Copy, PartialEq)]
172pub struct Cpml {
173 pub cells: usize,
175 pub grading_order: f64,
177 pub sigma_factor: f64,
179 pub kappa_max: f64,
181 pub alpha_max: f64,
183}
184
185impl Default for Cpml {
186 fn default() -> Self {
187 Cpml {
188 cells: 8,
189 grading_order: 3.0,
190 sigma_factor: 1.0,
191 kappa_max: 1.0,
192 alpha_max: 0.0,
193 }
194 }
195}
196
197impl Cpml {
198 pub fn cells(n: usize) -> Self {
200 Cpml {
201 cells: n,
202 ..Cpml::default()
203 }
204 }
205
206 pub fn grading_order(mut self, m: f64) -> Self {
208 self.grading_order = m;
209 self
210 }
211
212 pub fn sigma_factor(mut self, k: f64) -> Self {
214 self.sigma_factor = k;
215 self
216 }
217
218 pub fn kappa_max(mut self, k: f64) -> Self {
220 self.kappa_max = k;
221 self
222 }
223
224 pub fn alpha_max(mut self, a: f64) -> Self {
226 self.alpha_max = a;
227 self
228 }
229
230 pub fn sigma_max(&self, eta: f64, delta: f64) -> f64 {
234 self.sigma_factor * 0.8 * (self.grading_order + 1.0) / (eta * delta)
235 }
236
237 pub fn validate(&self) -> Result<()> {
240 let bad = |what: &str, v: f64, req: &str| {
241 Err(Error::Invalid(format!("CPML: {what} = {v} must be {req}")))
242 };
243 if self.cells == 0 {
244 return Err(Error::Invalid("CPML: needs at least one cell".into()));
245 }
246 if !(self.grading_order.is_finite() && self.grading_order >= 0.0) {
247 return bad("grading order", self.grading_order, "finite and >= 0");
248 }
249 if !(self.sigma_factor.is_finite() && self.sigma_factor >= 0.0) {
250 return bad("sigma factor", self.sigma_factor, "finite and >= 0");
251 }
252 if !(self.kappa_max.is_finite() && self.kappa_max >= 1.0) {
253 return bad("kappa_max", self.kappa_max, "finite and >= 1");
254 }
255 if !(self.alpha_max.is_finite() && self.alpha_max >= 0.0) {
256 return bad("alpha_max", self.alpha_max, "finite and >= 0");
257 }
258 Ok(())
259 }
260}
261
262#[derive(Debug, Clone, Copy, PartialEq, Default)]
264pub enum Boundary {
265 #[default]
267 Pec,
268 Pmc,
270 Mur(Mur),
272 Pml(Cpml),
274 Periodic,
280}
281
282impl Boundary {
283 pub const fn mur() -> Self {
285 Boundary::Mur(Mur::new())
286 }
287
288 pub fn pml(n: usize) -> Self {
290 Boundary::Pml(Cpml::cells(n))
291 }
292
293 pub fn cells(&self) -> usize {
296 match self {
297 Boundary::Pml(p) => p.cells,
298 _ => 0,
299 }
300 }
301
302 pub fn validate(&self) -> Result<()> {
304 match self {
305 Boundary::Pec | Boundary::Pmc | Boundary::Periodic => Ok(()),
306 Boundary::Mur(m) => m.validate(),
307 Boundary::Pml(p) => p.validate(),
308 }
309 }
310}
311
312#[derive(Debug, Clone, Copy, PartialEq, Default)]
314pub struct Boundaries([Boundary; 6]);
315
316impl Boundaries {
317 pub fn all(b: Boundary) -> Self {
319 Boundaries([b; 6])
320 }
321
322 pub fn with(mut self, face: Face, b: Boundary) -> Self {
324 self.0[face.index()] = b;
325 self
326 }
327
328 pub fn set(&mut self, face: Face, b: Boundary) {
330 self.0[face.index()] = b;
331 }
332
333 pub fn get(&self, face: Face) -> &Boundary {
335 &self.0[face.index()]
336 }
337
338 pub fn as_array(&self) -> &[Boundary; 6] {
340 &self.0
341 }
342
343 pub fn iter(&self) -> impl Iterator<Item = (Face, &Boundary)> {
345 Face::ALL.into_iter().zip(self.0.iter())
346 }
347
348 pub fn validate(&self) -> Result<()> {
351 for (face, b) in self.iter() {
352 b.validate()
353 .map_err(|e| Error::Invalid(format!("boundary {face}: {e}")))?;
354 if *b == Boundary::Periodic && self[face.opposite()] != Boundary::Periodic {
355 return Err(Error::Invalid(format!(
356 "boundary {face}: periodic needs the opposite face {} periodic too",
357 face.opposite()
358 )));
359 }
360 }
361 Ok(())
362 }
363
364 pub fn is_periodic(&self, axis: Axis) -> bool {
366 self[Face::new(axis, false)] == Boundary::Periodic
367 && self[Face::new(axis, true)] == Boundary::Periodic
368 }
369}
370
371impl From<[Boundary; 6]> for Boundaries {
372 fn from(b: [Boundary; 6]) -> Self {
373 Boundaries(b)
374 }
375}
376
377impl Index<Face> for Boundaries {
378 type Output = Boundary;
379 fn index(&self, face: Face) -> &Boundary {
380 &self.0[face.index()]
381 }
382}
383
384impl IndexMut<Face> for Boundaries {
385 fn index_mut(&mut self, face: Face) -> &mut Boundary {
386 &mut self.0[face.index()]
387 }
388}
389
390#[derive(Debug, Clone, Copy, PartialEq)]
394pub struct EndCriteria {
395 pub energy_ratio: f64,
397 pub check_interval: u64,
399}
400
401impl EndCriteria {
402 pub const DEFAULT_CHECK_INTERVAL: u64 = 50;
404
405 pub fn energy_decay_db(db: f64) -> Self {
409 EndCriteria::energy_ratio(10f64.powf(db / 10.0))
410 }
411
412 pub fn energy_ratio(ratio: f64) -> Self {
414 EndCriteria {
415 energy_ratio: ratio,
416 check_interval: Self::DEFAULT_CHECK_INTERVAL,
417 }
418 }
419
420 pub fn check_interval(mut self, steps: u64) -> Self {
422 self.check_interval = steps;
423 self
424 }
425
426 pub fn decay_db(&self) -> f64 {
428 10.0 * self.energy_ratio.log10()
429 }
430
431 pub fn validate(&self) -> Result<()> {
433 let r = self.energy_ratio;
434 if !(r > 0.0 && r <= 1.0) {
435 return Err(Error::Invalid(format!(
436 "end criteria: energy ratio {r} must be in (0, 1]"
437 )));
438 }
439 if self.check_interval == 0 {
440 return Err(Error::Invalid(
441 "end criteria: check interval must be at least 1 step".into(),
442 ));
443 }
444 Ok(())
445 }
446}
447
448#[derive(Debug, Clone)]
465pub struct Fdtd {
466 waveform: Option<Waveform>,
467 boundaries: Boundaries,
468 end_criteria: Option<EndCriteria>,
469 max_timesteps: u64,
470 timestep_factor: f64,
471 timestep: Option<f64>,
472 threads: Option<usize>,
473 max_frequency: Option<f64>,
474 max_time: Option<f64>,
475 oversampling: Option<u32>,
476 bloch_wavevector: Option<[f64; 3]>,
477 bloch_frequency: Option<f64>,
478 multigrid: Vec<f64>,
479}
480
481impl Default for Fdtd {
482 fn default() -> Self {
483 Fdtd {
484 waveform: None,
485 boundaries: Boundaries::default(),
486 end_criteria: None,
487 max_timesteps: Self::DEFAULT_MAX_TIMESTEPS,
488 timestep_factor: Self::DEFAULT_TIMESTEP_FACTOR,
489 timestep: None,
490 threads: None,
491 max_frequency: None,
492 max_time: None,
493 oversampling: None,
494 bloch_wavevector: None,
495 bloch_frequency: None,
496 multigrid: Vec::new(),
497 }
498 }
499}
500
501impl Fdtd {
502 pub const DEFAULT_MAX_TIMESTEPS: u64 = 1_000_000;
504 pub const DEFAULT_TIMESTEP_FACTOR: f64 = 0.99;
507
508 pub fn new() -> Self {
513 Fdtd::default()
514 }
515
516 pub fn waveform(mut self, w: Waveform) -> Self {
518 self.waveform = Some(w);
519 self
520 }
521
522 pub fn boundaries(mut self, b: Boundaries) -> Self {
524 self.boundaries = b;
525 self
526 }
527
528 pub fn boundary(mut self, face: Face, b: Boundary) -> Self {
530 self.boundaries.set(face, b);
531 self
532 }
533
534 pub fn end_criteria(mut self, e: EndCriteria) -> Self {
537 self.end_criteria = Some(e);
538 self
539 }
540
541 pub fn no_end_criteria(mut self) -> Self {
543 self.end_criteria = None;
544 self
545 }
546
547 pub fn max_timesteps(mut self, n: u64) -> Self {
549 self.max_timesteps = n;
550 self
551 }
552
553 pub fn timestep_factor(mut self, s: f64) -> Self {
556 self.timestep_factor = s;
557 self
558 }
559
560 pub fn timestep(mut self, dt: f64) -> Self {
563 self.timestep = Some(dt);
564 self
565 }
566
567 pub fn threads(mut self, n: usize) -> Self {
569 self.threads = Some(n);
570 self
571 }
572
573 pub fn max_frequency(mut self, f: f64) -> Self {
577 self.max_frequency = Some(f);
578 self
579 }
580
581 pub fn max_time(mut self, t: f64) -> Self {
585 self.max_time = Some(t);
586 self
587 }
588
589 pub fn oversampling(mut self, n: u32) -> Self {
593 self.oversampling = Some(n);
594 self
595 }
596
597 pub fn bloch_wavevector(mut self, k: [f64; 3]) -> Self {
612 self.bloch_wavevector = Some(k);
613 self
614 }
615
616 pub fn bloch_frequency(mut self, f: f64) -> Self {
621 self.bloch_frequency = Some(f);
622 self
623 }
624
625 pub fn multigrid(mut self, radii: impl Into<Vec<f64>>) -> Self {
640 self.multigrid = radii.into();
641 self
642 }
643
644 pub fn get_multigrid(&self) -> &[f64] {
646 &self.multigrid
647 }
648
649 pub fn get_bloch_wavevector(&self) -> Option<[f64; 3]> {
651 self.bloch_wavevector
652 }
653
654 pub fn get_bloch_frequency(&self) -> Option<f64> {
656 self.bloch_frequency
657 }
658
659 pub fn get_max_frequency(&self) -> Option<f64> {
663 self.max_frequency
664 .or_else(|| self.waveform.as_ref().and_then(Waveform::max_frequency))
665 }
666
667 pub fn get_explicit_max_frequency(&self) -> Option<f64> {
669 self.max_frequency
670 }
671
672 pub fn get_max_time(&self) -> Option<f64> {
674 self.max_time
675 }
676
677 pub fn get_oversampling(&self) -> Option<u32> {
679 self.oversampling
680 }
681
682 pub fn timestep_limit(&self, dt: f64) -> u64 {
686 match self.max_time {
687 Some(t) if dt > 0.0 => {
688 let r = t / dt;
690 let n = if (r - r.round()).abs() <= 1e-9 * r {
691 r.round()
692 } else {
693 r.ceil()
694 };
695 if n < self.max_timesteps as f64 {
696 (n as u64).max(1)
697 } else {
698 self.max_timesteps
699 }
700 }
701 _ => self.max_timesteps,
702 }
703 }
704
705 pub fn get_waveform(&self) -> Option<&Waveform> {
707 self.waveform.as_ref()
708 }
709
710 pub fn get_boundaries(&self) -> &Boundaries {
712 &self.boundaries
713 }
714
715 pub fn boundaries_mut(&mut self) -> &mut Boundaries {
717 &mut self.boundaries
718 }
719
720 pub fn get_end_criteria(&self) -> Option<&EndCriteria> {
722 self.end_criteria.as_ref()
723 }
724
725 pub fn get_max_timesteps(&self) -> u64 {
727 self.max_timesteps
728 }
729
730 pub fn get_timestep_factor(&self) -> f64 {
732 self.timestep_factor
733 }
734
735 pub fn get_timestep(&self) -> Option<f64> {
737 self.timestep
738 }
739
740 pub fn get_threads(&self) -> Option<usize> {
743 self.threads
744 }
745
746 pub fn resolved_threads(&self) -> usize {
751 self.threads.unwrap_or_else(|| {
752 std::thread::available_parallelism()
753 .map(|n| n.get())
754 .unwrap_or(1)
755 })
756 }
757
758 pub fn validate(&self) -> Result<()> {
763 if let Some(w) = &self.waveform {
764 w.validate()?;
765 }
766 self.boundaries.validate()?;
767 if let Some(e) = &self.end_criteria {
768 e.validate()?;
769 }
770 if self.max_timesteps == 0 {
771 return Err(Error::Invalid("max_timesteps must be at least 1".into()));
772 }
773 let s = self.timestep_factor;
774 if !(s > 0.0 && s <= 1.0) {
775 return Err(Error::Invalid(format!(
776 "timestep factor {s} must be in (0, 1]"
777 )));
778 }
779 if let Some(r) = self
780 .multigrid
781 .iter()
782 .find(|r| !(r.is_finite() && **r > 0.0))
783 {
784 return Err(Error::Invalid(format!(
785 "multigrid radius {r} must be finite and > 0"
786 )));
787 }
788 if let Some(dt) = self.timestep
789 && !(dt.is_finite() && dt > 0.0)
790 {
791 return Err(Error::Invalid(format!(
792 "timestep {dt} must be finite and > 0"
793 )));
794 }
795 if self.threads == Some(0) {
796 return Err(Error::Invalid("thread count must be at least 1".into()));
797 }
798 if let Some(f) = self.max_frequency
799 && !(f.is_finite() && f > 0.0)
800 {
801 return Err(Error::Invalid(format!(
802 "maximum frequency {f} must be finite and > 0"
803 )));
804 }
805 if let Some(t) = self.max_time
806 && !(t.is_finite() && t > 0.0)
807 {
808 return Err(Error::Invalid(format!(
809 "maximum time {t} must be finite and > 0"
810 )));
811 }
812 if self.oversampling == Some(0) {
813 return Err(Error::Invalid("oversampling must be at least 1".into()));
814 }
815 if let Some(k) = self.bloch_wavevector {
816 for a in Axis::ALL {
817 let ka = k[a.index()];
818 if !ka.is_finite() {
819 return Err(Error::Invalid(format!(
820 "Bloch wavevector component {ka} along {a} must be finite"
821 )));
822 }
823 if ka != 0.0 && !self.boundaries.is_periodic(a) {
824 return Err(Error::Invalid(format!(
825 "Bloch wavevector has a component along {a}, whose faces are not \
826 periodic"
827 )));
828 }
829 }
830 }
831 if let Some(f) = self.bloch_frequency
832 && !(f.is_finite() && f > 0.0)
833 {
834 return Err(Error::Invalid(format!(
835 "Bloch design frequency {f} must be finite and > 0"
836 )));
837 }
838 Ok(())
839 }
840}
841
842#[cfg(test)]
843mod tests {
844 use super::*;
845 use crate::constants::{C0, Z0};
846
847 #[test]
848 fn face_helpers() {
849 for (i, f) in Face::ALL.into_iter().enumerate() {
850 assert_eq!(f.index(), i);
851 assert_eq!(Face::from_index(i), Some(f));
852 assert_eq!(Face::new(f.axis(), f.is_max()), f);
853 assert_eq!(f.opposite().opposite(), f);
854 assert_ne!(f.opposite(), f);
855 assert_eq!(f.opposite().axis(), f.axis());
856 }
857 assert_eq!(Face::from_index(6), None);
858 assert_eq!(Face::new(Axis::Y, true), Face::YMax);
859 assert_eq!(Face::ZMin.axis(), Axis::Z);
860 assert!(!Face::XMin.is_max());
861 assert_eq!(Face::XMax.to_string(), "xmax");
862 }
863
864 #[test]
865 fn boundaries() {
866 let b = Boundaries::default();
867 assert!(b.iter().all(|(_, b)| *b == Boundary::Pec));
868 let b = Boundaries::all(Boundary::pml(8))
869 .with(Face::ZMin, Boundary::Pec)
870 .with(Face::XMax, Boundary::mur());
871 assert_eq!(*b.get(Face::ZMin), Boundary::Pec);
872 assert_eq!(b[Face::XMax], Boundary::Mur(Mur::new()));
873 assert_eq!(b[Face::YMin], Boundary::Pml(Cpml::default()));
874 assert_eq!(b[Face::YMin].cells(), 8);
875 assert_eq!(b[Face::ZMin].cells(), 0);
876 let mut b2 = b;
877 b2[Face::ZMin] = Boundary::Pmc;
878 b2.set(Face::ZMax, Boundary::Pmc);
879 assert_eq!(b2.as_array()[4], Boundary::Pmc);
880 assert_eq!(b2.as_array()[5], Boundary::Pmc);
881 assert!(b.validate().is_ok());
882 let bad = b.with(Face::YMax, Boundary::pml(0));
883 let e = bad.validate().unwrap_err().to_string();
884 assert!(e.contains("ymax"), "{e}");
885 assert_eq!(
886 Boundaries::from([Boundary::Pmc; 6]),
887 Boundaries::all(Boundary::Pmc)
888 );
889 }
890
891 #[test]
892 fn periodic_pairs() {
893 let b = Boundaries::all(Boundary::pml(8))
894 .with(Face::XMin, Boundary::Periodic)
895 .with(Face::XMax, Boundary::Periodic);
896 assert!(b.validate().is_ok());
897 assert!(b.is_periodic(Axis::X));
898 assert!(!b.is_periodic(Axis::Y));
899 let e = b
900 .with(Face::YMax, Boundary::Periodic)
901 .validate()
902 .unwrap_err()
903 .to_string();
904 assert!(e.contains("ymax") && e.contains("ymin"), "{e}");
905 assert_eq!(Boundary::Periodic.cells(), 0);
906 }
907
908 #[test]
909 fn cpml() {
910 let p = Cpml::cells(10)
911 .grading_order(4.0)
912 .sigma_factor(0.5)
913 .kappa_max(5.0)
914 .alpha_max(0.05);
915 assert_eq!(p.cells, 10);
916 assert_eq!(p.grading_order, 4.0);
917 assert!(p.validate().is_ok());
918 let d = Cpml::default();
919 assert_eq!((d.cells, d.grading_order, d.sigma_factor), (8, 3.0, 1.0));
920 assert_eq!((d.kappa_max, d.alpha_max), (1.0, 0.0));
921 let s = d.sigma_max(Z0, 1e-3);
922 assert!((s - 0.8 * 4.0 / (Z0 * 1e-3)).abs() < 1e-9 * s);
923 assert!((p.sigma_max(Z0, 1e-3) - 0.5 * 0.8 * 5.0 / (Z0 * 1e-3)).abs() < 1e-9);
924 assert!(Cpml::cells(0).validate().is_err());
925 assert!(d.kappa_max(0.5).validate().is_err());
926 assert!(d.alpha_max(-1.0).validate().is_err());
927 assert!(d.sigma_factor(f64::NAN).validate().is_err());
928 assert!(d.grading_order(-1.0).validate().is_err());
929 }
930
931 #[test]
932 fn mur() {
933 assert_eq!(Mur::new().velocity(), C0);
934 assert_eq!(Mur::with_phase_velocity(1e8).velocity(), 1e8);
935 assert!(Mur::new().validate().is_ok());
936 assert!(Mur::with_phase_velocity(0.0).validate().is_err());
937 assert!(
938 Boundary::Mur(Mur::with_phase_velocity(f64::INFINITY))
939 .validate()
940 .is_err()
941 );
942 }
943
944 #[test]
945 fn end_criteria() {
946 let e = EndCriteria::energy_decay_db(-40.0);
947 assert!((e.energy_ratio - 1e-4).abs() < 1e-18);
948 assert!((e.decay_db() + 40.0).abs() < 1e-12);
949 assert_eq!(e.check_interval, EndCriteria::DEFAULT_CHECK_INTERVAL);
950 let e = EndCriteria::energy_ratio(1e-5).check_interval(10);
951 assert_eq!(e.check_interval, 10);
952 assert!(e.validate().is_ok());
953 assert!(e.check_interval(0).validate().is_err());
954 assert!(EndCriteria::energy_ratio(0.0).validate().is_err());
955 assert!(EndCriteria::energy_ratio(1.5).validate().is_err());
956 assert!(EndCriteria::energy_ratio(f64::NAN).validate().is_err());
957 assert!(EndCriteria::energy_decay_db(0.0).validate().is_ok());
958 }
959
960 #[test]
961 fn fdtd_builder_and_getters() {
962 let f = Fdtd::new();
963 assert!(f.get_waveform().is_none());
964 assert_eq!(*f.get_boundaries(), Boundaries::default());
965 assert!(f.get_end_criteria().is_none());
966 assert_eq!(f.get_max_timesteps(), Fdtd::DEFAULT_MAX_TIMESTEPS);
967 assert_eq!(f.get_timestep_factor(), 0.99);
968 assert_eq!(f.get_timestep(), None);
969 assert_eq!(f.get_threads(), None);
970 assert!(f.resolved_threads() >= 1);
971 assert!(f.validate().is_ok());
972
973 let mut f = Fdtd::new()
974 .waveform(Waveform::gaussian(1e9, 0.5e9))
975 .boundaries(Boundaries::all(Boundary::pml(8)))
976 .boundary(Face::ZMin, Boundary::Pec)
977 .end_criteria(EndCriteria::energy_decay_db(-50.0))
978 .max_timesteps(1234)
979 .timestep_factor(0.5)
980 .timestep(1e-12)
981 .threads(3);
982 assert!(f.validate().is_ok());
983 assert!(matches!(
984 f.get_waveform(),
985 Some(Waveform::Gaussian { f0: 1e9, .. })
986 ));
987 assert_eq!(f.get_boundaries()[Face::ZMin], Boundary::Pec);
988 assert_eq!(f.get_boundaries()[Face::ZMax], Boundary::pml(8));
989 assert!(f.get_end_criteria().is_some());
990 assert_eq!(f.get_max_timesteps(), 1234);
991 assert_eq!(f.get_timestep_factor(), 0.5);
992 assert_eq!(f.get_timestep(), Some(1e-12));
993 assert_eq!((f.get_threads(), f.resolved_threads()), (Some(3), 3));
994 f.boundaries_mut()[Face::XMin] = Boundary::Pmc;
995 assert_eq!(f.get_boundaries()[Face::XMin], Boundary::Pmc);
996 assert!(f.clone().no_end_criteria().get_end_criteria().is_none());
997 }
998
999 #[test]
1000 fn fdtd_validation() {
1001 let ok = Fdtd::new();
1002 assert!(
1003 ok.clone()
1004 .waveform(Waveform::gaussian(1e9, 0.0))
1005 .validate()
1006 .is_err()
1007 );
1008 assert!(
1009 ok.clone()
1010 .boundary(Face::XMin, Boundary::pml(0))
1011 .validate()
1012 .is_err()
1013 );
1014 assert!(
1015 ok.clone()
1016 .end_criteria(EndCriteria::energy_ratio(-1.0))
1017 .validate()
1018 .is_err()
1019 );
1020 assert!(ok.clone().max_timesteps(0).validate().is_err());
1021 assert!(ok.clone().timestep_factor(0.0).validate().is_err());
1022 assert!(ok.clone().timestep_factor(1.01).validate().is_err());
1023 assert!(ok.clone().timestep_factor(f64::NAN).validate().is_err());
1024 assert!(ok.clone().timestep_factor(1.0).validate().is_ok());
1025 assert!(ok.clone().timestep(0.0).validate().is_err());
1026 assert!(ok.clone().timestep(f64::INFINITY).validate().is_err());
1027 assert!(ok.clone().threads(0).validate().is_err());
1028 assert!(ok.clone().max_frequency(0.0).validate().is_err());
1029 assert!(ok.clone().max_time(f64::NAN).validate().is_err());
1030 assert!(ok.clone().oversampling(0).validate().is_err());
1031 }
1032
1033 #[test]
1034 fn max_frequency_time_and_oversampling() {
1035 let f = Fdtd::new();
1036 assert_eq!(f.get_max_frequency(), None);
1037 assert_eq!(f.get_max_time(), None);
1038 assert_eq!(f.get_oversampling(), None);
1039 assert_eq!(f.timestep_limit(1e-12), Fdtd::DEFAULT_MAX_TIMESTEPS);
1040 let f = f.waveform(Waveform::gaussian(1e9, 5e8));
1041 assert_eq!(f.get_max_frequency(), Some(1.5e9));
1042 assert_eq!(f.get_explicit_max_frequency(), None);
1043 let f = f
1044 .max_frequency(3e9)
1045 .max_time(1e-9)
1046 .oversampling(8)
1047 .max_timesteps(500);
1048 assert!(f.validate().is_ok());
1049 assert_eq!(f.get_max_frequency(), Some(3e9));
1050 assert_eq!(
1051 (f.get_max_time(), f.get_oversampling()),
1052 (Some(1e-9), Some(8))
1053 );
1054 assert_eq!(f.timestep_limit(1e-11), 100);
1055 assert_eq!(f.timestep_limit(3e-12), 334);
1056 assert_eq!(f.timestep_limit(1e-13), 500);
1057 assert_eq!(f.timestep_limit(1.0), 1);
1058 }
1059}