use serde::{Deserialize, Serialize};
use crate::geometry::Point;
use crate::stack::Structure;
use crate::units::{Length, Wavelength};
use crate::{Error, Result};
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
pub struct Raster {
pub nx: usize,
pub ny: usize,
pub x0: f64,
pub x1: f64,
pub y0: f64,
pub y1: f64,
pub values: Vec<f32>,
}
impl Raster {
pub fn at(&self, i: usize, j: usize) -> f32 {
self.values[j * self.nx + i]
}
pub fn range(&self) -> (f32, f32) {
self.values
.iter()
.fold((f32::INFINITY, f32::NEG_INFINITY), |(lo, hi), &v| {
(lo.min(v), hi.max(v))
})
}
}
fn centres(lo: f64, hi: f64, step: f64) -> Result<Vec<f64>> {
if !(lo.is_finite() && hi.is_finite() && hi > lo) {
return Err(Error::invalid(
"raster",
format!("the range {lo} to {hi} um is empty"),
));
}
if !(step.is_finite() && step > 0.0) {
return Err(Error::invalid(
"raster",
format!("the step must be positive, got {step} um"),
));
}
let n = ((hi - lo) / step).round().max(1.0);
if n > 10_000.0 {
return Err(Error::invalid(
"raster",
format!("{n} cells along one side is more than 10000"),
));
}
let n = n as usize;
let h = (hi - lo) / n as f64;
Ok((0..n).map(|i| lo + (i as f64 + 0.5) * h).collect())
}
impl Structure {
pub fn top_view(
&self,
layer: &str,
x: (Length, Length),
y: (Length, Length),
step: Length,
wavelength: Wavelength,
) -> Result<Raster> {
let (_, bottom, top) = self
.stack()
.layer(layer)
.ok_or_else(|| Error::invalid("raster", format!("the stack has no layer {layer}")))?;
let z = (bottom + top) / 2.0;
let xs = centres(x.0.to_um(), x.1.to_um(), step.to_um())?;
let ys = centres(y.0.to_um(), y.1.to_um(), step.to_um())?;
let mut values = Vec::with_capacity(xs.len() * ys.len());
for &yv in &ys {
for &xv in &xs {
let m = self.material_at(Point::um(xv, yv), z);
values.push(m.permittivity(wavelength)?.re as f32);
}
}
Ok(Raster {
nx: xs.len(),
ny: ys.len(),
x0: x.0.to_um(),
x1: x.1.to_um(),
y0: y.0.to_um(),
y1: y.1.to_um(),
values,
})
}
pub fn side_view(
&self,
y: Length,
x: (Length, Length),
z: (Length, Length),
step: Length,
wavelength: Wavelength,
) -> Result<Raster> {
let xs = centres(x.0.to_um(), x.1.to_um(), step.to_um())?;
let zs = centres(z.0.to_um(), z.1.to_um(), step.to_um())?;
let mut values = Vec::with_capacity(xs.len() * zs.len());
for &zv in &zs {
for &xv in &xs {
let m = self.material_at(
Point {
x: Length::um(xv),
y,
},
Length::um(zv),
);
values.push(m.permittivity(wavelength)?.re as f32);
}
}
Ok(Raster {
nx: xs.len(),
ny: zs.len(),
x0: x.0.to_um(),
x1: x.1.to_um(),
y0: z.0.to_um(),
y1: z.1.to_um(),
values,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::geometry::Shape;
use crate::stack::LayerStack;
fn waveguide() -> Structure {
let mut s = Structure::new(LayerStack::soi_220());
let wg = Shape::rect(Point::um(0.0, 0.0), Length::um(4.0), Length::nm(600.0)).unwrap();
s.draw("Si", wg).unwrap();
s
}
fn lam() -> Wavelength {
Wavelength::um(1.55).unwrap()
}
#[test]
fn the_top_view_shows_silicon_in_the_waveguide_and_oxide_around() {
let r = waveguide()
.top_view(
"Si",
(Length::um(-1.0), Length::um(1.0)),
(Length::um(-1.0), Length::um(1.0)),
Length::nm(100.0),
lam(),
)
.unwrap();
assert_eq!((r.nx, r.ny), (20, 20));
let si = 3.4757f32 * 3.4757;
let ox = 1.444f32 * 1.444;
assert!((r.at(10, 10) - si).abs() < 1e-3, "{}", r.at(10, 10));
assert!((r.at(10, 0) - ox).abs() < 2e-3, "{}", r.at(10, 0));
let silicon_rows = (0..r.ny).filter(|&j| r.at(0, j) > 10.0).count();
assert_eq!(silicon_rows, 6);
let (lo, hi) = r.range();
assert!(lo < 2.1 && hi > 12.0);
}
#[test]
fn the_side_view_shows_the_layers() {
let r = waveguide()
.side_view(
Length::ZERO,
(Length::um(-1.0), Length::um(1.0)),
(Length::um(-0.5), Length::um(3.0)),
Length::nm(10.0),
lam(),
)
.unwrap();
let z_index = |z: f64| ((z + 0.5) / 0.01) as usize;
assert!(r.at(100, z_index(-0.25)) > 10.0);
assert!(r.at(100, z_index(1.0)) < 2.2);
assert!(r.at(100, z_index(2.11)) > 10.0);
assert!(r.at(100, z_index(2.6)) < 2.2);
let rows = (z_index(1.5)..r.ny)
.filter(|&j| r.at(100, j) > 10.0)
.count();
assert_eq!(rows, 22);
}
#[test]
fn bad_rasters_are_errors() {
let s = waveguide();
let range = (Length::um(-1.0), Length::um(1.0));
let step = Length::nm(100.0);
assert!(s.top_view("Metal", range, range, step, lam()).is_err());
assert!(
s.top_view("Si", (Length::um(1.0), Length::um(1.0)), range, step, lam())
.is_err()
);
assert!(s.top_view("Si", range, range, Length::ZERO, lam()).is_err());
assert!(
s.top_view("Si", range, range, Length::nm(0.01), lam())
.is_err()
);
let e = s
.top_view("Si", range, range, step, Wavelength::um(1.0).unwrap())
.unwrap_err();
assert!(matches!(e, Error::OutsideValidity { .. }), "{e}");
}
}