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(&parabola_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(&parabola_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(&degree!(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