1D Example: Two particles with Pöschl–Teller interaction
This example demonstrates how to use the FewBodyToolkit.jl
package to compute bound states for two particles in 1D. Here we use the Pöschl–Teller interaction, since it has analytic solutions. In relative coordinates, this system is equivalent to a single particle in a potential. It is governed by the following Schrödinger equation: \[ -\frac{1}{2} \frac{d^2}{dr^2}\psi + V(r)\psi = E\psi \] with the Pöschl–Teller potential \[ V(r) = -\frac{\lambda(\lambda+1)}{2} \frac{1}{\cosh^2(r)}. \]
Setup
using Printf, Interpolations, Antique, FewBodyToolkit
Input parameters
Physical parameters
mass_arr=[1.0,Inf] # this ensures a reduced mass of 1.0
mur = 1/(1/mass_arr[1]+1/mass_arr[2]) # reduced mass
lambda=8.0
function v_poschl(r)
return -lambda*(lambda+1)/2/mur*1/cosh(r)^2
end;
We define the physical parameters as a NamedTuple
which carries the information about the Hamiltonian.
phys_params = make_phys_params2B(;mur,vint_arr=[v_poschl],dim=1)
(hbar = 1.0, mur = 1.0, vint_arr = [Main.v_poschl], lmin = 0, lmax = 0, dim = 1)
By leaving out the optional parameters, we use the defaults:
lmin = lmax = 0
: minimum and maximum angular momentum (in 1D this corresponds to even states)hbar = 1.0
: when working in dimensionless units
Numerical parameters
nmax=6 # number of Gaussian basis functions
r1=0.1;rnmax=10.0; # r1 and rnmax defining the widths of the basis functions
gem_params = (;nmax,r1,rnmax);
We define the numerical parameters as a NamedTuple
:
num_params = make_num_params2B(;gem_params)
(gem_params = (nmax = 6, r1 = 0.1, rnmax = 10.0), theta_csm = 0.0, omega_cr = 0.9, threshold = 1.0e-8)
1. Numerical solution
We solve the two-body system by calling GEM2B_solve
.
energies_arr = GEM2B_solve(phys_params,num_params)
6-element Vector{Float64}:
-31.932496613454482
-15.805972074522492
-6.501477313720615
-0.6324540023353372
0.030563997664721954
101.2561984619004
Determine the number of bound states
simax = findlast(energies_arr.<0)
4
The Pöschl–Teller potential has lambda = 8
eigenvalues. In this example we focus on the even states, hence there are only 4 bound states. Their energies can be found exactly: \[ E_n = -\frac{(\lambda-n)^2}{2\mu} \] where \( n = 1, 2, ... , \lambda-1 \) is the state index. The package Antique.jl provides these exact energies in a convenient way.
PT = Antique.PoschlTeller(λ=8)
ex_arr = [Antique.E(PT,n=i) for i=0:2:Int(floor(lambda-1))]
println("1. Numerical solution of the 1D problem:")
comparison(energies_arr, ex_arr, simax)
1. Numerical solution of the 1D problem:
Index Numerical Reference Difference
1 -31.932497 -32.000000 -0.067503
2 -15.805972 -18.000000 -2.194028
3 -6.501477 -8.000000 -1.498523
4 -0.632454 -2.000000 -1.367546
So far, the numerical solutions are not very accurate. This is because the basis parmeters are not optimal.
2. Optimization of basis parameters
We can optimize the basis parameters for a specific state indicated by stateindex
using GEM_Optim_2B
.
stateindex = 4 # which state to optimize for
params_opt = GEM2B.GEM_Optim_2B(phys_params, num_params, stateindex)
gem_params_opt = (;nmax, r1 = params_opt[1], rnmax = params_opt[2])
num_params_opt = make_num_params2B(;gem_params=gem_params_opt)
e2_opt = GEM2B.GEM2B_solve(phys_params,num_params_opt)
println("\n2. Optimization of GEM parameters for E2[$stateindex]:")
@printf("%-15s %-15s %-15s %-15s %-15s\n", "r1", "rnmax", "E2[$(stateindex-1)]", "E2[$stateindex]", "E2[$(stateindex+1)]")
println("Before optimization:")
@printf("%-15.6f %-15.6f %-15.6f %-15.6f %-15.6f\n", gem_params.r1, gem_params.rnmax, energies_arr[stateindex-1], energies_arr[stateindex], energies_arr[stateindex+1])
println("After optimization:")
@printf("%-15.6f %-15.6f %-15.6f %-15.6f %-15.6f\n", gem_params_opt.r1, gem_params_opt.rnmax, e2_opt[stateindex-1], e2_opt[stateindex], e2_opt[stateindex+1])
2. Optimization of GEM parameters for E2[4]:
r1 rnmax E2[3] E2[4] E2[5]
Before optimization:
0.100000 10.000000 -6.501477 -0.632454 0.030564
After optimization:
0.276789 1.960739 -7.991911 -1.999891 0.618960
With the optimized parameters, the exact energies are reproduced very well, using only 6 basis functions.
comparison(e2_opt, ex_arr, simax; s1="Optimized")
Index Optimized Reference Difference
1 -31.999856 -32.000000 -0.000144
2 -17.994518 -18.000000 -0.005482
3 -7.991911 -8.000000 -0.008089
4 -1.999891 -2.000000 -0.000109
3. Using an interpolated interaction
We can also create a potential from interpolated data.
r_arr = -10.0:0.5:10.0
v_arr = v_poschl.(r_arr)
v_interpol = cubic_spline_interpolation(r_arr,v_arr,extrapolation_bc=Line())
v_int(r) = v_interpol(r); # we have to transform the interaction to an object of type "function"
As input to the solver we need to define new physical parameters with the interpolated interaction. Moreover, we use the optimized numerical parameters from the previous step.
phys_params_interpol = make_phys_params2B(;mur,vint_arr=[v_int],dim=1)
println("\n3. Numerical solution using an interpolated interaction:")
energies_interpol = GEM2B_solve(phys_params_interpol,num_params_opt)
comparison(energies_interpol, e2_opt, simax;s1="Interpolated", s2="Optimized")
3. Numerical solution using an interpolated interaction:
Index Interpolated Optimized Difference
1 -31.954138 -31.999856 -0.045718
2 -18.029918 -17.994518 0.035400
3 -7.988909 -7.991911 -0.003002
4 -2.001690 -1.999891 0.001799
4. Inverse problem: Tuning the potential strength
We can use v0GEMOptim
to scale the interaction such that the state indicated by stateindex
has a fixed energy target_e2
. At the same time, the basis parameters are optimized for this state.
stateindex = 3; target_e2 = -18.0;
println("\n4. Scaling the potential such that E2[$stateindex] = $target_e2:")
phys_params_scaled,num_params_scaled,vscale = GEM2B.v0GEMOptim(phys_params,num_params,stateindex,target_e2)
e2_v0 = GEM2B.GEM2B_solve(phys_params_scaled,num_params_scaled)
println("After scaling:")
@printf("%-15s %-15s %-15s %-15s %-15s\n", "r1", "rnmax", "E2[$(stateindex-1)]", "E2[$stateindex]", "E2[$(stateindex+1)]")
@printf("%-15.6f %-15.6f %-15.6f %-15.6f %-15.6f\n", num_params_scaled.gem_params.r1, num_params_scaled.gem_params.rnmax, e2_v0[stateindex-1], e2_v0[stateindex], e2_v0[stateindex+1])
4. Scaling the potential such that E2[3] = -18.0:
After scaling:
r1 rnmax E2[2] E2[3] E2[4]
0.360712 0.930744 -32.000002 -18.000000 -7.984015
Here, we scale the potential such that the energy of the state with stateindex = 3
is equal to target_e2 = -18.0
. For this special potential, this corresponds to increasing the number of states, and therefore lambda
by 2. Hence, the we expect the scaling factor to be approximately (lambda+2)*(lambda+2+1)/(lambda*(lambda+1))
println("vscale = $(round(vscale,digits=8)) should be approximately (λ+2)*(λ+2+1)/(λ*(λ+1)) = ", round((lambda+2)*(lambda+2+1)/(lambda*(lambda+1)),digits=8) )
vscale = 1.52777784 should be approximately (λ+2)*(λ+2+1)/(λ*(λ+1)) = 1.52777778
This page was generated using Literate.jl.