为什么这种非标准 FDTD 实施会导致 EM 脉冲幅度无限增加?

计算科学 有限差分 Python 微分方程 电磁学 细胞内粒子
2021-12-12 22:19:40

我一直在研究 Python 中的 Particle-In-Cell 框架,并注意到一个问题,即 EM 脉冲的幅度随着模拟的更新而无限增加。

目前,我正在使用本文中概述的非标准有限差分算法:JLVay 2008

这是用于在真空中推进电场和磁场的算法:

@n.njit(parallel=True)
def E_Update(E, B, J, ds, dt, R):
    C = 2.99792458e8
    EPSILON = 8.8541878128e-12
    MU = 1.25663706212e-6
    for i in n.prange(1,E.shape[0]-1):
        for j in n.prange(1,E.shape[1]-1):
            for k in n.prange(1,E.shape[2]-1):
                # x-component update
                # -> Solve DyBz
                Bz_y0 = B[i,j-1,k,2]
                Bz_y1 = B[i,j,k,2]
                DyBz = (Bz_y1-Bz_y0)/ds
                # -> Solve DzBy
                By_z0 = B[i,j,k-1,1]
                By_z1 = B[i,j,k,1]
                DzBy = (By_z1-By_z0)/ds
                # -> Solve Ex
                R[i,j,k,0] = (1/EPSILON)*((DyBz-DzBy) - J[i,j,k,0])*dt + E[i,j,k,0]

                # y-component update
                # -> Solve DxBz
                Bz_x0 = B[i-1,j,k,2]
                Bz_x1 = B[i,j,k,2]
                DxBz = (Bz_x1-Bz_x0)/ds
                # -> Solve DzBx 
                Bx_z0 = B[i,j,k-1,0]
                Bx_z1 = B[i,j,k,0]
                DzBx = (Bx_z1-Bx_z0)/ds
                # -> Solve Ey
                R[i,j,k,1] = (1/EPSILON)*((-1)*(DyBz-DzBy) - J[i,j,k,1])*dt + E[i,j,k,1]
                
                # z-component update
                # -> Solve DxBy
                By_x0 = B[i-1,j,k,1]
                By_x1 = B[i,j,k,1]
                DxBy = (By_x1-By_x0)/ds
                # -> Solve DyBx
                Bx_y0 = B[i,j-1,k,0]
                Bx_y1 = B[i,j,k,0]
                DyBx = (Bx_y1-Bx_y0)/ds
                # -> Solve Ez
                R[i,j,k,2] = (1/EPSILON)*((DxBy-DyBx) - J[i,j,k,2])*dt + E[i,j,k,2]

@n.njit(parallel=True)
def B_Update(E, B, ds, dt, a, b, g, R):
    C = 2.99792458e8
    EPSILON = 8.8541878128e-12
    MU = 1.25663706212e-6
    C1 = ((1-(dt/(2*MU)))/(1+(dt/(2*MU))))
    C2 = (1/(1+(dt/(2*MU))))
    for i in n.prange(1,E.shape[0]-1):
        for j in n.prange(1,E.shape[1]-1):
            for k in n.prange(1,E.shape[2]-1):
                
                # x-component update
                # -> Solve DzEy 
                Ey_z0 = E[i,j,k,1]
                Ey_z1 = E[i,j,k+1,1] 
                DzEy = (Ey_z1-Ey_z0)/ds 
                # -> Solve S1zEy
                S1zEyQ1 = E[i+1,j,k,1] # Ey(i+1,j,k)
                S1zEyQ2 = E[i-1,j,k,1] # Ey(i-1,j,k)
                S1zEyQ3 = E[i,j+1,k,1] # Ey(i,j+1,k)
                S1zEyQ4 = E[i,j-1,k,1] # Ey(i,j-1,k)
                S1zEy = S1zEyQ1 + S1zEyQ2 + S1zEyQ3 + S1zEyQ4
                # -> Solve S2zEy
                S2zEyQ1 = E[i+1,j+1,k,1] # Ey[i+1,j+1,k]
                S2zEyQ2 = E[i+1,j-1,k,1] # Ey[i+1,j-1,k]
                S2zEyQ3 = E[i-1,j+1,k,1] # Ey[i-1,j+1,k]
                S2zEyQ4 = E[i-1,j-1,k,1] # Ey[i-1,j-1,k]
                S2zEy = S2zEyQ1 + S2zEyQ2 + S2zEyQ3 + S2zEyQ4
                # -> Solve DSzEy
                DSzEy = (a+b*S1zEy+g*S2zEy)*(DzEy)
                # -> Solve DyEz
                Ez_y0 = E[i,j,k,2]
                Ez_y1 = E[i,j+1,k,2]
                DyEz = (Ez_y1-Ez_y0)/ds
                # -> Solve S1yEz
                S1yEzQ1 = E[i+1,j,k,2] # Ez(i+1,j,k)
                S1yEzQ2 = E[i-1,j,k,2] # Ez(i-1,j,k)
                S1yEzQ3 = E[i,j,k+1,2] # Ez(i,j,k+1)
                S1yEzQ4 = E[i,j,k-1,2] # Ez(i,j,k-1)
                S1yEz = S1yEzQ1 + S1yEzQ2 + S1yEzQ3 + S1yEzQ4
                # -> Solve S2yEz
                S2yEzQ1 = E[i+1,j,k+1,2] # Ez(i+1,j,k+1)
                S2yEzQ2 = E[i+1,j,k-1,2] # Ez(i+1,j,k-1)
                S2yEzQ3 = E[i-1,j,k+1,2] # Ez(i-1,j,k+1)
                S2yEzQ4 = E[i-1,j,k-1,2] # Ez(i-1,j,k-1)
                S2yEz = S2yEzQ1 + S2yEzQ2 + S2yEzQ3 + S2yEzQ4
                # -> Solve DSyEz
                DSyEz = (a+b*S1yEz+g*S2yEz)*(DyEz)
                # -> Solve DtBx
                R[i,j,k,0] = (1/MU)*((DSzEy-DSyEz))*dt + B[i,j,k,0]
                
                # y-component update
                # -> Solve DzEx
                Ex_z0 = E[i,j,k,0]
                Ex_z1 = E[i,j,k+1,0]
                DzEx = (Ex_z1-Ex_z0)/ds
                # -> Solve S1zEx
                S1zExQ1 = E[i+1,j,k,0] # Ex(i+1,j,k)
                S1zExQ2 = E[i-1,j,k,0] # Ex(i-1,j,k)
                S1zExQ3 = E[i,j+1,k,0] # Ex(i,j+1,k)
                S1zExQ4 = E[i,j-1,k,0] # Ex(i,j-1,k)
                S1zEx = S1zExQ1 + S1zExQ2 + S1zExQ3 + S1zExQ4
                # -> Solve S2zEx
                S2zExQ1 = E[i+1,j+1,k,0] # Ex(i+1,j+1,k)
                S2zExQ2 = E[i+1,j-1,k,0] # Ex(i+1,j-1,k)
                S2zExQ3 = E[i-1,j+1,k,0] # Ex(i-1,j+1,k)
                S2zExQ4 = E[i-1,j-1,k,0] # Ex(i-1,j-1,k)
                S2zEx = S2zExQ1 + S2zExQ2 + S2zExQ3 + S2zExQ4
                # -> Solve DSzEx
                DSzEx = (a+b*S1zEx+g*S2zEx)*(DzEx)
                # -> Solve DxEz
                Ez_x0 = E[i,j,k,2]
                Ez_x1 = E[i+1,j,k,2]
                DxEz = (Ez_x1-Ez_x0)/ds
                # -> Solve S1xEz
                S1xEzQ1 = E[i,j+1,k,2] # Ez(i,j+1,k)
                S1xEzQ2 = E[i,j-1,k,2] # Ez(i,j-1,k)
                S1xEzQ3 = E[i,j,k+1,2] # Ez(i,j,k+1)
                S1xEzQ4 = E[i,j,k-1,2] # Ez(i,j,k-1)
                S1xEz = S1xEzQ1 + S1xEzQ2 + S1xEzQ3 + S1xEzQ4
                # -> Solve S2xEz
                S2xEzQ1 = E[i,j+1,k+1,2] # Ez(i,j+1,k+1)
                S2xEzQ2 = E[i,j+1,k-1,2] # Ez(i,j+1,k-1)
                S2xEzQ3 = E[i,j-1,k+1,2] # Ez(i,j-1,k+1)
                S2xEzQ4 = E[i,j-1,k-1,2] # Ez(i,j-1,k-1)
                S2xEz = S2xEzQ1+S2xEzQ2+S2xEzQ3+S2xEzQ4
                # -> Solve DSxEz
                DSxEz = (a+b*S1xEz+g*S2xEz)*(DxEz)
                # -> Solve DtBy
                R[i,j,k,1] = (1/MU)*((DSxEz-DSzEx))*dt + B[i,j,k,1]
                
                # z-component update
                # -> Solve DyEx
                Ex_y0 = E[i,j,k,0]
                Ex_y1 = E[i,j+1,k,0]
                DyEx = (Ex_y1-Ex_y0)/ds
                # -> Solve S1yEx
                S1yExQ1 = E[i+1,j,k,0] # Ex(i+1,j,k)
                S1yExQ2 = E[i-1,j,k,0] # Ex(i-1,j,k)
                S1yExQ3 = E[i,j,k+1,0] # Ex(i,j,k+1)
                S1yExQ4 = E[i,j,k-1,0] # Ex(i,j,k-1)
                S1yEx = S1yExQ1 + S1yExQ2 + S1yExQ3 + S1yExQ4
                # -> Solve S2yEx
                S2yExQ1 = E[i+1,j,k+1,0] # Ex(i+1,j,k+1)
                S2yExQ2 = E[i+1,j,k-1,0] # Ex(i+1,j,k-1)
                S2yExQ3 = E[i-1,j,k+1,0] # Ex(i-1,j,k+1)
                S2yExQ4 = E[i-1,j,k-1,0] # Ex(i-1,j,k-1)
                S2yEx = S2yExQ1 + S2yExQ2 + S2yExQ3 + S2yExQ4
                # -> Solve DSyEx
                DSyEx = (a+b*S1yEx+g*S2yEx)*(DyEx)
                # -> Solve DxEy
                Ey_x0 = E[i,j,k,1]
                Ey_x1 = E[i+1,j,k,1]
                DxEy = (Ey_x1-Ey_x0)/ds
                # -> Solve S1xEy
                S1xEyQ1 = E[i,j+1,k,1] # Ey(i,j+1,k)
                S1xEyQ2 = E[i,j-1,k,1] # Ey(i,j-1,k)
                S1xEyQ3 = E[i,j,k+1,1] # Ey(i,j,k+1)
                S1xEyQ4 = E[i,j,k-1,1] # Ey(i,j,k-1)
                S1xEy = S1xEyQ1 + S1xEyQ2 + S1xEyQ3 + S1xEyQ4
                # -> Solve S2xEy
                S2xEyQ1 = E[i,j+1,k+1,1] # Ey(i,j+1,k+1)
                S2xEyQ2 = E[i,j+1,k-1,1] # Ey(i,j+1,k-1)
                S2xEyQ3 = E[i,j-1,k+1,1] # Ey(i,j-1,k+1)
                S2xEyQ4 = E[i,j-1,k-1,1] # Ey(i,j-1,k-1)
                S2xEy = S2xEyQ1 + S2xEyQ2 + S2xEyQ3 + S2xEyQ4
                # -> Solve DSxEy
                DSxEy = (a+b*S1xEy+g*S2xEy)*(DxEy)
                # -> Solve DtBz
                R[i,j,k,2] = (1/MU)*((DSyEx-DSxEy))*dt + B[i,j,k,2]

每个更新步骤都涉及通过Δt/2,然后电场由Δt,最后 B-Field 再次通过Δt/2.

到目前为止,我已尝试通过以下方式解决此问题:

  1. 恢复到标准 FDTD(即将 B_Update 方法中的 a、b 和 g 分别设置为 1、0 和 0)
  2. 通过保持时间步长比穿过 Yee-Cell 长度的光的穿越时间低几个数量级来确保满足 CFL 条件。
  3. 使用自然单位进行模拟(c=μ=ϵ=1)

我发现的唯一涉及 FDTD 模拟“爆炸”问题的资源是关于算法基础的 YouTube 讲座FDTD 方法的稳定性取决于在适当的时间和空间坐标处更新方程中使用的某些值的存在。我可能是错的,但我相信我写的更新函数满足了这个稳定性要求。

任何建议的更改或资源建议将不胜感激。

0个回答
没有发现任何回复~