#!/usr/bin/env ruby
#  Harmonic oscillator in quantum mechanics
#
#  This example is taken from "eigen1.cpp"
#  in "Numerische Physik" p214-216 (Springer).
#
#  Reference:
#    "Numerische Physik", by Harald Wiedemann, Springer (2004)
#    ISBN: 3-540-40774-X
#    http://www.springeronline.com/sgw/cda/frontpage/0,10735,1-102-22-22345455-0,00.html
require("gsl")

STDERR.puts("Harmonic oscillator in quantum mechanics:")

# Values used in the book:
#NMAX = 512
#dx = 0.02

# These are much faster:
NMAX = 64
dx = 0.16

# Potential
V = GSL::Vector[NMAX]
for n in 0...NMAX do
  x = (n - NMAX/2)*dx
  V[n] = 0.5*x*x
end

# Hamiltonian
H = GSL::Matrix.calloc(NMAX, NMAX)
H.set_diagonal(1.0/dx/dx + V)
tmp = -0.5/dx/dx
for n1 in 1...NMAX do
  H[n1-1,n1] = tmp
  H[n1,n1-1] = tmp
end
for n1 in 0...(NMAX-1) do
  H[n1+1,n1] = tmp
  H[n1,n1+1] = tmp
end

# Calculate eigen values and eigen vectors
STDERR.print("  Solving the eigen system of #{NMAX}X#{NMAX} dimensions...")
STDERR.flush
eval, evec = H.eigen_symmv
GSL::Eigen.symmv_sort(eval, evec, GSL::Eigen::SORT_VAL_ASC)
STDERR.puts("OK")
STDERR.flush

x2 = GSL::Vector[NMAX]
for n1 in 0...NMAX do
  x2[n1] = 0
  for n2 in 0...NMAX do
    x = (n2 - NMAX/2)*dx
    x2[n1] += GSL::pow_2(evec[n2,n1]*x)
  end
end

# Energy eigen values, see p217 "Tabelle 5.1"
# The differences with Tabelle 5.1 are from NMAX and dx.
# If we use NMAX=512 and dx=0.02, we obtain the same results (but much slower).
STDERR.puts("  Eigen values:")
STDERR.printf("  %2s Exact %5s %10s | %2s Exact %5s %10s\n", 
              "n", "E", "err\(\%\)", "n", "E", "err\(\%\)")
STDERR.print("  -----------------------------------------------------\n")
for n1 in 0..6 do
  exact1 = n1 + 0.5
  exact2 = n1 + 7 + 0.5
  STDERR.printf("  %2d %4.1f %8.5f %+7.5f | %2d %4.1f %8.5f %+7.5f\n", 
                n1, exact1, eval[n1], (exact1 - eval[n1])/exact1*100,
                n1+7, exact2, eval[n1+7], (exact2-eval[n1+7])/exact2*100)
end
STDERR.flush

# Eigen vectors of n = 0, 1, 2, 10. See p217 "Abb 5.3"
c = Math::sqrt(1.0/dx)
vec0 = evec.col(0).scale(c)    
vec1 = evec.col(1).scale(c)
vec2 = evec.col(2).scale(c)
vec10 = evec.col(10).scale(c)
File.open("qhoscillator.dat", "w") do |fp|
  for i in 0...NMAX do
    x = (i - NMAX/2)*dx
    fp.printf("%e %e %e %e %e\n", x, -vec0[i], vec1[i], -vec2[i], -vec10[i])
  end
end
system("gnuplot -persist qhoscillator.gp")
File.delete("qhoscillator.dat")

