use super::Polarization;
use super::slab::Slab;
use crate::units::{Length, Wavelength};
use crate::{Error, Result};
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Ridge {
below: f64,
core: f64,
above: f64,
height: Length,
slab: Length,
width: Length,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Eim {
pub centre: f64,
pub side: f64,
pub effective_index: f64,
}
impl Ridge {
pub fn new(
below: f64,
core: f64,
above: f64,
height: Length,
slab: Length,
width: Length,
) -> Result<Ridge> {
let ok = |n: f64| n.is_finite() && n >= 1.0;
if !ok(below) || !ok(core) || !ok(above) || core <= below || core <= above {
return Err(Error::invalid(
"ridge",
format!("the core ({core}) must have the highest index, not {below} and {above}"),
));
}
let positive = |l: Length| l.to_um().is_finite() && l.to_um() > 0.0;
if !positive(height) || !positive(width) {
return Err(Error::invalid(
"ridge",
format!("the height and width must be positive, got {height} and {width}"),
));
}
let s = slab.to_um();
if !(s.is_finite() && s >= 0.0 && s < height.to_um()) {
return Err(Error::invalid(
"ridge",
format!("the slab must be thinner than the ridge, got {slab} under {height}"),
));
}
Ok(Ridge {
below,
core,
above,
height,
slab,
width,
})
}
pub fn strip(
below: f64,
core: f64,
above: f64,
height: Length,
width: Length,
) -> Result<Ridge> {
Ridge::new(below, core, above, height, Length::ZERO, width)
}
pub fn mode(&self, like: Polarization, wavelength: Wavelength) -> Result<Eim> {
let (vertical, lateral) = match like {
Polarization::Te => (Polarization::Te, Polarization::Tm),
Polarization::Tm => (Polarization::Tm, Polarization::Te),
};
let fundamental = |slab: Slab, polarization: Polarization, what: &str| {
slab.modes(polarization, wavelength)
.first()
.map(|m| m.effective_index())
.ok_or_else(|| {
Error::invalid("effective index method", format!("{what} guides no mode"))
})
};
let centre = fundamental(
Slab::new(self.below, self.core, self.above, self.height)?,
vertical,
"the ridge's vertical slab",
)?;
let side = if self.slab.to_um() > 0.0 {
fundamental(
Slab::new(self.below, self.core, self.above, self.slab)?,
vertical,
"the rib's side slab",
)?
} else {
self.above
};
if centre <= side {
return Err(Error::invalid(
"effective index method",
format!(
"the ridge ({centre}) is no higher than its sides ({side}): no lateral mode"
),
));
}
let effective_index = fundamental(
Slab::new(side, centre, side, self.width)?,
lateral,
"the lateral slab",
)?;
Ok(Eim {
centre,
side,
effective_index,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
fn lam(um: f64) -> Wavelength {
Wavelength::um(um).unwrap()
}
#[test]
fn the_books_strip_gives_its_effective_index() {
let lateral = Slab::new(1.444, 2.845, 1.444, Length::nm(500.0)).unwrap();
let n = lateral.modes(Polarization::Tm, lam(1.55))[0].effective_index();
assert!((n - 2.489).abs() < 5e-4, "{n}");
let strip =
Ridge::strip(1.444, 3.473, 1.444, Length::nm(220.0), Length::nm(500.0)).unwrap();
let eim = strip.mode(Polarization::Te, lam(1.55)).unwrap();
assert!((eim.centre - 2.845).abs() < 5e-4, "{}", eim.centre);
assert_eq!(eim.side, 1.444);
assert!(
(eim.effective_index - 2.489).abs() < 1e-3,
"{}",
eim.effective_index
);
}
#[test]
fn a_wide_ridge_tends_to_its_slab() {
for like in [Polarization::Te, Polarization::Tm] {
let wide = |w: f64| {
Ridge::strip(1.444, 3.473, 1.444, Length::nm(220.0), Length::um(w))
.unwrap()
.mode(like, lam(1.55))
.unwrap()
};
let (a, b) = (wide(5.0), wide(50.0));
assert!(a.effective_index < b.effective_index && b.effective_index < b.centre);
assert!(b.centre - b.effective_index < 1e-4, "{like:?}: {b:?}");
}
}
#[test]
fn a_ribs_sides_are_its_slab() {
let rib = Ridge::new(
1.444,
3.473,
1.444,
Length::nm(220.0),
Length::nm(90.0),
Length::nm(500.0),
)
.unwrap();
let eim = rib.mode(Polarization::Te, lam(1.55)).unwrap();
let side = Slab::new(1.444, 3.473, 1.444, Length::nm(90.0))
.unwrap()
.modes(Polarization::Te, lam(1.55))[0]
.effective_index();
assert_eq!(eim.side, side);
let strip =
Ridge::strip(1.444, 3.473, 1.444, Length::nm(220.0), Length::nm(500.0)).unwrap();
assert!(
eim.effective_index
> strip
.mode(Polarization::Te, lam(1.55))
.unwrap()
.effective_index
);
}
#[test]
fn bad_ridges_are_errors() {
let (h, w) = (Length::nm(220.0), Length::nm(500.0));
assert!(Ridge::strip(1.444, 1.4, 1.444, h, w).is_err());
assert!(Ridge::strip(1.444, 3.473, 1.444, Length::ZERO, w).is_err());
assert!(Ridge::new(1.444, 3.473, 1.444, h, Length::nm(220.0), w).is_err());
let thin = Ridge::strip(1.444, 1.6, 1.0, Length::nm(20.0), w).unwrap();
assert!(thin.mode(Polarization::Te, lam(1.55)).is_err());
}
}