From dde1c687a2d6558a37bf401ee228826fb776bdee Mon Sep 17 00:00:00 2001 From: Udo Eisenbarth <u.eisenbarth@gsi.de> Date: Fri, 28 Mar 2025 11:14:29 +0100 Subject: [PATCH 1/7] Fix newline issue while saving OPM files --- opossum/src/lightdata/energy_spectrum_builder.rs | 2 +- opossum/src/main.rs | 2 +- opossum/src/nodes/node_group/mod.rs | 5 ++++- opossum/src/nodes/node_group/optic_graph.rs | 3 ++- opossum/src/opm_document.rs | 6 +++--- opossum/src/optic_ref.rs | 4 +++- opossum/src/spectrum.rs | 4 ++-- 7 files changed, 16 insertions(+), 10 deletions(-) diff --git a/opossum/src/lightdata/energy_spectrum_builder.rs b/opossum/src/lightdata/energy_spectrum_builder.rs index b8e46f2b..68f392f4 100644 --- a/opossum/src/lightdata/energy_spectrum_builder.rs +++ b/opossum/src/lightdata/energy_spectrum_builder.rs @@ -49,4 +49,4 @@ impl Display for EnergyDataBuilder { Self::LaserLines(l, r) => write!(f, "LaserLines({:?}, {})", l, r.value), } } -} \ No newline at end of file +} diff --git a/opossum/src/main.rs b/opossum/src/main.rs index a3c2396c..7cdc7176 100644 --- a/opossum/src/main.rs +++ b/opossum/src/main.rs @@ -85,7 +85,7 @@ fn create_report_and_data_files( write!( output, "{}", - ron::ser::to_string_pretty(&report, ron::ser::PrettyConfig::default()).unwrap() + ron::ser::to_string_pretty(&report, ron::ser::PrettyConfig::new().new_line("\n")).unwrap() ) .map_err(|e| OpossumError::Other(format!("writing report file failed: {e}")))?; let mut report_path = report_directory.to_path_buf(); diff --git a/opossum/src/nodes/node_group/mod.rs b/opossum/src/nodes/node_group/mod.rs index eb72e852..765083f6 100644 --- a/opossum/src/nodes/node_group/mod.rs +++ b/opossum/src/nodes/node_group/mod.rs @@ -733,7 +733,10 @@ mod test { let mut scenery = NodeGroup::default(); scenery.add_node(Dummy::default()).unwrap(); let report = scenery.toplevel_report().unwrap(); - assert!(ron::ser::to_string_pretty(&report, ron::ser::PrettyConfig::default()).is_ok()); + assert!( + ron::ser::to_string_pretty(&report, ron::ser::PrettyConfig::new().new_line("\n")) + .is_ok() + ); // How shall we further parse the output? } #[test] diff --git a/opossum/src/nodes/node_group/optic_graph.rs b/opossum/src/nodes/node_group/optic_graph.rs index 772e6e9f..f1018632 100644 --- a/opossum/src/nodes/node_group/optic_graph.rs +++ b/opossum/src/nodes/node_group/optic_graph.rs @@ -1648,7 +1648,8 @@ mod test { vec!["input_1", "input_2"] ); let serialized = - ron::ser::to_string_pretty(&graph, ron::ser::PrettyConfig::default()).unwrap(); + ron::ser::to_string_pretty(&graph, ron::ser::PrettyConfig::new().new_line("\n")) + .unwrap(); let deserialized: OpticGraph = ron::from_str(&serialized).unwrap(); assert_eq!( deserialized.port_map(&PortType::Input).port_names(), diff --git a/opossum/src/opm_document.rs b/opossum/src/opm_document.rs index 3eb361ee..049fa3bb 100644 --- a/opossum/src/opm_document.rs +++ b/opossum/src/opm_document.rs @@ -126,9 +126,9 @@ impl OpmDocument { /// /// This function will return an error if the serialization of the internal structures fail. pub fn to_opm_file_string(&self) -> OpmResult<String> { - ron::ser::to_string_pretty(&self, ron::ser::PrettyConfig::default()).map_err(|e| { - OpossumError::OpticScenery(format!("serialization of OpmDocument failed: {e}")) - }) + ron::ser::to_string_pretty(&self, ron::ser::PrettyConfig::new().new_line("\n")).map_err( + |e| OpossumError::OpticScenery(format!("serialization of OpmDocument failed: {e}")), + ) } /// Returns the list of analyzers of this [`OpmDocument`]. #[must_use] diff --git a/opossum/src/optic_ref.rs b/opossum/src/optic_ref.rs index 70d89d45..96a6c68d 100644 --- a/opossum/src/optic_ref.rs +++ b/opossum/src/optic_ref.rs @@ -224,7 +224,9 @@ mod test { #[test] fn serialize() { let optic_ref = OpticRef::new(Arc::new(Mutex::new(Dummy::default())), None); - let _ = ron::ser::to_string_pretty(&optic_ref, ron::ser::PrettyConfig::default()).unwrap(); + let _ = + ron::ser::to_string_pretty(&optic_ref, ron::ser::PrettyConfig::new().new_line("\n")) + .unwrap(); } #[test] fn deserialize() { diff --git a/opossum/src/spectrum.rs b/opossum/src/spectrum.rs index c2cc3231..bbb66adc 100644 --- a/opossum/src/spectrum.rs +++ b/opossum/src/spectrum.rs @@ -999,10 +999,10 @@ mod test { #[test] fn serialize() { let s = prep(); - let s_yaml = ron::ser::to_string_pretty(&s, ron::ser::PrettyConfig::default()); + let s_yaml = ron::ser::to_string_pretty(&s, ron::ser::PrettyConfig::new().new_line("\n")); assert!(s_yaml.is_ok()); assert_eq!(s_yaml.unwrap(), - "(\r\n data: [\r\n (1.0, 0.0),\r\n (1.5, 0.0),\r\n (2.0, 0.0),\r\n (2.5, 0.0),\r\n (3.0, 0.0),\r\n (3.5, 0.0),\r\n ],\r\n)".to_string()); + "(\n data: [\n (1.0, 0.0),\n (1.5, 0.0),\n (2.0, 0.0),\n (2.5, 0.0),\n (3.0, 0.0),\n (3.5, 0.0),\n ],\n)".to_string()); } #[test] fn deserialize() { -- GitLab From 5134821ea654e879d0be89bd61ca033fe53ad599 Mon Sep 17 00:00:00 2001 From: Udo Eisenbarth <u.eisenbarth@gsi.de> Date: Fri, 28 Mar 2025 11:26:21 +0100 Subject: [PATCH 2/7] Update examples using EnergyDataBuilder --- .../{opm_file.rs => empty_opm_file.rs} | 0 opossum/examples/group_reverse.rs | 9 +- .../examples/inverse_beam_splitter_test.rs | 9 +- opossum/examples/michaelson.rs | 9 +- opossum/examples/nalgebra_matrix_testing.rs | 212 ------------------ opossum/examples/reference_test.rs | 9 +- 6 files changed, 24 insertions(+), 224 deletions(-) rename opossum/examples/{opm_file.rs => empty_opm_file.rs} (100%) delete mode 100644 opossum/examples/nalgebra_matrix_testing.rs diff --git a/opossum/examples/opm_file.rs b/opossum/examples/empty_opm_file.rs similarity index 100% rename from opossum/examples/opm_file.rs rename to opossum/examples/empty_opm_file.rs diff --git a/opossum/examples/group_reverse.rs b/opossum/examples/group_reverse.rs index 332cadcb..f6a66ae3 100644 --- a/opossum/examples/group_reverse.rs +++ b/opossum/examples/group_reverse.rs @@ -4,18 +4,21 @@ use num::Zero; use opossum::{ analyzers::AnalyzerType, error::OpmResult, + joule, lightdata::{energy_spectrum_builder::EnergyDataBuilder, light_data_builder::LightDataBuilder}, + nanometer, nodes::{Dummy, EnergyMeter, NodeGroup, Source}, optic_node::OpticNode, - spectrum_helper::create_he_ne_spec, OpmDocument, }; use uom::si::f64::Length; fn main() -> OpmResult<()> { let mut scenery = NodeGroup::new("Inverse Group test"); - let light_data_builder = - LightDataBuilder::Energy(EnergyDataBuilder::Raw(create_he_ne_spec(1.0)?)); + let light_data_builder = LightDataBuilder::Energy(EnergyDataBuilder::LaserLines( + vec![(nanometer!(633.0), joule!(1.0))], + nanometer!(1.0), + )); let i_s = scenery.add_node(Source::new("Source", light_data_builder))?; let mut group = NodeGroup::default(); diff --git a/opossum/examples/inverse_beam_splitter_test.rs b/opossum/examples/inverse_beam_splitter_test.rs index aa609df5..bea16541 100644 --- a/opossum/examples/inverse_beam_splitter_test.rs +++ b/opossum/examples/inverse_beam_splitter_test.rs @@ -2,11 +2,12 @@ use num::Zero; use opossum::{ analyzers::AnalyzerType, error::OpmResult, + joule, lightdata::{energy_spectrum_builder::EnergyDataBuilder, light_data_builder::LightDataBuilder}, + nanometer, nodes::{BeamSplitter, EnergyMeter, NodeGroup, Source}, optic_node::OpticNode, ray::SplittingConfig, - spectrum_helper::create_he_ne_spec, OpmDocument, }; use std::path::Path; @@ -14,8 +15,10 @@ use uom::si::f64::Length; fn main() -> OpmResult<()> { let mut scenery = NodeGroup::new("inverse beam splitter test"); - let light_data_builder = - LightDataBuilder::Energy(EnergyDataBuilder::Raw(create_he_ne_spec(1.0)?)); + let light_data_builder = LightDataBuilder::Energy(EnergyDataBuilder::LaserLines( + vec![(nanometer!(633.0), joule!(1.0))], + nanometer!(1.0), + )); let i_s = scenery.add_node(Source::new("Source", light_data_builder))?; let mut bs = BeamSplitter::new("bs", &SplittingConfig::Ratio(0.6)).unwrap(); bs.set_inverted(true)?; diff --git a/opossum/examples/michaelson.rs b/opossum/examples/michaelson.rs index 9d77f2c8..87018a62 100644 --- a/opossum/examples/michaelson.rs +++ b/opossum/examples/michaelson.rs @@ -2,9 +2,10 @@ use num::Zero; use opossum::{ analyzers::AnalyzerType, error::OpmResult, + joule, lightdata::{energy_spectrum_builder::EnergyDataBuilder, light_data_builder::LightDataBuilder}, + nanometer, nodes::{BeamSplitter, Dummy, NodeGroup, NodeReference, Source}, - spectrum_helper::create_he_ne_spec, OpmDocument, }; use std::path::Path; @@ -12,8 +13,10 @@ use uom::si::f64::Length; fn main() -> OpmResult<()> { let mut scenery = NodeGroup::new("Michaelson interferomater"); - let light_data_builder = - LightDataBuilder::Energy(EnergyDataBuilder::Raw(create_he_ne_spec(1.0)?)); + let light_data_builder = LightDataBuilder::Energy(EnergyDataBuilder::LaserLines( + vec![(nanometer!(633.0), joule!(1.0))], + nanometer!(1.0), + )); let src = scenery.add_node(Source::new("Source", light_data_builder))?; let bs = scenery.add_node(BeamSplitter::default())?; let sample = scenery.add_node(Dummy::new("Sample"))?; diff --git a/opossum/examples/nalgebra_matrix_testing.rs b/opossum/examples/nalgebra_matrix_testing.rs deleted file mode 100644 index 9a65649c..00000000 --- a/opossum/examples/nalgebra_matrix_testing.rs +++ /dev/null @@ -1,212 +0,0 @@ -use itertools::Itertools; -use nalgebra::DVector; -use std::time::Instant; - -fn pairwise_sumation(input: &[f64]) -> f64 { - let sub_array_size = 32; - let vec_len = input.len(); - if vec_len < sub_array_size { - let mut sum = 0.; - for val in input { - sum += *val; - } - sum - } else { - let new_size = vec_len / 2; - pairwise_sumation(&input[..new_size]) + pairwise_sumation(&input[new_size..vec_len]) - } -} - -// fn pairwise_sum_matrix(input: &DVectorView<f64>) -> f64 { -// let vec_len = input.len(); -// if vec_len < 64 { -// input.sum() -// } else { -// let new_size = vec_len / 2; -// pairwise_sum_matrix(&input.rows(0, new_size)) -// + pairwise_sum_matrix(&input.rows(new_size, vec_len - new_size)) -// } -// } - -fn kahansum2(input: Vec<f64>) -> f64 { - // Prepare the accumulator. - // Prepare the accumulator. - let mut sum = 0.0; - // A running compensation for lost low-order bits. - let mut c = 0.0; - // The array input has elements indexed input[1] to input[input.length]. - for i in input.iter() { - // c is zero the first time around. - let y = i - c; - // Alas, sum is big, y small, so low-order digits of y are lost. - let t = sum + y; - // (t - sum) cancels the high-order part of y; - // subtracting y recovers negative (low part of y) - c = (t - sum) - y; - // Algebraically, c should always be zero. Beware - // overly-aggressive optimizing compilers! - sum = t; - // Next time around, the lost low part will be added to y in a fresh attempt. - } - return sum; -} - -fn kahansum_vector(input: &DVector<f64>) -> f64 { - // Prepare the accumulator. - // Prepare the accumulator. - let mut sum = 0.0; - // A running compensation for lost low-order bits. - let mut c = 0.0; - // The array input has elements indexed input[1] to input[input.length]. - for i in input { - // c is zero the first time around. - let y = i - &c; - // Alas, sum is big, y small, so low-order digits of y are lost. - let t = &sum + &y; - // (t - sum) cancels the high-order part of y; - // subtracting y recovers negative (low part of y) - c = (&t - &sum) - &y; - // Algebraically, c should always be zero. Beware - // overly-aggressive optimizing compilers! - sum = t; - // Next time around, the lost low part will be added to y in a fresh attempt. - } - return sum; -} - -fn main() { - // let mat = DVector::from(vec![-3., -2., -1., 0., 1., 2., 3.]); - // let mat2 = MatrixXx1::from(vec![-3., f64::NAN, -2., -1., 1., f64::INFINITY, 3.]); - // let mat22 = MatrixXx1::from(vec![f64::NAN, f64::INFINITY]); - // let mat3 = MatrixXx1::from(vec![f64::INFINITY, -2., -1., 0., 1., 2., 3.]); - // let mat4 = MatrixXx1::from(vec![f64::NAN, f64::NAN]); - // let infinite_plus = f64::INFINITY; - // let infinite_minus = -f64::INFINITY; - // let mut vec = Vec::<f64>::new(); - - // let test = mat.rows(0, 5); - // let test = mat.fixed_columns - - // let mat_wo_inf = mat2.iter().filter_map(|x| { - // if !x.is_nan() & x.is_finite(){ - // Some(x.clone()) - // } - // else{ - // None - // } - // }).collect::<Vec<f64>>(); - - // let mat_wo_inf = MatrixXx1::from( - // mat22 - // .iter() - // .cloned() - // .filter(|x| !x.is_nan() & x.is_finite()) - // .collect::<Vec<f64>>(), - // ); - // let bla: Vec<&f64> = mat_wo_inf.to_vec().clone(); - // let test = MatrixXx1::from_vec(bla); - // let mat_new = MatrixXx1::from_vec(mat_wo_inf.clone()); - - // println!("{mat2}"); - // println!("{mat_wo_inf}"); - // println!("{}", mat_wo_inf.len()); - - // // println!("min:\t{}\nmax:\t{}\n", mat.min(), mat.max()); - // println!("min:\t{}\nmax:\t{}\n", mat2.min(), mat2.max()); - // println!("min:\t{}\nmax:\t{}\n", mat3.min(), mat3.max()); - // println!("min:\t{}\nmax:\t{}\n", mat4.min(), mat4.max()); - - // println!("min:\t{}\nmax:\t{}\n", mat_wo_inf.min(), mat_wo_inf.max()); - let s1 = 1000.100; - let s2 = 999.; - let test = (s1 - s2 - 1.1) / s1; - let test = format!("{:+e}", test); - println!("{test}"); - - let energy = 1. + f64::EPSILON; - let num_rays = 1000001; - - let energy_per_ray = energy * 1. / num_rays as f64; - - let mut rays_vec2 = Vec::<f64>::with_capacity(num_rays); - for _ in 0..(num_rays as usize) { - rays_vec2.push(energy_per_ray) - } - - let mat = DVector::from_vec(rays_vec2.clone()); - let rays_vec1 = rays_vec2.clone(); - let rays_vec3 = rays_vec2.clone(); - let energy_vec = rays_vec2.iter().fold(0., |a, b| a + b); - - let start = Instant::now(); - let energy_vec0 = kahansum_vector(&mat); //sum_with_accumulator::<NaiveSum<f64>>(); - let duration = start.elapsed(); - println!( - "Time elapsed in kahan matrix summation() is: {:?}", - duration - ); - - let start = Instant::now(); - let energy_vec1 = rays_vec2.iter().cloned().tree_reduce(|a, b| a + b); //sum_with_accumulator::<NaiveSum<f64>>(); - let duration = start.elapsed(); - println!( - "Time elapsed in pariwise itertools summation() is: {:?}", - duration - ); - - let start = Instant::now(); - let energy_vec2 = kahansum2(rays_vec1); - let duration = start.elapsed(); - println!("Time elapsed in kahan summation() is: {:?}", duration); - - let start = Instant::now(); - let energy_vec3 = pairwise_sumation(&rays_vec3[..]); - let duration = start.elapsed(); - println!( - "Time elapsed in pairwise self written summation() is: {:?}", - duration - ); - - println!("energy:{}", energy_vec0); - println!("energy:{}", energy_vec); - println!("energy:{}", energy_vec1.unwrap()); - println!("energy:{}", energy_vec2); - println!("energy:{}", energy_vec3); - - // println!("energy:{}", energy_vec); - // println!("energy:{}", energy_vec2); - // println!("energy:{}", energy_vec3); - // let mut summed_time = Duration::new(0, 0); - // for i in 1..1000{ - // let energy = (i as f64)+f64::EPSILON; - // let num_rays = 1000 * i; - // let energy_per_ray = energy/num_rays as f64; - // let mut rays_vec = Vec::<f64>::with_capacity(num_rays); - // for i in 0..(num_rays as usize){ - // rays_vec.push(energy_per_ray) - // } - // let start = Instant::now(); - // let _ = kahansum2(rays_vec); - // let duration = start.elapsed(); - // summed_time += duration; - - // } - // println!("Time elapsed in kahan summation() is: {:?}", summed_time); - - // let mut summed_time = Duration::new(0, 0); - // for i in 1..1000{ - // let energy = (i as f64)+f64::EPSILON; - // let num_rays = 1000 * i; - // let energy_per_ray = energy/num_rays as f64; - // let mut rays_vec = Vec::<f64>::with_capacity(num_rays); - // for i in 0..(num_rays as usize){ - // rays_vec.push(energy_per_ray) - // } - // let start = Instant::now(); - // let _ = pairwise_sumation(&rays_vec); - // let duration = start.elapsed(); - // summed_time += duration; - - // } - // println!("Time elapsed in pairwise summation() is: {:?}", summed_time); -} diff --git a/opossum/examples/reference_test.rs b/opossum/examples/reference_test.rs index e87695dc..d0ba6e19 100644 --- a/opossum/examples/reference_test.rs +++ b/opossum/examples/reference_test.rs @@ -2,9 +2,10 @@ use num::Zero; use opossum::{ analyzers::AnalyzerType, error::OpmResult, + joule, lightdata::{energy_spectrum_builder::EnergyDataBuilder, light_data_builder::LightDataBuilder}, + nanometer, nodes::{EnergyMeter, IdealFilter, NodeGroup, NodeReference, Source}, - spectrum_helper::create_he_ne_spec, OpmDocument, }; use std::path::Path; @@ -12,8 +13,10 @@ use uom::si::f64::Length; fn main() -> OpmResult<()> { let mut scenery = NodeGroup::new("Reference node demo"); - let light_data_builder = - LightDataBuilder::Energy(EnergyDataBuilder::Raw(create_he_ne_spec(1.0)?)); + let light_data_builder = LightDataBuilder::Energy(EnergyDataBuilder::LaserLines( + vec![(nanometer!(633.0), joule!(1.0))], + nanometer!(1.0), + )); let src = scenery.add_node(Source::new("source", light_data_builder))?; let filt = scenery.add_node(IdealFilter::new( "50 % filter", -- GitLab From 0bd5ae29e8f6277401fa0ca87b7e7badecb025b8 Mon Sep 17 00:00:00 2001 From: Udo Eisenbarth <u.eisenbarth@gsi.de> Date: Fri, 28 Mar 2025 13:16:28 +0100 Subject: [PATCH 3/7] MAke PositionDistribution serializable --- opossum/src/coatings/constant_r.rs | 14 +++++----- opossum/src/coatings/fresnel.rs | 11 ++++---- opossum/src/coatings/ideal_ar.rs | 11 ++++---- opossum/src/coatings/mod.rs | 4 --- .../src/position_distributions/fibonacci.rs | 16 +++++++++-- opossum/src/position_distributions/grid.rs | 8 +++++- .../hexagonal_tiling.rs | 28 +++++-------------- .../src/position_distributions/hexapolar.rs | 8 +++++- opossum/src/position_distributions/mod.rs | 20 +++++++++++++ opossum/src/position_distributions/random.rs | 7 +++++ opossum/src/position_distributions/sobol.rs | 7 +++++ 11 files changed, 87 insertions(+), 47 deletions(-) diff --git a/opossum/src/coatings/constant_r.rs b/opossum/src/coatings/constant_r.rs index d412ab4d..0b3489f3 100644 --- a/opossum/src/coatings/constant_r.rs +++ b/opossum/src/coatings/constant_r.rs @@ -38,14 +38,14 @@ impl Coating for ConstantR { ) -> f64 { self.reflectivity } - - fn to_enum(&self) -> super::CoatingType { - CoatingType::ConstantR { - reflectivity: self.reflectivity, +} +impl From<ConstantR> for CoatingType { + fn from(coating: ConstantR) -> Self { + Self::ConstantR { + reflectivity: coating.reflectivity, } } } - #[cfg(test)] mod test { use super::*; @@ -64,10 +64,10 @@ mod test { assert!(ConstantR::new(1.1).is_err()); } #[test] - fn to_enum() { + fn from() { let coating = ConstantR::new(0.5).unwrap(); assert!(matches!( - coating.to_enum(), + coating.into(), CoatingType::ConstantR { reflectivity: 0.5 } )); } diff --git a/opossum/src/coatings/fresnel.rs b/opossum/src/coatings/fresnel.rs index 3a6da68b..d14f6952 100644 --- a/opossum/src/coatings/fresnel.rs +++ b/opossum/src/coatings/fresnel.rs @@ -32,11 +32,12 @@ impl Coating for Fresnel { // so far, we assume unpolarized (50/50) rays -> take average r_p.mul_add(r_p, r_s.powi(2)) / 2. } - fn to_enum(&self) -> super::CoatingType { - CoatingType::Fresnel +} +impl From<Fresnel> for CoatingType { + fn from(_coating: Fresnel) -> Self { + Self::Fresnel } } - #[cfg(test)] mod test { use super::*; @@ -45,9 +46,9 @@ mod test { use nalgebra::vector; #[test] - fn to_enum() { + fn from() { let coating = Fresnel; - assert!(matches!(coating.to_enum(), CoatingType::Fresnel)); + assert!(matches!(coating.into(), CoatingType::Fresnel)); } #[test] fn calc_refl_same_index() { diff --git a/opossum/src/coatings/ideal_ar.rs b/opossum/src/coatings/ideal_ar.rs index bebd5985..00cea71b 100644 --- a/opossum/src/coatings/ideal_ar.rs +++ b/opossum/src/coatings/ideal_ar.rs @@ -19,11 +19,12 @@ impl Coating for IdealAR { ) -> f64 { 0.0 } - fn to_enum(&self) -> super::CoatingType { - CoatingType::IdealAR +} +impl From<IdealAR> for CoatingType { + fn from(_coating: IdealAR) -> Self { + Self::IdealAR } } - #[cfg(test)] mod test { use super::*; @@ -31,9 +32,9 @@ mod test { use nalgebra::vector; #[test] - fn to_enum() { + fn from() { let coating = IdealAR; - assert!(matches!(coating.to_enum(), CoatingType::IdealAR)); + assert!(matches!(coating.into(), CoatingType::IdealAR)); } #[test] fn calc_refl() { diff --git a/opossum/src/coatings/mod.rs b/opossum/src/coatings/mod.rs index ceac2daf..0c3f2dce 100644 --- a/opossum/src/coatings/mod.rs +++ b/opossum/src/coatings/mod.rs @@ -63,8 +63,4 @@ pub trait Coating { /// Calculate the reflectivity based on the concrete model for an incoming [`Ray`] on a surface with /// a given `surface_normal` at the intersection point and the refractive index of the following medium. fn calc_reflectivity(&self, incoming_ray: &Ray, surface_normal: Vector3<f64>, n2: f64) -> f64; - /// Return the corresponding [`CoatingType`] for a given [`Coating`]. - /// - /// This function is mainly used for serialization / deserialization. - fn to_enum(&self) -> CoatingType; } diff --git a/opossum/src/position_distributions/fibonacci.rs b/opossum/src/position_distributions/fibonacci.rs index a44b72fe..9df84e12 100644 --- a/opossum/src/position_distributions/fibonacci.rs +++ b/opossum/src/position_distributions/fibonacci.rs @@ -7,11 +7,13 @@ use crate::error::{OpmResult, OpossumError}; use super::PositionDistribution; use nalgebra::{point, Point3}; use num::{ToPrimitive, Zero}; +use serde::{Deserialize, Serialize}; use uom::si::f64::Length; /// Rectangular Fibonacci distribution /// /// For further details see [here](https://en.wikipedia.org/wiki/Fibonacci_sequence) +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct FibonacciRectangle { nr_of_rays: usize, side_length_x: Length, @@ -62,11 +64,15 @@ impl PositionDistribution for FibonacciRectangle { points } } - +impl From<FibonacciRectangle> for super::PosDistType { + fn from(f: FibonacciRectangle) -> Self { + Self::FibonacciRectangle(f) + } +} /// Rectangular Fibbonacci distribution /// /// For further details see [here](https://en.wikipedia.org/wiki/Fibonacci_sequence) -#[derive(Clone)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct FibonacciEllipse { nr_of_rays: usize, radius_x: Length, @@ -119,7 +125,11 @@ impl PositionDistribution for FibonacciEllipse { points } } - +impl From<FibonacciEllipse> for super::PosDistType { + fn from(f: FibonacciEllipse) -> Self { + Self::FibonacciEllipse(f) + } +} #[cfg(test)] mod test { use super::*; diff --git a/opossum/src/position_distributions/grid.rs b/opossum/src/position_distributions/grid.rs index 2bda4beb..33b9d8bb 100644 --- a/opossum/src/position_distributions/grid.rs +++ b/opossum/src/position_distributions/grid.rs @@ -7,10 +7,11 @@ use crate::{ }; use nalgebra::Point3; use num::Zero; +use serde::{Deserialize, Serialize}; use uom::si::f64::Length; /// Rectangular, evenly-sized grid distribution -#[derive(Clone)] +#[derive(Clone, Serialize, Deserialize, Debug)] pub struct Grid { nr_of_points: (usize, usize), side_length: (Length, Length), @@ -91,6 +92,11 @@ impl PositionDistribution for Grid { } } +impl From<Grid> for super::PosDistType { + fn from(grid: Grid) -> Self { + Self::Grid(grid) + } +} #[cfg(test)] mod test { use super::*; diff --git a/opossum/src/position_distributions/hexagonal_tiling.rs b/opossum/src/position_distributions/hexagonal_tiling.rs index a6499eeb..388a578e 100644 --- a/opossum/src/position_distributions/hexagonal_tiling.rs +++ b/opossum/src/position_distributions/hexagonal_tiling.rs @@ -9,10 +9,11 @@ use crate::{ use super::PositionDistribution; use nalgebra::{Point2, Point3, Vector3}; use num::{ToPrimitive, Zero}; +use serde::{Deserialize, Serialize}; use uom::si::f64::Length; /// Circular, hexapolar distribution -#[derive(Clone)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct HexagonalTiling { nr_of_hex_along_radius: u8, radius: Length, @@ -93,25 +94,10 @@ impl PositionDistribution for HexagonalTiling { } points } - // fn generate(&self) -> Vec<Point3<Length>> { - // let mut points: Vec<Point3<Length>> = Vec::new(); - // // Add center point - // points.push(Point3::origin()); +} - // let radius_step = self.radius/self.nr_of_hex_along_radius.to_f64().unwrap(); - // for i in 1_u8..self.nr_of_hex_along_radius+1{ - // // let mut last_point = points.last().unwrap().clone(); - // // last_point.x += radius_step; - // let mut hex = Point3::origin(); - // hex.x += radius_step*i.to_f64().unwrap(); - // for j in 0_u8..6{ - // for k in 0_u8..i{ - // points.push(hex); - // let angle = PI/3.*(2.+j.to_f64().unwrap()); - // hex = hex + Vector3::new(f64::cos(angle)*radius_step, f64::sin(angle)*radius_step,Length::zero()); - // } - // } - // } - // points - // } +impl From<HexagonalTiling> for super::PosDistType { + fn from(hexagonal_tiling: HexagonalTiling) -> Self { + Self::HexagonalTiling(hexagonal_tiling) + } } diff --git a/opossum/src/position_distributions/hexapolar.rs b/opossum/src/position_distributions/hexapolar.rs index 5b7214b0..160101ad 100644 --- a/opossum/src/position_distributions/hexapolar.rs +++ b/opossum/src/position_distributions/hexapolar.rs @@ -4,10 +4,11 @@ use crate::error::{OpmResult, OpossumError}; use super::PositionDistribution; use nalgebra::{point, Point3}; use num::Zero; +use serde::{Deserialize, Serialize}; use uom::si::f64::Length; /// Circular, hexapolar distribution -#[derive(Clone)] +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct Hexapolar { nr_of_rings: u8, radius: Length, @@ -54,6 +55,11 @@ impl PositionDistribution for Hexapolar { points } } +impl From<Hexapolar> for super::PosDistType { + fn from(dist: Hexapolar) -> Self { + Self::Hexapolar(dist) + } +} #[cfg(test)] mod test { use super::*; diff --git a/opossum/src/position_distributions/mod.rs b/opossum/src/position_distributions/mod.rs index 4a44bdbf..252044fe 100644 --- a/opossum/src/position_distributions/mod.rs +++ b/opossum/src/position_distributions/mod.rs @@ -17,6 +17,7 @@ //! ``` //! `points` now contains a vector of 10 randomly-placed 3D points within the rectangle (-0.5 mm .. 0.5 mm) x (-1.0 mm .. 1.0 mm). use nalgebra::Point3; +use serde::{Deserialize, Serialize}; use uom::si::f64::Length; mod fibonacci; @@ -40,3 +41,22 @@ pub trait PositionDistribution { /// This function generates a vector of 3D points (of dimension [`Length`]) with the given parameters defined earlier. fn generate(&self) -> Vec<Point3<Length>>; } + +/// Enum for the different types of position distributions +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum PosDistType { + /// Rectangular, uniform random distribution + Random(random::Random), + /// Rectangular, evenly-sized grid distribution + Grid(grid::Grid), + /// Hexagonal tiling distribution + HexagonalTiling(hexagonal_tiling::HexagonalTiling), + /// Hexapolar distribution + Hexapolar(hexapolar::Hexapolar), + /// Fibonacci rectangle distribution + FibonacciRectangle(fibonacci::FibonacciRectangle), + /// Fibonacci ellipse distribution + FibonacciEllipse(fibonacci::FibonacciEllipse), + /// Pseudo random Sobol distribution + Sobol(sobol::SobolDist), +} diff --git a/opossum/src/position_distributions/random.rs b/opossum/src/position_distributions/random.rs index 96f4163f..b88c350b 100644 --- a/opossum/src/position_distributions/random.rs +++ b/opossum/src/position_distributions/random.rs @@ -5,9 +5,11 @@ use crate::error::{OpmResult, OpossumError}; use nalgebra::{point, Point3}; use num::Zero; use rand::Rng; +use serde::{Deserialize, Serialize}; use uom::si::f64::Length; /// Rectangular, uniform random distribution +#[derive(Debug, Clone, Serialize, Deserialize)] pub struct Random { nr_of_points: usize, side_length_x: Length, @@ -64,6 +66,11 @@ impl PositionDistribution for Random { points } } +impl From<Random> for super::PosDistType { + fn from(random: Random) -> Self { + Self::Random(random) + } +} #[cfg(test)] mod test { use super::*; diff --git a/opossum/src/position_distributions/sobol.rs b/opossum/src/position_distributions/sobol.rs index c164006d..23f966d4 100644 --- a/opossum/src/position_distributions/sobol.rs +++ b/opossum/src/position_distributions/sobol.rs @@ -3,12 +3,14 @@ use super::PositionDistribution; use crate::error::{OpmResult, OpossumError}; use nalgebra::{point, Point3}; use num::Zero; +use serde::{Deserialize, Serialize}; use sobol::{params::JoeKuoD6, Sobol}; use uom::si::f64::Length; /// Rectangluar, low-discrepancy quasirandom distribution /// /// For further details see [here](https://en.wikipedia.org/wiki/Sobol_sequence) +#[derive(Clone, Debug, Serialize, Deserialize)] pub struct SobolDist { nr_of_points: usize, side_length_x: Length, @@ -68,6 +70,11 @@ impl PositionDistribution for SobolDist { points } } +impl From<SobolDist> for super::PosDistType { + fn from(f: SobolDist) -> Self { + Self::Sobol(f) + } +} #[cfg(test)] mod test { use super::*; -- GitLab From e8930cdf17c1b127b21cff0ee120df7eddab8ad7 Mon Sep 17 00:00:00 2001 From: Udo Eisenbarth <u.eisenbarth@gsi.de> Date: Fri, 28 Mar 2025 13:34:09 +0100 Subject: [PATCH 4/7] Add From traits for refractive index --- opossum/src/refractive_index/mod.rs | 2 +- opossum/src/refractive_index/refr_index_conrady.rs | 5 +++++ opossum/src/refractive_index/refr_index_const.rs | 6 +++++- opossum/src/refractive_index/refr_index_schott.rs | 5 +++++ opossum/src/refractive_index/refr_index_sellmeier1.rs | 5 +++++ 5 files changed, 21 insertions(+), 2 deletions(-) diff --git a/opossum/src/refractive_index/mod.rs b/opossum/src/refractive_index/mod.rs index 326934a4..e25ec90d 100644 --- a/opossum/src/refractive_index/mod.rs +++ b/opossum/src/refractive_index/mod.rs @@ -66,7 +66,7 @@ impl From<RefractiveIndexType> for Proptype { } } /// All refractive index models must implement this trait. -pub trait RefractiveIndex { +pub trait RefractiveIndex: { /// Get the refractive index value of the current model for the given wavelength. /// /// # Errors diff --git a/opossum/src/refractive_index/refr_index_conrady.rs b/opossum/src/refractive_index/refr_index_conrady.rs index 4c6859e0..915ed5a3 100644 --- a/opossum/src/refractive_index/refr_index_conrady.rs +++ b/opossum/src/refractive_index/refr_index_conrady.rs @@ -65,6 +65,11 @@ impl RefractiveIndex for RefrIndexConrady { RefractiveIndexType::Conrady(self.clone()) } } +impl From<RefrIndexConrady> for RefractiveIndexType { + fn from(refr: RefrIndexConrady) -> Self { + Self::Conrady(refr) + } +} #[cfg(test)] mod test { use super::*; diff --git a/opossum/src/refractive_index/refr_index_const.rs b/opossum/src/refractive_index/refr_index_const.rs index 5f999e24..f20a5573 100644 --- a/opossum/src/refractive_index/refr_index_const.rs +++ b/opossum/src/refractive_index/refr_index_const.rs @@ -46,7 +46,11 @@ impl RefractiveIndex for RefrIndexConst { RefractiveIndexType::Const(self.clone()) } } - +impl From<RefrIndexConst> for RefractiveIndexType { + fn from(i: RefrIndexConst) -> Self { + Self::Const(i) + } +} #[cfg(test)] mod test { use num::Zero; diff --git a/opossum/src/refractive_index/refr_index_schott.rs b/opossum/src/refractive_index/refr_index_schott.rs index 888fedd2..19a6780e 100644 --- a/opossum/src/refractive_index/refr_index_schott.rs +++ b/opossum/src/refractive_index/refr_index_schott.rs @@ -93,6 +93,11 @@ impl RefractiveIndex for RefrIndexSchott { RefractiveIndexType::Schott(self.clone()) } } +impl From<RefrIndexSchott> for RefractiveIndexType { + fn from(refr: RefrIndexSchott) -> Self { + Self::Schott(refr) + } +} #[cfg(test)] mod test { use super::*; diff --git a/opossum/src/refractive_index/refr_index_sellmeier1.rs b/opossum/src/refractive_index/refr_index_sellmeier1.rs index 8f1d9955..85f250a4 100644 --- a/opossum/src/refractive_index/refr_index_sellmeier1.rs +++ b/opossum/src/refractive_index/refr_index_sellmeier1.rs @@ -91,6 +91,11 @@ impl RefractiveIndex for RefrIndexSellmeier1 { RefractiveIndexType::Sellmeier1(self.clone()) } } +impl From<RefrIndexSellmeier1> for RefractiveIndexType { + fn from(refr: RefrIndexSellmeier1) -> Self { + Self::Sellmeier1(refr) + } +} #[cfg(test)] mod test { use crate::nanometer; -- GitLab From 41753f5f276e9d9259f2d114c2e0fdf87e38dc9a Mon Sep 17 00:00:00 2001 From: Udo Eisenbarth <u.eisenbarth@gsi.de> Date: Fri, 28 Mar 2025 14:17:53 +0100 Subject: [PATCH 5/7] Add RayDataBuilder::Collimated --- .../energy_distributions/general_gaussian.rs | 9 +++++- opossum/src/energy_distributions/mod.rs | 30 +++++++++++++------ opossum/src/energy_distributions/uniform.rs | 11 +++++-- opossum/src/lightdata/ray_data_builder.rs | 19 +++++++++++- .../src/position_distributions/fibonacci.rs | 4 +-- opossum/src/position_distributions/grid.rs | 2 +- .../hexagonal_tiling.rs | 2 +- .../src/position_distributions/hexapolar.rs | 2 +- opossum/src/position_distributions/mod.rs | 17 ++++++++++- opossum/src/position_distributions/random.rs | 2 +- opossum/src/position_distributions/sobol.rs | 2 +- opossum/src/refractive_index/mod.rs | 2 +- 12 files changed, 79 insertions(+), 23 deletions(-) diff --git a/opossum/src/energy_distributions/general_gaussian.rs b/opossum/src/energy_distributions/general_gaussian.rs index 9489459d..cb37cec8 100644 --- a/opossum/src/energy_distributions/general_gaussian.rs +++ b/opossum/src/energy_distributions/general_gaussian.rs @@ -8,11 +8,14 @@ use crate::{ }; use kahan::KahanSummator; use nalgebra::Point2; +use serde::{Deserialize, Serialize}; use uom::si::{ angle::radian, energy::joule, f64::{Angle, Energy, Length}, }; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct General2DGaussian { total_energy: Energy, mu_xy: Point2<Length>, @@ -122,7 +125,11 @@ impl EnergyDistribution for General2DGaussian { self.total_energy } } - +impl From<General2DGaussian> for super::EnergyDistType { + fn from(g: General2DGaussian) -> Self { + Self::General2DGaussian(g) + } +} #[cfg(test)] mod test { use super::*; diff --git a/opossum/src/energy_distributions/mod.rs b/opossum/src/energy_distributions/mod.rs index d5a90479..22c01756 100644 --- a/opossum/src/energy_distributions/mod.rs +++ b/opossum/src/energy_distributions/mod.rs @@ -1,10 +1,14 @@ //! Module for handling energy distributions -use nalgebra::Point2; -use uom::si::f64::{Energy, Length}; - pub mod general_gaussian; pub mod uniform; +pub use general_gaussian::General2DGaussian; +use serde::{Deserialize, Serialize}; +pub use uniform::UniformDist; + +use crate::joule; use kahan::KahanSummator; +use nalgebra::Point2; +use uom::si::f64::{Energy, Length}; pub trait EnergyDistribution { fn apply(&self, input: &[Point2<Length>]) -> Vec<Energy>; @@ -31,10 +35,18 @@ pub trait EnergyDistribution { } } -pub use general_gaussian::General2DGaussian; -pub use uniform::UniformDist; +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum EnergyDistType { + Uniform(UniformDist), + General2DGaussian(general_gaussian::General2DGaussian), +} -use crate::joule; -// pub use hexapolar::Hexapolar; -// pub use random::Random; -// pub use sobol::SobolDist; +impl EnergyDistType { + #[must_use] + pub fn generate(&self) -> &dyn EnergyDistribution { + match self { + Self::Uniform(dist) => dist, + Self::General2DGaussian(dist) => dist, + } + } +} diff --git a/opossum/src/energy_distributions/uniform.rs b/opossum/src/energy_distributions/uniform.rs index 5e7f7233..e5640fe0 100644 --- a/opossum/src/energy_distributions/uniform.rs +++ b/opossum/src/energy_distributions/uniform.rs @@ -2,14 +2,15 @@ use nalgebra::Point2; use num::ToPrimitive; +use serde::{Deserialize, Serialize}; use uom::si::{ energy::joule, f64::{Energy, Length}, }; -use crate::error::{OpmResult, OpossumError}; - use super::EnergyDistribution; +use crate::error::{OpmResult, OpossumError}; +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct UniformDist { total_energy: Energy, } @@ -43,7 +44,11 @@ impl EnergyDistribution for UniformDist { self.total_energy } } - +impl From<UniformDist> for super::EnergyDistType { + fn from(ud: UniformDist) -> Self { + Self::Uniform(ud) + } +} #[cfg(test)] mod test { use super::*; diff --git a/opossum/src/lightdata/ray_data_builder.rs b/opossum/src/lightdata/ray_data_builder.rs index 5ee4d67b..46d7c082 100644 --- a/opossum/src/lightdata/ray_data_builder.rs +++ b/opossum/src/lightdata/ray_data_builder.rs @@ -4,8 +4,12 @@ //! This builder allows easier serialization / deserialization in OPM files. use std::fmt::Display; -use crate::{error::OpmResult, rays::Rays}; +use crate::{ + energy_distributions::EnergyDistType, error::OpmResult, position_distributions::PosDistType, + rays::Rays, +}; use serde::{Deserialize, Serialize}; +use uom::si::f64::Length; use super::LightData; @@ -14,6 +18,8 @@ use super::LightData; pub enum RayDataBuilder { /// Raw [`Rays`] data. Raw(Rays), + /// Collimated [`Rays`] data with a given [`PosDistType`] and [`EnergyDistType`] as well as a given wavelength. + Collimated(PosDistType, EnergyDistType, Length), } impl RayDataBuilder { @@ -24,6 +30,11 @@ impl RayDataBuilder { pub fn build(self) -> OpmResult<LightData> { match self { Self::Raw(rays) => Ok(LightData::Geometric(rays)), + Self::Collimated(pos_dist, energy_dist, wave_length) => { + let rays = + Rays::new_collimated(wave_length, energy_dist.generate(), pos_dist.generate())?; + Ok(LightData::Geometric(rays)) + } } } } @@ -32,6 +43,12 @@ impl Display for RayDataBuilder { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Raw(r) => write!(f, "Raw({r})"), + Self::Collimated(pos_dist, energy_dist, wave_length) => { + write!( + f, + "Collimated({pos_dist:?}, {energy_dist:?}, {wave_length:?})" + ) + } } } } diff --git a/opossum/src/position_distributions/fibonacci.rs b/opossum/src/position_distributions/fibonacci.rs index 9df84e12..cb45fc9b 100644 --- a/opossum/src/position_distributions/fibonacci.rs +++ b/opossum/src/position_distributions/fibonacci.rs @@ -13,7 +13,7 @@ use uom::si::f64::Length; /// Rectangular Fibonacci distribution /// /// For further details see [here](https://en.wikipedia.org/wiki/Fibonacci_sequence) -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct FibonacciRectangle { nr_of_rays: usize, side_length_x: Length, @@ -72,7 +72,7 @@ impl From<FibonacciRectangle> for super::PosDistType { /// Rectangular Fibbonacci distribution /// /// For further details see [here](https://en.wikipedia.org/wiki/Fibonacci_sequence) -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct FibonacciEllipse { nr_of_rays: usize, radius_x: Length, diff --git a/opossum/src/position_distributions/grid.rs b/opossum/src/position_distributions/grid.rs index 33b9d8bb..0ef74457 100644 --- a/opossum/src/position_distributions/grid.rs +++ b/opossum/src/position_distributions/grid.rs @@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize}; use uom::si::f64::Length; /// Rectangular, evenly-sized grid distribution -#[derive(Clone, Serialize, Deserialize, Debug)] +#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)] pub struct Grid { nr_of_points: (usize, usize), side_length: (Length, Length), diff --git a/opossum/src/position_distributions/hexagonal_tiling.rs b/opossum/src/position_distributions/hexagonal_tiling.rs index 388a578e..3fccec78 100644 --- a/opossum/src/position_distributions/hexagonal_tiling.rs +++ b/opossum/src/position_distributions/hexagonal_tiling.rs @@ -13,7 +13,7 @@ use serde::{Deserialize, Serialize}; use uom::si::f64::Length; /// Circular, hexapolar distribution -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct HexagonalTiling { nr_of_hex_along_radius: u8, radius: Length, diff --git a/opossum/src/position_distributions/hexapolar.rs b/opossum/src/position_distributions/hexapolar.rs index 160101ad..95e64928 100644 --- a/opossum/src/position_distributions/hexapolar.rs +++ b/opossum/src/position_distributions/hexapolar.rs @@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize}; use uom::si::f64::Length; /// Circular, hexapolar distribution -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct Hexapolar { nr_of_rings: u8, radius: Length, diff --git a/opossum/src/position_distributions/mod.rs b/opossum/src/position_distributions/mod.rs index 252044fe..097ff23d 100644 --- a/opossum/src/position_distributions/mod.rs +++ b/opossum/src/position_distributions/mod.rs @@ -43,7 +43,7 @@ pub trait PositionDistribution { } /// Enum for the different types of position distributions -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub enum PosDistType { /// Rectangular, uniform random distribution Random(random::Random), @@ -60,3 +60,18 @@ pub enum PosDistType { /// Pseudo random Sobol distribution Sobol(sobol::SobolDist), } +impl PosDistType { + /// Generate the point distribution. + #[must_use] + pub fn generate(&self) -> &dyn PositionDistribution { + match self { + Self::Random(dist) => dist, + Self::Grid(dist) => dist, + Self::HexagonalTiling(dist) => dist, + Self::Hexapolar(dist) => dist, + Self::FibonacciRectangle(dist) => dist, + Self::FibonacciEllipse(dist) => dist, + Self::Sobol(dist) => dist, + } + } +} diff --git a/opossum/src/position_distributions/random.rs b/opossum/src/position_distributions/random.rs index b88c350b..b51661eb 100644 --- a/opossum/src/position_distributions/random.rs +++ b/opossum/src/position_distributions/random.rs @@ -9,7 +9,7 @@ use serde::{Deserialize, Serialize}; use uom::si::f64::Length; /// Rectangular, uniform random distribution -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct Random { nr_of_points: usize, side_length_x: Length, diff --git a/opossum/src/position_distributions/sobol.rs b/opossum/src/position_distributions/sobol.rs index 23f966d4..bbcf8fa3 100644 --- a/opossum/src/position_distributions/sobol.rs +++ b/opossum/src/position_distributions/sobol.rs @@ -10,7 +10,7 @@ use uom::si::f64::Length; /// Rectangluar, low-discrepancy quasirandom distribution /// /// For further details see [here](https://en.wikipedia.org/wiki/Sobol_sequence) -#[derive(Clone, Debug, Serialize, Deserialize)] +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub struct SobolDist { nr_of_points: usize, side_length_x: Length, diff --git a/opossum/src/refractive_index/mod.rs b/opossum/src/refractive_index/mod.rs index e25ec90d..326934a4 100644 --- a/opossum/src/refractive_index/mod.rs +++ b/opossum/src/refractive_index/mod.rs @@ -66,7 +66,7 @@ impl From<RefractiveIndexType> for Proptype { } } /// All refractive index models must implement this trait. -pub trait RefractiveIndex: { +pub trait RefractiveIndex { /// Get the refractive index value of the current model for the given wavelength. /// /// # Errors -- GitLab From da2cbc70def4990102165a88a81d1e4bb964db4d Mon Sep 17 00:00:00 2001 From: Udo Eisenbarth <u.eisenbarth@gsi.de> Date: Fri, 28 Mar 2025 15:24:59 +0100 Subject: [PATCH 6/7] Update examples and source_helpers. --- opossum/examples/apodization.rs | 2 +- opossum/examples/fresnel_coating.rs | 12 +++++------- opossum/examples/ghost_focus.rs | 17 +++++++---------- opossum/examples/laser_system.rs | 7 ------- opossum/src/lightdata/ray_data_builder.rs | 23 +++++++++++++++++++---- opossum/src/nodes/source_helper.rs | 23 +++++++++++------------ 6 files changed, 43 insertions(+), 41 deletions(-) diff --git a/opossum/examples/apodization.rs b/opossum/examples/apodization.rs index 571f71be..9f5c810d 100644 --- a/opossum/examples/apodization.rs +++ b/opossum/examples/apodization.rs @@ -25,7 +25,7 @@ fn main() -> OpmResult<()> { RectangleConfig::new(millimeter!(15.), millimeter!(15.), millimeter!(0.0, 0.0))?; let aperture = Aperture::BinaryRectangle(rect_config); - dummy.set_aperture(&PortType::Input, "front", &aperture)?; + dummy.set_aperture(&PortType::Input, "input_1", &aperture)?; let dummy = dummy.with_decenter(millimeter!(-5.0, 5.0, 0.0))?; let i_d = scenery.add_node(dummy)?; diff --git a/opossum/examples/fresnel_coating.rs b/opossum/examples/fresnel_coating.rs index a0883b9c..19715ec3 100644 --- a/opossum/examples/fresnel_coating.rs +++ b/opossum/examples/fresnel_coating.rs @@ -10,7 +10,6 @@ use opossum::{ optic_node::OpticNode, optic_ports::PortType, position_distributions::Grid, - rays::Rays, refractive_index::RefrIndexConst, utils::geom_transformation::Isometry, OpmDocument, @@ -19,12 +18,11 @@ use std::path::Path; fn main() -> OpmResult<()> { let mut scenery = NodeGroup::new("Fresnel coating example"); - let rays = Rays::new_collimated( - nanometer!(1000.), - &UniformDist::new(joule!(1.))?, - &Grid::new((millimeter!(9.), millimeter!(9.)), (100, 100))?, - )?; - let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays)); + let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Collimated { + pos_dist: Grid::new((millimeter!(9.), millimeter!(9.)), (100, 100))?.into(), + energy_dist: UniformDist::new(joule!(1.))?.into(), + wave_length: nanometer!(1000.), + }); let mut source = Source::new("src", light_data_builder); source.set_isometry(Isometry::identity())?; let src = scenery.add_node(source)?; diff --git a/opossum/examples/ghost_focus.rs b/opossum/examples/ghost_focus.rs index 0c3bfbd8..4896e59b 100644 --- a/opossum/examples/ghost_focus.rs +++ b/opossum/examples/ghost_focus.rs @@ -12,7 +12,6 @@ use opossum::{ optic_ports::PortType, position_distributions::HexagonalTiling, radian, - rays::Rays, utils::geom_transformation::Isometry, OpmDocument, }; @@ -21,21 +20,19 @@ use std::path::Path; fn main() -> OpmResult<()> { let mut scenery = NodeGroup::default(); scenery.node_attr_mut().set_name("Folded Telescope"); - - let rays = Rays::new_collimated( - nanometer!(1000.0), - &General2DGaussian::new( + let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Collimated { + pos_dist: HexagonalTiling::new(millimeter!(15.0), 25, millimeter!(0.0, 0.))?.into(), + energy_dist: General2DGaussian::new( joule!(2.), millimeter!(0., 0.), millimeter!(8., 8.), 5., radian!(0.), false, - )?, - &HexagonalTiling::new(millimeter!(15.0), 25, millimeter!(0.0, 0.))?, - )?; - - let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays)); + )? + .into(), + wave_length: nanometer!(1000.0), + }); let mut src = Source::new("collimated ray source", light_data_builder); src.set_isometry(Isometry::identity())?; let i_src = scenery.add_node(src)?; diff --git a/opossum/examples/laser_system.rs b/opossum/examples/laser_system.rs index 6c97f96b..8d7786d9 100644 --- a/opossum/examples/laser_system.rs +++ b/opossum/examples/laser_system.rs @@ -16,13 +16,6 @@ use uom::si::f64::Length; fn main() -> OpmResult<()> { let mut scenery = NodeGroup::new("laser system"); // Main beam line - - // let source = Source::new( - // "Source", - // &LightData::Energy(DataEnergy { - // spectrum: create_he_ne_spec(1.0)?, - // }), - // ); let source = round_collimated_ray_source(millimeter!(1.0), joule!(1.0), 3)?; let i_src = scenery.add_node(source)?; let i_l1 = scenery.add_node(ParaxialSurface::new("f=100", millimeter!(100.0))?)?; diff --git a/opossum/src/lightdata/ray_data_builder.rs b/opossum/src/lightdata/ray_data_builder.rs index 46d7c082..65d80058 100644 --- a/opossum/src/lightdata/ray_data_builder.rs +++ b/opossum/src/lightdata/ray_data_builder.rs @@ -18,8 +18,15 @@ use super::LightData; pub enum RayDataBuilder { /// Raw [`Rays`] data. Raw(Rays), - /// Collimated [`Rays`] data with a given [`PosDistType`] and [`EnergyDistType`] as well as a given wavelength. - Collimated(PosDistType, EnergyDistType, Length), + /// Collimated [`Rays`] data with a given [`PosDistType`] and [`EnergyDistType`] as well as a given single wavelength. + Collimated { + /// Position distribution. + pos_dist: PosDistType, + /// Energy distribution. + energy_dist: EnergyDistType, + /// Wavelength of the rays. + wave_length: Length, + }, } impl RayDataBuilder { @@ -30,7 +37,11 @@ impl RayDataBuilder { pub fn build(self) -> OpmResult<LightData> { match self { Self::Raw(rays) => Ok(LightData::Geometric(rays)), - Self::Collimated(pos_dist, energy_dist, wave_length) => { + Self::Collimated { + pos_dist, + energy_dist, + wave_length, + } => { let rays = Rays::new_collimated(wave_length, energy_dist.generate(), pos_dist.generate())?; Ok(LightData::Geometric(rays)) @@ -43,7 +54,11 @@ impl Display for RayDataBuilder { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Raw(r) => write!(f, "Raw({r})"), - Self::Collimated(pos_dist, energy_dist, wave_length) => { + Self::Collimated { + pos_dist, + energy_dist, + wave_length, + } => { write!( f, "Collimated({pos_dist:?}, {energy_dist:?}, {wave_length:?})" diff --git a/opossum/src/nodes/source_helper.rs b/opossum/src/nodes/source_helper.rs index 72a5875e..6fb1017a 100644 --- a/opossum/src/nodes/source_helper.rs +++ b/opossum/src/nodes/source_helper.rs @@ -2,6 +2,7 @@ //! Helper functions for easier creation of `standard` ray [`Source`]s. use super::Source; use crate::{ + energy_distributions::UniformDist, error::OpmResult, lightdata::{light_data_builder::LightDataBuilder, ray_data_builder::RayDataBuilder}, nanometer, @@ -29,12 +30,11 @@ pub fn round_collimated_ray_source( energy: Energy, nr_of_rings: u8, ) -> OpmResult<Source> { - let rays = Rays::new_uniform_collimated( - nanometer!(1000.0), - energy, - &Hexapolar::new(radius, nr_of_rings)?, - )?; - let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays)); + let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Collimated { + pos_dist: Hexapolar::new(radius, nr_of_rings)?.into(), + energy_dist: UniformDist::new(energy)?.into(), + wave_length: nanometer!(1000.0), + }); let mut src = Source::new("collimated line ray source", light_data_builder); src.set_isometry(Isometry::identity())?; Ok(src) @@ -55,12 +55,11 @@ pub fn collimated_line_ray_source( energy: Energy, nr_of_points_y: usize, ) -> OpmResult<Source> { - let rays = Rays::new_uniform_collimated( - nanometer!(1000.0), - energy, - &Grid::new((Length::zero(), size_y), (1, nr_of_points_y))?, - )?; - let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays)); + let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Collimated { + pos_dist: Grid::new((Length::zero(), size_y), (1, nr_of_points_y))?.into(), + energy_dist: UniformDist::new(energy)?.into(), + wave_length: nanometer!(1000.0), + }); let mut src = Source::new("collimated line ray source", light_data_builder); src.set_isometry(Isometry::identity())?; Ok(src) -- GitLab From 452b4ee6e061eb615614f0643a2ce0aac7f6c143 Mon Sep 17 00:00:00 2001 From: Udo Eisenbarth <u.eisenbarth@gsi.de> Date: Fri, 28 Mar 2025 16:44:15 +0100 Subject: [PATCH 7/7] Serialize SpectrumDistribution for RayDataBuilder::Collimated --- opossum/Cargo.toml | 1 - opossum/examples/folded_telescope.rs | 15 ++-- opossum/examples/fresnel_coating.rs | 3 +- opossum/examples/ghost_focus.rs | 3 +- opossum/examples/hhts/hhts.rs | 75 +++---------------- opossum/examples/prism_dispersion.rs | 23 ++---- opossum/src/console.rs | 10 +-- opossum/src/lightdata/ray_data_builder.rs | 21 +++--- opossum/src/main.rs | 2 +- opossum/src/nodes/source_helper.rs | 5 +- opossum/src/spectral_distribution/gaussian.rs | 7 ++ .../src/spectral_distribution/laser_lines.rs | 74 ++++++++++++++++++ opossum/src/spectral_distribution/mod.rs | 22 ++++++ opossum/src/spectrum.rs | 6 +- 14 files changed, 155 insertions(+), 112 deletions(-) create mode 100644 opossum/src/spectral_distribution/laser_lines.rs diff --git a/opossum/Cargo.toml b/opossum/Cargo.toml index 6ec8db83..33f720c6 100644 --- a/opossum/Cargo.toml +++ b/opossum/Cargo.toml @@ -30,7 +30,6 @@ opm_macros_lib = { path = "opm_macros_lib" } petgraph = { version = "0.7", features = ["serde-1"] } # the graph library uom = {version="0.36", features = ["serde"] } serde = { version = "1", features = ['rc'] } -#serde_yaml = "0.9" ron="0.9" csv = "1" diff --git a/opossum/examples/folded_telescope.rs b/opossum/examples/folded_telescope.rs index dbce98b7..62777a11 100644 --- a/opossum/examples/folded_telescope.rs +++ b/opossum/examples/folded_telescope.rs @@ -13,7 +13,6 @@ use opossum::{ nodes::{Lens, NodeGroup, NodeReference, RayPropagationVisualizer, Source, ThinMirror}, optic_node::{Alignable, OpticNode}, position_distributions::Hexapolar, - rays::Rays, refractive_index::RefrIndexSellmeier1, spectral_distribution::Gaussian, utils::geom_transformation::Isometry, @@ -32,18 +31,18 @@ pub fn main() -> OpmResult<()> { nanometer!(300.)..nanometer!(1200.), )?; let mut scenery = NodeGroup::default(); - let rays = Rays::new_collimated_with_spectrum( - &Gaussian::new( + let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Collimated { + pos_dist: Hexapolar::new(millimeter!(10.), 10)?.into(), + energy_dist: UniformDist::new(joule!(1.))?.into(), + spect_dist: Gaussian::new( (nanometer!(1054.), nanometer!(1068.)), 1, nanometer!(1054.), nanometer!(8.), 1., - )?, - &UniformDist::new(joule!(1.))?, - &Hexapolar::new(millimeter!(10.), 10)?, - )?; - let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays)); + )? + .into(), + }); let mut src = Source::new("collimated ray source", light_data_builder); src.set_alignment_wavelength(alignment_wvl)?; src.set_isometry(Isometry::identity())?; diff --git a/opossum/examples/fresnel_coating.rs b/opossum/examples/fresnel_coating.rs index 19715ec3..9485fdda 100644 --- a/opossum/examples/fresnel_coating.rs +++ b/opossum/examples/fresnel_coating.rs @@ -11,6 +11,7 @@ use opossum::{ optic_ports::PortType, position_distributions::Grid, refractive_index::RefrIndexConst, + spectral_distribution::LaserLines, utils::geom_transformation::Isometry, OpmDocument, }; @@ -21,7 +22,7 @@ fn main() -> OpmResult<()> { let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Collimated { pos_dist: Grid::new((millimeter!(9.), millimeter!(9.)), (100, 100))?.into(), energy_dist: UniformDist::new(joule!(1.))?.into(), - wave_length: nanometer!(1000.), + spect_dist: LaserLines::new(vec![(nanometer!(1000.), 1.0)])?.into(), }); let mut source = Source::new("src", light_data_builder); source.set_isometry(Isometry::identity())?; diff --git a/opossum/examples/ghost_focus.rs b/opossum/examples/ghost_focus.rs index 4896e59b..0f9c4b87 100644 --- a/opossum/examples/ghost_focus.rs +++ b/opossum/examples/ghost_focus.rs @@ -12,6 +12,7 @@ use opossum::{ optic_ports::PortType, position_distributions::HexagonalTiling, radian, + spectral_distribution::LaserLines, utils::geom_transformation::Isometry, OpmDocument, }; @@ -31,7 +32,7 @@ fn main() -> OpmResult<()> { false, )? .into(), - wave_length: nanometer!(1000.0), + spect_dist: LaserLines::new(vec![(nanometer!(1000.0), 1.0)])?.into(), }); let mut src = Source::new("collimated ray source", light_data_builder); src.set_isometry(Isometry::identity())?; diff --git a/opossum/examples/hhts/hhts.rs b/opossum/examples/hhts/hhts.rs index 42d9f979..62407dd7 100644 --- a/opossum/examples/hhts/hhts.rs +++ b/opossum/examples/hhts/hhts.rs @@ -25,8 +25,8 @@ use opossum::{ position_distributions::HexagonalTiling, radian, ray::SplittingConfig, - rays::Rays, refractive_index::{refr_index_schott::RefrIndexSchott, RefrIndexSellmeier1}, + spectral_distribution::LaserLines, spectrum::Spectrum, spectrum_helper::generate_filter_spectrum, utils::geom_transformation::Isometry, @@ -35,17 +35,6 @@ use opossum::{ use uom::si::f64::Length; fn main() -> OpmResult<()> { - let wvl_1w = nanometer!(1054.0); - let wvl_2w = wvl_1w / 2.0; - - let energy_1w = joule!(100.0); - let energy_2w = joule!(50.0); - - // let beam_dist_1w = Hexapolar::new(millimeter!(76.05493), 10)?; - let beam_dist_1w = HexagonalTiling::new(millimeter!(100.), 10, millimeter!(0., 0.))?; - let beam_dist_2w = HexagonalTiling::new(millimeter!(100.), 10, millimeter!(1., 1.))?; - // let beam_dist_2w = beam_dist_1w.clone(); - let refr_index_hk9l = RefrIndexSellmeier1::new( 6.14555251E-1, 6.56775017E-1, @@ -75,70 +64,26 @@ fn main() -> OpmResult<()> { )?; // apertures - // let circle_config = CircleConfig::new(millimeter!(25.4), millimeter!(0., 0.))?; - // let a_2inch = Aperture::BinaryCircle(circle_config); let circle_config = CircleConfig::new(millimeter!(12.7), millimeter!(0., 0.))?; let a_1inch = Aperture::BinaryCircle(circle_config); // collimated source - - let rays_1w = Rays::new_collimated( - wvl_1w, - &General2DGaussian::new( - energy_1w, + let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Collimated { + pos_dist: HexagonalTiling::new(millimeter!(100.), 10, millimeter!(0., 0.))?.into(), + energy_dist: General2DGaussian::new( + joule!(150.0), millimeter!(0., 0.), millimeter!(60.6389113608, 60.6389113608), 5., radian!(0.), false, - )?, - &beam_dist_1w, - )?; - let mut rays_2w = Rays::new_collimated( - wvl_2w, - &General2DGaussian::new( - energy_2w, - millimeter!(0., 0.), - millimeter!(60.6389113608, 60.6389113608), - 5., - radian!(0.), - false, - )?, - &beam_dist_2w, - )?; - // let rays_1w = Rays::new_uniform_collimated(wvl_1w, energy_1w, &beam_dist_1w)?; - // let mut rays_2w = Rays::new_uniform_collimated(wvl_2w, energy_2w, &beam_dist_2w)?; - - // point source - - // let rays_1w = Rays::new_hexapolar_point_source( - // millimeter!( - // 0., - // 75.0, - // 0., - // ), - // degree!(0.183346572), - // 6, - // wvl_1w, - // energy_1w, - // )?; - // let mut rays_2w = Rays::new_hexapolar_point_source( - // millimeter!( - // 0., - // 75.0, - // 0., - // ), - // degree!(0.183346572), - // 6, - // wvl_2w, - // energy_2w, - // )?; - - let mut rays = rays_1w; - rays.add_rays(&mut rays_2w); + )? + .into(), + spect_dist: LaserLines::new(vec![(nanometer!(1053.0), 1.0), (nanometer!(527.0), 0.5)])? + .into(), + }); let mut scenery = NodeGroup::new("HHT Sensor"); - let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays)); let mut src = Source::new("Source", light_data_builder); src.set_isometry(Isometry::identity())?; let src = scenery.add_node(src)?; diff --git a/opossum/examples/prism_dispersion.rs b/opossum/examples/prism_dispersion.rs index a0e5e96e..010d9f62 100644 --- a/opossum/examples/prism_dispersion.rs +++ b/opossum/examples/prism_dispersion.rs @@ -4,6 +4,7 @@ use num::Zero; use opossum::{ analyzers::{AnalyzerType, RayTraceConfig}, degree, + energy_distributions::UniformDist, error::OpmResult, joule, lightdata::{light_data_builder::LightDataBuilder, ray_data_builder::RayDataBuilder}, @@ -11,8 +12,8 @@ use opossum::{ nodes::{NodeGroup, RayPropagationVisualizer, Source, SpotDiagram, Wedge}, optic_node::{Alignable, OpticNode}, position_distributions::Grid, - rays::Rays, refractive_index::RefrIndexSellmeier1, + spectral_distribution::LaserLines, utils::geom_transformation::Isometry, OpmDocument, }; @@ -31,22 +32,14 @@ fn main() -> OpmResult<()> { let beam_size_y = millimeter!(10.0); let nr_of_rays = 5; let wedge_angle_in_degree = 10.0; - let mut rays_1w = Rays::new_uniform_collimated( - nanometer!(1053.), - joule!(1.), - &Grid::new((Length::zero(), beam_size_y), (1, nr_of_rays))?, - )?; - - let mut rays_2w = Rays::new_uniform_collimated( - nanometer!(527.), - joule!(1.), - &Grid::new((Length::zero(), beam_size_y), (1, nr_of_rays))?, - )?; - - rays_1w.add_rays(&mut rays_2w); let mut scenery = NodeGroup::default(); - let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays_1w)); + let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Collimated { + pos_dist: Grid::new((Length::zero(), beam_size_y), (1, nr_of_rays))?.into(), + energy_dist: UniformDist::new(joule!(1.0))?.into(), + spect_dist: LaserLines::new(vec![(nanometer!(1053.0), 1.0), (nanometer!(527.0), 1.0)])? + .into(), + }); let mut light_src = Source::new("collimated ray source", light_data_builder); light_src.set_isometry(Isometry::identity())?; let src = scenery.add_node(light_src)?; diff --git a/opossum/src/console.rs b/opossum/src/console.rs index 7eeaf45a..18385dc9 100644 --- a/opossum/src/console.rs +++ b/opossum/src/console.rs @@ -253,13 +253,13 @@ mod test { let path_inexistent_file = Path::new("./files_for_testing/opm/nonexistent.opm"); let path_inexistent_dir = Path::new("./files_for_testing/this_dir_does_not_exist/empty.opm"); - let path_not_yaml = Path::new("./files_for_testing/opm/is_not_a_opm.txt"); + let path_not_opm = Path::new("./files_for_testing/opm/is_not_a_opm.txt"); let path_is_dir = Path::new("./files_for_testing/opm/"); assert_eq!(file_path_is_valid(path_valid), true); assert_eq!(file_path_is_valid(path_inexistent_file), false); assert_eq!(file_path_is_valid(path_inexistent_dir), false); - assert_eq!(file_path_is_valid(path_not_yaml), false); + assert_eq!(file_path_is_valid(path_not_opm), false); assert_eq!(file_path_is_valid(path_is_dir), false); } #[test] @@ -267,7 +267,7 @@ mod test { let path_valid = "./files_for_testing/opm/opticscenery.opm"; let path_inexistent_file = "./files_for_testing/opm/nonexistent.opm"; let path_inexistent_dir = "./files_for_testing/this_dir_does_not_exist/empty.opm"; - let path_not_yaml = "./files_for_testing/opm/is_not_an_opm.txt"; + let path_not_opm = "./files_for_testing/opm/is_not_an_opm.txt"; let path_is_dir = "./files_for_testing/opm/"; assert_eq!( @@ -276,7 +276,7 @@ mod test { ); assert_eq!(eval_file_path_input(path_inexistent_file), None); assert_eq!(eval_file_path_input(path_inexistent_dir), None); - assert_eq!(eval_file_path_input(path_not_yaml), None); + assert_eq!(eval_file_path_input(path_not_opm), None); assert_eq!(eval_file_path_input(path_is_dir), None); } #[test] @@ -295,7 +295,7 @@ mod test { } #[test] fn get_parent_dir_test() { - let path_valid = "./files_for_testing/opm/empty_yaml.yaml".to_owned(); + let path_valid = "./files_for_testing/opm/my_file.opm".to_owned(); assert_eq!( get_parent_dir(&PathBuf::from(path_valid)), PathBuf::from("./files_for_testing/opm") diff --git a/opossum/src/lightdata/ray_data_builder.rs b/opossum/src/lightdata/ray_data_builder.rs index 65d80058..f57cb47f 100644 --- a/opossum/src/lightdata/ray_data_builder.rs +++ b/opossum/src/lightdata/ray_data_builder.rs @@ -4,14 +4,12 @@ //! This builder allows easier serialization / deserialization in OPM files. use std::fmt::Display; +use super::LightData; use crate::{ energy_distributions::EnergyDistType, error::OpmResult, position_distributions::PosDistType, - rays::Rays, + rays::Rays, spectral_distribution::SpecDistType, }; use serde::{Deserialize, Serialize}; -use uom::si::f64::Length; - -use super::LightData; /// Builder for the generation of [`LightData::Geometric`]. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] @@ -25,7 +23,7 @@ pub enum RayDataBuilder { /// Energy distribution. energy_dist: EnergyDistType, /// Wavelength of the rays. - wave_length: Length, + spect_dist: SpecDistType, }, } @@ -40,10 +38,13 @@ impl RayDataBuilder { Self::Collimated { pos_dist, energy_dist, - wave_length, + spect_dist, } => { - let rays = - Rays::new_collimated(wave_length, energy_dist.generate(), pos_dist.generate())?; + let rays = Rays::new_collimated_with_spectrum( + spect_dist.generate(), + energy_dist.generate(), + pos_dist.generate(), + )?; Ok(LightData::Geometric(rays)) } } @@ -57,11 +58,11 @@ impl Display for RayDataBuilder { Self::Collimated { pos_dist, energy_dist, - wave_length, + spect_dist, } => { write!( f, - "Collimated({pos_dist:?}, {energy_dist:?}, {wave_length:?})" + "Collimated({pos_dist:?}, {energy_dist:?}, {spect_dist:?})" ) } } diff --git a/opossum/src/main.rs b/opossum/src/main.rs index 7cdc7176..f88aaca8 100644 --- a/opossum/src/main.rs +++ b/opossum/src/main.rs @@ -79,7 +79,7 @@ fn create_report_and_data_files( let mut output = create_dot_or_report_file_instance( report_directory, &format!("report_{report_number}"), - "yaml", + "ron", "analysis report", )?; write!( diff --git a/opossum/src/nodes/source_helper.rs b/opossum/src/nodes/source_helper.rs index 6fb1017a..0e585f67 100644 --- a/opossum/src/nodes/source_helper.rs +++ b/opossum/src/nodes/source_helper.rs @@ -9,6 +9,7 @@ use crate::{ optic_node::OpticNode, position_distributions::{Grid, Hexapolar}, rays::Rays, + spectral_distribution::LaserLines, utils::geom_transformation::Isometry, }; use nalgebra::Point3; @@ -33,7 +34,7 @@ pub fn round_collimated_ray_source( let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Collimated { pos_dist: Hexapolar::new(radius, nr_of_rings)?.into(), energy_dist: UniformDist::new(energy)?.into(), - wave_length: nanometer!(1000.0), + spect_dist: LaserLines::new(vec![(nanometer!(1000.0), 1.0)])?.into(), }); let mut src = Source::new("collimated line ray source", light_data_builder); src.set_isometry(Isometry::identity())?; @@ -58,7 +59,7 @@ pub fn collimated_line_ray_source( let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Collimated { pos_dist: Grid::new((Length::zero(), size_y), (1, nr_of_points_y))?.into(), energy_dist: UniformDist::new(energy)?.into(), - wave_length: nanometer!(1000.0), + spect_dist: LaserLines::new(vec![(nanometer!(1000.0), 1.0)])?.into(), }); let mut src = Source::new("collimated line ray source", light_data_builder); src.set_isometry(Isometry::identity())?; diff --git a/opossum/src/spectral_distribution/gaussian.rs b/opossum/src/spectral_distribution/gaussian.rs index 962ed775..5fcf1255 100644 --- a/opossum/src/spectral_distribution/gaussian.rs +++ b/opossum/src/spectral_distribution/gaussian.rs @@ -1,3 +1,4 @@ +use serde::{Deserialize, Serialize}; use uom::si::f64::Length; use super::SpectralDistribution; @@ -8,6 +9,7 @@ use crate::utils::math_distribution_functions::gaussian; use itertools::Itertools; use kahan::KahanSummator; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Gaussian { wvl_range: (Length, Length), num_points: usize, @@ -94,6 +96,11 @@ impl SpectralDistribution for Gaussian { .collect_vec()) } } +impl From<Gaussian> for super::SpecDistType { + fn from(g: Gaussian) -> Self { + Self::Gaussian(g) + } +} #[cfg(test)] mod test { use crate::{ diff --git a/opossum/src/spectral_distribution/laser_lines.rs b/opossum/src/spectral_distribution/laser_lines.rs new file mode 100644 index 00000000..5654173a --- /dev/null +++ b/opossum/src/spectral_distribution/laser_lines.rs @@ -0,0 +1,74 @@ +use serde::{Deserialize, Serialize}; +use uom::si::f64::Length; + +use crate::error::{OpmResult, OpossumError}; + +use super::SpectralDistribution; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +/// A struct representing a collection of laser lines with their respective wavelengths and relative intensities. +pub struct LaserLines { + lines: Vec<(Length, f64)>, +} +impl LaserLines { + /// Creates a new `LaserLines` instance with the given laser lines. + /// + /// The given intensities are normalized to sum to 1.0. + /// + /// # Arguments + /// + /// * `lines` - A vector of tuples containing the wavelength and intensity of each laser line. + /// + /// # Errors + /// + /// This function returns an error if + /// * the vector is empty, + /// * any wavelength is negative or infinite, + /// * any intensity is negative or infinite, + /// * the sum of intensities is zero. + pub fn new(lines: Vec<(Length, f64)>) -> OpmResult<Self> { + // Check if the lines are non-empty and contain valid data + if lines.is_empty() { + return Err(OpossumError::Other("Laser lines cannot be empty".into())); + } + for (wavelength, intensity) in &lines { + if !wavelength.is_normal() || wavelength.is_sign_negative() { + return Err(OpossumError::Other( + "Wavelength must be positive and finite".into(), + )); + } + if !intensity.is_normal() || intensity.is_sign_negative() { + return Err(OpossumError::Other( + "Intensity must be positive and finite".into(), + )); + } + } + // Normalize the intensities to sum to 1.0 + let sum_intensity: f64 = lines.iter().map(|(_, intensity)| *intensity).sum(); + if sum_intensity == 0.0 { + return Err(OpossumError::Other( + "Sum of intensities cannot be zero".into(), + )); + } + let lines: Vec<(Length, f64)> = lines + .into_iter() + .map(|(wavelength, intensity)| (wavelength, intensity / sum_intensity)) + .collect(); + Ok(Self { lines }) + } +} +impl SpectralDistribution for LaserLines { + /// Generates the laser lines. + /// + /// # Returns + /// + /// A vector of tuples containing the wavelength and intensity of each laser line. + fn generate(&self) -> OpmResult<Vec<(Length, f64)>> { + Ok(self.lines.clone()) + } +} +impl From<LaserLines> for super::SpecDistType { + fn from(laser_lines: LaserLines) -> Self { + Self::LaserLines(laser_lines) + } +} diff --git a/opossum/src/spectral_distribution/mod.rs b/opossum/src/spectral_distribution/mod.rs index 1b855950..10ad4323 100644 --- a/opossum/src/spectral_distribution/mod.rs +++ b/opossum/src/spectral_distribution/mod.rs @@ -1,9 +1,12 @@ //! Module for handling spectral distributions use crate::error::OpmResult; +use serde::{Deserialize, Serialize}; use uom::si::f64::Length; pub mod gaussian; +pub mod laser_lines; pub use gaussian::Gaussian; +pub use laser_lines::LaserLines; pub trait SpectralDistribution { /// Creates a Gaussian spectral distribution @@ -11,3 +14,22 @@ pub trait SpectralDistribution { /// This function only propagates errors of the contained functions fn generate(&self) -> OpmResult<Vec<(Length, f64)>>; } + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +/// Enum representing different types of spectral distributions +pub enum SpecDistType { + Gaussian(gaussian::Gaussian), + LaserLines(laser_lines::LaserLines), +} +impl SpecDistType { + /// Generates the spectral distribution + /// # Errors + /// This function only propagates errors of the contained functions + #[must_use] + pub fn generate(&self) -> &dyn SpectralDistribution { + match self { + Self::Gaussian(g) => g, + Self::LaserLines(l) => l, + } + } +} diff --git a/opossum/src/spectrum.rs b/opossum/src/spectrum.rs index bbb66adc..78d29593 100644 --- a/opossum/src/spectrum.rs +++ b/opossum/src/spectrum.rs @@ -999,9 +999,9 @@ mod test { #[test] fn serialize() { let s = prep(); - let s_yaml = ron::ser::to_string_pretty(&s, ron::ser::PrettyConfig::new().new_line("\n")); - assert!(s_yaml.is_ok()); - assert_eq!(s_yaml.unwrap(), + let s_ron = + ron::ser::to_string_pretty(&s, ron::ser::PrettyConfig::new().new_line("\n")).unwrap(); + assert_eq!(s_ron, "(\n data: [\n (1.0, 0.0),\n (1.5, 0.0),\n (2.0, 0.0),\n (2.5, 0.0),\n (3.0, 0.0),\n (3.5, 0.0),\n ],\n)".to_string()); } #[test] -- GitLab