deep_causality_physics/photonics/
diffraction.rs

1/*
2 * SPDX-License-Identifier: MIT
3 * Copyright (c) "2025" . The DeepCausality Authors and Contributors. All Rights Reserved.
4 */
5
6use crate::PhysicsError;
7use crate::dynamics::quantities::Length;
8use crate::photonics::quantities::{RayAngle, Wavelength};
9use std::f64::consts::PI;
10
11/// Calculates the Single Slit Diffraction Irradiance.
12///
13/// Fraunhofer approximation:
14/// $$ I(\theta) = I_0 \left( \frac{\sin \beta}{\beta} \right)^2 $$
15/// where $\beta = \frac{\pi a \sin \theta}{\lambda}$.
16///
17/// # Arguments
18/// *   `i0` - Peak irradiance $I_0$ at $\theta = 0$.
19/// *   `slit_width` - Width of the slit $a$.
20/// *   `theta` - Diffraction angle $\theta$.
21/// *   `wavelength` - Wavelength $\lambda$.
22///
23/// # Returns
24/// *   `Result<f64, PhysicsError>` - Irradiance at angle $\theta$.
25pub fn single_slit_irradiance_kernel(
26    i0: f64,
27    slit_width: Length,
28    theta: RayAngle,
29    wavelength: Wavelength,
30) -> Result<f64, PhysicsError> {
31    if i0 < 0.0 {
32        return Err(PhysicsError::PhysicalInvariantBroken(
33            "Irradiance i0 cannot be negative".into(),
34        ));
35    }
36
37    let a = slit_width.value();
38    let lambda = wavelength.value();
39    let angle = theta.value();
40
41    if lambda == 0.0 {
42        return Err(PhysicsError::Singularity("Wavelength is zero".into()));
43    }
44
45    let beta = (PI * a * angle.sin()) / lambda;
46
47    // Limit lim beta->0 (sin beta / beta) = 1
48    if beta.abs() < 1e-9 {
49        return Ok(i0);
50    }
51
52    let sinc = beta.sin() / beta;
53    let i = i0 * sinc * sinc;
54
55    Ok(i)
56}
57
58/// Calculates the diffraction angle for a Grating using the Grating Equation.
59///
60/// $$ d (\sin \theta_m - \sin \theta_i) = m \lambda $$
61///
62/// Solves for $\theta_m$.
63///
64/// # Arguments
65/// *   `pitch` - Grating pitch $d$ (distance between grooves).
66/// *   `order` - Diffraction order $m$ (integer).
67/// *   `incidence` - Angle of incidence $\theta_i$.
68/// *   `wavelength` - Wavelength $\lambda$.
69///
70/// # Returns
71/// *   `Result<RayAngle, PhysicsError>` - Diffraction angle $\theta_m$.
72pub fn grating_equation_kernel(
73    pitch: Length,
74    order: i32,
75    incidence: RayAngle,
76    wavelength: Wavelength,
77) -> Result<RayAngle, PhysicsError> {
78    let d = pitch.value();
79    let m = order as f64;
80    let lambda = wavelength.value();
81    let theta_i = incidence.value();
82
83    if d <= 0.0 {
84        return Err(PhysicsError::PhysicalInvariantBroken(
85            "Grating pitch must be positive".into(),
86        ));
87    }
88
89    // sin(theta_m) = (m * lambda / d) + sin(theta_i)
90    let sin_theta_m = (m * lambda / d) + theta_i.sin();
91
92    if sin_theta_m.abs() > 1.0 {
93        return Err(PhysicsError::PhysicalInvariantBroken(format!(
94            "Diffraction order {} does not exist for this configuration (sin_theta = {})",
95            order, sin_theta_m
96        )));
97    }
98
99    let theta_m = sin_theta_m.asin();
100    RayAngle::new(theta_m)
101}