diff --git a/opossum/examples/parabolic_mirror.rs b/opossum/examples/parabolic_mirror.rs index 3a33dc78da2950709ceea5418c2d12f2c5680a82..4b27dc0c06a8f966ccc23721d0e310424e319cfe 100644 --- a/opossum/examples/parabolic_mirror.rs +++ b/opossum/examples/parabolic_mirror.rs @@ -36,8 +36,8 @@ fn main() -> OpmResult<()> { let i_sd = scenery.add_node(&SpotDiagram::default())?; let i_wf = scenery.add_node(&WaveFront::default())?; let i_pm = scenery.add_node(&EnergyMeter::default())?; - scenery.connect_nodes(i_src, "output_1", i_m2, "input_1", millimeter!(100.0))?; - // scenery.connect_nodes(i_m1, "output_1", i_m2, "input_1", millimeter!(100.0))?; + scenery.connect_nodes(i_src, "output_1", i_m1, "input_1", millimeter!(100.0))?; + scenery.connect_nodes(i_m1, "output_1", i_m2, "input_1", millimeter!(100.0))?; scenery.connect_nodes(i_m2, "output_1", i_prop_vis, "input_1", millimeter!(80.0))?; scenery.connect_nodes(i_prop_vis, "output_1", i_sd, "input_1", millimeter!(0.1))?; scenery.connect_nodes(i_sd, "output_1", i_wf, "input_1", millimeter!(0.1))?; diff --git a/opossum/examples/parabolic_mirror_ghost_focus.rs b/opossum/examples/parabolic_mirror_ghost_focus.rs index 116c37be6f4cea7061bca50ba0bb4a55817447de..492dcdeb96feb1dcfe3f1cd295fad78e8f77456e 100644 --- a/opossum/examples/parabolic_mirror_ghost_focus.rs +++ b/opossum/examples/parabolic_mirror_ghost_focus.rs @@ -1,3 +1,4 @@ +use nalgebra::Vector2; use opossum::{ analyzers::{AnalyzerType, RayTraceConfig}, coatings::CoatingType, @@ -6,7 +7,7 @@ use opossum::{ joule, millimeter, nodes::{ round_collimated_ray_source, NodeGroup, ParabolicMirror, RayPropagationVisualizer, - SpotDiagram, ThinMirror, + ThinMirror, }, optic_node::{Alignable, OpticNode}, optic_ports::PortType, @@ -34,42 +35,17 @@ fn main() -> OpmResult<()> { "input_1", &CoatingType::ConstantR { reflectivity: 0.5 }, )?; - // let i_m2 = scenery.add_node(&mirror2.clone())?; - // let mut mirror3 = ThinMirror::new("mirror 3").with_tilt(degree!(45., 0.0, 0.0))?; - // mirror3.set_coating( - // &PortType::Input, - // "input_1", - // &CoatingType::ConstantR { reflectivity: 0.5 }, - // )?; - // let i_m3 = scenery.add_node(&mirror3.clone())?; let i_m2 = scenery.add_node( - &ParabolicMirror::new_with_off_axis_y( - "parabola 1", - millimeter!(100.0), - false, - degree!(-90.), - )?, // .with_oap_angle(degree!(90.0))?, + &ParabolicMirror::new("parabola 1", millimeter!(100.), false)? + .with_oap_angle(degree!(-90.))? + .with_oap_direction(Vector2::new(0., 1.))?, )?; let i_m3 = scenery.add_node( - &ParabolicMirror::new_with_off_axis_y( - "parabola 2", - millimeter!(100.0), - true, - degree!(90.), - )?, // .with_oap_angle(degree!(-90.0))?, + &ParabolicMirror::new("parabola 2", millimeter!(100.), true)? + .with_oap_angle(degree!(90.))? + .with_oap_direction(Vector2::new(0., 1.))?, )?; - // let mut wedge = Wedge::new( - // "wedge", - // millimeter!(20.0), - // degree!(0.5), - // &RefrIndexConst::new(1.5)?, - // )? - // .with_tilt(degree!(0.5, 0.0, 0.0))?; - // wedge.set_coating(&PortType::Input, "input_1", &CoatingType::Fresnel)?; - // wedge.set_coating(&PortType::Output, "output_1", &CoatingType::Fresnel)?; - // let i_w = scenery.add_node(&wedge)?; let i_rpv = scenery.add_node(&RayPropagationVisualizer::default())?; - let i_sd = scenery.add_node(&SpotDiagram::default())?; scenery.connect_nodes(i_src, "output_1", i_m1, "input_1", millimeter!(50.0))?; scenery.connect_nodes(i_m1, "output_1", i_m2, "input_1", millimeter!(50.0))?; diff --git a/opossum/files_for_testing/opm/optic_ref.opm b/opossum/files_for_testing/opm/optic_ref.opm index dbb0d44e8a646fe3e73075136c3ac4bcd24d4a78..d3514ce46413d6a03f23ccc7945b169d964ca5fe 100644 --- a/opossum/files_for_testing/opm/optic_ref.opm +++ b/opossum/files_for_testing/opm/optic_ref.opm @@ -7,71 +7,57 @@ attributes: lidt: 10000. ports: inputs: - front: - geo_surface: !Flat - s: - isometry: - transform: - rotation: - - 0.0 - - 0.0 - - 0.0 - - 1.0 - translation: - - 0.0 - - 0.0 - - 0.0 - inverse: - rotation: - - 0.0 - - 0.0 - - 0.0 - - 1.0 - translation: - - 0.0 - - 0.0 - - 0.0 + input_1: + anchor_point_iso: + transform: + rotation: + - 0.0 + - 0.0 + - 0.0 + - 1.0 + translation: + - 0.0 + - 0.0 + - 0.0 + inverse: + rotation: + - 0.0 + - 0.0 + - 0.0 + - 1.0 + translation: + - 0.0 + - 0.0 + - 0.0 aperture: None coating: IdealAR - lidt: 10000. - backward_rays_cache: [] - forward_rays_cache: [] - hit_map: - hit_map: [] - critical_fluence: {} + lidt: 10000.0 outputs: - rear: - geo_surface: !Flat - s: - isometry: - transform: - rotation: - - 0.0 - - 0.0 - - 0.0 - - 1.0 - translation: - - 0.0 - - 0.0 - - 0.0 - inverse: - rotation: - - 0.0 - - 0.0 - - 0.0 - - 1.0 - translation: - - 0.0 - - 0.0 - - 0.0 + output_1: + anchor_point_iso: + transform: + rotation: + - 0.0 + - 0.0 + - 0.0 + - 1.0 + translation: + - 0.0 + - 0.0 + - 0.0 + inverse: + rotation: + - 0.0 + - 0.0 + - 0.0 + - 1.0 + translation: + - 0.0 + - 0.0 + - 0.0 aperture: None coating: IdealAR - lidt: 10000. - backward_rays_cache: [] - forward_rays_cache: [] - hit_map: - hit_map: [] - critical_fluence: {} + lidt: 10000.0 inverted: false props: \ No newline at end of file diff --git a/opossum/files_for_testing/opm/opticscenery.opm b/opossum/files_for_testing/opm/opticscenery.opm index 7a3b3f0bb7110991dffe207c43feec2f9c3e3c9e..d9b99f951acdf00a2e6c17cf0ecf831f72165cc5 100644 --- a/opossum/files_for_testing/opm/opticscenery.opm +++ b/opossum/files_for_testing/opm/opticscenery.opm @@ -5,7 +5,7 @@ scenery: ports: inputs: {} outputs: {} - uuid: 7743e13b-7c3f-44fe-90f7-61e0ba6fa4d3 + uuid: e112dc7b-235d-4779-a8fe-81daec52dc85 lidt: 10000.0 props: expand view: !Bool false @@ -17,71 +17,57 @@ scenery: ports: inputs: input_1: - geo_surface: !Flat - s: - isometry: - transform: - rotation: - - 0.0 - - 0.0 - - 0.0 - - 1.0 - translation: - - 0.0 - - 0.0 - - 0.0 - inverse: - rotation: - - 0.0 - - 0.0 - - 0.0 - - 1.0 - translation: - - 0.0 - - 0.0 - - 0.0 + anchor_point_iso: + transform: + rotation: + - 0.0 + - 0.0 + - 0.0 + - 1.0 + translation: + - 0.0 + - 0.0 + - 0.0 + inverse: + rotation: + - 0.0 + - 0.0 + - 0.0 + - 1.0 + translation: + - 0.0 + - 0.0 + - 0.0 aperture: None coating: IdealAR lidt: 10000.0 - backward_rays_cache: [] - forward_rays_cache: [] - hit_map: - hit_map: [] - critical_fluence: {} outputs: output_1: - geo_surface: !Flat - s: - isometry: - transform: - rotation: - - 0.0 - - 0.0 - - 0.0 - - 1.0 - translation: - - 0.0 - - 0.0 - - 0.0 - inverse: - rotation: - - 0.0 - - 0.0 - - 0.0 - - 1.0 - translation: - - 0.0 - - 0.0 - - 0.0 + anchor_point_iso: + transform: + rotation: + - 0.0 + - 0.0 + - 0.0 + - 1.0 + translation: + - 0.0 + - 0.0 + - 0.0 + inverse: + rotation: + - 0.0 + - 0.0 + - 0.0 + - 1.0 + translation: + - 0.0 + - 0.0 + - 0.0 aperture: None coating: IdealAR lidt: 10000.0 - backward_rays_cache: [] - forward_rays_cache: [] - hit_map: - hit_map: [] - critical_fluence: {} - uuid: d8c43481-43b7-4bad-8305-7246a62e4016 + uuid: 587fa699-5e98-4d08-b5a5-f9885151f3d1 lidt: 10000.0 props: {} inverted: false @@ -91,77 +77,63 @@ scenery: ports: inputs: input_1: - geo_surface: !Flat - s: - isometry: - transform: - rotation: - - 0.0 - - 0.0 - - 0.0 - - 1.0 - translation: - - 0.0 - - 0.0 - - 0.0 - inverse: - rotation: - - 0.0 - - 0.0 - - 0.0 - - 1.0 - translation: - - 0.0 - - 0.0 - - 0.0 + anchor_point_iso: + transform: + rotation: + - 0.0 + - 0.0 + - 0.0 + - 1.0 + translation: + - 0.0 + - 0.0 + - 0.0 + inverse: + rotation: + - 0.0 + - 0.0 + - 0.0 + - 1.0 + translation: + - 0.0 + - 0.0 + - 0.0 aperture: None coating: IdealAR lidt: 10000.0 - backward_rays_cache: [] - forward_rays_cache: [] - hit_map: - hit_map: [] - critical_fluence: {} outputs: output_1: - geo_surface: !Flat - s: - isometry: - transform: - rotation: - - 0.0 - - 0.0 - - 0.0 - - 1.0 - translation: - - 0.0 - - 0.0 - - 0.0 - inverse: - rotation: - - 0.0 - - 0.0 - - 0.0 - - 1.0 - translation: - - 0.0 - - 0.0 - - 0.0 + anchor_point_iso: + transform: + rotation: + - 0.0 + - 0.0 + - 0.0 + - 1.0 + translation: + - 0.0 + - 0.0 + - 0.0 + inverse: + rotation: + - 0.0 + - 0.0 + - 0.0 + - 1.0 + translation: + - 0.0 + - 0.0 + - 0.0 aperture: None coating: IdealAR lidt: 10000.0 - backward_rays_cache: [] - forward_rays_cache: [] - hit_map: - hit_map: [] - critical_fluence: {} - uuid: e6729ff1-e14a-468d-a08f-d0b9dd131dab + uuid: a81f485c-26f7-4b3c-a6ac-4a62746f6cad lidt: 10000.0 props: {} inverted: false edges: - - - d8c43481-43b7-4bad-8305-7246a62e4016 - - e6729ff1-e14a-468d-a08f-d0b9dd131dab + - - 587fa699-5e98-4d08-b5a5-f9885151f3d1 + - a81f485c-26f7-4b3c-a6ac-4a62746f6cad - output_1 - input_1 - 0.0 diff --git a/opossum/src/analyzers/analyzable.rs b/opossum/src/analyzers/analyzable.rs index 66208fc7015dea1497f39df88c703670d674bb46..90f95148b2dde93c90db38b133293167847da19b 100644 --- a/opossum/src/analyzers/analyzable.rs +++ b/opossum/src/analyzers/analyzable.rs @@ -38,8 +38,9 @@ pub trait Analyzable: OpticNode + AnalysisEnergy + AnalysisRayTrace + AnalysisGh // } fn set_surface_iso(&mut self, port_str: &str, iso: &Isometry) -> OpmResult<()> { + let eff_iso = self.effective_surface_iso(port_str)?; if let Some(input_surf) = self.get_optic_surface_mut(port_str) { - input_surf.set_isometry(iso); + input_surf.set_isometry(&eff_iso.append(iso)); } else { return Err(OpossumError::OpticPort("No surface found.".into())); } diff --git a/opossum/src/analyzers/raytrace.rs b/opossum/src/analyzers/raytrace.rs index b175b50d0d5d991e2ecb3c100bf0224c5486b6c7..d6b602cdb442fbec62bd591990e20353e76ce3b7 100644 --- a/opossum/src/analyzers/raytrace.rs +++ b/opossum/src/analyzers/raytrace.rs @@ -13,7 +13,6 @@ use crate::{ rays::Rays, refractive_index::RefractiveIndexType, reporting::analysis_report::AnalysisReport, - utils::geom_transformation::Isometry, }; use log::{info, warn}; use serde::{Deserialize, Serialize}; @@ -103,11 +102,7 @@ pub trait AnalysisRayTrace: OpticNode { refraction_intended: bool, ) -> OpmResult<()> { let uuid = *self.node_attr().uuid(); - let Some(iso) = &self.effective_iso() else { - return Err(OpossumError::Analysis( - "surface has no isometry defined".into(), - )); - }; + let iso = &self.effective_surface_iso(optic_surf_name)?; let Some(surf) = self.get_optic_surface_mut(optic_surf_name) else { return Err(OpossumError::Analysis(format!( "Cannot find surface: \"{optic_surf_name}\" of node: \"{}\"", @@ -150,33 +145,28 @@ pub trait AnalysisRayTrace: OpticNode { analyzer_type: &AnalyzerType, ) -> OpmResult<()> { let optic_name = format!("'{}' ({})", self.name(), self.node_type()); - if let Some(iso) = self.effective_iso() { - let mut apodized = false; - if let Some(surf) = self.get_optic_surface_mut(optic_surf_name) { - surf.set_isometry(&iso); - for rays in &mut *rays_bundle { - rays.refract_on_surface(surf, None, true)?; + let iso = self.effective_surface_iso(optic_surf_name)?; + let mut apodized = false; + if let Some(surf) = self.get_optic_surface_mut(optic_surf_name) { + surf.set_isometry(&iso); + for rays in &mut *rays_bundle { + rays.refract_on_surface(surf, None, true)?; - apodized |= rays.apodize(surf.aperture(), &iso)?; - if apodized { - warn!("Rays have been apodized at input aperture of {}. Results might not be accurate.", optic_name); - } - if let AnalyzerType::GhostFocus(_) = analyzer_type { - surf.evaluate_fluence_of_ray_bundle(rays)?; - } - if let AnalyzerType::RayTrace(c) = analyzer_type { - rays.invalidate_by_threshold_energy(c.min_energy_per_ray)?; - } + apodized |= rays.apodize(surf.aperture(), &iso)?; + if apodized { + warn!("Rays have been apodized at input aperture of {}. Results might not be accurate.", optic_name); + } + if let AnalyzerType::GhostFocus(_) = analyzer_type { + surf.evaluate_fluence_of_ray_bundle(rays)?; + } + if let AnalyzerType::RayTrace(c) = analyzer_type { + rays.invalidate_by_threshold_energy(c.min_energy_per_ray)?; } - } else { - return Err(OpossumError::Analysis("no surface found".into())); } - self.set_apodization_warning(apodized); } else { - return Err(OpossumError::Analysis( - "no location for surface defined. Aborting".into(), - )); + return Err(OpossumError::Analysis("no surface found".into())); } + self.set_apodization_warning(apodized); // merge all rays if let Some(ld) = self.get_light_data_mut() { @@ -249,12 +239,7 @@ pub trait AnalysisRayTrace: OpticNode { fn get_node_attributes_ray_trace( &self, node_attr: &NodeAttr, - ) -> OpmResult<(Isometry, RefractiveIndexType, Length, Angle)> { - let Some(eff_iso) = self.effective_iso() else { - return Err(OpossumError::Analysis( - "no location for surface defined".into(), - )); - }; + ) -> OpmResult<(RefractiveIndexType, Length, Angle)> { let Ok(Proptype::RefractiveIndex(index_model)) = node_attr.get_property("refractive index") else { return Err(OpossumError::Analysis( @@ -274,7 +259,7 @@ pub trait AnalysisRayTrace: OpticNode { degree!(0.) }; - Ok((eff_iso, index_model.value.clone(), *center_thickness, angle)) + Ok((index_model.value.clone(), *center_thickness, angle)) } } // /// enum to define the mode of the raytracing analysis. diff --git a/opossum/src/nodes/beam_splitter/analysis_raytrace.rs b/opossum/src/nodes/beam_splitter/analysis_raytrace.rs index 185b359dbfc482624d48a4ad31c0897ee7567544..ea390c63d6738a576b7449a1fe074ae10f9250bc 100644 --- a/opossum/src/nodes/beam_splitter/analysis_raytrace.rs +++ b/opossum/src/nodes/beam_splitter/analysis_raytrace.rs @@ -55,12 +55,8 @@ impl AnalysisRayTrace for BeamSplitter { match input1 { LightData::Geometric(r) => { let mut rays = r.clone(); - let Some(iso) = self.effective_iso() else { - return Err(OpossumError::Analysis( - "no location for surface defined. Aborting".into(), - )); - }; - if let Some(surf) = self.get_optic_surface_mut("input1") { + let iso = self.effective_surface_iso(input_port1)?; + if let Some(surf) = self.get_optic_surface_mut(input_port1) { surf.set_isometry(&iso); rays.refract_on_surface(surf, None, refraction_intended)?; } else { diff --git a/opossum/src/nodes/beam_splitter/mod.rs b/opossum/src/nodes/beam_splitter/mod.rs index 61a928963d13393071538a4bee58dc57e3f39a0b..6f4f8855614c86f5bbd007f7eccb909e688356f5 100644 --- a/opossum/src/nodes/beam_splitter/mod.rs +++ b/opossum/src/nodes/beam_splitter/mod.rs @@ -197,11 +197,7 @@ impl BeamSplitter { match input1 { LightData::Geometric(r) => { let mut rays = r.clone(); - let Some(iso) = self.effective_iso() else { - return Err(OpossumError::Analysis( - "no location for surface defined. Aborting".into(), - )); - }; + let iso = self.effective_surface_iso("input1")?; if let Some(surf) = self.get_optic_surface_mut("input1") { surf.set_isometry(&iso); @@ -238,11 +234,7 @@ impl BeamSplitter { match input2 { LightData::Geometric(r) => { let mut rays = r.clone(); - let Some(iso) = self.effective_iso() else { - return Err(OpossumError::Analysis( - "no location for surface defined. Aborting".into(), - )); - }; + let iso = self.effective_surface_iso("input2")?; if let Some(surf) = self.get_optic_surface_mut("input2") { surf.set_isometry(&iso); @@ -276,11 +268,8 @@ impl BeamSplitter { }; in_ray1.merge(&split2); in_ray2.merge(&split1); - let Some(iso) = self.effective_iso() else { - return Err(OpossumError::Analysis( - "no location for surface defined. Aborting".into(), - )); - }; + let iso = self.effective_surface_iso("out1_trans1_refl2")?; + if let Some(aperture) = self .ports() .aperture(&PortType::Output, "out1_trans1_refl2") diff --git a/opossum/src/nodes/cylindric_lens/analysis_ghostfocus.rs b/opossum/src/nodes/cylindric_lens/analysis_ghostfocus.rs index 7b46702f6a9247bd7be4b4e40b63178952205d35..26d0725c510f7f1cdb2edb1498b4e64b41f2e49e 100644 --- a/opossum/src/nodes/cylindric_lens/analysis_ghostfocus.rs +++ b/opossum/src/nodes/cylindric_lens/analysis_ghostfocus.rs @@ -21,18 +21,17 @@ impl AnalysisGhostFocus for CylindricLens { _ray_collection: &mut Vec<Rays>, _bounce_lvl: usize, ) -> OpmResult<LightRays> { - let (eff_iso, refri, center_thickness, _) = - self.get_node_attributes_ray_trace(&self.node_attr)?; + let (refri, center_thickness, _) = self.get_node_attributes_ray_trace(&self.node_attr)?; let in_port = &self.ports().names(&PortType::Input)[0]; let out_port = &self.ports().names(&PortType::Output)[0]; let thickness_iso = Isometry::new_along_z(center_thickness)?; if self.inverted() { - self.set_surface_iso(out_port, &eff_iso)?; - self.set_surface_iso(in_port, &eff_iso.append(&thickness_iso))?; + self.set_surface_iso(out_port, &Isometry::identity())?; + self.set_surface_iso(in_port, &thickness_iso)?; } else { - self.set_surface_iso(in_port, &eff_iso)?; - self.set_surface_iso(out_port, &eff_iso.append(&thickness_iso))?; + self.set_surface_iso(in_port, &Isometry::identity())?; + self.set_surface_iso(out_port, &thickness_iso)?; }; let mut rays_bundle = incoming_data diff --git a/opossum/src/nodes/cylindric_lens/analysis_raytrace.rs b/opossum/src/nodes/cylindric_lens/analysis_raytrace.rs index a6f374c388c509f0307dd73812fc7baf095720e8..cd9c5f80b46a5b0074ce39b37b030dfda0e034d5 100644 --- a/opossum/src/nodes/cylindric_lens/analysis_raytrace.rs +++ b/opossum/src/nodes/cylindric_lens/analysis_raytrace.rs @@ -28,16 +28,15 @@ impl AnalysisRayTrace for CylindricLens { )); }; - let (eff_iso, refri, center_thickness, _) = - self.get_node_attributes_ray_trace(&self.node_attr)?; + let (refri, center_thickness, _) = self.get_node_attributes_ray_trace(&self.node_attr)?; let thickness_iso = Isometry::new_along_z(center_thickness)?; if self.inverted() { - self.set_surface_iso(out_port, &eff_iso)?; - self.set_surface_iso(in_port, &eff_iso.append(&thickness_iso))?; + self.set_surface_iso(out_port, &Isometry::identity())?; + self.set_surface_iso(in_port, &thickness_iso)?; } else { - self.set_surface_iso(in_port, &eff_iso)?; - self.set_surface_iso(out_port, &eff_iso.append(&thickness_iso))?; + self.set_surface_iso(in_port, &Isometry::identity())?; + self.set_surface_iso(out_port, &thickness_iso)?; }; let mut rays_bundle = vec![rays]; diff --git a/opossum/src/nodes/dummy.rs b/opossum/src/nodes/dummy.rs index 26847b0cf48f4ea24a78b255afcba0eae90d1176..e81d65a3c96dce3ac753c6225cffe4ef6f377cea 100644 --- a/opossum/src/nodes/dummy.rs +++ b/opossum/src/nodes/dummy.rs @@ -86,11 +86,8 @@ impl AnalysisRayTrace for Dummy { }; if let LightData::Geometric(rays) = data { let mut rays = rays.clone(); - let Some(iso) = self.effective_iso() else { - return Err(OpossumError::Analysis( - "no location for surface defined. Aborting".into(), - )); - }; + let iso = self.effective_surface_iso(in_port)?; + let refraction_intended = true; if let Some(surf) = self.get_optic_surface_mut(in_port) { surf.set_isometry(&iso); diff --git a/opossum/src/nodes/ideal_filter.rs b/opossum/src/nodes/ideal_filter.rs index c3be6efede7335ea0eddcf4e4075222ff4da690e..6e8dc901303e114de22cef8f21953270f1ba4165 100644 --- a/opossum/src/nodes/ideal_filter.rs +++ b/opossum/src/nodes/ideal_filter.rs @@ -210,11 +210,7 @@ impl AnalysisRayTrace for IdealFilter { )); }; let mut rays = r.clone(); - let Some(iso) = self.effective_iso() else { - return Err(OpossumError::Analysis( - "no location for surface defined. Aborting".into(), - )); - }; + let iso = self.effective_surface_iso(in_port)?; let Some(surf) = self.get_optic_surface_mut(in_port) else { return Err(OpossumError::Analysis("no surface found. Aborting".into())); }; diff --git a/opossum/src/nodes/lens/analysis_ghostfocus.rs b/opossum/src/nodes/lens/analysis_ghostfocus.rs index c6c4e8fd1a4b3759363c0e4ad7dfeec6ecf93868..e1e920e5e5c7c339720175eaf76d3578fa7b07c7 100644 --- a/opossum/src/nodes/lens/analysis_ghostfocus.rs +++ b/opossum/src/nodes/lens/analysis_ghostfocus.rs @@ -20,8 +20,7 @@ impl AnalysisGhostFocus for Lens { _ray_collection: &mut Vec<Rays>, _bounce_lvl: usize, ) -> OpmResult<LightRays> { - let (eff_iso, refri, center_thickness, _) = - self.get_node_attributes_ray_trace(&self.node_attr)?; + let (refri, center_thickness, _) = self.get_node_attributes_ray_trace(&self.node_attr)?; let in_port = &self.ports().names(&PortType::Input)[0]; let out_port = &self.ports().names(&PortType::Output)[0]; let mut rays_bundle = incoming_data @@ -29,13 +28,12 @@ impl AnalysisGhostFocus for Lens { .map_or_else(Vec::<Rays>::new, std::clone::Clone::clone); let thickness_iso: Isometry = Isometry::new_along_z(center_thickness)?; - if self.inverted() { - self.set_surface_iso(out_port, &eff_iso)?; - self.set_surface_iso(in_port, &eff_iso.append(&thickness_iso))?; + self.set_surface_iso(out_port, &Isometry::identity())?; + self.set_surface_iso(in_port, &thickness_iso)?; } else { - self.set_surface_iso(in_port, &eff_iso)?; - self.set_surface_iso(out_port, &eff_iso.append(&thickness_iso))?; + self.set_surface_iso(in_port, &Isometry::identity())?; + self.set_surface_iso(out_port, &thickness_iso)?; }; let refraction_intended = true; diff --git a/opossum/src/nodes/lens/analysis_raytrace.rs b/opossum/src/nodes/lens/analysis_raytrace.rs index f4fb12cb4f1dd1872a3246759cd5651d5264eef4..25bf5df83fc20da0c279099e0ab9b182d83da7c1 100644 --- a/opossum/src/nodes/lens/analysis_raytrace.rs +++ b/opossum/src/nodes/lens/analysis_raytrace.rs @@ -27,16 +27,15 @@ impl AnalysisRayTrace for Lens { )); }; - let (eff_iso, refri, center_thickness, _) = - self.get_node_attributes_ray_trace(&self.node_attr)?; + let (refri, center_thickness, _) = self.get_node_attributes_ray_trace(&self.node_attr)?; let thickness_iso = Isometry::new_along_z(center_thickness)?; if self.inverted() { - self.set_surface_iso(out_port, &eff_iso)?; - self.set_surface_iso(in_port, &eff_iso.append(&thickness_iso))?; + self.set_surface_iso(out_port, &Isometry::identity())?; + self.set_surface_iso(in_port, &thickness_iso)?; } else { - self.set_surface_iso(in_port, &eff_iso)?; - self.set_surface_iso(out_port, &eff_iso.append(&thickness_iso))?; + self.set_surface_iso(in_port, &Isometry::identity())?; + self.set_surface_iso(out_port, &thickness_iso)?; }; let mut rays_bundle = vec![rays]; diff --git a/opossum/src/nodes/node_attr.rs b/opossum/src/nodes/node_attr.rs index bf6cb2ed1cb810a0bbaa8abe73d4e386002d1ad2..d15fa6181052478ab476894833caa672e3275fb7 100644 --- a/opossum/src/nodes/node_attr.rs +++ b/opossum/src/nodes/node_attr.rs @@ -144,6 +144,7 @@ impl NodeAttr { pub fn get_property(&self, name: &str) -> OpmResult<&Proptype> { self.props.get(name) } + /// Return the value of a boolean property. /// /// # Errors diff --git a/opossum/src/nodes/parabolic_mirror.rs b/opossum/src/nodes/parabolic_mirror.rs index 39bebf4bc0886c664dce7637173f1611121a4feb..f81f43e51cd4fda01e5e364775552f29b3a650df 100644 --- a/opossum/src/nodes/parabolic_mirror.rs +++ b/opossum/src/nodes/parabolic_mirror.rs @@ -118,7 +118,7 @@ impl ParabolicMirror { } let mut parabola = Self::default(); parabola.node_attr.set_name(name); - parabola.set_properties(focal_length, collimating, None, None)?; + parabola.set_parabola_properties(focal_length, collimating, None, None)?; parabola.update_surfaces()?; Ok(parabola) } @@ -147,11 +147,11 @@ impl ParabolicMirror { let mut parabola = Self::default(); parabola.node_attr.set_name(name); - parabola.set_properties( + parabola.set_parabola_properties( focal_length, collimating, - Some(oa_angle), - Some(Vector2::new(1., 0.)), + Some(&oa_angle), + Some(&Vector2::new(1., 0.)), )?; parabola.update_surfaces()?; Ok(parabola) @@ -181,11 +181,11 @@ impl ParabolicMirror { let mut parabola = Self::default(); parabola.node_attr.set_name(name); - parabola.set_properties( + parabola.set_parabola_properties( focal_length, collimating, - Some(oa_angle), - Some(Vector2::new(0., 1.)), + Some(&oa_angle), + Some(&Vector2::new(0., 1.)), )?; parabola.update_surfaces()?; @@ -219,7 +219,12 @@ impl ParabolicMirror { let mut parabola = Self::default(); parabola.node_attr.set_name(name); - parabola.set_properties(focal_length, collimating, Some(oa_angle), Some(oa_dir))?; + parabola.set_parabola_properties( + focal_length, + collimating, + Some(&oa_angle), + Some(&oa_dir), + )?; parabola.update_surfaces()?; Ok(parabola) @@ -240,7 +245,7 @@ impl ParabolicMirror { if !oa_angle.is_finite() { return Err(OpossumError::Other("off-axis angle and finite".into())); } - if oa_angle.value.abs() > f64::consts::PI { + if oa_angle.value.abs() >= f64::consts::PI { return Err(OpossumError::Other( "off-axis angle must be smaller than 180°".into(), )); @@ -257,24 +262,28 @@ impl ParabolicMirror { } /// sets the properties of this parabola - fn set_properties( + fn set_parabola_properties( &mut self, focal_length: Length, collimating: bool, - oa_angle_opt: Option<Angle>, - oa_dir_opt: Option<Vector2<f64>>, + oa_angle_opt: Option<&Angle>, + oa_dir_opt: Option<&Vector2<f64>>, ) -> OpmResult<()> { + Self::check_attributes(focal_length, oa_angle_opt, oa_dir_opt)?; + self.node_attr .set_property("focal length", focal_length.into())?; self.node_attr .set_property("collimating", collimating.into())?; if let Some(oa_angle) = oa_angle_opt { - self.node_attr.set_property("oa angle", oa_angle.into())?; + self.node_attr + .set_property("oa angle", (*oa_angle).into())?; } if let Some(oa_dir) = oa_dir_opt { - self.node_attr.set_property("oa direction", oa_dir.into())?; + self.node_attr + .set_property("oa direction", oa_dir.normalize().into())?; } Ok(()) } @@ -339,7 +348,7 @@ impl ParabolicMirror { /// /// This function will return an error if the node properties cannot be set. pub fn with_oap_direction(mut self, oa_dir: Vector2<f64>) -> OpmResult<Self> { - self.set_property("oa direction", oa_dir.into())?; + self.set_property("oa direction", oa_dir.normalize().into())?; self.update_surfaces()?; Ok(self) } @@ -384,29 +393,36 @@ impl OpticNode for ParabolicMirror { } fn update_surfaces(&mut self) -> OpmResult<()> { let iso = self.calc_off_axis_isometry()?; - let parabola = Parabola::new(-1. * self.calc_parent_focal_length()?, &iso)?; + let parabola = Parabola::new( + -1. * self.calc_parent_focal_length()?, + &Isometry::identity(), + )?; let para_geo_surface = GeoSurfaceRef(Rc::new(RefCell::new(parabola))); if let Some(optic_surf) = self .ports_mut() .get_optic_surface_mut(&"input_1".to_string()) { optic_surf.set_geo_surface(para_geo_surface.clone()); + optic_surf.set_anchor_point_iso(iso.clone()); } else { - let mut optic_surf_rear = OpticSurface::default(); - optic_surf_rear.set_geo_surface(para_geo_surface.clone()); + let mut optic_surf = OpticSurface::default(); + optic_surf.set_geo_surface(para_geo_surface.clone()); + optic_surf.set_anchor_point_iso(iso.clone()); self.ports_mut() - .add_optic_surface(&PortType::Input, "input_1", optic_surf_rear)?; + .add_optic_surface(&PortType::Input, "input_1", optic_surf)?; } if let Some(optic_surf) = self .ports_mut() .get_optic_surface_mut(&"output_1".to_string()) { optic_surf.set_geo_surface(para_geo_surface); + optic_surf.set_anchor_point_iso(iso); } else { - let mut optic_surf_rear = OpticSurface::default(); - optic_surf_rear.set_geo_surface(para_geo_surface); + let mut optic_surf = OpticSurface::default(); + optic_surf.set_geo_surface(para_geo_surface); + optic_surf.set_anchor_point_iso(iso); self.ports_mut() - .add_optic_surface(&PortType::Output, "output_1", optic_surf_rear)?; + .add_optic_surface(&PortType::Output, "output_1", optic_surf)?; } Ok(()) } @@ -419,15 +435,15 @@ impl Dottable for ParabolicMirror { } impl LIDT for ParabolicMirror {} impl Analyzable for ParabolicMirror { - fn set_surface_iso(&mut self, port_str: &str, iso: &Isometry) -> OpmResult<()> { - let parabola_iso = self.calc_off_axis_isometry()?; - if let Some(input_surf) = self.get_optic_surface_mut(port_str) { - input_surf.set_isometry(&iso.append(¶bola_iso)); - } else { - return Err(OpossumError::OpticPort("No surface found.".into())); - } - Ok(()) - } + // fn set_surface_iso(&mut self, port_str: &str, iso: &Isometry) -> OpmResult<()> { + // let parabola_iso = self.calc_and_set_off_axis_isometry()?; + // if let Some(input_surf) = self.get_optic_surface_mut(port_str) { + // input_surf.set_isometry(&iso.append(¶bola_iso)); + // } else { + // return Err(OpossumError::OpticPort("No surface found.".into())); + // } + // Ok(()) + // } } impl AnalysisGhostFocus for ParabolicMirror { fn analyze( @@ -487,40 +503,38 @@ impl AnalysisRayTrace for ParabolicMirror { ) -> OpmResult<LightResult> { let in_port = &self.ports().names(&PortType::Input)[0]; let out_port = &self.ports().names(&PortType::Output)[0]; + let Some(data) = incoming_data.get(in_port) else { return Ok(LightResult::default()); }; - if let LightData::Geometric(mut rays) = data.clone() { - let reflected = if let Some(iso) = self.effective_iso() { - self.set_surface_iso(in_port, &iso)?; - if let Some(surf) = self.get_optic_surface_mut(in_port) { - let refraction_intended = false; - let mut reflected_rays = - rays.refract_on_surface(surf, None, refraction_intended)?; - if let Some(aperture) = self.ports().aperture(&PortType::Input, in_port) { - reflected_rays.apodize(aperture, &iso)?; - reflected_rays - .invalidate_by_threshold_energy(config.min_energy_per_ray())?; - reflected_rays - } else { - return Err(OpossumError::OpticPort("input aperture not found".into())); - } - } else { - return Err(OpossumError::Analysis("no surface found. Aborting".into())); - } - } else { - return Err(OpossumError::Analysis( - "no location for surface defined. Aborting".into(), - )); - }; - let light_data = LightData::Geometric(reflected); - let light_result = LightResult::from([(out_port.into(), light_data)]); - Ok(light_result) - } else { - Err(OpossumError::Analysis( + let LightData::Geometric(mut rays) = data.clone() else { + return Err(OpossumError::Analysis( "expected ray data at input port".into(), - )) + )); + }; + if rays.is_empty() { + return Ok(LightResult::default()); + }; + + let iso = self.effective_surface_iso(in_port)?; + self.set_surface_iso(in_port, &Isometry::identity())?; + + let Some(surf) = self.get_optic_surface_mut(in_port) else { + return Err(OpossumError::Analysis("no surface found. Aborting".into())); + }; + + let refraction_intended = false; + let mut reflected_rays = rays.refract_on_surface(surf, None, refraction_intended)?; + if let Some(aperture) = self.ports().aperture(&PortType::Input, in_port) { + reflected_rays.apodize(aperture, &iso)?; + reflected_rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?; + } else { + return Err(OpossumError::OpticPort("input aperture not found".into())); } + + let light_data = LightData::Geometric(reflected_rays); + let light_result = LightResult::from([(out_port.into(), light_data)]); + Ok(light_result) } fn calc_node_position( @@ -531,3 +545,626 @@ impl AnalysisRayTrace for ParabolicMirror { AnalysisRayTrace::analyze(self, incoming_data, config) } } + +#[cfg(test)] +mod test { + use crate::{ + analyzers::{ + energy::AnalysisEnergy, ghostfocus::AnalysisGhostFocus, raytrace::AnalysisRayTrace, + GhostFocusConfig, RayTraceConfig, + }, + degree, joule, + light_result::{light_result_to_light_rays, LightResult}, + lightdata::{DataEnergy, LightData}, + meter, millimeter, nanometer, + nodes::ParabolicMirror, + optic_node::OpticNode, + position_distributions::Hexapolar, + properties::Proptype, + rays::Rays, + spectrum_helper::create_he_ne_spec, + utils::geom_transformation::Isometry, + }; + use approx::assert_relative_eq; + use core::f64; + use nalgebra::{Matrix4, Vector2}; + #[test] + fn default() { + let parabola = ParabolicMirror::default(); + assert_eq!(parabola.node_attr.name().as_str(), "parabolic mirror"); + + let Proptype::Length(focal_length) = + parabola.node_attr.get_property("focal length").unwrap() + else { + panic!() + }; + assert_relative_eq!(focal_length.value, 1.); + let Proptype::Bool(collimate) = parabola.node_attr.get_property("collimating").unwrap() + else { + panic!() + }; + assert!(!collimate); + + let Proptype::Angle(angle) = parabola.node_attr.get_property("oa angle").unwrap() else { + panic!() + }; + assert_relative_eq!(angle.value, 0.); + let Proptype::Vec2(dir) = parabola.node_attr.get_property("oa direction").unwrap() else { + panic!() + }; + assert_relative_eq!(*dir, Vector2::new(1., 0.)); + } + #[test] + fn new() { + assert!(ParabolicMirror::new("Parabola", meter!(1.), true).is_ok()); + assert!(ParabolicMirror::new("Parabola", meter!(-1.), true).is_ok()); + assert!(ParabolicMirror::new("Parabola", meter!(-1.), false).is_ok()); + assert!(ParabolicMirror::new("Parabola", meter!(1.), false).is_ok()); + + assert!(ParabolicMirror::new("Parabola", meter!(0.), false).is_err()); + assert!(ParabolicMirror::new("Parabola", meter!(f64::NAN), false).is_err()); + assert!(ParabolicMirror::new("Parabola", meter!(f64::INFINITY), false).is_err()); + assert!(ParabolicMirror::new("Parabola", meter!(f64::NEG_INFINITY), false).is_err()); + } + #[test] + fn name() { + let p = ParabolicMirror::new("Parabola", meter!(1.), true).unwrap(); + assert_eq!(p.node_attr.name().as_str(), "Parabola"); + } + #[test] + fn new_with_off_axis_x() { + assert!( + ParabolicMirror::new_with_off_axis_x("Parabola", meter!(1.), true, degree!(45.)) + .is_ok() + ); + assert!( + ParabolicMirror::new_with_off_axis_x("Parabola", meter!(1.), true, degree!(-45.)) + .is_ok() + ); + assert!( + ParabolicMirror::new_with_off_axis_x("Parabola", meter!(1.), true, degree!(0.)).is_ok() + ); + assert!( + ParabolicMirror::new_with_off_axis_x("Parabola", meter!(1.), true, degree!(180.)) + .is_err() + ); + assert!( + ParabolicMirror::new_with_off_axis_x("Parabola", meter!(1.), true, degree!(190.)) + .is_err() + ); + assert!( + ParabolicMirror::new_with_off_axis_x("Parabola", meter!(1.), true, degree!(-180.)) + .is_err() + ); + assert!( + ParabolicMirror::new_with_off_axis_x("Parabola", meter!(1.), true, degree!(-190.)) + .is_err() + ); + assert!(ParabolicMirror::new_with_off_axis_x( + "Parabola", + meter!(1.), + true, + degree!(f64::NAN) + ) + .is_err()); + assert!(ParabolicMirror::new_with_off_axis_x( + "Parabola", + meter!(1.), + true, + degree!(f64::INFINITY) + ) + .is_err()); + assert!(ParabolicMirror::new_with_off_axis_x( + "Parabola", + meter!(1.), + true, + degree!(f64::NEG_INFINITY) + ) + .is_err()); + } + #[test] + fn new_with_off_axis_y() { + assert!( + ParabolicMirror::new_with_off_axis_y("Parabola", meter!(1.), true, degree!(45.)) + .is_ok() + ); + assert!( + ParabolicMirror::new_with_off_axis_y("Parabola", meter!(1.), true, degree!(-45.)) + .is_ok() + ); + assert!( + ParabolicMirror::new_with_off_axis_y("Parabola", meter!(1.), true, degree!(0.)).is_ok() + ); + assert!( + ParabolicMirror::new_with_off_axis_y("Parabola", meter!(1.), true, degree!(180.)) + .is_err() + ); + assert!( + ParabolicMirror::new_with_off_axis_y("Parabola", meter!(1.), true, degree!(190.)) + .is_err() + ); + assert!( + ParabolicMirror::new_with_off_axis_y("Parabola", meter!(1.), true, degree!(-180.)) + .is_err() + ); + assert!( + ParabolicMirror::new_with_off_axis_y("Parabola", meter!(1.), true, degree!(-190.)) + .is_err() + ); + assert!(ParabolicMirror::new_with_off_axis_y( + "Parabola", + meter!(1.), + true, + degree!(f64::NAN) + ) + .is_err()); + assert!(ParabolicMirror::new_with_off_axis_y( + "Parabola", + meter!(1.), + true, + degree!(f64::INFINITY) + ) + .is_err()); + assert!(ParabolicMirror::new_with_off_axis_y( + "Parabola", + meter!(1.), + true, + degree!(f64::NEG_INFINITY) + ) + .is_err()); + } + #[test] + fn new_with_off_axis() { + assert!(ParabolicMirror::new_with_off_axis( + "Parabola", + meter!(1.), + true, + degree!(45.), + Vector2::new(1., 0.) + ) + .is_ok()); + assert!(ParabolicMirror::new_with_off_axis( + "Parabola", + meter!(1.), + true, + degree!(45.), + Vector2::new(-1., 0.) + ) + .is_ok()); + assert!(ParabolicMirror::new_with_off_axis( + "Parabola", + meter!(1.), + true, + degree!(45.), + Vector2::new(0., 1.) + ) + .is_ok()); + assert!(ParabolicMirror::new_with_off_axis( + "Parabola", + meter!(1.), + true, + degree!(45.), + Vector2::new(0., -1.) + ) + .is_ok()); + assert!(ParabolicMirror::new_with_off_axis( + "Parabola", + meter!(1.), + true, + degree!(45.), + Vector2::new(-1., -1.) + ) + .is_ok()); + assert!(ParabolicMirror::new_with_off_axis( + "Parabola", + meter!(1.), + true, + degree!(45.), + Vector2::new(1., -1.) + ) + .is_ok()); + assert!(ParabolicMirror::new_with_off_axis( + "Parabola", + meter!(1.), + true, + degree!(45.), + Vector2::new(1., 1.) + ) + .is_ok()); + assert!(ParabolicMirror::new_with_off_axis( + "Parabola", + meter!(1.), + true, + degree!(45.), + Vector2::new(-1., 1.) + ) + .is_ok()); + assert!(ParabolicMirror::new_with_off_axis( + "Parabola", + meter!(1.), + true, + degree!(45.), + Vector2::new(0., 0.) + ) + .is_err()); + assert!(ParabolicMirror::new_with_off_axis( + "Parabola", + meter!(1.), + true, + degree!(45.), + Vector2::new(f64::NAN, 0.) + ) + .is_err()); + assert!(ParabolicMirror::new_with_off_axis( + "Parabola", + meter!(1.), + true, + degree!(45.), + Vector2::new(f64::INFINITY, 0.) + ) + .is_err()); + assert!(ParabolicMirror::new_with_off_axis( + "Parabola", + meter!(1.), + true, + degree!(45.), + Vector2::new(f64::NEG_INFINITY, 0.) + ) + .is_err()); + assert!(ParabolicMirror::new_with_off_axis( + "Parabola", + meter!(1.), + true, + degree!(45.), + Vector2::new(0., f64::NAN) + ) + .is_err()); + assert!(ParabolicMirror::new_with_off_axis( + "Parabola", + meter!(1.), + true, + degree!(45.), + Vector2::new(0., f64::INFINITY) + ) + .is_err()); + assert!(ParabolicMirror::new_with_off_axis( + "Parabola", + meter!(1.), + true, + degree!(45.), + Vector2::new(0., f64::NEG_INFINITY) + ) + .is_err()); + } + #[test] + fn set_parabola_properties() { + let mut parabola = ParabolicMirror::default(); + + assert!(parabola + .set_parabola_properties(meter!(10.), true, None, None) + .is_ok()); + let Proptype::Length(focal_length) = + parabola.node_attr.get_property("focal length").unwrap() + else { + panic!() + }; + assert_relative_eq!(focal_length.value, 10.); + let Proptype::Bool(collimate) = parabola.node_attr.get_property("collimating").unwrap() + else { + panic!() + }; + assert!(collimate); + + let Proptype::Angle(angle) = parabola.node_attr.get_property("oa angle").unwrap() else { + panic!() + }; + assert_relative_eq!(angle.value, 0.); + let Proptype::Vec2(dir) = parabola.node_attr.get_property("oa direction").unwrap() else { + panic!() + }; + assert_relative_eq!(*dir, Vector2::new(1., 0.)); + + assert!(parabola + .set_parabola_properties( + meter!(10.), + true, + Some(°ree!(45.)), + Some(&Vector2::new(3., 2.)) + ) + .is_ok()); + + let Proptype::Angle(angle) = parabola.node_attr.get_property("oa angle").unwrap() else { + panic!() + }; + assert_relative_eq!(angle.value, degree!(45.).value); + let Proptype::Vec2(dir) = parabola.node_attr.get_property("oa direction").unwrap() else { + panic!() + }; + assert_relative_eq!(*dir, Vector2::new(3., 2.).normalize()); + } + #[test] + fn calc_off_axis_isometry() { + let parabola = ParabolicMirror::new_with_off_axis( + "Parabola", + meter!(1.), + true, + degree!(45.), + Vector2::new(0., 1.), + ) + .unwrap(); + let transform_mat = Matrix4::from_vec(vec![ + -0., + 1., + -0., + -0., + -0.7071067811865475, + -0., + -0.7071067811865476, + -0.6035533905932736, + -0.7071067811865476, + 0., + 0.7071067811865475, + -0.3964466094067264, + 0., + 0., + 0., + 1., + ]) + .transpose(); + assert_relative_eq!( + transform_mat, + parabola + .calc_off_axis_isometry() + .unwrap() + .get_transform() + .to_matrix() + ); + } + + #[test] + fn calc_parent_focal_length() { + let parabola = ParabolicMirror::new_with_off_axis( + "Parabola", + meter!(1.), + true, + degree!(90.), + Vector2::new(0., 1.), + ) + .unwrap(); + assert_relative_eq!(parabola.calc_parent_focal_length().unwrap().value, 0.5); + } + + #[test] + fn with_oap_angle() { + let parabola = ParabolicMirror::default() + .with_oap_angle(degree!(45.)) + .unwrap(); + let Proptype::Angle(angle) = parabola.node_attr.get_property("oa angle").unwrap() else { + panic!() + }; + assert_relative_eq!(angle.value, 45. / 180. * f64::consts::PI); + } + #[test] + fn with_oap_direction() { + let parabola = ParabolicMirror::default() + .with_oap_direction(Vector2::new(0.35, 8.35)) + .unwrap(); + let Proptype::Vec2(oa_dir) = parabola.node_attr.get_property("oa direction").unwrap() + else { + panic!() + }; + assert_relative_eq!(*oa_dir, Vector2::new(0.35, 8.35).normalize()); + } + #[test] + fn analysis_raytrace_empty_input() { + let mut node = ParabolicMirror::default(); + let light_data = LightData::Geometric(Rays::default()); + let input = LightResult::from([("input_1".into(), light_data)]); + let output = + AnalysisRayTrace::analyze(&mut node, input, &RayTraceConfig::default()).unwrap(); + assert!(output.is_empty()); + } + + #[test] + fn analysis_raytrace_no_input() { + let mut node = ParabolicMirror::default(); + let light_data = LightData::Fourier; + let input = LightResult::from([("output_1".into(), light_data)]); + let output = + AnalysisRayTrace::analyze(&mut node, input, &RayTraceConfig::default()).unwrap(); + assert!(output.is_empty()); + } + + #[test] + fn analysis_raytrace_lightdata_energy() { + let mut node = ParabolicMirror::default(); + let light_data = LightData::Energy(DataEnergy { + spectrum: create_he_ne_spec(1.0).unwrap(), + }); + let input = LightResult::from([("input_1".into(), light_data)]); + assert!(AnalysisRayTrace::analyze(&mut node, input, &RayTraceConfig::default()).is_err()); + } + #[test] + fn analysis_raytrace_lightdata_ghost_focus() { + let mut node = ParabolicMirror::default(); + let light_data = LightData::GhostFocus(vec![Rays::default()]); + let input = LightResult::from([("input_1".into(), light_data)]); + assert!(AnalysisRayTrace::analyze(&mut node, input, &RayTraceConfig::default()).is_err()); + } + + #[test] + fn analysis_raytrace_lightdata_fourier() { + let mut node = ParabolicMirror::default(); + let light_data = LightData::Fourier; + let input = LightResult::from([("input_1".into(), light_data)]); + assert!(AnalysisRayTrace::analyze(&mut node, input, &RayTraceConfig::default()).is_err()); + } + + #[test] + fn analysis_raytrace_no_iso() { + let mut node = ParabolicMirror::default(); + let rays = Rays::new_uniform_collimated( + nanometer!(1000.), + joule!(1.), + &Hexapolar::new(millimeter!(1.), 3).unwrap(), + ) + .unwrap(); + let light_data = LightData::Geometric(rays); + let input = LightResult::from([("input_1".into(), light_data)]); + assert!(AnalysisRayTrace::analyze(&mut node, input, &RayTraceConfig::default()).is_err()); + } + #[test] + fn analysis_raytrace() { + let mut node = ParabolicMirror::default(); + node.set_isometry(Isometry::identity()); + let rays = Rays::new_uniform_collimated( + nanometer!(1000.), + joule!(1.), + &Hexapolar::new(millimeter!(1.), 3).unwrap(), + ) + .unwrap(); + let light_data = LightData::Geometric(rays); + let input = LightResult::from([("input_1".into(), light_data)]); + assert!(AnalysisRayTrace::analyze(&mut node, input, &RayTraceConfig::default()).is_ok()); + } + + #[test] + fn analysis_energy() { + let mut node = ParabolicMirror::default(); + let light_data = LightData::Energy(DataEnergy { + spectrum: create_he_ne_spec(1.0).unwrap(), + }); + let input = LightResult::from([("input_1".into(), light_data)]); + let output = AnalysisEnergy::analyze(&mut node, input); + assert!(output.is_ok()); + assert!(!output.unwrap().is_empty()); + } + + #[test] + fn analysis_energy_no_input() { + let mut node = ParabolicMirror::default(); + let light_data = LightData::Fourier; + let input = LightResult::from([("output_1".into(), light_data)]); + let output = AnalysisEnergy::analyze(&mut node, input).unwrap(); + assert!(output.is_empty()); + } + + #[test] + fn analysis_energy_lightdata_raytrace() { + let mut node = ParabolicMirror::default(); + let light_data = LightData::Geometric(Rays::default()); + let input = LightResult::from([("input_1".into(), light_data)]); + assert!(AnalysisEnergy::analyze(&mut node, input).is_ok()); + } + #[test] + fn analysis_energy_lightdata_ghost_focus() { + let mut node = ParabolicMirror::default(); + let light_data = LightData::GhostFocus(vec![Rays::default()]); + let input = LightResult::from([("input_1".into(), light_data)]); + assert!(AnalysisEnergy::analyze(&mut node, input).is_ok()); + } + + #[test] + fn analysis_energy_lightdata_fourier() { + let mut node = ParabolicMirror::default(); + let light_data = LightData::Fourier; + let input = LightResult::from([("input_1".into(), light_data)]); + assert!(AnalysisEnergy::analyze(&mut node, input).is_ok()); + } + + #[test] + fn analysis_ghost_focus_empty_input() { + let mut node = ParabolicMirror::default(); + let light_data = LightData::GhostFocus(Vec::<Rays>::new()); + let input = light_result_to_light_rays(LightResult::from([("input_1".into(), light_data)])) + .unwrap(); + let output = AnalysisGhostFocus::analyze( + &mut node, + input, + &GhostFocusConfig::default(), + &mut Vec::<Rays>::new(), + 0, + ) + .unwrap(); + assert!(output.values().last().unwrap().is_empty()); + } + + #[test] + fn analysis_ghost_focus_no_input() { + let mut node = ParabolicMirror::default(); + let light_data = LightData::GhostFocus(vec![Rays::default()]); + let input = + light_result_to_light_rays(LightResult::from([("output_1".into(), light_data)])) + .unwrap(); + let output = AnalysisGhostFocus::analyze( + &mut node, + input, + &GhostFocusConfig::default(), + &mut Vec::<Rays>::new(), + 0, + ) + .unwrap(); + assert!(output.values().last().unwrap().is_empty()); + } + + #[test] + fn analysis_ghost_focus_no_iso() { + let mut node = ParabolicMirror::default(); + let rays = Rays::new_uniform_collimated( + nanometer!(1000.), + joule!(1.), + &Hexapolar::new(millimeter!(1.), 3).unwrap(), + ) + .unwrap(); + let light_data = LightData::GhostFocus(vec![rays]); + let input = light_result_to_light_rays(LightResult::from([("input_1".into(), light_data)])) + .unwrap(); + let output = AnalysisGhostFocus::analyze( + &mut node, + input, + &GhostFocusConfig::default(), + &mut Vec::<Rays>::new(), + 0, + ); + assert!(output.is_err()); + } + #[test] + fn analysis_ghost_focus() { + let mut node = ParabolicMirror::default(); + node.set_isometry(Isometry::identity()); + let rays = Rays::new_uniform_collimated( + nanometer!(1000.), + joule!(1.), + &Hexapolar::new(millimeter!(1.), 3).unwrap(), + ) + .unwrap(); + let light_data = LightData::GhostFocus(vec![rays]); + let input = light_result_to_light_rays(LightResult::from([("input_1".into(), light_data)])) + .unwrap(); + let output = AnalysisGhostFocus::analyze( + &mut node, + input, + &GhostFocusConfig::default(), + &mut Vec::<Rays>::new(), + 0, + ); + assert!(output.is_ok()); + } + + #[test] + fn calc_node_position() { + let mut node = ParabolicMirror::default(); + node.set_isometry(Isometry::identity()); + let rays = Rays::new_uniform_collimated( + nanometer!(1000.), + joule!(1.), + &Hexapolar::new(millimeter!(1.), 3).unwrap(), + ) + .unwrap(); + let light_data = LightData::Geometric(rays); + let input = LightResult::from([("input_1".into(), light_data)]); + assert!(node + .calc_node_position(input, &RayTraceConfig::default()) + .is_ok()); + } +} diff --git a/opossum/src/nodes/paraxial_surface.rs b/opossum/src/nodes/paraxial_surface.rs index 6d480a2eada90402b272d631b02c82b448abc247..2956758edf974bc6e56c8674c9eade7fb054f3e6 100644 --- a/opossum/src/nodes/paraxial_surface.rs +++ b/opossum/src/nodes/paraxial_surface.rs @@ -130,11 +130,7 @@ impl AnalysisRayTrace for ParaxialSurface { else { return Err(OpossumError::Analysis("cannot read focal length".into())); }; - let Some(iso) = self.effective_iso() else { - return Err(OpossumError::Analysis( - "no location for surface defined. Aborting".into(), - )); - }; + let iso = self.effective_surface_iso(in_port)?; if let Some(surf) = self.get_optic_surface_mut(in_port) { let refraction_intended = true; surf.set_isometry(&iso); diff --git a/opossum/src/nodes/reflective_grating.rs b/opossum/src/nodes/reflective_grating.rs index 26f1ea2e035f69052d6ae296455a34c3b32e7aa2..7b16de468256ab367ce7059caee913fc5eed00d6 100644 --- a/opossum/src/nodes/reflective_grating.rs +++ b/opossum/src/nodes/reflective_grating.rs @@ -196,11 +196,7 @@ impl AnalysisRayTrace for ReflectiveGrating { return Err(OpossumError::Analysis("cannot read line density".into())); }; - let Some(iso) = self.effective_iso() else { - return Err(OpossumError::Analysis( - "no location for surface defined. Aborting".into(), - )); - }; + let iso = self.effective_surface_iso(in_port)?; if let Some(surf) = self.get_optic_surface_mut(in_port) { let refraction_intended = false; diff --git a/opossum/src/nodes/source.rs b/opossum/src/nodes/source.rs index eaf3baaa60183577e171c3788585835723241013..d4ee98b58ae751947f209e4bf71017524f884eda 100644 --- a/opossum/src/nodes/source.rs +++ b/opossum/src/nodes/source.rs @@ -225,7 +225,7 @@ impl AnalysisRayTrace for Source { )); }; if let LightData::Geometric(rays) = &mut data { - if let Some(iso) = self.effective_iso() { + if let Ok(iso) = self.effective_surface_iso("input_1") { *rays = rays.transformed_rays(&iso); // consider aperture only if not inverted (there is only an output port) if !self.inverted() { @@ -266,9 +266,9 @@ impl AnalysisRayTrace for Source { info!("No alignment wavelength defined, using energy-weighted central wavelength for alignment"); rays.get_optical_axis_ray() }?; - if let Some(iso) = self.effective_iso() { - axis_ray = axis_ray.transformed_ray(&iso); - } + let iso = self.effective_surface_iso("input_1")?; + axis_ray = axis_ray.transformed_ray(&iso); + let mut new_rays = Rays::default(); new_rays.add_ray(axis_ray); new_outgoing_edges @@ -304,9 +304,9 @@ impl AnalysisGhostFocus for Source { )); }; if let LightData::Geometric(rays) = &mut data { - if let Some(iso) = self.effective_iso() { - *rays = rays.transformed_rays(&iso); - } + let iso = self.effective_surface_iso("output_1")?; + *rays = rays.transformed_rays(&iso); + vec![rays.clone()] } else { return Err(OpossumError::Analysis( @@ -319,21 +319,17 @@ impl AnalysisGhostFocus for Source { } else { Vec::<Rays>::new() }; - if let Some(iso) = self.effective_iso() { - if let Some(surf) = self.get_optic_surface_mut("input_1") { - surf.set_isometry(&iso); - let refraction_intended = true; - for r in &mut rays { - r.refract_on_surface(surf, None, refraction_intended)?; - surf.evaluate_fluence_of_ray_bundle(r)?; - } - } else { - return Err(OpossumError::Analysis("no surface found. Aborting".into())); + let iso = self.effective_surface_iso("input_1")?; + + if let Some(surf) = self.get_optic_surface_mut("input_1") { + surf.set_isometry(&iso); + let refraction_intended = true; + for r in &mut rays { + r.refract_on_surface(surf, None, refraction_intended)?; + surf.evaluate_fluence_of_ray_bundle(r)?; } } else { - return Err(OpossumError::Analysis( - "no location for surface defined. Aborting".into(), - )); + return Err(OpossumError::Analysis("no surface found. Aborting".into())); } let mut out_light_rays = LightRays::default(); diff --git a/opossum/src/nodes/spot_diagram.rs b/opossum/src/nodes/spot_diagram.rs index 297ff3413b292d0332ef7c7851b322e3d2692317..2a32fc150509619aae79399c800e1850983acb63 100644 --- a/opossum/src/nodes/spot_diagram.rs +++ b/opossum/src/nodes/spot_diagram.rs @@ -103,7 +103,9 @@ impl OpticNode for SpotDiagram { let data = &self.light_data; if let Some(LightData::Geometric(rays)) = data { let mut transformed_rays = Rays::default(); - let iso = self.effective_iso().unwrap_or_else(Isometry::identity); + let iso = self + .effective_surface_iso("input_1") + .unwrap_or_else(|_| Isometry::identity()); for ray in rays { transformed_rays.add_ray(ray.inverse_transformed_ray(&iso)); } @@ -290,7 +292,7 @@ impl Plottable for SpotDiagram { let mut xy_pos_series = Vec::<MatrixXx2<Length>>::with_capacity(num_series); for ray_bundle in &split_rays_bundles { - let iso = self.effective_iso().unwrap_or_else(Isometry::identity); + let iso = self.effective_surface_iso("input_1")?; let xy_pos = ray_bundle.get_xy_rays_pos(true, &iso); x_max = xy_pos .column(0) diff --git a/opossum/src/nodes/thin_mirror.rs b/opossum/src/nodes/thin_mirror.rs index 1647d07dd3c621b4de55f33f8cc778bc1aea434b..cc7e4d2f93c8a92aee253cd619d916e55128b160 100644 --- a/opossum/src/nodes/thin_mirror.rs +++ b/opossum/src/nodes/thin_mirror.rs @@ -245,27 +245,21 @@ impl AnalysisRayTrace for ThinMirror { return Ok(LightResult::default()); }; if let LightData::Geometric(mut rays) = data.clone() { - let reflected = if let Some(iso) = self.effective_iso() { - if let Some(surf) = self.get_optic_surface_mut(in_port) { - surf.set_isometry(&iso); - let refraction_intended = false; - let mut reflected_rays = - rays.refract_on_surface(surf, None, refraction_intended)?; - if let Some(aperture) = self.ports().aperture(&PortType::Input, in_port) { - reflected_rays.apodize(aperture, &iso)?; - reflected_rays - .invalidate_by_threshold_energy(config.min_energy_per_ray())?; - reflected_rays - } else { - return Err(OpossumError::OpticPort("input aperture not found".into())); - } + let iso = self.effective_surface_iso(in_port)?; + let reflected = if let Some(surf) = self.get_optic_surface_mut(in_port) { + surf.set_isometry(&iso); + let refraction_intended = false; + let mut reflected_rays = + rays.refract_on_surface(surf, None, refraction_intended)?; + if let Some(aperture) = self.ports().aperture(&PortType::Input, in_port) { + reflected_rays.apodize(aperture, &iso)?; + reflected_rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?; + reflected_rays } else { - return Err(OpossumError::Analysis("no surface found. Aborting".into())); + return Err(OpossumError::OpticPort("input aperture not found".into())); } } else { - return Err(OpossumError::Analysis( - "no location for surface defined. Aborting".into(), - )); + return Err(OpossumError::Analysis("no surface found. Aborting".into())); }; let light_data = LightData::Geometric(reflected); let light_result = LightResult::from([(out_port.into(), light_data)]); diff --git a/opossum/src/nodes/wavefront.rs b/opossum/src/nodes/wavefront.rs index 6552ce2f9c6a462db6f1d8ba0cbd8d06a1678f6e..cbfc57450d62e3379eb9c18ee401b9c40b9f3af1 100644 --- a/opossum/src/nodes/wavefront.rs +++ b/opossum/src/nodes/wavefront.rs @@ -174,7 +174,9 @@ impl OpticNode for WaveFront { let mut props = Properties::default(); let data = &self.light_data; if let Some(LightData::Geometric(rays)) = data { - let iso = self.effective_iso().map_or_else(Isometry::identity, |v| v); + let iso = self + .effective_surface_iso("input_1") + .unwrap_or_else(|_| Isometry::identity()); let wf_data_opt = rays.get_wavefront_data_in_units_of_wvl(true, nanometer!(1.), &iso); if wf_data_opt.is_ok() diff --git a/opossum/src/nodes/wedge/analysis_ghostfocus.rs b/opossum/src/nodes/wedge/analysis_ghostfocus.rs index 2640a45fc101dad270a4bbf4de5886a00f80714a..e8bc4725d42e4c6665d90ae32a57370e6eaf64c4 100644 --- a/opossum/src/nodes/wedge/analysis_ghostfocus.rs +++ b/opossum/src/nodes/wedge/analysis_ghostfocus.rs @@ -31,19 +31,20 @@ impl AnalysisGhostFocus for Wedge { .get(in_port) .map_or_else(Vec::<Rays>::new, std::clone::Clone::clone); - let (eff_iso, refri, center_thickness, wedge) = + let (refri, center_thickness, wedge) = self.get_node_attributes_ray_trace(&self.node_attr)?; let thickness_iso: Isometry = Isometry::new_along_z(center_thickness)?; let wedge_iso = Isometry::new( Point3::origin(), Point3::new(wedge, Angle::zero(), Angle::zero()), )?; + if self.inverted() { - self.set_surface_iso(out_port, &eff_iso)?; - self.set_surface_iso(in_port, &eff_iso.append(&thickness_iso).append(&wedge_iso))?; + self.set_surface_iso(out_port, &Isometry::identity())?; + self.set_surface_iso(in_port, &thickness_iso.append(&wedge_iso))?; } else { - self.set_surface_iso(in_port, &eff_iso)?; - self.set_surface_iso(out_port, &eff_iso.append(&thickness_iso).append(&wedge_iso))?; + self.set_surface_iso(in_port, &Isometry::identity())?; + self.set_surface_iso(out_port, &thickness_iso.append(&wedge_iso))?; }; let refraction_intended = true; diff --git a/opossum/src/nodes/wedge/analysis_raytrace.rs b/opossum/src/nodes/wedge/analysis_raytrace.rs index 797181b5f0d44764055c736c0dbc4f49a427a049..9b6dd1de4816c042f9a70ff483944ead05829d4d 100644 --- a/opossum/src/nodes/wedge/analysis_raytrace.rs +++ b/opossum/src/nodes/wedge/analysis_raytrace.rs @@ -31,7 +31,7 @@ impl AnalysisRayTrace for Wedge { )); }; - let (eff_iso, refri, center_thickness, wedge) = + let (refri, center_thickness, wedge) = self.get_node_attributes_ray_trace(&self.node_attr)?; let thickness_iso = Isometry::new_along_z(center_thickness)?; let wedge_iso = Isometry::new( @@ -40,11 +40,11 @@ impl AnalysisRayTrace for Wedge { )?; if self.inverted() { - self.set_surface_iso(out_port, &eff_iso)?; - self.set_surface_iso(in_port, &eff_iso.append(&thickness_iso).append(&wedge_iso))?; + self.set_surface_iso(out_port, &Isometry::identity())?; + self.set_surface_iso(in_port, &thickness_iso.append(&wedge_iso))?; } else { - self.set_surface_iso(in_port, &eff_iso)?; - self.set_surface_iso(out_port, &eff_iso.append(&thickness_iso).append(&wedge_iso))?; + self.set_surface_iso(in_port, &Isometry::identity())?; + self.set_surface_iso(out_port, &thickness_iso.append(&wedge_iso))?; }; let mut rays_bundle = vec![rays]; diff --git a/opossum/src/opm_document.rs b/opossum/src/opm_document.rs index ad0d0b5a48245a5f8b5855fa63ccd751c98c30a1..d03b86a8e4783172cf05c057a2bf3d94fc25c33e 100644 --- a/opossum/src/opm_document.rs +++ b/opossum/src/opm_document.rs @@ -169,11 +169,11 @@ mod test { let node1 = document.scenery.node(NodeIndex::from(0)).unwrap(); let node2 = document.scenery.node(NodeIndex::from(1)).unwrap(); assert_eq!( - "d8c43481-43b7-4bad-8305-7246a62e4016", + "587fa699-5e98-4d08-b5a5-f9885151f3d1", node1.uuid().to_string() ); assert_eq!( - "e6729ff1-e14a-468d-a08f-d0b9dd131dab", + "a81f485c-26f7-4b3c-a6ac-4a62746f6cad", node2.uuid().to_string() ); } diff --git a/opossum/src/optic_node.rs b/opossum/src/optic_node.rs index 470e0716ee1ffc11c428f11bf631a28e2b7aa73a..561d538479baccd439bd15e97bd81b06e3065fc2 100644 --- a/opossum/src/optic_node.rs +++ b/opossum/src/optic_node.rs @@ -359,7 +359,7 @@ pub trait OpticNode: Dottable { /// Return the effective input isometry of this optical node. /// /// The effective input isometry is the base isometry modified by the local alignment isometry (if any). - fn effective_iso(&self) -> Option<Isometry> { + fn effective_node_iso(&self) -> Option<Isometry> { self.isometry().as_ref().and_then(|iso| { self.node_attr().alignment().as_ref().map_or_else( || Some(iso.clone()), @@ -367,6 +367,25 @@ pub trait OpticNode: Dottable { ) }) } + /// Return the effective input isometry of an [`OpticSurface`]. + /// + /// The effective input isometry is the base isometry modified by the local alignment isometry (if any) and the anchor point isometry. + /// + /// # Errors + /// This function errors if + /// - no effective node isometry is defined + /// - the surface with the specified name cannot be found + fn effective_surface_iso(&self, surf_name: &str) -> OpmResult<Isometry> { + let Some(eff_node_iso) = self.effective_node_iso() else { + return Err(OpossumError::Other("no effective node iso defined".into())); + }; + let Some(surf) = self.get_optic_surface(surf_name) else { + return Err(OpossumError::Other(format!( + "no surface with name {surf_name} defined" + ))); + }; + Ok(eff_node_iso.append(surf.anchor_point_iso())) + } /// Set local alignment (decenter, tilt) of an optical node. /// /// # Errors @@ -415,14 +434,22 @@ pub trait OpticNode: Dottable { ) } - ///returns a mutable reference to an optical surface - // fn get_surface_mut(&mut self, surf_name: &str) -> &mut OpticSurface; - + /// Returns a mutable reference to an [`OpticSurface`] of this [`OpticNode`] with the key `surf_name` + /// # Attributes + /// - `surf_name`: name of the optical surface, which is the key in the [`OpticPorts`] hashmap stat stores the surfaces fn get_optic_surface_mut(&mut self, surf_name: &str) -> Option<&mut OpticSurface> { self.node_attr_mut() .ports_mut() .get_optic_surface_mut(&surf_name.to_owned()) } + /// Returns a reference to an [`OpticSurface`] of this [`OpticNode`] with the key `surf_name` + /// # Attributes + /// - `surf_name`: name of the optical surface, which is the key in the [`OpticPorts`] hashmap stat stores the surfaces + fn get_optic_surface(&self, surf_name: &str) -> Option<&OpticSurface> { + self.node_attr() + .ports() + .get_optic_surface(&surf_name.to_owned()) + } } impl Debug for dyn OpticNode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { diff --git a/opossum/src/optic_ports.rs b/opossum/src/optic_ports.rs index 680b075a4175af92af4b1bd25842636718959bda..8a3342312e596a69e73a80c89713a254ed6a25c8 100644 --- a/opossum/src/optic_ports.rs +++ b/opossum/src/optic_ports.rs @@ -120,6 +120,16 @@ impl OpticPorts { } } + /// Returns a reference to an [`OpticSurface`] with the key `surf_name` + /// # Attributes + /// - `surf_name`: name of the optical surface, which is the key in the [`OpticPorts`] hashmap stat stores the surfaces + #[must_use] + pub fn get_optic_surface(&self, surf_name: &String) -> Option<&OpticSurface> { + self.inputs + .get(surf_name) + .map_or_else(|| self.outputs.get(surf_name), Some) + } + /// Returns a reference to the input / output ports of this [`OpticPorts`]. #[must_use] pub fn ports_mut(&mut self, port_type: &PortType) -> &mut BTreeMap<String, OpticSurface> { @@ -380,7 +390,7 @@ mod test { ports.add(&PortType::Output, "test2").unwrap(); assert_eq!( ports.to_string(), - "inputs:\n <test1> OpticSurface { aperture: None, coating: IdealAR, geometric surface: RefCell { value: Surface }, backward rays cache: [], forward rays cache: [], hitmap: HitMap { hit_map: [], critical_fluence: {} }, lidt: 10000.0 kg^1 s^-2 }\noutput:\n <test2> OpticSurface { aperture: None, coating: IdealAR, geometric surface: RefCell { value: Surface }, backward rays cache: [], forward rays cache: [], hitmap: HitMap { hit_map: [], critical_fluence: {} }, lidt: 10000.0 kg^1 s^-2 }\n".to_owned() + "inputs:\n <test1> OpticSurface { aperture: None, coating: IdealAR, geometric surface: RefCell { value: Surface }, lidt: 10000.0 kg^1 s^-2, .. }\noutput:\n <test2> OpticSurface { aperture: None, coating: IdealAR, geometric surface: RefCell { value: Surface }, lidt: 10000.0 kg^1 s^-2, .. }\n".to_owned() ); } #[test] @@ -392,7 +402,7 @@ mod test { ports.set_inverted(true); assert_eq!( ports.to_string(), - "inputs:\n <test2> OpticSurface { aperture: None, coating: IdealAR, geometric surface: RefCell { value: Surface }, backward rays cache: [], forward rays cache: [], hitmap: HitMap { hit_map: [], critical_fluence: {} }, lidt: 10000.0 kg^1 s^-2 }\noutput:\n <test1> OpticSurface { aperture: None, coating: IdealAR, geometric surface: RefCell { value: Surface }, backward rays cache: [], forward rays cache: [], hitmap: HitMap { hit_map: [], critical_fluence: {} }, lidt: 10000.0 kg^1 s^-2 }\nports are inverted\n".to_owned() + "inputs:\n <test2> OpticSurface { aperture: None, coating: IdealAR, geometric surface: RefCell { value: Surface }, lidt: 10000.0 kg^1 s^-2, .. }\noutput:\n <test1> OpticSurface { aperture: None, coating: IdealAR, geometric surface: RefCell { value: Surface }, lidt: 10000.0 kg^1 s^-2, .. }\nports are inverted\n".to_owned() ); } } diff --git a/opossum/src/rays.rs b/opossum/src/rays.rs index a9acdebaf8292a413f8a4d285e37dca71d0c8907..4ceb1fcf6fb8769c8c97e820ebacd4a35e055a6b 100644 --- a/opossum/src/rays.rs +++ b/opossum/src/rays.rs @@ -113,6 +113,11 @@ impl Rays { parent_pos_split_idx: 0, }) } + ///Checks if ray bundle contains no rays + #[must_use] + pub fn is_empty(&self) -> bool { + self.rays.is_empty() + } ///Returns the uuid of this ray bundle #[must_use] pub const fn uuid(&self) -> &Uuid { @@ -1329,6 +1334,16 @@ mod test { assert_eq!(Rays::default().nr_of_rays(false), 0); } #[test] + fn is_empty() { + assert!(Rays::default().is_empty()); + let mut rays = Rays::default(); + rays.add_ray( + Ray::new_collimated(millimeter!(0.0, 0.0, 0.0), nanometer!(1000.0), joule!(1.0)) + .unwrap(), + ); + assert!(!rays.is_empty()); + } + #[test] fn display() { let mut rays = Rays::default(); rays.add_ray( diff --git a/opossum/src/surface/optic_surface.rs b/opossum/src/surface/optic_surface.rs index a2880aef59936d433fe81ed65239952fd8f33a48..b99dd1f042347869d90b46f04a97c50a4359c7c4 100644 --- a/opossum/src/surface/optic_surface.rs +++ b/opossum/src/surface/optic_surface.rs @@ -22,6 +22,7 @@ use core::fmt::Debug; pub struct OpticSurface { #[serde(skip)] geo_surface: GeoSurfaceRef, + anchor_point_iso: Isometry, aperture: Aperture, coating: CoatingType, lidt: Fluence, @@ -40,6 +41,7 @@ impl Default for OpticSurface { fn default() -> Self { Self { geo_surface: GeoSurfaceRef::default(), + anchor_point_iso: Isometry::identity(), aperture: Aperture::default(), coating: CoatingType::IdealAR, lidt: J_per_cm2!(1.), @@ -192,7 +194,7 @@ impl OpticSurface { pub fn lidt(&self) -> &Fluence { &self.lidt } - /// Sets the laser induced damage threshold (LIDT) [`OpticSurface`] + /// Sets the laser induced damage threshold (LIDT) of this [`OpticSurface`] /// /// # Errors /// @@ -206,6 +208,17 @@ impl OpticSurface { self.lidt = lidt; Ok(()) } + + /// Sets the anchor point isometry of this [`OpticSurface`] + pub fn set_anchor_point_iso(&mut self, iso: Isometry) { + self.anchor_point_iso = iso; + } + + ///Returns a reference to the anchor point isometry of this [`OpticSurface`] + #[must_use] + pub const fn anchor_point_iso(&self) -> &Isometry { + &self.anchor_point_iso + } } impl Debug for OpticSurface { @@ -214,11 +227,8 @@ impl Debug for OpticSurface { .field("aperture", &self.aperture) .field("coating", &self.coating) .field("geometric surface", &self.geo_surface.0) - .field("backward rays cache", &self.backward_rays_cache) - .field("forward rays cache", &self.forward_rays_cache) - .field("hitmap", &self.hit_map) .field("lidt", &self.lidt) - .finish() + .finish_non_exhaustive() } } diff --git a/opossum/src/surface/parabola.rs b/opossum/src/surface/parabola.rs index 39681706d3bf350e6e17f0604e8bc5eb7becfeb4..18e18cf7d7d9b1d714973b5942dc34f5fda2036f 100644 --- a/opossum/src/surface/parabola.rs +++ b/opossum/src/surface/parabola.rs @@ -168,6 +168,19 @@ mod test { // assert!(parabola.calc_intersect_and_normal_do(&ray).is_some()); // } #[test] + fn intersect_ray_through_focus_convex() { + let parabola = Parabola::new(meter!(1.0), &Isometry::identity()).unwrap(); + let direction = vector![0.0, 0.5, 2.]; + let ray = Ray::new( + meter!(0.0, -0.5, -1.0), + direction, + nanometer!(1000.0), + joule!(1.0), + ) + .unwrap(); + assert!(parabola.calc_intersect_and_normal_do(&ray).is_some()); + } + #[test] fn intersect_touching() { let parabola = Parabola::new(meter!(1.0), &Isometry::identity()).unwrap(); let direction = vector![0.0, 1.0, 0.0]; diff --git a/opossum/src/utils/geom_transformation.rs b/opossum/src/utils/geom_transformation.rs index 8d57b8d0ce1cb0833aa4afbeadb9e305903a7303..401845cc15000f2fa0eb39b249d21a066b78028e 100644 --- a/opossum/src/utils/geom_transformation.rs +++ b/opossum/src/utils/geom_transformation.rs @@ -73,6 +73,17 @@ impl Isometry { ))) } + ///Returns the transform matrix of this [`Isometry`] + #[must_use] + pub const fn get_transform(&self) -> Isometry3<f64> { + self.transform + } + ///Returns the inverse transform matrix of this [`Isometry`] + #[must_use] + pub const fn get_inv_transform(&self) -> Isometry3<f64> { + self.inverse + } + /// Creates a new translation [`Isometry`] /// /// Internally, translation is handled in meter