#![no_std]
#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]
use embedded_f32_sqrt::sqrt;
pub struct SchrodingerSolver<const N: usize> {
pub potential: [f32; N],
pub dx: f32,
pub hbar: f32,
pub mass: f32,
}
impl<const N: usize> SchrodingerSolver<N> {
#[inline]
pub fn new(potential: [f32; N], dx: f32, hbar: f32, mass: f32) -> Self {
Self { potential, dx, hbar, mass }
}
pub fn integrate_wavefunction(&self, energy: f32, psi: &mut [f32; N]) -> f32 {
psi[0] = 0.0;
psi[1] = 0.001;
let k_factor = (2.0 * self.mass * self.dx * self.dx) / (self.hbar * self.hbar);
for i in 1..(N - 1) {
psi[i + 1] = 2.0 * psi[i]
- psi[i - 1]
- k_factor * (energy - self.potential[i]) * psi[i];
if psi[i + 1].is_infinite() || psi[i + 1].is_nan() {
return psi[i];
}
}
psi[N - 1]
}
pub fn normalize(&self, psi: &mut [f32; N]) -> Result<(), &'static str> {
let mut sum = 0.0f32;
for &val in psi.iter() {
sum += val * val * self.dx;
}
let norm = sqrt(sum)
.map_err(|_| "Erreur mathématique lors du calcul de la norme (NaN/Infinity)")?;
if norm > 0.0 {
for val in psi.iter_mut() {
*val /= norm;
}
Ok(())
} else {
Err("Impossible de normaliser une fonction d'onde de norme nulle")
}
}
pub fn find_eigenstate(
&self,
mut e_min: f32,
mut e_max: f32,
psi: &mut [f32; N],
) -> Result<f32, &'static str> {
let mut e_mid = 0.0f32;
for _ in 0..30 {
e_mid = 0.5 * (e_min + e_max);
let psi_boundary = self.integrate_wavefunction(e_mid, psi);
if psi_boundary > 0.0 {
e_min = e_mid;
} else {
e_max = e_mid;
}
}
self.normalize(psi)?;
Ok(e_mid)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_energie_fondamentale_puits_infini() {
const N: usize = 200;
let dx = 1.0 / N as f32;
let solver = SchrodingerSolver::new([0.0f32; N], dx, 1.0, 1.0);
let mut psi = [0.0f32; N];
let energy = solver
.find_eigenstate(1.0, 15.0, &mut psi)
.expect("find_eigenstate a échoué");
let e_ref = core::f32::consts::PI * core::f32::consts::PI / 2.0;
assert!(
(energy - e_ref).abs() < 0.5,
"Énergie fondamentale hors tolérance : {energy} vs {e_ref}"
);
}
#[test]
fn test_normalisation_norme_unitaire() {
const N: usize = 200;
let dx = 1.0 / N as f32;
let solver = SchrodingerSolver::new([0.0f32; N], dx, 1.0, 1.0);
let mut psi = [0.0f32; N];
solver
.find_eigenstate(1.0, 15.0, &mut psi)
.expect("find_eigenstate a échoué");
let norm_sq: f32 = psi.iter().map(|&v| v * v * dx).sum();
assert!(
(norm_sq - 1.0).abs() < 1e-3,
"La norme au carré devrait être ≈ 1, obtenu : {norm_sq}"
);
}
#[test]
fn test_potentiel_constant_decalage_energie() {
const N: usize = 200;
let dx = 1.0 / N as f32;
let solver_libre = SchrodingerSolver::new([0.0f32; N], dx, 1.0, 1.0);
let solver_offset = SchrodingerSolver::new([1.0f32; N], dx, 1.0, 1.0);
let mut psi = [0.0f32; N];
let e_libre = solver_libre
.find_eigenstate(1.0, 15.0, &mut psi)
.expect("Solver libre a échoué");
let e_offset = solver_offset
.find_eigenstate(2.0, 16.0, &mut psi)
.expect("Solver décalé a échoué");
assert!(
(e_offset - e_libre - 1.0).abs() < 0.5,
"Décalage en énergie incorrect : {e_offset} - {e_libre} ≠ 1.0"
);
}
#[test]
fn test_normalisation_vecteur_nul_retourne_erreur() {
const N: usize = 50;
let solver = SchrodingerSolver::new([0.0f32; N], 0.01, 1.0, 1.0);
let mut psi = [0.0f32; N];
let result = solver.normalize(&mut psi);
assert!(result.is_err(), "Devrait échouer sur une fonction d'onde nulle");
}
#[test]
fn test_integration_produit_valeur_finie() {
const N: usize = 100;
let dx = 0.01;
let solver = SchrodingerSolver::new([0.0f32; N], dx, 1.0, 1.0);
let mut psi = [0.0f32; N];
let boundary = solver.integrate_wavefunction(5.0, &mut psi);
assert!(
boundary.is_finite(),
"La valeur frontière doit être finie, obtenu : {boundary}"
);
}
}