谐振子的分裂算子 FFT 量子动力学

计算科学 量子力学
2021-12-18 05:16:06

我想使用分裂算子方法对谐振子中的位移高斯进行数值量子动力学(有关算法,请参见Hal Evans 的这些注释的底部)。

我对步骤 (3) 有疑问。我不确定如何表示动量项它只是循环的索引吗?p2

创建初始状态:

  unsigned int n=1400; ///> rank of the state vector psi(x). (x_0, x_1, ..., x_1399)^T
  const double dx=0.01;///> step of the state vector psi(x). dx = x_1-x_0;

  array1<Complex> psix(n,align); ///> array1 is the array class from fftw package that is wrapped into fftwpp. psi(x) is the state vector in the position space

  double x0 = 4; ///> the mean (center) of the gaussian of the initial state.

  for(unsigned int i=0; i < n; i++) { ///> go over all points in the position space
    double x = (i-0.5*n)*dx; ///> for each point, calculate x
    x+=x0; ///> shift the gaussian back by x0
    psix[i]=exp(-pow(x,2)); ///> create the gaussian for the initial state
  }

这是执行上述算法的代码部分。

  /// step 1
  for (size_t i = 0; i < n; ++i) {
    double x = (i-0.5*n)*dx; ///> get value of x for each element of position space vector
    ax[i]=exp(-ii*u(x)*dt*0.5*invhb)*psix[i];///> propagate half of the step using potential energy operator U:  a(x)=e^(-i*u*dt/(2*hb))*s(x,t)
  }
  /// step 2
  Forward.fft(ax,fs);///> do a fft into momentum space.
  /// step 3
  for (size_t i = 0; i < n; ++i) {
    double p=i;///> THE PROBLEM IS HERE!!! , assign the value of the momentum to be used in kinetic energy operator. I just used the omega from http://en.wikipedia.org/wiki/Discrete_Fourier_transform#Definition
    bp[i]=exp(-ii*p*p*dt*0.5*invm)*fs[i];///> B(p) ≡ e^( −i p2 Δt / 2m) A(p) ///> act with a kinetic energy operator onto the momentum-space state vector.
  }
  /// step 4
  Backward.fftNormalized(bp,gfs);///> inverse fourier transform back into the position space
  /// step 5
  for (size_t i = 0; i < n; ++i) {
    double x = (i-0.5*n)*dx;///> convert indices of the position-space state vector into values of position
    psix[i]=exp(-ii*u(x)*dt*0.5*invhb)*gfs[i];///> propagate with potential energy operator through the other half-step: psi(x, t+Δt)  =  e^(−i U Δt / 2ℏ) B(x)
  }

这是我完整的 C++ 代码(基于 fftwpp 包装器示例 到 fftw 的教程):

#include <limits>
#include <vector>
#include <complex>
//#include "gnuplot_i.hpp"
#include "Array.h"
#include "fftw++.h"

// instal fftw and 
// Compile with:
// g++ -std=c++11 -I fftw++-1.13 -fopenmp main.cc fftw++-1.13/fftw++.cc -lfftw3 -lfftw3_omp; ./a.out

using namespace std;
using namespace Array;
using namespace fftwpp;

double u(double x) {
  return x*x;
}
int main()
{
  fftw::maxthreads=get_max_threads();
  const Complex ii(0.0,1.0);
  const double invhb = 1.0;
  const double invm = 1.0;

  unsigned int n=1400;
  const double dx=0.01;
  const double dt=0.000001;
  unsigned int np=n/2+1;
  size_t align=sizeof(Complex);

  array1<Complex> psix(n,align);
  array1<Complex> fs(n,align);
  array1<Complex> gfs(n,align);

  fft1d Forward(n,-1,psix,fs);
  fft1d Backward(n,1,fs,gfs);

  double x0 = 4;

  for(unsigned int i=0; i < n; i++) {
    double x = (i-0.5*n)*dx;
    x+=x0;
    psix[i]=exp(-pow(x,2));
  }

  ofstream ofs("psix.dat");
  for (size_t i = 0; i < n; ++i) {
    double x = (i-0.5*n)*dx;
    ofs << x << "  " << real(psix[i]) << "  " << imag(psix[i]) << "\n";
  }

  int tn=10;
  //// following http://hep.physics.indiana.edu/~hgevans/p411-p610/material/08_pde_iv/schrodinger.html
  array1<Complex> ax(n,align); ///> a(x)=e^(-i*u*dt/(2*hb))*s(x,t)
  array1<Complex> bp(n,align); ///> B(p) ≡ e^( −i p2 Δt / 2m) A(p)
  for (size_t l = 0; l < 100; ++l) {
    for (size_t j = 0; j < tn; ++j) {
      /// step 1
      for (size_t i = 0; i < n; ++i) {
        double x = (i-0.5*n)*dx;
        ax[i]=exp(-ii*u(x)*dt*0.5*invhb)*psix[i];///> a(x)=e^(-i*u*dt/(2*hb))*s(x,t)
      }
      /// step 2
      Forward.fft(ax,fs);
      /// step 3
      for (size_t i = 0; i < n; ++i) {
        double p=i;
        //double p=i*dx;
        bp[i]=exp(-ii*p*p*dt*0.5*invm)*fs[i];///> B(p) ≡ e^( −i p2 Δt / 2m) A(p)
      }
      /// step 4
      Backward.fftNormalized(bp,gfs);
      /// step 5
      for (size_t i = 0; i < n; ++i) {
        double x = (i-0.5*n)*dx;
        psix[i]=exp(-ii*u(x)*dt*0.5*invhb)*gfs[i];///>psi(x, t+Δt)  =  e^(−i U Δt / 2ℏ) B(x)
      }
    }
    //// to plot gnuplots on the fly using gnuplot++, uncomment the lines below.
    //vector<double> xv,psir;
    //for (size_t i = 0; i < n; ++i) {
    //  xv.push_back((i-0.5*n)*dx);
    //  psir.push_back(real(psix[i]));
    //}
    //Gnuplot g("lines");
    //g.plot_xy(xv,psir);
    //std::cin.ignore( std::numeric_limits <std::streamsize> ::max(), '\n' );
  }

  ofstream offs("fs.dat");
  for (size_t i = 0; i < n; ++i) {
    double p=i*dx;
    offs << p << "  " << real(fs[i]) << "  " << imag(fs[i]) << "\n";
  }

  ofstream ofgfs("psixt.dat");
  for (size_t i = 0; i < n; ++i) {
    double x = (i-0.5*n)*dx;
    ofgfs << x << "  " << real(psix[i]) << "  " << imag(psix[i]) << "\n";
  }
}
1个回答

我没有看过你的代码,这不是它的地方(尽管计算科学可能是),但你的问题本质上看起来很简单。

您已经将波函数转换为动量空间表示,并且您希望使用哈密顿的动能部分来传播它。然后你被要求ψ(x)A(p)p2/2m

在动量空间中计算

B(p)eip2Δt/2mA(p)

这里是希尔伯特空间向量,采用动量表示,指数是一个矩阵,在该表示中也是对角线。A(p)B(p)

当然,在任何数值实现中,将被离散化为一个向量,该向量具有有限数量的复数项,表示一组幅度然后,您需要将每个幅度乘以相关相位,得到 A(p)(A(pi))i=1N

(B(pi))i=1N=(eipi2Δt/2mA(pi))i=1N.

我希望这可以清楚地说明您需要做什么。如果您对如何实现这一点有特定的问题,您应该在 Computational Science 询问,或者提供您的代码在做什么以及代码的哪一部分实现了麻烦的步骤的详细概述。


添加

好的,所以看起来你的问题是傅里叶变换向量上的索引的含义。这是不可能以一般方式解决的,因为它将取决于您的 FFT 算法使用的特定约定,但一般建议与实现无关。

在大多数实现中(并且您的实现看起来像其中之一),您将为具有离散索引的向量 ,该向量在有限范围内以有限分辨率对连续函数进行采样,然后进行离散傅里叶变换,其形式为 但不幸的初衷相对不同 。 因此,计算的 DFTA(x)a(i)i=0,...,na(i)=A(x0+iδx)

b(j)=i=0ne2πiij/n2a(i),
B(p)=dxeipx/A(x).
b(j)通常将是连续傅里叶变换的合理近似,但它们之间的关系可能因约定而异。通常,您将拥有的动量分辨率和动量范围与您对原始函数进行采样的位置范围和位置分辨率成反比。

还有一项非常重要的规定。DFT在其变换变量中是周期性的,这意味着大多数 DFT 实现将在范围的后半部分具有负频率这是从上面的定义中得出的,当然你需要检查你的实现。因此,通常必须对域执行某种“折叠”,并且b(i)通常会采用

b(j)={B(jδp),0jn/2B((jn)δp),n/2jn.