use crate::units::conversion::EPSILON_0;
use super::material::Q;
#[inline]
pub fn eps_fcm(eps_r: f64) -> f64 {
eps_r * EPSILON_0 * 1e-2
}
#[allow(clippy::too_many_arguments)]
pub fn poisson_residual(
i: usize,
psi: &[f64],
n: &[f64],
p: &[f64],
nd: &[f64],
na: &[f64],
dx: &[f64],
eps_r: f64,
) -> f64 {
let eps = eps_fcm(eps_r);
let dx_l = dx[i - 1];
let dx_r = dx[i];
let dx_avg = 0.5 * (dx_l + dx_r);
let flux_r = eps * (psi[i + 1] - psi[i]) / dx_r;
let flux_l = eps * (psi[i] - psi[i - 1]) / dx_l;
flux_r - flux_l + Q * (p[i] - n[i] + nd[i] - na[i]) * dx_avg
}
pub fn poisson_jacobian(i: usize, dx: &[f64], eps_r: f64) -> (f64, f64, f64, f64, f64) {
let eps = eps_fcm(eps_r);
let dx_l = dx[i - 1];
let dx_r = dx[i];
let dx_avg = 0.5 * (dx_l + dx_r);
let df_psi_l = eps / dx_l;
let df_psi_r = eps / dx_r;
let df_psi_c = -eps * (1.0 / dx_l + 1.0 / dx_r);
let df_n = -Q * dx_avg;
let df_p = Q * dx_avg;
(df_psi_l, df_psi_c, df_psi_r, df_n, df_p)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn poisson_residual_flat_potential_zero_charge() {
let n = 10;
let psi = vec![0.5_f64; n];
let carriers = vec![1e16_f64; n];
let nd = vec![1e16_f64; n];
let na = vec![1e16_f64; n];
let dx = vec![1e-5_f64; n - 1];
for i in 1..n - 1 {
let res = poisson_residual(i, &psi, &carriers, &carriers, &nd, &na, &dx, 11.7);
assert!(res.abs() < 1e-30, "Residual at node {i}: {res}");
}
}
}