diff --git a/Cargo.lock b/Cargo.lock
index 24b9766308f3cc19e33902d9f2696404b511d5ca..f41dbbd4645c3cafb793546de6e8ad5b91b85ed4 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -730,7 +730,7 @@ dependencies = [
  "downcast-rs",
  "either",
  "petgraph 0.6.5",
- "ron",
+ "ron 0.8.1",
  "serde",
  "smallvec",
  "thread_local",
@@ -784,7 +784,7 @@ dependencies = [
  "futures-lite",
  "js-sys",
  "parking_lot",
- "ron",
+ "ron 0.8.1",
  "serde",
  "stackfuture",
  "uuid",
@@ -4945,10 +4945,10 @@ dependencies = [
  "rand 0.9.0",
  "rayon",
  "regex",
+ "ron 0.9.0",
  "roots",
  "rprompt",
  "serde",
- "serde_yaml",
  "sobol",
  "spade",
  "strum",
@@ -5645,6 +5645,19 @@ dependencies = [
  "serde_derive",
 ]
 
+[[package]]
+name = "ron"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "63f3aa105dea217ef30d89581b65a4d527a19afc95ef5750be3890e8d3c5b837"
+dependencies = [
+ "base64 0.22.1",
+ "bitflags 2.8.0",
+ "serde",
+ "serde_derive",
+ "unicode-ident",
+]
+
 [[package]]
 name = "roots"
 version = "0.0.8"
@@ -5878,19 +5891,6 @@ dependencies = [
  "serde",
 ]
 
-[[package]]
-name = "serde_yaml"
-version = "0.9.34+deprecated"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47"
-dependencies = [
- "indexmap 2.7.1",
- "itoa",
- "ryu",
- "serde",
- "unsafe-libyaml",
-]
-
 [[package]]
 name = "sha1"
 version = "0.10.6"
@@ -6655,12 +6655,6 @@ version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
 
-[[package]]
-name = "unsafe-libyaml"
-version = "0.2.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861"
-
 [[package]]
 name = "uom"
 version = "0.36.0"
diff --git a/opossum/Cargo.toml b/opossum/Cargo.toml
index b4b02976ba8e18143acb07d74e92ac3ab64ba9a1..6ec8db8338d8a83df8f771f031b69cb7868b18bc 100644
--- a/opossum/Cargo.toml
+++ b/opossum/Cargo.toml
@@ -30,7 +30,8 @@ opm_macros_lib = { path = "opm_macros_lib" }
 petgraph = { version = "0.7", features = ["serde-1"] } # the graph library
 uom = {version="0.36", features = ["serde"] }
 serde = { version = "1", features = ['rc'] }
-serde_yaml = "0.9"
+#serde_yaml = "0.9"
+ron="0.9"
 
 csv = "1"
 plotters = "0.3"
@@ -41,6 +42,7 @@ clap = { version = "4", features = ["derive", "string"] } # command line argumen
 [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
 rprompt = "2"
 strum = { version = "0.26", features = ["derive"] }
+strum_macros = "0.26"
 
 uuid = { version = "1", features = ["v4", "fast-rng", "serde"] }
 
@@ -55,7 +57,7 @@ sobol="1"
 itertools = "0.14"
 nalgebra = {version = "0.33", features = ["serde-serialize", "rayon"]}
 colorous = "1"
-strum_macros = "0.26"
+
 delaunator = "1"
 num = "0.4"
 kahan = "0.1"
diff --git a/opossum/examples/beam_combine_test.rs b/opossum/examples/beam_combine_test.rs
index c0608752fef6298d9af89e723bac916af64d598e..f8bba58599b0f20ede6bed496f15b439c8b834a1 100644
--- a/opossum/examples/beam_combine_test.rs
+++ b/opossum/examples/beam_combine_test.rs
@@ -5,7 +5,7 @@ use num::Zero;
 use opossum::{
     analyzers::AnalyzerType,
     error::OpmResult,
-    lightdata::{DataEnergy, LightData},
+    lightdata::{energy_spectrum_builder::EnergyDataBuilder, light_data_builder::LightDataBuilder},
     nanometer,
     nodes::{BeamSplitter, Dummy, FilterType, IdealFilter, NodeGroup, Source},
     ray::SplittingConfig,
@@ -16,18 +16,12 @@ use uom::si::f64::Length;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::new("beam combiner demo");
-    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(
-        "Source 2",
-        &LightData::Energy(DataEnergy {
-            spectrum: create_nd_glass_spec(1.0)?,
-        }),
-    ))?;
+    let light_data_builder =
+        LightDataBuilder::Energy(EnergyDataBuilder::Raw(create_he_ne_spec(1.0)?));
+    let i_s1 = scenery.add_node(Source::new("Source 1", light_data_builder))?;
+    let light_data_builder =
+        LightDataBuilder::Energy(EnergyDataBuilder::Raw(create_nd_glass_spec(1.0)?));
+    let i_s2 = scenery.add_node(Source::new("Source 2", light_data_builder))?;
     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),
diff --git a/opossum/examples/filter_test.rs b/opossum/examples/filter_test.rs
index 8e5a470b5598a518841ca8f9e02803401fda3191..1bbbdb6980c68501b56ca2d24a65eeb8daba88c1 100644
--- a/opossum/examples/filter_test.rs
+++ b/opossum/examples/filter_test.rs
@@ -4,7 +4,7 @@ use num::Zero;
 use opossum::{
     analyzers::{AnalyzerType, RayTraceConfig},
     error::OpmResult,
-    lightdata::{DataEnergy, LightData},
+    lightdata::{energy_spectrum_builder::EnergyDataBuilder, light_data_builder::LightDataBuilder},
     nodes::{BeamSplitter, EnergyMeter, FilterType, IdealFilter, NodeGroup, Source, Spectrometer},
     ray::SplittingConfig,
     spectrum::Spectrum,
@@ -15,14 +15,11 @@ use uom::si::f64::Length;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::new("filter system demo");
-    let i_s = scenery.add_node(Source::new(
-        "Source",
-        &LightData::Energy(DataEnergy {
-            spectrum: create_he_ne_spec(1.0)?,
-        }),
-    ))?;
+    let light_data_builder =
+        LightDataBuilder::Energy(EnergyDataBuilder::Raw(create_he_ne_spec(1.0)?));
+    let i_s = scenery.add_node(Source::new("Source", light_data_builder))?;
     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 filter_spectrum = Spectrum::from_csv(Path::new("./opossum/opossum/NE03B.csv"))?;
     let i_f = scenery.add_node(IdealFilter::new(
         "filter",
         &FilterType::Spectrum(filter_spectrum),
diff --git a/opossum/examples/fluence_helper_test.rs b/opossum/examples/fluence_helper_test.rs
index 724d673815e6d8dc5fb503594cdfca0664639c4b..155563b516929ab5d0a3ae4d2b1dd0b6166ebebc 100644
--- a/opossum/examples/fluence_helper_test.rs
+++ b/opossum/examples/fluence_helper_test.rs
@@ -3,7 +3,7 @@ use opossum::{
     error::OpmResult,
     fluence_distributions::general_gaussian::General2DGaussian,
     joule,
-    lightdata::LightData,
+    lightdata::{light_data_builder::LightDataBuilder, ray_data_builder::RayDataBuilder},
     millimeter, nanometer,
     nodes::{FluenceDetector, NodeGroup, ParaxialSurface, Source},
     optic_node::OpticNode,
@@ -32,7 +32,8 @@ fn main() -> OpmResult<()> {
         rays.nr_of_rays(true),
         peak.get::<joule_per_square_centimeter>()
     );
-    let mut source = Source::new("source", &LightData::Geometric(rays));
+    let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays));
+    let mut source = Source::new("source", light_data_builder);
     source.set_isometry(Isometry::identity())?;
     let mut scenery = NodeGroup::default();
     let i_src = scenery.add_node(source)?;
diff --git a/opossum/examples/fluence_test.rs b/opossum/examples/fluence_test.rs
index 4f287f6e39c207e28ee4127dc3795e6220ddb09b..c1dbe805ea926e7b5cec28c55a8622bbbc9bdd1b 100644
--- a/opossum/examples/fluence_test.rs
+++ b/opossum/examples/fluence_test.rs
@@ -6,7 +6,7 @@ use opossum::{
     energy_distributions::General2DGaussian,
     error::OpmResult,
     joule,
-    lightdata::LightData,
+    lightdata::{light_data_builder::LightDataBuilder, ray_data_builder::RayDataBuilder},
     millimeter, nanometer,
     nodes::{FluenceDetector, NodeGroup, Source},
     optic_node::OpticNode,
@@ -41,7 +41,8 @@ fn main() -> OpmResult<()> {
             peak.get::<millijoule_per_square_centimeter>()
         );
     }
-    let mut source = Source::new("source", &LightData::Geometric(rays));
+    let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays));
+    let mut source = Source::new("source", light_data_builder);
     source.set_isometry(Isometry::identity())?;
     let mut scenery = NodeGroup::default();
     let i_src = scenery.add_node(source)?;
diff --git a/opossum/examples/folded_telescope.rs b/opossum/examples/folded_telescope.rs
index 599bc3afb85ede88f9801e1e962568e6d62de9a0..dbce98b7c2052ed93ff91a0f4379b3e041f6b886 100644
--- a/opossum/examples/folded_telescope.rs
+++ b/opossum/examples/folded_telescope.rs
@@ -8,7 +8,7 @@ use opossum::{
     energy_distributions::UniformDist,
     error::OpmResult,
     joule,
-    lightdata::LightData,
+    lightdata::{light_data_builder::LightDataBuilder, ray_data_builder::RayDataBuilder},
     millimeter, nanometer,
     nodes::{Lens, NodeGroup, NodeReference, RayPropagationVisualizer, Source, ThinMirror},
     optic_node::{Alignable, OpticNode},
@@ -43,9 +43,8 @@ pub fn main() -> OpmResult<()> {
         &UniformDist::new(joule!(1.))?,
         &Hexapolar::new(millimeter!(10.), 10)?,
     )?;
-
-    let light = LightData::Geometric(rays);
-    let mut src = Source::new("collimated ray source", &light);
+    let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays));
+    let mut src = Source::new("collimated ray source", light_data_builder);
     src.set_alignment_wavelength(alignment_wvl)?;
     src.set_isometry(Isometry::identity())?;
 
diff --git a/opossum/examples/fresnel_coating.rs b/opossum/examples/fresnel_coating.rs
index 04b6df61e63c41a4aea71cc5b90fd141ee322521..a0883b9c5dc1906cdaa2264ef83d27ace227a56f 100644
--- a/opossum/examples/fresnel_coating.rs
+++ b/opossum/examples/fresnel_coating.rs
@@ -4,7 +4,7 @@ use opossum::{
     energy_distributions::UniformDist,
     error::OpmResult,
     joule,
-    lightdata::LightData,
+    lightdata::{light_data_builder::LightDataBuilder, ray_data_builder::RayDataBuilder},
     millimeter, nanometer,
     nodes::{EnergyMeter, FluenceDetector, Lens, NodeGroup, RayPropagationVisualizer, Source},
     optic_node::OpticNode,
@@ -24,7 +24,8 @@ fn main() -> OpmResult<()> {
         &UniformDist::new(joule!(1.))?,
         &Grid::new((millimeter!(9.), millimeter!(9.)), (100, 100))?,
     )?;
-    let mut source = Source::new("src", &LightData::Geometric(rays));
+    let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays));
+    let mut source = Source::new("src", light_data_builder);
     source.set_isometry(Isometry::identity())?;
     let src = scenery.add_node(source)?;
     let fd1 = scenery.add_node(FluenceDetector::new("before lens"))?;
diff --git a/opossum/examples/ghost_focus.rs b/opossum/examples/ghost_focus.rs
index 3b432dbb91cf3c78e84d12c0ef4fd217494ff225..0c3bfbd884892ec47e2fc7332b924259026a8e06 100644
--- a/opossum/examples/ghost_focus.rs
+++ b/opossum/examples/ghost_focus.rs
@@ -5,7 +5,7 @@ use opossum::{
     energy_distributions::General2DGaussian,
     error::OpmResult,
     joule,
-    lightdata::LightData,
+    lightdata::{light_data_builder::LightDataBuilder, ray_data_builder::RayDataBuilder},
     millimeter, nanometer,
     nodes::{Lens, NodeGroup, Source, ThinMirror},
     optic_node::{Alignable, OpticNode},
@@ -35,8 +35,8 @@ fn main() -> OpmResult<()> {
         &HexagonalTiling::new(millimeter!(15.0), 25, millimeter!(0.0, 0.))?,
     )?;
 
-    let light = LightData::Geometric(rays);
-    let mut src = Source::new("collimated ray source", &light);
+    let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays));
+    let mut src = Source::new("collimated ray source", light_data_builder);
     src.set_isometry(Isometry::identity())?;
     let i_src = scenery.add_node(src)?;
     let mut lens = Lens::default();
diff --git a/opossum/examples/grating_examples/grating_examples.rs b/opossum/examples/grating_examples/grating_examples.rs
index ac781de4aacc9009a53872779d767f47015cbeb0..8d19bed30531af20685cbec13f6304629c73a87d 100644
--- a/opossum/examples/grating_examples/grating_examples.rs
+++ b/opossum/examples/grating_examples/grating_examples.rs
@@ -1,5 +1,7 @@
 use nalgebra::Vector3;
 use opossum::analyzers::{AnalyzerType, RayTraceConfig};
+use opossum::lightdata::light_data_builder::LightDataBuilder;
+use opossum::lightdata::ray_data_builder::RayDataBuilder;
 use opossum::nodes::{NodeGroup, NodeReference, ParaxialSurface, SpotDiagram, ThinMirror};
 use opossum::optic_node::Alignable;
 use opossum::refractive_index::{RefrIndexConst, RefractiveIndex};
@@ -7,9 +9,7 @@ use opossum::OpmDocument;
 use opossum::{
     energy_distributions::UniformDist,
     error::OpmResult,
-    joule,
-    lightdata::LightData,
-    millimeter, nanometer,
+    joule, millimeter, nanometer,
     nodes::{Lens, RayPropagationVisualizer, Source},
     optic_node::OpticNode,
     position_distributions::Hexapolar,
@@ -56,9 +56,8 @@ fn main() -> OpmResult<()> {
         &UniformDist::new(joule!(1.))?,
         &Hexapolar::new(millimeter!(1.), 4)?,
     )?;
-    let light = LightData::Geometric(rays);
-
-    let mut src = Source::new("collimated ray source", &light);
+    let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays));
+    let mut src = Source::new("collimated ray source", light_data_builder);
     src.set_alignment_wavelength(alignment_wvl)?;
     src.set_isometry(Isometry::identity())?;
     ////////////////////////////////////
@@ -324,9 +323,8 @@ fn main() -> OpmResult<()> {
         &UniformDist::new(joule!(1.))?,
         &Hexapolar::new(millimeter!(50.), 8)?,
     )?;
-
-    let light = LightData::Geometric(rays);
-    let mut src = Source::new("collimated ray source", &light);
+    let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays));
+    let mut src = Source::new("collimated ray source", light_data_builder);
     src.set_alignment_wavelength(alignment_wvl)?;
     src.set_isometry(Isometry::identity())?;
 
diff --git a/opossum/examples/group_reverse.rs b/opossum/examples/group_reverse.rs
index 5715b59555434521dfb50a16e22583893d9b3168..332cadcb1f1e3637cf66631b8cd1d67436e7b424 100644
--- a/opossum/examples/group_reverse.rs
+++ b/opossum/examples/group_reverse.rs
@@ -4,7 +4,7 @@ use num::Zero;
 use opossum::{
     analyzers::AnalyzerType,
     error::OpmResult,
-    lightdata::{DataEnergy, LightData},
+    lightdata::{energy_spectrum_builder::EnergyDataBuilder, light_data_builder::LightDataBuilder},
     nodes::{Dummy, EnergyMeter, NodeGroup, Source},
     optic_node::OpticNode,
     spectrum_helper::create_he_ne_spec,
@@ -14,12 +14,9 @@ use uom::si::f64::Length;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::new("Inverse Group test");
-    let i_s = scenery.add_node(Source::new(
-        "Source",
-        &LightData::Energy(DataEnergy {
-            spectrum: create_he_ne_spec(1.0)?,
-        }),
-    ))?;
+    let light_data_builder =
+        LightDataBuilder::Energy(EnergyDataBuilder::Raw(create_he_ne_spec(1.0)?));
+    let i_s = scenery.add_node(Source::new("Source", light_data_builder))?;
 
     let mut group = NodeGroup::default();
     group.set_expand_view(true)?;
diff --git a/opossum/examples/hhts/hhts.rs b/opossum/examples/hhts/hhts.rs
index 7a1d12915eed581924f1bae6d900625fd54153df..42d9f979d783f7230429464971052582d54dd878 100644
--- a/opossum/examples/hhts/hhts.rs
+++ b/opossum/examples/hhts/hhts.rs
@@ -14,7 +14,7 @@ use opossum::{
     energy_distributions::General2DGaussian,
     error::OpmResult,
     joule,
-    lightdata::LightData,
+    lightdata::{light_data_builder::LightDataBuilder, ray_data_builder::RayDataBuilder},
     millimeter, nanometer,
     nodes::{
         BeamSplitter, Dummy, EnergyMeter, FilterType, IdealFilter, Lens, Metertype, NodeGroup,
@@ -138,7 +138,8 @@ fn main() -> OpmResult<()> {
     rays.add_rays(&mut rays_2w);
 
     let mut scenery = NodeGroup::new("HHT Sensor");
-    let mut src = Source::new("Source", &LightData::Geometric(rays));
+    let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays));
+    let mut src = Source::new("Source", light_data_builder);
     src.set_isometry(Isometry::identity())?;
     let src = scenery.add_node(src)?;
     let input_group = scenery.add_node(hhts_input()?)?;
@@ -224,9 +225,9 @@ fn main() -> OpmResult<()> {
     let bs = group_bs.add_node(BeamSplitter::new("Dichroic BS HBSY21", &short_pass)?)?;
 
     // Long pass filter (1w)
-    let felh1000 = FilterType::Spectrum(Spectrum::from_csv(
+    let felh1000 = FilterType::Spectrum(Spectrum::from_csv(Path::new(
         "opossum/examples/hhts/FELH1000_Transmission.csv",
-    )?);
+    ))?);
     let mut node = IdealFilter::new("1w Longpass filter", &felh1000)?;
     node.set_aperture(&PortType::Input, "input_1", &a_1inch)?;
     let filter_1w = group_bs.add_node(node)?;
@@ -239,9 +240,9 @@ fn main() -> OpmResult<()> {
     )?;
 
     // Long pass filter (2w)
-    let fesh0700 = FilterType::Spectrum(Spectrum::from_csv(
+    let fesh0700 = FilterType::Spectrum(Spectrum::from_csv(Path::new(
         "opossum/examples/hhts/FESH0700_Transmission.csv",
-    )?);
+    ))?);
     let mut node = IdealFilter::new("2w Shortpass filter", &fesh0700)?;
     node.set_aperture(&PortType::Input, "input_1", &a_1inch)?;
     let filter_2w = group_bs.add_node(node)?;
diff --git a/opossum/examples/hhts/hhts_input.rs b/opossum/examples/hhts/hhts_input.rs
index c87ba072d8044c2c8ecea0441cbf26d19a665115..7a8044ea13e547a6ac1ec7c29bf48824bd3299f1 100644
--- a/opossum/examples/hhts/hhts_input.rs
+++ b/opossum/examples/hhts/hhts_input.rs
@@ -1,3 +1,5 @@
+use std::path::Path;
+
 use opossum::{
     error::OpmResult,
     millimeter,
@@ -6,15 +8,15 @@ use opossum::{
     spectrum::Spectrum,
 };
 pub fn hhts_input() -> OpmResult<NodeGroup> {
-    let dichroic_mirror = SplittingConfig::Spectrum(Spectrum::from_csv(
+    let dichroic_mirror = SplittingConfig::Spectrum(Spectrum::from_csv(Path::new(
         "opossum/examples/hhts/MM15_Transmission.csv",
-    )?);
-    let window_filter = FilterType::Spectrum(Spectrum::from_csv(
+    ))?);
+    let window_filter = FilterType::Spectrum(Spectrum::from_csv(Path::new(
         "opossum/examples/hhts/HHTS_W1_Transmission.csv",
-    )?);
-    let double_mirror = SplittingConfig::Spectrum(Spectrum::from_csv(
+    ))?);
+    let double_mirror = SplittingConfig::Spectrum(Spectrum::from_csv(Path::new(
         "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)?)?;
diff --git a/opossum/examples/inverse_beam_splitter_test.rs b/opossum/examples/inverse_beam_splitter_test.rs
index f40d79142d6f7edcc3a5da13b8ecc8092bef9d26..aa609df5cd658424ffedb0c2885fc1932708b9fb 100644
--- a/opossum/examples/inverse_beam_splitter_test.rs
+++ b/opossum/examples/inverse_beam_splitter_test.rs
@@ -2,7 +2,7 @@ use num::Zero;
 use opossum::{
     analyzers::AnalyzerType,
     error::OpmResult,
-    lightdata::{DataEnergy, LightData},
+    lightdata::{energy_spectrum_builder::EnergyDataBuilder, light_data_builder::LightDataBuilder},
     nodes::{BeamSplitter, EnergyMeter, NodeGroup, Source},
     optic_node::OpticNode,
     ray::SplittingConfig,
@@ -14,12 +14,9 @@ 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(
-        "Source",
-        &LightData::Energy(DataEnergy {
-            spectrum: create_he_ne_spec(1.0)?,
-        }),
-    ))?;
+    let light_data_builder =
+        LightDataBuilder::Energy(EnergyDataBuilder::Raw(create_he_ne_spec(1.0)?));
+    let i_s = scenery.add_node(Source::new("Source", light_data_builder))?;
     let mut bs = BeamSplitter::new("bs", &SplittingConfig::Ratio(0.6)).unwrap();
     bs.set_inverted(true)?;
     let i_bs = scenery.add_node(bs)?;
diff --git a/opossum/examples/michaelson.rs b/opossum/examples/michaelson.rs
index 90d5b42e1e75d0b14b4c7f046dc78c668e2ef489..9d77f2c81ed57f439e0d30e18e382d14eae967f8 100644
--- a/opossum/examples/michaelson.rs
+++ b/opossum/examples/michaelson.rs
@@ -2,7 +2,7 @@ use num::Zero;
 use opossum::{
     analyzers::AnalyzerType,
     error::OpmResult,
-    lightdata::{DataEnergy, LightData},
+    lightdata::{energy_spectrum_builder::EnergyDataBuilder, light_data_builder::LightDataBuilder},
     nodes::{BeamSplitter, Dummy, NodeGroup, NodeReference, Source},
     spectrum_helper::create_he_ne_spec,
     OpmDocument,
@@ -12,12 +12,9 @@ use uom::si::f64::Length;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::new("Michaelson interferomater");
-    let src = scenery.add_node(Source::new(
-        "Source",
-        &LightData::Energy(DataEnergy {
-            spectrum: create_he_ne_spec(1.0)?,
-        }),
-    ))?;
+    let light_data_builder =
+        LightDataBuilder::Energy(EnergyDataBuilder::Raw(create_he_ne_spec(1.0)?));
+    let src = scenery.add_node(Source::new("Source", light_data_builder))?;
     let bs = scenery.add_node(BeamSplitter::default())?;
     let sample = scenery.add_node(Dummy::new("Sample"))?;
     let rf = NodeReference::from_node(&scenery.node(sample)?);
diff --git a/opossum/examples/off_center_position_distribution.rs b/opossum/examples/off_center_position_distribution.rs
index 4295ad75c04b4fbc5efb497de050dca6e10f46b3..9f11a3caca5ee68f81567b1ad4f25d333848bf53 100644
--- a/opossum/examples/off_center_position_distribution.rs
+++ b/opossum/examples/off_center_position_distribution.rs
@@ -5,7 +5,7 @@ use opossum::{
     energy_distributions::General2DGaussian,
     error::OpmResult,
     joule,
-    lightdata::LightData,
+    lightdata::{light_data_builder::LightDataBuilder, ray_data_builder::RayDataBuilder},
     millimeter, nanometer,
     nodes::{NodeGroup, Source, SpotDiagram},
     optic_node::OpticNode,
@@ -54,8 +54,8 @@ fn main() -> OpmResult<()> {
     let mut rays = rays_1w;
     rays.add_rays(&mut rays_2w);
     let mut scenery = NodeGroup::new("test");
-
-    let mut src = Source::new("Source", &LightData::Geometric(rays));
+    let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays));
+    let mut src = Source::new("Source", light_data_builder);
     src.set_isometry(Isometry::identity())?;
     let src = scenery.add_node(src)?;
     let i_sd = scenery.add_node(SpotDiagram::default())?;
diff --git a/opossum/examples/prism_dispersion.rs b/opossum/examples/prism_dispersion.rs
index 73c11d201cc97e9771abbfe71a1aeb0dc72b896c..a0e5e96eab9e463aff5d2b709c62ec053dc1b2fd 100644
--- a/opossum/examples/prism_dispersion.rs
+++ b/opossum/examples/prism_dispersion.rs
@@ -6,7 +6,7 @@ use opossum::{
     degree,
     error::OpmResult,
     joule,
-    lightdata::LightData,
+    lightdata::{light_data_builder::LightDataBuilder, ray_data_builder::RayDataBuilder},
     millimeter, nanometer,
     nodes::{NodeGroup, RayPropagationVisualizer, Source, SpotDiagram, Wedge},
     optic_node::{Alignable, OpticNode},
@@ -46,8 +46,8 @@ fn main() -> OpmResult<()> {
     rays_1w.add_rays(&mut rays_2w);
 
     let mut scenery = NodeGroup::default();
-    let light = LightData::Geometric(rays_1w);
-    let mut light_src = Source::new("collimated ray source", &light);
+    let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays_1w));
+    let mut light_src = Source::new("collimated ray source", light_data_builder);
     light_src.set_isometry(Isometry::identity())?;
     let src = scenery.add_node(light_src)?;
 
diff --git a/opossum/examples/reference_test.rs b/opossum/examples/reference_test.rs
index a5ef3db6759bf5e0f4eced60ee3c628019b04803..e87695dc71fc463646556f5de62021cd1bb704ec 100644
--- a/opossum/examples/reference_test.rs
+++ b/opossum/examples/reference_test.rs
@@ -2,7 +2,7 @@ use num::Zero;
 use opossum::{
     analyzers::AnalyzerType,
     error::OpmResult,
-    lightdata::{DataEnergy, LightData},
+    lightdata::{energy_spectrum_builder::EnergyDataBuilder, light_data_builder::LightDataBuilder},
     nodes::{EnergyMeter, IdealFilter, NodeGroup, NodeReference, Source},
     spectrum_helper::create_he_ne_spec,
     OpmDocument,
@@ -12,12 +12,9 @@ use uom::si::f64::Length;
 
 fn main() -> OpmResult<()> {
     let mut scenery = NodeGroup::new("Reference node demo");
-    let src = scenery.add_node(Source::new(
-        "source",
-        &LightData::Energy(DataEnergy {
-            spectrum: create_he_ne_spec(1.0)?,
-        }),
-    ))?;
+    let light_data_builder =
+        LightDataBuilder::Energy(EnergyDataBuilder::Raw(create_he_ne_spec(1.0)?));
+    let src = scenery.add_node(Source::new("source", light_data_builder))?;
     let filt = scenery.add_node(IdealFilter::new(
         "50 % filter",
         &opossum::nodes::FilterType::Constant(0.5),
diff --git a/opossum/examples/spectrum_test.rs b/opossum/examples/spectrum_test.rs
index 3cca53f7e8b89796156edefffa5b8fc5222e7f1e..471f124be7fddf8f29e27cb5c3cf31bad2cdd4d8 100644
--- a/opossum/examples/spectrum_test.rs
+++ b/opossum/examples/spectrum_test.rs
@@ -20,7 +20,7 @@ fn main() -> OpmResult<()> {
         PltBackEnd::SVG,
     )?;
 
-    let s4 = Spectrum::from_csv("./opossum/playground/NE03B.csv")?;
+    let s4 = Spectrum::from_csv(Path::new("./opossum/playground/NE03B.csv"))?;
     s4.to_plot(
         Path::new("./opossum/playground/ne03b_raw.svg"),
         PltBackEnd::SVG,
diff --git a/opossum/examples/two_color_rays.rs b/opossum/examples/two_color_rays.rs
index bd0773f36d8e01e9eb9320ca6c011a607116ab7a..e183fd59ad4081265471de39c7a9b4f445ac2060 100644
--- a/opossum/examples/two_color_rays.rs
+++ b/opossum/examples/two_color_rays.rs
@@ -5,7 +5,7 @@ use opossum::{
     analyzers::{AnalyzerType, RayTraceConfig},
     error::OpmResult,
     joule,
-    lightdata::LightData,
+    lightdata::{light_data_builder::LightDataBuilder, ray_data_builder::RayDataBuilder},
     millimeter, nanometer,
     nodes::{Lens, NodeGroup, RayPropagationVisualizer, Source, SpotDiagram},
     position_distributions::{FibonacciEllipse, Hexapolar},
@@ -38,8 +38,8 @@ fn main() -> OpmResult<()> {
     rays_1w.add_rays(&mut rays_3w);
 
     let mut scenery = NodeGroup::default();
-    let light = LightData::Geometric(rays_1w);
-    let src = scenery.add_node(Source::new("collimated ray source", &light))?;
+    let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays_1w));
+    let src = scenery.add_node(Source::new("collimated ray source", light_data_builder))?;
     let l1 = scenery.add_node(Lens::new(
         "l1",
         millimeter!(200.0),
diff --git a/opossum/files_for_testing/opm/incorrect_opm.opm b/opossum/files_for_testing/opm/incorrect_opm.opm
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..dd626a0f311e80501dee9549fc8e3d1a2ba3d01f 100644
--- a/opossum/files_for_testing/opm/incorrect_opm.opm
+++ b/opossum/files_for_testing/opm/incorrect_opm.opm
@@ -0,0 +1 @@
+()
\ No newline at end of file
diff --git a/opossum/files_for_testing/opm/optic_ref.opm b/opossum/files_for_testing/opm/optic_ref.opm
index 796c89f368c3e95bca995a4a780999c18d38f58a..229203014623fb80ed05eebde918eade93711744 100644
--- a/opossum/files_for_testing/opm/optic_ref.opm
+++ b/opossum/files_for_testing/opm/optic_ref.opm
@@ -1,59 +1,44 @@
-attributes:
-  node_type: dummy
-  name: test123
-  uuid: 587ee70f-6f52-4420-89f6-e1618ff4dbdb
-  lidt: 10000.
-  ports:
-    inputs:
-      input_1:
-        anchor_point_iso:
-          transform:
-            rotation:
-            - 0.0
-            - 0.0
-            - 0.0
-            - 1.0
-            translation:
-            - 0.0
-            - 0.0
-            - 0.0
-          inverse:
-            rotation:
-            - 0.0
-            - 0.0
-            - 0.0
-            - 1.0
-            translation:
-            - 0.0
-            - 0.0
-            - 0.0
-        coating: IdealAR
-        lidt: 10000.0
-    outputs:
-      output_1:
-        anchor_point_iso:
-          transform:
-            rotation:
-            - 0.0
-            - 0.0
-            - 0.0
-            - 1.0
-            translation:
-            - 0.0
-            - 0.0
-            - 0.0
-          inverse:
-            rotation:
-            - 0.0
-            - 0.0
-            - 0.0
-            - 1.0
-            translation:
-            - 0.0
-            - 0.0
-            - 0.0
-        coating: IdealAR
-        lidt: 10000.0
-  inverted: false
-  props:
-      
\ No newline at end of file
+(
+    attributes: (
+        node_type: "dummy",
+        name: "test123",
+        ports: (
+            inputs: {
+                "input_1": (
+                    anchor_point_iso: (
+                        transform: (
+                            rotation: (0.0, 0.0, 0.0, 1.0),
+                            translation: (0.0, 0.0, 0.0),
+                        ),
+                        inverse: (
+                            rotation: (0.0, 0.0, 0.0, 1.0),
+                            translation: (0.0, 0.0, 0.0),
+                        ),
+                    ),
+                    coating: IdealAR,
+                    lidt: 10000.0,
+                ),
+            },
+            outputs: {
+                "output_1": (
+                    anchor_point_iso: (
+                        transform: (
+                            rotation: (0.0, 0.0, 0.0, 1.0),
+                            translation: (0.0, 0.0, 0.0),
+                        ),
+                        inverse: (
+                            rotation: (0.0, 0.0, 0.0, 1.0),
+                            translation: (0.0, 0.0, 0.0),
+                        ),
+                    ),
+                    coating: IdealAR,
+                    lidt: 10000.0,
+                ),
+            },
+        ),
+        uuid: "98248e7f-dc4c-4131-8710-f3d5be2ff087",
+        lidt: 10000.0,
+        props: {},
+        inverted: false,
+    ),
+)
\ No newline at end of file
diff --git a/opossum/files_for_testing/opm/opticscenery.opm b/opossum/files_for_testing/opm/opticscenery.opm
index 8e6dd541b47270fa7e4152f8894f7874366bfeaf..306d0b7b70ead4988c0c8b90417ebf3496349ecf 100644
--- a/opossum/files_for_testing/opm/opticscenery.opm
+++ b/opossum/files_for_testing/opm/opticscenery.opm
@@ -1,148 +1,129 @@
-opm file version: '0'
-scenery:
-  node_attr:
-    node_type: group
-    name: OpticScenery demo
-    ports:
-      inputs: {}
-      outputs: {}
-    uuid: df1fc497-4e6d-413d-8160-d7facb93aa27
-    lidt: 10000.0
-    props:
-      expand view: !Bool false
-    inverted: false
-  graph:
-    nodes:
-    - attributes:
-        node_type: dummy
-        name: dummy1
-        ports:
-          inputs:
-            input_1:
-              anchor_point_iso:
-                transform:
-                  rotation:
-                  - 0.0
-                  - 0.0
-                  - 0.0
-                  - 1.0
-                  translation:
-                  - 0.0
-                  - 0.0
-                  - 0.0
-                inverse:
-                  rotation:
-                  - 0.0
-                  - 0.0
-                  - 0.0
-                  - 1.0
-                  translation:
-                  - 0.0
-                  - 0.0
-                  - 0.0
-              coating: IdealAR
-              lidt: 10000.0
-          outputs:
-            output_1:
-              anchor_point_iso:
-                transform:
-                  rotation:
-                  - 0.0
-                  - 0.0
-                  - 0.0
-                  - 1.0
-                  translation:
-                  - 0.0
-                  - 0.0
-                  - 0.0
-                inverse:
-                  rotation:
-                  - 0.0
-                  - 0.0
-                  - 0.0
-                  - 1.0
-                  translation:
-                  - 0.0
-                  - 0.0
-                  - 0.0
-              coating: IdealAR
-              lidt: 10000.0
-        uuid: cc2aa814-61f0-470e-8c66-6d611e4e88a0
-        lidt: 10000.0
-        props: {}
-        inverted: false
-    - attributes:
-        node_type: dummy
-        name: dummy2
-        ports:
-          inputs:
-            input_1:
-              anchor_point_iso:
-                transform:
-                  rotation:
-                  - 0.0
-                  - 0.0
-                  - 0.0
-                  - 1.0
-                  translation:
-                  - 0.0
-                  - 0.0
-                  - 0.0
-                inverse:
-                  rotation:
-                  - 0.0
-                  - 0.0
-                  - 0.0
-                  - 1.0
-                  translation:
-                  - 0.0
-                  - 0.0
-                  - 0.0
-              coating: IdealAR
-              lidt: 10000.0
-          outputs:
-            output_1:
-              anchor_point_iso:
-                transform:
-                  rotation:
-                  - 0.0
-                  - 0.0
-                  - 0.0
-                  - 1.0
-                  translation:
-                  - 0.0
-                  - 0.0
-                  - 0.0
-                inverse:
-                  rotation:
-                  - 0.0
-                  - 0.0
-                  - 0.0
-                  - 1.0
-                  translation:
-                  - 0.0
-                  - 0.0
-                  - 0.0
-              coating: IdealAR
-              lidt: 10000.0
-        uuid: 9e04cd4d-a09b-4300-addb-987d495ca5e3
-        lidt: 10000.0
-        props: {}
-        inverted: false
-    edges:
-    - - cc2aa814-61f0-470e-8c66-6d611e4e88a0
-      - output_1
-      - 9e04cd4d-a09b-4300-addb-987d495ca5e3
-      - input_1
-      - 0.0
-    input_map: {}
-    output_map: {}
-global:
-  ambient_refr_index: !Const
-    refractive_index: 1.0
-analyzers:
-- !RayTrace
-  min_energy_per_ray: 1e-12
-  max_number_of_bounces: 1000
-  max_number_of_refractions: 1000
-  missed_surface_strategy: Stop
+(
+    opm_file_version: "0",
+    scenery: (
+        node_attr: (
+            node_type: "group",
+            name: "OpticScenery demo",
+            ports: (
+                inputs: {},
+                outputs: {},
+            ),
+            uuid: "1369c599-1750-43c4-8ff8-2ad317507397",
+            lidt: 10000.0,
+            props: {
+                "expand view": Bool(false),
+            },
+            inverted: false,
+        ),
+        graph: (
+            nodes: [
+                (
+                    attributes: (
+                        node_type: "dummy",
+                        name: "dummy1",
+                        ports: (
+                            inputs: {
+                                "input_1": (
+                                    anchor_point_iso: (
+                                        transform: (
+                                            rotation: (0.0, 0.0, 0.0, 1.0),
+                                            translation: (0.0, 0.0, 0.0),
+                                        ),
+                                        inverse: (
+                                            rotation: (0.0, 0.0, 0.0, 1.0),
+                                            translation: (0.0, 0.0, 0.0),
+                                        ),
+                                    ),
+                                    coating: IdealAR,
+                                    lidt: 10000.0,
+                                ),
+                            },
+                            outputs: {
+                                "output_1": (
+                                    anchor_point_iso: (
+                                        transform: (
+                                            rotation: (0.0, 0.0, 0.0, 1.0),
+                                            translation: (0.0, 0.0, 0.0),
+                                        ),
+                                        inverse: (
+                                            rotation: (0.0, 0.0, 0.0, 1.0),
+                                            translation: (0.0, 0.0, 0.0),
+                                        ),
+                                    ),
+                                    coating: IdealAR,
+                                    lidt: 10000.0,
+                                ),
+                            },
+                        ),
+                        uuid: "f9b965c3-0b5b-4d00-8e83-a435ad5b1f8b",
+                        lidt: 10000.0,
+                        props: {},
+                        inverted: false,
+                    ),
+                ),
+                (
+                    attributes: (
+                        node_type: "dummy",
+                        name: "dummy2",
+                        ports: (
+                            inputs: {
+                                "input_1": (
+                                    anchor_point_iso: (
+                                        transform: (
+                                            rotation: (0.0, 0.0, 0.0, 1.0),
+                                            translation: (0.0, 0.0, 0.0),
+                                        ),
+                                        inverse: (
+                                            rotation: (0.0, 0.0, 0.0, 1.0),
+                                            translation: (0.0, 0.0, 0.0),
+                                        ),
+                                    ),
+                                    coating: IdealAR,
+                                    lidt: 10000.0,
+                                ),
+                            },
+                            outputs: {
+                                "output_1": (
+                                    anchor_point_iso: (
+                                        transform: (
+                                            rotation: (0.0, 0.0, 0.0, 1.0),
+                                            translation: (0.0, 0.0, 0.0),
+                                        ),
+                                        inverse: (
+                                            rotation: (0.0, 0.0, 0.0, 1.0),
+                                            translation: (0.0, 0.0, 0.0),
+                                        ),
+                                    ),
+                                    coating: IdealAR,
+                                    lidt: 10000.0,
+                                ),
+                            },
+                        ),
+                        uuid: "881bad60-f450-4db5-bf96-098896e1edbe",
+                        lidt: 10000.0,
+                        props: {},
+                        inverted: false,
+                    ),
+                ),
+            ],
+            edges: [
+                ("f9b965c3-0b5b-4d00-8e83-a435ad5b1f8b", "output_1", "881bad60-f450-4db5-bf96-098896e1edbe", "input_1", 0.0),
+            ],
+            input_map: ({}),
+            output_map: ({}),
+        ),
+    ),
+    global: (
+        ambient_refr_index: Const((
+            refractive_index: 1.0,
+        )),
+    ),
+    analyzers: [
+        RayTrace((
+            min_energy_per_ray: 0.000000000001,
+            max_number_of_bounces: 1000,
+            max_number_of_refractions: 1000,
+            missed_surface_strategy: Stop,
+        )),
+    ],
+)
\ No newline at end of file
diff --git a/opossum/src/analyzers/energy.rs b/opossum/src/analyzers/energy.rs
index f8799bb8a9d268ab98a2a907f1b028fa259301be..9a743d2d8f34060775b6bb30c5bbef5330eeff9d 100644
--- a/opossum/src/analyzers/energy.rs
+++ b/opossum/src/analyzers/energy.rs
@@ -45,11 +45,12 @@ mod test {
     use super::EnergyAnalyzer;
     use crate::{
         analyzers::Analyzer,
-        lightdata::{DataEnergy, LightData},
+        lightdata::{
+            energy_spectrum_builder::EnergyDataBuilder, light_data_builder::LightDataBuilder,
+        },
         nodes::{EnergyMeter, NodeGroup, Source},
         spectrum_helper::create_he_ne_spec,
     };
-
     #[test]
     fn analyze_empty_scene() {
         let mut scenery = NodeGroup::default();
@@ -58,11 +59,9 @@ mod test {
     }
     fn create_scene() -> NodeGroup {
         let mut scenery = NodeGroup::default();
-        let data_energy = DataEnergy {
-            spectrum: create_he_ne_spec(1.0).unwrap(),
-        };
-        let light_data = LightData::Energy(data_energy);
-        let src = Source::new("source", &light_data);
+        let light_data_builder =
+            LightDataBuilder::Energy(EnergyDataBuilder::Raw(create_he_ne_spec(1.0).unwrap()));
+        let src = Source::new("source", light_data_builder);
         let i_src = scenery.add_node(src).unwrap();
         let i_em = scenery.add_node(EnergyMeter::default()).unwrap();
         scenery
diff --git a/opossum/src/analyzers/raytrace.rs b/opossum/src/analyzers/raytrace.rs
index 316a24eedaf8419a706a072048628cf32f4fd981..5b414500c8bfc09901bd242a1ec0b2deaa2a046b 100644
--- a/opossum/src/analyzers/raytrace.rs
+++ b/opossum/src/analyzers/raytrace.rs
@@ -271,7 +271,7 @@ pub trait AnalysisRayTrace: OpticNode {
             degree!(0.)
         };
 
-        Ok((index_model.value.clone(), *center_thickness, angle))
+        Ok((index_model.clone(), *center_thickness, angle))
     }
 }
 
diff --git a/opossum/src/dottable.rs b/opossum/src/dottable.rs
index 9424cc8b9f03367a5d08caf5f78ac1c37fdb1576..bdad9bfe5c1c62b970b250c486039509ff86e2d2 100644
--- a/opossum/src/dottable.rs
+++ b/opossum/src/dottable.rs
@@ -332,7 +332,7 @@ pub trait Dottable {
 #[cfg(test)]
 mod test {
     use crate::{
-        lightdata::LightData,
+        lightdata::light_data_builder::LightDataBuilder,
         nodes::{BeamSplitter, Dummy, EnergyMeter, Metertype, NodeGroup, Source},
         ray::SplittingConfig,
     };
@@ -381,7 +381,7 @@ mod test {
 
         let mut scenery = NodeGroup::default();
         let i_s = scenery
-            .add_node(Source::new("Source", &LightData::Fourier))
+            .add_node(Source::new("Source", LightDataBuilder::Fourier))
             .unwrap();
         let bs = BeamSplitter::new("test", &SplittingConfig::Ratio(0.6)).unwrap();
         // bs.node_attr_mut().set_name("Beam splitter");
diff --git a/opossum/src/lightdata/energy_spectrum_builder.rs b/opossum/src/lightdata/energy_spectrum_builder.rs
new file mode 100644
index 0000000000000000000000000000000000000000..68f392f43f86b2ee35506ec389eb1b731bd72615
--- /dev/null
+++ b/opossum/src/lightdata/energy_spectrum_builder.rs
@@ -0,0 +1,52 @@
+//! Builder for the generation of energy spectra.
+//!
+//! This module provides a builder for the generation of energy spectra to be used in `LightData::Energy`.
+//! Using this builder allows easier serialization / deserialization in OPM files.
+use crate::{error::OpmResult, spectrum::Spectrum};
+use serde::{Deserialize, Serialize};
+use std::{fmt::Display, path::PathBuf};
+use uom::si::f64::{Energy, Length};
+
+use super::{DataEnergy, LightData};
+
+/// Builder for the generation of energy spectra.
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
+pub enum EnergyDataBuilder {
+    /// Build a spectrum from raw data.
+    Raw(Spectrum),
+    /// Build a spectrum from a (CSV) file.
+    FromFile(PathBuf),
+    /// Build a spectrum from a set of (narrow) laser lines (center wavelength, energy) and a given spectrum resolution.
+    LaserLines(Vec<(Length, Energy)>, Length),
+}
+impl EnergyDataBuilder {
+    /// Build the spectrum from the builder.
+    ///
+    /// # Errors
+    /// This function will return an error if the concrete implementation of the builder fails.
+    pub fn build(&self) -> OpmResult<LightData> {
+        match self {
+            Self::Raw(s) => Ok(LightData::Energy(DataEnergy {
+                spectrum: s.clone(),
+            })),
+            Self::FromFile(p) => {
+                let spectrum = Spectrum::from_csv(p)?;
+                Ok(LightData::Energy(DataEnergy { spectrum }))
+            }
+            Self::LaserLines(l, r) => {
+                let spectrum = Spectrum::from_laser_lines(l.clone(), *r)?;
+                Ok(LightData::Energy(DataEnergy { spectrum }))
+            }
+        }
+    }
+}
+
+impl Display for EnergyDataBuilder {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Raw(s) => write!(f, "Raw({s})"),
+            Self::FromFile(p) => write!(f, "FromFile({})", p.display()),
+            Self::LaserLines(l, r) => write!(f, "LaserLines({:?}, {})", l, r.value),
+        }
+    }
+}
diff --git a/opossum/src/lightdata/light_data_builder.rs b/opossum/src/lightdata/light_data_builder.rs
new file mode 100644
index 0000000000000000000000000000000000000000..fe642e3097194ccdc2f0352d30627aac4dff9df1
--- /dev/null
+++ b/opossum/src/lightdata/light_data_builder.rs
@@ -0,0 +1,52 @@
+//! Builder for [`LightData`]
+//!
+//! This module provides a builder for the generation of [`LightData`] to be used in `Source`.
+//! This builder allows easier serialization / deserialization in OPM files.
+use serde::{Deserialize, Serialize};
+use std::fmt::Display;
+
+use super::{
+    energy_spectrum_builder::EnergyDataBuilder, ray_data_builder::RayDataBuilder, LightData,
+};
+use crate::{error::OpmResult, properties::Proptype};
+
+/// Builder for the generation of [`LightData`].
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
+pub enum LightDataBuilder {
+    /// Builder for the generation of [`LightData::Energy`].
+    Energy(EnergyDataBuilder),
+    /// Builder for the generation of [`LightData::Geometric`].
+    Geometric(RayDataBuilder),
+    /// Dummy Fourier
+    Fourier,
+}
+
+impl LightDataBuilder {
+    /// Create [`LightData`] from the builder definition.
+    ///
+    /// # Errors
+    ///
+    /// This function will return an error if the concrete implementation of the builder fails.
+    pub fn build(self) -> OpmResult<LightData> {
+        match self {
+            Self::Energy(e) => e.build(),
+            Self::Geometric(r) => r.build(),
+            Self::Fourier => Ok(LightData::Fourier),
+        }
+    }
+}
+
+impl Display for LightDataBuilder {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Energy(e) => write!(f, "Energy({e})"),
+            Self::Geometric(r) => write!(f, "Geometric({r})"),
+            Self::Fourier => write!(f, "Fourier"),
+        }
+    }
+}
+impl From<Option<LightDataBuilder>> for Proptype {
+    fn from(value: Option<LightDataBuilder>) -> Self {
+        Self::LightDataBuilder(value)
+    }
+}
diff --git a/opossum/src/lightdata.rs b/opossum/src/lightdata/mod.rs
similarity index 84%
rename from opossum/src/lightdata.rs
rename to opossum/src/lightdata/mod.rs
index a7dfddfe2dfea65609a01648c0717752655d0daf..a410ffe8053ad2ca8ed56a6abfe4d52c533daeb3 100644
--- a/opossum/src/lightdata.rs
+++ b/opossum/src/lightdata/mod.rs
@@ -1,5 +1,9 @@
 #![warn(missing_docs)]
 //! Data structures containing the light information flowing between [`OpticNode`s](crate::optic_node::OpticNode).
+
+pub mod energy_spectrum_builder;
+pub mod light_data_builder;
+pub mod ray_data_builder;
 use crate::{error::OpmResult, joule, nodes::FilterType, rays::Rays, spectrum::Spectrum};
 use serde::{Deserialize, Serialize};
 use std::fmt::Display;
@@ -35,6 +39,7 @@ impl Display for LightData {
         }
     }
 }
+
 #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
 /// Data structure for storing [`LightData::Energy`] data.
 ///
@@ -63,7 +68,10 @@ impl DataEnergy {
 }
 #[cfg(test)]
 mod test {
-    use crate::{properties::Proptype, spectrum_helper::create_visible_spec, utils::EnumProxy};
+    use crate::{
+        lightdata::light_data_builder::LightDataBuilder, properties::Proptype,
+        spectrum_helper::create_visible_spec,
+    };
 
     use super::*;
     use assert_matches::assert_matches;
@@ -85,23 +93,9 @@ mod test {
     fn debug() {
         assert_eq!(format!("{:?}", LightData::Fourier), "Fourier");
     }
-    // #[test]
-    // fn export_wrong() {
-    //     assert!(LightData::Fourier.export(Path::new("")).is_err());
-    // }
     #[test]
     fn from() {
-        let ld = Proptype::from(EnumProxy::<Option<LightData>> {
-            value: Some(LightData::Fourier),
-        });
-        assert_matches!(ld, Proptype::LightData(_));
+        let ld = Proptype::from(Some(LightDataBuilder::Fourier));
+        assert_matches!(ld, Proptype::LightDataBuilder(_));
     }
-    // #[test]
-    // fn data_energy_pdf_report() {
-    //     assert!(DataEnergy {
-    //         spectrum: create_visible_spec()
-    //     }
-    //     .pdf_report()
-    //     .is_ok());
-    // }
 }
diff --git a/opossum/src/lightdata/ray_data_builder.rs b/opossum/src/lightdata/ray_data_builder.rs
new file mode 100644
index 0000000000000000000000000000000000000000..5ee4d67b74b2b012be5729795f0356689b4beaf5
--- /dev/null
+++ b/opossum/src/lightdata/ray_data_builder.rs
@@ -0,0 +1,37 @@
+//! Builder for the generation of [`LightData`].
+//!
+//! This module provides a builder for the generation of [`LightData`] to be used in `Source`.
+//! This builder allows easier serialization / deserialization in OPM files.
+use std::fmt::Display;
+
+use crate::{error::OpmResult, rays::Rays};
+use serde::{Deserialize, Serialize};
+
+use super::LightData;
+
+/// Builder for the generation of [`LightData::Geometric`].
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
+pub enum RayDataBuilder {
+    /// Raw [`Rays`] data.
+    Raw(Rays),
+}
+
+impl RayDataBuilder {
+    /// Create [`LightData::Geometric`] from the builder definition.
+    ///
+    /// # Errors
+    /// This function will return an error if the concrete implementation of the builder fails.
+    pub fn build(self) -> OpmResult<LightData> {
+        match self {
+            Self::Raw(rays) => Ok(LightData::Geometric(rays)),
+        }
+    }
+}
+
+impl Display for RayDataBuilder {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        match self {
+            Self::Raw(r) => write!(f, "Raw({r})"),
+        }
+    }
+}
diff --git a/opossum/src/main.rs b/opossum/src/main.rs
index 6eedfb168c121faa61921d8347e81cf0c0bbe748..a3c2396c43949e954cc4d28cf55e7a533bcca25c 100644
--- a/opossum/src/main.rs
+++ b/opossum/src/main.rs
@@ -82,8 +82,12 @@ fn create_report_and_data_files(
         "yaml",
         "analysis report",
     )?;
-    write!(output, "{}", serde_yaml::to_string(&report).unwrap())
-        .map_err(|e| OpossumError::Other(format!("writing report file failed: {e}")))?;
+    write!(
+        output,
+        "{}",
+        ron::ser::to_string_pretty(&report, ron::ser::PrettyConfig::default()).unwrap()
+    )
+    .map_err(|e| OpossumError::Other(format!("writing report file failed: {e}")))?;
     let mut report_path = report_directory.to_path_buf();
     report.export_data(&report_path)?;
     report_path.push(format!("report_{report_number}.html"));
diff --git a/opossum/src/nodes/beam_splitter/mod.rs b/opossum/src/nodes/beam_splitter/mod.rs
index 7405a9b16c2555deb6d4edb4c5d9126edae2c31c..9e90c3c8868ac7ec26a40d320ffd0aede923e406 100644
--- a/opossum/src/nodes/beam_splitter/mod.rs
+++ b/opossum/src/nodes/beam_splitter/mod.rs
@@ -16,7 +16,7 @@ use crate::{
     rays::Rays,
     spectrum::{merge_spectra, Spectrum},
     surface::{geo_surface::GeoSurfaceRef, Plane},
-    utils::{geom_transformation::Isometry, EnumProxy},
+    utils::geom_transformation::Isometry,
 };
 use opm_macros_lib::OpmNode;
 use std::sync::{Arc, Mutex};
@@ -51,10 +51,7 @@ impl Default for BeamSplitter {
             .create_property(
                 "splitter config",
                 "config data of the beam splitter",
-                EnumProxy::<SplittingConfig> {
-                    value: SplittingConfig::Ratio(0.5),
-                }
-                .into(),
+                SplittingConfig::Ratio(0.5).into(),
             )
             .unwrap();
         let mut bs = Self { node_attr };
@@ -75,13 +72,8 @@ impl BeamSplitter {
         }
         let mut bs = Self::default();
         bs.node_attr.set_name(name);
-        bs.node_attr.set_property(
-            "splitter config",
-            EnumProxy::<SplittingConfig> {
-                value: config.clone(),
-            }
-            .into(),
-        )?;
+        bs.node_attr
+            .set_property("splitter config", config.clone().into())?;
         bs.update_surfaces()?;
         Ok(bs)
     }
@@ -93,7 +85,7 @@ impl BeamSplitter {
     #[must_use]
     pub fn splitting_config(&self) -> SplittingConfig {
         if let Ok(Proptype::SplitterType(config)) = self.node_attr.get_property("splitter config") {
-            return config.value.clone();
+            return config.clone();
         }
         panic!("property `splitter config` does not exist or has wrong data format")
     }
@@ -102,13 +94,8 @@ impl BeamSplitter {
     /// # Errors
     /// This function returns an [`OpossumError::Other`] if the [`SplittingConfig`] is invalid.
     pub fn set_splitting_config(&mut self, config: &SplittingConfig) -> OpmResult<()> {
-        self.node_attr.set_property(
-            "splitter config",
-            EnumProxy::<SplittingConfig> {
-                value: config.clone(),
-            }
-            .into(),
-        )?;
+        self.node_attr
+            .set_property("splitter config", config.clone().into())?;
         Ok(())
     }
     fn split_spectrum(
@@ -222,7 +209,7 @@ impl BeamSplitter {
                         ));
                     }
 
-                    let split_rays = rays.split(&splitting_config.value)?;
+                    let split_rays = rays.split(&splitting_config)?;
                     (rays, split_rays)
                 }
                 _ => {
@@ -251,7 +238,7 @@ impl BeamSplitter {
                             return Err(OpossumError::OpticPort("input aperture not found".into()));
                         };
 
-                        let split_rays = rays.split(&splitting_config.value)?;
+                        let split_rays = rays.split(&splitting_config)?;
                         (rays, split_rays)
                     } else {
                         return Err(OpossumError::OpticPort(
diff --git a/opossum/src/nodes/cylindric_lens/mod.rs b/opossum/src/nodes/cylindric_lens/mod.rs
index d4fe9ce3f26f3ecf01288dab1e19cba7f3e2a052..0271bc1213244cbe2edd9693d89f8ef2f3f76f01 100644
--- a/opossum/src/nodes/cylindric_lens/mod.rs
+++ b/opossum/src/nodes/cylindric_lens/mod.rs
@@ -11,7 +11,7 @@ use crate::{
     radian,
     refractive_index::{RefrIndexConst, RefractiveIndex, RefractiveIndexType},
     surface::{geo_surface::GeoSurfaceRef, Cylinder, Plane},
-    utils::{geom_transformation::Isometry, EnumProxy},
+    utils::geom_transformation::Isometry,
 };
 #[cfg(feature = "bevy")]
 use bevy::{math::primitives::Cuboid, render::mesh::Mesh};
@@ -81,10 +81,7 @@ impl Default for CylindricLens {
             .create_property(
                 "refractive index",
                 "refractive index of the lens material",
-                EnumProxy::<RefractiveIndexType> {
-                    value: RefractiveIndexType::Const(RefrIndexConst::new(1.5).unwrap()),
-                }
-                .into(),
+                RefractiveIndexType::Const(RefrIndexConst::new(1.5).unwrap()).into(),
             )
             .unwrap();
         let mut cyl_lens = Self { node_attr };
@@ -139,13 +136,9 @@ impl CylindricLens {
             .node_attr
             .set_property("center thickness", center_thickness.into())?;
 
-        cyl_lens.node_attr.set_property(
-            "refractive index",
-            EnumProxy::<RefractiveIndexType> {
-                value: refractive_index.to_enum(),
-            }
-            .into(),
-        )?;
+        cyl_lens
+            .node_attr
+            .set_property("refractive index", refractive_index.to_enum().into())?;
         cyl_lens.update_surfaces()?;
         Ok(cyl_lens)
     }
@@ -285,10 +278,7 @@ mod test {
         else {
             panic!()
         };
-        assert_eq!(
-            (*index).value.get_refractive_index(Length::zero()).unwrap(),
-            1.5
-        );
+        assert_eq!((*index).get_refractive_index(Length::zero()).unwrap(), 1.5);
     }
     #[test]
     fn new() {
@@ -334,9 +324,8 @@ mod test {
             panic!()
         };
         assert_eq!(*roc, millimeter!(11.0));
-        let Ok(Proptype::RefractiveIndex(EnumProxy::<RefractiveIndexType> {
-            value: RefractiveIndexType::Const(ref_index_const),
-        })) = node.node_attr.get_property("refractive index")
+        let Ok(Proptype::RefractiveIndex(RefractiveIndexType::Const(ref_index_const))) =
+            node.node_attr.get_property("refractive index")
         else {
             panic!()
         };
diff --git a/opossum/src/nodes/ideal_filter.rs b/opossum/src/nodes/ideal_filter.rs
index 02ad7bfccda0fead355243e5abc7d80450af4168..af8fda9a51adc9070a00208d638d6e555aef0bfd 100644
--- a/opossum/src/nodes/ideal_filter.rs
+++ b/opossum/src/nodes/ideal_filter.rs
@@ -13,7 +13,6 @@ use crate::{
     properties::Proptype,
     rays::Rays,
     spectrum::Spectrum,
-    utils::EnumProxy,
 };
 use opm_macros_lib::OpmNode;
 use serde::{Deserialize, Serialize};
@@ -26,6 +25,11 @@ pub enum FilterType {
     /// filter based on given transmission spectrum.
     Spectrum(Spectrum),
 }
+impl From<FilterType> for Proptype {
+    fn from(f: FilterType) -> Self {
+        Self::FilterType(f)
+    }
+}
 #[derive(OpmNode, Debug, Clone)]
 #[opm_node("darkgray")]
 /// An ideal filter with given transmission or optical density.
@@ -53,10 +57,7 @@ impl Default for IdealFilter {
             .create_property(
                 "filter type",
                 "used filter algorithm",
-                EnumProxy::<FilterType> {
-                    value: FilterType::Constant(1.0),
-                }
-                .into(),
+                FilterType::Constant(1.0).into(),
             )
             .unwrap();
         let mut idf = Self { node_attr };
@@ -80,13 +81,9 @@ impl IdealFilter {
             }
         }
         let mut filter = Self::default();
-        filter.node_attr.set_property(
-            "filter type",
-            EnumProxy::<FilterType> {
-                value: filter_type.clone(),
-            }
-            .into(),
-        )?;
+        filter
+            .node_attr
+            .set_property("filter type", filter_type.clone().into())?;
         filter.node_attr.set_name(name);
         Ok(filter)
     }
@@ -99,7 +96,7 @@ impl IdealFilter {
         if let Proptype::FilterType(filter_type) =
             self.node_attr.get_property("filter type").unwrap()
         {
-            filter_type.value.clone()
+            filter_type.clone()
         } else {
             panic!("wrong data type")
         }
@@ -112,13 +109,8 @@ impl IdealFilter {
     /// This function will return an error if a transmission factor > 1.0 is given (This would be an amplifiying filter :-) ).
     pub fn set_transmission(&mut self, transmission: f64) -> OpmResult<()> {
         if (0.0..=1.0).contains(&transmission) {
-            self.node_attr.set_property(
-                "filter type",
-                EnumProxy::<FilterType> {
-                    value: FilterType::Constant(transmission),
-                }
-                .into(),
-            )?;
+            self.node_attr
+                .set_property("filter type", FilterType::Constant(transmission).into())?;
             Ok(())
         } else {
             Err(OpossumError::Other(
@@ -136,10 +128,7 @@ impl IdealFilter {
         if density >= 0.0 {
             self.node_attr.set_property(
                 "filter type",
-                EnumProxy::<FilterType> {
-                    value: FilterType::Constant(f64::powf(10.0, -1.0 * density)),
-                }
-                .into(),
+                FilterType::Constant(f64::powf(10.0, -1.0 * density)).into(),
             )?;
             Ok(())
         } else {
diff --git a/opossum/src/nodes/lens/mod.rs b/opossum/src/nodes/lens/mod.rs
index eda4c73a1957adbe9daa7bdf7e4f8cd6be5fdf2a..becc5f93a9b7a83106bb3020c7e31dde3a39ffb4 100644
--- a/opossum/src/nodes/lens/mod.rs
+++ b/opossum/src/nodes/lens/mod.rs
@@ -11,7 +11,7 @@ use crate::{
     radian,
     refractive_index::{RefrIndexConst, RefractiveIndex, RefractiveIndexType},
     surface::{geo_surface::GeoSurfaceRef, Plane, Sphere},
-    utils::{geom_transformation::Isometry, EnumProxy},
+    utils::geom_transformation::Isometry,
 };
 #[cfg(feature = "bevy")]
 use bevy::{math::primitives::Cuboid, render::mesh::Mesh};
@@ -80,10 +80,7 @@ impl Default for Lens {
             .create_property(
                 "refractive index",
                 "refractive index of the lens material",
-                EnumProxy::<RefractiveIndexType> {
-                    value: RefractiveIndexType::Const(RefrIndexConst::new(1.5).unwrap()),
-                }
-                .into(),
+                RefractiveIndexType::Const(RefrIndexConst::new(1.5).unwrap()).into(),
             )
             .unwrap();
         let mut lens = Self { node_attr };
@@ -133,13 +130,8 @@ impl Lens {
         lens.node_attr
             .set_property("center thickness", center_thickness.into())?;
 
-        lens.node_attr.set_property(
-            "refractive index",
-            EnumProxy::<RefractiveIndexType> {
-                value: refractive_index.to_enum(),
-            }
-            .into(),
-        )?;
+        lens.node_attr
+            .set_property("refractive index", refractive_index.to_enum().into())?;
         lens.update_surfaces()?;
         Ok(lens)
     }
@@ -377,10 +369,7 @@ mod test {
         else {
             panic!()
         };
-        assert_eq!(
-            (*index).value.get_refractive_index(Length::zero()).unwrap(),
-            1.5
-        );
+        assert_eq!((*index).get_refractive_index(Length::zero()).unwrap(), 1.5);
     }
     #[test]
     fn new() {
@@ -416,9 +405,8 @@ mod test {
             panic!()
         };
         assert_eq!(*roc, millimeter!(11.0));
-        let Ok(Proptype::RefractiveIndex(EnumProxy::<RefractiveIndexType> {
-            value: RefractiveIndexType::Const(ref_index_const),
-        })) = node.node_attr.get_property("refractive index")
+        let Ok(Proptype::RefractiveIndex(RefractiveIndexType::Const(ref_index_const))) =
+            node.node_attr.get_property("refractive index")
         else {
             panic!()
         };
diff --git a/opossum/src/nodes/node_group/mod.rs b/opossum/src/nodes/node_group/mod.rs
index 5e8f955c525e08ad40904e22f65ac314f8a0ed0c..eb72e85290e722cc0e1cbafcf0bdb99ce88cc366 100644
--- a/opossum/src/nodes/node_group/mod.rs
+++ b/opossum/src/nodes/node_group/mod.rs
@@ -8,7 +8,9 @@ use crate::{
     analyzers::Analyzable,
     dottable::Dottable,
     error::{OpmResult, OpossumError},
-    lightdata::LightData,
+    lightdata::{
+        light_data_builder::LightDataBuilder, ray_data_builder::RayDataBuilder, LightData,
+    },
     optic_node::OpticNode,
     optic_ports::{OpticPorts, PortType},
     optic_ref::OpticRef,
@@ -16,7 +18,6 @@ use crate::{
     rays::Rays,
     reporting::{analysis_report::AnalysisReport, node_report::NodeReport},
     surface::optic_surface::OpticSurface,
-    utils::EnumProxy,
     SceneryResources,
 };
 use num::Zero;
@@ -178,23 +179,18 @@ impl NodeGroup {
         };
         let node_props = node_props.clone();
         drop(node);
-        if let Proptype::LightData(ld) = node_props {
-            if let Some(LightData::Geometric(rays)) = &ld.value {
-                let mut new_rays = rays.clone();
-                new_rays.set_node_origin_uuid(node_id);
+        if let Proptype::LightData(Some(LightData::Geometric(rays))) = node_props {
+            let mut new_rays = rays;
+            new_rays.set_node_origin_uuid(node_id);
 
-                let mut node_ref = node_ref
-                    .optical_ref
-                    .lock()
-                    .map_err(|_| OpossumError::Other("Mutex lock failed".to_string()))?;
-                node_ref.node_attr_mut().set_property(
-                    "light data",
-                    EnumProxy::<Option<LightData>> {
-                        value: Some(LightData::Geometric(new_rays)),
-                    }
-                    .into(),
-                )?;
-            }
+            let mut node_ref = node_ref
+                .optical_ref
+                .lock()
+                .map_err(|_| OpossumError::Other("Mutex lock failed".to_string()))?;
+            node_ref.node_attr_mut().set_property(
+                "light data",
+                Some(LightDataBuilder::Geometric(RayDataBuilder::Raw(new_rays))).into(),
+            )?;
         }
         Ok(())
     }
@@ -655,7 +651,7 @@ mod test {
         analyzers::{energy::AnalysisEnergy, raytrace::AnalysisRayTrace, RayTraceConfig},
         joule,
         light_result::LightResult,
-        lightdata::LightData,
+        lightdata::{light_data_builder::LightDataBuilder, ray_data_builder::RayDataBuilder},
         millimeter, nanometer,
         nodes::{test_helper::test_helper::*, Dummy, EnergyMeter, Source},
         optic_node::OpticNode,
@@ -737,7 +733,7 @@ mod test {
         let mut scenery = NodeGroup::default();
         scenery.add_node(Dummy::default()).unwrap();
         let report = scenery.toplevel_report().unwrap();
-        assert!(serde_yaml::to_string(&report).is_ok());
+        assert!(ron::ser::to_string_pretty(&report, ron::ser::PrettyConfig::default()).is_ok());
         // How shall we further parse the output?
     }
     #[test]
@@ -771,8 +767,9 @@ mod test {
             Ray::new_collimated(millimeter!(0., 0., 0.), nanometer!(1053.0), joule!(0.1)).unwrap(),
         );
         let mut scenery = NodeGroup::default();
+        let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays));
         let i_s = scenery
-            .add_node(Source::new("src", &LightData::Geometric(rays)))
+            .add_node(Source::new("src", light_data_builder))
             .unwrap();
         let mut em = EnergyMeter::default();
         em.set_isometry(Isometry::identity()).unwrap();
diff --git a/opossum/src/nodes/node_group/optic_graph.rs b/opossum/src/nodes/node_group/optic_graph.rs
index de21a4e00c07f310ec3f249c0be1bbcc36657dc6..772e6e9f475cf65ad114aa3874f63b39cd6fb5cc 100644
--- a/opossum/src/nodes/node_group/optic_graph.rs
+++ b/opossum/src/nodes/node_group/optic_graph.rs
@@ -1647,8 +1647,9 @@ mod test {
             graph.port_map(&PortType::Input).port_names(),
             vec!["input_1", "input_2"]
         );
-        let serialized = serde_yaml::to_string(&graph).unwrap();
-        let deserialized: OpticGraph = serde_yaml::from_str(&serialized).unwrap();
+        let serialized =
+            ron::ser::to_string_pretty(&graph, ron::ser::PrettyConfig::default()).unwrap();
+        let deserialized: OpticGraph = ron::from_str(&serialized).unwrap();
         assert_eq!(
             deserialized.port_map(&PortType::Input).port_names(),
             vec!["input_1", "input_2"]
diff --git a/opossum/src/nodes/source.rs b/opossum/src/nodes/source.rs
index ff3e4637eb78591fea2b8667e6d5e60cb95cc6e0..b8c1fefd5c6fd22254c37b6140a6c739fcb76667 100644
--- a/opossum/src/nodes/source.rs
+++ b/opossum/src/nodes/source.rs
@@ -14,14 +14,13 @@ use crate::{
     error::{OpmResult, OpossumError},
     joule,
     light_result::{LightRays, LightResult},
-    lightdata::LightData,
+    lightdata::{light_data_builder::LightDataBuilder, LightData},
     millimeter,
     optic_node::OpticNode,
     optic_ports::PortType,
     properties::Proptype,
     ray::Ray,
     rays::Rays,
-    utils::EnumProxy,
 };
 use std::fmt::Debug;
 
@@ -54,7 +53,7 @@ impl Default for Source {
             .create_property(
                 "light data",
                 "data of the emitted light",
-                EnumProxy::<Option<LightData>> { value: None }.into(),
+                Option::<LightDataBuilder>::None.into(),
             )
             .unwrap();
 
@@ -83,25 +82,20 @@ impl Source {
     ///
     /// ```rust
     /// use opossum::{
-    /// lightdata::{DataEnergy, LightData},
+    /// lightdata::{light_data_builder::LightDataBuilder, energy_spectrum_builder::EnergyDataBuilder},
     /// nodes::Source,
     /// spectrum_helper::create_he_ne_spec};
     ///
-    /// let source=Source::new("My Source", &LightData::Energy(DataEnergy {spectrum: create_he_ne_spec(1.0).unwrap()}));
+    /// let light_data_builder = LightDataBuilder::Energy(EnergyDataBuilder::Raw(create_he_ne_spec(1.0).unwrap()));
+    /// let source=Source::new("My Source", light_data_builder);
     /// ```
     #[must_use]
-    pub fn new(name: &str, light: &LightData) -> Self {
+    pub fn new(name: &str, light_data_builder: LightDataBuilder) -> Self {
         let mut source = Self::default();
         source.node_attr.set_name(name);
         source
             .node_attr
-            .set_property(
-                "light data",
-                EnumProxy::<Option<LightData>> {
-                    value: Some(light.clone()),
-                }
-                .into(),
-            )
+            .set_property("light data", Some(light_data_builder).into())
             .unwrap();
         source.update_surfaces().unwrap();
         source
@@ -122,29 +116,24 @@ impl Source {
             .set_property("alignment wavelength", Proptype::LengthOption(Some(wvl)))
     }
 
-    /// Sets the light data of this [`Source`]. The [`LightData`] provided here represents the input data of an `OpticScenery`.
+    /// Sets the light data builder of this [`Source`]. The [`LightData`] provided here represents the input data of an `OpticScenery`.
     ///
     /// # Attributes
-    /// * `light_data`: [`LightData`] that shall be set
+    /// * `light_data_builder`: [`LightDataBuilder`] that shall be set
     ///
     /// # Errors
     /// This function returns an error if the property "light data" can not be set
-    pub fn set_light_data(&mut self, light_data: &LightData) -> OpmResult<()> {
-        self.node_attr.set_property(
-            "light data",
-            EnumProxy::<Option<LightData>> {
-                value: Some(light_data.clone()),
-            }
-            .into(),
-        )?;
+    pub fn set_light_data(&mut self, light_data_builder: LightDataBuilder) -> OpmResult<()> {
+        self.node_attr
+            .set_property("light data", Some(light_data_builder).into())?;
         Ok(())
     }
 }
 impl Debug for Source {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         let light_prop = self.node_attr.get_property("light data").unwrap();
-        let data = if let Proptype::LightData(data) = &light_prop {
-            &data.value
+        let data = if let Proptype::LightDataBuilder(data) = &light_prop {
+            data
         } else {
             &None
         };
@@ -170,10 +159,14 @@ impl OpticNode for Source {
 }
 impl AnalysisEnergy for Source {
     fn analyze(&mut self, _incoming_data: LightResult) -> OpmResult<LightResult> {
-        if let Ok(Proptype::LightData(data)) = self.node_attr.get_property("light data") {
-            let Some(data) = data.value.clone() else {
+        if let Ok(Proptype::LightDataBuilder(light_data_builder)) =
+            self.node_attr.get_property("light data")
+        {
+            let data = if let Some(light_data_builder) = light_data_builder.clone() {
+                light_data_builder.build()?
+            } else {
                 return Err(OpossumError::Analysis(
-                    "source has empty light data defined".into(),
+                    "source has empty light data builder".into(),
                 ));
             };
             Ok(LightResult::from([("output_1".into(), data)]))
@@ -190,10 +183,14 @@ impl AnalysisRayTrace for Source {
         _incoming_edges: LightResult,
         config: &RayTraceConfig,
     ) -> OpmResult<LightResult> {
-        if let Ok(Proptype::LightData(data)) = self.node_attr.get_property("light data") {
-            let Some(mut data) = data.value.clone() else {
+        if let Ok(Proptype::LightDataBuilder(light_data_builder)) =
+            self.node_attr.get_property("light data")
+        {
+            let mut data = if let Some(lightdata_builder) = light_data_builder.clone() {
+                lightdata_builder.build()?
+            } else {
                 return Err(OpossumError::Analysis(
-                    "source has empty light data defined".into(),
+                    "source has empty light data builder".into(),
                 ));
             };
             if let LightData::Geometric(rays) = &mut data {
@@ -269,10 +266,14 @@ impl AnalysisGhostFocus for Source {
             };
             bouncing_rays.clone()
         } else if bounce_lvl == 0 {
-            if let Ok(Proptype::LightData(data)) = self.node_attr.get_property("light data") {
-                let Some(mut data) = data.value.clone() else {
+            if let Ok(Proptype::LightDataBuilder(light_data_builder)) =
+                self.node_attr.get_property("light data")
+            {
+                let mut data = if let Some(lightdata_builder) = light_data_builder.clone() {
+                    lightdata_builder.build()?
+                } else {
                     return Err(OpossumError::Analysis(
-                        "source has empty light data defined".into(),
+                        "source has empty light data builder".into(),
                     ));
                 };
                 if let LightData::Geometric(rays) = &mut data {
@@ -317,8 +318,12 @@ impl AnalysisGhostFocus for Source {
 mod test {
     use super::*;
     use crate::{
-        lightdata::DataEnergy, nanometer, optic_ports::PortType, position_distributions::Hexapolar,
-        spectrum_helper::create_he_ne_spec, utils::geom_transformation::Isometry,
+        lightdata::{energy_spectrum_builder::EnergyDataBuilder, ray_data_builder::RayDataBuilder},
+        nanometer,
+        optic_ports::PortType,
+        position_distributions::Hexapolar,
+        spectrum_helper::create_he_ne_spec,
+        utils::geom_transformation::Isometry,
     };
     use assert_matches::assert_matches;
     use core::f64;
@@ -328,8 +333,8 @@ mod test {
         let mut node = Source::default();
         assert_eq!(node.name(), "source");
         assert_eq!(node.node_type(), "source");
-        if let Ok(Proptype::LightData(light_data)) = node.properties().get("light data") {
-            assert_eq!(light_data.value, None);
+        if let Ok(Proptype::LightDataBuilder(light_data)) = node.properties().get("light data") {
+            assert!(light_data.is_none());
         } else {
             panic!("cannot unpack light data property");
         };
@@ -339,7 +344,7 @@ mod test {
     }
     #[test]
     fn new() {
-        let source = Source::new("test", &LightData::Fourier);
+        let source = Source::new("test", LightDataBuilder::Fourier);
         assert_eq!(source.name(), "test");
     }
     #[test]
@@ -392,12 +397,14 @@ mod test {
     #[test]
     fn test_set_light_data() {
         let mut src = Source::default();
-        if let Ok(Proptype::LightData(light_data)) = src.properties().get("light data") {
-            assert_eq!(light_data.value, None);
+        if let Proptype::LightDataBuilder(light_data) = src.properties().get("light data").unwrap()
+        {
+            assert!(light_data.is_none());
         }
-        src.set_light_data(&LightData::Fourier).unwrap();
-        if let Ok(Proptype::LightData(light_data)) = src.properties().get("light data") {
-            assert_matches!(light_data.value.clone().unwrap(), LightData::Fourier);
+        src.set_light_data(LightDataBuilder::Fourier).unwrap();
+        if let Proptype::LightDataBuilder(light_data) = src.properties().get("light data").unwrap()
+        {
+            assert_matches!(light_data.clone().unwrap(), LightDataBuilder::Fourier);
         }
     }
     #[test]
@@ -408,17 +415,16 @@ mod test {
     }
     #[test]
     fn analyze_energy_ok() {
-        let light = LightData::Energy(DataEnergy {
-            spectrum: create_he_ne_spec(1.0).unwrap(),
-        });
-        let mut node = Source::new("test", &light);
+        let light_builder =
+            LightDataBuilder::Energy(EnergyDataBuilder::Raw(create_he_ne_spec(1.0).unwrap()));
+        let mut node = Source::new("test", light_builder.clone());
         let output = AnalysisEnergy::analyze(&mut node, LightResult::default()).unwrap();
         assert!(output.contains_key("output_1"));
         assert_eq!(output.len(), 1);
         let output = output.get("output_1");
         assert!(output.is_some());
         let output = output.clone().unwrap();
-        assert_eq!(*output, light);
+        assert_eq!(*output, light_builder.build().unwrap());
     }
     #[test]
     fn analyze_raytrace_no_light_defined() {
@@ -431,7 +437,7 @@ mod test {
         );
         assert_eq!(
             output.unwrap_err(),
-            OpossumError::Analysis("source has empty light data defined".into())
+            OpossumError::Analysis("source has empty light data builder".into())
         );
     }
     #[test]
@@ -444,7 +450,8 @@ mod test {
             &Hexapolar::new(millimeter!(1.0), 1).unwrap(),
         )
         .unwrap();
-        node.set_light_data(&LightData::Geometric(rays)).unwrap();
+        let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays));
+        node.set_light_data(light_data_builder).unwrap();
         let output = AnalysisRayTrace::analyze(
             &mut node,
             LightResult::default(),
@@ -457,8 +464,8 @@ mod test {
         let mut node = Source::default();
         node.set_isometry(Isometry::identity()).unwrap();
         node.set_alignment_wavelength(nanometer!(630.0)).unwrap();
-        node.set_light_data(&LightData::Geometric(Rays::default()))
-            .unwrap();
+        let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(Rays::default()));
+        node.set_light_data(light_data_builder).unwrap();
         let output = AnalysisRayTrace::calc_node_positions(
             &mut node,
             LightResult::default(),
@@ -487,7 +494,7 @@ mod test {
         );
         assert_eq!(
             output.unwrap_err(),
-            OpossumError::Analysis("source has empty light data defined".into())
+            OpossumError::Analysis("source has empty light data builder".into())
         );
     }
     #[test]
@@ -500,8 +507,8 @@ mod test {
             &Hexapolar::new(millimeter!(1.0), 1).unwrap(),
         )
         .unwrap();
-        node.set_light_data(&LightData::Geometric(rays.clone()))
-            .unwrap();
+        let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays.clone()));
+        node.set_light_data(light_data_builder).unwrap();
         let mut light_rays = LightRays::new();
         light_rays.insert("input_1".into(), vec![rays]);
         let output = AnalysisGhostFocus::analyze(
@@ -517,8 +524,8 @@ mod test {
     fn debug() {
         assert_eq!(format!("{:?}", Source::default()), "Source: no data");
         assert_eq!(
-            format!("{:?}", Source::new("hallo", &LightData::Fourier)),
-            "Source: No display defined for this type of LightData"
+            format!("{:?}", Source::new("hallo", LightDataBuilder::Fourier)),
+            "Source: Fourier"
         );
     }
 }
diff --git a/opossum/src/nodes/source_helper.rs b/opossum/src/nodes/source_helper.rs
index 5ee5db38b015864804ad70560d8b506aefe3d9f1..72a5875ef07a8f99015bf09020a9a1f84d346f39 100644
--- a/opossum/src/nodes/source_helper.rs
+++ b/opossum/src/nodes/source_helper.rs
@@ -3,7 +3,7 @@
 use super::Source;
 use crate::{
     error::OpmResult,
-    lightdata::LightData,
+    lightdata::{light_data_builder::LightDataBuilder, ray_data_builder::RayDataBuilder},
     nanometer,
     optic_node::OpticNode,
     position_distributions::{Grid, Hexapolar},
@@ -34,8 +34,8 @@ pub fn round_collimated_ray_source(
         energy,
         &Hexapolar::new(radius, nr_of_rings)?,
     )?;
-    let light = LightData::Geometric(rays);
-    let mut src = Source::new("collimated ray source", &light);
+    let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays));
+    let mut src = Source::new("collimated line ray source", light_data_builder);
     src.set_isometry(Isometry::identity())?;
     Ok(src)
 }
@@ -60,8 +60,8 @@ pub fn collimated_line_ray_source(
         energy,
         &Grid::new((Length::zero(), size_y), (1, nr_of_points_y))?,
     )?;
-    let light = LightData::Geometric(rays);
-    let mut src = Source::new("collimated line ray source", &light);
+    let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays));
+    let mut src = Source::new("collimated line ray source", light_data_builder);
     src.set_isometry(Isometry::identity())?;
     Ok(src)
 }
@@ -84,15 +84,18 @@ pub fn point_ray_source(cone_angle: Angle, energy: Energy) -> OpmResult<Source>
         nanometer!(1000.0),
         energy,
     )?;
-    let light = LightData::Geometric(rays);
-    let mut src = Source::new("point ray source", &light);
+    let light_data_builder = LightDataBuilder::Geometric(RayDataBuilder::Raw(rays));
+    let mut src = Source::new("point ray source", light_data_builder);
     src.set_isometry(Isometry::identity())?;
     Ok(src)
 }
 #[cfg(test)]
 mod test {
     use super::*;
-    use crate::{degree, joule, millimeter, optic_node::OpticNode, properties::Proptype, ray::Ray};
+    use crate::{
+        degree, joule, lightdata::LightData, millimeter, optic_node::OpticNode,
+        properties::Proptype, ray::Ray,
+    };
     use approx::assert_abs_diff_eq;
     use uom::si::energy::joule;
     #[test]
@@ -102,8 +105,11 @@ mod test {
         assert!(round_collimated_ray_source(millimeter!(1.0), joule!(f64::INFINITY), 3).is_err());
         assert!(round_collimated_ray_source(millimeter!(-0.1), joule!(1.0), 3).is_err());
         let src = round_collimated_ray_source(Length::zero(), joule!(1.0), 3).unwrap();
-        if let Proptype::LightData(light_data) = src.properties().get("light data").unwrap() {
-            if let Some(LightData::Geometric(rays)) = &light_data.value {
+        if let Proptype::LightDataBuilder(light_data_builder) =
+            src.properties().get("light data").unwrap()
+        {
+            let data = light_data_builder.clone().unwrap().build().unwrap();
+            if let LightData::Geometric(rays) = data {
                 assert_eq!(rays.nr_of_rays(true), 1);
                 assert_abs_diff_eq!(
                     rays.total_energy().get::<joule>(),
@@ -117,8 +123,11 @@ mod test {
             panic!("property light data has wrong type");
         }
         let src = round_collimated_ray_source(millimeter!(1.0), joule!(1.0), 3).unwrap();
-        if let Proptype::LightData(data) = src.properties().get("light data").unwrap() {
-            if let Some(LightData::Geometric(rays)) = &data.value {
+        if let Proptype::LightDataBuilder(light_data_builder) =
+            src.properties().get("light data").unwrap()
+        {
+            let data = light_data_builder.clone().unwrap().build().unwrap();
+            if let LightData::Geometric(rays) = data {
                 assert_abs_diff_eq!(
                     rays.total_energy().get::<joule>(),
                     1.0,
@@ -138,8 +147,11 @@ mod test {
         assert!(point_ray_source(degree!(180.0), Energy::zero()).is_err());
         assert!(point_ray_source(degree!(190.0), Energy::zero()).is_err());
         let src = point_ray_source(Angle::zero(), joule!(1.0)).unwrap();
-        if let Proptype::LightData(data) = src.properties().get("light data").unwrap() {
-            if let Some(LightData::Geometric(rays)) = &data.value {
+        if let Proptype::LightDataBuilder(light_data_builder) =
+            src.properties().get("light data").unwrap()
+        {
+            let data = light_data_builder.clone().unwrap().build().unwrap();
+            if let LightData::Geometric(rays) = &data {
                 assert_abs_diff_eq!(
                     rays.total_energy().get::<joule>(),
                     1.0,
@@ -154,7 +166,7 @@ mod test {
         }
         let src = point_ray_source(degree!(1.0), joule!(1.0)).unwrap();
         if let Proptype::LightData(data) = src.properties().get("light data").unwrap() {
-            if let Some(LightData::Geometric(rays)) = &data.value {
+            if let Some(LightData::Geometric(rays)) = &data {
                 assert_abs_diff_eq!(
                     rays.total_energy().get::<joule>(),
                     1.0,
@@ -180,7 +192,7 @@ mod test {
 
         let s = collimated_line_ray_source(millimeter!(1.0), joule!(1.0), 2).unwrap();
         if let Proptype::LightData(data) = s.properties().get("light data").unwrap() {
-            if let Some(LightData::Geometric(rays)) = &data.value {
+            if let Some(LightData::Geometric(rays)) = &data {
                 assert_abs_diff_eq!(
                     rays.total_energy().get::<joule>(),
                     1.0,
diff --git a/opossum/src/nodes/wavefront.rs b/opossum/src/nodes/wavefront.rs
index 651344f1c7e68650426e9a5f07c32d268327c3db..40708e0b6e25529b6f3ad1732d37358e3fa52c53 100644
--- a/opossum/src/nodes/wavefront.rs
+++ b/opossum/src/nodes/wavefront.rs
@@ -380,10 +380,12 @@ mod test_wavefront_error_map {
 #[cfg(test)]
 mod test {
     use super::*;
+
+    use crate::lightdata::DataEnergy;
     use crate::optic_ports::PortType;
     use crate::utils::geom_transformation::Isometry;
     use crate::{
-        analyzers::RayTraceConfig, joule, lightdata::DataEnergy, millimeter, nanometer,
+        analyzers::RayTraceConfig, joule, millimeter, nanometer,
         nodes::test_helper::test_helper::*, position_distributions::Hexapolar, rays::Rays,
         spectrum_helper::create_he_ne_spec,
     };
diff --git a/opossum/src/nodes/wedge/mod.rs b/opossum/src/nodes/wedge/mod.rs
index 76ce3e417d622578dfb520957061faf5b6f6463a..83a460e497f4e83ec3c05a58d847c358a2a322c1 100644
--- a/opossum/src/nodes/wedge/mod.rs
+++ b/opossum/src/nodes/wedge/mod.rs
@@ -9,7 +9,7 @@ use crate::{
     properties::Proptype,
     refractive_index::{RefrIndexConst, RefractiveIndex, RefractiveIndexType},
     surface::{geo_surface::GeoSurfaceRef, Plane},
-    utils::{geom_transformation::Isometry, EnumProxy},
+    utils::geom_transformation::Isometry,
 };
 use nalgebra::Point3;
 use num::Zero;
@@ -60,10 +60,7 @@ impl Default for Wedge {
             .create_property(
                 "refractive index",
                 "refractive index of the lens material",
-                EnumProxy::<RefractiveIndexType> {
-                    value: RefractiveIndexType::Const(RefrIndexConst::new(1.5).unwrap()),
-                }
-                .into(),
+                RefractiveIndexType::Const(RefrIndexConst::new(1.5).unwrap()).into(),
             )
             .unwrap();
         node_attr
@@ -100,13 +97,9 @@ impl Wedge {
             .node_attr
             .set_property("center thickness", center_thickness.into())?;
 
-        wedge.node_attr.set_property(
-            "refractive index",
-            EnumProxy::<RefractiveIndexType> {
-                value: refractive_index.to_enum(),
-            }
-            .into(),
-        )?;
+        wedge
+            .node_attr
+            .set_property("refractive index", refractive_index.to_enum().into())?;
         if !wedge_angle.is_finite() || wedge_angle.get::<degree>().abs() > 90.0 {
             return Err(crate::error::OpossumError::Other(
                 "wedge angle must be within the interval ]-90 deg; 90 deg[ and finite".into(),
@@ -209,7 +202,7 @@ mod test {
             assert!(false, "could not read angle.");
         }
         if let Ok(Proptype::RefractiveIndex(p)) = node.properties().get("refractive index") {
-            if let RefractiveIndexType::Const(val) = &p.value {
+            if let RefractiveIndexType::Const(val) = &p {
                 let idx = val.get_refractive_index(nanometer!(1000.0)).unwrap();
                 assert_eq!(idx, 1.5);
             } else {
@@ -318,7 +311,7 @@ mod test {
             assert!(false, "could not read angle.");
         }
         if let Ok(Proptype::RefractiveIndex(p)) = n.properties().get("refractive index") {
-            if let RefractiveIndexType::Const(val) = &p.value {
+            if let RefractiveIndexType::Const(val) = &p {
                 let idx = val.get_refractive_index(nanometer!(1000.0)).unwrap();
                 assert_eq!(idx, 1.0);
             } else {
diff --git a/opossum/src/opm_document.rs b/opossum/src/opm_document.rs
index 5a54846b9927830312daae5cceaacdd92b1d3d8b..3eb361ee2e080c51e831ca88ee77335bc8b54ea9 100644
--- a/opossum/src/opm_document.rs
+++ b/opossum/src/opm_document.rs
@@ -29,7 +29,6 @@ use std::{
 /// The main structure of an OPOSSUM model.
 /// It contains the [`NodeGroup`] representing the optical model, a list of analyzers and a global configuration.
 pub struct OpmDocument {
-    #[serde(rename = "opm file version")]
     opm_file_version: String,
     #[serde(default)]
     scenery: NodeGroup,
@@ -69,23 +68,7 @@ impl OpmDocument {
         let contents = fs::read_to_string(path).map_err(|e| {
             OpossumError::OpmDocument(format!("cannot read file {} : {}", path.display(), e))
         })?;
-        let mut document: Self = serde_yaml::from_str(&contents)
-            .map_err(|e| OpossumError::OpmDocument(format!("parsing of model failed: {e}")))?;
-        if document.opm_file_version != env!("OPM_FILE_VERSION") {
-            warn!("OPM file version does not match the used OPOSSUM version.");
-            warn!(
-                "read version '{}' <-> program file version '{}'",
-                document.opm_file_version,
-                env!("OPM_FILE_VERSION")
-            );
-            warn!("This file might haven been written by an older or newer version of OPOSSUM. The model import might not be correct.");
-        }
-        document.scenery.after_deserialization_hook()?;
-        document
-            .scenery
-            .graph_mut()
-            .update_global_config(&Some(document.global_conf.clone()));
-        Ok(document)
+        Self::from_string(&contents)
     }
     /// Create a new [`OpmDocument`] from the given `.opm` file string.
     ///
@@ -93,7 +76,7 @@ impl OpmDocument {
     ///
     /// This function will return an error if the parsing of the `.opm` file failed.
     pub fn from_string(file_string: &str) -> OpmResult<Self> {
-        let mut document: Self = serde_yaml::from_str(file_string)
+        let mut document: Self = ron::from_str(file_string)
             .map_err(|e| OpossumError::OpmDocument(format!("parsing of model failed: {e}")))?;
         if document.opm_file_version != env!("OPM_FILE_VERSION") {
             warn!("OPM file version does not match the used OPOSSUM version.");
@@ -143,7 +126,7 @@ impl OpmDocument {
     ///
     /// This function will return an error if the serialization of the internal structures fail.
     pub fn to_opm_file_string(&self) -> OpmResult<String> {
-        serde_yaml::to_string(&self).map_err(|e| {
+        ron::ser::to_string_pretty(&self, ron::ser::PrettyConfig::default()).map_err(|e| {
             OpossumError::OpticScenery(format!("serialization of OpmDocument failed: {e}"))
         })
     }
@@ -273,7 +256,7 @@ mod test {
             OpmDocument::from_file(&Path::new("./files_for_testing/opm/incorrect_opm.opm"));
         assert_eq!(
             result.unwrap_err().to_string(),
-            "OpmDocument:parsing of model failed: missing field `opm file version`"
+            "OpmDocument:parsing of model failed: 1:2: Unexpected missing field named `opm_file_version` in `OpmDocument`"
         );
         assert!(
             OpmDocument::from_file(&PathBuf::from("./files_for_testing/opm/opticscenery.opm"))
diff --git a/opossum/src/optic_ref.rs b/opossum/src/optic_ref.rs
index b8d9dc1d1f56be6ac3fc4fe2f284174f26d31fdd..70d89d45de6b798289ab588089a671c5bc94ab7f 100644
--- a/opossum/src/optic_ref.rs
+++ b/opossum/src/optic_ref.rs
@@ -224,8 +224,7 @@ mod test {
     #[test]
     fn serialize() {
         let optic_ref = OpticRef::new(Arc::new(Mutex::new(Dummy::default())), None);
-        let serialized = serde_yaml::to_string(&optic_ref);
-        assert!(serialized.is_ok());
+        let _ = ron::ser::to_string_pretty(&optic_ref, ron::ser::PrettyConfig::default()).unwrap();
     }
     #[test]
     fn deserialize() {
@@ -233,12 +232,10 @@ mod test {
         path.push("files_for_testing/opm/optic_ref.opm");
         let file_content = &mut "".to_owned();
         let _ = File::open(path).unwrap().read_to_string(file_content);
-        let deserialized: Result<OpticRef, serde_yaml::Error> = serde_yaml::from_str(&file_content);
-        assert!(deserialized.is_ok());
-        let optic_ref = deserialized.unwrap();
+        let optic_ref: OpticRef = ron::from_str(&file_content).unwrap();
         assert_eq!(
             optic_ref.uuid(),
-            uuid!("587ee70f-6f52-4420-89f6-e1618ff4dbdb")
+            uuid!("98248e7f-dc4c-4131-8710-f3d5be2ff087")
         );
         let optic_ref = optic_ref.optical_ref.lock().expect("Mutex lock failed");
         assert_eq!(optic_ref.node_type(), "dummy");
diff --git a/opossum/src/properties/proptype.rs b/opossum/src/properties/proptype.rs
index 848c60d5b8a0f657ca385501d58276972d23ed39..a9f4386c471e93b5c52c145c2922c12a8fdcd345 100644
--- a/opossum/src/properties/proptype.rs
+++ b/opossum/src/properties/proptype.rs
@@ -4,7 +4,7 @@ use crate::{
     analyzers::ghostfocus::GhostFocusHistory,
     aperture::Aperture,
     error::{OpmResult, OpossumError},
-    lightdata::LightData,
+    lightdata::{light_data_builder::LightDataBuilder, LightData},
     nodes::{
         fluence_detector::{fluence_data::FluenceData, Fluence},
         ray_propagation_visualizer::RayPositionHistories,
@@ -19,7 +19,6 @@ use crate::{
     utils::{
         geom_transformation::Isometry,
         unit_format::{get_exponent_for_base_unit_in_e3_steps, get_prefix_for_base_unit},
-        EnumProxy,
     },
 };
 use nalgebra::{Vector2, Vector3};
@@ -52,11 +51,11 @@ pub enum Proptype {
     /// A boolean property
     Bool(bool),
     /// An optional [`LightData`] property
-    LightData(EnumProxy<Option<LightData>>),
+    LightData(Option<LightData>),
     /// Property for storing a [`FilterType`] of an [`IdealFilter`](crate::nodes::IdealFilter) node.
-    FilterType(EnumProxy<FilterType>),
+    FilterType(FilterType),
     /// Property for storing a [`SplittingConfig`] of an [`BeamSplitter`](crate::nodes::BeamSplitter) node.
-    SplitterType(EnumProxy<SplittingConfig>),
+    SplitterType(SplittingConfig),
     /// Property for storing a [`SpectrometerType`] of a [`Sepctrometer`](crate::nodes::Spectrometer) node.
     SpectrometerType(SpectrometerType),
     /// Property for storing a [`Metertype`] of an [`Energymeter`](crate::nodes::EnergyMeter) node.
@@ -97,9 +96,9 @@ pub enum Proptype {
     /// a (2D) geometric angle (e.g. component tilt)
     Angle(Angle),
     /// an optical refractive index model
-    RefractiveIndex(EnumProxy<RefractiveIndexType>),
+    RefractiveIndex(RefractiveIndexType),
     /// a (node) location / orientation
-    Isometry(EnumProxy<Option<Isometry>>),
+    Isometry(Option<Isometry>),
     /// Three dimensional Vector
     Vec3(Vector3<f64>),
     /// an optional length parameter. used, e.g., for the alignment wavelength of the source
@@ -108,6 +107,8 @@ pub enum Proptype {
     HitMap(HitMap),
     /// 2-dimenstional vector
     Vec2(Vector2<f64>),
+    /// [`LightData`] build configuration
+    LightDataBuilder(Option<LightDataBuilder>),
 }
 impl Proptype {
     /// Generate a html representation of a Proptype.
diff --git a/opossum/src/ray.rs b/opossum/src/ray.rs
index db7ea3aba5eb7963134e51f3f8b9e1ce62b5a75f..9edae49d996f40835d45610d0880b64ae21851df 100644
--- a/opossum/src/ray.rs
+++ b/opossum/src/ray.rs
@@ -19,6 +19,7 @@ use crate::{
     error::{OpmResult, OpossumError},
     joule, meter,
     nodes::{fluence_detector::Fluence, FilterType},
+    properties::Proptype,
     rays::{FluenceRays, Rays},
     spectrum::Spectrum,
     surface::{
@@ -48,7 +49,11 @@ impl SplittingConfig {
         }
     }
 }
-
+impl From<SplittingConfig> for Proptype {
+    fn from(config: SplittingConfig) -> Self {
+        Self::SplitterType(config)
+    }
+}
 ///Struct that contains all information about an optical ray
 #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
 pub struct Ray {
@@ -1451,7 +1456,7 @@ mod test {
         let mut ray = Ray::new_collimated(position, nanometer!(502.0), e_1j).unwrap();
         let mut spec_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
         spec_path.push("files_for_testing/spectrum/test_filter.csv");
-        let s = Spectrum::from_csv(spec_path.to_str().unwrap()).unwrap();
+        let s = Spectrum::from_csv(&spec_path).unwrap();
         let filter = FilterType::Spectrum(s);
         let _ = ray.filter_energy(&filter).unwrap();
         assert_eq!(ray.e, e_1j);
diff --git a/opossum/src/refractive_index/mod.rs b/opossum/src/refractive_index/mod.rs
index 07be32c8edd3cf9ceceba62c278fd86c515f978b..326934a4e4aba7f769f99a5cb3ee28278c08c663 100644
--- a/opossum/src/refractive_index/mod.rs
+++ b/opossum/src/refractive_index/mod.rs
@@ -15,6 +15,7 @@ pub use refr_index_const::RefrIndexConst;
 pub use refr_index_sellmeier1::RefrIndexSellmeier1;
 
 use crate::error::{OpmResult, OpossumError};
+use crate::properties::Proptype;
 
 /// Available models for the calculation of refractive index
 #[derive(Clone, Serialize, Deserialize, Debug)]
@@ -59,6 +60,11 @@ impl RefractiveIndexType {
     }
 }
 
+impl From<RefractiveIndexType> for Proptype {
+    fn from(refr: RefractiveIndexType) -> Self {
+        Self::RefractiveIndex(refr)
+    }
+}
 /// All refractive index models must implement this trait.
 pub trait RefractiveIndex {
     /// Get the refractive index value of the current model for the given wavelength.
diff --git a/opossum/src/spectrum.rs b/opossum/src/spectrum.rs
index e0e9268934b1efb399513ddc95e5a8494645a219..bf0fb58abc7c5e669f615d20b0c1a3ec57ace117 100644
--- a/opossum/src/spectrum.rs
+++ b/opossum/src/spectrum.rs
@@ -17,10 +17,14 @@ use std::{
     fmt::{Debug, Display},
     fs::File,
     ops::Range,
+    path::Path,
 };
-use uom::fmt::DisplayStyle::Abbreviation;
 use uom::num_traits::Zero;
 use uom::si::{f64::Length, length::micrometer, length::nanometer};
+use uom::{
+    fmt::DisplayStyle::Abbreviation,
+    si::{energy::joule, f64::Energy},
+};
 
 /// Structure for handling spectral data.
 ///
@@ -81,7 +85,7 @@ impl Spectrum {
     ///   - the file path is not found or could not be read.
     ///   - the file is empty.
     ///   - the file could not be parsed.
-    pub fn from_csv(path: &str) -> OpmResult<Self> {
+    pub fn from_csv(path: &Path) -> OpmResult<Self> {
         let file = File::open(path).map_err(|e| OpossumError::Spectrum(e.to_string()))?;
         let mut reader = ReaderBuilder::new()
             .has_headers(false)
@@ -109,6 +113,38 @@ impl Spectrum {
         }
         Ok(Self { data: datas })
     }
+    /// Generate a spectrum from a list of narrow laser lines (center wavelength, Energy) and a spectrum resolution.
+    ///
+    /// # Errors
+    ///
+    /// This function will return an error if
+    /// - the resolution is not positive
+    /// - the wavelength is negative
+    /// - the energy is negative
+    /// - the list of lines is empty
+    pub fn from_laser_lines(lines: Vec<(Length, Energy)>, resolution: Length) -> OpmResult<Self> {
+        if lines.is_empty() {
+            return Err(OpossumError::Spectrum("no laser lines provided".into()));
+        }
+        if resolution <= Length::zero() {
+            return Err(OpossumError::Spectrum("resolution must be positive".into()));
+        }
+        let mut min_lambda = lines[0].0;
+        let mut max_lambda = lines[0].0;
+        for line in &lines {
+            if line.0 < min_lambda {
+                min_lambda = line.0;
+            }
+            if line.0 > max_lambda {
+                max_lambda = line.0;
+            }
+        }
+        let mut s = Self::new(min_lambda..max_lambda, resolution)?;
+        for line in lines {
+            s.add_single_peak(line.0, line.1.get::<joule>())?;
+        }
+        Ok(s)
+    }
     fn lambda_vec(&self) -> Vec<f64> {
         self.data.iter().map(|data| data.0).collect()
     }
@@ -657,7 +693,9 @@ mod test {
     }
     #[test]
     fn from_csv_ok() {
-        let s = Spectrum::from_csv("files_for_testing/spectrum/spec_to_csv_test_01.csv");
+        let s = Spectrum::from_csv(Path::new(
+            "files_for_testing/spectrum/spec_to_csv_test_01.csv",
+        ));
         assert!(s.is_ok());
         let s = s.unwrap();
         let lambdas = s.lambda_vec();
@@ -673,10 +711,19 @@ mod test {
     }
     #[test]
     fn from_csv_err() {
-        assert!(Spectrum::from_csv("wrong_path.csv").is_err());
-        assert!(Spectrum::from_csv("files_for_testing/spectrum/spec_to_csv_test_02.csv").is_err());
-        assert!(Spectrum::from_csv("files_for_testing/spectrum/spec_to_csv_test_03.csv").is_err());
-        assert!(Spectrum::from_csv("files_for_testing/spectrum/spec_to_csv_test_04.csv").is_err());
+        assert!(Spectrum::from_csv(Path::new("wrong_path.csv")).is_err());
+        assert!(Spectrum::from_csv(Path::new(
+            "files_for_testing/spectrum/spec_to_csv_test_02.csv"
+        ))
+        .is_err());
+        assert!(Spectrum::from_csv(Path::new(
+            "files_for_testing/spectrum/spec_to_csv_test_03.csv"
+        ))
+        .is_err());
+        assert!(Spectrum::from_csv(Path::new(
+            "files_for_testing/spectrum/spec_to_csv_test_04.csv"
+        ))
+        .is_err());
     }
     #[test]
     fn range() {
@@ -919,17 +966,18 @@ mod test {
     #[test]
     fn serialize() {
         let s = prep();
-        let s_yaml = serde_yaml::to_string(&s);
+        let s_yaml = ron::ser::to_string_pretty(&s, ron::ser::PrettyConfig::default());
         assert!(s_yaml.is_ok());
         assert_eq!(s_yaml.unwrap(),
-        "data:\n- - 1.0\n  - 0.0\n- - 1.5\n  - 0.0\n- - 2.0\n  - 0.0\n- - 2.5\n  - 0.0\n- - 3.0\n  - 0.0\n- - 3.5\n  - 0.0\n".to_string());
+        "(\r\n    data: [\r\n        (1.0, 0.0),\r\n        (1.5, 0.0),\r\n        (2.0, 0.0),\r\n        (2.5, 0.0),\r\n        (3.0, 0.0),\r\n        (3.5, 0.0),\r\n    ],\r\n)".to_string());
     }
     #[test]
     fn deserialize() {
-        let s: std::result::Result<Spectrum, serde_yaml::Error>=serde_yaml::from_str("data:\n- - 1.0\n  - 0.1\n- - 1.5\n  - 0.2\n- - 2.0\n  - 0.3\n- - 2.5\n  - 0.4\n- - 3.0\n  - 0.5\n- - 3.5\n  - 0.6\n");
-        assert!(s.is_ok());
+        let s: Spectrum =
+            ron::from_str("(data:[(1.0, 0.1),(1.5,0.2),(2.0,0.3),(2.5,0.4),(3.0,0.5),(3.5,0.6)])")
+                .unwrap();
         assert_eq!(
-            s.unwrap().data,
+            s.data,
             vec![
                 (1.0, 0.1),
                 (1.5, 0.2),
diff --git a/opossum/src/utils/enum_proxy.rs b/opossum/src/utils/enum_proxy.rs
deleted file mode 100644
index 598fd354b2ea4b1e7617561e88a414e2259744d0..0000000000000000000000000000000000000000
--- a/opossum/src/utils/enum_proxy.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-//! Helper module for circumventing issues during serialization / deserialization of enum values.
-
-use crate::{
-    lightdata::LightData, nodes::FilterType, properties::Proptype, ray::SplittingConfig,
-    refractive_index::RefractiveIndexType,
-};
-use serde::{Deserialize, Serialize};
-
-#[derive(Serialize, Deserialize, Debug, Clone)]
-pub struct EnumProxy<T> {
-    pub value: T,
-}
-
-impl From<EnumProxy<SplittingConfig>> for Proptype {
-    fn from(value: EnumProxy<SplittingConfig>) -> Self {
-        Self::SplitterType(value)
-    }
-}
-
-impl From<EnumProxy<FilterType>> for Proptype {
-    fn from(value: EnumProxy<FilterType>) -> Self {
-        Self::FilterType(value)
-    }
-}
-
-impl From<EnumProxy<RefractiveIndexType>> for Proptype {
-    fn from(value: EnumProxy<RefractiveIndexType>) -> Self {
-        Self::RefractiveIndex(value)
-    }
-}
-
-impl From<EnumProxy<Option<LightData>>> for Proptype {
-    fn from(value: EnumProxy<Option<LightData>>) -> Self {
-        Self::LightData(value)
-    }
-}
diff --git a/opossum/src/utils/geom_transformation.rs b/opossum/src/utils/geom_transformation.rs
index f3936a6e511cd31980a9eb4bdab175018c983de3..3399fd09bed7f08c810a70bd3d5c9be18c3af140 100644
--- a/opossum/src/utils/geom_transformation.rs
+++ b/opossum/src/utils/geom_transformation.rs
@@ -2,7 +2,6 @@
 #![warn(missing_docs)]
 use std::fmt::Display;
 
-use super::EnumProxy;
 use crate::{
     degree,
     error::{OpmResult, OpossumError},
@@ -405,14 +404,9 @@ impl Display for Isometry {
         )
     }
 }
-impl From<EnumProxy<Option<Isometry>>> for Proptype {
-    fn from(value: EnumProxy<Option<Isometry>>) -> Self {
-        Self::Isometry(value)
-    }
-}
 impl From<Option<Isometry>> for Proptype {
     fn from(value: Option<Isometry>) -> Self {
-        Self::Isometry(EnumProxy { value })
+        Self::Isometry(value)
     }
 }
 #[cfg(feature = "bevy")]
@@ -980,12 +974,5 @@ mod test {
     #[test]
     fn from() {
         assert_matches!(Some(Isometry::identity()).into(), Proptype::Isometry(_));
-        assert_matches!(
-            EnumProxy {
-                value: Some(Isometry::identity())
-            }
-            .into(),
-            Proptype::Isometry(_)
-        );
     }
 }
diff --git a/opossum/src/utils/mod.rs b/opossum/src/utils/mod.rs
index de170643809e89c1cd68e27d03090301e760302a..bf52db7818d937ea65b7cc2889341374fbfcd7de 100644
--- a/opossum/src/utils/mod.rs
+++ b/opossum/src/utils/mod.rs
@@ -1,5 +1,4 @@
 //! Module for additional computational capabilities
-pub mod enum_proxy;
 pub mod filter_data;
 pub mod geom_transformation;
 pub mod griddata;
@@ -8,5 +7,4 @@ pub mod math_utils;
 pub mod test_helper;
 pub mod unit_format;
 pub mod uom_macros;
-pub use enum_proxy::EnumProxy;
 pub use math_utils::{f64_to_usize, usize_to_f64};
diff --git a/opossum_backend/src/nodes.rs b/opossum_backend/src/nodes.rs
index d3639817ec993c681e4b09ad489eff5faf912a91..b69d199d52f37b83cb713f2a2553a1e98c70f20e 100644
--- a/opossum_backend/src/nodes.rs
+++ b/opossum_backend/src/nodes.rs
@@ -103,33 +103,24 @@ async fn get_subnodes(
 async fn post_subnode(
     data: web::Data<AppState>,
     path: web::Path<Uuid>,
-    node_type: String,
+    node_type: web::Json<String>,
 ) -> Result<Json<NodeInfo>, ErrorResponse> {
+    let node_type = node_type.into_inner();
     let new_node = create_node_ref(&node_type)?;
     let mut document = data.document.lock().unwrap();
-    let scenery = document.scenery_mut();
-    let uuid = path.into_inner();
-    if uuid.is_nil() {
-        scenery.add_node_ref(new_node)?;
+        let uuid = path.into_inner();
+    let new_node_uuid=if uuid.is_nil() {
+        let scenery = document.scenery_mut();
+        scenery.add_node_ref(new_node.clone())?
     } else {
-        let node_ref = scenery.node_recursive(uuid)?;
-        node_ref
-            .optical_ref
-            .lock()
-            .unwrap()
-            .as_group_mut()?
-            .add_node_ref(new_node)?;
-    }
-    let node_ref = scenery.node_recursive(uuid)?;
-    let node = node_ref.optical_ref.lock().unwrap();
-    let name = node.name();
-    let node_type = node.node_type();
-    drop(node);
-    drop(document);
+        let scenery = document.scenery_mut();
+        scenery.node_recursive(uuid)?.optical_ref.lock().unwrap().as_group_mut()?.add_node_ref(new_node.clone())?  
+    };
+    let node = new_node.optical_ref.lock().unwrap();
     let node_info = NodeInfo {
-        uuid: node_ref.uuid(),
-        name,
-        node_type,
+        uuid: new_node_uuid,
+        name: node.name(),
+        node_type: node.node_type(),
     };
     Ok(Json(node_info))
 }