diff --git a/opossum/examples/beam_combine_test.rs b/opossum/examples/beam_combine_test.rs
index 03389495c4d63e5259ba1cb2fa7ef4dd224fb63b..88c10990385122f848639d48a0383c7bf01e8a99 100644
--- a/opossum/examples/beam_combine_test.rs
+++ b/opossum/examples/beam_combine_test.rs
@@ -16,19 +16,19 @@ use uom::si::f64::Length;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::new("beam combiner demo");
-    let i_s1 = scenery.add_node(Source::new(
+    let i_s1 = scenery.add_node(&Source::new(
         "Source 1",
         &LightData::Energy(DataEnergy {
             spectrum: create_he_ne_spec(1.0).unwrap(),
         }),
     ))?;
-    let i_s2 = scenery.add_node(Source::new(
+    let i_s2 = scenery.add_node(&Source::new(
         "Source 2",
         &LightData::Energy(DataEnergy {
             spectrum: create_nd_glass_spec(1.0)?,
         }),
     ))?;
-    let i_bs = scenery.add_node(BeamSplitter::new("bs", &SplittingConfig::Ratio(0.5)).unwrap())?;
+    let i_bs = scenery.add_node(&BeamSplitter::new("bs", &SplittingConfig::Ratio(0.5)).unwrap())?;
     let filter_spectrum = generate_filter_spectrum(
         nanometer!(400.0)..nanometer!(1100.0),
         nanometer!(1.0),
@@ -36,11 +36,11 @@ fn main() -> OpmResult<()> {
             cut_off: nanometer!(700.0),
         },
     )?;
-    let i_f = scenery.add_node(IdealFilter::new(
+    let i_f = scenery.add_node(&IdealFilter::new(
         "filter",
         &FilterType::Spectrum(filter_spectrum),
     )?)?;
-    let i_d1 = scenery.add_node(Detector::default())?; // Detector 1
+    let i_d1 = scenery.add_node(&Detector::default())?; // Detector 1
 
     scenery.connect_nodes(i_s1, "out1", i_bs, "input1", Length::zero())?;
     scenery.connect_nodes(i_s2, "out1", i_bs, "input2", Length::zero())?;
diff --git a/opossum/examples/cylindric_lens_test.rs b/opossum/examples/cylindric_lens_test.rs
index 7017b5a83ca708bac50a68ff07151c5f245cc4cb..6cc0bb4fe0c953d615408a1d50eaf00d87a6a4cd 100644
--- a/opossum/examples/cylindric_lens_test.rs
+++ b/opossum/examples/cylindric_lens_test.rs
@@ -16,7 +16,7 @@ use std::path::Path;
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::default();
 
-    let src = scenery.add_node(round_collimated_ray_source(
+    let src = scenery.add_node(&round_collimated_ray_source(
         millimeter!(20.0),
         joule!(1.0),
         3,
@@ -29,9 +29,9 @@ fn main() -> OpmResult<()> {
         &RefrIndexConst::new(1.5068)?,
     )?
     .with_tilt(degree!(0.0, 0.0, 45.0))?;
-    let l1 = scenery.add_node(lens)?;
-    let det = scenery.add_node(RayPropagationVisualizer::default())?;
-    let det2 = scenery.add_node(SpotDiagram::default())?;
+    let l1 = scenery.add_node(&lens)?;
+    let det = scenery.add_node(&RayPropagationVisualizer::default())?;
+    let det2 = scenery.add_node(&SpotDiagram::default())?;
     scenery.connect_nodes(src, "out1", l1, "front", millimeter!(50.0))?;
     scenery.connect_nodes(l1, "rear", det, "in1", millimeter!(100.0))?;
     scenery.connect_nodes(det, "out1", det2, "in1", millimeter!(0.0))?;
diff --git a/opossum/examples/filter_test.rs b/opossum/examples/filter_test.rs
index 3971eba70c7c0aa353b441d14bf7a3cd60de001d..fc0e0d22e6288f2bb24c26fe2d14fb54b1f4c644 100644
--- a/opossum/examples/filter_test.rs
+++ b/opossum/examples/filter_test.rs
@@ -15,24 +15,24 @@ use uom::si::f64::Length;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::new("filter system demo");
-    let i_s = scenery.add_node(Source::new(
+    let i_s = scenery.add_node(&Source::new(
         "Source",
         &LightData::Energy(DataEnergy {
             spectrum: create_he_ne_spec(1.0)?,
         }),
     ))?;
-    let i_bs = scenery.add_node(BeamSplitter::new("bs", &SplittingConfig::Ratio(0.6)).unwrap())?;
+    let i_bs = scenery.add_node(&BeamSplitter::new("bs", &SplittingConfig::Ratio(0.6)).unwrap())?;
     let filter_spectrum = Spectrum::from_csv("./opossum/opossum/NE03B.csv")?;
-    let i_f = scenery.add_node(IdealFilter::new(
+    let i_f = scenery.add_node(&IdealFilter::new(
         "filter",
         &FilterType::Spectrum(filter_spectrum),
     )?)?;
-    let i_d1 = scenery.add_node(EnergyMeter::new(
+    let i_d1 = scenery.add_node(&EnergyMeter::new(
         "Energy meter 1",
         opossum::nodes::Metertype::IdealEnergyMeter,
     ))?;
-    let i_d2 = scenery.add_node(Spectrometer::default())?;
-    let i_d3 = scenery.add_node(EnergyMeter::new(
+    let i_d2 = scenery.add_node(&Spectrometer::default())?;
+    let i_d3 = scenery.add_node(&EnergyMeter::new(
         "Energy meter 2",
         opossum::nodes::Metertype::IdealEnergyMeter,
     ))?;
diff --git a/opossum/examples/folded_telescope.rs b/opossum/examples/folded_telescope.rs
index d542253cddfd6d30bce7d012e7d14f830370bb69..4c68f65074251dafaa2f3b448b4108467e30fd56 100644
--- a/opossum/examples/folded_telescope.rs
+++ b/opossum/examples/folded_telescope.rs
@@ -49,10 +49,10 @@ pub fn main() -> OpmResult<()> {
     src.set_alignment_wavelength(alignment_wvl)?;
     src.set_isometry(Isometry::identity());
 
-    let i_src = scenery.add_node(src)?;
+    let i_src = scenery.add_node(&src)?;
     // focal length = 996.7 mm (Thorlabs LA1779-B)
     let lens1 = scenery.add_node(
-        Lens::new(
+        &Lens::new(
             "Lens 1",
             millimeter!(515.1),
             millimeter!(f64::INFINITY),
@@ -63,12 +63,12 @@ pub fn main() -> OpmResult<()> {
     )?;
 
     let mir_1 = ThinMirror::new("mirr").align_like_node_at_distance(lens1, millimeter!(996.7));
-    let mir_1 = scenery.add_node(mir_1)?;
+    let mir_1 = scenery.add_node(&mir_1)?;
     let mut lens_1_ref = NodeReference::from_node(&scenery.node(lens1)?);
     lens_1_ref.set_inverted(true)?;
-    let lens_1_ref = scenery.add_node(lens_1_ref)?;
+    let lens_1_ref = scenery.add_node(&lens_1_ref)?;
 
-    let i_prop_vis = scenery.add_node(RayPropagationVisualizer::new(
+    let i_prop_vis = scenery.add_node(&RayPropagationVisualizer::new(
         "Ray_positions",
         Some(Vector3::y()),
     )?)?;
diff --git a/opossum/examples/fresnel_coating.rs b/opossum/examples/fresnel_coating.rs
index 65e1ae31fd3501e41487b7d361f45a57fe40cac0..21e7862b57b4225a6405fe0c036dd3ebbea38fe5 100644
--- a/opossum/examples/fresnel_coating.rs
+++ b/opossum/examples/fresnel_coating.rs
@@ -26,8 +26,8 @@ fn main() -> OpmResult<()> {
     )?;
     let mut source = Source::new("src", &LightData::Geometric(rays));
     source.set_isometry(Isometry::identity());
-    let src = scenery.add_node(source)?;
-    let fd1 = scenery.add_node(FluenceDetector::new("before lens"))?;
+    let src = scenery.add_node(&source)?;
+    let fd1 = scenery.add_node(&FluenceDetector::new("before lens"))?;
 
     let mut lens1 = Lens::new(
         "Lens",
@@ -37,10 +37,10 @@ fn main() -> OpmResult<()> {
         &RefrIndexConst::new(1.5)?,
     )?;
     lens1.set_coating(&PortType::Input, "front", &CoatingType::Fresnel)?;
-    let l1 = scenery.add_node(lens1)?;
-    let fd2 = scenery.add_node(FluenceDetector::new("after lens"))?;
-    let ed = scenery.add_node(EnergyMeter::default())?;
-    let det = scenery.add_node(RayPropagationVisualizer::default())?;
+    let l1 = scenery.add_node(&lens1)?;
+    let fd2 = scenery.add_node(&FluenceDetector::new("after lens"))?;
+    let ed = scenery.add_node(&EnergyMeter::default())?;
+    let det = scenery.add_node(&RayPropagationVisualizer::default())?;
 
     scenery.connect_nodes(src, "out1", fd1, "in1", millimeter!(10.0))?;
     scenery.connect_nodes(fd1, "out1", l1, "front", millimeter!(1.0))?;
diff --git a/opossum/examples/ghost_focus.rs b/opossum/examples/ghost_focus.rs
index 510cb76b706726a24bd08e02b77ca2b4f76a876a..34ea7df12789992df3158d32d7e835c6f94460a0 100644
--- a/opossum/examples/ghost_focus.rs
+++ b/opossum/examples/ghost_focus.rs
@@ -1,10 +1,13 @@
 use opossum::{
-    analyzers::{AnalyzerType, GhostFocusConfig},
+    analyzers::{AnalyzerType, GhostFocusConfig, RayTraceConfig},
     coatings::CoatingType,
     degree,
     error::OpmResult,
     joule, millimeter,
-    nodes::{round_collimated_ray_source, Lens, NodeGroup, SpotDiagram, Wedge},
+    nodes::{
+        collimated_line_ray_source, round_collimated_ray_source, Lens, NodeGroup, SpotDiagram,
+        Wedge,
+    },
     optic_node::{Alignable, OpticNode},
     optic_ports::PortType,
     refractive_index::RefrIndexConst,
@@ -16,15 +19,14 @@ fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::default();
     let i_src = scenery.add_node(
         // collimated_line_ray_source(millimeter!(50.), joule!(1.), 100)?
-        round_collimated_ray_source(millimeter!(50.0), joule!(1.0), 7)?,
+        &round_collimated_ray_source(millimeter!(50.0), joule!(1.0), 7)?,
     )?;
-    let i_sd = scenery.add_node(SpotDiagram::default())?;
+    let i_sd = scenery.add_node(&SpotDiagram::default())?;
 
     let mut lens = Lens::default();
-
     lens.set_coating(&PortType::Input, "front", &CoatingType::Fresnel)?;
     lens.set_coating(&PortType::Output, "rear", &CoatingType::Fresnel)?;
-    let i_l = scenery.add_node(lens)?;
+    let i_l = scenery.add_node(&lens)?;
 
     let mut wedge = Wedge::new(
         "wedge",
@@ -35,9 +37,9 @@ fn main() -> OpmResult<()> {
     .with_tilt(degree!(5.0, 0.0, 0.0))?;
     wedge.set_coating(&PortType::Input, "front", &CoatingType::Fresnel)?;
     wedge.set_coating(&PortType::Output, "rear", &CoatingType::Fresnel)?;
-    let i_w = scenery.add_node(wedge)?;
+    let i_w = scenery.add_node(&wedge)?;
 
-    let i_sd2 = scenery.add_node(SpotDiagram::default())?;
+    let i_sd2 = scenery.add_node(&SpotDiagram::default())?;
     scenery.connect_nodes(i_src, "out1", i_sd, "in1", millimeter!(20.0))?;
     scenery.connect_nodes(i_sd, "out1", i_l, "front", millimeter!(80.0))?;
     scenery.connect_nodes(i_l, "rear", i_w, "front", millimeter!(70.0))?;
diff --git a/opossum/examples/grating_examples/detector_group.rs b/opossum/examples/grating_examples/detector_group.rs
index e1b68ec9552d1fb56b9622486064f27b13038a2e..a6510bf0c2c1d90b0e7c24326cc8cdf1784c9092 100644
--- a/opossum/examples/grating_examples/detector_group.rs
+++ b/opossum/examples/grating_examples/detector_group.rs
@@ -8,21 +8,21 @@ use opossum::{
 pub fn detector_group() -> OpmResult<NodeGroup> {
     let mut cb = NodeGroup::new("Detector Group");
 
-    let i_prop_vis_top_view = cb.add_node(RayPropagationVisualizer::new(
+    let i_prop_vis_top_view = cb.add_node(&RayPropagationVisualizer::new(
         "Ray_positions_top",
         Some(Vector3::y()),
     )?)?;
-    let i_prop_vis_side_view = cb.add_node(RayPropagationVisualizer::new(
+    let i_prop_vis_side_view = cb.add_node(&RayPropagationVisualizer::new(
         "Ray_positions_side",
         Some(Vector3::x()),
     )?)?;
-    let paraxial_lens = cb.add_node(ParaxialSurface::new("ideal lens", millimeter!(500.))?)?;
+    let paraxial_lens = cb.add_node(&ParaxialSurface::new("ideal lens", millimeter!(500.))?)?;
     let spot_monitor = SpotDiagram::new("spot diagram");
     // let rect_config = RectangleConfig::new(millimeter!(150.), millimeter!(150.), micrometer!(0.,0.))?;
     // let aperture = Aperture::BinaryRectangle(rect_config);
     // spot_monitor.set_aperture(&PortType::Input, "in1", &aperture)?;
     // spot_monitor.set_property("plot_aperture", true.into())?;
-    let spot_diag = cb.add_node(spot_monitor)?;
+    let spot_diag = cb.add_node(&spot_monitor)?;
 
     cb.connect_nodes(paraxial_lens, "rear", spot_diag, "in1", millimeter!(500.0))?;
     cb.connect_nodes(
@@ -40,7 +40,7 @@ pub fn detector_group() -> OpmResult<NodeGroup> {
         millimeter!(0.0),
     )?;
 
-    cb.add_node(RayPropagationVisualizer::new(
+    cb.add_node(&RayPropagationVisualizer::new(
         "stale visualizer",
         Some(Vector3::x()),
     )?)?;
diff --git a/opossum/examples/grating_examples/folded_martinez.rs b/opossum/examples/grating_examples/folded_martinez.rs
index 84cd1b07a349d7618a7b27247e1d540ff73f49bb..0472fb58bc5e275332d88b725dcab13bb9b5e310 100644
--- a/opossum/examples/grating_examples/folded_martinez.rs
+++ b/opossum/examples/grating_examples/folded_martinez.rs
@@ -20,12 +20,12 @@ pub fn folded_martinez(
     let mut cb = NodeGroup::new("Martinez stretcher");
 
     let i_g1 = cb.add_node(
-        ReflectiveGrating::new("grating 1", num_per_mm!(1740.), -1)?
+        &ReflectiveGrating::new("grating 1", num_per_mm!(1740.), -1)?
             .with_rot_from_littrow(alignment_wvl, degree!(-4.))?,
     )?;
     // focal length = 996.7 mm (Thorlabs LA1779-B)
     let lens1 = cb.add_node(
-        Lens::new(
+        &Lens::new(
             "Lens 1",
             millimeter!(515.1),
             millimeter!(f64::INFINITY),
@@ -35,24 +35,25 @@ pub fn folded_martinez(
         .with_decenter(centimeter!(0., 0., 0.))?,
     )?;
 
-    let mir_1 = cb
-        .add_node(ThinMirror::new("mirr").align_like_node_at_distance(lens1, telescope_distance))?;
-    let mir_1_ref = cb.add_node(NodeReference::from_node(&cb.node(mir_1)?))?;
+    let mir_1 = cb.add_node(
+        &ThinMirror::new("mirr").align_like_node_at_distance(lens1, telescope_distance),
+    )?;
+    let mir_1_ref = cb.add_node(&NodeReference::from_node(&cb.node(mir_1)?))?;
     let mut lens_1_ref1 = NodeReference::from_node(&cb.node(lens1)?);
     lens_1_ref1.set_inverted(true)?;
-    let lens_1_ref1 = cb.add_node(lens_1_ref1)?;
-    let lens_1_ref2 = cb.add_node(NodeReference::from_node(&cb.node(lens1)?))?;
+    let lens_1_ref1 = cb.add_node(&lens_1_ref1)?;
+    let lens_1_ref2 = cb.add_node(&NodeReference::from_node(&cb.node(lens1)?))?;
     let mut lens_1_ref3 = NodeReference::from_node(&cb.node(lens1)?);
     lens_1_ref3.set_inverted(true)?;
-    let lens_1_ref3 = cb.add_node(lens_1_ref3)?;
-    let g1ref1 = cb.add_node(NodeReference::from_node(&cb.node(i_g1)?))?;
-    let g1ref2 = cb.add_node(NodeReference::from_node(&cb.node(i_g1)?))?;
-    let g1ref3 = cb.add_node(NodeReference::from_node(&cb.node(i_g1)?))?;
-    let retro_mir1 = cb.add_node(ThinMirror::new("retro_mir1"))?;
+    let lens_1_ref3 = cb.add_node(&lens_1_ref3)?;
+    let g1ref1 = cb.add_node(&NodeReference::from_node(&cb.node(i_g1)?))?;
+    let g1ref2 = cb.add_node(&NodeReference::from_node(&cb.node(i_g1)?))?;
+    let g1ref3 = cb.add_node(&NodeReference::from_node(&cb.node(i_g1)?))?;
+    let retro_mir1 = cb.add_node(&ThinMirror::new("retro_mir1"))?;
     // let retro_mir1 =
-    //     cb.add_node(ThinMirror::new("retro_mir1").with_tilt(degree!(-45., 0., 0.))?)?;
+    //     cb.add_node(&ThinMirror::new("retro_mir1").with_tilt(degree!(-45., 0., 0.))?)?;
     // let retro_mir2 =
-    //     cb.add_node(ThinMirror::new("retro_mir2").with_tilt(degree!(-45., 0., 0.))?)?;
+    //     cb.add_node(&ThinMirror::new("retro_mir2").with_tilt(degree!(-45., 0., 0.))?)?;
 
     //first grating pass up to 0° mirror
     cb.connect_nodes(
diff --git a/opossum/examples/grating_examples/folded_martinez_longer_f.rs b/opossum/examples/grating_examples/folded_martinez_longer_f.rs
index 2231aa935834bbf518c803f22425f69c32863072..4a557f4fd751ef363a9b6cffe8ab562335301997 100644
--- a/opossum/examples/grating_examples/folded_martinez_longer_f.rs
+++ b/opossum/examples/grating_examples/folded_martinez_longer_f.rs
@@ -20,12 +20,12 @@ pub fn folded_martinez_longer_f(
     let mut cb = NodeGroup::new("Martinez stretcher");
 
     let i_g1 = cb.add_node(
-        ReflectiveGrating::new("grating 1", num_per_mm!(1740.), -1)?
+        &ReflectiveGrating::new("grating 1", num_per_mm!(1740.), -1)?
             .with_rot_from_littrow(alignment_wvl, degree!(-4.))?,
     )?;
     // focal length = 996.7 mm (Thorlabs LA1779-B)
     let lens1 = cb.add_node(
-        Lens::new(
+        &Lens::new(
             "Lens 1",
             millimeter!(1250.),
             millimeter!(f64::INFINITY),
@@ -35,24 +35,25 @@ pub fn folded_martinez_longer_f(
         .with_decenter(centimeter!(0., 0., 0.))?,
     )?;
 
-    let mir_1 = cb
-        .add_node(ThinMirror::new("mirr").align_like_node_at_distance(lens1, telescope_distance))?;
-    let mir_1_ref = cb.add_node(NodeReference::from_node(&cb.node(mir_1)?))?;
+    let mir_1 = cb.add_node(
+        &ThinMirror::new("mirr").align_like_node_at_distance(lens1, telescope_distance),
+    )?;
+    let mir_1_ref = cb.add_node(&NodeReference::from_node(&cb.node(mir_1)?))?;
     let mut lens_1_ref1 = NodeReference::from_node(&cb.node(lens1)?);
     lens_1_ref1.set_inverted(true)?;
-    let lens_1_ref1 = cb.add_node(lens_1_ref1)?;
-    let lens_1_ref2 = cb.add_node(NodeReference::from_node(&cb.node(lens1)?))?;
+    let lens_1_ref1 = cb.add_node(&lens_1_ref1)?;
+    let lens_1_ref2 = cb.add_node(&NodeReference::from_node(&cb.node(lens1)?))?;
     let mut lens_1_ref3 = NodeReference::from_node(&cb.node(lens1)?);
     lens_1_ref3.set_inverted(true)?;
-    let lens_1_ref3 = cb.add_node(lens_1_ref3)?;
-    let g1ref1 = cb.add_node(NodeReference::from_node(&cb.node(i_g1)?))?;
-    let g1ref2 = cb.add_node(NodeReference::from_node(&cb.node(i_g1)?))?;
-    let g1ref3 = cb.add_node(NodeReference::from_node(&cb.node(i_g1)?))?;
-    let retro_mir1 = cb.add_node(ThinMirror::new("retro_mir1"))?;
+    let lens_1_ref3 = cb.add_node(&lens_1_ref3)?;
+    let g1ref1 = cb.add_node(&NodeReference::from_node(&cb.node(i_g1)?))?;
+    let g1ref2 = cb.add_node(&NodeReference::from_node(&cb.node(i_g1)?))?;
+    let g1ref3 = cb.add_node(&NodeReference::from_node(&cb.node(i_g1)?))?;
+    let retro_mir1 = cb.add_node(&ThinMirror::new("retro_mir1"))?;
     // let retro_mir1 =
-    //     cb.add_node(ThinMirror::new("retro_mir1").with_tilt(degree!(-45., 0., 0.))?)?;
+    //     cb.add_node(&ThinMirror::new("retro_mir1").with_tilt(degree!(-45., 0., 0.))?)?;
     // let retro_mir2 =
-    //     cb.add_node(ThinMirror::new("retro_mir2").with_tilt(degree!(-45., 0., 0.))?)?;
+    //     cb.add_node(&ThinMirror::new("retro_mir2").with_tilt(degree!(-45., 0., 0.))?)?;
 
     //first grating pass up to 0° mirror
     cb.connect_nodes(
diff --git a/opossum/examples/grating_examples/folded_martinez_paraxial_lens.rs b/opossum/examples/grating_examples/folded_martinez_paraxial_lens.rs
index 9856641ac18431f61a90a773a182f32cd6b932a9..341a0389db58a80c6db41216a0aaecdc5cd900c4 100644
--- a/opossum/examples/grating_examples/folded_martinez_paraxial_lens.rs
+++ b/opossum/examples/grating_examples/folded_martinez_paraxial_lens.rs
@@ -18,32 +18,33 @@ pub fn folded_martinez_paraxial_lens(
     let mut cb = NodeGroup::new("Martinez stretcher");
 
     let i_g1 = cb.add_node(
-        ReflectiveGrating::new("grating 1", num_per_mm!(1740.), -1)?
+        &ReflectiveGrating::new("grating 1", num_per_mm!(1740.), -1)?
             .with_rot_from_littrow(alignment_wvl, degree!(-4.))?,
     )?;
     // focal length = 996.7 mm (Thorlabs LA1779-B)
     let lens1 = cb.add_node(
-        ParaxialSurface::new("paraxial lens", telescope_distance)?
+        &ParaxialSurface::new("paraxial lens", telescope_distance)?
             .with_decenter(centimeter!(0., 1., 0.))?,
     )?;
 
-    let mir_1 = cb
-        .add_node(ThinMirror::new("mirr").align_like_node_at_distance(lens1, telescope_distance))?;
-    let mir_1_ref = cb.add_node(NodeReference::from_node(&cb.node(mir_1)?))?;
+    let mir_1 = cb.add_node(
+        &ThinMirror::new("mirr").align_like_node_at_distance(lens1, telescope_distance),
+    )?;
+    let mir_1_ref = cb.add_node(&NodeReference::from_node(&cb.node(mir_1)?))?;
     let mut lens_1_ref1 = NodeReference::from_node(&cb.node(lens1)?);
     lens_1_ref1.set_inverted(true)?;
-    let lens_1_ref1 = cb.add_node(lens_1_ref1)?;
-    let lens_1_ref2 = cb.add_node(NodeReference::from_node(&cb.node(lens1)?))?;
+    let lens_1_ref1 = cb.add_node(&lens_1_ref1)?;
+    let lens_1_ref2 = cb.add_node(&NodeReference::from_node(&cb.node(lens1)?))?;
     let mut lens_1_ref3 = NodeReference::from_node(&cb.node(lens1)?);
     lens_1_ref3.set_inverted(true)?;
-    let lens_1_ref3 = cb.add_node(lens_1_ref3)?;
-    let g1ref1 = cb.add_node(NodeReference::from_node(&cb.node(i_g1)?))?;
-    let g1ref2 = cb.add_node(NodeReference::from_node(&cb.node(i_g1)?))?;
-    let g1ref3 = cb.add_node(NodeReference::from_node(&cb.node(i_g1)?))?;
+    let lens_1_ref3 = cb.add_node(&lens_1_ref3)?;
+    let g1ref1 = cb.add_node(&NodeReference::from_node(&cb.node(i_g1)?))?;
+    let g1ref2 = cb.add_node(&NodeReference::from_node(&cb.node(i_g1)?))?;
+    let g1ref3 = cb.add_node(&NodeReference::from_node(&cb.node(i_g1)?))?;
     let retro_mir1 =
-        cb.add_node(ThinMirror::new("retro_mir1").with_tilt(degree!(-45., 0., 0.))?)?;
+        cb.add_node(&ThinMirror::new("retro_mir1").with_tilt(degree!(-45., 0., 0.))?)?;
     let retro_mir2 =
-        cb.add_node(ThinMirror::new("retro_mir2").with_tilt(degree!(-45., 0., 0.))?)?;
+        cb.add_node(&ThinMirror::new("retro_mir2").with_tilt(degree!(-45., 0., 0.))?)?;
 
     //first grating pass up to 0° mirror
     cb.connect_nodes(i_g1, "diffracted", lens1, "front", millimeter!(800.))?;
diff --git a/opossum/examples/grating_examples/grating_examples.rs b/opossum/examples/grating_examples/grating_examples.rs
index 053e3d8d3b63ff2440f50d0be4983eb624e93899..0475fc14317e44492d88d4022c2214ceb955dd40 100644
--- a/opossum/examples/grating_examples/grating_examples.rs
+++ b/opossum/examples/grating_examples/grating_examples.rs
@@ -66,9 +66,9 @@ fn main() -> OpmResult<()> {
     ////////////////////////////////////
     let mut scenery = NodeGroup::new("treacy compressor");
 
-    let i_src = scenery.add_node(src.clone())?;
-    let compressor_node = scenery.add_node(treacy_compressor(alignment_wvl)?)?;
-    let detectors = scenery.add_node(detector_group()?)?;
+    let i_src = scenery.add_node(&src.clone())?;
+    let compressor_node = scenery.add_node(&treacy_compressor(alignment_wvl)?)?;
+    let detectors = scenery.add_node(&detector_group()?)?;
 
     scenery.connect_nodes(i_src, "out1", compressor_node, "input", millimeter!(400.0))?;
     scenery.connect_nodes(
@@ -91,10 +91,10 @@ fn main() -> OpmResult<()> {
     let telescope_distance = millimeter!(1015.515 * 0.995);
     let mut scenery = NodeGroup::new("non-ideal folded Martinez stretcher");
 
-    let i_src = scenery.add_node(src.clone())?;
+    let i_src = scenery.add_node(&src.clone())?;
     let stretcher_node =
-        scenery.add_node(folded_martinez(telescope_distance, &nbk7, alignment_wvl)?)?;
-    let detectors = scenery.add_node(detector_group()?)?;
+        scenery.add_node(&folded_martinez(telescope_distance, &nbk7, alignment_wvl)?)?;
+    let detectors = scenery.add_node(&detector_group()?)?;
 
     scenery.connect_nodes(i_src, "out1", stretcher_node, "input", millimeter!(400.0))?;
     scenery.connect_nodes(
@@ -119,10 +119,10 @@ fn main() -> OpmResult<()> {
     let telescope_distance = millimeter!(1017.14885);
     let mut scenery = NodeGroup::new("ideal folded Martinez stretcher");
 
-    let i_src = scenery.add_node(src.clone())?;
+    let i_src = scenery.add_node(&src.clone())?;
     let stretcher_node =
-        scenery.add_node(folded_martinez(telescope_distance, &nbk7, alignment_wvl)?)?;
-    let detectors = scenery.add_node(detector_group()?)?;
+        scenery.add_node(&folded_martinez(telescope_distance, &nbk7, alignment_wvl)?)?;
+    let detectors = scenery.add_node(&detector_group()?)?;
 
     scenery.connect_nodes(i_src, "out1", stretcher_node, "input", millimeter!(400.0))?;
     scenery.connect_nodes(
@@ -145,10 +145,10 @@ fn main() -> OpmResult<()> {
     let telescope_distance = millimeter!(1015.515);
     let mut scenery = NodeGroup::new("ideal folded Martinez stretcher circle of least conf");
 
-    let i_src = scenery.add_node(src.clone())?;
+    let i_src = scenery.add_node(&src.clone())?;
     let stretcher_node =
-        scenery.add_node(folded_martinez(telescope_distance, &nbk7, alignment_wvl)?)?;
-    let detectors = scenery.add_node(detector_group()?)?;
+        scenery.add_node(&folded_martinez(telescope_distance, &nbk7, alignment_wvl)?)?;
+    let detectors = scenery.add_node(&detector_group()?)?;
 
     scenery.connect_nodes(i_src, "out1", stretcher_node, "input", millimeter!(400.0))?;
     scenery.connect_nodes(
@@ -173,13 +173,13 @@ fn main() -> OpmResult<()> {
     let telescope_distance = millimeter!(2467.1);
     let mut scenery = NodeGroup::new("ideal folded Martinez stretcher longer f");
 
-    let i_src = scenery.add_node(src.clone())?;
-    let stretcher_node = scenery.add_node(folded_martinez_longer_f(
+    let i_src = scenery.add_node(&src.clone())?;
+    let stretcher_node = scenery.add_node(&folded_martinez_longer_f(
         telescope_distance,
         &nbk7,
         alignment_wvl,
     )?)?;
-    let detectors = scenery.add_node(detector_group()?)?;
+    let detectors = scenery.add_node(&detector_group()?)?;
 
     scenery.connect_nodes(i_src, "out1", stretcher_node, "input", millimeter!(400.0))?;
     scenery.connect_nodes(
@@ -204,13 +204,13 @@ fn main() -> OpmResult<()> {
     let telescope_distance = millimeter!(1017.14885);
     let mut scenery = NodeGroup::new("achromatic ideal folded Martinez stretcher");
 
-    let i_src = scenery.add_node(src.clone())?;
-    let stretcher_node = scenery.add_node(folded_martinez(
+    let i_src = scenery.add_node(&src.clone())?;
+    let stretcher_node = scenery.add_node(&folded_martinez(
         telescope_distance,
         &RefrIndexConst::new(nbk7.get_refractive_index(nanometer!(1054.))?)?,
         alignment_wvl,
     )?)?;
-    let detectors = scenery.add_node(detector_group()?)?;
+    let detectors = scenery.add_node(&detector_group()?)?;
 
     scenery.connect_nodes(i_src, "out1", stretcher_node, "input", millimeter!(400.0))?;
     scenery.connect_nodes(
@@ -235,12 +235,12 @@ fn main() -> OpmResult<()> {
     let telescope_distance = millimeter!(1017.14885);
     let mut scenery = NodeGroup::new("paraxial folded Martinez stretcher");
 
-    let i_src = scenery.add_node(src.clone())?;
-    let stretcher_node = scenery.add_node(folded_martinez_paraxial_lens(
+    let i_src = scenery.add_node(&src.clone())?;
+    let stretcher_node = scenery.add_node(&folded_martinez_paraxial_lens(
         telescope_distance,
         alignment_wvl,
     )?)?;
-    let detectors = scenery.add_node(detector_group()?)?;
+    let detectors = scenery.add_node(&detector_group()?)?;
 
     scenery.connect_nodes(i_src, "out1", stretcher_node, "input", millimeter!(400.0))?;
     scenery.connect_nodes(
@@ -288,9 +288,9 @@ fn main() -> OpmResult<()> {
     src.set_alignment_wavelength(alignment_wvl)?;
     src.set_isometry(Isometry::identity());
 
-    let i_src = scenery.add_node(src)?;
+    let i_src = scenery.add_node(&src)?;
     // focal length = 996.7 mm (Thorlabs LA1779-B)
-    let lens1 = scenery.add_node(Lens::new(
+    let lens1 = scenery.add_node(&Lens::new(
         "Lens 1",
         millimeter!(515.1),
         millimeter!(f64::INFINITY),
@@ -298,14 +298,15 @@ fn main() -> OpmResult<()> {
         &nbk7,
     )?)?;
     let mir_1 =
-        scenery.add_node(ThinMirror::new("mirr").align_like_node_at_distance(lens1, tel_dist))?;
+        scenery.add_node(&ThinMirror::new("mirr").align_like_node_at_distance(lens1, tel_dist))?;
     let mut lens_1_ref1 = NodeReference::from_node(&scenery.node(lens1)?);
     lens_1_ref1.set_inverted(true)?;
-    let lens_1_ref1 = scenery.add_node(lens_1_ref1)?;
+    let lens_1_ref1 = scenery.add_node(&lens_1_ref1)?;
 
-    let paraxial_lens = scenery.add_node(ParaxialSurface::new("ideal lens", millimeter!(500.))?)?;
-    let spot_diag = scenery.add_node(SpotDiagram::new("spot diagram"))?;
-    let i_prop_vis = scenery.add_node(RayPropagationVisualizer::new(
+    let paraxial_lens =
+        scenery.add_node(&ParaxialSurface::new("ideal lens", millimeter!(500.))?)?;
+    let spot_diag = scenery.add_node(&SpotDiagram::new("spot diagram"))?;
+    let i_prop_vis = scenery.add_node(&RayPropagationVisualizer::new(
         "Ray_positions",
         Some(Vector3::y()),
     )?)?;
diff --git a/opossum/examples/grating_examples/treacy_compressor.rs b/opossum/examples/grating_examples/treacy_compressor.rs
index ad1a4c6c22ae628d0f26613f8780e9f709c69b53..9a6eabe56517402bc7a1a3416d49242f6985a869 100644
--- a/opossum/examples/grating_examples/treacy_compressor.rs
+++ b/opossum/examples/grating_examples/treacy_compressor.rs
@@ -11,22 +11,22 @@ pub fn treacy_compressor(alignment_wvl: Length) -> OpmResult<NodeGroup> {
     let mut cb = NodeGroup::new("Treacy compressor");
 
     let i_g1 = cb.add_node(
-        ReflectiveGrating::new("grating 1", num_per_mm!(1740.), -1)?
+        &ReflectiveGrating::new("grating 1", num_per_mm!(1740.), -1)?
             .with_rot_from_littrow(alignment_wvl, degree!(-4.))?,
     )?;
 
     let i_g2 = cb.add_node(
-        ReflectiveGrating::new("grating 2", num_per_mm!(1740.), -1)?
+        &ReflectiveGrating::new("grating 2", num_per_mm!(1740.), -1)?
             .to_rot_from_littrow(alignment_wvl, degree!(-4.) + radian!(10e-6))?,
     )?;
 
     let i_g3 = cb.add_node(
-        ReflectiveGrating::new("grating 3", num_per_mm!(1740.), 1)?
+        &ReflectiveGrating::new("grating 3", num_per_mm!(1740.), 1)?
             .with_rot_from_littrow(alignment_wvl, degree!(4.))?,
     )?;
 
     let i_g4 = cb.add_node(
-        ReflectiveGrating::new("grating 4", num_per_mm!(1740.), 1)?
+        &ReflectiveGrating::new("grating 4", num_per_mm!(1740.), 1)?
             .to_rot_from_littrow(alignment_wvl, degree!(4.))?,
     )?;
 
diff --git a/opossum/examples/group_reverse.rs b/opossum/examples/group_reverse.rs
index db4310667fc72c2455863a4f2cbff8a6cffdf371..c50da6bb015fd1261f66115c98c781dbf70aa110 100644
--- a/opossum/examples/group_reverse.rs
+++ b/opossum/examples/group_reverse.rs
@@ -14,7 +14,7 @@ use uom::si::f64::Length;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::new("Inverse Group test");
-    let i_s = scenery.add_node(Source::new(
+    let i_s = scenery.add_node(&Source::new(
         "Source",
         &LightData::Energy(DataEnergy {
             spectrum: create_he_ne_spec(1.0)?,
@@ -23,16 +23,16 @@ fn main() -> OpmResult<()> {
 
     let mut group = NodeGroup::default();
     group.set_expand_view(true)?;
-    let g_n1 = group.add_node(Dummy::new("node1"))?;
-    let g_n2 = group.add_node(Dummy::new("node2"))?;
+    let g_n1 = group.add_node(&Dummy::new("node1"))?;
+    let g_n2 = group.add_node(&Dummy::new("node2"))?;
 
     group.connect_nodes(g_n1, "rear", g_n2, "front", Length::zero())?;
     group.map_input_port(g_n1, "front", "in1")?;
     group.map_output_port(g_n2, "rear", "out1")?;
     group.set_inverted(true)?;
 
-    let i_g = scenery.add_node(group)?;
-    let i_d = scenery.add_node(EnergyMeter::default())?;
+    let i_g = scenery.add_node(&group)?;
+    let i_d = scenery.add_node(&EnergyMeter::default())?;
 
     scenery.connect_nodes(i_s, "out1", i_g, "out1", Length::zero())?;
     scenery.connect_nodes(i_g, "in1", i_d, "in1", Length::zero())?;
diff --git a/opossum/examples/group_test.rs b/opossum/examples/group_test.rs
index 6521e18def9d0516747a954dd588322697f2937d..b6f6827d6cd6bb9e388cc0c4f573ef9a3fe3834d 100644
--- a/opossum/examples/group_test.rs
+++ b/opossum/examples/group_test.rs
@@ -14,18 +14,18 @@ use std::path::Path;
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::new("group test");
 
-    let i_src = scenery.add_node(collimated_line_ray_source(
+    let i_src = scenery.add_node(&collimated_line_ray_source(
         millimeter!(20.0),
         joule!(1.0),
         6,
     )?)?;
     let mut group1 = NodeGroup::new("group 1");
     group1.set_expand_view(true)?;
-    let i_g1_l = group1.add_node(Lens::default())?;
+    let i_g1_l = group1.add_node(&Lens::default())?;
     group1.map_input_port(i_g1_l, "front", "input")?;
-    let i_g1_bs = group1.add_node(BeamSplitter::default())?;
+    let i_g1_bs = group1.add_node(&BeamSplitter::default())?;
     group1.connect_nodes(i_g1_l, "rear", i_g1_bs, "input1", millimeter!(100.0))?;
-    let i_g1_m = group1.add_node(ThinMirror::default().with_tilt(degree!(45.0, 0.0, 0.0))?)?;
+    let i_g1_m = group1.add_node(&ThinMirror::default().with_tilt(degree!(45.0, 0.0, 0.0))?)?;
     group1.connect_nodes(
         i_g1_bs,
         "out1_trans1_refl2",
@@ -36,12 +36,12 @@ fn main() -> OpmResult<()> {
     group1.map_output_port(i_g1_bs, "out2_trans2_refl1", "output1")?;
     group1.map_output_port(i_g1_m, "reflected", "output2")?;
 
-    let scene_g1 = scenery.add_node(group1)?;
+    let scene_g1 = scenery.add_node(&group1)?;
 
     scenery.connect_nodes(i_src, "out1", scene_g1, "input", millimeter!(50.0))?;
 
-    let i_prop1 = scenery.add_node(RayPropagationVisualizer::new("direct", None)?)?;
-    let i_prop2 = scenery.add_node(RayPropagationVisualizer::new("mirrored", None)?)?;
+    let i_prop1 = scenery.add_node(&RayPropagationVisualizer::new("direct", None)?)?;
+    let i_prop2 = scenery.add_node(&RayPropagationVisualizer::new("mirrored", None)?)?;
 
     scenery.connect_nodes(scene_g1, "output1", i_prop1, "in1", millimeter!(100.0))?;
     scenery.connect_nodes(scene_g1, "output2", i_prop2, "in1", millimeter!(150.0))?;
diff --git a/opossum/examples/hhts/cambox_1w.rs b/opossum/examples/hhts/cambox_1w.rs
index 73ab9336898886406e03d3abc496d430e160d7aa..e8c62a27718e0017f0da359d35c6616608f72166 100644
--- a/opossum/examples/hhts/cambox_1w.rs
+++ b/opossum/examples/hhts/cambox_1w.rs
@@ -14,21 +14,21 @@ pub fn cambox_1w() -> OpmResult<NodeGroup> {
 
     let mut cb = NodeGroup::new("CamBox 1w");
 
-    let d1 = cb.add_node(Dummy::new("d1"))?;
-    let bs1 = cb.add_node(BeamSplitter::new("bs1", &SplittingConfig::Ratio(0.5))?)?;
+    let d1 = cb.add_node(&Dummy::new("d1"))?;
+    let bs1 = cb.add_node(&BeamSplitter::new("bs1", &SplittingConfig::Ratio(0.5))?)?;
 
     cb.connect_nodes(d1, "rear", bs1, "input1", millimeter!(35.0))?;
 
     // FF path
-    let bs_ff = cb.add_node(BeamSplitter::new("bs_ff", &SplittingConfig::Ratio(0.04))?)?;
-    let ff_lens = cb.add_node(ParaxialSurface::new("FF lens", millimeter!(100.0))?)?;
+    let bs_ff = cb.add_node(&BeamSplitter::new("bs_ff", &SplittingConfig::Ratio(0.04))?)?;
+    let ff_lens = cb.add_node(&ParaxialSurface::new("FF lens", millimeter!(100.0))?)?;
     let mut node = SpotDiagram::new("FF cam");
     node.set_aperture(&PortType::Input, "in1", &cam_aperture)?;
-    let ff_cam = cb.add_node(node)?;
+    let ff_cam = cb.add_node(&node)?;
 
     let mut ff_fluence = FluenceDetector::new("FF fluence");
     ff_fluence.set_aperture(&PortType::Input, "in1", &cam_aperture)?;
-    let ff_fluence_cam = cb.add_node(ff_fluence)?;
+    let ff_fluence_cam = cb.add_node(&ff_fluence)?;
 
     cb.connect_nodes(
         bs1,
@@ -48,17 +48,17 @@ pub fn cambox_1w() -> OpmResult<NodeGroup> {
     cb.connect_nodes(ff_cam, "out1", ff_fluence_cam, "in1", millimeter!(0.0))?;
 
     // NF path
-    let nf_lens1 = cb.add_node(ParaxialSurface::new("NF lens1", millimeter!(125.0))?)?;
-    let nf_lens2 = cb.add_node(ParaxialSurface::new("NF lens2", millimeter!(125.0))?)?;
-    let nf_bs = cb.add_node(BeamSplitter::new("nf bs", &SplittingConfig::Ratio(0.5))?)?;
+    let nf_lens1 = cb.add_node(&ParaxialSurface::new("NF lens1", millimeter!(125.0))?)?;
+    let nf_lens2 = cb.add_node(&ParaxialSurface::new("NF lens2", millimeter!(125.0))?)?;
+    let nf_bs = cb.add_node(&BeamSplitter::new("nf bs", &SplittingConfig::Ratio(0.5))?)?;
     let mut node = SpotDiagram::new("NF cam");
     node.set_aperture(&PortType::Input, "in1", &cam_aperture)?;
     node.set_property("plot_aperture", true.into())?;
-    let nf_cam = cb.add_node(node)?;
+    let nf_cam = cb.add_node(&node)?;
 
     let mut nf_fluence = FluenceDetector::new("NF fluence");
     nf_fluence.set_aperture(&PortType::Input, "in1", &cam_aperture)?;
-    let nf_fluence_cam = cb.add_node(nf_fluence)?;
+    let nf_fluence_cam = cb.add_node(&nf_fluence)?;
 
     cb.connect_nodes(
         bs1,
diff --git a/opossum/examples/hhts/cambox_2w.rs b/opossum/examples/hhts/cambox_2w.rs
index 270b1459fe1072745fd698e47f0fa7b6deba3ef3..a68ad3e35bcdaa0c77078f420fec779e47291db6 100644
--- a/opossum/examples/hhts/cambox_2w.rs
+++ b/opossum/examples/hhts/cambox_2w.rs
@@ -13,22 +13,22 @@ pub fn cambox_2w() -> OpmResult<NodeGroup> {
 
     let mut cb = NodeGroup::new("CamBox 2w");
 
-    let d1 = cb.add_node(Dummy::new("d1"))?;
-    let bs1 = cb.add_node(BeamSplitter::new("bs1", &SplittingConfig::Ratio(0.5))?)?;
+    let d1 = cb.add_node(&Dummy::new("d1"))?;
+    let bs1 = cb.add_node(&BeamSplitter::new("bs1", &SplittingConfig::Ratio(0.5))?)?;
 
     cb.connect_nodes(d1, "rear", bs1, "input1", millimeter!(35.0))?;
 
     // FF path
-    let bs_ff = cb.add_node(BeamSplitter::new("bs_ff", &SplittingConfig::Ratio(0.04))?)?;
-    let ff_lens = cb.add_node(ParaxialSurface::new("FF lens", millimeter!(100.0))?)?;
+    let bs_ff = cb.add_node(&BeamSplitter::new("bs_ff", &SplittingConfig::Ratio(0.04))?)?;
+    let ff_lens = cb.add_node(&ParaxialSurface::new("FF lens", millimeter!(100.0))?)?;
     let mut node = SpotDiagram::new("FF cam");
     //node.set_aperture(&PortType::Input, "in1", &cam_aperture)?;
     node.set_property("plot_aperture", true.into())?;
-    let ff_cam = cb.add_node(node)?;
+    let ff_cam = cb.add_node(&node)?;
 
     let ff_fluence = FluenceDetector::new("FF fluence");
     //ff_fluence.set_aperture(&PortType::Input, "in1", &cam_aperture)?;
-    let ff_fluence_cam = cb.add_node(ff_fluence)?;
+    let ff_fluence_cam = cb.add_node(&ff_fluence)?;
 
     cb.connect_nodes(
         bs1,
@@ -48,16 +48,16 @@ pub fn cambox_2w() -> OpmResult<NodeGroup> {
     cb.connect_nodes(ff_cam, "out1", ff_fluence_cam, "in1", millimeter!(0.0))?;
 
     // NF path
-    let nf_lens1 = cb.add_node(ParaxialSurface::new("NF lens1", millimeter!(125.0))?)?;
-    let nf_lens2 = cb.add_node(ParaxialSurface::new("NF lens2", millimeter!(125.0))?)?;
-    let nf_bs = cb.add_node(BeamSplitter::new("nf bs", &SplittingConfig::Ratio(0.5))?)?;
+    let nf_lens1 = cb.add_node(&ParaxialSurface::new("NF lens1", millimeter!(125.0))?)?;
+    let nf_lens2 = cb.add_node(&ParaxialSurface::new("NF lens2", millimeter!(125.0))?)?;
+    let nf_bs = cb.add_node(&BeamSplitter::new("nf bs", &SplittingConfig::Ratio(0.5))?)?;
     let node = SpotDiagram::new("NF cam");
     //node.set_aperture(&PortType::Input, "in1", &cam_aperture)?;
-    let nf_cam = cb.add_node(node)?;
+    let nf_cam = cb.add_node(&node)?;
 
     let nf_fluence = FluenceDetector::new("NF fluence");
     // nf_fluence.set_aperture(&PortType::Input, "in1", &cam_aperture)?;
-    let nf_fluence_cam = cb.add_node(nf_fluence)?;
+    let nf_fluence_cam = cb.add_node(&nf_fluence)?;
 
     cb.connect_nodes(
         bs1,
diff --git a/opossum/examples/hhts/hhts.rs b/opossum/examples/hhts/hhts.rs
index 0082486c7bf30578959441b83f9631a03a72d197..69865d4809e4818bc9f45d230f8878bf30a9470c 100644
--- a/opossum/examples/hhts/hhts.rs
+++ b/opossum/examples/hhts/hhts.rs
@@ -140,20 +140,20 @@ fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::new("HHT Sensor");
     let mut src = Source::new("Source", &LightData::Geometric(rays));
     src.set_isometry(Isometry::identity());
-    let src = scenery.add_node(src)?;
-    let input_group = scenery.add_node(hhts_input()?)?;
+    let src = scenery.add_node(&src)?;
+    let input_group = scenery.add_node(&hhts_input()?)?;
     scenery.connect_nodes(src, "out1", input_group, "input", Length::zero())?;
 
     // T1
     let mut group_t1 = NodeGroup::new("T1");
-    let t1_l1a = group_t1.add_node(Lens::new(
+    let t1_l1a = group_t1.add_node(&Lens::new(
         "T1 L1a",
         millimeter!(518.34008),
         millimeter!(-847.40402),
         millimeter!(30.0),
         &refr_index_hk9l,
     )?)?;
-    let t1_l1b = group_t1.add_node(Lens::new(
+    let t1_l1b = group_t1.add_node(&Lens::new(
         "T1 L1b",
         millimeter!(-788.45031),
         millimeter!(-2551.88619),
@@ -169,15 +169,15 @@ fn main() -> OpmResult<()> {
     )?;
     // node.set_aperture(&PortType::Input, "front", &a_2inch)?;
     // node.set_aperture(&PortType::Output, "rear", &a_2inch)?;
-    let t1_l2a = group_t1.add_node(node)?;
-    let t1_l2b = group_t1.add_node(Lens::new(
+    let t1_l2a = group_t1.add_node(&node)?;
+    let t1_l2b = group_t1.add_node(&Lens::new(
         "T1 L2b",
         millimeter!(76.76954),
         millimeter!(-118.59590),
         millimeter!(14.0),
         &refr_index_hzf52,
     )?)?;
-    let t1_l2c = group_t1.add_node(Lens::new(
+    let t1_l2c = group_t1.add_node(&Lens::new(
         "T1 L2c",
         millimeter!(-63.45837),
         millimeter!(66.33014),
@@ -194,7 +194,7 @@ fn main() -> OpmResult<()> {
     group_t1.map_output_port(t1_l2c, "rear", "output")?;
 
     group_t1.set_expand_view(false)?;
-    let t1 = scenery.add_node(group_t1)?;
+    let t1 = scenery.add_node(&group_t1)?;
 
     scenery.connect_nodes(input_group, "output", t1, "input", millimeter!(100.0))?;
 
@@ -215,7 +215,7 @@ fn main() -> OpmResult<()> {
     // real spectrum (Thorlabs HBSY21)
     //let hbsyx2 = SplittingConfig::Spectrum(Spectrum::from_csv("opossum/examples/hhts/HBSYx2_Reflectivity_45deg_unpol.csv")?);
 
-    let bs = group_bs.add_node(BeamSplitter::new("Dichroic BS HBSY21", &short_pass)?)?;
+    let bs = group_bs.add_node(&BeamSplitter::new("Dichroic BS HBSY21", &short_pass)?)?;
 
     // Long pass filter (1w)
     let felh1000 = FilterType::Spectrum(Spectrum::from_csv(
@@ -223,7 +223,7 @@ fn main() -> OpmResult<()> {
     )?);
     let mut node = IdealFilter::new("1w Longpass filter", &felh1000)?;
     node.set_aperture(&PortType::Input, "front", &a_1inch)?;
-    let filter_1w = group_bs.add_node(node)?;
+    let filter_1w = group_bs.add_node(&node)?;
     group_bs.connect_nodes(bs, "out2_trans2_refl1", filter_1w, "front", Length::zero())?;
 
     // Long pass filter (2w)
@@ -232,35 +232,35 @@ fn main() -> OpmResult<()> {
     )?);
     let mut node = IdealFilter::new("2w Shortpass filter", &fesh0700)?;
     node.set_aperture(&PortType::Input, "front", &a_1inch)?;
-    let filter_2w = group_bs.add_node(node)?;
+    let filter_2w = group_bs.add_node(&node)?;
     group_bs.connect_nodes(bs, "out1_trans1_refl2", filter_2w, "front", Length::zero())?;
 
     group_bs.map_input_port(bs, "input1", "input")?;
     group_bs.map_output_port(filter_1w, "rear", "output_1w")?;
     group_bs.map_output_port(filter_2w, "rear", "output_2w")?;
 
-    let bs_group = scenery.add_node(group_bs)?;
+    let bs_group = scenery.add_node(&group_bs)?;
 
     scenery.connect_nodes(t1, "output", bs_group, "input", millimeter!(100.0))?;
     // 1w branch
 
     // T2_1w
     let mut group_t2_1w = NodeGroup::new("T2 1w");
-    let t2_1w_in = group_t2_1w.add_node(Lens::new(
+    let t2_1w_in = group_t2_1w.add_node(&Lens::new(
         "T2 1w In",
         millimeter!(405.38435),
         millimeter!(-702.52114),
         millimeter!(9.5),
         &refr_index_hk9l,
     )?)?;
-    let t2_1w_field = group_t2_1w.add_node(Lens::new(
+    let t2_1w_field = group_t2_1w.add_node(&Lens::new(
         "T2 1w Field",
         millimeter!(179.59020),
         millimeter!(f64::INFINITY),
         millimeter!(9.5),
         &refr_index_hk9l,
     )?)?;
-    let t2_1w_exit = group_t2_1w.add_node(Lens::new(
+    let t2_1w_exit = group_t2_1w.add_node(&Lens::new(
         "T2 1w Exit",
         millimeter!(f64::INFINITY),
         millimeter!(-202.81235),
@@ -285,26 +285,26 @@ fn main() -> OpmResult<()> {
 
     group_t2_1w.map_input_port(t2_1w_in, "front", "input")?;
     group_t2_1w.map_output_port(t2_1w_exit, "rear", "output")?;
-    let t2_1w = scenery.add_node(group_t2_1w)?;
+    let t2_1w = scenery.add_node(&group_t2_1w)?;
 
     // T3_1w
     let mut group_t3_1w = NodeGroup::new("T3 1w");
 
-    let t3_1w_input = group_t3_1w.add_node(Lens::new(
+    let t3_1w_input = group_t3_1w.add_node(&Lens::new(
         "T3 1w Input",
         millimeter!(f64::INFINITY),
         millimeter!(-417.35031),
         millimeter!(9.5),
         &refr_index_hk9l,
     )?)?;
-    let t3_1w_exit = group_t3_1w.add_node(Lens::new(
+    let t3_1w_exit = group_t3_1w.add_node(&Lens::new(
         "T3 1w Exit",
         millimeter!(156.35054),
         millimeter!(f64::INFINITY),
         millimeter!(9.5),
         &refr_index_hk9l,
     )?)?;
-    let d_1w_12 = group_t3_1w.add_node(Dummy::new("1w d12"))?;
+    let d_1w_12 = group_t3_1w.add_node(&Dummy::new("1w d12"))?;
     group_t3_1w.connect_nodes(
         t3_1w_input,
         "rear",
@@ -316,18 +316,18 @@ fn main() -> OpmResult<()> {
 
     group_t3_1w.map_input_port(t3_1w_input, "front", "input")?;
     group_t3_1w.map_output_port(d_1w_12, "rear", "output")?;
-    let t3_1w = scenery.add_node(group_t3_1w)?;
+    let t3_1w = scenery.add_node(&group_t3_1w)?;
 
     scenery.connect_nodes(bs_group, "output_1w", t2_1w, "input", millimeter!(537.5190))?;
     scenery.connect_nodes(t2_1w, "output", t3_1w, "input", millimeter!(664.58900))?;
 
     let mut group_det_1w = NodeGroup::new("Detectors 1w");
 
-    let det_prop = group_det_1w.add_node(RayPropagationVisualizer::new("Propagation", None)?)?;
-    let det_wavefront_1w = group_det_1w.add_node(WaveFront::new("Wavefront"))?;
-    let cambox_1w = group_det_1w.add_node(cambox_1w()?)?;
+    let det_prop = group_det_1w.add_node(&RayPropagationVisualizer::new("Propagation", None)?)?;
+    let det_wavefront_1w = group_det_1w.add_node(&WaveFront::new("Wavefront"))?;
+    let cambox_1w = group_det_1w.add_node(&cambox_1w()?)?;
     let det_energy_1w =
-        group_det_1w.add_node(EnergyMeter::new("Energy", Metertype::IdealEnergyMeter))?;
+        group_det_1w.add_node(&EnergyMeter::new("Energy", Metertype::IdealEnergyMeter))?;
 
     group_det_1w.connect_nodes(det_prop, "out1", det_wavefront_1w, "in1", Length::zero())?;
     group_det_1w.connect_nodes(
@@ -341,7 +341,7 @@ fn main() -> OpmResult<()> {
 
     group_det_1w.map_input_port(det_prop, "in1", "input")?;
 
-    let det_1w = scenery.add_node(group_det_1w)?;
+    let det_1w = scenery.add_node(&group_det_1w)?;
     scenery.connect_nodes(t3_1w, "output", det_1w, "input", Length::zero())?;
 
     // 2w branch
@@ -349,21 +349,21 @@ fn main() -> OpmResult<()> {
     // T2_2w
     let mut group_t2_2w = NodeGroup::new("T2 2w");
 
-    let t2_2w_in = group_t2_2w.add_node(Lens::new(
+    let t2_2w_in = group_t2_2w.add_node(&Lens::new(
         "T2 2w In",
         millimeter!(536.5733),
         millimeter!(-677.68238),
         millimeter!(9.5),
         &refr_index_hk9l,
     )?)?;
-    let t2_2w_field = group_t2_2w.add_node(Lens::new(
+    let t2_2w_field = group_t2_2w.add_node(&Lens::new(
         "T2 2w Field",
         millimeter!(208.48421),
         millimeter!(f64::INFINITY),
         millimeter!(9.5),
         &refr_index_hk9l,
     )?)?;
-    let t2_2w_exit = group_t2_2w.add_node(Lens::new(
+    let t2_2w_exit = group_t2_2w.add_node(&Lens::new(
         "T2 2w Exit",
         millimeter!(-767.51217),
         millimeter!(-178.98988),
@@ -387,26 +387,26 @@ fn main() -> OpmResult<()> {
 
     group_t2_2w.map_input_port(t2_2w_in, "front", "input")?;
     group_t2_2w.map_output_port(t2_2w_exit, "rear", "output")?;
-    let t2_2w = scenery.add_node(group_t2_2w)?;
+    let t2_2w = scenery.add_node(&group_t2_2w)?;
 
     // T3_2w
     let mut group_t3_2w = NodeGroup::new("T3 2w");
 
-    let t3_2w_input = group_t3_2w.add_node(Lens::new(
+    let t3_2w_input = group_t3_2w.add_node(&Lens::new(
         "T3 2w Input",
         millimeter!(932.92634),
         millimeter!(-724.14405),
         millimeter!(9.5),
         &refr_index_hk9l,
     )?)?;
-    let t3_2w_exit = group_t3_2w.add_node(Lens::new(
+    let t3_2w_exit = group_t3_2w.add_node(&Lens::new(
         "T3 2w Exit",
         millimeter!(161.31174),
         millimeter!(-1069.52277),
         millimeter!(9.5),
         &refr_index_hk9l,
     )?)?;
-    let d_2w_12 = group_t3_2w.add_node(Dummy::new("2w d12"))?;
+    let d_2w_12 = group_t3_2w.add_node(&Dummy::new("2w d12"))?;
     group_t3_2w.connect_nodes(
         t3_2w_input,
         "rear",
@@ -418,7 +418,7 @@ fn main() -> OpmResult<()> {
 
     group_t3_2w.map_input_port(t3_2w_input, "front", "input")?;
     group_t3_2w.map_output_port(d_2w_12, "rear", "output")?;
-    let t3_2w = scenery.add_node(group_t3_2w)?;
+    let t3_2w = scenery.add_node(&group_t3_2w)?;
 
     scenery.connect_nodes(bs_group, "output_2w", t2_2w, "input", millimeter!(474.589))?;
     scenery.connect_nodes(t2_2w, "output", t3_2w, "input", millimeter!(622.09000))?;
@@ -426,11 +426,12 @@ fn main() -> OpmResult<()> {
     // 2w detectors
     let mut group_det_2w = NodeGroup::new("Detectors 2w");
 
-    let det_prop_2w = group_det_2w.add_node(RayPropagationVisualizer::new("Propagation", None)?)?;
-    let det_wavefront_2w = group_det_2w.add_node(WaveFront::new("Wavefront"))?;
+    let det_prop_2w =
+        group_det_2w.add_node(&RayPropagationVisualizer::new("Propagation", None)?)?;
+    let det_wavefront_2w = group_det_2w.add_node(&WaveFront::new("Wavefront"))?;
     let det_energy_2w =
-        group_det_2w.add_node(EnergyMeter::new("Energy", Metertype::IdealEnergyMeter))?;
-    let cambox_2w = group_det_2w.add_node(cambox_2w()?)?;
+        group_det_2w.add_node(&EnergyMeter::new("Energy", Metertype::IdealEnergyMeter))?;
+    let cambox_2w = group_det_2w.add_node(&cambox_2w()?)?;
 
     group_det_2w.connect_nodes(det_prop_2w, "out1", det_wavefront_2w, "in1", Length::zero())?;
     group_det_2w.connect_nodes(
@@ -443,7 +444,7 @@ fn main() -> OpmResult<()> {
     group_det_2w.connect_nodes(det_energy_2w, "out1", cambox_2w, "input", Length::zero())?;
 
     group_det_2w.map_input_port(det_prop_2w, "in1", "input")?;
-    let det_2w = scenery.add_node(group_det_2w)?;
+    let det_2w = scenery.add_node(&group_det_2w)?;
 
     scenery.connect_nodes(t3_2w, "output", det_2w, "input", Length::zero())?;
 
diff --git a/opossum/examples/hhts/hhts_input.rs b/opossum/examples/hhts/hhts_input.rs
index 76623b34cb2e6c7d1075fc872aa0410313258eb4..74ec337c17f700e0f8bf4b0e36317f59d5d1f709 100644
--- a/opossum/examples/hhts/hhts_input.rs
+++ b/opossum/examples/hhts/hhts_input.rs
@@ -16,14 +16,14 @@ pub fn hhts_input() -> OpmResult<NodeGroup> {
         "opossum/examples/hhts/HHTS_T1_PM_Transmission.csv",
     )?);
     let mut group = NodeGroup::new("HHTS Input");
-    let d1 = group.add_node(Dummy::new("d1"))?;
-    let mm15 = group.add_node(BeamSplitter::new("MM15", &dichroic_mirror)?)?;
-    let window = group.add_node(IdealFilter::new("window", &window_filter)?)?;
-    let hhts_t1_cm = group.add_node(BeamSplitter::new("HHTS_T1_CM", &dichroic_mirror)?)?;
+    let d1 = group.add_node(&Dummy::new("d1"))?;
+    let mm15 = group.add_node(&BeamSplitter::new("MM15", &dichroic_mirror)?)?;
+    let window = group.add_node(&IdealFilter::new("window", &window_filter)?)?;
+    let hhts_t1_cm = group.add_node(&BeamSplitter::new("HHTS_T1_CM", &dichroic_mirror)?)?;
     let meter = EnergyMeter::new("Beamdump", Metertype::IdealEnergyMeter);
-    let beam_dump = group.add_node(meter)?;
+    let beam_dump = group.add_node(&meter)?;
 
-    let hhts_t1_pm = group.add_node(BeamSplitter::new("HHTS_T1_PM", &double_mirror)?)?;
+    let hhts_t1_pm = group.add_node(&BeamSplitter::new("HHTS_T1_PM", &double_mirror)?)?;
 
     group.connect_nodes(d1, "rear", mm15, "input1", millimeter!(500.0))?;
     group.connect_nodes(
diff --git a/opossum/examples/inverse_beam_splitter_test.rs b/opossum/examples/inverse_beam_splitter_test.rs
index d038034b965b08dc451ebe83868e67925177d268..29a2ecd64379276122fda4ae92c4e98bd5b5db4d 100644
--- a/opossum/examples/inverse_beam_splitter_test.rs
+++ b/opossum/examples/inverse_beam_splitter_test.rs
@@ -14,7 +14,7 @@ use uom::si::f64::Length;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::new("inverse beam splitter test");
-    let i_s = scenery.add_node(Source::new(
+    let i_s = scenery.add_node(&Source::new(
         "Source",
         &LightData::Energy(DataEnergy {
             spectrum: create_he_ne_spec(1.0)?,
@@ -22,12 +22,12 @@ fn main() -> OpmResult<()> {
     ))?;
     let mut bs = BeamSplitter::new("bs", &SplittingConfig::Ratio(0.6)).unwrap();
     bs.set_inverted(true)?;
-    let i_bs = scenery.add_node(bs)?;
-    let i_d1 = scenery.add_node(EnergyMeter::new(
+    let i_bs = scenery.add_node(&bs)?;
+    let i_d1 = scenery.add_node(&EnergyMeter::new(
         "Energy meter 1",
         opossum::nodes::Metertype::IdealEnergyMeter,
     ))?;
-    let i_d2 = scenery.add_node(EnergyMeter::new(
+    let i_d2 = scenery.add_node(&EnergyMeter::new(
         "Energy meter 2",
         opossum::nodes::Metertype::IdealEnergyMeter,
     ))?;
diff --git a/opossum/examples/kepler.rs b/opossum/examples/kepler.rs
index 5a480a49f5ef19cbbab0fa670bfd45eef7fbc2ff..34d4713f29db5439e2a38f3ec8605577b73a9494 100644
--- a/opossum/examples/kepler.rs
+++ b/opossum/examples/kepler.rs
@@ -12,7 +12,7 @@ use std::path::Path;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::default();
-    let i_src = scenery.add_node(collimated_line_ray_source(
+    let i_src = scenery.add_node(&collimated_line_ray_source(
         millimeter!(20.0),
         joule!(1.0),
         3,
@@ -20,9 +20,9 @@ fn main() -> OpmResult<()> {
     let mut lens1 = ParaxialSurface::new("100 mm lens", millimeter!(100.0))?;
     let circle = CircleConfig::new(millimeter!(25.), millimeter!(0., 0.))?;
     lens1.set_aperture(&PortType::Input, "front", &Aperture::BinaryCircle(circle))?;
-    let i_pl1 = scenery.add_node(lens1)?;
-    let i_pl2 = scenery.add_node(ParaxialSurface::new("50 mm lens", millimeter!(50.0))?)?;
-    let i_sd3 = scenery.add_node(RayPropagationVisualizer::new("after telecope", None)?)?;
+    let i_pl1 = scenery.add_node(&lens1)?;
+    let i_pl2 = scenery.add_node(&ParaxialSurface::new("50 mm lens", millimeter!(50.0))?)?;
+    let i_sd3 = scenery.add_node(&RayPropagationVisualizer::new("after telecope", None)?)?;
     scenery.connect_nodes(i_src, "out1", i_pl1, "front", millimeter!(50.0))?;
     scenery.connect_nodes(i_pl1, "rear", i_pl2, "front", millimeter!(150.0))?;
     scenery.connect_nodes(i_pl2, "rear", i_sd3, "in1", millimeter!(50.0))?;
diff --git a/opossum/examples/laser_system.rs b/opossum/examples/laser_system.rs
index eb23c5b03f2aa21a39e37bdfa7206b0e40d0a20b..162d71926ad9f63fd560954f88a41b35eef84bf5 100644
--- a/opossum/examples/laser_system.rs
+++ b/opossum/examples/laser_system.rs
@@ -17,19 +17,19 @@ use uom::si::f64::Length;
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::new("laser system");
     // Main beam line
-    let i_src = scenery.add_node(round_collimated_ray_source(
+    let i_src = scenery.add_node(&round_collimated_ray_source(
         millimeter!(1.0),
         joule!(1.0),
         3,
     )?)?;
-    let i_l1 = scenery.add_node(ParaxialSurface::new("f=100", millimeter!(100.0))?)?;
-    let i_l2 = scenery.add_node(ParaxialSurface::new("f=200", millimeter!(200.0))?)?;
-    let i_bs = scenery.add_node(BeamSplitter::new("1% BS", &SplittingConfig::Ratio(0.99))?)?;
-    let i_e1 = scenery.add_node(EnergyMeter::new(
+    let i_l1 = scenery.add_node(&ParaxialSurface::new("f=100", millimeter!(100.0))?)?;
+    let i_l2 = scenery.add_node(&ParaxialSurface::new("f=200", millimeter!(200.0))?)?;
+    let i_bs = scenery.add_node(&BeamSplitter::new("1% BS", &SplittingConfig::Ratio(0.99))?)?;
+    let i_e1 = scenery.add_node(&EnergyMeter::new(
         "Energy meter 1",
         opossum::nodes::Metertype::IdealEnergyMeter,
     ))?;
-    let i_sd1 = scenery.add_node(SpotDiagram::new("Output"))?;
+    let i_sd1 = scenery.add_node(&SpotDiagram::new("Output"))?;
 
     scenery.connect_nodes(i_src, "out1", i_l1, "front", Length::zero())?;
     scenery.connect_nodes(i_l1, "rear", i_l2, "front", millimeter!(300.0))?;
@@ -38,7 +38,7 @@ fn main() -> OpmResult<()> {
     scenery.connect_nodes(i_e1, "out1", i_sd1, "in1", Length::zero())?;
 
     // Diagnostic beam line
-    let i_f = scenery.add_node(IdealFilter::new(
+    let i_f = scenery.add_node(&IdealFilter::new(
         "OD1 filter",
         &opossum::nodes::FilterType::Constant(0.1),
     )?)?;
@@ -47,11 +47,14 @@ fn main() -> OpmResult<()> {
     // Cam Box
     let mut cam_box = NodeGroup::new("CamBox");
 
-    let i_cb_bs = cam_box.add_node(BeamSplitter::new("50/50 BS", &SplittingConfig::Ratio(0.5))?)?;
-    let i_cb_l = cam_box.add_node(ParaxialSurface::new("FF lens", millimeter!(100.0))?)?;
-    let i_cb_sd1 = cam_box.add_node(SpotDiagram::new("Nearfield"))?;
-    let i_cb_sd2 = cam_box.add_node(SpotDiagram::new("Farfield"))?;
-    let i_cb_e = cam_box.add_node(EnergyMeter::new(
+    let i_cb_bs = cam_box.add_node(&BeamSplitter::new(
+        "50/50 BS",
+        &SplittingConfig::Ratio(0.5),
+    )?)?;
+    let i_cb_l = cam_box.add_node(&ParaxialSurface::new("FF lens", millimeter!(100.0))?)?;
+    let i_cb_sd1 = cam_box.add_node(&SpotDiagram::new("Nearfield"))?;
+    let i_cb_sd2 = cam_box.add_node(&SpotDiagram::new("Farfield"))?;
+    let i_cb_e = cam_box.add_node(&EnergyMeter::new(
         "Energy meter",
         opossum::nodes::Metertype::IdealEnergyMeter,
     ))?;
@@ -75,7 +78,7 @@ fn main() -> OpmResult<()> {
     cam_box.connect_nodes(i_cb_sd1, "out1", i_cb_e, "in1", Length::zero())?;
 
     cam_box.map_input_port(i_cb_bs, "input1", "input")?;
-    let i_cam_box = scenery.add_node(cam_box)?;
+    let i_cam_box = scenery.add_node(&cam_box)?;
     scenery.connect_nodes(i_f, "rear", i_cam_box, "input", Length::zero())?;
 
     let mut doc = OpmDocument::new(scenery);
diff --git a/opossum/examples/lens_inverse.rs b/opossum/examples/lens_inverse.rs
index 01fdc6474aa26b9311a10d74bd705bb45adc6dd5..61e8593d073d2aed23996b10aa2c098730bff947 100644
--- a/opossum/examples/lens_inverse.rs
+++ b/opossum/examples/lens_inverse.rs
@@ -15,23 +15,23 @@ use std::path::Path;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::default();
-    let i_src = scenery.add_node(collimated_line_ray_source(
+    let i_src = scenery.add_node(&collimated_line_ray_source(
         millimeter!(20.0),
         joule!(1.0),
         6,
     )?)?;
-    let i_l1 = scenery.add_node(Lens::new(
+    let i_l1 = scenery.add_node(&Lens::new(
         "lens",
         millimeter!(100.0),
         millimeter!(-100.0),
         millimeter!(10.0),
         &RefrIndexConst::new(1.5)?,
     )?)?;
-    let i_m2 = scenery.add_node(ThinMirror::new("mirror").with_tilt(degree!(5.0, 0.0, 0.0))?)?;
+    let i_m2 = scenery.add_node(&ThinMirror::new("mirror").with_tilt(degree!(5.0, 0.0, 0.0))?)?;
     let mut l1_ref = NodeReference::from_node(&scenery.node(i_l1)?);
     l1_ref.set_inverted(true)?;
-    let i_l1_ref = scenery.add_node(l1_ref)?;
-    let i_sd3 = scenery.add_node(RayPropagationVisualizer::default())?;
+    let i_l1_ref = scenery.add_node(&l1_ref)?;
+    let i_sd3 = scenery.add_node(&RayPropagationVisualizer::default())?;
     scenery.connect_nodes(i_src, "out1", i_l1, "front", millimeter!(30.0))?;
     scenery.connect_nodes(i_l1, "rear", i_m2, "input", millimeter!(90.0))?;
     scenery.connect_nodes(i_m2, "reflected", i_l1_ref, "rear", millimeter!(0.0))?;
diff --git a/opossum/examples/lens_test.rs b/opossum/examples/lens_test.rs
index 92916702051514aa1a676dd5621cb3dbdd413f24..b7108ebdb5c2c78358930be5262d4fd504edd4d7 100644
--- a/opossum/examples/lens_test.rs
+++ b/opossum/examples/lens_test.rs
@@ -12,7 +12,7 @@ use std::path::Path;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::new("Lens Ray-trace test");
-    let src = scenery.add_node(collimated_line_ray_source(
+    let src = scenery.add_node(&collimated_line_ray_source(
         millimeter!(20.0),
         joule!(1.0),
         6,
@@ -24,7 +24,7 @@ fn main() -> OpmResult<()> {
         &RefrIndexConst::new(1.5068)?,
     )?
     .with_tilt(degree!(15.0, 0.0, 0.0))?;
-    let l1 = scenery.add_node(lens1)?;
+    let l1 = scenery.add_node(&lens1)?;
     let lens2 = Lens::new(
         "Lens 2",
         millimeter!(205.55),
@@ -33,8 +33,8 @@ fn main() -> OpmResult<()> {
         &RefrIndexConst::new(1.5068).unwrap(),
     )?
     .with_tilt(degree!(15.0, 0.0, 0.0))?;
-    let l2 = scenery.add_node(lens2)?;
-    let det = scenery.add_node(RayPropagationVisualizer::new("Ray plot", None)?)?;
+    let l2 = scenery.add_node(&lens2)?;
+    let det = scenery.add_node(&RayPropagationVisualizer::new("Ray plot", None)?)?;
     scenery.connect_nodes(src, "out1", l1, "front", millimeter!(50.0))?;
     scenery.connect_nodes(l1, "rear", l2, "front", millimeter!(50.0))?;
     scenery.connect_nodes(l2, "rear", det, "in1", millimeter!(50.0))?;
diff --git a/opossum/examples/michaelson.rs b/opossum/examples/michaelson.rs
index 4ef39f22378a66bad9598eb453b15c4885e512e3..cb8f289140dc807723555f19cf181cdf06207119 100644
--- a/opossum/examples/michaelson.rs
+++ b/opossum/examples/michaelson.rs
@@ -12,21 +12,21 @@ use uom::si::f64::Length;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::new("Michaelson interferomater");
-    let src = scenery.add_node(Source::new(
+    let src = scenery.add_node(&Source::new(
         "Source",
         &LightData::Energy(DataEnergy {
             spectrum: create_he_ne_spec(1.0)?,
         }),
     ))?;
-    let bs = scenery.add_node(BeamSplitter::default())?;
-    let sample = scenery.add_node(Dummy::new("Sample"))?;
+    let bs = scenery.add_node(&BeamSplitter::default())?;
+    let sample = scenery.add_node(&Dummy::new("Sample"))?;
     let rf = NodeReference::from_node(&scenery.node(sample)?);
-    let r_sample = scenery.add_node(rf)?;
-    let m1 = scenery.add_node(Dummy::new("Mirror"))?;
-    let m2 = scenery.add_node(Dummy::new("Mirror"))?;
+    let r_sample = scenery.add_node(&rf)?;
+    let m1 = scenery.add_node(&Dummy::new("Mirror"))?;
+    let m2 = scenery.add_node(&Dummy::new("Mirror"))?;
     let rf = NodeReference::from_node(&scenery.node(bs)?);
-    let r_bs = scenery.add_node(rf)?;
-    let det = scenery.add_node(Detector::default())?;
+    let r_bs = scenery.add_node(&rf)?;
+    let det = scenery.add_node(&Detector::default())?;
 
     scenery.connect_nodes(src, "out1", bs, "input1", Length::zero())?;
     scenery.connect_nodes(bs, "out1_trans1_refl2", sample, "front", Length::zero())?;
diff --git a/opossum/examples/mirror.rs b/opossum/examples/mirror.rs
index f33b415060cadadc0eb16c2a3edee5ab482bbf4a..ca155644ca8e37281ca0a0f2bfd2ab8c5cbc207a 100644
--- a/opossum/examples/mirror.rs
+++ b/opossum/examples/mirror.rs
@@ -16,7 +16,7 @@ use std::path::Path;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::default();
-    let i_src = scenery.add_node(round_collimated_ray_source(
+    let i_src = scenery.add_node(&round_collimated_ray_source(
         millimeter!(20.0),
         joule!(1.0),
         3,
@@ -27,16 +27,16 @@ fn main() -> OpmResult<()> {
         "input",
         &CoatingType::ConstantR { reflectivity: 0.5 },
     )?;
-    let i_m1 = scenery.add_node(mirror1)?;
+    let i_m1 = scenery.add_node(&mirror1)?;
     let i_m2 = scenery.add_node(
-        ThinMirror::new("mirror 2")
+        &ThinMirror::new("mirror 2")
             .with_curvature(millimeter!(-100.0))?
             .with_tilt(degree!(22.5, 0.0, 0.0))?,
     )?;
-    let i_prop_vis = scenery.add_node(RayPropagationVisualizer::default())?;
-    let i_sd = scenery.add_node(SpotDiagram::default())?;
-    let i_wf = scenery.add_node(WaveFront::default())?;
-    let i_pm = scenery.add_node(EnergyMeter::default())?;
+    let i_prop_vis = scenery.add_node(&RayPropagationVisualizer::default())?;
+    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, "out1", i_m1, "input", millimeter!(100.0))?;
     scenery.connect_nodes(i_m1, "reflected", i_m2, "input", millimeter!(100.0))?;
     scenery.connect_nodes(i_m2, "reflected", i_prop_vis, "in1", millimeter!(80.0))?;
diff --git a/opossum/examples/mirror_inverse.rs b/opossum/examples/mirror_inverse.rs
index 4c02385d041942316b49b371e2ef38d65ca297a5..7fe457ae247ae9710e1d7b397a46ca80c971b34a 100644
--- a/opossum/examples/mirror_inverse.rs
+++ b/opossum/examples/mirror_inverse.rs
@@ -14,19 +14,20 @@ use std::path::Path;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::default();
-    let i_src = scenery.add_node(collimated_line_ray_source(
+    let i_src = scenery.add_node(&collimated_line_ray_source(
         millimeter!(20.0),
         joule!(1.0),
         10,
     )?)?;
-    let i_m1 = scenery.add_node(ThinMirror::new("mirror 1").with_tilt(degree!(45.0, 0.0, 0.0))?)?;
-    let i_m2 = scenery.add_node(ThinMirror::new("mirror 2").with_tilt(degree!(2.0, 0.0, 0.0))?)?;
+    let i_m1 =
+        scenery.add_node(&ThinMirror::new("mirror 1").with_tilt(degree!(45.0, 0.0, 0.0))?)?;
+    let i_m2 = scenery.add_node(&ThinMirror::new("mirror 2").with_tilt(degree!(2.0, 0.0, 0.0))?)?;
     let m1_ref = NodeReference::from_node(&scenery.node(i_m1)?);
-    let i_m1_ref = scenery.add_node(m1_ref)?;
-    let i_sd3 = scenery.add_node(RayPropagationVisualizer::default())?;
-    let i_sd = scenery.add_node(SpotDiagram::default())?;
+    let i_m1_ref = scenery.add_node(&m1_ref)?;
+    let i_sd3 = scenery.add_node(&RayPropagationVisualizer::default())?;
+    let i_sd = scenery.add_node(&SpotDiagram::default())?;
     let sd_ref = NodeReference::from_node(&scenery.node(i_sd)?);
-    let i_sd_ref = scenery.add_node(sd_ref)?;
+    let i_sd_ref = scenery.add_node(&sd_ref)?;
     scenery.connect_nodes(i_src, "out1", i_sd, "in1", millimeter!(40.0))?;
     scenery.connect_nodes(i_sd, "out1", i_m1, "input", millimeter!(40.0))?;
     scenery.connect_nodes(i_m1, "reflected", i_m2, "input", millimeter!(50.0))?;
diff --git a/opossum/examples/opticscenery.rs b/opossum/examples/opticscenery.rs
index a06ec4d83ee8ae72f5fb0fa78087deedd6c52ed2..9da8d3a22fecf8c2e0ed6d6dbb585d5dae2cc6a3 100644
--- a/opossum/examples/opticscenery.rs
+++ b/opossum/examples/opticscenery.rs
@@ -10,8 +10,8 @@ use uom::si::f64::Length;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::new("OpticScenery demo");
-    let node1 = scenery.add_node(Dummy::new("dummy1"))?;
-    let node2 = scenery.add_node(Dummy::new("dummy2"))?;
+    let node1 = scenery.add_node(&Dummy::new("dummy1"))?;
+    let node2 = scenery.add_node(&Dummy::new("dummy2"))?;
     scenery.connect_nodes(node1, "rear", node2, "front", Length::zero())?;
 
     let mut doc = OpmDocument::new(scenery);
diff --git a/opossum/examples/paraxial_lens_wavefront.rs b/opossum/examples/paraxial_lens_wavefront.rs
index 78d195f339105706dd0cb113f000128b7fe979ab..7cdc47ad8ceaef8fbb0b3981e82c904cb2bea255 100644
--- a/opossum/examples/paraxial_lens_wavefront.rs
+++ b/opossum/examples/paraxial_lens_wavefront.rs
@@ -16,10 +16,10 @@ use uom::si::f64::Length;
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::new("Lens Ray-trace test");
     let src = scenery
-        .add_node(round_collimated_ray_source(millimeter!(5.0), joule!(1.0), 30).unwrap())?;
-    let lens = scenery.add_node(ParaxialSurface::new("f=100 mm", millimeter!(100.0))?)?;
-    let wf = scenery.add_node(WaveFront::default())?;
-    let det = scenery.add_node(RayPropagationVisualizer::default())?;
+        .add_node(&round_collimated_ray_source(millimeter!(5.0), joule!(1.0), 30).unwrap())?;
+    let lens = scenery.add_node(&ParaxialSurface::new("f=100 mm", millimeter!(100.0))?)?;
+    let wf = scenery.add_node(&WaveFront::default())?;
+    let det = scenery.add_node(&RayPropagationVisualizer::default())?;
     scenery.connect_nodes(src, "out1", lens, "front", Length::zero())?;
     scenery.connect_nodes(lens, "rear", wf, "in1", millimeter!(90.0))?;
     scenery.connect_nodes(wf, "out1", det, "in1", Length::zero())?;
diff --git a/opossum/examples/point_src_wavefront.rs b/opossum/examples/point_src_wavefront.rs
index 73730dc5ee5f761527cb2185c11d334a9d910838..75868fbe63de6417145814c04288031c5f5b26d9 100644
--- a/opossum/examples/point_src_wavefront.rs
+++ b/opossum/examples/point_src_wavefront.rs
@@ -11,8 +11,8 @@ use std::path::Path;
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::default();
     let source = point_ray_source(degree!(90.0), joule!(1.))?;
-    let i_s = scenery.add_node(source)?;
-    let i_wf1 = scenery.add_node(WaveFront::new("wf_monitor 1"))?;
+    let i_s = scenery.add_node(&source)?;
+    let i_wf1 = scenery.add_node(&WaveFront::new("wf_monitor 1"))?;
 
     scenery.connect_nodes(i_s, "out1", i_wf1, "in1", meter!(0.1))?;
 
diff --git a/opossum/examples/prism_dispersion.rs b/opossum/examples/prism_dispersion.rs
index 3f47127c9dff301f4aecf56cebb0a3b7d8e209fe..37d8cdd62b95ca6a21034a5d2f4f01c8bca4df2e 100644
--- a/opossum/examples/prism_dispersion.rs
+++ b/opossum/examples/prism_dispersion.rs
@@ -49,10 +49,10 @@ fn main() -> OpmResult<()> {
     let light = LightData::Geometric(rays_1w);
     let mut light_src = Source::new("collimated ray source", &light);
     light_src.set_isometry(Isometry::identity());
-    let src = scenery.add_node(light_src)?;
+    let src = scenery.add_node(&light_src)?;
 
     let w1 = scenery.add_node(
-        Wedge::new(
+        &Wedge::new(
             "prism 1",
             millimeter!(20.0),
             degree!(wedge_angle_in_degree),
@@ -67,9 +67,9 @@ fn main() -> OpmResult<()> {
         &refr_index_hk9l,
     )?
     .with_tilt(degree!(wedge_angle_in_degree / 2.0, 0.0, 0.0))?;
-    let w2 = scenery.add_node(wedge2)?;
+    let w2 = scenery.add_node(&wedge2)?;
     let w3 = scenery.add_node(
-        Wedge::new(
+        &Wedge::new(
             "prism 3",
             millimeter!(20.0),
             degree!(-1.0 * wedge_angle_in_degree),
@@ -78,7 +78,7 @@ fn main() -> OpmResult<()> {
         .with_tilt(degree!(wedge_angle_in_degree / 2.0, 0.0, 0.0))?,
     )?;
     let w4 = scenery.add_node(
-        Wedge::new(
+        &Wedge::new(
             "prism 4",
             millimeter!(20.0),
             degree!(wedge_angle_in_degree),
@@ -86,8 +86,8 @@ fn main() -> OpmResult<()> {
         )?
         .with_tilt(degree!(wedge_angle_in_degree / -2.0, 0.0, 0.0))?,
     )?;
-    let det = scenery.add_node(RayPropagationVisualizer::default())?;
-    let sd = scenery.add_node(SpotDiagram::default())?;
+    let det = scenery.add_node(&RayPropagationVisualizer::default())?;
+    let sd = scenery.add_node(&SpotDiagram::default())?;
     scenery.connect_nodes(src, "out1", w1, "front", millimeter!(50.0))?;
     scenery.connect_nodes(w1, "rear", w2, "front", millimeter!(100.0))?;
     scenery.connect_nodes(w2, "rear", w3, "front", millimeter!(150.0))?;
diff --git a/opossum/examples/prism_pair.rs b/opossum/examples/prism_pair.rs
index 721a1dd166937d1ed21d20f5885f9ddcfac33434..bd3224d81a78ede5595787a976ffd91c3684c6d3 100644
--- a/opossum/examples/prism_pair.rs
+++ b/opossum/examples/prism_pair.rs
@@ -13,7 +13,7 @@ use std::path::Path;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::new("Prism Pair test");
-    let src = scenery.add_node(collimated_line_ray_source(
+    let src = scenery.add_node(&collimated_line_ray_source(
         millimeter!(50.0),
         joule!(1.0),
         7,
@@ -24,7 +24,7 @@ fn main() -> OpmResult<()> {
         degree!(30.0),
         &RefrIndexConst::new(1.5068)?,
     )?;
-    let p1 = scenery.add_node(prism1)?;
+    let p1 = scenery.add_node(&prism1)?;
 
     let mut prism2 = Wedge::new(
         "Prism2",
@@ -34,10 +34,10 @@ fn main() -> OpmResult<()> {
     )?;
     let iso = Isometry::new(millimeter!(0.0, 20.0, 110.0), degree!(30.0, 0.0, 0.0))?;
     prism2.set_isometry(iso);
-    let p2 = scenery.add_node(prism2)?;
+    let p2 = scenery.add_node(&prism2)?;
 
-    let det = scenery.add_node(RayPropagationVisualizer::default())?;
-    let sd = scenery.add_node(SpotDiagram::default())?;
+    let det = scenery.add_node(&RayPropagationVisualizer::default())?;
+    let sd = scenery.add_node(&SpotDiagram::default())?;
 
     scenery.connect_nodes(src, "out1", p1, "front", millimeter!(10.0))?;
     scenery.connect_nodes(p1, "rear", p2, "front", millimeter!(100.0))?;
diff --git a/opossum/examples/ray_propagation.rs b/opossum/examples/ray_propagation.rs
index 7279b698ba43060bb3a19db917faf942e31f2407..5fa111549b2dd9e002012bdff65bda8f321f66b0 100644
--- a/opossum/examples/ray_propagation.rs
+++ b/opossum/examples/ray_propagation.rs
@@ -13,12 +13,12 @@ use std::path::Path;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::default();
-    let src = scenery.add_node(round_collimated_ray_source(
+    let src = scenery.add_node(&round_collimated_ray_source(
         millimeter!(5.0),
         joule!(1.0),
         10,
     )?)?;
-    let l1 = scenery.add_node(Lens::new(
+    let l1 = scenery.add_node(&Lens::new(
         "l1",
         millimeter!(200.0),
         millimeter!(-200.0),
@@ -37,8 +37,8 @@ fn main() -> OpmResult<()> {
         "front",
         &Aperture::BinaryCircle(CircleConfig::new(millimeter!(3.), millimeter!(0., 0.))?),
     )?;
-    let l2 = scenery.add_node(lens2)?;
-    let det = scenery.add_node(RayPropagationVisualizer::default())?;
+    let l2 = scenery.add_node(&lens2)?;
+    let det = scenery.add_node(&RayPropagationVisualizer::default())?;
     scenery.connect_nodes(src, "out1", l1, "front", millimeter!(30.0))?;
     scenery.connect_nodes(l1, "rear", l2, "front", millimeter!(197.22992))?;
     scenery.connect_nodes(l2, "rear", det, "in1", millimeter!(30.0))?;
diff --git a/opossum/examples/ray_source.rs b/opossum/examples/ray_source.rs
index b48aa6626e52d72a84740c4779f532b3353c6e69..4ec7d510b3476fe46fe9907c6661fc6305d7c0b6 100644
--- a/opossum/examples/ray_source.rs
+++ b/opossum/examples/ray_source.rs
@@ -18,11 +18,11 @@ fn main() -> OpmResult<()> {
     let aperture =
         Aperture::BinaryCircle(CircleConfig::new(millimeter!(1.0), millimeter![0.5, 0.5])?);
     source.set_aperture(&PortType::Output, "out1", &aperture)?;
-    let i_s = scenery.add_node(source)?;
+    let i_s = scenery.add_node(&source)?;
     let dummy = Dummy::default();
-    let i_dummy = scenery.add_node(dummy)?;
-    let i_d = scenery.add_node(EnergyMeter::default())?;
-    let i_sd = scenery.add_node(SpotDiagram::default())?;
+    let i_dummy = scenery.add_node(&dummy)?;
+    let i_d = scenery.add_node(&EnergyMeter::default())?;
+    let i_sd = scenery.add_node(&SpotDiagram::default())?;
     scenery.connect_nodes(i_s, "out1", i_dummy, "front", Length::zero())?;
     scenery.connect_nodes(i_dummy, "rear", i_d, "in1", Length::zero())?;
     scenery.connect_nodes(i_d, "out1", i_sd, "in1", Length::zero())?;
diff --git a/opossum/examples/reference_test.rs b/opossum/examples/reference_test.rs
index 5f6d85b1f1965a9615e949e3f8899017f552adf1..2973a496d74dadf1d6a60991b42468bb8d9f69ff 100644
--- a/opossum/examples/reference_test.rs
+++ b/opossum/examples/reference_test.rs
@@ -12,18 +12,18 @@ use uom::si::f64::Length;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::new("Reference node demo");
-    let src = scenery.add_node(Source::new(
+    let src = scenery.add_node(&Source::new(
         "source",
         &LightData::Energy(DataEnergy {
             spectrum: create_he_ne_spec(1.0)?,
         }),
     ))?;
-    let filt = scenery.add_node(IdealFilter::new(
+    let filt = scenery.add_node(&IdealFilter::new(
         "50 % filter",
         &opossum::nodes::FilterType::Constant(0.5),
     )?)?;
-    let reference = scenery.add_node(NodeReference::from_node(&scenery.node(filt).unwrap()))?;
-    let detector = scenery.add_node(EnergyMeter::default())?;
+    let reference = scenery.add_node(&NodeReference::from_node(&scenery.node(filt).unwrap()))?;
+    let detector = scenery.add_node(&EnergyMeter::default())?;
     scenery.connect_nodes(src, "out1", filt, "front", Length::zero())?;
     scenery.connect_nodes(filt, "rear", reference, "front", Length::zero())?;
     scenery.connect_nodes(reference, "rear", detector, "in1", Length::zero())?;
diff --git a/opossum/examples/surface_wavefront.rs b/opossum/examples/surface_wavefront.rs
index e63c7dba51e877eba1da6da14481366107f4c05c..03ee0850ffa1b451e404ea3889622e1743543e31 100644
--- a/opossum/examples/surface_wavefront.rs
+++ b/opossum/examples/surface_wavefront.rs
@@ -18,12 +18,12 @@ use uom::si::f64::Length;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::default();
-    let src = scenery.add_node(round_collimated_ray_source(
+    let src = scenery.add_node(&round_collimated_ray_source(
         millimeter!(5.0),
         joule!(1.0),
         5,
     )?)?;
-    let l1 = scenery.add_node(Lens::new(
+    let l1 = scenery.add_node(&Lens::new(
         "l1",
         millimeter!(200.0),
         millimeter!(-200.0),
@@ -39,10 +39,10 @@ fn main() -> OpmResult<()> {
     )?;
     let circle = CircleConfig::new(millimeter!(3.0), millimeter!(0., 0.))?;
     lens.set_aperture(&PortType::Output, "rear", &Aperture::BinaryCircle(circle))?;
-    let l2 = scenery.add_node(lens)?;
-    let det = scenery.add_node(RayPropagationVisualizer::default())?;
-    let wf = scenery.add_node(WaveFront::default())?;
-    let sd = scenery.add_node(SpotDiagram::default())?;
+    let l2 = scenery.add_node(&lens)?;
+    let det = scenery.add_node(&RayPropagationVisualizer::default())?;
+    let wf = scenery.add_node(&WaveFront::default())?;
+    let sd = scenery.add_node(&SpotDiagram::default())?;
     scenery.connect_nodes(src, "out1", l1, "front", millimeter!(30.0))?;
     scenery.connect_nodes(l1, "rear", l2, "front", millimeter!(197.22992))?;
     scenery.connect_nodes(l2, "rear", wf, "in1", millimeter!(30.0))?;
diff --git a/opossum/examples/tilted_src.rs b/opossum/examples/tilted_src.rs
index 9958bb65482b6ac094f2ebce16e23b5210ca3671..af044ee5f9a14edc46156d4a08778e8e21d2c774 100644
--- a/opossum/examples/tilted_src.rs
+++ b/opossum/examples/tilted_src.rs
@@ -13,14 +13,15 @@ fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::default();
     let src = collimated_line_ray_source(millimeter!(20.0), joule!(1.0), 21)?
         .with_tilt(degree!(20.0, 0.0, 0.0))?;
-    let i_src = scenery.add_node(src)?;
-    let i_m1 = scenery.add_node(ThinMirror::new("mirror 1").with_tilt(degree!(45.0, 0.0, 0.0))?)?;
+    let i_src = scenery.add_node(&src)?;
+    let i_m1 =
+        scenery.add_node(&ThinMirror::new("mirror 1").with_tilt(degree!(45.0, 0.0, 0.0))?)?;
     let i_m2 = scenery.add_node(
-        ThinMirror::new("mirror 2")
+        &ThinMirror::new("mirror 2")
             .with_curvature(millimeter!(-100.0))?
             .with_tilt(degree!(45.0, 0.0, 0.0))?,
     )?;
-    let i_sd3 = scenery.add_node(RayPropagationVisualizer::default())?;
+    let i_sd3 = scenery.add_node(&RayPropagationVisualizer::default())?;
 
     scenery.connect_nodes(i_src, "out1", i_m1, "input", millimeter!(100.0))?;
     scenery.connect_nodes(i_m1, "reflected", i_m2, "input", millimeter!(100.0))?;
diff --git a/opossum/examples/tilted_wavefront_sensor.rs b/opossum/examples/tilted_wavefront_sensor.rs
index 4cd9ce3e9a9ecd014217a7052b93cd8ca83512bf..99d0a98976f4dc74e43d8a646f6293a115b72f76 100644
--- a/opossum/examples/tilted_wavefront_sensor.rs
+++ b/opossum/examples/tilted_wavefront_sensor.rs
@@ -12,11 +12,11 @@ use opossum::{
 use std::path::Path;
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::default();
-    let src =
-        scenery.add_node(round_collimated_ray_source(millimeter!(5.0), joule!(1.0), 5).unwrap())?;
-    let wf = scenery.add_node(WaveFront::default().with_tilt(degree!(10.0, 0.0, 0.0))?)?;
-    let sd = scenery.add_node(SpotDiagram::default().with_tilt(degree!(30.0, 0.0, 0.0))?)?;
-    let det = scenery.add_node(RayPropagationVisualizer::default())?;
+    let src = scenery
+        .add_node(&round_collimated_ray_source(millimeter!(5.0), joule!(1.0), 5).unwrap())?;
+    let wf = scenery.add_node(&WaveFront::default().with_tilt(degree!(10.0, 0.0, 0.0))?)?;
+    let sd = scenery.add_node(&SpotDiagram::default().with_tilt(degree!(30.0, 0.0, 0.0))?)?;
+    let det = scenery.add_node(&RayPropagationVisualizer::default())?;
     scenery.connect_nodes(src, "out1", wf, "in1", millimeter!(20.0))?;
     scenery.connect_nodes(wf, "out1", sd, "in1", millimeter!(20.0))?;
     scenery.connect_nodes(sd, "out1", det, "in1", millimeter!(20.0))?;
diff --git a/opossum/examples/two_color_rays.rs b/opossum/examples/two_color_rays.rs
index bd2c635d2f43545f922b2c6007f0016609e6fde6..9e3a1a7be25478fc9826c6a368c91a3851b5a581 100644
--- a/opossum/examples/two_color_rays.rs
+++ b/opossum/examples/two_color_rays.rs
@@ -39,24 +39,24 @@ fn main() -> OpmResult<()> {
 
     let mut scenery = NodeGroup::default();
     let light = LightData::Geometric(rays_1w);
-    let src = scenery.add_node(Source::new("collimated ray source", &light))?;
-    let l1 = scenery.add_node(Lens::new(
+    let src = scenery.add_node(&Source::new("collimated ray source", &light))?;
+    let l1 = scenery.add_node(&Lens::new(
         "l1",
         millimeter!(200.0),
         millimeter!(-200.0),
         millimeter!(10.0),
         &RefrIndexConst::new(2.0).unwrap(),
     )?)?;
-    let l2 = scenery.add_node(Lens::new(
+    let l2 = scenery.add_node(&Lens::new(
         "l1",
         millimeter!(200.0),
         millimeter!(-200.0),
         millimeter!(10.0),
         &RefrIndexConst::new(2.0).unwrap(),
     )?)?;
-    let det = scenery.add_node(RayPropagationVisualizer::default())?;
-    // let wf = scenery.add_node(WaveFront::default());
-    let sd = scenery.add_node(SpotDiagram::default())?;
+    let det = scenery.add_node(&RayPropagationVisualizer::default())?;
+    // let wf = scenery.add_node(&WaveFront::default());
+    let sd = scenery.add_node(&SpotDiagram::default())?;
     scenery.connect_nodes(src, "out1", l1, "front", millimeter!(30.0))?;
     scenery.connect_nodes(l1, "rear", l2, "front", millimeter!(197.22992))?;
     scenery.connect_nodes(l2, "rear", det, "in1", millimeter!(30.0))?;
diff --git a/opossum/examples/two_srcs.rs b/opossum/examples/two_srcs.rs
index b438a22f146e19b05e51a42f959225f9f12695ad..cd160d672d70542e72a127ef6d070e567f61f23b 100644
--- a/opossum/examples/two_srcs.rs
+++ b/opossum/examples/two_srcs.rs
@@ -9,18 +9,18 @@ use std::path::Path;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::default();
-    let i_src1 = scenery.add_node(collimated_line_ray_source(
+    let i_src1 = scenery.add_node(&collimated_line_ray_source(
         millimeter!(20.0),
         joule!(1.0),
         21,
     )?)?;
-    let i_src2 = scenery.add_node(collimated_line_ray_source(
+    let i_src2 = scenery.add_node(&collimated_line_ray_source(
         millimeter!(20.0),
         joule!(1.0),
         21,
     )?)?;
-    let i_bs = scenery.add_node(BeamSplitter::default())?;
-    let i_sd = scenery.add_node(RayPropagationVisualizer::default())?;
+    let i_bs = scenery.add_node(&BeamSplitter::default())?;
+    let i_sd = scenery.add_node(&RayPropagationVisualizer::default())?;
 
     scenery.connect_nodes(i_src1, "out1", i_bs, "input1", millimeter!(100.0))?;
     scenery.connect_nodes(i_src2, "out1", i_bs, "input2", millimeter!(110.0))?;
diff --git a/opossum/examples/wavefront.rs b/opossum/examples/wavefront.rs
index e0dbe91119085a00fcdcd3649b0a832294f58213..bd0181658da9ac966d3b873bcbba73865599e07c 100644
--- a/opossum/examples/wavefront.rs
+++ b/opossum/examples/wavefront.rs
@@ -15,16 +15,16 @@ use uom::si::f64::Length;
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::new("Wavefont Demo");
     let source = round_collimated_ray_source(meter!(5e-3), joule!(1.), 15)?;
-    let i_s = scenery.add_node(source)?;
-    let i_wf1 = scenery.add_node(WaveFront::new("wf_monitor 1"))?;
-    let i_l = scenery.add_node(ParaxialSurface::new("lens", meter!(0.1))?)?;
-    let i_wf2 = scenery.add_node(WaveFront::new("wf_monitor 2"))?;
-    let i_sp = scenery.add_node(SpotDiagram::new("spot 3"))?;
-    let i_l2 = scenery.add_node(ParaxialSurface::new("lens", meter!(0.1))?)?;
-    let i_wf3 = scenery.add_node(WaveFront::new("wf_mon3"))?;
-    let i_r1 = scenery.add_node(RayPropagationVisualizer::new("ray_mon1", None)?)?;
-    let i_s1 = scenery.add_node(Spectrometer::new("spec_mon", SpectrometerType::Ideal))?;
-    let i_fl1 = scenery.add_node(FluenceDetector::new("fluence monitor"))?;
+    let i_s = scenery.add_node(&source)?;
+    let i_wf1 = scenery.add_node(&WaveFront::new("wf_monitor 1"))?;
+    let i_l = scenery.add_node(&ParaxialSurface::new("lens", meter!(0.1))?)?;
+    let i_wf2 = scenery.add_node(&WaveFront::new("wf_monitor 2"))?;
+    let i_sp = scenery.add_node(&SpotDiagram::new("spot 3"))?;
+    let i_l2 = scenery.add_node(&ParaxialSurface::new("lens", meter!(0.1))?)?;
+    let i_wf3 = scenery.add_node(&WaveFront::new("wf_mon3"))?;
+    let i_r1 = scenery.add_node(&RayPropagationVisualizer::new("ray_mon1", None)?)?;
+    let i_s1 = scenery.add_node(&Spectrometer::new("spec_mon", SpectrometerType::Ideal))?;
+    let i_fl1 = scenery.add_node(&FluenceDetector::new("fluence monitor"))?;
 
     scenery.connect_nodes(i_s, "out1", i_wf1, "in1", meter!(0.1))?;
     scenery.connect_nodes(i_wf1, "out1", i_l, "front", Length::zero())?;
diff --git a/opossum/files_for_testing/opm/optic_ref.opm b/opossum/files_for_testing/opm/optic_ref.opm
index eae11320cc186842a6c8fabd823a267679e6dfaa..0c78e26357c055b84e5fc886bad6b504ebfb159d 100644
--- a/opossum/files_for_testing/opm/optic_ref.opm
+++ b/opossum/files_for_testing/opm/optic_ref.opm
@@ -4,6 +4,7 @@ attributes:
   alignment: null
   isometry: null
   name: test123
+  uuid: 587ee70f-6f52-4420-89f6-e1618ff4dbdb
   ports:
     inputs:
       front: 
diff --git a/opossum/files_for_testing/opm/opticscenery.opm b/opossum/files_for_testing/opm/opticscenery.opm
index fe6b67f2de62d1ec5330bb0f29e17cf3ee5e3d1a..9bafe8e8fe51a3a0002dfc199cd6689d45340302 100644
--- a/opossum/files_for_testing/opm/opticscenery.opm
+++ b/opossum/files_for_testing/opm/opticscenery.opm
@@ -5,6 +5,7 @@ scenery:
     ports:
       inputs: {}
       outputs: {}
+    uuid: b8cb049b-4eb9-4db8-8842-2fc7c70c27ba
     props:
       expand view: !Bool false
       graph: !OpticGraph
@@ -13,6 +14,7 @@ scenery:
           id: 180328fe-7ad4-4568-b501-183b88c4daee
           attributes:
             name: dummy1
+            uuid: 180328fe-7ad4-4568-b501-183b88c4daee
             ports:
               inputs:
                 front:
@@ -28,6 +30,7 @@ scenery:
           id: 642ce76e-b071-43c0-a77e-1bdbb99b40d8
           attributes:
             name: dummy2
+            uuid: 642ce76e-b071-43c0-a77e-1bdbb99b40d8
             ports:
               inputs:
                 front:
diff --git a/opossum/src/analyzers/ghostfocus.rs b/opossum/src/analyzers/ghostfocus.rs
index 578d0bca951a76674454e486da4317d1f41f707a..8b5d76d89e4d9137f496eee75aee4f73fb02c145 100644
--- a/opossum/src/analyzers/ghostfocus.rs
+++ b/opossum/src/analyzers/ghostfocus.rs
@@ -1,20 +1,26 @@
 //! Analyzer performing a ghost focus analysis using ray tracing
+use std::collections::{hash_map::Values, HashMap};
+
 use chrono::Local;
 use log::{info, warn};
 use nalgebra::{MatrixXx2, MatrixXx3, Vector3};
 use plotters::style::RGBAColor;
 use serde::{Deserialize, Serialize};
-use uom::si::{f64::Length, length::millimeter};
+use uom::si::{f64::Length, length::millimeter, radiant_exposure::joule_per_square_centimeter};
+use uuid::Uuid;
 
 use crate::{
     error::{OpmResult, OpossumError},
     get_version,
     light_result::{LightRays, LightResult},
     millimeter,
-    nodes::NodeGroup,
+    nodes::{NodeGroup, OpticGraph},
     optic_node::OpticNode,
     plottable::{PlotArgs, PlotData, PlotParameters, PlotSeries, PlotType, Plottable},
-    properties::{Properties, Proptype},
+    properties::{
+        proptype::{count_str, format_value_with_prefix},
+        Properties, Proptype,
+    },
     rays::Rays,
     reporting::{analysis_report::AnalysisReport, node_report::NodeReport},
 };
@@ -98,7 +104,6 @@ impl Analyzer for GhostFocusAnalyzer {
                 scenery.add_to_accumulated_rays(rays, bounce);
             }
         }
-
         Ok(())
     }
     fn report(&self, scenery: &NodeGroup) -> OpmResult<AnalysisReport> {
@@ -115,14 +120,64 @@ impl Analyzer for GhostFocusAnalyzer {
         analysis_report.add_node_report(node_report);
         for node in scenery.graph().nodes() {
             let node_name = &node.optical_ref.borrow().name();
-            let uuid = node.uuid().as_simple().to_string();
-            let mut props = Properties::default();
             let hit_maps = node.optical_ref.borrow().hit_maps();
             for hit_map in &hit_maps {
-                props.create(hit_map.0, "surface hit map", None, hit_map.1.clone().into())?;
+                let critical_positions = hit_map.1.critical_fluences();
+                if !critical_positions.is_empty() {
+                    for (i, (rays_uuid, (fluence, hist_idx))) in
+                        critical_positions.iter().enumerate()
+                    {
+                        let mut hit_map_props = Properties::default();
+
+                        hit_map_props.create(
+                            "Peak fluence",
+                            "Peak fluence on this surface",
+                            None,
+                            format!(
+                                "{}J/cm²",
+                                format_value_with_prefix(
+                                    fluence.get::<joule_per_square_centimeter>()
+                                )
+                            )
+                            .into(),
+                        )?;
+
+                        let critical_ghost_hist = GhostFocusHistory::from((
+                            scenery.accumulated_rays(),
+                            *rays_uuid,
+                            *hist_idx,
+                        ));
+                        hit_map_props.create(
+                            "Origin",
+                            "Surface bounces that enabled this fluence",
+                            None,
+                            critical_ghost_hist
+                                .rays_origin_report_str(scenery.graph())
+                                .into(),
+                        )?;
+
+                        hit_map_props.create(
+                            "Ray propagation",
+                            "ray propagation",
+                            None,
+                            Proptype::from(critical_ghost_hist),
+                        )?;
+                        let hit_map_report = NodeReport::new(
+                            "surface",
+                            &format!(
+                                "{} critical fluence on surface '{}' of node '{}'",
+                                count_str(i + 1),
+                                hit_map.0,
+                                node_name
+                            ),
+                            &Uuid::new_v4().as_simple().to_string(),
+                            hit_map_props,
+                        );
+
+                        analysis_report.add_node_report(hit_map_report);
+                    }
+                }
             }
-            let node_report = NodeReport::new("hitmap", node_name, &uuid, props);
-            analysis_report.add_node_report(node_report);
         }
         analysis_report.set_analysis_type("Ghost Focus Analysis");
         Ok(analysis_report)
@@ -154,13 +209,58 @@ pub trait AnalysisGhostFocus: OpticNode + AnalysisRayTrace {
     }
 }
 
+///Struct to store the node origin uuid and parent ray bundle Uuid of a ray bundle
+#[derive(Serialize, Deserialize, Clone, Debug, Default)]
+pub struct RaysOrigin {
+    parent_rays: Option<Uuid>,
+    node_origin: Option<Uuid>,
+}
+impl RaysOrigin {
+    ///creates a new [`RaysOrigin`]
+    #[must_use]
+    pub const fn new(parent_rays: Option<Uuid>, node_origin: Option<Uuid>) -> Self {
+        Self {
+            parent_rays,
+            node_origin,
+        }
+    }
+}
+
+/// Struct to store the correlation between a ray bundle and its parent ray bundle as well as its node origin
+#[derive(Serialize, Deserialize, Clone, Debug, Default)]
+pub struct RaysNodeCorrelation {
+    correlation: HashMap<Uuid, RaysOrigin>,
+}
+impl RaysNodeCorrelation {
+    ///creates a new [`RaysNodeCorrelation`]
+    #[must_use]
+    pub fn new(rays_uuid: &Uuid, rays_origin: &RaysOrigin) -> Self {
+        let mut correlation = HashMap::<Uuid, RaysOrigin>::new();
+        correlation.insert(*rays_uuid, rays_origin.clone());
+        Self { correlation }
+    }
+
+    /// inserts a key value pair in the correlation hashmap
+    pub fn insert(&mut self, k: &Uuid, v: &RaysOrigin) {
+        self.correlation.insert(*k, v.clone());
+    }
+
+    /// returns the values of the correlation hashmap
+    #[must_use]
+    pub fn values(&self) -> Values<'_, Uuid, RaysOrigin> {
+        self.correlation.values()
+    }
+}
+
 /// struct that holds the history of the ray positions that is needed for report generation
 #[derive(Serialize, Deserialize, Clone, Debug)]
 pub struct GhostFocusHistory {
     /// vector of ray positions for each raybundle at a specifc spectral position
     pub rays_pos_history: Vec<Vec<Vec<MatrixXx3<Length>>>>,
-    /// view direction if the rayposition thistory is plotted
+    /// view direction if the ray position history is plotted
     pub plot_view_direction: Option<Vector3<f64>>,
+    ///stores the corrleation between a rays bundle and its parent node as well as parent ray bundle for each bounce in a vector
+    pub ray_node_correlation: Vec<RaysNodeCorrelation>,
 }
 impl GhostFocusHistory {
     /// Projects the positions o fthie [`GhostFocusHistory`] onto a 2D plane
@@ -236,32 +336,125 @@ impl GhostFocusHistory {
 
         Ok(projected_history)
     }
+
+    fn add_specific_ray_history(
+        &mut self,
+        accumulated_rays: &Vec<HashMap<Uuid, Rays>>,
+        rays_uuid: &Uuid,
+        hist_idx: usize,
+    ) {
+        for (bounce, ray_vecs_in_bounce) in accumulated_rays.iter().enumerate() {
+            if ray_vecs_in_bounce.contains_key(rays_uuid) {
+                let mut rays_per_bounce_history =
+                    Vec::<Vec<MatrixXx3<Length>>>::with_capacity(ray_vecs_in_bounce.len());
+                if let Some(rays) = ray_vecs_in_bounce.get(rays_uuid) {
+                    let mut rays_history =
+                        Vec::<MatrixXx3<Length>>::with_capacity(rays.nr_of_rays(true));
+                    for ray in rays {
+                        if let Some(ray_hist) = ray.position_history_from_to(0, hist_idx) {
+                            rays_history.push(ray_hist);
+                        }
+                    }
+                    rays_per_bounce_history.push(rays_history);
+                    self.ray_node_correlation[bounce].insert(
+                        rays.uuid(),
+                        &RaysOrigin::new(*rays.parent_id(), *rays.node_origin()),
+                    );
+
+                    self.rays_pos_history[bounce] = rays_per_bounce_history;
+                    if let Some(parent_uuid) = rays.parent_id() {
+                        self.add_specific_ray_history(
+                            accumulated_rays,
+                            parent_uuid,
+                            *rays.parent_pos_split_idx(),
+                        );
+                    }
+                }
+                break;
+            }
+        }
+    }
+
+    ///Returns the report string for the critical ray origin in the ghost focus analysis
+    #[must_use]
+    pub fn rays_origin_report_str(&self, graph: &OpticGraph) -> String {
+        let mut report_str = String::new();
+        for (bounce, rays_correlation) in self.ray_node_correlation.iter().enumerate() {
+            if bounce == 0 {
+                report_str += "Origin at node '";
+            } else {
+                report_str += format!("bounce {bounce} at node '").as_str();
+            }
+            for rays_origin in rays_correlation.values() {
+                if let Some(node_uuid) = rays_origin.node_origin {
+                    if let Some(opt_ref) = graph.node_by_uuid(node_uuid) {
+                        report_str +=
+                            format!("{}', ", opt_ref.optical_ref.borrow().name()).as_str();
+                    }
+                }
+            }
+        }
+        report_str
+    }
 }
 
-impl From<Vec<Vec<Rays>>> for GhostFocusHistory {
-    fn from(value: Vec<Vec<Rays>>) -> Self {
+impl From<Vec<HashMap<Uuid, Rays>>> for GhostFocusHistory {
+    fn from(value: Vec<HashMap<Uuid, Rays>>) -> Self {
         let mut ghost_focus_history =
             Vec::<Vec<Vec<MatrixXx3<Length>>>>::with_capacity(value.len());
+        let mut ray_node_correlation = Vec::<RaysNodeCorrelation>::with_capacity(value.len());
         for ray_vecs_in_bounce in &value {
             let mut rays_per_bounce_history =
                 Vec::<Vec<MatrixXx3<Length>>>::with_capacity(ray_vecs_in_bounce.len());
-            for rays in ray_vecs_in_bounce {
+            let mut ray_node_bounce_correlation = RaysNodeCorrelation::default();
+            for rays in ray_vecs_in_bounce.values() {
                 let mut rays_history =
                     Vec::<MatrixXx3<Length>>::with_capacity(rays.nr_of_rays(false));
                 for ray in rays {
                     rays_history.push(ray.position_history());
                 }
+                ray_node_bounce_correlation.insert(
+                    rays.uuid(),
+                    &RaysOrigin::new(*rays.parent_id(), *rays.node_origin()),
+                );
                 rays_per_bounce_history.push(rays_history);
             }
             ghost_focus_history.push(rays_per_bounce_history);
+            ray_node_correlation.push(ray_node_bounce_correlation);
         }
         Self {
             rays_pos_history: ghost_focus_history,
             plot_view_direction: None,
+            ray_node_correlation,
         }
     }
 }
 
+impl From<(&Vec<HashMap<Uuid, Rays>>, Uuid, usize)> for GhostFocusHistory {
+    ///value contains :
+    /// 0: a vector of Hashmaps that contain Rays. Same structure as the `accumulated_rays` in [`NodeGroup`]
+    /// 1: the uuid of a ray bundle within field 0
+    /// 2: the index of the position in the ray position history up to which it should be displayed
+    fn from(value: (&Vec<HashMap<Uuid, Rays>>, Uuid, usize)) -> Self {
+        let (acc_rays, rays_uuid, hist_idx) = value;
+        let mut ray_pos_history = Vec::<Vec<Vec<MatrixXx3<Length>>>>::with_capacity(acc_rays.len());
+        let mut ray_node_correlation = Vec::<RaysNodeCorrelation>::with_capacity(acc_rays.len());
+        for _i in 0..acc_rays.len() {
+            ray_pos_history.push(Vec::<Vec<MatrixXx3<Length>>>::new());
+            ray_node_correlation.push(RaysNodeCorrelation::default());
+        }
+        let mut ghost_focus_history = Self {
+            rays_pos_history: ray_pos_history,
+            plot_view_direction: None,
+            ray_node_correlation,
+        };
+
+        ghost_focus_history.add_specific_ray_history(acc_rays, &rays_uuid, hist_idx);
+
+        ghost_focus_history
+    }
+}
+
 impl Plottable for GhostFocusHistory {
     fn add_plot_specific_params(&self, plt_params: &mut PlotParameters) -> OpmResult<()> {
         plt_params
diff --git a/opossum/src/analyzers/raytrace.rs b/opossum/src/analyzers/raytrace.rs
index 496cdfd5f8cfe3303f39a6795ace86a302904a7a..9935e5086dcadace61a9961e7e655f85716dbff0 100644
--- a/opossum/src/analyzers/raytrace.rs
+++ b/opossum/src/analyzers/raytrace.rs
@@ -93,6 +93,7 @@ pub trait AnalysisRayTrace: OpticNode + Surface {
         backward: bool,
         port_name: &str,
     ) -> OpmResult<()> {
+        let uuid = *self.node_attr().uuid();
         if backward {
             for rays in &mut *rays_bundle {
                 if let Some(aperture) = self.ports().aperture(&PortType::Input, port_name) {
@@ -104,7 +105,13 @@ pub trait AnalysisRayTrace: OpticNode + Surface {
                     return Err(OpossumError::OpticPort("output aperture not found".into()));
                 };
                 let surf = self.get_surface_mut(port_name);
-                let reflected_rear = rays.refract_on_surface(surf, Some(refri))?;
+                let mut reflected_rear = rays.refract_on_surface(surf, Some(refri))?;
+                reflected_rear.set_node_origin_uuid(uuid);
+
+                if let AnalyzerType::GhostFocus(_) = analyzer_type {
+                    surf.evaluate_fluence_of_ray_bundle(rays)?;
+                }
+
                 surf.add_to_forward_rays_cache(reflected_rear);
             }
             for rays in self.get_surface_mut(port_name).backwards_rays_cache() {
@@ -121,7 +128,11 @@ pub trait AnalysisRayTrace: OpticNode + Surface {
                     return Err(OpossumError::OpticPort("input aperture not found".into()));
                 };
                 let surf = self.get_surface_mut(port_name);
-                let reflected_front = rays.refract_on_surface(surf, Some(refri))?;
+                let mut reflected_front = rays.refract_on_surface(surf, Some(refri))?;
+                reflected_front.set_node_origin_uuid(uuid);
+                if let AnalyzerType::GhostFocus(_) = analyzer_type {
+                    surf.evaluate_fluence_of_ray_bundle(rays)?;
+                }
                 surf.add_to_backward_rays_cache(reflected_front);
             }
             for rays in self.get_surface_mut(port_name).forward_rays_cache() {
@@ -142,10 +153,17 @@ pub trait AnalysisRayTrace: OpticNode + Surface {
         backward: bool,
         port_name: &str,
     ) -> OpmResult<()> {
+        let uuid: uuid::Uuid = *self.node_attr().uuid();
         let surf = self.get_surface_mut(port_name);
         if backward {
             for rays in &mut *rays_bundle {
-                let reflected_front = rays.refract_on_surface(surf, Some(refri))?;
+                let mut reflected_front = rays.refract_on_surface(surf, Some(refri))?;
+                reflected_front.set_node_origin_uuid(uuid);
+
+                if let AnalyzerType::GhostFocus(_) = analyzer_type {
+                    surf.evaluate_fluence_of_ray_bundle(rays)?;
+                }
+
                 surf.add_to_forward_rays_cache(reflected_front);
             }
             for rays in surf.backwards_rays_cache() {
@@ -163,7 +181,11 @@ pub trait AnalysisRayTrace: OpticNode + Surface {
             }
         } else {
             for rays in &mut *rays_bundle {
-                let reflected_rear = rays.refract_on_surface(surf, Some(refri))?;
+                let mut reflected_rear = rays.refract_on_surface(surf, Some(refri))?;
+                reflected_rear.set_node_origin_uuid(uuid);
+                if let AnalyzerType::GhostFocus(_) = analyzer_type {
+                    surf.evaluate_fluence_of_ray_bundle(rays)?;
+                }
                 surf.add_to_backward_rays_cache(reflected_rear);
             }
             for rays in surf.forward_rays_cache() {
@@ -188,12 +210,19 @@ pub trait AnalysisRayTrace: OpticNode + Surface {
     /// This functionis used for the propagation through ideal detectors, such as a spot diagram
     /// # Errors
     /// This function errors if the effective isometry is not defined
-    fn pass_through_inert_surface(&mut self, rays_bundle: &mut Vec<Rays>) -> OpmResult<()> {
+    fn pass_through_inert_surface(
+        &mut self,
+        rays_bundle: &mut Vec<Rays>,
+        analyzer_type: &AnalyzerType,
+    ) -> OpmResult<()> {
         if let Some(iso) = self.effective_iso() {
             let surf = self.get_surface_mut("");
             surf.set_isometry(&iso);
             for rays in &mut *rays_bundle {
                 rays.refract_on_surface(surf, None)?;
+                if let AnalyzerType::GhostFocus(_) = analyzer_type {
+                    surf.evaluate_fluence_of_ray_bundle(rays)?;
+                }
             }
         } else {
             return Err(OpossumError::Analysis(
@@ -400,10 +429,10 @@ mod test {
         // simulate simple system for integration test
         let mut group = NodeGroup::default();
         let i_src = group
-            .add_node(round_collimated_ray_source(millimeter!(10.0), joule!(1.0), 3).unwrap())
+            .add_node(&round_collimated_ray_source(millimeter!(10.0), joule!(1.0), 3).unwrap())
             .unwrap();
         let i_l1 = group
-            .add_node(ParaxialSurface::new("f=100", millimeter!(100.0)).unwrap())
+            .add_node(&ParaxialSurface::new("f=100", millimeter!(100.0)).unwrap())
             .unwrap();
         group
             .connect_nodes(i_src, "out1", i_l1, "front", millimeter!(50.0))
diff --git a/opossum/src/dottable.rs b/opossum/src/dottable.rs
index 078d01be0ff284f0b40f4d73df9a176d1e908e31..fd1b3e036b3ce0c962f8c775253f3fc6445fc3a0 100644
--- a/opossum/src/dottable.rs
+++ b/opossum/src/dottable.rs
@@ -365,7 +365,7 @@ mod test {
         let file_content_lr = get_file_content("./files_for_testing/dot/to_dot_w_node_LR.dot");
 
         let mut scenery = NodeGroup::default();
-        scenery.add_node(Dummy::new("Test")).unwrap();
+        scenery.add_node(&Dummy::new("Test")).unwrap();
 
         let scenery_dot_str_tb = scenery.toplevel_dot("TB").unwrap();
         let scenery_dot_str_lr = scenery.toplevel_dot("LR").unwrap();
@@ -381,19 +381,19 @@ mod test {
 
         let mut scenery = NodeGroup::default();
         let i_s = scenery
-            .add_node(Source::new("Source", &LightData::Fourier))
+            .add_node(&Source::new("Source", &LightData::Fourier))
             .unwrap();
         let bs = BeamSplitter::new("test", &SplittingConfig::Ratio(0.6)).unwrap();
         // bs.node_attr_mut().set_name("Beam splitter");
-        let i_bs = scenery.add_node(bs).unwrap();
+        let i_bs = scenery.add_node(&bs).unwrap();
         let i_d1 = scenery
-            .add_node(EnergyMeter::new(
+            .add_node(&EnergyMeter::new(
                 "Energy meter 1",
                 Metertype::IdealEnergyMeter,
             ))
             .unwrap();
         let i_d2 = scenery
-            .add_node(EnergyMeter::new(
+            .add_node(&EnergyMeter::new(
                 "Energy meter 2",
                 Metertype::IdealEnergyMeter,
             ))
@@ -421,8 +421,8 @@ mod test {
         let mut scenery = NodeGroup::default();
         let mut group1 = NodeGroup::new("group 1");
         group1.set_expand_view(true).unwrap();
-        let g1_n1 = group1.add_node(Dummy::new("node1")).unwrap();
-        let g1_n2 = group1.add_node(BeamSplitter::default()).unwrap();
+        let g1_n1 = group1.add_node(&Dummy::new("node1")).unwrap();
+        let g1_n2 = group1.add_node(&BeamSplitter::default()).unwrap();
         group1
             .map_output_port(g1_n2, "out1_trans1_refl2", "out1")
             .unwrap();
@@ -431,8 +431,8 @@ mod test {
             .unwrap();
 
         let mut nested_group = NodeGroup::new("group 1_1");
-        let nested_g_n1 = nested_group.add_node(Dummy::new("node1_1")).unwrap();
-        let nested_g_n2 = nested_group.add_node(Dummy::new("node1_2")).unwrap();
+        let nested_g_n1 = nested_group.add_node(&Dummy::new("node1_1")).unwrap();
+        let nested_g_n2 = nested_group.add_node(&Dummy::new("node1_2")).unwrap();
         nested_group.set_expand_view(true).unwrap();
 
         nested_group
@@ -445,23 +445,23 @@ mod test {
             .map_output_port(nested_g_n2, "rear", "out1")
             .unwrap();
 
-        let nested_group_index = group1.add_node(nested_group).unwrap();
+        let nested_group_index = group1.add_node(&nested_group).unwrap();
         group1
             .connect_nodes(nested_group_index, "out1", g1_n1, "front", Length::zero())
             .unwrap();
 
         let mut group2: NodeGroup = NodeGroup::new("group 2");
         group2.set_expand_view(false).unwrap();
-        let g2_n1 = group2.add_node(Dummy::new("node2_1")).unwrap();
-        let g2_n2 = group2.add_node(Dummy::new("node2_2")).unwrap();
+        let g2_n1 = group2.add_node(&Dummy::new("node2_1")).unwrap();
+        let g2_n2 = group2.add_node(&Dummy::new("node2_2")).unwrap();
         group2.map_input_port(g2_n1, "front", "in1").unwrap();
 
         group2
             .connect_nodes(g2_n1, "rear", g2_n2, "front", Length::zero())
             .unwrap();
 
-        let scene_g1 = scenery.add_node(group1).unwrap();
-        let scene_g2 = scenery.add_node(group2).unwrap();
+        let scene_g1 = scenery.add_node(&group1).unwrap();
+        let scene_g2 = scenery.add_node(&group2).unwrap();
 
         // set_output_port
         scenery
diff --git a/opossum/src/nodes/beam_splitter.rs b/opossum/src/nodes/beam_splitter.rs
index ef1463d0be04ac9acf19b822af80ba0174cf47a3..58a8ae6d3abcbfbb2247c32f28c6ea42bbc894c3 100644
--- a/opossum/src/nodes/beam_splitter.rs
+++ b/opossum/src/nodes/beam_splitter.rs
@@ -19,7 +19,7 @@ use crate::{
     utils::EnumProxy,
 };
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 /// An ideal beamsplitter node with a given splitting ratio.
 ///
 /// ## Optical Ports
diff --git a/opossum/src/nodes/cylindric_lens/mod.rs b/opossum/src/nodes/cylindric_lens/mod.rs
index f0f0547fd045497212dfaba0be9d77b71841e380..1d96efa4eb55f7c173e1663ce4ab03ec84e22c5e 100644
--- a/opossum/src/nodes/cylindric_lens/mod.rs
+++ b/opossum/src/nodes/cylindric_lens/mod.rs
@@ -26,7 +26,7 @@ mod analysis_energy;
 mod analysis_ghostfocus;
 mod analysis_raytrace;
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 /// A real cylindric lens with spherical (or flat) surfaces. By default, the curvature is aligned along the (local) y axis.
 ///
 ///
diff --git a/opossum/src/nodes/detector.rs b/opossum/src/nodes/detector.rs
index 4a0b3b3c002e29f913259ba8964644519c34b4b8..5a3a2123d649fc684c81e9a6cf93eb51276d0303 100644
--- a/opossum/src/nodes/detector.rs
+++ b/opossum/src/nodes/detector.rs
@@ -34,6 +34,7 @@ use std::fmt::Debug;
 ///
 /// During analysis, the output port contains a replica of the input port similar to a [`Dummy`](crate::nodes::Dummy) node. This way,
 /// different detector nodes can be "stacked" or used somewhere in between arbitrary optic nodes.
+#[derive(Clone)]
 pub struct Detector {
     light_data: Option<LightData>,
     node_attr: NodeAttr,
@@ -168,6 +169,7 @@ impl AnalysisRayTrace for Detector {
         self.light_data = Some(ld);
     }
 }
+
 impl Surface for Detector {
     fn get_surface_mut(&mut self, _surf_name: &str) -> &mut OpticalSurface {
         todo!()
diff --git a/opossum/src/nodes/energy_meter.rs b/opossum/src/nodes/energy_meter.rs
index 0737cc6f6cd092609c824e05dd4bbb0d11009d4e..13df2d6d6d338be6166c4bfc6ab6db7a91326bfa 100644
--- a/opossum/src/nodes/energy_meter.rs
+++ b/opossum/src/nodes/energy_meter.rs
@@ -63,6 +63,7 @@ impl From<Metertype> for Proptype {
 ///
 /// During analysis, the output port contains a replica of the input port similar to a [`Dummy`](crate::nodes::Dummy) node. This way,
 /// different dectector nodes can be "stacked" or used somewhere in between arbitrary optic nodes.
+#[derive(Clone)]
 pub struct EnergyMeter {
     light_data: Option<LightData>,
     node_attr: NodeAttr,
diff --git a/opossum/src/nodes/fluence_detector.rs b/opossum/src/nodes/fluence_detector.rs
index 1ba600ca179b02b801068e19171b5cf17086ba67..4048710359fa09c01caf476d7b50c8359a7a510d 100644
--- a/opossum/src/nodes/fluence_detector.rs
+++ b/opossum/src/nodes/fluence_detector.rs
@@ -10,7 +10,7 @@ use super::node_attr::NodeAttr;
 use crate::{
     analyzers::{
         energy::AnalysisEnergy, ghostfocus::AnalysisGhostFocus, raytrace::AnalysisRayTrace,
-        Analyzable, GhostFocusConfig, RayTraceConfig,
+        Analyzable, AnalyzerType, GhostFocusConfig, RayTraceConfig,
     },
     dottable::Dottable,
     error::{OpmResult, OpossumError},
@@ -199,7 +199,10 @@ impl AnalysisGhostFocus for FluenceDetector {
             return Ok(out_light_rays);
         };
         let mut rays = bouncing_rays.clone();
-        self.pass_through_inert_surface(&mut rays)?;
+        self.pass_through_inert_surface(
+            &mut rays,
+            &AnalyzerType::GhostFocus(GhostFocusConfig::default()),
+        )?;
 
         let mut out_light_rays = LightRays::default();
         out_light_rays.insert(out_port.to_string(), rays.clone());
diff --git a/opossum/src/nodes/ideal_filter.rs b/opossum/src/nodes/ideal_filter.rs
index 15067d1cdad863ff25801e95e3c7d386d40efa77..e4a598f8bfff1c94eba91a551c83c14e5275c780 100644
--- a/opossum/src/nodes/ideal_filter.rs
+++ b/opossum/src/nodes/ideal_filter.rs
@@ -26,7 +26,7 @@ pub enum FilterType {
     /// filter based on given transmission spectrum.
     Spectrum(Spectrum),
 }
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 /// An ideal filter with given transmission or optical density.
 ///
 /// ## Optical Ports
diff --git a/opossum/src/nodes/lens/analysis_ghostfocus.rs b/opossum/src/nodes/lens/analysis_ghostfocus.rs
index b3a42a319bf87959e868622970808993ad773c1c..357c4ba8786e432ed285f607b5bd96c27c1d595b 100644
--- a/opossum/src/nodes/lens/analysis_ghostfocus.rs
+++ b/opossum/src/nodes/lens/analysis_ghostfocus.rs
@@ -46,7 +46,6 @@ impl AnalysisGhostFocus for Lens {
         };
 
         let mut rays_bundle = incoming_rays.clone();
-
         self.enter_through_surface(
             &mut rays_bundle,
             &AnalyzerType::GhostFocus(config.clone()),
diff --git a/opossum/src/nodes/lens/mod.rs b/opossum/src/nodes/lens/mod.rs
index e9f2fd9014fb343d9231a04c4fd97634cd29a2a5..053d75ed213b19865969e8505b875ed2d0bc8cf4 100644
--- a/opossum/src/nodes/lens/mod.rs
+++ b/opossum/src/nodes/lens/mod.rs
@@ -26,7 +26,7 @@ mod analysis_energy;
 mod analysis_ghostfocus;
 mod analysis_raytrace;
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 /// A real lens with spherical (or flat) surfaces.
 ///
 ///
diff --git a/opossum/src/nodes/node_attr.rs b/opossum/src/nodes/node_attr.rs
index 843bf66b359ba89c8f269c56d48c669680df48f4..9c6eeef07256e46baa429b5e195ae773a279c74c 100644
--- a/opossum/src/nodes/node_attr.rs
+++ b/opossum/src/nodes/node_attr.rs
@@ -6,6 +6,7 @@ use std::{cell::RefCell, rc::Rc};
 use petgraph::graph::NodeIndex;
 use serde::{Deserialize, Serialize};
 use uom::si::f64::Length;
+use uuid::Uuid;
 
 use crate::{
     error::{OpmResult, OpossumError},
@@ -22,6 +23,7 @@ pub struct NodeAttr {
     node_type: String,
     name: String,
     ports: OpticPorts,
+    uuid: Uuid,
     #[serde(default)]
     props: Properties,
     #[serde(skip_serializing_if = "Option::is_none")]
@@ -68,6 +70,7 @@ impl NodeAttr {
             inverted: false,
             alignment: None,
             align_like_node_at_distance: None,
+            uuid: Uuid::new_v4(),
         }
     }
     /// Returns the name property of this node.
@@ -198,6 +201,16 @@ impl NodeAttr {
         self.ports = ports;
     }
 
+    /// Returns a reference to the uuid of this [`NodeAttr`].
+    #[must_use]
+    pub const fn uuid(&self) -> &Uuid {
+        &self.uuid
+    }
+    ///Sets the uuid of this [`NodeAttr`].
+    pub fn set_uuid(&mut self, uuid: &Uuid) {
+        self.uuid = *uuid;
+    }
+
     /// set the nodeindex and distance of the node to which this node should be aligned to
     pub fn set_align_like_node_at_distance(&mut self, node_idx: NodeIndex, distance: Length) {
         self.align_like_node_at_distance = Some((node_idx, distance));
diff --git a/opossum/src/nodes/node_group/analysis_ghostfocus.rs b/opossum/src/nodes/node_group/analysis_ghostfocus.rs
index cfdc2fb20a157987eb66225de400bdd4cdba2d97..0846be6a5d4224163c84ca21cfb43823ba6a0dd0 100644
--- a/opossum/src/nodes/node_group/analysis_ghostfocus.rs
+++ b/opossum/src/nodes/node_group/analysis_ghostfocus.rs
@@ -66,10 +66,6 @@ impl AnalysisGhostFocus for NodeGroup {
                 let outgoing_edges = light_rays_to_light_result(outgoing_edges);
 
                 for outgoing_edge in outgoing_edges {
-                    println!(
-                        "{}",
-                        self.graph.node_by_idx(idx)?.optical_ref.borrow().name()
-                    );
                     let no_sink =
                         self.graph
                             .set_outgoing_edge_data(idx, &outgoing_edge.0, &outgoing_edge.1);
diff --git a/opossum/src/nodes/node_group/mod.rs b/opossum/src/nodes/node_group/mod.rs
index c85f126a88c5aefbc26415217282dfc49cf81259..2efdc2c1e90668a91f77b0d18d87a69b9500d204 100644
--- a/opossum/src/nodes/node_group/mod.rs
+++ b/opossum/src/nodes/node_group/mod.rs
@@ -9,6 +9,7 @@ use crate::{
     dottable::Dottable,
     error::{OpmResult, OpossumError},
     get_version,
+    lightdata::LightData,
     optic_node::OpticNode,
     optic_ports::{OpticPorts, PortType},
     optic_ref::OpticRef,
@@ -16,15 +17,22 @@ use crate::{
     rays::Rays,
     reporting::{analysis_report::AnalysisReport, node_report::NodeReport},
     surface::{OpticalSurface, Surface},
+    utils::EnumProxy,
     SceneryResources,
 };
 use chrono::Local;
 pub use optic_graph::OpticGraph;
 use petgraph::prelude::NodeIndex;
 use serde::{Deserialize, Serialize};
-use std::{cell::RefCell, collections::BTreeMap, io::Write, rc::Rc};
+use std::{
+    cell::RefCell,
+    collections::{BTreeMap, HashMap},
+    io::Write,
+    rc::Rc,
+};
 use tempfile::NamedTempFile;
 use uom::si::f64::Length;
+use uuid::Uuid;
 #[derive(Debug, Clone, Serialize, Deserialize)]
 /// The basic building block of an optical system. It represents a group of other optical
 /// nodes ([`OpticNode`]s) arranged in a (sub)graph.
@@ -38,8 +46,8 @@ use uom::si::f64::Length;
 ///
 /// fn main() -> OpmResult<()> {
 ///   let mut scenery = NodeGroup::new("OpticScenery demo");
-///   let node1 = scenery.add_node(Dummy::new("dummy1"))?;
-///   let node2 = scenery.add_node(Dummy::new("dummy2"))?;
+///   let node1 = scenery.add_node(&Dummy::new("dummy1"))?;
+///   let node2 = scenery.add_node(&Dummy::new("dummy2"))?;
 ///   scenery.connect_nodes(node1, "rear", node2, "front", millimeter!(100.0))?;
 ///   Ok(())
 /// }
@@ -71,7 +79,7 @@ pub struct NodeGroup {
     #[serde(skip)]
     input_port_distances: BTreeMap<String, Length>,
     #[serde(skip)]
-    accumulated_rays: Vec<Vec<Rays>>,
+    accumulated_rays: Vec<HashMap<Uuid, Rays>>,
 }
 impl Default for NodeGroup {
     fn default() -> Self {
@@ -91,7 +99,7 @@ impl Default for NodeGroup {
             graph: OpticGraph::default(),
             input_port_distances: BTreeMap::default(),
             node_attr,
-            accumulated_rays: Vec::<Vec<Rays>>::new(),
+            accumulated_rays: Vec::<HashMap<Uuid, Rays>>::new(),
         }
     }
 }
@@ -116,12 +124,41 @@ impl NodeGroup {
     ///
     /// # Panics
     /// This function panics if the property "graph" can not be updated. Produces an error of type [`OpossumError::Properties`]
-    pub fn add_node<T: Analyzable + 'static>(&mut self, node: T) -> OpmResult<NodeIndex> {
-        let idx = self.graph.add_node(node);
+    pub fn add_node<T: Analyzable + Clone + 'static>(&mut self, node: &T) -> OpmResult<NodeIndex> {
+        let idx = self.graph.add_node(node.clone())?;
+
+        // save uuid of node in rays if present
+        self.store_node_uuid_in_rays_bundle(node, idx)?;
+
         self.node_attr
             .set_property("graph", self.graph.clone().into())
             .unwrap();
-        idx
+        Ok(idx)
+    }
+
+    fn store_node_uuid_in_rays_bundle<T: Analyzable + Clone + 'static>(
+        &mut self,
+        node: &T,
+        node_idx: NodeIndex,
+    ) -> OpmResult<()> {
+        if let Ok(Proptype::LightData(ld)) = node.node_attr().get_property("light data") {
+            if let Some(LightData::Geometric(rays)) = &ld.value {
+                let node_from_graph = self.graph_mut().node_by_idx_mut(node_idx)?;
+
+                let mut new_rays = rays.clone();
+                new_rays.set_node_origin_uuid(node_from_graph.uuid());
+
+                let mut node_ref = node_from_graph.optical_ref.borrow_mut();
+                node_ref.node_attr_mut().set_property(
+                    "light data",
+                    EnumProxy::<Option<LightData>> {
+                        value: Some(LightData::Geometric(new_rays)),
+                    }
+                    .into(),
+                )?;
+            }
+        }
+        Ok(())
     }
     /// Return a reference to the optical node specified by its [`NodeIndex`].
     ///
@@ -372,7 +409,7 @@ impl NodeGroup {
     /// This function returns a bundle of all rays that propagated in a group after a ghost focus analysis.
     /// This function is in particular helpful for generating a global ray propagation plot.
     #[must_use]
-    pub const fn accumulated_rays(&self) -> &Vec<Vec<Rays>> {
+    pub const fn accumulated_rays(&self) -> &Vec<HashMap<Uuid, Rays>> {
         &self.accumulated_rays
     }
 
@@ -382,9 +419,11 @@ impl NodeGroup {
     /// - bounce: bouncle level of these rays
     pub fn add_to_accumulated_rays(&mut self, rays: &Rays, bounce: usize) {
         if self.accumulated_rays.len() <= bounce {
-            self.accumulated_rays.push(vec![rays.clone()]);
+            let mut hashed_rays = HashMap::<Uuid, Rays>::new();
+            hashed_rays.insert(*rays.uuid(), rays.clone());
+            self.accumulated_rays.push(hashed_rays);
         } else {
-            self.accumulated_rays[bounce].push(rays.clone());
+            self.accumulated_rays[bounce].insert(*rays.uuid(), rays.clone());
         }
     }
 
@@ -466,7 +505,7 @@ impl OpticNode for NodeGroup {
         for node in nodes {
             node.optical_ref.borrow_mut().reset_data();
         }
-        self.accumulated_rays = Vec::<Vec<Rays>>::new();
+        self.accumulated_rays = Vec::<HashMap<Uuid, Rays>>::new();
     }
 }
 
@@ -548,8 +587,8 @@ mod test {
     #[test]
     fn ports() {
         let mut og = NodeGroup::default();
-        let sn1_i = og.add_node(Dummy::default()).unwrap();
-        let sn2_i = og.add_node(Dummy::default()).unwrap();
+        let sn1_i = og.add_node(&Dummy::default()).unwrap();
+        let sn2_i = og.add_node(&Dummy::default()).unwrap();
         og.connect_nodes(sn1_i, "rear", sn2_i, "front", Length::zero())
             .unwrap();
         assert!(og.ports().names(&PortType::Input).is_empty());
@@ -568,8 +607,8 @@ mod test {
     #[test]
     fn ports_inverted() {
         let mut og = NodeGroup::default();
-        let sn1_i = og.add_node(Dummy::default()).unwrap();
-        let sn2_i = og.add_node(Dummy::default()).unwrap();
+        let sn1_i = og.add_node(&Dummy::default()).unwrap();
+        let sn2_i = og.add_node(&Dummy::default()).unwrap();
         og.connect_nodes(sn1_i, "rear", sn2_i, "front", Length::zero())
             .unwrap();
         og.map_input_port(sn1_i, "front", "input").unwrap();
@@ -587,7 +626,7 @@ mod test {
     #[test]
     fn report() {
         let mut scenery = NodeGroup::default();
-        scenery.add_node(Detector::default()).unwrap();
+        scenery.add_node(&Detector::default()).unwrap();
         let report = scenery.toplevel_report().unwrap();
         assert!(serde_yaml::to_string(&report).is_ok());
         // How shall we further parse the output?
@@ -601,8 +640,8 @@ mod test {
     #[test]
     fn analyze_dummy() {
         let mut scenery = NodeGroup::default();
-        let node1 = scenery.add_node(Dummy::default()).unwrap();
-        let node2 = scenery.add_node(Dummy::default()).unwrap();
+        let node1 = scenery.add_node(&Dummy::default()).unwrap();
+        let node2 = scenery.add_node(&Dummy::default()).unwrap();
         scenery
             .connect_nodes(node1, "rear", node2, "front", Length::zero())
             .unwrap();
@@ -624,11 +663,11 @@ mod test {
         );
         let mut scenery = NodeGroup::default();
         let i_s = scenery
-            .add_node(Source::new("src", &LightData::Geometric(rays)))
+            .add_node(&Source::new("src", &LightData::Geometric(rays)))
             .unwrap();
         let mut em = EnergyMeter::default();
         em.set_isometry(Isometry::identity());
-        let i_e = scenery.add_node(em).unwrap();
+        let i_e = scenery.add_node(&em).unwrap();
         scenery
             .connect_nodes(i_s, "out1", i_e, "in1", Length::zero())
             .unwrap();
diff --git a/opossum/src/nodes/node_group/optic_graph.rs b/opossum/src/nodes/node_group/optic_graph.rs
index 332974d4f7eff895c6f4e4dcfa0e1b52d4104b44..6fe528ffe6db6c70705dea288b04643bb3aa1a98 100644
--- a/opossum/src/nodes/node_group/optic_graph.rs
+++ b/opossum/src/nodes/node_group/optic_graph.rs
@@ -62,11 +62,14 @@ impl OpticGraph {
                 "cannot add nodes if group is set as inverted".into(),
             ));
         }
+        let uuid = *node.node_attr().uuid();
+
         let idx = self.g.add_node(OpticRef::new(
             Rc::new(RefCell::new(node)),
-            None,
+            Some(uuid),
             self.global_confg.clone(),
         ));
+
         Ok(idx)
     }
     /// Connect two optical nodes within this [`OpticGraph`].
@@ -348,7 +351,9 @@ impl OpticGraph {
             .edges_directed(target_node, petgraph::Direction::Incoming)
             .any(|e| e.weight().target_port() == target_port)
     }
-    fn node_by_uuid(&self, uuid: Uuid) -> Option<OpticRef> {
+    /// Returns Some(`OpticRef`) if the provided uuid is connected with a node in the graph. None, otherwise.
+    #[must_use]
+    pub fn node_by_uuid(&self, uuid: Uuid) -> Option<OpticRef> {
         self.g
             .node_weights()
             .find(|node| node.uuid() == uuid)
@@ -368,6 +373,21 @@ impl OpticGraph {
             .ok_or_else(|| OpossumError::OpticScenery("node index does not exist".into()))?;
         Ok(node.clone())
     }
+
+    /// Return a mutable reference to the optical node specified by its node index.
+    ///
+    /// This function is mainly useful for setting up a reference node.
+    ///
+    /// # Errors
+    ///
+    /// This function will return [`OpossumError::OpticScenery`] if the node does not exist.
+    pub fn node_by_idx_mut(&mut self, node: NodeIndex) -> OpmResult<&mut OpticRef> {
+        let node = self
+            .g
+            .node_weight_mut(node)
+            .ok_or_else(|| OpossumError::OpticScenery("node index does not exist".into()))?;
+        Ok(node)
+    }
     fn node_idx_by_uuid(&self, uuid: Uuid) -> Option<NodeIndex> {
         self.g
             .node_indices()
diff --git a/opossum/src/nodes/ray_propagation_visualizer.rs b/opossum/src/nodes/ray_propagation_visualizer.rs
index 41f9173de15d0e3c48124e56476ae1187d4e5a5f..cef8299d54bce72726b2e3ce1b9b7f7a9f781d3a 100644
--- a/opossum/src/nodes/ray_propagation_visualizer.rs
+++ b/opossum/src/nodes/ray_propagation_visualizer.rs
@@ -13,7 +13,7 @@ use super::node_attr::NodeAttr;
 use crate::{
     analyzers::{
         energy::AnalysisEnergy, ghostfocus::AnalysisGhostFocus, raytrace::AnalysisRayTrace,
-        Analyzable, GhostFocusConfig, RayTraceConfig,
+        Analyzable, AnalyzerType, GhostFocusConfig, RayTraceConfig,
     },
     dottable::Dottable,
     error::{OpmResult, OpossumError},
@@ -168,7 +168,10 @@ impl AnalysisGhostFocus for RayPropagationVisualizer {
             return Ok(out_light_rays);
         };
         let mut rays = bouncing_rays.clone();
-        self.pass_through_inert_surface(&mut rays)?;
+        self.pass_through_inert_surface(
+            &mut rays,
+            &AnalyzerType::GhostFocus(GhostFocusConfig::default()),
+        )?;
 
         let mut out_light_rays = LightRays::default();
         out_light_rays.insert(out_port.to_string(), rays.clone());
diff --git a/opossum/src/nodes/reference.rs b/opossum/src/nodes/reference.rs
index 350a5cb489b0c24a987f46b4566ed7eb07390731..5a7b8613139420d0e4f62cadefe4542fd7ca1a30 100644
--- a/opossum/src/nodes/reference.rs
+++ b/opossum/src/nodes/reference.rs
@@ -21,7 +21,7 @@ use crate::{
 
 use super::node_attr::NodeAttr;
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 /// A virtual component referring to another existing component.
 ///
 /// This node type is necessary in order to model resonators (loops) or double-pass systems.
@@ -199,7 +199,7 @@ mod test {
     #[test]
     fn from_node() {
         let mut scenery = NodeGroup::default();
-        let idx = scenery.add_node(Dummy::default()).unwrap();
+        let idx = scenery.add_node(&Dummy::default()).unwrap();
         let node_ref = scenery.node(idx).unwrap();
         let node = NodeReference::from_node(&node_ref);
         assert!(node.reference.is_some());
@@ -207,7 +207,7 @@ mod test {
     #[test]
     fn from_node_name() {
         let mut scenery = NodeGroup::default();
-        let idx = scenery.add_node(Dummy::default()).unwrap();
+        let idx = scenery.add_node(&Dummy::default()).unwrap();
         let node_ref = scenery.node(idx).unwrap();
         let node_name = format!("ref ({})", node_ref.optical_ref.borrow().name());
         let node = NodeReference::from_node(&node_ref);
@@ -217,7 +217,7 @@ mod test {
     #[test]
     fn assign_reference() {
         let mut scenery = NodeGroup::default();
-        let idx = scenery.add_node(Dummy::default()).unwrap();
+        let idx = scenery.add_node(&Dummy::default()).unwrap();
         let node_ref = scenery.node(idx).unwrap();
         let mut node = NodeReference::default();
         assert!(node.reference.is_none());
@@ -237,7 +237,7 @@ mod test {
     #[test]
     fn ports_non_empty() {
         let mut scenery = NodeGroup::default();
-        let idx = scenery.add_node(Dummy::default()).unwrap();
+        let idx = scenery.add_node(&Dummy::default()).unwrap();
         let node = NodeReference::from_node(&scenery.node(idx).unwrap());
         assert_eq!(node.ports().names(&PortType::Input), vec!["front"]);
         assert_eq!(node.ports().names(&PortType::Output), vec!["rear"]);
@@ -245,7 +245,7 @@ mod test {
     #[test]
     fn ports_inverted() {
         let mut scenery = NodeGroup::default();
-        let idx = scenery.add_node(Dummy::default()).unwrap();
+        let idx = scenery.add_node(&Dummy::default()).unwrap();
         let mut node = NodeReference::from_node(&scenery.node(idx).unwrap());
         node.set_inverted(true.into()).unwrap();
         assert_eq!(node.ports().names(&PortType::Input), vec!["rear"]);
@@ -254,7 +254,7 @@ mod test {
     #[test]
     fn analyze_empty() {
         let mut scenery = NodeGroup::default();
-        let idx = scenery.add_node(Dummy::default()).unwrap();
+        let idx = scenery.add_node(&Dummy::default()).unwrap();
         let mut node = NodeReference::from_node(&scenery.node(idx).unwrap());
         let output = AnalysisEnergy::analyze(&mut node, LightResult::default()).unwrap();
         assert!(output.is_empty());
@@ -268,7 +268,7 @@ mod test {
     #[test]
     fn analyze() {
         let mut scenery = NodeGroup::default();
-        let idx = scenery.add_node(Dummy::default()).unwrap();
+        let idx = scenery.add_node(&Dummy::default()).unwrap();
         let mut node = NodeReference::from_node(&scenery.node(idx).unwrap());
 
         let mut input = LightResult::default();
@@ -287,7 +287,7 @@ mod test {
     #[test]
     fn analyze_inverse() {
         let mut scenery = NodeGroup::default();
-        let idx = scenery.add_node(Dummy::default()).unwrap();
+        let idx = scenery.add_node(&Dummy::default()).unwrap();
         let mut node = NodeReference::from_node(&scenery.node(idx).unwrap());
         node.set_inverted(true).unwrap();
         let mut input = LightResult::default();
@@ -307,7 +307,7 @@ mod test {
     #[test]
     fn analyze_non_invertible_ref() {
         let mut scenery = NodeGroup::default();
-        let idx = scenery.add_node(Source::default()).unwrap();
+        let idx = scenery.add_node(&Source::default()).unwrap();
         let mut node = NodeReference::from_node(&scenery.node(idx).unwrap());
         node.set_inverted(true).unwrap();
         let mut input = LightResult::default();
diff --git a/opossum/src/nodes/source.rs b/opossum/src/nodes/source.rs
index 4a0ca6feb5aaa865ad0017bc4eb44677b966909c..af85f8da9c0cece41cb8892595121fc294f6d701 100644
--- a/opossum/src/nodes/source.rs
+++ b/opossum/src/nodes/source.rs
@@ -303,6 +303,7 @@ impl AnalysisGhostFocus for Source {
             self.surface.set_isometry(&iso);
             for r in &mut rays {
                 r.refract_on_surface(&mut self.surface, None)?;
+                self.surface.evaluate_fluence_of_ray_bundle(r)?;
             }
         } else {
             return Err(OpossumError::Analysis(
diff --git a/opossum/src/nodes/spectrometer.rs b/opossum/src/nodes/spectrometer.rs
index fee7f36ddab2ba3e287f36256fbb361291aaf7fb..91c8608b0f6e3ca24c219bb7f191df7018201b9a 100644
--- a/opossum/src/nodes/spectrometer.rs
+++ b/opossum/src/nodes/spectrometer.rs
@@ -214,7 +214,6 @@ impl OpticNode for Spectrometer {
         self.surface.reset_hit_map();
     }
 }
-
 impl Surface for Spectrometer {
     fn get_surface_mut(&mut self, _surf_name: &str) -> &mut OpticalSurface {
         todo!()
diff --git a/opossum/src/nodes/spot_diagram.rs b/opossum/src/nodes/spot_diagram.rs
index 437473fa96515df49c3babbe376f6c3777307899..398cfaece36b55b4fc39d01a87cadc862f2d0bf6 100644
--- a/opossum/src/nodes/spot_diagram.rs
+++ b/opossum/src/nodes/spot_diagram.rs
@@ -12,7 +12,7 @@ use super::node_attr::NodeAttr;
 use crate::{
     analyzers::{
         energy::AnalysisEnergy, ghostfocus::AnalysisGhostFocus, raytrace::AnalysisRayTrace,
-        Analyzable, GhostFocusConfig, RayTraceConfig,
+        Analyzable, AnalyzerType, GhostFocusConfig, RayTraceConfig,
     },
     dottable::Dottable,
     error::{OpmResult, OpossumError},
@@ -214,7 +214,10 @@ impl AnalysisGhostFocus for SpotDiagram {
             return Ok(out_light_rays);
         };
         let mut rays = bouncing_rays.clone();
-        self.pass_through_inert_surface(&mut rays)?;
+        self.pass_through_inert_surface(
+            &mut rays,
+            &AnalyzerType::GhostFocus(GhostFocusConfig::default()),
+        )?;
 
         let mut out_light_rays = LightRays::default();
         out_light_rays.insert(out_port.to_string(), rays);
diff --git a/opossum/src/nodes/thin_mirror.rs b/opossum/src/nodes/thin_mirror.rs
index 0e8170238c281f7563fdc146d887341ae446151f..f9f42b650ad5b03a461bb56d65bc47d5b8f39583 100644
--- a/opossum/src/nodes/thin_mirror.rs
+++ b/opossum/src/nodes/thin_mirror.rs
@@ -20,7 +20,7 @@ use crate::{
 use num::Zero;
 use uom::si::f64::Length;
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 /// An infinitely thin mirror with a spherical (or flat) surface.
 ///
 ///
diff --git a/opossum/src/nodes/wedge/mod.rs b/opossum/src/nodes/wedge/mod.rs
index 1855e86ea2f9f1c89359688e314d1492661e8068..ac949d8cec60bbecf37051a4d6bea71910cd764a 100644
--- a/opossum/src/nodes/wedge/mod.rs
+++ b/opossum/src/nodes/wedge/mod.rs
@@ -23,7 +23,7 @@ mod analysis_energy;
 mod analysis_ghostfocus;
 mod analysis_raytrace;
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 /// An optical element with two flat surfaces, a given thickness and a  given wedge angle (= wedged window).
 ///
 ///
diff --git a/opossum/src/optic_node.rs b/opossum/src/optic_node.rs
index 5b5c2558f583ea73244110ca908a3c9c3b7064ef..225492e4605a065de0fbec5de377d3996b7ddbc9 100644
--- a/opossum/src/optic_node.rs
+++ b/opossum/src/optic_node.rs
@@ -228,6 +228,8 @@ pub trait OpticNode: Dottable {
         }
         node_attr_mut.update_properties(node_attributes.properties().clone());
         node_attr_mut.set_ports(node_attributes.ports().clone());
+
+        node_attr_mut.set_uuid(node_attributes.uuid());
     }
     /// Get the node type of this [`OpticNode`]
     fn node_type(&self) -> String {
diff --git a/opossum/src/properties/mod.rs b/opossum/src/properties/mod.rs
index 3b42b5014bf83c78a1052d6a80ffa18da140de7a..df11f239f363aaca99c86baba611251f30d8b8e3 100644
--- a/opossum/src/properties/mod.rs
+++ b/opossum/src/properties/mod.rs
@@ -53,6 +53,11 @@ impl Properties {
         self.props.insert(name.into(), new_property);
         Ok(())
     }
+    /// Returns the number of properties that have been set
+    #[must_use]
+    pub fn nr_of_props(&self) -> usize {
+        self.props.len()
+    }
     /// Set the value of the property with the given name.
     ///
     /// # Errors
diff --git a/opossum/src/properties/proptype.rs b/opossum/src/properties/proptype.rs
index 0bb7d3d36ed159eb9b8ef281938187942bde9c37..d8e7efd66dc563b0d32e1d9700d85542b49ed2d2 100644
--- a/opossum/src/properties/proptype.rs
+++ b/opossum/src/properties/proptype.rs
@@ -144,6 +144,7 @@ impl Proptype {
             Self::WaveFrontData(_) | Self::FluenceDetector(_) => {
                 tt.render("image", &format!("data/{id}_{property_name}.png"))
             }
+
             Self::NodeReport(report) => {
                 let html_node_report = HtmlNodeReport {
                     node_name: report.name().into(),
@@ -231,8 +232,21 @@ impl From<GhostFocusHistory> for Proptype {
         Self::GhostFocusHistory(value)
     }
 }
+#[must_use]
+pub fn count_str(i: usize) -> String {
+    if i == 1 {
+        "1st".to_owned()
+    } else if i == 2 {
+        "2nd".to_owned()
+    } else if i == 3 {
+        "3rd".to_owned()
+    } else {
+        format!("{i}th")
+    }
+}
 
-fn format_value_with_prefix(value: f64) -> String {
+#[must_use]
+pub fn format_value_with_prefix(value: f64) -> String {
     if value.is_nan() {
         return String::from("     nan ");
     }
diff --git a/opossum/src/ray.rs b/opossum/src/ray.rs
index 38cf782542c8931431853ea9f5286b360736a94c..5166cd8f5854eb093300b862ce86dda660f8152f 100644
--- a/opossum/src/ray.rs
+++ b/opossum/src/ray.rs
@@ -10,6 +10,7 @@ use uom::si::{
     f64::{Energy, Length},
     length::{meter, millimeter, nanometer},
 };
+use uuid::Uuid;
 
 use crate::{
     error::{OpmResult, OpossumError},
@@ -120,6 +121,12 @@ impl Ray {
     ) -> OpmResult<Self> {
         Self::new(position, Vector3::z(), wave_length, energy)
     }
+
+    ///Returns the number of positions in the position history, ergo the length of the history vector
+    #[must_use]
+    pub fn ray_history_len(&self) -> usize {
+        self.pos_hist.len()
+    }
     /// Create a ray with a position at the global coordinate origin pointing along the positive z-axis.
     ///
     /// # Errors
@@ -189,6 +196,48 @@ impl Ray {
         positions[(nr_of_pos, 2)] = self.pos.z;
         positions
     }
+
+    /// Returns the position history of this [`Ray`] starting from and ending at a specific index.
+    ///
+    /// This function returns a matrix with all positions (end of propagation and intersection points) of a ray path.
+    /// **Note**: This function adds to current ray position to the list.
+    #[must_use]
+    pub fn position_history_from_to(
+        &self,
+        start_idx: usize,
+        end_idx: usize,
+    ) -> Option<MatrixXx3<Length>> {
+        if start_idx >= self.pos_hist.len() {
+            return None;
+        }
+
+        let end_idx = if end_idx > self.pos_hist.len() {
+            self.pos_hist.len()
+        } else {
+            end_idx
+        };
+
+        let nr_of_pos = end_idx - start_idx;
+        let mut positions = MatrixXx3::<Length>::zeros(nr_of_pos);
+
+        for (idx, hist_idx) in (start_idx..end_idx - 1).enumerate() {
+            positions[(idx, 0)] = self.pos_hist[hist_idx].x;
+            positions[(idx, 1)] = self.pos_hist[hist_idx].y;
+            positions[(idx, 2)] = self.pos_hist[hist_idx].z;
+        }
+
+        if end_idx == self.pos_hist.len() {
+            positions[(nr_of_pos - 1, 0)] = self.pos.x;
+            positions[(nr_of_pos - 1, 1)] = self.pos.y;
+            positions[(nr_of_pos - 1, 2)] = self.pos.z;
+        } else {
+            positions[(nr_of_pos - 1, 0)] = self.pos_hist[end_idx].x;
+            positions[(nr_of_pos - 1, 1)] = self.pos_hist[end_idx].y;
+            positions[(nr_of_pos - 1, 2)] = self.pos_hist[end_idx].z;
+        }
+        Some(positions)
+    }
+
     /// Returns the path length of this [`Ray`].
     ///
     /// Return the geometric path length of the ray.
@@ -404,6 +453,7 @@ impl Ray {
         &mut self,
         os: &mut OpticalSurface,
         n2: Option<f64>,
+        ray_bundle_uuid: &Uuid,
     ) -> OpmResult<Option<Self>> {
         let n2 = n2.unwrap_or_else(|| self.refractive_index());
         if n2 < 1.0 || !n2.is_finite() {
@@ -443,14 +493,17 @@ impl Ray {
                 self.e = input_energy * (1. - reflectivity);
                 let mut reflected_ray = self.clone();
                 reflected_ray.pos_hist.clear();
-                reflected_ray.add_to_pos_hist(reflected_ray.pos);
                 reflected_ray.dir = reflected_dir;
                 reflected_ray.e = input_energy * reflectivity;
                 reflected_ray.number_of_bounces += 1;
                 self.refractive_index = n2;
                 self.number_of_refractions += 1;
                 // save on hit map of surface
-                os.add_to_hit_map((intersection_point, input_energy), self.number_of_bounces);
+                os.add_to_hit_map(
+                    (intersection_point, input_energy),
+                    self.number_of_bounces,
+                    ray_bundle_uuid,
+                );
 
                 Ok(Some(reflected_ray))
             } else {
@@ -988,10 +1041,19 @@ mod test {
         .unwrap();
         let mut s = OpticalSurface::new(Box::new(Plane::new(&isometry)));
         s.set_coating(CoatingType::ConstantR { reflectivity });
-        assert!(ray.refract_on_surface(&mut s, Some(0.9)).is_err());
-        assert!(ray.refract_on_surface(&mut s, Some(f64::NAN)).is_err());
-        assert!(ray.refract_on_surface(&mut s, Some(f64::INFINITY)).is_err());
-        let reflected_ray = ray.refract_on_surface(&mut s, Some(1.5)).unwrap().unwrap();
+        assert!(ray
+            .refract_on_surface(&mut s, Some(0.9), &Uuid::new_v4())
+            .is_err());
+        assert!(ray
+            .refract_on_surface(&mut s, Some(f64::NAN), &Uuid::new_v4())
+            .is_err());
+        assert!(ray
+            .refract_on_surface(&mut s, Some(f64::INFINITY), &Uuid::new_v4())
+            .is_err());
+        let reflected_ray = ray
+            .refract_on_surface(&mut s, Some(1.5), &Uuid::new_v4())
+            .unwrap()
+            .unwrap();
 
         // refracted ray
         assert_eq!(ray.pos, millimeter!(0., 0., 10.));
@@ -1007,7 +1069,7 @@ mod test {
         assert_eq!(reflected_ray.pos, millimeter!(0., 0., 10.));
         assert_eq!(reflected_ray.refractive_index, 1.0);
         assert_eq!(reflected_ray.dir, -1.0 * Vector3::z());
-        assert_eq!(reflected_ray.pos_hist, vec![millimeter!(0., 0., 10.)]);
+        assert_eq!(reflected_ray.pos_hist, vec![]);
         assert_eq!(reflected_ray.path_length(), plane_z_pos);
         assert_eq!(reflected_ray.number_of_bounces(), 1);
         assert_eq!(reflected_ray.number_of_refractions(), 0);
@@ -1015,7 +1077,8 @@ mod test {
 
         let position = millimeter!(0., 1., 0.);
         let mut ray = Ray::new_collimated(position, wvl, e).unwrap();
-        ray.refract_on_surface(&mut s, Some(1.5)).unwrap();
+        ray.refract_on_surface(&mut s, Some(1.5), &Uuid::new_v4())
+            .unwrap();
         assert_eq!(ray.pos, millimeter!(0., 1., 10.));
         assert_eq!(ray.dir, Vector3::z());
         assert_eq!(ray.path_length, plane_z_pos);
@@ -1038,7 +1101,8 @@ mod test {
         )
         .unwrap();
         let mut s = OpticalSurface::new(Box::new(Plane::new(&isometry)));
-        ray.refract_on_surface(&mut s, None).unwrap();
+        ray.refract_on_surface(&mut s, None, &Uuid::new_v4())
+            .unwrap();
         assert_eq!(ray.pos, millimeter!(0., 10., 10.));
         assert_eq!(ray.dir[0], 0.0);
         assert_abs_diff_eq!(ray.dir[1], direction.normalize()[1]);
@@ -1061,7 +1125,8 @@ mod test {
         )
         .unwrap();
         let mut s = OpticalSurface::new(Box::new(Plane::new(&isometry)));
-        ray.refract_on_surface(&mut s, Some(1.5)).unwrap();
+        ray.refract_on_surface(&mut s, Some(1.5), &Uuid::new_v4())
+            .unwrap();
         assert_eq!(ray.pos, millimeter!(0., 0., 0.));
         assert_eq!(ray.dir, direction);
         assert_eq!(ray.refractive_index, 1.0);
@@ -1083,17 +1148,25 @@ mod test {
         )
         .unwrap();
         let mut s = OpticalSurface::new(Box::new(Plane::new(&isometry)));
-        assert!(ray.refract_on_surface(&mut s, Some(0.9)).is_err());
-        assert!(ray.refract_on_surface(&mut s, Some(f64::NAN)).is_err());
-        assert!(ray.refract_on_surface(&mut s, Some(f64::INFINITY)).is_err());
-        ray.refract_on_surface(&mut s, Some(1.0)).unwrap();
+        assert!(ray
+            .refract_on_surface(&mut s, Some(0.9), &Uuid::new_v4())
+            .is_err());
+        assert!(ray
+            .refract_on_surface(&mut s, Some(f64::NAN), &Uuid::new_v4())
+            .is_err());
+        assert!(ray
+            .refract_on_surface(&mut s, Some(f64::INFINITY), &Uuid::new_v4())
+            .is_err());
+        ray.refract_on_surface(&mut s, Some(1.0), &Uuid::new_v4())
+            .unwrap();
         assert_eq!(ray.pos, millimeter!(0., 10., 10.));
         assert_eq!(ray.dir[0], 0.0);
         assert_abs_diff_eq!(ray.dir[1], direction.normalize()[1]);
         assert_abs_diff_eq!(ray.dir[2], direction.normalize()[2]);
         assert_abs_diff_eq!(ray.path_length.value, 2.0_f64.sqrt() * plane_z_pos.value);
         let mut ray = Ray::new(position, direction, wvl, e).unwrap();
-        ray.refract_on_surface(&mut s, Some(1.5)).unwrap();
+        ray.refract_on_surface(&mut s, Some(1.5), &Uuid::new_v4())
+            .unwrap();
         assert_eq!(ray.number_of_bounces(), 0);
         assert_eq!(ray.number_of_refractions(), 1);
         assert_eq!(ray.pos, millimeter!(0., 10., 10.));
@@ -1102,7 +1175,8 @@ mod test {
         assert_abs_diff_eq!(ray.dir[2], 0.8819171036881969);
         let direction = vector![1.0, 0.0, 1.0];
         let mut ray = Ray::new(position, direction, wvl, e).unwrap();
-        ray.refract_on_surface(&mut s, Some(1.5)).unwrap();
+        ray.refract_on_surface(&mut s, Some(1.5), &Uuid::new_v4())
+            .unwrap();
         assert_eq!(ray.pos, millimeter!(10., 0., 10.));
         assert_eq!(ray.dir[0], 0.4714045207910317);
         assert_abs_diff_eq!(ray.dir[1], 0.0);
@@ -1122,7 +1196,9 @@ mod test {
         )
         .unwrap();
         let mut s = OpticalSurface::new(Box::new(Plane::new(&isometry)));
-        let reflected = ray.refract_on_surface(&mut s, Some(1.0)).unwrap();
+        let reflected = ray
+            .refract_on_surface(&mut s, Some(1.0), &Uuid::new_v4())
+            .unwrap();
         assert!(reflected.is_none());
         assert_eq!(ray.pos, millimeter!(0., 20., 10.));
         let test_reflect = vector![0.0, 2.0, -1.0].normalize();
diff --git a/opossum/src/rays.rs b/opossum/src/rays.rs
index 7b76e74abb1a81c5e3420f374a59d01ba1fc3c00..a32e998cb18cbc41a98010b5c773bf4734e7dd1f 100644
--- a/opossum/src/rays.rs
+++ b/opossum/src/rays.rs
@@ -50,11 +50,32 @@ use uom::{
         length::{micrometer, millimeter, nanometer},
     },
 };
+use uuid::Uuid;
 /// Struct containing all relevant information of a ray bundle
-#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq)]
+#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
 pub struct Rays {
     /// vector containing the individual rays
     rays: Vec<Ray>,
+    /// origin node of this ray bundle
+    node_origin: Option<Uuid>,
+    /// id of this ray bundle
+    uuid: Uuid,
+    /// parent id of this ray bundle
+    parent_id: Option<Uuid>,
+    ///the index of the position history of the parent ray bundle at which this ray bundle was generated
+    parent_pos_split_idx: usize,
+}
+
+impl Default for Rays {
+    fn default() -> Self {
+        Self {
+            rays: Vec::default(),
+            node_origin: Option::default(),
+            uuid: Uuid::new_v4(),
+            parent_id: Option::default(),
+            parent_pos_split_idx: usize::default(),
+        }
+    }
 }
 impl Rays {
     /// Generate a set of collimated rays (collinear with optical axis) with uniform energy distribution.
@@ -84,7 +105,75 @@ impl Rays {
             let ray = Ray::new_collimated(point, wave_length, energy_per_ray)?;
             rays.push(ray);
         }
-        Ok(Self { rays })
+        Ok(Self {
+            rays,
+            node_origin: None,
+            uuid: Uuid::new_v4(),
+            parent_id: None,
+            parent_pos_split_idx: 0,
+        })
+    }
+    ///Returns the uuid of this ray bundle
+    #[must_use]
+    pub const fn uuid(&self) -> &Uuid {
+        &self.uuid
+    }
+    ///get the bounce level of this ray bundle
+    #[must_use]
+    pub fn bounce_lvl(&self) -> usize {
+        if self.rays.is_empty() {
+            0
+        } else {
+            let valid_rays = self.rays.iter().filter(|r| r.valid()).collect_vec();
+            if valid_rays.is_empty() {
+                0
+            } else {
+                valid_rays[0].number_of_bounces()
+            }
+        }
+    }
+    ///returns the length of the position history
+    #[must_use]
+    pub fn ray_history_len(&self) -> usize {
+        if self.rays.is_empty() {
+            0
+        } else {
+            let valid_rays = self.rays.iter().filter(|r| r.valid()).collect_vec();
+            if valid_rays.is_empty() {
+                0
+            } else {
+                valid_rays[0].ray_history_len()
+            }
+        }
+    }
+    ///Returns the uuid of node at which this ray bundle originated
+    #[must_use]
+    pub const fn node_origin(&self) -> &Option<Uuid> {
+        &self.node_origin
+    }
+    ///Returns the uuid of tha parent ray bundle of this ray bundle
+    #[must_use]
+    pub const fn parent_id(&self) -> &Option<Uuid> {
+        &self.parent_id
+    }
+    ///Returns the index of the position history of its parent ray bundle
+    #[must_use]
+    pub const fn parent_pos_split_idx(&self) -> &usize {
+        &self.parent_pos_split_idx
+    }
+    /// Sets the parent uuid of this ray bundle
+    pub fn set_parent_uuid(&mut self, parent_uuid: Uuid) {
+        self.parent_id = Some(parent_uuid);
+    }
+
+    /// Sets the node origin uuid of this ray bundle
+    pub fn set_node_origin_uuid(&mut self, node_uuid: Uuid) {
+        self.node_origin = Some(node_uuid);
+    }
+
+    /// Sets the parent node split index node origin uuid of this ray bundle
+    pub fn set_parent_node_split_idx(&mut self, split_idx: usize) {
+        self.parent_pos_split_idx = split_idx;
     }
 
     /// Generate a set of collimated rays (collinear with optical axis) with specified energy, spectral and position distribution.
@@ -124,7 +213,13 @@ impl Rays {
                 rays.push(ray);
             }
         }
-        Ok(Self { rays })
+        Ok(Self {
+            rays,
+            node_origin: None,
+            uuid: Uuid::new_v4(),
+            parent_id: None,
+            parent_pos_split_idx: 0,
+        })
     }
 
     /// Generate a set of collimated rays (collinear with optical axis) with specified energy distribution and position distribution.
@@ -162,7 +257,13 @@ impl Rays {
                 rays.push(ray);
             }
         }
-        Ok(Self { rays })
+        Ok(Self {
+            rays,
+            node_origin: None,
+            uuid: Uuid::new_v4(),
+            parent_id: None,
+            parent_pos_split_idx: 0,
+        })
     }
     /// Generate a ray cone (= point source)
     ///
@@ -206,7 +307,13 @@ impl Rays {
             let ray = Ray::new(position, direction, wave_length, energy_per_ray)?;
             rays.push(ray);
         }
-        Ok(Self { rays })
+        Ok(Self {
+            rays,
+            node_origin: None,
+            uuid: Uuid::new_v4(),
+            parent_id: None,
+            parent_pos_split_idx: 0,
+        })
     }
     /// Returns the total energy of this [`Rays`].
     ///
@@ -668,7 +775,7 @@ impl Rays {
     /// This function refracts all `valid` [`Ray`]s on a given surface.
     ///
     /// The refractive index of the surface is given by the `refractive_index` parameter. If this parameter is
-    /// set to `None`, the refractive index of the incoming individual beam is used. This way it is possibe to model
+    /// set to `None`, the refractive index of the incoming individual beam is used. This way it is possible to model
     /// a "passive" surface, which does not change the direction of the [`Ray`].
     ///
     /// # Warnings
@@ -695,7 +802,7 @@ impl Rays {
                 } else {
                     None
                 };
-                if let Some(reflected) = ray.refract_on_surface(surface, n2)? {
+                if let Some(reflected) = ray.refract_on_surface(surface, n2, &self.uuid)? {
                     reflected_rays.add_ray(reflected);
                 } else {
                     rays_missed = true;
@@ -710,6 +817,8 @@ impl Rays {
             warn!("ray bundle contains no valid rays - not propagating");
         }
         //surface.set_backwards_rays_cache(reflected_rays.clone());
+        reflected_rays.set_parent_uuid(self.uuid);
+        reflected_rays.set_parent_node_split_idx(self.ray_history_len());
         Ok(reflected_rays)
     }
     /// Diffract a bundle of [`Rays`] on a periodic surface, e.g., a grating
@@ -754,6 +863,7 @@ impl Rays {
         if !valid_rays_found {
             warn!("ray bundle contains no valid rays - not propagating");
         }
+        reflected_rays.set_parent_uuid(self.uuid);
         Ok(reflected_rays)
     }
     /// Filter a ray bundle by a given filter.
@@ -1114,7 +1224,13 @@ impl Display for Rays {
 
 impl From<Vec<Ray>> for Rays {
     fn from(value: Vec<Ray>) -> Self {
-        Self { rays: value }
+        Self {
+            rays: value,
+            node_origin: None,
+            uuid: Uuid::new_v4(),
+            parent_id: None,
+            parent_pos_split_idx: 0,
+        }
     }
 }
 
diff --git a/opossum/src/surface/hit_map.rs b/opossum/src/surface/hit_map.rs
index da2c1d0c5c2e2a343d4d18c5c0c9f9a4991690ac..e3baf612db4eefcd0e839d808704293d0884549f 100644
--- a/opossum/src/surface/hit_map.rs
+++ b/opossum/src/surface/hit_map.rs
@@ -1,56 +1,195 @@
 //! Data structure for storing intersection points (and energies) of [`Rays`](crate::rays::Rays) hitting an
 //! [`OpticalSurface`](crate::surface::OpticalSurface).
+use std::collections::HashMap;
+
+use log::warn;
 use nalgebra::{DVector, MatrixXx2, Point2, Point3};
 use plotters::style::RGBAColor;
 use serde::{Deserialize, Serialize};
-use uom::si::f64::{Energy, Length};
+use uom::si::{
+    energy::joule,
+    f64::{Energy, Length},
+    length::centimeter,
+    radiant_exposure::joule_per_square_centimeter,
+};
+use uuid::Uuid;
 
 use crate::{
-    error::OpmResult,
+    error::{OpmResult, OpossumError},
+    nodes::fluence_detector::Fluence,
     plottable::{AxLims, PlotArgs, PlotData, PlotParameters, PlotSeries, PlotType, Plottable},
     properties::Proptype,
-    utils::unit_format::{
-        get_exponent_for_base_unit_in_e3_steps, get_prefix_for_base_unit,
-        get_unit_value_as_length_with_format_by_exponent,
+    utils::{
+        griddata::{calc_closed_poly_area, create_voronoi_cells, VoronoiedData},
+        unit_format::{
+            get_exponent_for_base_unit_in_e3_steps, get_prefix_for_base_unit,
+            get_unit_value_as_length_with_format_by_exponent,
+        },
     },
+    J_per_cm2,
 };
 
+#[derive(Default, Debug, Clone, Serialize, Deserialize)]
+///Storage struct for `RaysHitMap` on a surface from a single bounce
+pub struct BouncedHitMap {
+    hit_map: HashMap<Uuid, RaysHitMap>,
+}
+
+impl BouncedHitMap {
+    /// Add intersection point (with energy) to this [`BouncedHitMap`].
+    pub fn add_to_hitmap(&mut self, hit_point: (Point3<Length>, Energy), uuid: &Uuid) {
+        if let Some(rays_hit_map) = self.hit_map.get_mut(uuid) {
+            rays_hit_map.add_to_hitmap(hit_point);
+        } else {
+            self.hit_map.insert(*uuid, RaysHitMap::new(vec![hit_point]));
+        }
+    }
+
+    /// creates a new [`BouncedHitMap`]
+    #[must_use]
+    pub const fn new(hit_points: HashMap<Uuid, RaysHitMap>) -> Self {
+        Self {
+            hit_map: hit_points,
+        }
+    }
+    ///returns a reference to a [`RaysHitMap`] in this [`BouncedHitMap`]
+    #[must_use]
+    pub fn get_rays_hit_map(&self, uuid: &Uuid) -> Option<&RaysHitMap> {
+        self.hit_map.get(uuid)
+    }
+}
+
+#[derive(Default, Debug, Clone, Serialize, Deserialize)]
+///Storage struct for hitpoints on a surface from a single raybundle
+pub struct RaysHitMap {
+    hit_map: Vec<(Point3<Length>, Energy)>,
+}
+
+impl RaysHitMap {
+    /// Add intersection point (with energy) to this [`HitMap`].
+    pub fn add_to_hitmap(&mut self, hit_point: (Point3<Length>, Energy)) {
+        self.hit_map.push(hit_point);
+    }
+
+    /// creates a new [`RaysHitMap`]
+    #[must_use]
+    pub fn new(hit_points: Vec<(Point3<Length>, Energy)>) -> Self {
+        Self {
+            hit_map: hit_points,
+        }
+    }
+
+    /// Calculates the fluence of a ray bundle that is stored in this hitmap
+    /// # Attributes
+    /// - `max_fluence`: the maximum allowed fluence on this surface
+    /// # Errors
+    /// This function errors if no reasonable axlimits  can be estimated due to only non-finite values in the positions
+    #[allow(clippy::type_complexity)]
+    pub fn calc_fluence(
+        &self,
+        max_fluence: Fluence,
+    ) -> OpmResult<Option<(VoronoiedData, AxLims, AxLims, Fluence, Fluence)>> {
+        let mut show_hitmap = false;
+        let max_fluence_jcm2 = max_fluence.get::<joule_per_square_centimeter>();
+        let mut pos_in_cm = MatrixXx2::<f64>::zeros(self.hit_map.len());
+        let mut energy = DVector::<f64>::zeros(self.hit_map.len());
+        let mut energy_in_ray_bundle = 0.;
+
+        for (row, p) in self.hit_map.iter().enumerate() {
+            pos_in_cm[(row, 0)] = p.0.x.get::<centimeter>();
+            pos_in_cm[(row, 1)] = p.0.y.get::<centimeter>();
+            energy[row] = p.1.get::<joule>();
+            energy_in_ray_bundle += energy[row];
+        }
+
+        let proj_ax1_lim = AxLims::finite_from_dvector(&pos_in_cm.column(0)).ok_or_else(|| {
+            OpossumError::Other(
+                "cannot construct voronoi cells with non-finite axes bounds!".into(),
+            )
+        })?;
+        let proj_ax2_lim = AxLims::finite_from_dvector(&pos_in_cm.column(1)).ok_or_else(|| {
+            OpossumError::Other(
+                "cannot construct voronoi cells with non-finite axes bounds!".into(),
+            )
+        })?;
+        let (voronoi, beam_area) = create_voronoi_cells(&pos_in_cm).map_err(|_| {
+            OpossumError::Other(
+                "Voronoi diagram for fluence estimation could not be created!".into(),
+            )
+        })?;
+
+        //get the voronoi cells
+        let v_cells = voronoi.cells();
+        let mut fluence_scatter = DVector::from_element(voronoi.sites.len(), f64::NAN);
+        let mut max_fluence_val = f64::NEG_INFINITY;
+        for (i, v_cell) in v_cells.iter().enumerate() {
+            let v_neighbours = v_cell
+                .points()
+                .iter()
+                .map(|p| Point2::new(p.x, p.y))
+                .collect::<Vec<Point2<f64>>>();
+            if v_neighbours.len() >= 3 {
+                let poly_area = calc_closed_poly_area(&v_neighbours)?;
+                fluence_scatter[i] = energy[i] / poly_area;
+                if fluence_scatter[i] > max_fluence_jcm2 {
+                    if max_fluence_val < fluence_scatter[i] {
+                        max_fluence_val = fluence_scatter[i];
+                    }
+                    show_hitmap = true;
+                }
+            } else {
+                warn!(
+                    "polygon could not be created. number of neighbors {}",
+                    v_neighbours.len()
+                );
+            }
+        }
+        if show_hitmap {
+            Ok(Some((
+                VoronoiedData::combine_data_with_voronoi_diagram(voronoi, fluence_scatter)?,
+                proj_ax1_lim,
+                proj_ax2_lim,
+                J_per_cm2!(energy_in_ray_bundle / beam_area),
+                J_per_cm2!(max_fluence_val),
+            )))
+        } else {
+            Ok(None)
+        }
+    }
+}
+
 /// Data structure for storing intersection points (and energies) of [`Rays`](crate::rays::Rays) hitting an
 /// [`OpticalSurface`](crate::surface::OpticalSurface).
 #[derive(Default, Debug, Clone, Serialize, Deserialize)]
 pub struct HitMap {
     /// Stores the hitpoints of the rays on this surface, separated by their bounce level and the individual ray bundle
-    /// First Vec stores a vector of hitmaps from different ray bundles, sorted by their bounce level
-    /// Second Vec stores the hitmaps of the individual ray bundle. No sorting.
-    /// Last vec stores the hitpoints of one ray bundle on this surface
-    #[allow(clippy::type_complexity)]
-    hit_map: Vec<Vec<Vec<(Point3<Length>, Energy)>>>,
+    hit_map: Vec<BouncedHitMap>,
+    /// Stores the fluence and position in the history of the ray bundles that create a critical fluence on this surface. key value is the uuid of the ray bundle
+    critical_fluence: HashMap<Uuid, (Fluence, usize)>,
 }
 impl HitMap {
     /// Returns a reference to the hit map of this [`HitMap`].
     ///
     /// This function returns a vector of intersection points (with energies) of [`Rays`](crate::rays::Rays) that hit the surface.
     #[must_use]
-    #[allow(clippy::type_complexity)]
-    pub fn hit_map(&self) -> &[Vec<Vec<(Point3<Length>, Energy)>>] {
+    pub fn hit_map(&self) -> &[BouncedHitMap] {
         &self.hit_map
     }
     /// Add intersection point (with energy) to this [`HitMap`].
     ///
-    pub fn add_to_hitmap(&mut self, hit_point: (Point3<Length>, Energy), bounce: usize) {
+    pub fn add_to_hitmap(
+        &mut self,
+        hit_point: (Point3<Length>, Energy),
+        bounce: usize,
+        uuid: &Uuid,
+    ) {
+        // make sure that vector is large enough to insert the data
         if self.hit_map.len() <= bounce {
-            for _i in 0..=bounce {
-                if self.hit_map.len() < bounce {
-                    self.hit_map.push(vec![vec![]]);
-                }
-            }
-            self.hit_map.push(vec![vec![hit_point]]);
-        } else {
-            let hm_len = self.hit_map[bounce].len();
-            if hm_len > 0 {
-                self.hit_map[bounce][hm_len - 1].push(hit_point);
+            for _i in 0..bounce + 1 - self.hit_map.len() {
+                self.hit_map.push(BouncedHitMap::default());
             }
         }
+        self.hit_map[bounce].add_to_hitmap(hit_point, uuid);
     }
     /// Reset this [`HitMap`].
     ///
@@ -63,6 +202,39 @@ impl HitMap {
     pub fn is_empty(&self) -> bool {
         self.hit_map.is_empty()
     }
+
+    /// returns a reference to the `critical_fluence` field of this [`HitMap`] which contains:
+    /// - the uuid of the ray bundle that causes this critical fluence on this surface as key
+    /// - a tuple containing the calculated peak fluence and the index of the position history which can be used to reconstruct the ray-propagation plot later on
+    #[must_use]
+    pub fn critical_fluences(&self) -> &HashMap<Uuid, (Fluence, usize)> {
+        &self.critical_fluence
+    }
+
+    // pub fn get_critical_fluences(&self) -> OpmResult<Vec<(Uuid, usize, Fluence, Fluence)>>{
+    //     let max_fluence = J_per_cm2!(0.02);
+    //     let mut critical_positions = Vec::<(Uuid, usize, Fluence, Fluence)>::new();
+    //     for (bounce, bounced_hit_map) in self.hit_map.iter().enumerate(){
+    //         for (uuid, rays_hit_map) in &bounced_hit_map.hit_map{
+    //             if let Some((_vdat, _x_lim, _y_lim, average_fluence, peak_fluence)) = rays_hit_map.calc_fluence(max_fluence)?{
+    //                 critical_positions.push((uuid.clone(), bounce, average_fluence, peak_fluence));
+    //             }
+    //         }
+    //     }
+    //     Ok(critical_positions)
+    // }
+
+    ///stores a critical fluence in a hitmap
+    pub fn add_critical_fluence(&mut self, uuid: &Uuid, rays_hist_pos: usize, fluence: Fluence) {
+        self.critical_fluence
+            .insert(*uuid, (fluence, rays_hist_pos));
+    }
+
+    ///returns a reference to a [`RaysHitMap`] in this [`HitMap`]
+    #[must_use]
+    pub fn get_rays_hit_map(&self, bounce: usize, uuid: &Uuid) -> Option<&RaysHitMap> {
+        self.hit_map[bounce].get_rays_hit_map(uuid)
+    }
 }
 impl From<HitMap> for Proptype {
     fn from(value: HitMap) -> Self {
@@ -86,18 +258,17 @@ impl Plottable for HitMap {
             let mut x_min = f64::INFINITY;
             let mut y_min = f64::INFINITY;
 
-            for bounced_ray_bundles in &self.hit_map {
-                for ray_bundle in bounced_ray_bundles {
-                    let mut xy_pos = Vec::<Point2<Length>>::with_capacity(ray_bundle.len());
-                    for p in ray_bundle {
-                        xy_pos.push(Point2::new(p.0.x, p.0.y));
+            for (i, bounced_ray_bundles) in self.hit_map.iter().enumerate() {
+                xy_positions.push(Vec::<Point2<Length>>::new());
+                for rays_hitmap in bounced_ray_bundles.hit_map.values() {
+                    for p in &rays_hitmap.hit_map {
+                        xy_positions[i].push(Point2::new(p.0.x, p.0.y));
 
                         x_max = x_max.max(p.0.x.value);
                         y_max = y_max.max(p.0.y.value);
                         x_min = x_min.min(p.0.x.value);
                         y_min = y_min.min(p.0.y.value);
                     }
-                    xy_positions.push(xy_pos);
                 }
             }
             let x_exponent = get_exponent_for_base_unit_in_e3_steps(x_max);
@@ -165,8 +336,6 @@ impl Plottable for HitMap {
     }
     fn add_plot_specific_params(&self, plt_params: &mut PlotParameters) -> OpmResult<()> {
         plt_params
-            // .set(&PlotArgs::XLabel("x position (b)".into()))?
-            // .set(&PlotArgs::YLabel("y position (b)".into()))?
             .set(&PlotArgs::AxisEqual(true))?
             .set(&PlotArgs::PlotAutoSize(true))?
             .set(&PlotArgs::PlotSize((800, 800)))?;
diff --git a/opossum/src/surface/optical_surface.rs b/opossum/src/surface/optical_surface.rs
index bb045f3adb0d015078e29efab2d019c237af5f61..55670cedd78771b14f2b376590ca1e1d8040423d 100644
--- a/opossum/src/surface/optical_surface.rs
+++ b/opossum/src/surface/optical_surface.rs
@@ -1,8 +1,15 @@
 use nalgebra::Point3;
 use uom::si::f64::{Energy, Length};
+use uuid::Uuid;
 
-use super::{hit_map::HitMap, GeoSurface, Plane};
-use crate::{coatings::CoatingType, rays::Rays, utils::geom_transformation::Isometry};
+use super::{
+    hit_map::{HitMap, RaysHitMap},
+    GeoSurface, Plane,
+};
+use crate::{
+    coatings::CoatingType, error::OpmResult, nodes::fluence_detector::Fluence, rays::Rays,
+    utils::geom_transformation::Isometry, J_per_cm2,
+};
 
 /// This struct represents an optical surface, which consists of the geometric surface shape ([`GeoSurface`]) and further
 /// properties such as the [`CoatingType`].
@@ -13,6 +20,7 @@ pub struct OpticalSurface {
     backward_rays_cache: Vec<Rays>,
     forward_rays_cache: Vec<Rays>,
     hit_map: HitMap,
+    lidt: Fluence,
 }
 impl Default for OpticalSurface {
     fn default() -> Self {
@@ -22,6 +30,7 @@ impl Default for OpticalSurface {
             backward_rays_cache: Vec::<Rays>::new(),
             forward_rays_cache: Vec::<Rays>::new(),
             hit_map: HitMap::default(),
+            lidt: J_per_cm2!(0.1),
         }
     }
 }
@@ -33,6 +42,7 @@ impl Clone for OpticalSurface {
             backward_rays_cache: self.backward_rays_cache.clone(),
             forward_rays_cache: self.forward_rays_cache.clone(),
             hit_map: self.hit_map.clone(),
+            lidt: self.lidt,
         }
     }
 }
@@ -46,6 +56,7 @@ impl OpticalSurface {
             backward_rays_cache: Vec::<Rays>::new(),
             forward_rays_cache: Vec::<Rays>::new(),
             hit_map: HitMap::default(),
+            lidt: J_per_cm2!(0.1),
         }
     }
     /// Returns a reference to the coating of this [`OpticalSurface`].
@@ -99,13 +110,41 @@ impl OpticalSurface {
     pub const fn hit_map(&self) -> &HitMap {
         &self.hit_map
     }
+    ///stores a critical fluence in a hitmap
+    pub fn add_critical_fluence(&mut self, uuid: &Uuid, rays_hist_pos: usize, fluence: Fluence) {
+        self.hit_map
+            .add_critical_fluence(uuid, rays_hist_pos, fluence);
+    }
+
+    ///returns a reference to a [`RaysHitMap`] in this [`OpticalSurface`]
+    #[must_use]
+    pub fn get_rays_hit_map(&self, bounce: usize, uuid: &Uuid) -> Option<&RaysHitMap> {
+        self.hit_map.get_rays_hit_map(bounce, uuid)
+    }
     /// Add intersection point (with energy) to hit map.
     ///
-    pub fn add_to_hit_map(&mut self, hit_point: (Point3<Length>, Energy), bounce: usize) {
-        self.hit_map.add_to_hitmap(hit_point, bounce);
+    pub fn add_to_hit_map(
+        &mut self,
+        hit_point: (Point3<Length>, Energy),
+        bounce: usize,
+        rays_uuid: &Uuid,
+    ) {
+        self.hit_map.add_to_hitmap(hit_point, bounce, rays_uuid);
     }
     /// Reset hit map of this [`OpticalSurface`].
     pub fn reset_hit_map(&mut self) {
         self.hit_map.reset();
     }
+
+    /// Evaluate the fluence of a given ray bundle on this surface. If the fluence surpasses its lidt, store the critical fluence parameters in the hitmap
+    /// # Errors
+    /// This function errors  on error propagation of `calc_fluence`
+    pub fn evaluate_fluence_of_ray_bundle(&mut self, rays: &Rays) -> OpmResult<()> {
+        if let Some(rays_hit_map) = self.get_rays_hit_map(rays.bounce_lvl(), rays.uuid()) {
+            if let Some((_, _, _, _, peak_fluence)) = rays_hit_map.calc_fluence(self.lidt)? {
+                self.add_critical_fluence(rays.uuid(), rays.ray_history_len(), peak_fluence);
+            }
+        }
+        Ok(())
+    }
 }