From d0c86bb070220f1e29a7c658656ef0f5ef42c39d Mon Sep 17 00:00:00 2001
From: "y.zobus" <y.zobus@gsi.de>
Date: Mon, 4 Nov 2024 14:22:17 +0100
Subject: [PATCH 1/2] test: :white_check_mark: Added test for rotated and
 shifted paraxial lens

---
 opossum/examples/paraxial_lens_wavefront.rs |  26 ++---
 opossum/src/nodes/paraxial_surface.rs       | 117 ++++++++++++++++++--
 opossum/src/ray.rs                          |  23 +---
 3 files changed, 123 insertions(+), 43 deletions(-)

diff --git a/opossum/examples/paraxial_lens_wavefront.rs b/opossum/examples/paraxial_lens_wavefront.rs
index 0abb911a..488751ee 100644
--- a/opossum/examples/paraxial_lens_wavefront.rs
+++ b/opossum/examples/paraxial_lens_wavefront.rs
@@ -2,27 +2,27 @@ use std::path::Path;
 
 use num::Zero;
 use opossum::{
-    analyzers::{AnalyzerType, RayTraceConfig},
-    error::OpmResult,
-    joule, millimeter,
-    nodes::{
-        round_collimated_ray_source, NodeGroup, ParaxialSurface, RayPropagationVisualizer,
-        WaveFront,
-    },
-    OpmDocument,
+    analyzers::{AnalyzerType, RayTraceConfig}, degree, error::OpmResult, joule, millimeter, nodes::{
+        collimated_line_ray_source, round_collimated_ray_source, NodeGroup, ParaxialSurface, RayPropagationVisualizer, WaveFront
+    }, optic_node::OpticNode, utils::geom_transformation::Isometry, OpmDocument
 };
 use uom::si::f64::Length;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::new("Lens Ray-trace test");
     let src = scenery
-        .add_node(&round_collimated_ray_source(millimeter!(5.0), joule!(1.0), 30).unwrap())?;
-    let lens = scenery.add_node(&ParaxialSurface::new("f=100 mm", millimeter!(100.0))?)?;
-    let wf = scenery.add_node(&WaveFront::default())?;
+        .add_node(&collimated_line_ray_source(millimeter!(f64::sqrt(2.)*100.0), joule!(1.0), 10).unwrap())?;
+    let mut lens = ParaxialSurface::new("f=100 mm", millimeter!(100.0))?;
+    lens.set_isometry(
+        Isometry::new(millimeter!(0.0, 0.0, 100.0), degree!(45.0, 0.0, 0.0)).unwrap(),
+    );
+    let lens = scenery.add_node(&lens)?;
+
+    // let wf = scenery.add_node(&WaveFront::default())?;
     let det = scenery.add_node(&RayPropagationVisualizer::default())?;
     scenery.connect_nodes(src, "output_1", lens, "input_1", Length::zero())?;
-    scenery.connect_nodes(lens, "output_1", wf, "input_1", millimeter!(90.0))?;
-    scenery.connect_nodes(wf, "output_1", det, "input_1", Length::zero())?;
+    scenery.connect_nodes(lens, "output_1", det, "input_1", millimeter!(300.0))?;
+    // scenery.connect_nodes(wf, "output_1", det, "input_1", Length::zero())?;
 
     let mut doc = OpmDocument::new(scenery);
     doc.add_analyzer(AnalyzerType::RayTrace(RayTraceConfig::default()));
diff --git a/opossum/src/nodes/paraxial_surface.rs b/opossum/src/nodes/paraxial_surface.rs
index ffff1c31..f1675d5f 100644
--- a/opossum/src/nodes/paraxial_surface.rs
+++ b/opossum/src/nodes/paraxial_surface.rs
@@ -181,6 +181,7 @@ mod test {
         nodes::test_helper::test_helper::*, optic_ports::PortType, ray::Ray, rays::Rays,
         utils::geom_transformation::Isometry,
     };
+    use approx::assert_relative_eq;
     use assert_matches::assert_matches;
     use nalgebra::Vector3;
     #[test]
@@ -255,7 +256,7 @@ mod test {
         test_analyze_wrong_data_type::<ParaxialSurface>("input_1");
     }
     #[test]
-    fn analyze_geometric_no_isometery() {
+    fn analyze_geometric_no_isometry() {
         test_analyze_geometric_no_isometry::<ParaxialSurface>("input_1");
     }
     #[test]
@@ -283,13 +284,113 @@ mod test {
             assert!(false, "could not get LightData");
         }
     }
-    // #[test]
-    // #[ignore]
-    // fn export_data() {
-    //     assert!(ParaxialSurface::default()
-    //         .export_data(Path::new(""))
-    //         .is_ok());
-    // }
+    #[test]
+    fn test_shifted_x(){
+        let mut node = ParaxialSurface::new("test", millimeter!(10.)).unwrap();
+        node.set_isometry(
+            Isometry::new(millimeter!(10.0, 0.0, 10.0), degree!(0.0, 0.0, 0.0)).unwrap(),
+        );
+        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(),
+        );
+        let mut input = LightResult::default();
+        input.insert("input_1".into(), LightData::Geometric(rays));
+        let output =
+            AnalysisRayTrace::analyze(&mut node, input, &RayTraceConfig::default()).unwrap();
+
+            if let Some(LightData::Geometric(rays)) = output.get("output_1") {
+                assert_eq!(rays.nr_of_rays(true), 1);
+                let ray = rays.iter().next().unwrap();
+                assert_eq!(ray.position(), millimeter!(0.0, 0.0, 10.0));
+                assert_eq!(ray.direction(), Vector3::new(1.,0.,1.));
+            } else {
+                assert!(false, "could not get LightData");
+            }
+    }
+    #[test]
+    fn test_shifted_y(){
+        let mut node = ParaxialSurface::new("test", millimeter!(10.)).unwrap();
+        node.set_isometry(
+            Isometry::new(millimeter!(0.0, 10.0, 10.0), degree!(0.0, 0.0, 0.0)).unwrap(),
+        );
+        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(),
+        );
+        let mut input = LightResult::default();
+        input.insert("input_1".into(), LightData::Geometric(rays));
+        let output =
+            AnalysisRayTrace::analyze(&mut node, input, &RayTraceConfig::default()).unwrap();
+
+            if let Some(LightData::Geometric(rays)) = output.get("output_1") {
+                assert_eq!(rays.nr_of_rays(true), 1);
+                let ray = rays.iter().next().unwrap();
+                assert_eq!(ray.position(), millimeter!(0.0, 0.0, 10.0));
+                assert_eq!(ray.direction(), Vector3::new(0.,1.,1.));
+            } else {
+                assert!(false, "could not get LightData");
+            }
+    }
+
+    #[test]
+    fn test_rotated_y(){
+        let mut node = ParaxialSurface::new("test", millimeter!(10.)).unwrap();
+        node.set_isometry(
+            Isometry::new(millimeter!(0.0, 0.0, 10.0), degree!(45.0, 0.0, 0.0)).unwrap(),
+        );
+        let mut rays = Rays::default();
+        rays.add_ray(
+            Ray::new_collimated(millimeter!(0.0, 10.0/f64::sqrt(2.), 0.0), nanometer!(1000.0), joule!(1.0))
+                .unwrap(),
+        );
+        let mut input = LightResult::default();
+        input.insert("input_1".into(), LightData::Geometric(rays));
+        let output =
+            AnalysisRayTrace::analyze(&mut node, input, &RayTraceConfig::default()).unwrap();
+
+            if let Some(LightData::Geometric(rays)) = output.get("output_1") {
+                assert_eq!(rays.nr_of_rays(true), 1);
+                let ray = rays.iter().next().unwrap();
+                assert_relative_eq!(ray.position()[0].value, 0.0);
+                assert_relative_eq!(ray.position()[1].value, 0.01/f64::sqrt(2.));
+                assert_relative_eq!(ray.position()[2].value, 0.01/f64::sqrt(2.)+0.01);
+                assert_relative_eq!(ray.direction(), Vector3::new(0.,-1.,1.).normalize());
+            } else {
+                assert!(false, "could not get LightData");
+            }
+    }
+
+    #[test]
+    fn test_rotated_x(){
+        let mut node = ParaxialSurface::new("test", millimeter!(10.)).unwrap();
+        node.set_isometry(
+            Isometry::new(millimeter!(0.0, 0.0, 10.0), degree!(0.0, 45.0, 0.0)).unwrap(),
+        );
+        let mut rays = Rays::default();
+        rays.add_ray(
+            Ray::new_collimated(millimeter!(-10.0/f64::sqrt(2.), 0.0, 0.0), nanometer!(1000.0), joule!(1.0))
+                .unwrap(),
+        );
+        let mut input = LightResult::default();
+        input.insert("input_1".into(), LightData::Geometric(rays));
+        let output =
+            AnalysisRayTrace::analyze(&mut node, input, &RayTraceConfig::default()).unwrap();
+
+            if let Some(LightData::Geometric(rays)) = output.get("output_1") {
+                assert_eq!(rays.nr_of_rays(true), 1);
+                let ray = rays.iter().next().unwrap();
+                assert_relative_eq!(ray.position()[0].value, -0.01/f64::sqrt(2.));
+                assert_relative_eq!(ray.position()[1].value, 0.0);
+                assert_relative_eq!(ray.position()[2].value, 0.01/f64::sqrt(2.)+0.01);
+                assert_relative_eq!(ray.direction(), Vector3::new(1.,0.,1.).normalize());
+            } else {
+                assert!(false, "could not get LightData");
+            }
+    }
+
     #[test]
     fn as_ref_node_mut() {
         let mut node = ParaxialSurface::default();
diff --git a/opossum/src/ray.rs b/opossum/src/ray.rs
index 770df717..cadabd30 100644
--- a/opossum/src/ray.rs
+++ b/opossum/src/ray.rs
@@ -302,28 +302,7 @@ impl Ray {
     pub fn to_isometry(&self, up_direction: Vector3<f64>) -> Isometry {
         Isometry::new_from_view(self.position(), self.direction(), up_direction)
     }
-    /// Propagate a ray freely along its direction. The length is given as the projection on the z-axis (=optical axis).
-    ///
-    /// This function also respects the refractive index stored in the ray while calculating the optical path length.
-    ///
-    /// # Errors
-    /// This functions returns an error if the initial ray direction has a zero z component (= ray not propagating in z direction).
-    // pub fn propagate_along_z(&mut self, length_along_z: Length) -> OpmResult<()> {
-    //     if self.dir[2].abs() < f64::EPSILON {
-    //         return Err(OpossumError::Other(
-    //             "z-Axis of direction vector must be != 0.0".into(),
-    //         ));
-    //     }
-    //     self.pos_hist.push(self.pos);
-    //     let length_in_ray_dir = length_along_z / self.dir[2];
-    //     self.pos += vector![
-    //         length_in_ray_dir * self.dir.x,
-    //         length_in_ray_dir * self.dir.y,
-    //         length_in_ray_dir * self.dir.z
-    //     ];
-    //     self.path_length += length_in_ray_dir * self.refractive_index * self.dir.norm();
-    //     Ok(())
-    // }
+
     /// Refract a ray on a paraxial surface of a given focal length.
     ///
     /// Modify the ray direction in order to simulate a perfect lens. **Note**: This function also
-- 
GitLab


From 3508b44c3e65354b1ed6dfe7429b5c1d05493432 Mon Sep 17 00:00:00 2001
From: "y.zobus" <y.zobus@gsi.de>
Date: Mon, 4 Nov 2024 14:24:21 +0100
Subject: [PATCH 2/2] cargo fmt  + cargo xtask ci

---
 opossum/examples/paraxial_lens_wavefront.rs | 26 +++---
 opossum/src/nodes/paraxial_surface.rs       | 96 +++++++++++----------
 2 files changed, 65 insertions(+), 57 deletions(-)

diff --git a/opossum/examples/paraxial_lens_wavefront.rs b/opossum/examples/paraxial_lens_wavefront.rs
index 488751ee..0abb911a 100644
--- a/opossum/examples/paraxial_lens_wavefront.rs
+++ b/opossum/examples/paraxial_lens_wavefront.rs
@@ -2,27 +2,27 @@ use std::path::Path;
 
 use num::Zero;
 use opossum::{
-    analyzers::{AnalyzerType, RayTraceConfig}, degree, error::OpmResult, joule, millimeter, nodes::{
-        collimated_line_ray_source, round_collimated_ray_source, NodeGroup, ParaxialSurface, RayPropagationVisualizer, WaveFront
-    }, optic_node::OpticNode, utils::geom_transformation::Isometry, OpmDocument
+    analyzers::{AnalyzerType, RayTraceConfig},
+    error::OpmResult,
+    joule, millimeter,
+    nodes::{
+        round_collimated_ray_source, NodeGroup, ParaxialSurface, RayPropagationVisualizer,
+        WaveFront,
+    },
+    OpmDocument,
 };
 use uom::si::f64::Length;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::new("Lens Ray-trace test");
     let src = scenery
-        .add_node(&collimated_line_ray_source(millimeter!(f64::sqrt(2.)*100.0), joule!(1.0), 10).unwrap())?;
-    let mut lens = ParaxialSurface::new("f=100 mm", millimeter!(100.0))?;
-    lens.set_isometry(
-        Isometry::new(millimeter!(0.0, 0.0, 100.0), degree!(45.0, 0.0, 0.0)).unwrap(),
-    );
-    let lens = scenery.add_node(&lens)?;
-
-    // let wf = scenery.add_node(&WaveFront::default())?;
+        .add_node(&round_collimated_ray_source(millimeter!(5.0), joule!(1.0), 30).unwrap())?;
+    let lens = scenery.add_node(&ParaxialSurface::new("f=100 mm", millimeter!(100.0))?)?;
+    let wf = scenery.add_node(&WaveFront::default())?;
     let det = scenery.add_node(&RayPropagationVisualizer::default())?;
     scenery.connect_nodes(src, "output_1", lens, "input_1", Length::zero())?;
-    scenery.connect_nodes(lens, "output_1", det, "input_1", millimeter!(300.0))?;
-    // scenery.connect_nodes(wf, "output_1", det, "input_1", Length::zero())?;
+    scenery.connect_nodes(lens, "output_1", wf, "input_1", millimeter!(90.0))?;
+    scenery.connect_nodes(wf, "output_1", det, "input_1", Length::zero())?;
 
     let mut doc = OpmDocument::new(scenery);
     doc.add_analyzer(AnalyzerType::RayTrace(RayTraceConfig::default()));
diff --git a/opossum/src/nodes/paraxial_surface.rs b/opossum/src/nodes/paraxial_surface.rs
index f1675d5f..2abee2af 100644
--- a/opossum/src/nodes/paraxial_surface.rs
+++ b/opossum/src/nodes/paraxial_surface.rs
@@ -285,7 +285,7 @@ mod test {
         }
     }
     #[test]
-    fn test_shifted_x(){
+    fn test_shifted_x() {
         let mut node = ParaxialSurface::new("test", millimeter!(10.)).unwrap();
         node.set_isometry(
             Isometry::new(millimeter!(10.0, 0.0, 10.0), degree!(0.0, 0.0, 0.0)).unwrap(),
@@ -300,17 +300,17 @@ mod test {
         let output =
             AnalysisRayTrace::analyze(&mut node, input, &RayTraceConfig::default()).unwrap();
 
-            if let Some(LightData::Geometric(rays)) = output.get("output_1") {
-                assert_eq!(rays.nr_of_rays(true), 1);
-                let ray = rays.iter().next().unwrap();
-                assert_eq!(ray.position(), millimeter!(0.0, 0.0, 10.0));
-                assert_eq!(ray.direction(), Vector3::new(1.,0.,1.));
-            } else {
-                assert!(false, "could not get LightData");
-            }
+        if let Some(LightData::Geometric(rays)) = output.get("output_1") {
+            assert_eq!(rays.nr_of_rays(true), 1);
+            let ray = rays.iter().next().unwrap();
+            assert_eq!(ray.position(), millimeter!(0.0, 0.0, 10.0));
+            assert_eq!(ray.direction(), Vector3::new(1., 0., 1.));
+        } else {
+            assert!(false, "could not get LightData");
+        }
     }
     #[test]
-    fn test_shifted_y(){
+    fn test_shifted_y() {
         let mut node = ParaxialSurface::new("test", millimeter!(10.)).unwrap();
         node.set_isometry(
             Isometry::new(millimeter!(0.0, 10.0, 10.0), degree!(0.0, 0.0, 0.0)).unwrap(),
@@ -325,70 +325,78 @@ mod test {
         let output =
             AnalysisRayTrace::analyze(&mut node, input, &RayTraceConfig::default()).unwrap();
 
-            if let Some(LightData::Geometric(rays)) = output.get("output_1") {
-                assert_eq!(rays.nr_of_rays(true), 1);
-                let ray = rays.iter().next().unwrap();
-                assert_eq!(ray.position(), millimeter!(0.0, 0.0, 10.0));
-                assert_eq!(ray.direction(), Vector3::new(0.,1.,1.));
-            } else {
-                assert!(false, "could not get LightData");
-            }
+        if let Some(LightData::Geometric(rays)) = output.get("output_1") {
+            assert_eq!(rays.nr_of_rays(true), 1);
+            let ray = rays.iter().next().unwrap();
+            assert_eq!(ray.position(), millimeter!(0.0, 0.0, 10.0));
+            assert_eq!(ray.direction(), Vector3::new(0., 1., 1.));
+        } else {
+            assert!(false, "could not get LightData");
+        }
     }
 
     #[test]
-    fn test_rotated_y(){
+    fn test_rotated_y() {
         let mut node = ParaxialSurface::new("test", millimeter!(10.)).unwrap();
         node.set_isometry(
             Isometry::new(millimeter!(0.0, 0.0, 10.0), degree!(45.0, 0.0, 0.0)).unwrap(),
         );
         let mut rays = Rays::default();
         rays.add_ray(
-            Ray::new_collimated(millimeter!(0.0, 10.0/f64::sqrt(2.), 0.0), nanometer!(1000.0), joule!(1.0))
-                .unwrap(),
+            Ray::new_collimated(
+                millimeter!(0.0, 10.0 / f64::sqrt(2.), 0.0),
+                nanometer!(1000.0),
+                joule!(1.0),
+            )
+            .unwrap(),
         );
         let mut input = LightResult::default();
         input.insert("input_1".into(), LightData::Geometric(rays));
         let output =
             AnalysisRayTrace::analyze(&mut node, input, &RayTraceConfig::default()).unwrap();
 
-            if let Some(LightData::Geometric(rays)) = output.get("output_1") {
-                assert_eq!(rays.nr_of_rays(true), 1);
-                let ray = rays.iter().next().unwrap();
-                assert_relative_eq!(ray.position()[0].value, 0.0);
-                assert_relative_eq!(ray.position()[1].value, 0.01/f64::sqrt(2.));
-                assert_relative_eq!(ray.position()[2].value, 0.01/f64::sqrt(2.)+0.01);
-                assert_relative_eq!(ray.direction(), Vector3::new(0.,-1.,1.).normalize());
-            } else {
-                assert!(false, "could not get LightData");
-            }
+        if let Some(LightData::Geometric(rays)) = output.get("output_1") {
+            assert_eq!(rays.nr_of_rays(true), 1);
+            let ray = rays.iter().next().unwrap();
+            assert_relative_eq!(ray.position()[0].value, 0.0);
+            assert_relative_eq!(ray.position()[1].value, 0.01 / f64::sqrt(2.));
+            assert_relative_eq!(ray.position()[2].value, 0.01 / f64::sqrt(2.) + 0.01);
+            assert_relative_eq!(ray.direction(), Vector3::new(0., -1., 1.).normalize());
+        } else {
+            assert!(false, "could not get LightData");
+        }
     }
 
     #[test]
-    fn test_rotated_x(){
+    fn test_rotated_x() {
         let mut node = ParaxialSurface::new("test", millimeter!(10.)).unwrap();
         node.set_isometry(
             Isometry::new(millimeter!(0.0, 0.0, 10.0), degree!(0.0, 45.0, 0.0)).unwrap(),
         );
         let mut rays = Rays::default();
         rays.add_ray(
-            Ray::new_collimated(millimeter!(-10.0/f64::sqrt(2.), 0.0, 0.0), nanometer!(1000.0), joule!(1.0))
-                .unwrap(),
+            Ray::new_collimated(
+                millimeter!(-10.0 / f64::sqrt(2.), 0.0, 0.0),
+                nanometer!(1000.0),
+                joule!(1.0),
+            )
+            .unwrap(),
         );
         let mut input = LightResult::default();
         input.insert("input_1".into(), LightData::Geometric(rays));
         let output =
             AnalysisRayTrace::analyze(&mut node, input, &RayTraceConfig::default()).unwrap();
 
-            if let Some(LightData::Geometric(rays)) = output.get("output_1") {
-                assert_eq!(rays.nr_of_rays(true), 1);
-                let ray = rays.iter().next().unwrap();
-                assert_relative_eq!(ray.position()[0].value, -0.01/f64::sqrt(2.));
-                assert_relative_eq!(ray.position()[1].value, 0.0);
-                assert_relative_eq!(ray.position()[2].value, 0.01/f64::sqrt(2.)+0.01);
-                assert_relative_eq!(ray.direction(), Vector3::new(1.,0.,1.).normalize());
-            } else {
-                assert!(false, "could not get LightData");
-            }
+        if let Some(LightData::Geometric(rays)) = output.get("output_1") {
+            assert_eq!(rays.nr_of_rays(true), 1);
+            let ray = rays.iter().next().unwrap();
+            assert_relative_eq!(ray.position()[0].value, -0.01 / f64::sqrt(2.));
+            assert_relative_eq!(ray.position()[1].value, 0.0);
+            assert_relative_eq!(ray.position()[2].value, 0.01 / f64::sqrt(2.) + 0.01);
+            assert_relative_eq!(ray.direction(), Vector3::new(1., 0., 1.).normalize());
+        } else {
+            assert!(false, "could not get LightData");
+        }
     }
 
     #[test]
-- 
GitLab