use std::f64::consts::PI;
#[derive(Debug, Clone, Copy)]
pub struct WaveguideBend {
pub n_eff: f64,
pub n_g: f64,
pub n_core: f64,
pub n_clad: f64,
pub core_width: f64,
pub wavelength: f64,
}
impl WaveguideBend {
pub fn new(
n_eff: f64,
n_g: f64,
n_core: f64,
n_clad: f64,
core_width: f64,
wavelength: f64,
) -> Self {
Self {
n_eff,
n_g,
n_core,
n_clad,
core_width,
wavelength,
}
}
pub fn soi_strip_1550() -> Self {
Self {
n_eff: 2.44,
n_g: 4.18,
n_core: 3.48,
n_clad: 1.444,
core_width: 500e-9,
wavelength: 1550e-9,
}
}
pub fn n_eff_bend(&self, radius: f64) -> f64 {
self.n_eff * (1.0 + self.core_width / (2.0 * radius))
}
pub fn phase_shift_full_ring(&self, radius: f64) -> f64 {
let k0 = 2.0 * PI / self.wavelength;
let dn = self.n_eff_bend(radius) - self.n_eff;
k0 * dn * 2.0 * PI * radius
}
fn mode_parameters(&self) -> (f64, f64, f64, f64) {
let k0 = 2.0 * PI / self.wavelength;
let beta = k0 * self.n_eff;
let kappa_x_sq = k0 * k0 * self.n_core * self.n_core - beta * beta;
let kappa_x = if kappa_x_sq > 0.0 {
kappa_x_sq.sqrt()
} else {
0.0
};
let gamma_sq = beta * beta - k0 * k0 * self.n_clad * self.n_clad;
let gamma_min = 1.0 / (100.0 * self.core_width);
let gamma = if gamma_sq > 0.0 {
gamma_sq.sqrt().max(gamma_min)
} else {
gamma_min
};
let a = self.core_width / 2.0;
(kappa_x, beta, gamma, a)
}
pub fn bend_loss_db_per_90deg(&self, bend_radius_m: f64) -> f64 {
let (kappa_x, beta, gamma, a) = self.mode_parameters();
if kappa_x == 0.0 {
return f64::INFINITY;
}
let prefactor = (kappa_x * kappa_x) / (beta * gamma * (1.0 + gamma * a));
let exp_evanescent = (2.0 * gamma * a).exp();
let cubic_exp_arg = -(2.0 / 3.0) * (gamma.powi(3) / beta.powi(2)) * bend_radius_m;
let cubic_exp = if cubic_exp_arg < -745.0 {
0.0
} else {
cubic_exp_arg.exp()
};
let alpha_bend_npm = prefactor * exp_evanescent * cubic_exp;
alpha_bend_npm * (PI * bend_radius_m / 2.0) * 10.0 / std::f64::consts::LN_10
}
pub fn bend_loss_db_per_turn(&self, radius: f64) -> f64 {
4.0 * self.bend_loss_db_per_90deg(radius)
}
pub fn minimum_bend_radius(&self, target_db: f64) -> f64 {
let mut r_lo = 1e-6;
let mut r_hi = 1e-3;
for _ in 0..50 {
let r_mid = (r_lo + r_hi) / 2.0;
if self.bend_loss_db_per_90deg(r_mid) > target_db {
r_lo = r_mid;
} else {
r_hi = r_mid;
}
}
(r_lo + r_hi) / 2.0
}
pub fn mode_mismatch_loss_db(&self, radius: f64) -> f64 {
let dn_relative = self.core_width / (2.0 * radius);
let sigma = 0.05; let loss = dn_relative * dn_relative / (sigma * sigma);
4.343 * loss }
pub fn total_loss_db_per_90deg(&self, radius: f64) -> f64 {
self.bend_loss_db_per_90deg(radius) + self.mode_mismatch_loss_db(radius)
}
}
#[derive(Debug, Clone, Copy)]
pub struct SBend {
pub waveguide: WaveguideBend,
pub offset: f64,
pub length: f64,
}
impl SBend {
pub fn new(waveguide: WaveguideBend, offset: f64, length: f64) -> Self {
Self {
waveguide,
offset,
length,
}
}
pub fn min_radius(&self) -> f64 {
self.length * self.length / (8.0 * self.offset)
}
pub fn total_loss_db(&self) -> f64 {
let r_min = self.min_radius();
2.0 * self.waveguide.total_loss_db_per_90deg(r_min)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn n_eff_bend_larger_than_straight() {
let wg = WaveguideBend::soi_strip_1550();
let n_bend = wg.n_eff_bend(5e-6);
assert!(n_bend > wg.n_eff);
}
#[test]
fn larger_radius_less_loss() {
let wg = WaveguideBend::soi_strip_1550();
let loss_small = wg.bend_loss_db_per_90deg(2e-6);
let loss_large = wg.bend_loss_db_per_90deg(50e-6);
assert!(loss_large < loss_small, "Large R should have less loss");
}
#[test]
fn bend_loss_positive() {
let wg = WaveguideBend::soi_strip_1550();
let loss = wg.bend_loss_db_per_90deg(5e-6);
assert!(loss >= 0.0);
}
#[test]
fn minimum_radius_within_range() {
let wg = WaveguideBend::soi_strip_1550();
let r_min = wg.minimum_bend_radius(0.1);
assert!(r_min > 0.1e-6 && r_min < 500e-6, "r_min={r_min:.2e}");
}
#[test]
fn sbend_radius_formula() {
let wg = WaveguideBend::soi_strip_1550();
let sb = SBend::new(wg, 10e-6, 100e-6);
let r = sb.min_radius();
assert!((r - 125e-6).abs() < 1e-9, "r={r:.2e}");
}
#[test]
fn n_eff_bend_approaches_straight_at_large_r() {
let wg = WaveguideBend::soi_strip_1550();
let n_bend = wg.n_eff_bend(1.0); let rel_diff = (n_bend - wg.n_eff).abs() / wg.n_eff;
assert!(rel_diff < 1e-6);
}
}