基于Python的二维谱元法声波方程正演计算

谱元法其实一种高阶的有限元方法,本质上还是属于伽辽金方法的一种,首先按照有限单元的思想对空间进行离散,然后再单元内部进行插值。同时,利用某些配点和积分方案完成区域的积分。

谱元法与有限元法的区别和联系?

首先有限元的单元形式多种多样,而谱元法一般适用在二维四边形和三维六面体中,不能像有限元方法的三角单元那样。另外,谱元法选取的插值基函数为拉格朗日基函数,有限元采用的是线性插值基函数。在单元内部,谱元法使用gauss lobatto legendre(简称gll)积分方案,将质量矩阵和刚度矩阵变为一系列权重系数的组合,同时由于多项式的正交性,最终的质量矩阵为对角矩阵。

1.1 谱元法求解声波方程的步骤

  1. 声波方程的弱形式

∂ t 2 u = v 2 ∇ 2 u + f ∬ ∂ t 2 u ⋅ α d Ω = ∬ v 2 ∇ 2 u ⋅ α d Ω + ∬ f ⋅ α d Ω \begin{aligned} \partial^2_tu&=v^2\nabla^2u+f\\ \iint\partial^2_tu\cdot \alpha d\Omega&=\iint v^2\nabla^2u\cdot\alpha d\Omega+\iint f\cdot\alpha d\Omega\\ \end{aligned} t2ut2uαdΩ=v22u+f=v22uαdΩ+fαdΩ其中 α ( x , y ) \alpha(x,y) α(x,y)为测试函数

  1. 使用分部积分,并应用自由边界条件

∬ v 2 ∇ 2 u ⋅ α d Ω = [ v 2 ∇ u ⋅ α ] ∣ l 1 l 2 − v 2 ∬ ∇ u ∇ α d Ω = − v 2 ∬ ∇ u ∇ α d Ω ( [ v 2 ∇ u ⋅ α ] ∣ l 1 l 2 = 0 ) \begin{aligned} \iint v^2\nabla^2u\cdot\alpha d\Omega&=\left.\left[v^2\nabla u\cdot\alpha\right]\right|_{l_1}^{l_2}-v^2\iint\nabla u\nabla\alpha d\Omega\\ &=-v^2\iint\nabla u\nabla\alpha d\Omega\qquad(\left.\left[v^2\nabla u\cdot\alpha\right]\right|_{l_1}^{l_2}=0) \end{aligned} v22uαdΩ=[v2uα] l1l2v2uαdΩ=v2uαdΩ([v2uα] l1l2=0)

  1. 对解进行插值近似

u ( t , x , y ) = ∑ i = 0 N u i ( t ) ℓ i ( x , y ) ∬ ∂ t 2 [ ∑ i = 0 N u i ( t ) ℓ i ( x , y ) ] ⋅ α d Ω + v 2 ∬ ∇ [ ∑ i = 0 N u i ( t ) ℓ i ( x , y ) ] ⋅ ∇ α d Ω = ∬ f ( t , x , y ) ⋅ α d Ω ∑ i = 0 N ∂ t 2 u i ( t ) ∬ ℓ i ( x , y ) α ( x , y ) d Ω + v 2 ∑ i = 0 N u i ( t ) ∬ ∇ ℓ i ( x , y ) ∇ α ( x , y ) d Ω = ∬ f ( t , x , y ) ⋅ α d Ω \begin{aligned} &u(t,x,y)=\sum_{i=0}^{N}u_i(t)\ell_i(x,y)\\ \iint\partial^2_t\left[\sum_{i=0}^{N}u_i(t)\ell_i(x,y)\right]\cdot \alpha d\Omega&+v^2\iint\nabla\left[\sum_{i=0}^{N}u_i(t)\ell_i(x,y)\right]\cdot\nabla\alpha d\Omega=\iint f(t,x,y)\cdot\alpha d\Omega\\ \sum_{i=0}^{N}\partial_t^2u_i(t)\iint\ell_i(x,y)\alpha(x,y)d\Omega&+v^2\sum_{i=0}^{N}u_i(t)\iint\nabla\ell_i(x,y)\nabla\alpha(x,y)d\Omega=\iint f(t,x,y)\cdot\alpha d\Omega \end{aligned} t2[i=0Nui(t)i(x,y)]αdΩi=0Nt2ui(t)i(x,y)α(x,y)dΩu(t,x,y)=i=0Nui(t)i(x,y)+v2[i=0Nui(t)i(x,y)]αdΩ=f(t,x,y)αdΩ+v2i=0Nui(t)i(x,y)α(x,y)dΩ=f(t,x,y)αdΩ其中 ℓ i ( x , y ) = ℓ i ( x ) × ℓ i ( y ) \ell_i(x,y)=\ell_i(x)\times\ell_i(y) i(x,y)=i(x)×i(y)为二维的拉格朗日插值基函数。

  1. 应用伽辽金法,令测试函数为相同的基函数

∑ i = 0 N ∂ t 2 u i ( t ) ∬ ℓ i ( x , y ) ℓ j ( x , y ) d Ω + v 2 ∑ i = 0 N u i ( t ) ∬ ∇ ℓ i ( x , y ) ∇ ℓ j ( x , y ) d Ω = ∬ f ( t , x , y ) ℓ j ( x , y ) d Ω \begin{aligned} \sum_{i=0}^{N}\partial_t^2u_i(t)\iint\ell_i(x,y)\ell_j(x,y)d\Omega&+v^2\sum_{i=0}^{N}u_i(t)\iint\nabla\ell_i(x,y)\nabla\ell_j(x,y)d\Omega=\iint f(t,x,y)\ell_j(x,y)d\Omega \end{aligned} i=0Nt2ui(t)i(x,y)j(x,y)dΩ+v2i=0Nui(t)i(x,y)j(x,y)dΩ=f(t,x,y)j(x,y)dΩ

  1. 将原方程写成弱形式的矩阵方程组

M T ∂ t 2 u + K T u = f M T = ∬ ℓ i ( x , y ) ℓ j ( x , y ) d Ω K T = ∬ ∇ ℓ i ( x , y ) ∇ ℓ j ( x , y ) d Ω f = ∬ f ( t , x , y ) ℓ j ( x , y ) d Ω \begin{aligned} &\mathbf{M}^T\partial^2_t\mathbf{u}+\mathbf{K}^T\mathbf{u}=\mathbf{f}\\ \mathbf{M}^T&=\iint\ell_i(x,y)\ell_j(x,y)d\Omega\\ \mathbf{K}^T&=\iint\nabla\ell_i(x,y)\nabla\ell_j(x,y)d\Omega\\ \mathbf{f}&=\iint f(t,x,y)\ell_j(x,y)d\Omega \end{aligned} MTKTfMTt2u+KTu=f=i(x,y)j(x,y)dΩ=i(x,y)j(x,y)dΩ=f(t,x,y)j(x,y)dΩ

  1. 使用二阶中心差分对时间进行延拓

u ( t + d t ) = d t 2 ( M T ) − 1 [ f − K T u ] + 2 u − u ( t − d t ) \begin{aligned} \mathbf{u}(t+dt)=dt^2(\mathbf{M}^T)^{-1}\left[\mathbf{f}-\mathbf{K}^T\mathbf{u}\right]+2\mathbf{u}-\mathbf{u}(t-dt) \end{aligned} u(t+dt)=dt2(MT)1[fKTu]+2uu(tdt)

1.2 拉格朗日多项式与数值积分

根据数值定理,对于 N + 1 N+1 N+1 个点有不超过 N N N 次多项式与之对应,拉格朗日多项式满足该条件,且具有以下形式:
ℓ i N ( x ) = ∏ i ≠ j N + 1 x − x j x i − x j , i , j = 1 , 2 , … , N + 1 \begin{aligned} \ell_i^N(x)=\prod_{i\ne j}^{N+1}\dfrac{x-x_j}{x_i-x_j},\qquad i,j=1,2,\dots,N+1 \end{aligned} iN(x)=i=jN+1xixjxxj,i,j=1,2,,N+1其特点是在当前节点处值为1,其余节点处为零。
连续函数的积分可以用可以解析积分的多项式近似代替来计算。我们再次使用拉格朗日多项式作为插值函数,得到高斯-洛巴托-勒让德积分。在这里,gll点被用来计算积分。
∫ − 1 1 f ( x ) d x ≈ ∫ − 1 1 P N ( x ) d x = ∑ i = 1 N + 1 ω i f ( x i ) \begin{aligned} \int_{-1}^1f(x)dx\approx\int_{-1}^1P_N(x)dx=\sum_{i=1}^{N+1}\omega_if(x_i) \end{aligned} 11f(x)dx11PN(x)dx=i=1N+1ωif(xi)其中 x i x_i xi为gll点, ω i \omega_i ωi为相应gll点的权重系数。

1.3 网格划分

本文使用的网格是长宽相同的正方形单元网格,单元内部使用gll点进行划分。

网格划分
正方形单元映射

1.4 质量矩阵的计算

写出质量矩阵的计算式:
M = ∬ ℓ i ( x , y ) ℓ j ( x , y ) d Ω = ∫ − 1 1 ∫ − 1 1 ℓ i ( u , v ) ℓ j ( u , v ) ∣ J ∣ d u d v = ∫ − 1 1 ∑ k = 0 N ω i ℓ i ( u k , v ) ℓ j ( u k , v ) ∣ J ∣ d v = ∑ k = 0 N ∑ l = 0 N ω i ω j ℓ i ( u k , v l ) ℓ j ( u k , v l ) ∣ J ∣ = ∑ k = 0 N ∑ l = 0 N ω i ω j ℓ i ( u k ) ℓ i ( v l ) ℓ j ( u k ) ℓ j ( v l ) ∣ J ∣ = ω i ω j δ i j ∣ J ∣ \begin{aligned} M&=\iint\ell_i(x,y)\ell_j(x,y)d\Omega\\ &=\int_{-1}^1\int_{-1}^1\ell_i(u,v)\ell_j(u,v)\left|J\right|dudv\\ &=\int_{-1}^1\sum_{k=0}^N\omega_i\ell_i(u_k,v)\ell_j(u_k,v)\left|J\right|dv\\ &=\sum_{k=0}^N\sum_{l=0}^N\omega_i\omega_j\ell_i(u_k,v_l)\ell_j(u_k,v_l)\left|J\right|\\ &=\sum_{k=0}^N\sum_{l=0}^N\omega_i\omega_j\ell_i(u_k)\ell_i(v_l)\ell_j(u_k)\ell_j(v_l)\left|J\right|\\ &=\omega_i\omega_j\delta_{ij}\left|J\right| \end{aligned} M=i(x,y)j(x,y)dΩ=1111i(u,v)j(u,v)Jdudv=11k=0Nωii(uk,v)j(uk,v)Jdv=k=0Nl=0Nωiωji(uk,vl)j(uk,vl)J=k=0Nl=0Nωiωji(uk)i(vl)j(uk)j(vl)J=ωiωjδijJ由此可知,谱元法的质量矩阵是一个对角矩阵,且元素为对应位置的积分权重系数的乘积。由于本文使用的网格中每一个单元形状一样,所以都有相同的局部质量矩阵,计算可得该局部单元的质量矩阵为:

局部质量矩阵及对角元素

1.5 刚度矩阵的计算

写出刚度矩阵的计算式:
K = ∬ ∇ ℓ i ( x , y ) ∇ ℓ j ( x , y ) d Ω = ∫ − 1 1 ∫ − 1 1 ∇ ℓ i ( x , y ) ∇ ℓ j ( x , y ) ∣ J ∣ d u d v = ∫ − 1 1 ∫ − 1 1 [ ∇ ℓ i ( u , v ) J − 1 ] [ ∇ ℓ j ( u , v ) J − 1 ] ∣ J ∣ d u d v = ∫ − 1 1 ∫ − 1 1 { ∇ [ ℓ i ( u ) ℓ i ( v ) ] J − 1 } { ∇ [ ℓ j ( u ) ℓ j ( v ) ] J − 1 } ∣ J ∣ d u d v = ∫ − 1 1 ∫ − 1 1 { [ ∂ u ℓ i ( u ) ℓ i ( v ) , ∂ v ℓ i ( u ) ℓ i ( v ) ] J − 1 } { [ ∂ u ℓ j ( u ) ℓ j ( v ) , ∂ v ℓ j ( u ) ℓ j ( v ) ] J − 1 } ∣ J ∣ d u d v = ∫ − 1 1 ∫ − 1 1 2 l e [ ∂ u ℓ i ( u ) ℓ i ( v ) , ∂ v ℓ i ( u ) ℓ i ( v ) ] 2 l e [ ∂ u ℓ j ( u ) ℓ j ( v ) , ∂ v ℓ j ( u ) ℓ j ( v ) ] T ∣ J ∣ d u d v ( J − 1 = [ 2 l e 0 0 2 l e ] ) = ∫ − 1 1 ∫ − 1 1 [ ∂ u ℓ i ( u ) ℓ i ( v ) , ∂ v ℓ i ( u ) ℓ i ( v ) ] [ ∂ u ℓ j ( u ) ℓ j ( v ) , ∂ v ℓ j ( u ) ℓ j ( v ) ] T d u d v ( ∣ J ∣ = ( l e 2 ) 2 ) = ∫ − 1 1 ∑ k = 0 N ω k [ ∂ u ℓ i ( u k ) ℓ i ( v ) , ∂ v ℓ i ( u k ) ℓ i ( v ) ] [ ∂ u ℓ j ( u k ) ℓ j ( v ) , ∂ v ℓ j ( u k ) ℓ j ( v ) ] T d v = ∑ k = 0 N ∑ l = 0 N ω k ω l [ ∂ u ℓ i ( u k ) ℓ i ( v l ) , ∂ v ℓ i ( u k ) ℓ i ( v l ) ] [ ∂ u ℓ j ( u k ) ℓ j ( v l ) , ∂ v ℓ j ( u k ) ℓ j ( v l ) ] T = ∑ k = 0 N ∑ l = 0 N ω k ω l [ ∂ u ℓ i ( u k ) ℓ i ( v l ) ∂ u ℓ j ( u k ) ℓ j ( v l ) + ∂ v ℓ i ( u k ) ℓ i ( v l ) ∂ v ℓ j ( u k ) ℓ j ( v l ) ] = ∑ k = 0 N ∑ l = 0 N ω k ω l [ ∂ u ℓ i ( u k ) ∂ u ℓ j ( u k ) δ i u , j u + ∂ v ℓ i ( u k ) ∂ v ℓ j ( u k ) δ i v , j v ] \begin{aligned} K&=\iint\nabla\ell_i(x,y)\nabla\ell_j(x,y)d\Omega\\ &=\int_{-1}^1\int_{-1}^1\nabla\ell_i(x,y)\nabla\ell_j(x,y)\left|J\right|dudv\\ &=\int_{-1}^1\int_{-1}^1\left[\nabla\ell_i(u,v)J^{-1}\right]\left[\nabla\ell_j(u,v)J^{-1}\right]\left|J\right|dudv\\ &=\int_{-1}^1\int_{-1}^1\left\{\nabla\left[\ell_i(u)\ell_i(v)\right]J^{-1}\right\}\left\{\nabla\left[\ell_j(u)\ell_j(v)\right]J^{-1}\right\}\left|J\right|dudv\\ &=\int_{-1}^1\int_{-1}^1\left\{\left[\partial_u\ell_i(u)\ell_i(v),\partial_v\ell_i(u)\ell_i(v)\right]J^{-1}\right\}\left\{\left[\partial_u\ell_j(u)\ell_j(v),\partial_v\ell_j(u)\ell_j(v)\right]J^{-1}\right\}\left|J\right|dudv\\ &=\int_{-1}^1\int_{-1}^1\dfrac{2}{le}\left[\partial_u\ell_i(u)\ell_i(v),\partial_v\ell_i(u)\ell_i(v)\right]\dfrac{2}{le}\left[\partial_u\ell_j(u)\ell_j(v),\partial_v\ell_j(u)\ell_j(v)\right]^T\left|J\right|dudv\quad(J^{-1}=\begin{bmatrix} \dfrac{2}{le}&0\\ 0&\dfrac{2}{le} \end{bmatrix})\\ &=\int_{-1}^1\int_{-1}^1\left[\partial_u\ell_i(u)\ell_i(v),\partial_v\ell_i(u)\ell_i(v)\right]\left[\partial_u\ell_j(u)\ell_j(v),\partial_v\ell_j(u)\ell_j(v)\right]^Tdudv\quad(\left|J\right|=(\dfrac{le}{2})^2)\\ &=\int_{-1}^1\sum_{k=0}^N\omega_k\left[\partial_u\ell_i(u_k)\ell_i(v),\partial_v\ell_i(u_k)\ell_i(v)\right]\left[\partial_u\ell_j(u_k)\ell_j(v),\partial_v\ell_j(u_k)\ell_j(v)\right]^Tdv\\ &=\sum_{k=0}^N\sum_{l=0}^N\omega_k\omega_l\left[\partial_u\ell_i(u_k)\ell_i(v_l),\partial_v\ell_i(u_k)\ell_i(v_l)\right]\left[\partial_u\ell_j(u_k)\ell_j(v_l),\partial_v\ell_j(u_k)\ell_j(v_l)\right]^T\\ &=\sum_{k=0}^N\sum_{l=0}^N\omega_k\omega_l\left[\partial_u\ell_i(u_k)\ell_i(v_l)\partial_u\ell_j(u_k)\ell_j(v_l)+\partial_v\ell_i(u_k)\ell_i(v_l)\partial_v\ell_j(u_k)\ell_j(v_l)\right]\\ &=\sum_{k=0}^N\sum_{l=0}^N\omega_k\omega_l\left[\partial_u\ell_i(u_k)\partial_u\ell_j(u_k)\delta_{iu,ju}+\partial_v\ell_i(u_k)\partial_v\ell_j(u_k)\delta_{iv,jv}\right]\\ \end{aligned} K=i(x,y)j(x,y)dΩ=1111i(x,y)j(x,y)Jdudv=1111[i(u,v)J1][j(u,v)J1]Jdudv=1111{[i(u)i(v)]J1}{[j(u)j(v)]J1}Jdudv=1111{[ui(u)i(v),vi(u)i(v)]J1}{[uj(u)j(v),vj(u)j(v)]J1}Jdudv=1111le2[ui(u)i(v),vi(u)i(v)]le2[uj(u)j(v),vj(u)j(v)]TJdudv(J1= le200le2 )=1111[ui(u)i(v),vi(u)i(v)][uj(u)j(v),vj(u)j(v)]Tdudv(J=(2le)2)=11k=0Nωk[ui(uk)i(v),vi(uk)i(v)][uj(uk)j(v),vj(uk)j(v)]Tdv=k=0Nl=0Nωkωl[ui(uk)i(vl),vi(uk)i(vl)][uj(uk)j(vl),vj(uk)j(vl)]T=k=0Nl=0Nωkωl[ui(uk)i(vl)uj(uk)j(vl)+vi(uk)i(vl)vj(uk)j(vl)]=k=0Nl=0Nωkωl[ui(uk)uj(uk)δiu,ju+vi(uk)vj(uk)δiv,jv]
即局部刚度矩阵计算只在 i , j i,j i,j 点横纵坐标至少有一个相等的情况下才有值,一个都不相等则为零。所以局部刚度计算可按三种情况进行计算:

局部刚度矩阵非零的三种情况
由此,我们可以计算得到局部刚度矩阵:

二、数值实验

2.1 参数设置

本次实验计算的为均匀模型下的弹性波(与声波无异),具体参数设置如下:

2.2 总装质量矩阵与刚度矩阵

将之前计算的局部质量矩阵与局部刚度矩阵按照单元进行组装,得到全局的质量矩阵与刚度矩阵如下(绘制部分):

全局矩阵

2.3 震源

本次实验采用高斯震源,具有以下形式:

高斯震源

2.4 实验结果

将计算好的矩阵进行显示格式迭代,可得各个时刻的波场值:

高斯震源

三、示例代码

#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
作者:@fm
'''


def gll(N):
    """
    Returns GLL (Gauss Lobato Legendre module with collocation points and
    weights)
    """
    # Initialization of integration weights and collocation points
    # [xi, weights] =  gll(N)
    # Values taken from Diploma Thesis Bernhard Schuberth
    if N == 2:
        xi = [-1.0, 0.0, 1.0]
        weights = [0.33333333, 1.33333333, 0.33333333]
    elif N == 3:
        xi = [-1.0, -0.447213595499957, 0.447213595499957, 1.0]
        weights = [0.1666666667, 0.833333333, 0.833333333, 0.1666666666]
    elif N == 4:
        xi = [-1.0, -0.6546536707079772, 0.0, 0.6546536707079772, 1.0]
        weights = [0.1, 0.544444444, 0.711111111, 0.544444444, 0.1]
    elif N == 5:
        xi = [-1.0, -0.7650553239294647, -0.285231516480645, 0.285231516480645,
              0.7650553239294647, 1.0]
        weights = [0.0666666666666667,  0.3784749562978470,
                   0.5548583770354862, 0.5548583770354862, 0.3784749562978470,
                   0.0666666666666667]
    elif N == 6:
        xi = [-1.0, -0.8302238962785670, -0.4688487934707142, 0.0,
              0.4688487934707142, 0.8302238962785670, 1.0]
        weights = [0.0476190476190476, 0.2768260473615659, 0.4317453812098627,
                   0.4876190476190476, 0.4317453812098627, 0.2768260473615659,
                   0.0476190476190476]
    elif N == 7:
        xi = [-1.0, -0.8717401485096066, -0.5917001814331423,
              -0.2092992179024789, 0.2092992179024789, 0.5917001814331423,
              0.8717401485096066, 1.0]
        weights = [0.0357142857142857, 0.2107042271435061, 0.3411226924835044,
                   0.4124587946587038, 0.4124587946587038, 0.3411226924835044,
                   0.2107042271435061, 0.0357142857142857]
    elif N == 8:
        xi = [-1.0, -0.8997579954114602, -0.6771862795107377,
              -0.3631174638261782, 0.0, 0.3631174638261782,
              0.6771862795107377, 0.8997579954114602, 1.0]
        weights = [0.0277777777777778, 0.1654953615608055, 0.2745387125001617,
                   0.3464285109730463, 0.3715192743764172, 0.3464285109730463,
                   0.2745387125001617, 0.1654953615608055, 0.0277777777777778]
    elif N == 9:
        xi = [-1.0, -0.9195339081664589, -0.7387738651055050,
              -0.4779249498104445, -0.1652789576663870, 0.1652789576663870,
              0.4779249498104445, 0.7387738651055050, 0.9195339081664589, 1.0]
        weights = [0.0222222222222222, 0.1333059908510701, 0.2248893420631264,
                   0.2920426836796838, 0.3275397611838976, 0.3275397611838976,
                   0.2920426836796838, 0.2248893420631264, 0.1333059908510701,
                   0.0222222222222222]
    elif N == 10:
        xi = [-1.0, -0.9340014304080592, -0.7844834736631444,
              -0.5652353269962050, -0.2957581355869394, 0.0,
              0.2957581355869394, 0.5652353269962050, 0.7844834736631444,
              0.9340014304080592, 1.0]
        weights = [0.0181818181818182, 0.1096122732669949, 0.1871698817803052,
                   0.2480481042640284, 0.2868791247790080, 0.3002175954556907,
                   0.2868791247790080, 0.2480481042640284, 0.1871698817803052,
                   0.1096122732669949, 0.0181818181818182]
    elif N == 11:
        xi = [-1.0, -0.9448992722228822, -0.8192793216440067,
              -0.6328761530318606, -0.3995309409653489, -0.1365529328549276,
              0.1365529328549276, 0.3995309409653489, 0.6328761530318606,
              0.8192793216440067, 0.9448992722228822, 1.0]
        weights = [0.0151515151515152, 0.0916845174131962, 0.1579747055643701,
                   0.2125084177610211, 0.2512756031992013, 0.2714052409106962,
                   0.2714052409106962, 0.2512756031992013, 0.2125084177610211,
                   0.1579747055643701, 0.0916845174131962, 0.0151515151515152]
    elif N == 12:
        xi = [-1.0, -0.9533098466421639, -0.8463475646518723,
              -0.6861884690817575, -0.4829098210913362, -0.2492869301062400,
              0.0, 0.2492869301062400, 0.4829098210913362,
              0.6861884690817575, 0.8463475646518723, 0.9533098466421639,
              1.0]
        weights = [0.0128205128205128, 0.0778016867468189, 0.1349819266896083,
                   0.1836468652035501, 0.2207677935661101, 0.2440157903066763,
                   0.2519308493334467, 0.2440157903066763, 0.2207677935661101,
                   0.1836468652035501, 0.1349819266896083, 0.0778016867468189,
                   0.0128205128205128]
    else:
        raise NotImplementedError

    return xi, weights
# -*- coding: utf-8 -*-
'''
作者:@fm
'''
import numpy as np
from gll import gll

def creat_mesh(le, nx, ny, N):
    '''
    element的大小
    nx:x方向上的element的数量
    ny:y方向上的element的数量
    N:拉格朗日多项式的阶数
    用于谱元法的网格剖分
    '''
    [xi,w]=gll(N=N)
    for i in range(len(xi)):
        xi[i]=(xi[i]+1)/2*le
    # node的数量
    numN = (N*nx+1)*(N*ny+1)
    # element的数量
    numE = nx*ny
    # 一个element有多少nodes
    NofE = (N+1)*(N+1)
    # 二维坐标
    D = 2
    # nodes 坐标
    NC = np.zeros([numN, D])
    
    # nodes 坐标计算
    n = 0
    for i in range(0, N*ny+1):
        for j in range(0, N*nx+1):
            NC[n, 0] = xi[j%N]+(j//N)*le
            NC[n, 1] = xi[i%N]+(i//N)*le

            n += 1
    # element 索引,一个element所有的节点进行索引
    n2 = 0
    EI = np.zeros([numE,(N+1),(N+1)])
    for i in range(ny):
        for j in range(nx):
            for ix in range(N+1):
                
                EI[n2,ix,0:(N+1)]=np.reshape(np.array(range(1,N+2)),[N+1,])+j*N+i*N*(N*nx+1)+ix*(N*nx+1)
            n2+=1
                
    EI = EI.astype(int)    
    return NC, EI
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
作者:@fm
'''
import numpy as np

from gll import gll
from lagrange import lagrange
from legendre import legendre


def lagrange1st(N):
    """
    # Calculation of 1st derivatives of Lagrange polynomials
    # at GLL collocation points
    # out = legendre1st(N)
    # out is a matrix with columns -> GLL nodes
    #                        rows -> order
    """
    out = np.zeros([N+1, N+1])

    [xi, w] = gll(N)

    # initialize dij matrix (see Funaro 1993)
    d = np.zeros([N + 1, N + 1])

    for i in range(-1, N):
        for j in range(-1, N):
            if i != j:
                d[i + 1, j + 1] = legendre(N, xi[i + 1]) / \
                    legendre(N, xi[j + 1]) * 1.0 / (xi[i + 1] - xi[j + 1])
            if i == -1:
                if j == -1:
                    d[i + 1, j + 1] = -1.0 / 4.0 * N * (N + 1)
            if i == N-1:
                if j == N-1:
                    d[i + 1, j + 1] = 1.0 / 4.0 * N * (N + 1)

    # Calculate matrix with 1st derivatives of Lagrange polynomials
    for n in range(-1, N):
        for i in range(-1, N):
            sum = 0
            for j in range(-1, N):
                sum = sum + d[i + 1, j + 1] * lagrange(N, n, xi[j + 1])

            out[n + 1, i + 1] = sum
    return(out)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
'''
作者:@fm
'''


def lagrange(N, i, x):
    """
    Function to calculate  Lagrange polynomial for order N and polynomial
    i[0, N] at location x.
    """
    from gll import gll

    [xi, weights] = gll(N)
    fac = 1
    for j in range(-1, N):
        if j != i:
            fac = fac * ((x - xi[j + 1]) / (xi[i + 1] - xi[j + 1]))
    return fac
# -*- coding: utf-8 -*-
'''
作者:@fm
'''
import numpy as np
import matplotlib.pyplot as plt 
import os
import shutil
from gll import gll
from lagrange1st import lagrange1st 
from ricker import ricker
from creat_grid_sem import creat_mesh
from lagrange import lagrange
filepath='images_i21'
nt    = 1000         # number of time steps
xmax  = 600.        # Length of domain [m]
vs    = 2500.         # S velocity [m/s]
rho   = 2000          # Density [kg/m^3]
mu    = rho * vs**2   # Shear modulus mu
N     = 4           # Order of Lagrange polynomials
nx    = 30       # Number of x elements
ny    = 30           # Number of y elements
Tdom  = .1            # Dominant period of Ricker source wavelet
iplot = 30            # Plotting each iplot snapshot
# Space domain
le = xmax/nx        # Length of elements
NC, EI =  creat_mesh(le=le,nx=nx,ny=ny,N=N)
numN = np.size(NC,0)
# 一个单元有多少个节点
nodes_of_one_ele = np.size(EI,1)*np.size(EI,2)
# 计算最小空间间距
dx_min = min(np.diff(NC[0:N+1,0]))
dx_max = max(np.diff(NC[0:N+1,0]))
print(dx_min,dx_max)
# stability limit
eps = 0.1
dt = eps*dx_min/vs
# jacobian map
DJ = (le/2)**2 # (le/2)**2
[xi, wi] = gll(N)
[xj, wj] = gll(N)  
print('dt={:.4f},nt={:.4f}'.format(dt,nt))
def RemoveDir(filepath):
    '''
    如果文件夹不存在就创建,如果文件存在就清空!
    
    '''
    if not os.path.exists(filepath):
        os.mkdir(filepath)
    else:
        shutil.rmtree(filepath)
        os.mkdir(filepath)
def creat_mass_matrix(NC,EI):
    '''
    计算质量矩阵的逆
    '''
    EI =EI -1
    # 节点shul
    
    # 局部质量矩阵,只存储对角元素
    Me = np.zeros(nodes_of_one_ele, dtype =  float)
    n = 0
    for j in range(np.size(EI,2)):
        for i in range(np.size(EI,1)):
            Me[n]=rho*wi[i]*wj[j]*DJ
            n +=1
    M = np.zeros(numN, dtype =  float)
    # 组装质量矩阵
    for ele in EI:
        M[ele.flatten()] += Me
    Minv = np.identity(numN, dtype =  float)
    # 计算质量矩阵的逆
    for i in range(numN):
        Minv[i,i]=1./M[i]
    return Minv
        
        
            
    
def creat_stiffness_matrix(NC,EI):
    '''
    建立刚度矩阵
    '''
    EI=EI-1
    # 节点数量
    numN = np.size(NC,0)
    # 一个单元具有多少节点
    nodes_of_one_ele=np.size(EI,1)*np.size(EI,2)
    l1df = lagrange1st(N)
    # 全局刚度矩阵
    K = np.zeros([numN,numN], dtype =  float)
    # 局部刚度矩阵
    Ke = np.zeros([nodes_of_one_ele,nodes_of_one_ele], dtype =  float)
    # 计算局部刚度矩阵
    for i in range (nodes_of_one_ele):
        for j in range(nodes_of_one_ele):
            ix = i%(N+1)
            iy = i//(N+1)
            
            jx = j%(N+1)
            jy = j//(N+1)
            # 计算三种非零情况
            if ix==jx and iy==jy:
                for k in range(np.size(EI,1)):
                    Ke[i,j]+= mu*wi[k]*wj[jy]*l1df[ix,k]*l1df[jx,k]
                for l in range(np.size(EI,2)):
                    Ke[i,j]+= mu*wi[ix]*wj[l]*l1df[iy,l]*l1df[jy,l]
            elif ix==jx and not iy==jy:
                
                for l in range(np.size(EI,2)):
                    Ke[i,j]+= mu*wi[ix]*wj[l]*l1df[iy,l]*l1df[jy,l]
            elif not ix==jx and  iy==jy:
                for k in range(np.size(EI,1)):
                    Ke[i,j]+= mu*wi[k]*wj[jy]*l1df[ix,k]*l1df[jx,k]
    # 组装刚度矩阵
    for ele in EI:
        K[np.ix_(ele.flatten(), ele.flatten())]+=Ke
    print('K.size={}x{}'.format(numN,numN))
    return K
    
mass_inv = creat_mass_matrix(NC=NC,EI=EI)
K = creat_stiffness_matrix(NC=NC,EI=EI)
numN = np.size(NC,0)
# SE Solution, Time extrapolation
# ---------------------------------------------------------------
# initialize source time function and force vector f
# initialize time axis
t = np.arange(0, nt)*dt

# Initialization of the source time function
pt = 60*dt    # Gaussian width
t0 = 3*pt      # Time shift
src = -2/pt**2 * (t-t0) * np.exp(-1/pt**2 * (t-t0)**2)
# src  = ricker(dt,Tdom)
isrc = int(np.floor(numN/2))   # Source location
# isrc +=int( N/2+(nx*N+1)*(N/2))
# Initialization of solution vectors
u = np.zeros(numN, dtype =  float)
uold = np.zeros(numN, dtype =  float)
unew = np.zeros(numN, dtype =  float)
f = np.zeros(numN, dtype =  float) 
f[isrc]=1
print(u.shape)
px=[]
py=[]
for i in range(N*nx+1):
    px.append(NC[i,0])
for i in range(N*ny+1):
    py.append(NC[i*(N*nx+1),1])
xx,yy = np.meshgrid(px,py)
RemoveDir(filepath)
plt.figure()
for it in range(nt): 
    unew = dt**2 * mass_inv @ (f*src[it] - K @ u) + 2 * u - uold
    uold = u
    u = unew
    if not it % iplot:
        plt.pcolor(xx,yy,np.reshape(u,[N*nx+1,N*ny+1]))
        cb = plt.colorbar() 
        plt.axis('equal')
        plt.title('t={:.4f}s[{}x{}] N={} it={} vs={}'.format(dt*it,nx,ny,N,it,vs ))
        plt.savefig(filepath+'/{}.png'.format(it))
        cb.remove()
        print('{}%{}'.format(it, nt))


plt.show()
    
#!/usr/bin/env python
# -*- coding: utf-8 -*-
作者:@fm
import numpy as np


def legendre(N, x):
    """
    Returns the value of Legendre Polynomial P_N(x) at position x[-1, 1].
    """
    P = np.zeros(2 * N)

    if N == 0:
        P[0] = 1
    elif N == 1:
        P[1] = x
    else:
        P[0] = 1
        P[1] = x
    for i in range(2, N + 1):
        P[i] = (1.0 / float(i)) * ((2 * i - 1) * x * P[i - 1] - (i - 1) *
                                   P[i - 2])

    return(P[N])
  • 6
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值