具有周期性边界条件的薛定谔方程

计算科学 pde 线性代数 边界条件
2021-12-05 08:50:05

我有几个关于以下问题的问题:

我正在尝试使用曲柄尼科尔森离散化求解一维薛定谔方程,然后反转得到的三对角矩阵。我的问题现在已经演变成周期性边界条件的问题,因此我修改了我的代码以使用 Sherman Morrison 算法。

假设v当我希望反转三对角矩阵时,我的 RHS 在每个时间步。的大小v是我在空间上的网格点数。当我按照周期性情况的要求设置v[0]v[-1]相互关联时,我的等式就会爆炸。我不知道为什么会这样。我正在使用 python2.7 和 scipy 的内置 solve_banded 来求解方程。

这引出了我的第二个问题:我使用 python 是因为它是我最了解的语言,但我发现它相当慢(即使使用 numpy 和 scipy 提供的优化)。我已经尝试过使用 C++,因为我相当熟悉它。我以为我会使用经过 BLAS 优化的 GSL,但没有找到文档来创建复数向量或用这种复数向量求解三对角矩阵。

我想要我的程序中的对象,因为我觉得这将是我以后概括包括波函数之间的耦合的最简单方法,因此我坚持使用面向对象的语言。

我可以尝试手动编写三对角矩阵求解器,但是当我在 python 中这样做时遇到了问题。随着我以越来越精细的时间步长进行长时间的进化,错误累积起来让我胡说八道。牢记这一点,我决定使用内置方法。

非常感谢任何建议。

编辑:这是相关的代码片段。该符号是从维基百科关于三对角矩阵 (TDM) 方程的页面借用的。v 是曲柄尼科尔森算法在每个时间步的 RHS。向量 a、b 和 c 是 TDM 的对角线。周期性情况的更正算法来自CFD Wiki我做了一点重命名。他们称之为 u, v 我称之为 U, V(大写)。我称 q 为补码,y 为临时解决方案和实际解决方案 self.currentState。v[0] 和 v[-1] 的分配是导致问题的原因,因此已被注释掉。你可以忽略伽玛的因素。它们是用于模拟玻色爱因斯坦凝聚体的非线性因子。

for T in np.arange(self.timeArraySize):
        for i in np.arange(0,self.spaceArraySize-1):
            v[i] = Y*self.currentState[i+1] + (1-2*Y)*self.currentState[i] + Y*self.currentState[i-1] - 1j*0.5*self.timeStep*potential[i]*self.currentState[i] - self.gamma*1j*0.5*self.timeStep*(abs(self.currentState[i])**2)*self.currentState[i]
            b[i] = 1+2*Y + 1j*0.5*self.timeStep*potential[i] + self.gamma*self.timeStep*1j*0.5*(abs(self.currentState[i])**2)

        #v[0] = Y*self.currentState[1] + (1-2*Y)*self.currentState[0] + Y*self.currentState[-1] - 1j*0.5*self.timeStep*potential[0]*self.currentState[0]# - self.gamma*1j*0.5*self.timeStep*(abs(self.currentState[0])**2)*self.currentState[0]
        #v[-1] = Y*self.currentState[0] + (1-2*Y)*self.currentState[-1] + Y*self.currentState[-2] - 1j*0.5*self.timeStep*potential[-1]*self.currentState[-1]# - self.gamma*1j*0.5*self.timeStep*(abs(self.currentState[-1])**2)*self.currentState[-1]
        b[0] = 1+2*Y + 1j*0.5*self.timeStep*potential[0] + self.gamma*self.timeStep*1j*0.5*(abs(self.currentState[0])**2)
        b[-1] = 1+2*Y + 1j*0.5*self.timeStep*potential[-1] + self.gamma*self.timeStep*1j*0.5*(abs(self.currentState[-1])**2)

        diagCorrection[0], diagCorrection[-1] = - b[0], - c[-1]*a[0]/b[0]

        tridiag = np.matrix([
            c,
            b - diagCorrection,
            a,
        ])

        temp = solve_banded((1,1), tridiag, v)

        U = np.zeros(self.spaceArraySize, dtype=np.complex64)
        U[0], U[-1] = -b[0], c[-1]

        V = np.zeros(self.spaceArraySize, dtype=np.complex64)
        V[0], V[-1] = 1, -a[0]/b[0]

        complement = solve_banded((1,1), tridiag, U)

        num = np.dot(V, temp)
        den = 1 + np.dot(V, complement)

        self.currentState = temp  - (num/den)*complement
1个回答

第二个问题

调用 Scipy/Numpy 的代码通常只有在可以矢量化的情况下才会很快;你不应该在 python 循环中有任何“慢”的东西。即便如此,它还是不可避免地会比在编译语言中使用类似库的速度慢一点。

for i in np.arange(0,self.spaceArraySize-1):
            v[i] = Y*self.currentState[i+1] + (1-2*Y)*self.currentState[i]   ...
            b[i] = 1+2*Y + 1j*0.5*self.timeStep*potential[i] + ...

这就是我所说的“在 python 循环中慢”的意思。对于大多数数值应用程序来说, Python 的for速度慢得令人无法接受,而 Scipy/Numpy 根本不会影响这一点。如果你打算使用 python,这个内部循环应该表示为一个或两个 Numpy/Scipy 函数,这些库可能提供也可能不提供。如果它们没有提供允许您像这样迭代数组并访问相邻元素的东西,那么 python 是您想要做的错误工具。

此外,您正在执行求逆而不是矩阵向量求解。矩阵求逆,然后是矩阵向量乘法,比矩阵向量求解慢得多几乎可以肯定,这比其他任何事情都更能减慢您的代码速度。

如果你想使用 C/C++,GSL 在复杂线性代数方面有点欠缺。我建议直接使用 BLAS 或 LAPACK,或者使用 PETSc 或 Trilinos 之类的库。如果你安装了 MKL,你也可以使用它。您可能还想查看面向对象的 Fortran 2008。

您的矩阵是稀疏的,因此您需要确保使用稀疏库。

我还要说,您在这里所做的似乎足够低级,以至于面向对象可能不应该是您的主要关注点。Fortran 90+ 数组可能非常适合您的需要,并且 F90 编译器可以自动并行化一些循环。

此外,您可能想查看具有该sparse()功能的 Octave 或 Matlab。如果使用得当,这些应该能够很快运行。