#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TextureType {
Planar,
RandomPyramid,
InvertedPyramid,
VGroove,
Lambertian,
NanoCone,
}
#[derive(Debug, Clone, Copy)]
pub struct LightTrappingModel {
pub texture: TextureType,
pub n_semiconductor: f64,
pub thickness: f64,
pub r_front: f64,
pub r_back: f64,
}
impl LightTrappingModel {
pub fn new(texture: TextureType, n_semi: f64, thickness: f64) -> Self {
Self {
texture,
n_semiconductor: n_semi,
thickness,
r_front: 0.03, r_back: 0.9, }
}
pub fn csi_random_pyramid() -> Self {
Self::new(TextureType::RandomPyramid, 3.5, 180e-6)
}
pub fn csi_planar() -> Self {
Self::new(TextureType::Planar, 3.5, 180e-6)
}
pub fn lambertian(n_semi: f64, thickness: f64) -> Self {
Self::new(TextureType::Lambertian, n_semi, thickness)
}
pub fn yablonovitch_limit(&self) -> f64 {
4.0 * self.n_semiconductor * self.n_semiconductor
}
pub fn path_length_enhancement(&self) -> f64 {
let f_max = self.yablonovitch_limit();
match self.texture {
TextureType::Planar => 1.0 + self.r_back, TextureType::RandomPyramid => 0.7 * f_max, TextureType::InvertedPyramid => 0.8 * f_max,
TextureType::VGroove => 0.5 * f_max,
TextureType::Lambertian => f_max,
TextureType::NanoCone => 0.6 * f_max,
}
}
pub fn absorptance(&self, alpha_per_m: f64) -> f64 {
let f = self.path_length_enhancement();
let al = alpha_per_m * self.thickness;
1.0 - (1.0 - self.r_front) * (-f * al).exp()
}
pub fn current_enhancement(&self, alpha_per_m: f64) -> f64 {
let planar =
LightTrappingModel::new(TextureType::Planar, self.n_semiconductor, self.thickness);
let a_text = self.absorptance(alpha_per_m);
let a_planar = planar.absorptance(alpha_per_m);
if a_planar < 1e-10 {
return 1.0;
}
a_text / a_planar
}
pub fn effective_front_reflectance(&self, r_bare: f64) -> f64 {
match self.texture {
TextureType::Planar => r_bare,
TextureType::RandomPyramid | TextureType::InvertedPyramid => r_bare * 0.6,
TextureType::VGroove => r_bare * 0.5,
TextureType::Lambertian => r_bare * 0.1,
TextureType::NanoCone => r_bare * 0.3,
}
}
pub fn effective_path_length_um(&self) -> f64 {
let f = self.path_length_enhancement();
f * self.thickness * 1e6
}
}
#[derive(Debug, Clone, Copy)]
pub struct InvertedPyramidArray {
pub pitch: f64,
pub half_width: f64,
pub depth: f64,
pub facet_angle: f64,
}
impl InvertedPyramidArray {
pub fn standard_si() -> Self {
let p = 5e-6;
let w = 0.45 * p; Self {
pitch: p,
half_width: w,
depth: w, facet_angle: f64::to_radians(54.7),
}
}
pub fn surface_area_ratio(&self) -> f64 {
let p = self.pitch;
let facet_area = 4.0
* self.half_width
* (self.depth * self.depth + self.half_width * self.half_width).sqrt();
let coverage = (2.0 * self.half_width / p).powi(2);
1.0 + coverage * (facet_area / (p * p) - 1.0)
}
pub fn average_reflections(&self) -> f64 {
let angle_rad = self.facet_angle;
let hit_opposite = (2.0 * angle_rad).cos() < 0.0; if hit_opposite {
2.0
} else {
1.5
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn yablonovitch_limit_si() {
let m = LightTrappingModel::csi_random_pyramid();
let f_max = m.yablonovitch_limit();
assert!((f_max - 49.0).abs() < 1.0, "F_max={f_max:.1}");
}
#[test]
fn lambertian_has_max_enhancement() {
let m_lamb = LightTrappingModel::lambertian(3.5, 180e-6);
let m_rand = LightTrappingModel::csi_random_pyramid();
assert!(m_lamb.path_length_enhancement() >= m_rand.path_length_enhancement());
}
#[test]
fn planar_has_minimum_enhancement() {
let m_plane = LightTrappingModel::csi_planar();
let m_rand = LightTrappingModel::csi_random_pyramid();
assert!(m_rand.path_length_enhancement() > m_plane.path_length_enhancement());
}
#[test]
fn absorptance_increased_with_texturing() {
let m_rand = LightTrappingModel::csi_random_pyramid();
let m_plane = LightTrappingModel::csi_planar();
let alpha = 100.0; let a_rand = m_rand.absorptance(alpha);
let a_plane = m_plane.absorptance(alpha);
assert!(
a_rand > a_plane,
"A_texture={a_rand:.4} > A_planar={a_plane:.4}"
);
}
#[test]
fn current_enhancement_ge_1() {
let m = LightTrappingModel::csi_random_pyramid();
let enh = m.current_enhancement(1000.0);
assert!(enh >= 1.0, "enhancement={enh:.3}");
}
#[test]
fn effective_path_length_positive() {
let m = LightTrappingModel::csi_random_pyramid();
assert!(m.effective_path_length_um() > 0.0);
}
#[test]
fn inverted_pyramid_area_ratio_gt_1() {
let pyr = InvertedPyramidArray::standard_si();
assert!(pyr.surface_area_ratio() > 1.0);
}
#[test]
fn inverted_pyramid_reflections_positive() {
let pyr = InvertedPyramidArray::standard_si();
assert!(pyr.average_reflections() > 0.0);
}
}