From 5d5c4d25296c6c22c14aa4ec2a2292ff62dd3e1f Mon Sep 17 00:00:00 2001
From: Udo Eisenbarth <u.eisenbarth@gsi.de>
Date: Fri, 25 Oct 2024 14:30:00 +0200
Subject: [PATCH] fix: :bug: Fix error during apodization of rays: Aperture did
 not consider isometries.

---
 opossum/examples/apodization.rs               | 39 ++++++++++++++++
 opossum/examples/rays.rs                      | 12 -----
 opossum/src/analyzers/raytrace.rs             | 18 ++++++--
 opossum/src/nodes/beam_splitter/mod.rs        | 39 +++++++++-------
 opossum/src/nodes/detector.rs                 | 32 ++++++-------
 opossum/src/nodes/dummy.rs                    | 28 +++++------
 opossum/src/nodes/energy_meter.rs             | 34 +++++++-------
 opossum/src/nodes/fluence_detector.rs         | 34 +++++++-------
 opossum/src/nodes/ideal_filter.rs             | 26 +++++------
 opossum/src/nodes/paraxial_surface.rs         | 24 +++++-----
 .../src/nodes/ray_propagation_visualizer.rs   | 40 ++++++++--------
 opossum/src/nodes/reflective_grating.rs       |  2 +-
 opossum/src/nodes/source.rs                   | 20 ++++----
 opossum/src/nodes/spectrometer.rs             | 34 +++++++-------
 opossum/src/nodes/spot_diagram.rs             | 46 +++++++++----------
 opossum/src/nodes/thin_mirror.rs              |  2 +-
 opossum/src/nodes/wavefront.rs                | 34 +++++++-------
 opossum/src/rays.rs                           |  7 +--
 18 files changed, 260 insertions(+), 211 deletions(-)
 create mode 100644 opossum/examples/apodization.rs
 delete mode 100644 opossum/examples/rays.rs

diff --git a/opossum/examples/apodization.rs b/opossum/examples/apodization.rs
new file mode 100644
index 00000000..707be419
--- /dev/null
+++ b/opossum/examples/apodization.rs
@@ -0,0 +1,39 @@
+use std::path::Path;
+
+use opossum::{
+    analyzers::{AnalyzerType, RayTraceConfig},
+    aperture::{Aperture, RectangleConfig},
+    error::OpmResult,
+    joule, millimeter,
+    nodes::{round_collimated_ray_source, Dummy, NodeGroup, SpotDiagram},
+    optic_node::{Alignable, OpticNode},
+    optic_ports::PortType,
+    OpmDocument,
+};
+
+fn main() -> OpmResult<()> {
+    let mut scenery = NodeGroup::default();
+
+    let i_src = scenery.add_node(&round_collimated_ray_source(
+        millimeter!(20.0),
+        joule!(1.0),
+        10,
+    )?)?;
+
+    let mut dummy = Dummy::default();
+    let rect_config =
+        RectangleConfig::new(millimeter!(15.), millimeter!(15.), millimeter!(0.0, 0.0))?;
+    let aperture = Aperture::BinaryRectangle(rect_config);
+    dummy.set_aperture(&PortType::Input, "front", &aperture)?;
+    let dummy = dummy.with_decenter(millimeter!(-5.0, 5.0, 0.0))?;
+
+    let i_d = scenery.add_node(&dummy)?;
+    let i_sd = scenery.add_node(&SpotDiagram::default())?;
+
+    scenery.connect_nodes(i_src, "out1", i_d, "front", millimeter!(50.0))?;
+    scenery.connect_nodes(i_d, "rear", i_sd, "in1", millimeter!(50.0))?;
+
+    let mut doc = OpmDocument::new(scenery);
+    doc.add_analyzer(AnalyzerType::RayTrace(RayTraceConfig::default()));
+    doc.save_to_file(Path::new("./opossum/playground/apodization.opm"))
+}
diff --git a/opossum/examples/rays.rs b/opossum/examples/rays.rs
deleted file mode 100644
index cc1a1544..00000000
--- a/opossum/examples/rays.rs
+++ /dev/null
@@ -1,12 +0,0 @@
-use opossum::{
-    error::OpmResult, joule, millimeter, nanometer, position_distributions::Random, rays::Rays,
-};
-
-fn main() -> OpmResult<()> {
-    let _rays = Rays::new_uniform_collimated(
-        nanometer!(1054.0),
-        joule!(1.0),
-        &Random::new(millimeter!(1.0), millimeter!(1.0), 200)?,
-    )?;
-    Ok(())
-}
diff --git a/opossum/src/analyzers/raytrace.rs b/opossum/src/analyzers/raytrace.rs
index 9935e508..e5eca4b1 100644
--- a/opossum/src/analyzers/raytrace.rs
+++ b/opossum/src/analyzers/raytrace.rs
@@ -94,10 +94,15 @@ pub trait AnalysisRayTrace: OpticNode + Surface {
         port_name: &str,
     ) -> OpmResult<()> {
         let uuid = *self.node_attr().uuid();
+        let Some(iso) = &self.effective_iso() else {
+            return Err(OpossumError::Analysis(
+                "surface has no isometry defined".into(),
+            ));
+        };
         if backward {
             for rays in &mut *rays_bundle {
                 if let Some(aperture) = self.ports().aperture(&PortType::Input, port_name) {
-                    rays.apodize(aperture)?;
+                    rays.apodize(aperture, iso)?;
                     if let AnalyzerType::RayTrace(ref config) = analyzer_type {
                         rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
                     }
@@ -120,7 +125,7 @@ pub trait AnalysisRayTrace: OpticNode + Surface {
         } else {
             for rays in &mut *rays_bundle {
                 if let Some(aperture) = self.ports().aperture(&PortType::Input, port_name) {
-                    rays.apodize(aperture)?;
+                    rays.apodize(aperture, &self.effective_iso().unwrap())?;
                     if let AnalyzerType::RayTrace(ref config) = analyzer_type {
                         rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
                     }
@@ -154,6 +159,11 @@ pub trait AnalysisRayTrace: OpticNode + Surface {
         port_name: &str,
     ) -> OpmResult<()> {
         let uuid: uuid::Uuid = *self.node_attr().uuid();
+        let Some(iso) = &self.effective_iso() else {
+            return Err(OpossumError::Analysis(
+                "surface has no isometry defined".into(),
+            ));
+        };
         let surf = self.get_surface_mut(port_name);
         if backward {
             for rays in &mut *rays_bundle {
@@ -171,7 +181,7 @@ pub trait AnalysisRayTrace: OpticNode + Surface {
             }
             for rays in &mut *rays_bundle {
                 if let Some(aperture) = self.ports().aperture(&PortType::Output, port_name) {
-                    rays.apodize(aperture)?;
+                    rays.apodize(aperture, iso)?;
                     if let AnalyzerType::RayTrace(config) = analyzer_type {
                         rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
                     }
@@ -193,7 +203,7 @@ pub trait AnalysisRayTrace: OpticNode + Surface {
             }
             for rays in &mut *rays_bundle {
                 if let Some(aperture) = self.ports().aperture(&PortType::Output, port_name) {
-                    rays.apodize(aperture)?;
+                    rays.apodize(aperture, iso)?;
                     if let AnalyzerType::RayTrace(config) = analyzer_type {
                         rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
                     }
diff --git a/opossum/src/nodes/beam_splitter/mod.rs b/opossum/src/nodes/beam_splitter/mod.rs
index 736be84b..6a34d193 100644
--- a/opossum/src/nodes/beam_splitter/mod.rs
+++ b/opossum/src/nodes/beam_splitter/mod.rs
@@ -199,19 +199,20 @@ impl BeamSplitter {
                     if let Some(iso) = self.effective_iso() {
                         let mut plane = OpticalSurface::new(Box::new(Plane::new(&iso)));
                         rays.refract_on_surface(&mut plane, None)?;
+                        if let Some(aperture) = self.ports().aperture(&PortType::Input, "input1") {
+                            rays.apodize(aperture, &iso)?;
+                            if let AnalyzerType::RayTrace(config) = analyzer_type {
+                                rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+                            }
+                        } else {
+                            return Err(OpossumError::OpticPort("input aperture not found".into()));
+                        };
                     } else {
                         return Err(OpossumError::Analysis(
                             "no location for surface defined. Aborting".into(),
                         ));
                     }
-                    if let Some(aperture) = self.ports().aperture(&PortType::Input, "input1") {
-                        rays.apodize(aperture)?;
-                        if let AnalyzerType::RayTrace(config) = analyzer_type {
-                            rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-                        }
-                    } else {
-                        return Err(OpossumError::OpticPort("input aperture not found".into()));
-                    };
+
                     let split_rays = rays.split(&splitting_config.value)?;
                     (rays, split_rays)
                 }
@@ -228,22 +229,23 @@ impl BeamSplitter {
             match input2 {
                 LightData::Geometric(r) => {
                     let mut rays = r.clone();
-                    if let Some(iso) = self.effective_iso() {
-                        let mut plane = OpticalSurface::new(Box::new(Plane::new(&iso)));
-                        rays.refract_on_surface(&mut plane, None)?;
-                    } else {
+                    let Some(iso) = self.effective_iso() else {
                         return Err(OpossumError::Analysis(
                             "no location for surface defined. Aborting".into(),
                         ));
-                    }
+                    };
+
+                    let mut plane = OpticalSurface::new(Box::new(Plane::new(&iso)));
+                    rays.refract_on_surface(&mut plane, None)?;
                     if let Some(aperture) = self.ports().aperture(&PortType::Input, "input2") {
-                        rays.apodize(aperture)?;
+                        rays.apodize(aperture, &iso)?;
                         if let AnalyzerType::RayTrace(config) = analyzer_type {
                             rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
                         }
                     } else {
                         return Err(OpossumError::OpticPort("input aperture not found".into()));
                     };
+
                     let split_rays = rays.split(&splitting_config.value)?;
                     (rays, split_rays)
                 }
@@ -258,11 +260,16 @@ 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(),
+            ));
+        };
         if let Some(aperture) = self
             .ports()
             .aperture(&PortType::Output, "out1_trans1_refl2")
         {
-            in_ray1.apodize(aperture)?;
+            in_ray1.apodize(aperture, &iso)?;
             if let AnalyzerType::RayTrace(config) = analyzer_type {
                 in_ray1.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
             }
@@ -273,7 +280,7 @@ impl BeamSplitter {
             .ports()
             .aperture(&PortType::Output, "out2_trans2_refl1")
         {
-            in_ray2.apodize(aperture)?;
+            in_ray2.apodize(aperture, &iso)?;
             if let AnalyzerType::RayTrace(config) = analyzer_type {
                 in_ray2.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
             }
diff --git a/opossum/src/nodes/detector.rs b/opossum/src/nodes/detector.rs
index 5a3a2123..c45b833c 100644
--- a/opossum/src/nodes/detector.rs
+++ b/opossum/src/nodes/detector.rs
@@ -132,27 +132,27 @@ impl AnalysisRayTrace for Detector {
             if let Some(iso) = self.effective_iso() {
                 self.surface.set_isometry(&iso);
                 rays.refract_on_surface(&mut self.surface, None)?;
+                if let Some(aperture) = self.ports().aperture(&PortType::Input, in_port) {
+                    let rays_apodized = rays.apodize(aperture, &iso)?;
+                    if rays_apodized {
+                        warn!("Rays have been apodized at input aperture of {}. Results might not be accurate.", self as &mut dyn OpticNode);
+                    }
+                    rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+                } else {
+                    return Err(OpossumError::OpticPort("input aperture not found".into()));
+                };
+                self.light_data = Some(LightData::Geometric(rays.clone()));
+                if let Some(aperture) = self.ports().aperture(&PortType::Output, out_port) {
+                    rays.apodize(aperture, &iso)?;
+                    rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+                } else {
+                    return Err(OpossumError::OpticPort("input aperture not found".into()));
+                };
             } else {
                 return Err(OpossumError::Analysis(
                     "no location for surface defined. Aborting".into(),
                 ));
             }
-            if let Some(aperture) = self.ports().aperture(&PortType::Input, in_port) {
-                let rays_apodized = rays.apodize(aperture)?;
-                if rays_apodized {
-                    warn!("Rays have been apodized at input aperture of {}. Results might not be accurate.", self as &mut dyn OpticNode);
-                }
-                rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-            } else {
-                return Err(OpossumError::OpticPort("input aperture not found".into()));
-            };
-            self.light_data = Some(LightData::Geometric(rays.clone()));
-            if let Some(aperture) = self.ports().aperture(&PortType::Output, out_port) {
-                rays.apodize(aperture)?;
-                rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-            } else {
-                return Err(OpossumError::OpticPort("input aperture not found".into()));
-            };
             Ok(LightResult::from([(
                 out_port.into(),
                 LightData::Geometric(rays),
diff --git a/opossum/src/nodes/dummy.rs b/opossum/src/nodes/dummy.rs
index 1ad3ee5c..e5a4cf8c 100644
--- a/opossum/src/nodes/dummy.rs
+++ b/opossum/src/nodes/dummy.rs
@@ -9,7 +9,7 @@ use crate::{
     error::{OpmResult, OpossumError},
     light_result::LightResult,
     lightdata::LightData,
-    optic_node::OpticNode,
+    optic_node::{Alignable, OpticNode},
     optic_ports::{OpticPorts, PortType},
     surface::{OpticalSurface, Plane, Surface},
     utils::geom_transformation::Isometry,
@@ -103,23 +103,24 @@ impl AnalysisRayTrace for Dummy {
             if let Some(iso) = self.effective_iso() {
                 self.surface.set_isometry(&iso);
                 rays.refract_on_surface(&mut self.surface, None)?;
+                if let Some(aperture) = self.ports().aperture(&PortType::Input, inport) {
+                    rays.apodize(aperture, &iso)?;
+                    rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+                } else {
+                    return Err(OpossumError::OpticPort("input aperture not found".into()));
+                };
+                if let Some(aperture) = self.ports().aperture(&PortType::Output, outport) {
+                    rays.apodize(aperture, &iso)?;
+                    rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+                } else {
+                    return Err(OpossumError::OpticPort("output aperture not found".into()));
+                };
             } else {
                 return Err(OpossumError::Analysis(
                     "no location for surface defined. Aborting".into(),
                 ));
             }
-            if let Some(aperture) = self.ports().aperture(&PortType::Input, inport) {
-                rays.apodize(aperture)?;
-                rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-            } else {
-                return Err(OpossumError::OpticPort("input aperture not found".into()));
-            };
-            if let Some(aperture) = self.ports().aperture(&PortType::Output, outport) {
-                rays.apodize(aperture)?;
-                rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-            } else {
-                return Err(OpossumError::OpticPort("input aperture not found".into()));
-            };
+
             Ok(LightResult::from([(
                 outport.into(),
                 LightData::Geometric(rays),
@@ -142,6 +143,7 @@ impl OpticNode for Dummy {
     }
 }
 
+impl Alignable for Dummy {}
 impl Dottable for Dummy {}
 
 #[cfg(test)]
diff --git a/opossum/src/nodes/energy_meter.rs b/opossum/src/nodes/energy_meter.rs
index 13df2d6d..54dafa48 100644
--- a/opossum/src/nodes/energy_meter.rs
+++ b/opossum/src/nodes/energy_meter.rs
@@ -251,28 +251,28 @@ impl AnalysisRayTrace for EnergyMeter {
             if let Some(iso) = self.effective_iso() {
                 self.surface.set_isometry(&iso);
                 rays.refract_on_surface(&mut self.surface, None)?;
+                if let Some(aperture) = self.ports().aperture(&PortType::Input, inport) {
+                    let rays_apodized = rays.apodize(aperture, &iso)?;
+                    if rays_apodized {
+                        warn!("Rays have been apodized at input aperture of {}. Results might not be accurate.", self as &mut dyn OpticNode);
+                        self.apodization_warning = true;
+                    }
+                    rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+                } else {
+                    return Err(OpossumError::OpticPort("input aperture not found".into()));
+                };
+                self.light_data = Some(LightData::Geometric(rays.clone()));
+                if let Some(aperture) = self.ports().aperture(&PortType::Output, outport) {
+                    rays.apodize(aperture, &iso)?;
+                    rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+                } else {
+                    return Err(OpossumError::OpticPort("output aperture not found".into()));
+                };
             } else {
                 return Err(OpossumError::Analysis(
                     "no location for surface defined. Aborting".into(),
                 ));
             }
-            if let Some(aperture) = self.ports().aperture(&PortType::Input, inport) {
-                let rays_apodized = rays.apodize(aperture)?;
-                if rays_apodized {
-                    warn!("Rays have been apodized at input aperture of {}. Results might not be accurate.", self as &mut dyn OpticNode);
-                    self.apodization_warning = true;
-                }
-                rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-            } else {
-                return Err(OpossumError::OpticPort("input aperture not found".into()));
-            };
-            self.light_data = Some(LightData::Geometric(rays.clone()));
-            if let Some(aperture) = self.ports().aperture(&PortType::Output, outport) {
-                rays.apodize(aperture)?;
-                rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-            } else {
-                return Err(OpossumError::OpticPort("output aperture not found".into()));
-            };
             Ok(LightResult::from([(
                 outport.into(),
                 LightData::Geometric(rays),
diff --git a/opossum/src/nodes/fluence_detector.rs b/opossum/src/nodes/fluence_detector.rs
index 40487103..ff09075b 100644
--- a/opossum/src/nodes/fluence_detector.rs
+++ b/opossum/src/nodes/fluence_detector.rs
@@ -242,28 +242,28 @@ impl AnalysisRayTrace for FluenceDetector {
             if let Some(iso) = self.effective_iso() {
                 self.surface.set_isometry(&iso);
                 rays.refract_on_surface(&mut self.surface, None)?;
+                if let Some(aperture) = self.ports().aperture(&PortType::Input, inport) {
+                    let rays_apodized = rays.apodize(aperture, &iso)?;
+                    if rays_apodized {
+                        warn!("Rays have been apodized at input aperture of {}. Results might not be accurate.", self as &mut dyn OpticNode);
+                        self.apodization_warning = true;
+                    }
+                    rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+                } else {
+                    return Err(OpossumError::OpticPort("input aperture not found".into()));
+                };
+                self.light_data = Some(LightData::Geometric(rays.clone()));
+                if let Some(aperture) = self.ports().aperture(&PortType::Output, outport) {
+                    rays.apodize(aperture, &iso)?;
+                    rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+                } else {
+                    return Err(OpossumError::OpticPort("output aperture not found".into()));
+                };
             } else {
                 return Err(OpossumError::Analysis(
                     "no location for surface defined. Aborting".into(),
                 ));
             }
-            if let Some(aperture) = self.ports().aperture(&PortType::Input, inport) {
-                let rays_apodized = rays.apodize(aperture)?;
-                if rays_apodized {
-                    warn!("Rays have been apodized at input aperture of {}. Results might not be accurate.", self as &mut dyn OpticNode);
-                    self.apodization_warning = true;
-                }
-                rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-            } else {
-                return Err(OpossumError::OpticPort("input aperture not found".into()));
-            };
-            self.light_data = Some(LightData::Geometric(rays.clone()));
-            if let Some(aperture) = self.ports().aperture(&PortType::Output, outport) {
-                rays.apodize(aperture)?;
-                rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-            } else {
-                return Err(OpossumError::OpticPort("output aperture not found".into()));
-            };
             Ok(LightResult::from([(
                 outport.into(),
                 LightData::Geometric(rays),
diff --git a/opossum/src/nodes/ideal_filter.rs b/opossum/src/nodes/ideal_filter.rs
index e4a598f8..e766db11 100644
--- a/opossum/src/nodes/ideal_filter.rs
+++ b/opossum/src/nodes/ideal_filter.rs
@@ -242,24 +242,24 @@ impl AnalysisRayTrace for IdealFilter {
                     .clone(),
             );
             rays.refract_on_surface(&mut self.surface, None)?;
+            rays.filter_energy(&self.filter_type())?;
+            if let Some(aperture) = self.ports().aperture(&PortType::Input, in_port) {
+                rays.apodize(aperture, &iso)?;
+                rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+            } else {
+                return Err(OpossumError::OpticPort("input aperture not found".into()));
+            };
+            if let Some(aperture) = self.ports().aperture(&PortType::Output, out_port) {
+                rays.apodize(aperture, &iso)?;
+                rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+            } else {
+                return Err(OpossumError::OpticPort("output aperture not found".into()));
+            };
         } else {
             return Err(OpossumError::Analysis(
                 "no location for surface defined. Aborting".into(),
             ));
         }
-        rays.filter_energy(&self.filter_type())?;
-        if let Some(aperture) = self.ports().aperture(&PortType::Input, in_port) {
-            rays.apodize(aperture)?;
-            rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-        } else {
-            return Err(OpossumError::OpticPort("input aperture not found".into()));
-        };
-        if let Some(aperture) = self.ports().aperture(&PortType::Output, out_port) {
-            rays.apodize(aperture)?;
-            rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-        } else {
-            return Err(OpossumError::OpticPort("output aperture not found".into()));
-        };
         let light_data = LightData::Geometric(rays);
         Ok(LightResult::from([(out_port.into(), light_data)]))
     }
diff --git a/opossum/src/nodes/paraxial_surface.rs b/opossum/src/nodes/paraxial_surface.rs
index 4b3287d4..987f461f 100644
--- a/opossum/src/nodes/paraxial_surface.rs
+++ b/opossum/src/nodes/paraxial_surface.rs
@@ -148,23 +148,23 @@ impl AnalysisRayTrace for ParaxialSurface {
                     None,
                 )?;
                 rays.refract_paraxial(*focal_length, &iso)?;
+                if let Some(aperture) = self.ports().aperture(&PortType::Input, in_port) {
+                    rays.apodize(aperture, &iso)?;
+                    rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+                } else {
+                    return Err(OpossumError::OpticPort("input aperture not found".into()));
+                };
+                if let Some(aperture) = self.ports().aperture(&PortType::Output, out_port) {
+                    rays.apodize(aperture, &iso)?;
+                    rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+                } else {
+                    return Err(OpossumError::OpticPort("output aperture not found".into()));
+                };
             } else {
                 return Err(OpossumError::Analysis(
                     "no location for surface defined. Aborting".into(),
                 ));
             }
-            if let Some(aperture) = self.ports().aperture(&PortType::Input, in_port) {
-                rays.apodize(aperture)?;
-                rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-            } else {
-                return Err(OpossumError::OpticPort("input aperture not found".into()));
-            };
-            if let Some(aperture) = self.ports().aperture(&PortType::Output, out_port) {
-                rays.apodize(aperture)?;
-                rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-            } else {
-                return Err(OpossumError::OpticPort("output aperture not found".into()));
-            };
             let mut light_result = LightResult::default();
             light_result.insert(out_port.into(), LightData::Geometric(rays));
             Ok(light_result)
diff --git a/opossum/src/nodes/ray_propagation_visualizer.rs b/opossum/src/nodes/ray_propagation_visualizer.rs
index cef8299d..f7ef9cca 100644
--- a/opossum/src/nodes/ray_propagation_visualizer.rs
+++ b/opossum/src/nodes/ray_propagation_visualizer.rs
@@ -213,31 +213,31 @@ impl AnalysisRayTrace for RayPropagationVisualizer {
             if let Some(iso) = self.effective_iso() {
                 self.surface.set_isometry(&iso);
                 rays.refract_on_surface(&mut self.surface, None)?;
+                if let Some(aperture) = self.ports().aperture(&PortType::Input, inport) {
+                    let rays_apodized = rays.apodize(aperture, &iso)?;
+                    if rays_apodized {
+                        warn!("Rays have been apodized at input aperture of {}. Results might not be accurate.", self as &mut dyn OpticNode);
+                        self.apodization_warning = true;
+                    }
+                    rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+                } else {
+                    return Err(OpossumError::OpticPort("input aperture not found".into()));
+                };
+                self.light_data = Some(LightData::Geometric(rays.clone()));
+                if let Some(aperture) = self.ports().aperture(&PortType::Output, outport) {
+                    let rays_apodized = rays.apodize(aperture, &iso)?;
+                    if rays_apodized {
+                        warn!("Rays have been apodized at input aperture of {}. Results might not be accurate.", self as &mut dyn OpticNode);
+                    }
+                    rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+                } else {
+                    return Err(OpossumError::OpticPort("output aperture not found".into()));
+                };
             } else {
                 return Err(OpossumError::Analysis(
                     "no location for surface defined. Aborting".into(),
                 ));
             }
-            if let Some(aperture) = self.ports().aperture(&PortType::Input, inport) {
-                let rays_apodized = rays.apodize(aperture)?;
-                if rays_apodized {
-                    warn!("Rays have been apodized at input aperture of {}. Results might not be accurate.", self as &mut dyn OpticNode);
-                    self.apodization_warning = true;
-                }
-                rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-            } else {
-                return Err(OpossumError::OpticPort("input aperture not found".into()));
-            };
-            self.light_data = Some(LightData::Geometric(rays.clone()));
-            if let Some(aperture) = self.ports().aperture(&PortType::Output, outport) {
-                let rays_apodized = rays.apodize(aperture)?;
-                if rays_apodized {
-                    warn!("Rays have been apodized at input aperture of {}. Results might not be accurate.", self as &mut dyn OpticNode);
-                }
-                rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-            } else {
-                return Err(OpossumError::OpticPort("output aperture not found".into()));
-            };
             Ok(LightResult::from([(
                 outport.into(),
                 LightData::Geometric(rays),
diff --git a/opossum/src/nodes/reflective_grating.rs b/opossum/src/nodes/reflective_grating.rs
index 4579db76..4cd18960 100644
--- a/opossum/src/nodes/reflective_grating.rs
+++ b/opossum/src/nodes/reflective_grating.rs
@@ -220,7 +220,7 @@ impl AnalysisRayTrace for ReflectiveGrating {
                 )?;
 
                 if let Some(aperture) = self.ports().aperture(&PortType::Input, inport) {
-                    diffracted_rays.apodize(aperture)?;
+                    diffracted_rays.apodize(aperture, &iso)?;
                     diffracted_rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
                     diffracted_rays
                 } else {
diff --git a/opossum/src/nodes/source.rs b/opossum/src/nodes/source.rs
index af85f8da..d35ccf5a 100644
--- a/opossum/src/nodes/source.rs
+++ b/opossum/src/nodes/source.rs
@@ -210,15 +210,17 @@ impl AnalysisRayTrace for Source {
             if let LightData::Geometric(rays) = &mut data {
                 if let Some(iso) = self.effective_iso() {
                     *rays = rays.transformed_rays(&iso);
-                }
-                // consider aperture only if not inverted (there is only an output port)
-                if !self.inverted() {
-                    if let Some(aperture) = self.ports().aperture(&PortType::Output, "out1") {
-                        rays.apodize(aperture)?;
-                        rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-                    } else {
-                        return Err(OpossumError::OpticPort("input aperture not found".into()));
-                    };
+                    // consider aperture only if not inverted (there is only an output port)
+                    if !self.inverted() {
+                        if let Some(aperture) = self.ports().aperture(&PortType::Output, "out1") {
+                            rays.apodize(aperture, &iso)?;
+                            rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+                        } else {
+                            return Err(OpossumError::OpticPort(
+                                "output aperture not found".into(),
+                            ));
+                        };
+                    }
                 }
             }
             Ok(LightResult::from([("out1".into(), data)]))
diff --git a/opossum/src/nodes/spectrometer.rs b/opossum/src/nodes/spectrometer.rs
index 91c8608b..2b4b5378 100644
--- a/opossum/src/nodes/spectrometer.rs
+++ b/opossum/src/nodes/spectrometer.rs
@@ -281,28 +281,28 @@ impl AnalysisRayTrace for Spectrometer {
             if let Some(iso) = self.effective_iso() {
                 self.surface.set_isometry(&iso);
                 rays.refract_on_surface(&mut self.surface, None)?;
+                if let Some(aperture) = self.ports().aperture(&PortType::Input, inport) {
+                    let rays_apodized = rays.apodize(aperture, &iso)?;
+                    if rays_apodized {
+                        warn!("Rays have been apodized at input aperture of {}. Results might not be accurate.", self as &mut dyn OpticNode);
+                        self.apodization_warning = true;
+                    }
+                    rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+                } else {
+                    return Err(OpossumError::OpticPort("input aperture not found".into()));
+                };
+                self.light_data = Some(LightData::Geometric(rays.clone()));
+                if let Some(aperture) = self.ports().aperture(&PortType::Output, outport) {
+                    rays.apodize(aperture, &iso)?;
+                    rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+                } else {
+                    return Err(OpossumError::OpticPort("output aperture not found".into()));
+                };
             } else {
                 return Err(OpossumError::Analysis(
                     "no location for surface defined. Aborting".into(),
                 ));
             }
-            if let Some(aperture) = self.ports().aperture(&PortType::Input, inport) {
-                let rays_apodized = rays.apodize(aperture)?;
-                if rays_apodized {
-                    warn!("Rays have been apodized at input aperture of {}. Results might not be accurate.", self as &mut dyn OpticNode);
-                    self.apodization_warning = true;
-                }
-                rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-            } else {
-                return Err(OpossumError::OpticPort("input aperture not found".into()));
-            };
-            self.light_data = Some(LightData::Geometric(rays.clone()));
-            if let Some(aperture) = self.ports().aperture(&PortType::Output, outport) {
-                rays.apodize(aperture)?;
-                rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-            } else {
-                return Err(OpossumError::OpticPort("output aperture not found".into()));
-            };
             Ok(LightResult::from([(
                 outport.into(),
                 LightData::Geometric(rays),
diff --git a/opossum/src/nodes/spot_diagram.rs b/opossum/src/nodes/spot_diagram.rs
index 398cfaec..3fcbeeda 100644
--- a/opossum/src/nodes/spot_diagram.rs
+++ b/opossum/src/nodes/spot_diagram.rs
@@ -259,34 +259,34 @@ impl AnalysisRayTrace for SpotDiagram {
             if let Some(iso) = self.effective_iso() {
                 let mut plane = OpticalSurface::new(Box::new(Plane::new(&iso)));
                 rays.refract_on_surface(&mut plane, None)?;
+                if let Some(aperture) = self.ports().aperture(&PortType::Input, "in1") {
+                    let rays_apodized = rays.apodize(aperture, &iso)?;
+                    if rays_apodized {
+                        warn!("Rays have been apodized at input aperture of {}. Results might not be accurate.", self as &mut dyn OpticNode);
+                        self.apodization_warning = true;
+                    }
+                    rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+                } else {
+                    return Err(OpossumError::OpticPort("input aperture not found".into()));
+                };
+                if let Some(LightData::Geometric(old_rays)) = &self.light_data {
+                    let mut rays_tob_merged = old_rays.clone();
+                    rays_tob_merged.merge(&rays);
+                    self.light_data = Some(LightData::Geometric(rays_tob_merged.clone()));
+                } else {
+                    self.light_data = Some(LightData::Geometric(rays.clone()));
+                }
+                if let Some(aperture) = self.ports().aperture(&PortType::Output, "out1") {
+                    rays.apodize(aperture, &iso)?;
+                    rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+                } else {
+                    return Err(OpossumError::OpticPort("output aperture not found".into()));
+                };
             } else {
                 return Err(OpossumError::Analysis(
                     "no location for surface defined. Aborting".into(),
                 ));
             }
-            if let Some(aperture) = self.ports().aperture(&PortType::Input, "in1") {
-                let rays_apodized = rays.apodize(aperture)?;
-                if rays_apodized {
-                    warn!("Rays have been apodized at input aperture of {}. Results might not be accurate.", self as &mut dyn OpticNode);
-                    self.apodization_warning = true;
-                }
-                rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-            } else {
-                return Err(OpossumError::OpticPort("input aperture not found".into()));
-            };
-            if let Some(LightData::Geometric(old_rays)) = &self.light_data {
-                let mut rays_tob_merged = old_rays.clone();
-                rays_tob_merged.merge(&rays);
-                self.light_data = Some(LightData::Geometric(rays_tob_merged.clone()));
-            } else {
-                self.light_data = Some(LightData::Geometric(rays.clone()));
-            }
-            if let Some(aperture) = self.ports().aperture(&PortType::Output, "out1") {
-                rays.apodize(aperture)?;
-                rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-            } else {
-                return Err(OpossumError::OpticPort("output aperture not found".into()));
-            };
             Ok(LightResult::from([(
                 outport.into(),
                 LightData::Geometric(rays),
diff --git a/opossum/src/nodes/thin_mirror.rs b/opossum/src/nodes/thin_mirror.rs
index f9f42b65..d2bd8037 100644
--- a/opossum/src/nodes/thin_mirror.rs
+++ b/opossum/src/nodes/thin_mirror.rs
@@ -181,7 +181,7 @@ impl AnalysisRayTrace for ThinMirror {
                 );
                 let mut reflected_rays = rays.refract_on_surface(&mut surface, None)?;
                 if let Some(aperture) = self.ports().aperture(&PortType::Input, inport) {
-                    reflected_rays.apodize(aperture)?;
+                    reflected_rays.apodize(aperture, &iso)?;
                     reflected_rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
                     reflected_rays
                 } else {
diff --git a/opossum/src/nodes/wavefront.rs b/opossum/src/nodes/wavefront.rs
index dc62f47d..adc2f545 100644
--- a/opossum/src/nodes/wavefront.rs
+++ b/opossum/src/nodes/wavefront.rs
@@ -320,28 +320,28 @@ impl AnalysisRayTrace for WaveFront {
             if let Some(iso) = self.effective_iso() {
                 self.surface.set_isometry(&iso);
                 rays.refract_on_surface(&mut self.surface, None)?;
+                if let Some(aperture) = self.ports().aperture(&PortType::Input, inport) {
+                    let rays_apodized = rays.apodize(aperture, &iso)?;
+                    if rays_apodized {
+                        warn!("Rays have been apodized at input aperture of {}. Results might not be accurate.", self as &mut dyn OpticNode);
+                        self.apodization_warning = true;
+                    }
+                    rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+                } else {
+                    return Err(OpossumError::OpticPort("input aperture not found".into()));
+                };
+                self.light_data = Some(LightData::Geometric(rays.clone()));
+                if let Some(aperture) = self.ports().aperture(&PortType::Output, outport) {
+                    rays.apodize(aperture, &iso)?;
+                    rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
+                } else {
+                    return Err(OpossumError::OpticPort("output aperture not found".into()));
+                };
             } else {
                 return Err(OpossumError::Analysis(
                     "no location for surface defined. Aborting".into(),
                 ));
             }
-            if let Some(aperture) = self.ports().aperture(&PortType::Input, inport) {
-                let rays_apodized = rays.apodize(aperture)?;
-                if rays_apodized {
-                    warn!("Rays have been apodized at input aperture of {}. Results might not be accurate.", self as &mut dyn OpticNode);
-                    self.apodization_warning = true;
-                }
-                rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-            } else {
-                return Err(OpossumError::OpticPort("input aperture not found".into()));
-            };
-            self.light_data = Some(LightData::Geometric(rays.clone()));
-            if let Some(aperture) = self.ports().aperture(&PortType::Output, outport) {
-                rays.apodize(aperture)?;
-                rays.invalidate_by_threshold_energy(config.min_energy_per_ray())?;
-            } else {
-                return Err(OpossumError::OpticPort("output aperture not found".into()));
-            };
             Ok(LightResult::from([(
                 outport.into(),
                 LightData::Geometric(rays),
diff --git a/opossum/src/rays.rs b/opossum/src/rays.rs
index a32e998c..33f2c4d8 100644
--- a/opossum/src/rays.rs
+++ b/opossum/src/rays.rs
@@ -355,11 +355,12 @@ impl Rays {
     /// # Errors
     ///
     /// This function returns an error if a single ray cannot be properly apodized (e.g. filter factor outside (0.0..=1.0)).
-    pub fn apodize(&mut self, aperture: &Aperture) -> OpmResult<bool> {
+    pub fn apodize(&mut self, aperture: &Aperture, iso: &Isometry) -> OpmResult<bool> {
         let mut beams_invalided = false;
         for ray in &mut self.rays {
             if ray.valid() {
-                let ap_factor = aperture.apodization_factor(&ray.position().xy());
+                let ap_factor =
+                    aperture.apodization_factor(&ray.inverse_transformed_ray(iso).position().xy());
                 if ap_factor > 0.0 {
                     ray.filter_energy(&FilterType::Constant(ap_factor))?;
                 } else {
@@ -1908,7 +1909,7 @@ mod test {
         assert_eq!(rays.total_energy(), joule!(2.0));
         let circle_config = CircleConfig::new(millimeter!(0.5), millimeter!(0.0, 0.0)).unwrap();
         let aperture = Aperture::BinaryCircle(circle_config);
-        rays.apodize(&aperture).unwrap();
+        rays.apodize(&aperture, &Isometry::identity()).unwrap();
         assert_eq!(rays.total_energy(), joule!(1.0));
     }
     #[test]
-- 
GitLab