numerics
Loading...
Searching...
No Matches
tdse_solver.hpp
Go to the documentation of this file.
1/// @file tdse_solver.hpp
2/// @brief 2-D Time-Dependent Schrödinger Equation solver
3///
4/// Algorithm: Strang operator splitting
5/// e^{-iHdt} ~= e^{-iVdt/2} * e^{-iTx*dt/2} * e^{-iTy*dt} * e^{-iTx*dt/2} * e^{-iVdt/2}
6///
7/// Each kinetic sub-step uses Crank-Nicolson -> complex tridiagonal solve (Thomas algorithm).
8/// Potential kick is a diagonal phase multiplication: psi *= exp(-i*V*tau).
9///
10/// Eigenstate computation uses num::lanczos on the real Hamiltonian matrix (H is real
11/// symmetric for real V, so eigenstates are real).
12///
13/// Bessel function zeros (for CircularWell exact eigenvalues) are found via num::brent.
14/// Norm/energy observables use num::gauss_legendre on radial marginals.
15///
16/// Grid: NxN interior points, domain [0,L]x[0,L], Dirichlet BCs (psi=0 on boundary).
17/// Storage: row-major idx = i*N + j, i = row (x), j = col (y).
18#pragma once
19
20#include "core/types.hpp"
21#include "core/vector.hpp"
22#include "pde/adi.hpp"
23#include <vector>
24#include <complex>
25#include <cmath>
26
27namespace tdse {
28
29// Potential types
30
31enum class Potential {
32 Free, ///< V = 0 (free particle)
33 Barrier, ///< Single vertical barrier with one gap -- tunnelling
34 DoubleSlit, ///< Double-slit wall -- interference
35 Harmonic, ///< V = 1/2*omega^2*r^2 (coherent state dynamics)
36 CircularWell, ///< V = 0 inside radius R, V = V0 outside (Bessel eigenstates)
37};
38
39inline const char* potential_name(Potential p) {
40 switch (p) {
41 case Potential::Free: return "Free particle";
42 case Potential::Barrier: return "Single barrier";
43 case Potential::DoubleSlit: return "Double slit";
44 case Potential::Harmonic: return "Harmonic oscillator";
45 case Potential::CircularWell:return "Circular well";
46 }
47 return "?";
48}
49
50// Eigenmode (real, from Lanczos)
51
52struct EigenMode {
53 double energy; ///< Ritz eigenvalue (~= energy)
54 std::vector<double> phi; ///< NxN real wavefunction, L^2-normalised
55 double exact_energy = -1; ///< Analytical eigenvalue if available (CircularWell)
56};
57
58// Stats
59
60struct Stats {
61 double step_ms = 0; ///< Wall time for one step()
62 double norm = 1; ///< integral|psi|^2 dA (should stay ~= 1)
63 double energy = 0; ///< <H> = integralpsi* H psi dA
64 int n_modes = 0; ///< Number of computed eigenmodes
65};
66
67
68// TDSESolver
69
71public:
72 int N; ///< Interior grid points per axis
73 double L; ///< Domain length ([0,L]x[0,L])
74 double h; ///< Grid spacing = L/(N+1)
75 double dt; ///< Time step
76
77 num::CVector psi; ///< NxN wavefunction (complex, length N^2)
78 num::Vector V; ///< NxN potential (real, length N^2)
79
80 std::vector<EigenMode> modes;
81 bool modes_ready = false;
82
84
85
86 TDSESolver(int N, double L, double dt);
87
88 /// Build potential and recompute Thomas factorisations.
89 void set_potential(Potential p, double param = 0.0);
90
91 /// Gaussian wavepacket: psi = A*exp(-(r-r0)^2/sigma^2)*exp(i*k*r), then normalised.
92 void init_gaussian(double x0, double y0, double kx, double ky, double sigma);
93
94 /// Set psi to the k-th eigenmode (modes must be computed first).
95 void set_mode(int k);
96
97
98 /// Advance one full time step (Strang splitting).
99 void step();
100
101
102 /// Run Lanczos to find the k lowest eigenstates of H.
103 /// For CircularWell also fills EigenMode::exact_energy via Bessel zero search.
104 void compute_modes(int k = 8);
105
106
107 inline int at(int i, int j) const { return i * N + j; }
108
109 double prob(int i, int j) const {
110 const auto z = psi[at(i,j)];
111 return z.real()*z.real() + z.imag()*z.imag();
112 }
113 double phase_ang(int i, int j) const { return std::arg(psi[at(i,j)]); }
114
115 /// integral|psi|^2 dA (Gauss-Legendre over each grid strip).
116 double compute_norm() const;
117
118 /// <psi|H|psi> using 5-point Laplacian and the potential.
119 double compute_energy() const;
120
121 void renormalize();
122
123private:
124
125 void kick_V(double tau); ///< psi *= exp(-i*V*tau)
126 void sweep_x(double tau); ///< CN in x (col_fiber_sweep)
127 void sweep_y(double tau); ///< CN in y (row_fiber_sweep)
128
129 num::CrankNicolsonADI adi_; ///< Prefactored CN tridiagonals for Strang splitting
130
131 void refactor_(); ///< Rebuild ADI factorisations from h and dt
132
133 Potential current_potential_ = Potential::Free;
134};
135
136} // namespace tdse
Crank-Nicolson ADI solver for 2D parabolic equations via fiber sweeps.
Dense vector with optional GPU storage, templated over scalar type T.
Definition vector.hpp:23
double dt
Time step.
int N
Interior grid points per axis.
num::Vector V
NxN potential (real, length N^2)
double L
Domain length ([0,L]x[0,L])
double compute_energy() const
<psi|H|psi> using 5-point Laplacian and the potential.
void set_mode(int k)
Set psi to the k-th eigenmode (modes must be computed first).
void init_gaussian(double x0, double y0, double kx, double ky, double sigma)
Gaussian wavepacket: psi = A*exp(-(r-r0)^2/sigma^2)*exp(i*k*r), then normalised.
std::vector< EigenMode > modes
void compute_modes(int k=8)
double prob(int i, int j) const
double h
Grid spacing = L/(N+1)
num::CVector psi
NxN wavefunction (complex, length N^2)
void set_potential(Potential p, double param=0.0)
Build potential and recompute Thomas factorisations.
double compute_norm() const
integral|psi|^2 dA (Gauss-Legendre over each grid strip).
int at(int i, int j) const
double phase_ang(int i, int j) const
void step()
Advance one full time step (Strang splitting).
Core type definitions.
@ DoubleSlit
Double-slit wall – interference.
@ Barrier
Single vertical barrier with one gap – tunnelling.
@ CircularWell
V = 0 inside radius R, V = V0 outside (Bessel eigenstates)
@ Harmonic
V = 1/2*omega^2*r^2 (coherent state dynamics)
@ Free
V = 0 (free particle)
const char * potential_name(Potential p)
double exact_energy
Analytical eigenvalue if available (CircularWell)
std::vector< double > phi
NxN real wavefunction, L^2-normalised.
double energy
Ritz eigenvalue (~= energy)
double norm
integral|psi|^2 dA (should stay ~= 1)
int n_modes
Number of computed eigenmodes.
double step_ms
Wall time for one step()
double energy
<H> = integralpsi* H psi dA
Vector operations.