1use std::f64::consts::PI;
2
3#[derive(Clone, Copy, Debug, PartialEq)]
5pub struct Complex {
6 pub re: f64,
7 pub im: f64,
8}
9
10impl Complex {
11 pub fn new(re: f64, im: f64) -> Self {
12 Self { re, im }
13 }
14
15 pub fn zero() -> Self {
16 Self { re: 0.0, im: 0.0 }
17 }
18
19 pub fn one() -> Self {
20 Self { re: 1.0, im: 0.0 }
21 }
22
23 pub fn i() -> Self {
24 Self { re: 0.0, im: 1.0 }
25 }
26
27 pub fn norm_sq(&self) -> f64 {
28 self.re * self.re + self.im * self.im
29 }
30
31 pub fn norm(&self) -> f64 {
32 self.norm_sq().sqrt()
33 }
34
35 pub fn conj(&self) -> Self {
36 Self { re: self.re, im: -self.im }
37 }
38
39 pub fn exp(self) -> Self {
40 let e = self.re.exp();
41 Self {
42 re: e * self.im.cos(),
43 im: e * self.im.sin(),
44 }
45 }
46
47 pub fn from_polar(r: f64, theta: f64) -> Self {
48 Self {
49 re: r * theta.cos(),
50 im: r * theta.sin(),
51 }
52 }
53
54 pub fn arg(&self) -> f64 {
55 self.im.atan2(self.re)
56 }
57
58 pub fn scale(self, s: f64) -> Self {
59 Self { re: self.re * s, im: self.im * s }
60 }
61}
62
63impl std::ops::Add for Complex {
64 type Output = Self;
65 fn add(self, rhs: Self) -> Self {
66 Self { re: self.re + rhs.re, im: self.im + rhs.im }
67 }
68}
69
70impl std::ops::AddAssign for Complex {
71 fn add_assign(&mut self, rhs: Self) {
72 self.re += rhs.re;
73 self.im += rhs.im;
74 }
75}
76
77impl std::ops::Sub for Complex {
78 type Output = Self;
79 fn sub(self, rhs: Self) -> Self {
80 Self { re: self.re - rhs.re, im: self.im - rhs.im }
81 }
82}
83
84impl std::ops::SubAssign for Complex {
85 fn sub_assign(&mut self, rhs: Self) {
86 self.re -= rhs.re;
87 self.im -= rhs.im;
88 }
89}
90
91impl std::ops::Mul for Complex {
92 type Output = Self;
93 fn mul(self, rhs: Self) -> Self {
94 Self {
95 re: self.re * rhs.re - self.im * rhs.im,
96 im: self.re * rhs.im + self.im * rhs.re,
97 }
98 }
99}
100
101impl std::ops::MulAssign for Complex {
102 fn mul_assign(&mut self, rhs: Self) {
103 let re = self.re * rhs.re - self.im * rhs.im;
104 let im = self.re * rhs.im + self.im * rhs.re;
105 self.re = re;
106 self.im = im;
107 }
108}
109
110impl std::ops::Div for Complex {
111 type Output = Self;
112 fn div(self, rhs: Self) -> Self {
113 let d = rhs.norm_sq();
114 Self {
115 re: (self.re * rhs.re + self.im * rhs.im) / d,
116 im: (self.im * rhs.re - self.re * rhs.im) / d,
117 }
118 }
119}
120
121impl std::ops::Neg for Complex {
122 type Output = Self;
123 fn neg(self) -> Self {
124 Self { re: -self.re, im: -self.im }
125 }
126}
127
128impl std::ops::Mul<f64> for Complex {
129 type Output = Self;
130 fn mul(self, rhs: f64) -> Self {
131 Self { re: self.re * rhs, im: self.im * rhs }
132 }
133}
134
135impl std::ops::Mul<Complex> for f64 {
136 type Output = Complex;
137 fn mul(self, rhs: Complex) -> Complex {
138 Complex { re: self * rhs.re, im: self * rhs.im }
139 }
140}
141
142impl std::ops::Div<f64> for Complex {
143 type Output = Self;
144 fn div(self, rhs: f64) -> Self {
145 Self { re: self.re / rhs, im: self.im / rhs }
146 }
147}
148
149impl Default for Complex {
150 fn default() -> Self {
151 Self::zero()
152 }
153}
154
155#[derive(Clone, Debug)]
157pub struct WaveFunction1D {
158 pub psi: Vec<Complex>,
159 pub dx: f64,
160 pub x_min: f64,
161}
162
163impl WaveFunction1D {
164 pub fn new(psi: Vec<Complex>, dx: f64, x_min: f64) -> Self {
165 Self { psi, dx, x_min }
166 }
167
168 pub fn n(&self) -> usize {
169 self.psi.len()
170 }
171
172 pub fn x_at(&self, i: usize) -> f64 {
173 self.x_min + i as f64 * self.dx
174 }
175
176 pub fn x_max(&self) -> f64 {
177 self.x_min + (self.n() - 1) as f64 * self.dx
178 }
179
180 pub fn probability_density(&self) -> Vec<f64> {
181 self.psi.iter().map(|c| c.norm_sq()).collect()
182 }
183
184 pub fn norm_squared(&self) -> f64 {
185 self.psi.iter().map(|c| c.norm_sq()).sum::<f64>() * self.dx
186 }
187
188 pub fn normalize(&mut self) {
189 let n = self.norm_squared().sqrt();
190 if n > 1e-30 {
191 for c in &mut self.psi {
192 *c = *c / n;
193 }
194 }
195 }
196}
197
198fn solve_tridiagonal(a: &[Complex], b: &[Complex], c: &[Complex], d: &mut [Complex]) -> Vec<Complex> {
201 let n = d.len();
202 let mut cp = vec![Complex::zero(); n];
203 let mut dp = vec![Complex::zero(); n];
204
205 cp[0] = c[0] / b[0];
206 dp[0] = d[0] / b[0];
207
208 for i in 1..n {
209 let m = b[i] - a[i] * cp[i - 1];
210 cp[i] = if i < n - 1 { c[i] / m } else { Complex::zero() };
211 dp[i] = (d[i] - a[i] * dp[i - 1]) / m;
212 }
213
214 let mut x = vec![Complex::zero(); n];
215 x[n - 1] = dp[n - 1];
216 for i in (0..n - 1).rev() {
217 x[i] = dp[i] - cp[i] * x[i + 1];
218 }
219 x
220}
221
222#[derive(Clone)]
224pub struct SchrodingerSolver1D {
225 pub psi: WaveFunction1D,
226 pub potential: Vec<f64>,
227 pub mass: f64,
228 pub hbar: f64,
229 pub dt: f64,
230}
231
232impl SchrodingerSolver1D {
233 pub fn new(psi: WaveFunction1D, potential: Vec<f64>, mass: f64, hbar: f64, dt: f64) -> Self {
234 Self { psi, potential, mass, hbar, dt }
235 }
236
237 pub fn step(&mut self) {
239 let n = self.psi.n();
240 let dx = self.psi.dx;
241 let r = Complex::new(0.0, self.hbar * self.dt / (4.0 * self.mass * dx * dx));
242
243 let mut a_lower = vec![Complex::zero(); n];
244 let mut a_diag = vec![Complex::zero(); n];
245 let mut a_upper = vec![Complex::zero(); n];
246 let mut rhs = vec![Complex::zero(); n];
247
248 for j in 0..n {
249 let v_term = Complex::new(0.0, self.dt * self.potential[j] / (2.0 * self.hbar));
250
251 a_diag[j] = Complex::one() + r * 2.0 + v_term;
253 if j > 0 {
254 a_lower[j] = -r;
255 }
256 if j < n - 1 {
257 a_upper[j] = -r;
258 }
259
260 let psi_j = self.psi.psi[j];
262 let psi_left = if j > 0 { self.psi.psi[j - 1] } else { Complex::zero() };
263 let psi_right = if j < n - 1 { self.psi.psi[j + 1] } else { Complex::zero() };
264
265 rhs[j] = (Complex::one() - r * 2.0 - v_term) * psi_j
266 + r * psi_left
267 + r * psi_right;
268 }
269
270 self.psi.psi = solve_tridiagonal(&a_lower, &a_diag, &a_upper, &mut rhs);
271 }
272
273 pub fn step_split_operator(&mut self) {
275 let n = self.psi.n();
276 let dx = self.psi.dx;
277 let dt = self.dt;
278 let hbar = self.hbar;
279 let mass = self.mass;
280
281 for j in 0..n {
283 let phase = -self.potential[j] * dt / (2.0 * hbar);
284 let exp_v = Complex::from_polar(1.0, phase);
285 self.psi.psi[j] = self.psi.psi[j] * exp_v;
286 }
287
288 let mut psi_k = dft(&self.psi.psi);
290 let dk = 2.0 * PI / (n as f64 * dx);
291 for j in 0..n {
292 let k = if j <= n / 2 {
293 j as f64 * dk
294 } else {
295 (j as f64 - n as f64) * dk
296 };
297 let phase = -hbar * k * k * dt / (2.0 * mass);
298 let exp_t = Complex::from_polar(1.0, phase);
299 psi_k[j] = psi_k[j] * exp_t;
300 }
301 self.psi.psi = idft(&psi_k);
302
303 for j in 0..n {
305 let phase = -self.potential[j] * dt / (2.0 * hbar);
306 let exp_v = Complex::from_polar(1.0, phase);
307 self.psi.psi[j] = self.psi.psi[j] * exp_v;
308 }
309 }
310}
311
312pub fn dft(input: &[Complex]) -> Vec<Complex> {
314 let n = input.len();
315 let mut output = vec![Complex::zero(); n];
316 for k in 0..n {
317 let mut sum = Complex::zero();
318 for j in 0..n {
319 let angle = -2.0 * PI * (k as f64) * (j as f64) / (n as f64);
320 sum += input[j] * Complex::from_polar(1.0, angle);
321 }
322 output[k] = sum;
323 }
324 output
325}
326
327pub fn idft(input: &[Complex]) -> Vec<Complex> {
329 let n = input.len();
330 let mut output = vec![Complex::zero(); n];
331 for j in 0..n {
332 let mut sum = Complex::zero();
333 for k in 0..n {
334 let angle = 2.0 * PI * (k as f64) * (j as f64) / (n as f64);
335 sum += input[k] * Complex::from_polar(1.0, angle);
336 }
337 output[j] = sum / n as f64;
338 }
339 output
340}
341
342pub fn energy_eigenvalues(potential: &[f64], n_states: usize, dx: f64, mass: f64, hbar: f64) -> Vec<f64> {
345 let n = potential.len();
346 let v_min = potential.iter().cloned().fold(f64::INFINITY, f64::min);
347 let v_max = potential.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
348
349 let mut eigenvalues = Vec::new();
350 let length = (n.max(2) - 1) as f64 * dx;
356 let e_scale = hbar * hbar * std::f64::consts::PI.powi(2) / (2.0 * mass * length * length);
357 let de = (e_scale / 50.0).max((v_max - v_min + 10.0) / 200_000.0);
358 let mut e = v_min + de;
359 let mut prev_end = shoot(potential, e, dx, mass, hbar);
360
361 while eigenvalues.len() < n_states && e < v_max + 50.0 {
362 e += de;
363 let cur_end = shoot(potential, e, dx, mass, hbar);
364 if prev_end * cur_end < 0.0 {
365 let mut lo = e - de;
367 let mut hi = e;
368 for _ in 0..60 {
369 let mid = (lo + hi) / 2.0;
370 let mid_val = shoot(potential, mid, dx, mass, hbar);
371 if mid_val * shoot(potential, lo, dx, mass, hbar) < 0.0 {
372 hi = mid;
373 } else {
374 lo = mid;
375 }
376 }
377 eigenvalues.push((lo + hi) / 2.0);
378 }
379 prev_end = cur_end;
380 }
381 eigenvalues
382}
383
384fn shoot(potential: &[f64], energy: f64, dx: f64, mass: f64, hbar: f64) -> f64 {
387 let n = potential.len();
388 let coeff = 2.0 * mass / (hbar * hbar);
389 let mut psi_prev = 0.0_f64;
390 let mut psi_curr = 1e-10_f64;
391
392 for i in 1..n - 1 {
393 let k_sq = coeff * (potential[i] - energy);
394 let psi_next = 2.0 * psi_curr - psi_prev + dx * dx * k_sq * psi_curr;
395 psi_prev = psi_curr;
396 psi_curr = psi_next;
397 }
398 psi_curr
399}
400
401pub fn energy_eigenstates(
403 potential: &[f64],
404 n_states: usize,
405 dx: f64,
406 x_min: f64,
407 mass: f64,
408 hbar: f64,
409) -> Vec<WaveFunction1D> {
410 let eigenvalues = energy_eigenvalues(potential, n_states, dx, mass, hbar);
411 let n = potential.len();
412 let coeff = 2.0 * mass / (hbar * hbar);
413
414 eigenvalues
415 .iter()
416 .map(|&energy| {
417 let mut psi = vec![0.0_f64; n];
418 psi[1] = 1e-10;
419 for i in 1..n - 1 {
420 let k_sq = coeff * (potential[i] - energy);
421 psi[i + 1] = 2.0 * psi[i] - psi[i - 1] + dx * dx * k_sq * psi[i];
422 }
423 let psi_c: Vec<Complex> = psi.iter().map(|&v| Complex::new(v, 0.0)).collect();
424 let mut wf = WaveFunction1D::new(psi_c, dx, x_min);
425 wf.normalize();
426 wf
427 })
428 .collect()
429}
430
431#[derive(Clone)]
433pub struct SchrodingerSolver2D {
434 pub psi: Vec<Vec<Complex>>,
435 pub potential: Vec<Vec<f64>>,
436 pub nx: usize,
437 pub ny: usize,
438 pub dx: f64,
439 pub dy: f64,
440 pub dt: f64,
441 pub mass: f64,
442 pub hbar: f64,
443}
444
445impl SchrodingerSolver2D {
446 pub fn new(
447 psi: Vec<Vec<Complex>>,
448 potential: Vec<Vec<f64>>,
449 nx: usize,
450 ny: usize,
451 dx: f64,
452 dy: f64,
453 dt: f64,
454 mass: f64,
455 hbar: f64,
456 ) -> Self {
457 Self { psi, potential, nx, ny, dx, dy, dt, mass, hbar }
458 }
459
460 pub fn step_2d(&mut self) {
462 let nx = self.nx;
463 let ny = self.ny;
464 let rx = Complex::new(0.0, self.hbar * self.dt / (4.0 * self.mass * self.dx * self.dx));
465 let ry = Complex::new(0.0, self.hbar * self.dt / (4.0 * self.mass * self.dy * self.dy));
466
467 let mut psi_half = vec![vec![Complex::zero(); ny]; nx];
469 for j in 0..ny {
470 let mut a = vec![Complex::zero(); nx];
471 let mut b = vec![Complex::zero(); nx];
472 let mut c = vec![Complex::zero(); nx];
473 let mut d = vec![Complex::zero(); nx];
474
475 for i in 0..nx {
476 let v_term = Complex::new(0.0, self.dt * self.potential[i][j] / (4.0 * self.hbar));
477 b[i] = Complex::one() + rx * 2.0 + v_term;
478 if i > 0 { a[i] = -rx; }
479 if i < nx - 1 { c[i] = -rx; }
480
481 let psi_ij = self.psi[i][j];
482 let psi_up = if j > 0 { self.psi[i][j - 1] } else { Complex::zero() };
483 let psi_down = if j < ny - 1 { self.psi[i][j + 1] } else { Complex::zero() };
484
485 d[i] = (Complex::one() - ry * 2.0 - v_term) * psi_ij
486 + ry * psi_up
487 + ry * psi_down;
488 }
489 let sol = solve_tridiagonal(&a, &b, &c, &mut d);
490 for i in 0..nx {
491 psi_half[i][j] = sol[i];
492 }
493 }
494
495 for i in 0..nx {
497 let mut a = vec![Complex::zero(); ny];
498 let mut b = vec![Complex::zero(); ny];
499 let mut c = vec![Complex::zero(); ny];
500 let mut d = vec![Complex::zero(); ny];
501
502 for j in 0..ny {
503 let v_term = Complex::new(0.0, self.dt * self.potential[i][j] / (4.0 * self.hbar));
504 b[j] = Complex::one() + ry * 2.0 + v_term;
505 if j > 0 { a[j] = -ry; }
506 if j < ny - 1 { c[j] = -ry; }
507
508 let psi_ij = psi_half[i][j];
509 let psi_left = if i > 0 { psi_half[i - 1][j] } else { Complex::zero() };
510 let psi_right = if i < nx - 1 { psi_half[i + 1][j] } else { Complex::zero() };
511
512 d[j] = (Complex::one() - rx * 2.0 - v_term) * psi_ij
513 + rx * psi_left
514 + rx * psi_right;
515 }
516 let sol = solve_tridiagonal(&a, &b, &c, &mut d);
517 for j in 0..ny {
518 self.psi[i][j] = sol[j];
519 }
520 }
521 }
522}
523
524pub fn probability_density_1d(psi: &[Complex]) -> Vec<f64> {
526 psi.iter().map(|c| c.norm_sq()).collect()
527}
528
529pub fn probability_density_2d(psi: &[Vec<Complex>]) -> Vec<Vec<f64>> {
531 psi.iter()
532 .map(|row| row.iter().map(|c| c.norm_sq()).collect())
533 .collect()
534}
535
536pub fn normalize(psi: &mut [Complex], dx: f64) {
538 let norm_sq: f64 = psi.iter().map(|c| c.norm_sq()).sum::<f64>() * dx;
539 let norm = norm_sq.sqrt();
540 if norm > 1e-30 {
541 for c in psi.iter_mut() {
542 *c = *c / norm;
543 }
544 }
545}
546
547pub fn expectation_x(psi: &WaveFunction1D) -> f64 {
549 let mut sum = 0.0;
550 for i in 0..psi.n() {
551 let x = psi.x_at(i);
552 sum += psi.psi[i].norm_sq() * x;
553 }
554 sum * psi.dx
555}
556
557pub fn expectation_p(psi: &WaveFunction1D, hbar: f64) -> f64 {
559 let n = psi.n();
560 let dx = psi.dx;
561 let mut sum = Complex::zero();
562 for i in 1..n - 1 {
563 let dpsi = (psi.psi[i + 1] - psi.psi[i - 1]) / (2.0 * dx);
564 sum += psi.psi[i].conj() * dpsi;
565 }
566 let result = Complex::new(0.0, -hbar) * sum * dx;
567 result.re
568}
569
570pub fn uncertainty_x(psi: &WaveFunction1D) -> f64 {
572 let ex = expectation_x(psi);
573 let mut ex2 = 0.0;
574 for i in 0..psi.n() {
575 let x = psi.x_at(i);
576 ex2 += psi.psi[i].norm_sq() * x * x;
577 }
578 ex2 *= psi.dx;
579 (ex2 - ex * ex).max(0.0).sqrt()
580}
581
582pub fn uncertainty_p(psi: &WaveFunction1D, hbar: f64) -> f64 {
584 let ep = expectation_p(psi, hbar);
585 let n = psi.n();
586 let dx = psi.dx;
587
588 let mut sum = 0.0;
590 for i in 1..n - 1 {
591 let d2psi = (psi.psi[i + 1] - psi.psi[i] * 2.0 + psi.psi[i - 1]) / (dx * dx);
592 let integrand = psi.psi[i].conj() * d2psi;
593 sum += integrand.re;
594 }
595 let ep2 = -hbar * hbar * sum * dx;
596 (ep2 - ep * ep).max(0.0).sqrt()
597}
598
599#[cfg(test)]
600mod tests {
601 use super::*;
602
603 #[test]
604 fn test_complex_arithmetic() {
605 let a = Complex::new(1.0, 2.0);
606 let b = Complex::new(3.0, 4.0);
607 let sum = a + b;
608 assert!((sum.re - 4.0).abs() < 1e-10);
609 assert!((sum.im - 6.0).abs() < 1e-10);
610
611 let prod = a * b;
612 assert!((prod.re - (-5.0)).abs() < 1e-10);
613 assert!((prod.im - 10.0).abs() < 1e-10);
614
615 let div = a / b;
616 let expected_re = 11.0 / 25.0;
617 let expected_im = 2.0 / 25.0;
618 assert!((div.re - expected_re).abs() < 1e-10);
619 assert!((div.im - expected_im).abs() < 1e-10);
620 }
621
622 #[test]
623 fn test_complex_exp() {
624 let z = Complex::new(0.0, PI);
625 let result = z.exp();
626 assert!((result.re - (-1.0)).abs() < 1e-10);
627 assert!(result.im.abs() < 1e-10);
628 }
629
630 #[test]
631 fn test_complex_from_polar() {
632 let c = Complex::from_polar(2.0, PI / 4.0);
633 assert!((c.re - 2.0_f64.sqrt()).abs() < 1e-10);
634 assert!((c.im - 2.0_f64.sqrt()).abs() < 1e-10);
635 }
636
637 #[test]
638 fn test_normalization() {
639 let n = 200;
640 let dx = 0.1;
641 let x_min = -10.0;
642 let sigma = 1.0;
643 let psi: Vec<Complex> = (0..n)
644 .map(|i| {
645 let x = x_min + i as f64 * dx;
646 Complex::new((-x * x / (2.0 * sigma * sigma)).exp(), 0.0)
647 })
648 .collect();
649 let mut wf = WaveFunction1D::new(psi, dx, x_min);
650 wf.normalize();
651 let norm = wf.norm_squared();
652 assert!((norm - 1.0).abs() < 0.01);
653 }
654
655 #[test]
656 fn test_crank_nicolson_preserves_norm() {
657 let n = 128;
658 let dx = 0.1;
659 let x_min = -6.4;
660 let sigma = 1.0;
661 let psi: Vec<Complex> = (0..n)
662 .map(|i| {
663 let x = x_min + i as f64 * dx;
664 Complex::new((-x * x / (2.0 * sigma * sigma)).exp(), 0.0)
665 })
666 .collect();
667 let mut wf = WaveFunction1D::new(psi, dx, x_min);
668 wf.normalize();
669 let potential = vec![0.0; n];
670 let mut solver = SchrodingerSolver1D::new(wf, potential, 1.0, 1.0, 0.001);
671 let norm_before = solver.psi.norm_squared();
672 for _ in 0..50 {
673 solver.step();
674 }
675 let norm_after = solver.psi.norm_squared();
676 assert!((norm_after - norm_before).abs() < 0.05);
677 }
678
679 #[test]
680 fn test_split_operator_preserves_norm() {
681 let n = 64;
682 let dx = 0.2;
683 let x_min = -6.4;
684 let sigma = 1.0;
685 let psi: Vec<Complex> = (0..n)
686 .map(|i| {
687 let x = x_min + i as f64 * dx;
688 Complex::new((-x * x / (2.0 * sigma * sigma)).exp(), 0.0)
689 })
690 .collect();
691 let mut wf = WaveFunction1D::new(psi, dx, x_min);
692 wf.normalize();
693 let potential = vec![0.0; n];
694 let mut solver = SchrodingerSolver1D::new(wf, potential, 1.0, 1.0, 0.001);
695 let norm_before = solver.psi.norm_squared();
696 for _ in 0..20 {
697 solver.step_split_operator();
698 }
699 let norm_after = solver.psi.norm_squared();
700 assert!((norm_after - norm_before).abs() < 0.05);
701 }
702
703 #[test]
704 fn test_uncertainty_principle() {
705 let n = 512;
706 let dx = 0.05;
707 let x_min = -12.8;
708 let sigma = 1.0;
709 let hbar = 1.0;
710 let psi: Vec<Complex> = (0..n)
711 .map(|i| {
712 let x = x_min + i as f64 * dx;
713 Complex::new((-x * x / (4.0 * sigma * sigma)).exp(), 0.0)
714 })
715 .collect();
716 let mut wf = WaveFunction1D::new(psi, dx, x_min);
717 wf.normalize();
718 let dx_unc = uncertainty_x(&wf);
719 let dp_unc = uncertainty_p(&wf, hbar);
720 let product = dx_unc * dp_unc;
721 assert!(product >= hbar / 2.0 - 0.1, "Uncertainty product {} < hbar/2", product);
723 }
724
725 #[test]
726 fn test_dft_idft_roundtrip() {
727 let input = vec![
728 Complex::new(1.0, 0.0),
729 Complex::new(0.0, 1.0),
730 Complex::new(-1.0, 0.0),
731 Complex::new(0.0, -1.0),
732 ];
733 let transformed = dft(&input);
734 let recovered = idft(&transformed);
735 for (a, b) in input.iter().zip(recovered.iter()) {
736 assert!((a.re - b.re).abs() < 1e-10);
737 assert!((a.im - b.im).abs() < 1e-10);
738 }
739 }
740
741 #[test]
742 fn test_tridiagonal_solver() {
743 let a = [Complex::zero(), Complex::new(-1.0, 0.0), Complex::new(-1.0, 0.0)];
745 let b = [Complex::new(2.0, 0.0), Complex::new(2.0, 0.0), Complex::new(2.0, 0.0)];
746 let c = [Complex::new(-1.0, 0.0), Complex::new(-1.0, 0.0), Complex::zero()];
747 let mut d = [Complex::new(1.0, 0.0), Complex::new(0.0, 0.0), Complex::new(1.0, 0.0)];
748 let x = solve_tridiagonal(&a, &b, &c, &mut d);
749 let r0 = b[0] * x[0] + c[0] * x[1];
751 assert!((r0.re - 1.0).abs() < 1e-10);
752 }
753
754 #[test]
755 fn test_infinite_well_eigenvalues() {
756 let n = 200;
757 let l = 1.0;
758 let dx = l / (n as f64 - 1.0);
759 let hbar = 1.0;
760 let mass = 0.5; let potential: Vec<f64> = (0..n)
762 .map(|i| {
763 let x = i as f64 * dx;
764 if x < 0.01 || x > l - 0.01 { 1e6 } else { 0.0 }
765 })
766 .collect();
767 let evals = energy_eigenvalues(&potential, 3, dx, mass, hbar);
768 if evals.len() >= 2 {
770 let ratio = evals[1] / evals[0];
771 assert!((ratio - 4.0).abs() < 1.0, "Ratio: {}", ratio);
773 }
774 assert_eq!(evals.len(), 3, "should find the three lowest levels");
775 }
776
777 #[test]
778 fn test_2d_solver_runs() {
779 let nx = 16;
780 let ny = 16;
781 let psi = vec![vec![Complex::zero(); ny]; nx];
782 let potential = vec![vec![0.0; ny]; nx];
783 let mut solver = SchrodingerSolver2D::new(psi, potential, nx, ny, 0.1, 0.1, 0.001, 1.0, 1.0);
784 solver.psi[nx / 2][ny / 2] = Complex::new(1.0, 0.0);
786 solver.step_2d();
787 let center_prob = solver.psi[nx / 2][ny / 2].norm_sq();
789 assert!(center_prob < 1.1); }
791}