Search code examples
c++physicsnumerical-methodsodeodeint

Why does my solution for the quantum harmonic oscillator blow up?


I am using odeint to solve for the energy levels of the QHO (Griffiths problem 2.55).

I am integrating from x=0 to x=3. When I plot the results, I expect to see half of a gaussian with a tail that explodes towards positive or negative infinity, depending on whether I set the energy parameter to be above or below a valid energy level.

Instead, my solution blows up to positive infinity right away, and will not show any other behavior.

Here is my code, including my derivation of the system of ODEs in a comment:

#include <boost/numeric/odeint.hpp>
#include <cmath>
#include <vector>
#include "print.hpp"

namespace ode = boost::numeric::odeint;

//constexpr auto ℏ = 6.582119569e-16; // eV·Hz⁻¹
constexpr auto ℏ = 1.0;

int main(int argc, char** argv) {
  constexpr static auto mass = 1.0;
  constexpr static auto frequency = 2.0;
  constexpr static auto energy = 0.99 * 0.5*ℏ*frequency;

  const auto& m = mass;
  const auto& ω = frequency;
  const auto& Ε = energy;

  using State = std::vector<double>;

  auto Ψ₀ = State{ 1.0, 0.0 };
  auto x₀ = 0.0;
  auto x₁ = 3.0;
  auto Δ₀x = 1e-2;

  ode::integrate(
      [](const State& q, State& dqdx, const double x) {
        // convert schrödinger eqn into system of 1st order ode:
        // (-ℏ²/2m)(∂²Ψ/∂x) + ½mω²x²Ψ = EΨ
        // ⇒ { (-ℏ²/2m)(∂Ψ'/∂x) + ½mω²x²Ψ = EΨ
        //   , ψ' = ∂Ψ/∂x
        //   }
        // ⇒ { ∂Ψ'/∂x = (EΨ - ½mω²x²Ψ)/(-ℏ²/2m)
        //   , ∂Ψ/∂x = ψ'
        //   }
        // ⇒ { ∂Ψ'/∂x = ((E-½mω²x²)/(-ℏ²/2m))Ψ
        //   , ∂Ψ/∂x = Ψ'
        //   }
        auto& dΨdx = dqdx[0];
        auto& d²Ψdx² = dqdx[1];

        const auto& Ψ = q[0];

        dΨdx = q[1];
        d²Ψdx² = (std::pow(m*ω*x/ℏ, 2) - Ε) * Ψ;
      },
      Ψ₀,
      x₀, x₁, Δ₀x,
      [](const auto& q, auto x) {
        std::cout << x << " → " << q << std::endl;
      });
}

Here is some example output:

    x                        Ψ          Ψ'
    0                        1         0
    0.01                     0.999951  -0.0098985
    0.055                    0.998506  -0.0542012
    0.2575                   0.968801  -0.229886
    0.406848                 0.927982  -0.306824
    0.552841                 0.881662  -0.315318
    0.698835                 0.839878  -0.242402
    0.825922                 0.817189  -0.101718
    0.953009                 0.817616  0.124082
    1.0801                   0.853256  0.457388
    1.20718                  0.940137  0.939688
    1.31092                  1.06489   1.495
    1.41925                  1.26832   2.30939
    1.50629                  1.50698   3.22125
    1.59738                  1.85714   4.54112
    1.67542                  2.2693    6.10168
    1.75345                  2.82426   8.23418
    1.83149                  3.57561   11.1845
    1.89812                  4.42976   14.6191
    1.96476                  5.55      19.2346
    2.03139                  7.02934   25.4872
    2.09803                  8.99722   34.0259
    2.15585                  11.2396   43.9977
    2.21367                  14.1481   57.2333
    2.2715                   17.9436   74.9054
    2.32932                  22.9271   98.6414
    2.38714                  29.5111   130.712
    2.43818                  37.1021   168.461
    2.48922                  46.9104   218.185
    2.54026                  59.6467   283.99
    2.5913                   76.2675   371.487
    2.64234                  98.0659   488.377
    2.69338                  126.798   645.271
    2.73898                  160.271   831.155
    2.78458                  203.477   1074.9
    2.83018                  259.47    1395.74
    2.87578                  332.33    1819.67
    2.92138                  427.52    2381.96
    2.96698                  552.389   3130.66
    3                        666.846   3825.59                                 

Why does the output not match my expectations?

edit: here is an ascii version of the code in case anyone has issues with unicode:

    #include <boost/numeric/odeint.hpp>
    #include <cmath>
    #include <vector>
    
    namespace ode = boost::numeric::odeint;

    constexpr auto hbar = 1.0;

    int main(int argc, char** argv) {
      constexpr static auto mass = 1.0;
      constexpr static auto frequency = 2.0;
      constexpr static auto energy = 0.99 * 0.5*hbar*frequency;

      using State = std::vector<double>;

      auto state_init = State{ 1.0, 0.0 };
      auto x_init = 0.0;
      auto x_final = 3.0;
      auto x_step_init = 1e-2;

      ode::integrate(
          [](const State& q, State& dqdx, const double x) {
            auto& dPsi_dx = dqdx[0];
            auto& d2Psi_dx2 = dqdx[1];

            const auto& psi = q[0];

            dPsi_dx = q[1];
            d2Psi_dx2 = (std::pow(mass*frequency*x/hbar, 2) - energy) * psi;
          },
          state_init,
          x_init, x_final, x_step_init,
          [](const auto& q, auto x) {
            std::cout << x << ", " << q[0] << "," << q[1] << std::endl;
          });
    }

Solution

  • This is not a coding error but an error in transforming the equations. After the last equation you have in the comments, you need another step

     ⇒  ∂²Ψ/∂x² = (EΨ - ½mω²x²Ψ)/(-ℏ²/2m)
     ⇒  ∂²Ψ/∂x² = ((mωx)²-2mE/ℏ²)Ψ
    

    Note that the constant in the factor is different from the one you use in code.


    Mathematically, y''=C·(x²-a²) is in an oscillating regime for |x|<a and in an exponential regime for |x|>a. The oscillating frequency depends on the size of the factor C·(a²-x²), so if a is small the frequency is small and the wave length can be larger than 2a so that there is no guarantee for a root. For a large enough the frequency around x=0 will become large enough so that zeros close to x=0 are guaranteed. Somewhere in-between is the lowest energy of an eigenstate.

    By omitting the factor the energy used was shifted below the lowest eigenstate, so that only the exponential behavior was visible.