Skip to content
Snippets Groups Projects
beam_splitter.rs 6.08 KiB
Newer Older
Udo Eisenbarth's avatar
Udo Eisenbarth committed
#![warn(missing_docs)]
use std::collections::HashMap;

Udo Eisenbarth's avatar
Udo Eisenbarth committed
use crate::{
    analyzer::AnalyzerType,
    error::OpossumError,
    lightdata::{DataEnergy, LightData},
    optic_node::{Dottable, LightResult, Optical},
    optic_ports::OpticPorts,
Udo Eisenbarth's avatar
Udo Eisenbarth committed
    spectrum::{merge_spectra, Spectrum},
Udo Eisenbarth's avatar
Udo Eisenbarth committed
};
type Result<T> = std::result::Result<T, OpossumError>;

Udo Eisenbarth's avatar
Udo Eisenbarth committed
/// An ideal beamsplitter node with a given splitting ratio.
Udo Eisenbarth's avatar
Udo Eisenbarth committed
///
/// ## Optical Ports
///   - Inputs
///     - `input1`
///     - `input2`
///   - Outputs
///     - `out1_trans1_refl2`
///     - `out2_trans2_refl1`
Udo Eisenbarth's avatar
Udo Eisenbarth committed
pub struct BeamSplitter {
Udo Eisenbarth's avatar
Udo Eisenbarth committed
    ratio: f64,
Udo Eisenbarth's avatar
Udo Eisenbarth committed
}

Udo Eisenbarth's avatar
Udo Eisenbarth committed
impl BeamSplitter {
    /// Creates a new [`BeamSplitter`] with a given splitting ratio.
    /// 
    /// ## Errors
    /// This function returns an [`OpossumError::Other`] if the splitting ratio is outside the closed interval
    /// [0.0..1.0].
    pub fn new(ratio: f64) -> Result<Self> {
        if (0.0..=1.0).contains(&ratio) {
            Ok(Self { ratio })
        } else {
            Err(OpossumError::Other(
                "splitting ration must be within (0.0..1.0)".into(),
            ))
        }
Udo Eisenbarth's avatar
Udo Eisenbarth committed
    }

Udo Eisenbarth's avatar
Udo Eisenbarth committed
    /// Returns the splitting ratio of this [`BeamSplitter`].
Udo Eisenbarth's avatar
Udo Eisenbarth committed
    pub fn ratio(&self) -> f64 {
Udo Eisenbarth's avatar
Udo Eisenbarth committed
        self.ratio
    }
Udo Eisenbarth's avatar
Udo Eisenbarth committed
    /// Sets the splitting ratio of this [`BeamSplitter`].
    /// 
    /// ## Errors
    /// This function returns an [`OpossumError::Other`] if the splitting ratio is outside the closed interval
    /// [0.0..1.0].
    pub fn set_ratio(&mut self, ratio: f64) -> Result<()> {
        if (0.0..=1.0).contains(&ratio) {
            self.ratio = ratio;
            Ok(())
        } else {
            Err(OpossumError::Other(
                "splitting ration must be within (0.0..1.0)".into(),
            ))
        }
Udo Eisenbarth's avatar
Udo Eisenbarth committed
    }
Udo Eisenbarth's avatar
Udo Eisenbarth committed
    fn analyze_energy(&mut self, incoming_data: LightResult) -> Result<LightResult> {
        let in1 = incoming_data.get("input1");
        let in2 = incoming_data.get("input2");
        let mut out1_1_spectrum: Option<Spectrum> = None;
        let mut out1_2_spectrum: Option<Spectrum> = None;
        let mut out2_1_spectrum: Option<Spectrum> = None;
        let mut out2_2_spectrum: Option<Spectrum> = None;
        if let Some(Some(in1)) = in1 {
            match in1 {
                LightData::Energy(e) => {
                    let mut s = e.spectrum.clone();
                    s.scale_vertical(self.ratio).unwrap();
                    out1_1_spectrum = Some(s);
                    let mut s = e.spectrum.clone();
                    s.scale_vertical(1.0 - self.ratio).unwrap();
                    out1_2_spectrum = Some(s);
                _ => return Err(OpossumError::Analysis("expected DataEnergy value".into())),
        if let Some(Some(in2)) = in2 {
            match in2 {
                LightData::Energy(e) => {
                    let mut s = e.spectrum.clone();
                    s.scale_vertical(self.ratio).unwrap();
                    out2_1_spectrum = Some(s);
                    let mut s = e.spectrum.clone();
                    s.scale_vertical(1.0 - self.ratio).unwrap();
                    out2_2_spectrum = Some(s);
                }
                _ => return Err(OpossumError::Analysis("expected DataEnergy value".into())),
Udo Eisenbarth's avatar
Udo Eisenbarth committed
        let out1_spec = merge_spectra(out1_1_spectrum, out2_2_spectrum);
        let out2_spec = merge_spectra(out1_2_spectrum, out2_1_spectrum);
        let mut out1_data: Option<LightData> = None;
        let mut out2_data: Option<LightData> = None;
        if let Some(out1_spec) = out1_spec {
            out1_data = Some(LightData::Energy(DataEnergy {
                spectrum: out1_spec,
            }))
        if let Some(out2_spec) = out2_spec {
            out2_data = Some(LightData::Energy(DataEnergy {
                spectrum: out2_spec,
            }))
        Ok(HashMap::from([
            ("out1_trans1_refl2".into(), out1_data),
            ("out2_trans2_refl1".into(), out2_data),
Udo Eisenbarth's avatar
Udo Eisenbarth committed
}

Udo Eisenbarth's avatar
Udo Eisenbarth committed
impl Default for BeamSplitter {
Udo Eisenbarth's avatar
Udo Eisenbarth committed
    /// Create a 50:50 beamsplitter.
    fn default() -> Self {
        Self { ratio: 0.5 }
    }
}
Udo Eisenbarth's avatar
Udo Eisenbarth committed
impl Optical for BeamSplitter {
Udo Eisenbarth's avatar
Udo Eisenbarth committed
    fn node_type(&self) -> &str {
        "beam splitter"
Udo Eisenbarth's avatar
Udo Eisenbarth committed
    }
    fn ports(&self) -> OpticPorts {
        let mut ports = OpticPorts::new();
        ports.add_input("input1").unwrap();
        ports.add_input("input2").unwrap();
        ports.add_output("out1_trans1_refl2").unwrap();
        ports.add_output("out2_trans2_refl1").unwrap();
        ports
    }

    fn analyze(
        &mut self,
        incoming_data: LightResult,
        analyzer_type: &AnalyzerType,
    ) -> Result<LightResult> {
        match analyzer_type {
            AnalyzerType::Energy => self.analyze_energy(incoming_data),
            _ => Err(OpossumError::Analysis(
                "analysis type not yet implemented".into(),
            )),
Udo Eisenbarth's avatar
Udo Eisenbarth committed
impl Dottable for BeamSplitter {
Udo Eisenbarth's avatar
Udo Eisenbarth committed
    fn node_color(&self) -> &str {
        "lightpink"
    }

#[cfg(test)]
mod test {
    use super::*;
    #[test]
    fn new() {
        let splitter=BeamSplitter::new(0.5);
        assert!(splitter.is_ok());
        assert_eq!(splitter.unwrap().ratio, 0.5);
        assert!(BeamSplitter::new(-0.01).is_err());
        assert!(BeamSplitter::new(1.01).is_err());
    }
    #[test]
    fn default() {
        let splitter=BeamSplitter::default();
        assert_eq!(splitter.ratio, 0.5);
    }
    #[test]
    fn ratio() {
        let splitter=BeamSplitter::new(0.5).unwrap();
        assert_eq!(splitter.ratio(), 0.5);
    }
    #[test]
    fn set_ratio() {
        let mut splitter=BeamSplitter::new(0.0).unwrap();
        assert!(splitter.set_ratio(1.0).is_ok());
        assert_eq!(splitter.ratio, 1.0);
        assert!(splitter.set_ratio(-0.1).is_err());
        assert!(splitter.set_ratio(1.1).is_err());
    }
    #[test]
    fn node_type() {
        let splitter=BeamSplitter::new(0.0).unwrap();
        assert_eq!(splitter.node_type(), "beam splitter");
    }
    #[test]
    fn node_color() {
        let splitter=BeamSplitter::new(0.0).unwrap();
        assert_eq!(splitter.node_color(), "lightpink");
    }
}