Created
November 29, 2025 15:42
-
-
Save marl0ny/ccfc6d5d2032e56d0e985f61fbf1ebd7 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| /* Numerically find the lowest energy bound states for a potential in 3D. | |
| This uses a uniform cartesian grid finite difference approximation, where a | |
| second-order accurate stencil is used for the Laplacian. | |
| Dirichlet boundary conditions are imposed. | |
| The eigenvalue problem is solved using Spectra (https://spectralib.org/), | |
| and Eigen (https://eigen.tuxfamily.org/index.php?title=Main_Page). | |
| If Spectra and Eigen are in the same directory as this file, compile with: | |
| clang++ -std=c++17 -O3 -I${PWD} -c energy_eigenstates3d.cpp; | |
| clang++ energy_eigenstates3d.o -o ees3d | |
| Run the program with: | |
| `./ees3d` | |
| */ | |
| #include <Eigen/Core> | |
| #include <Eigen/Sparse> | |
| #include <Spectra/SymEigsSolver.h> | |
| #include <Spectra/SymEigsShiftSolver.h> | |
| #include <Spectra/MatOp/SparseSymShiftSolve.h> | |
| #include <Spectra/MatOp/SparseGenMatProd.h> | |
| #include <iostream> | |
| #define FPTYPE float | |
| using complex = std::complex<FPTYPE>; | |
| using namespace Eigen; | |
| using namespace Spectra; | |
| struct Settings { | |
| enum { | |
| LAPLACIAN_2ND_OR_7_PT=0, | |
| }; | |
| FPTYPE hbar, m; | |
| FPTYPE width, height, length; | |
| FPTYPE global_shift; | |
| int grid_width, grid_height, grid_length; | |
| int n_states; | |
| int laplacian_stencil; | |
| }; | |
| static void construct_hamiltonian_2nd_order_7pt( | |
| std::vector<Triplet<FPTYPE>> &triplets, | |
| std::vector<FPTYPE> &potential, | |
| Settings &settings | |
| ) { | |
| triplets.reserve( | |
| settings.grid_width*settings.grid_height*settings.grid_length*7); | |
| FPTYPE dx = settings.width/FPTYPE(settings.grid_width); | |
| FPTYPE dy = settings.height/FPTYPE(settings.grid_height); | |
| FPTYPE dz = settings.length/FPTYPE(settings.grid_length); | |
| FPTYPE hbar = settings.hbar; | |
| FPTYPE m = settings.m; | |
| FPTYPE global_shift = settings.global_shift; | |
| int grid_width = settings.grid_width; | |
| int grid_height = settings.grid_height; | |
| int grid_length = settings.grid_length; | |
| #define AT(i, j, k) (i)*grid_height*grid_width + (j)*grid_width + (k) | |
| for (int i = 0; i < grid_length; i++) { | |
| for (int j = 0; j < grid_height; j++) { | |
| for (int k = 0; k < grid_width; k++) { | |
| FPTYPE dx2 = dx*dx, dy2 = dy*dy, dz2 = dz*dz; | |
| FPTYPE hbar2_2m = hbar*hbar/(2.0*m); | |
| FPTYPE potential_val = potential[AT(i, j, k)] + global_shift; | |
| triplets.push_back( | |
| { | |
| AT(i, j, k), AT(i, j, k), | |
| -2.0*(-hbar2_2m/dx2 - hbar2_2m/dy2 - hbar2_2m/dz2) | |
| + potential_val | |
| } | |
| ); | |
| if (i < grid_length - 1) | |
| triplets.push_back( | |
| {AT(i, j, k), AT(i+1, j, k), -hbar2_2m/dz2}); | |
| if (i > 0) | |
| triplets.push_back( | |
| {AT(i, j, k), AT(i-1, j, k), -hbar2_2m/dz2}); | |
| if (j < grid_height - 1) | |
| triplets.push_back( | |
| {AT(i, j, k), AT(i, j+1, k), -hbar2_2m/dy2}); | |
| if (j > 0) | |
| triplets.push_back( | |
| {AT(i, j, k), AT(i, j-1, k), -hbar2_2m/dy2}); | |
| if (k < grid_height - 1) | |
| triplets.push_back( | |
| {AT(i, j, k), AT(i, j, k+1), -hbar2_2m/dx2}); | |
| if (k > 0) | |
| triplets.push_back( | |
| {AT(i, j, k), AT(i, j, k-1), -hbar2_2m/dx2}); | |
| } | |
| } | |
| } | |
| #undef AT | |
| } | |
| int main(int argc, char **argv) { | |
| Settings settings = { | |
| .hbar=1.0, .m=1.0, | |
| .width=10.0, .height=10.0, .length=10.0, | |
| .global_shift=0.0, | |
| .grid_width=32, .grid_height=32, .grid_length=32, | |
| .n_states=4, | |
| .laplacian_stencil=Settings::LAPLACIAN_2ND_OR_7_PT | |
| }; | |
| // For a grid size of 50^3, expect to wait almost two minutes to find | |
| // the lowest four energy eigenvalues. | |
| if (argc > 1) { | |
| settings.grid_width = std::stoi(argv[1]); | |
| settings.grid_height = settings.grid_width; | |
| settings.grid_length = settings.grid_width; | |
| } | |
| if (argc > 2) | |
| settings.n_states = std::stoi(argv[2]); | |
| size_t matrix_size | |
| = settings.grid_width*settings.grid_height*settings.grid_length; | |
| std::vector<Triplet<FPTYPE>> triplets; | |
| std::vector<FPTYPE> potential(matrix_size, 0.0); | |
| int grid_width = settings.grid_width, grid_height = settings.grid_height, | |
| grid_length = settings.grid_length; | |
| FPTYPE width = settings.width, height = settings.height, | |
| length = settings.length; | |
| #define AT(i, j, k) (i)*grid_height*grid_width + (j)*grid_width + (k) | |
| for (int i = 0; i < settings.grid_length; i++) { | |
| for (int j = 0; j < settings.grid_height; j++) { | |
| for (int k = 0; k < settings.grid_width; k++) { | |
| FPTYPE x = width*(k + 0.5)/grid_width - width/2.0; | |
| FPTYPE y = height*(j + 0.5)/grid_height - height/2.0; | |
| FPTYPE z = length*(i + 0.5)/grid_length - length/2.0; | |
| potential[AT(i, j, k)] = x*x + y*y + z*z; | |
| } | |
| } | |
| } | |
| #undef AT | |
| construct_hamiltonian_2nd_order_7pt(triplets, potential, settings); | |
| size_t reserve_size = 7; | |
| SparseMatrix<FPTYPE> mat(matrix_size, matrix_size); | |
| mat.reserve(VectorXi::Constant(matrix_size, reserve_size)); | |
| mat.setFromTriplets(triplets.begin(), triplets.end()); | |
| mat.makeCompressed(); | |
| SparseSymShiftSolve<FPTYPE> op(mat); | |
| SymEigsShiftSolver<SparseSymShiftSolve<FPTYPE>> eigs( | |
| op, settings.n_states, | |
| int(settings.n_states*1.25), 0.0); | |
| eigs.init(); | |
| int nconv = eigs.compute(SortRule::LargestMagn); | |
| VectorX<complex> eigenvalues; | |
| if (eigs.info() == CompInfo::Successful) { | |
| eigenvalues = eigs.eigenvalues(); | |
| } else { | |
| fprintf(stderr, "Unable to compute eigenvalues and eigenvectors.\n"); | |
| } | |
| std::cout << | |
| "Numerical computation of the lowest energies for " | |
| "V(x, y, z) = x^2 + y^2 + z^2:" | |
| << std::endl; | |
| for (auto &e: eigenvalues) | |
| std::cout << e << std::endl; | |
| std::cout << "nconv: " << nconv << std::endl; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment