The numerical description of 1D-elastic waves propagating in a heterogeneous media is a natural extension of the homogeneous case. From an algorithmic point of view, now we allow both mass, and stiffness matrices to be initialized separately for each element. In contrast with the homogeneous case, elastic parameters \(\lambda\) and \(\mu\) may vary at each collocation point.
From a theoretical point of view, we basically follow the same strategy developed in the homogeneous case. The numerical solution for the 1D elastic wave equation
where \(\mathbf{M}\) is known as the mass matrix, and \(\mathbf{K}\) the stiffness matrix.
The above solution is exactly the same presented for the classic finite-element method. Now we introduce appropriated basis functions and integration scheme to efficiently solve the system of matrices.
Interpolation with Lagrange Polynomials
At the elemental level (see section 7.4), we introduce as interpolating functions the Lagrange polynomials and use \(\xi\) as the space variable representing our elemental domain:
The integral of a continuous function \(f(x)\) can be calculated after replacing \(f(x)\) by a polynomial approximation that can be integrated analytically. As interpolating functions we use again the Lagrange polynomials and obtain Gauss-Lobatto-Legendre quadrature. Here, the GLL points are used to perform the integral.
# Import all necessary libraries, this is a configuration step for the exercise.# Please run it before the simulation code!import numpy as npimport matplotlib# Show Plot in The Notebookmatplotlib.use("nbagg")import matplotlib.pyplot as pltfrom gll import gllfrom lagrange1st import lagrange1st from ricker import ricker
1. Initialization of setup
Code
# Initialization of setup# ---------------------------------------------------------------nt =7500# number of time stepsxmax =8000.# Length of domain [m]N =3# Order of Lagrange polynomialsne =150# Number of elementsTdom =.4# Dominant period of Ricker source waveletiplot =30# Plotting each iplot snapshotvs =2500.# S velocity [m/s]rho =2000# Density [kg/m^3]# variables for elemental matricesMe = np.zeros(N+1, dtype =float)Ke = np.zeros((N+1, N+1), dtype =float)# ----------------------------------------------------------------# Initialization of GLL points integration weights[xi, w] = gll(N) # xi, N+1 coordinates [-1 1] of GLL points# w Integration weights at GLL locations# Space domainle = xmax/ne # Length of elements# Vector with GLL points k =0xg = np.zeros((N*ne)+1) xg[k] =0for i inrange(1,ne+1):for j inrange(0,N): k = k+1 xg[k] = (i-1)*le +.5*(xi[j+1]+1)*le# ---------------------------------------------------------------dxmin =min(np.diff(xg)) eps =0.1# Courant valuedt = eps*dxmin/vs # Global time step# Mapping - JacobianJ = le/2Ji =1/J # Inverse Jacobian# 1st derivative of Lagrange polynomialsl1d = lagrange1st(N) # Array with GLL as columns for each N+1 polynomial
2. Low velocity zone
The introduction of an specific velocity model is done after allowing space-dependent elastic parameters. i.e.
The following python code introduces a low-velocity zone (-40%) at the center of the model spanning 50 elements and visualizes the model. There is the possibility to try different velocity and density models by defining new python methods.
Code
# Elastic parameters, Low velocity zone(-40%)# ----------------------------------------------------------------el_span =25# Number of elements spanning the Low velocity zonepercent =0.4# percentage of velocity reduction a = el_span*N +1# width of the fault zone in grid pointsb =int(np.floor((N*ne +1)/2)) # half of the space domain. vs = vs * np.ones((N*ne +1))rho = rho * np.ones((N*ne +1))vs[b-int(a/2):b+int(a/2)] =max(vs) * percentmu = rho * vs**2# Shear modulus mu
We implement the mass matrix at each element using the integration weights at GLL locations \(w\), the Jacobian \(J\), and density \(\rho\). Then, perform the global assembly of the mass matrix, compute its inverse, and display the inverse mass matrix to visually inspect how it looks like.
Code
# Global Mass matrix# --------------------------------------------------------------- k =-1m =-1ng = (ne-1)*N + N +1M = np.zeros(2*ng) for i inrange(1, ne+1): # ------------------------------------# Elemental Mass matrix# ------------------------------------for l inrange(0, N+1): m +=1 Me[l] = rho[m] * w[l] * J #stored as a vector since it's diagonal m -=1# ------------------------------------for j inrange(0, N+1): k = k +1if i>1:if j==0: k = k -1 M[k] = M[k] + Me[j]# Inverse matrix of M # --------------------------------------------------------------- Minv = np.identity(ng)for i inrange(0,ng): Minv[i,i] =1./M[i]# Display inverse mass matrix inv(M)# --------------------------------------------------------------plt.figure()plt.imshow(Minv)plt.title('Mass Matrix $\mathbf{M}$')plt.axis("off")plt.tight_layout()plt.show()
4. The Stiffness matrix
On the other hand, the general form of the stiffness matrix at the elemental level is
Implementing, now, for the stiffness matrix at each element using the integration weights at GLL locations \(w\), the Jacobian \(J\), and shear stress \(\mu\). Then, perform the global assembly of the mass matrix and display the matrix.
Code
# Global Stiffness Matrix# --------------------------------------------------------------- K = np.zeros([ng, ng])xe =0for e inrange(1, ne +1): i0 = (e -1)*N +1 j0 = i0# ------------------------------------# Elemental Stiffness Matrix# ------------------------------------for i inrange(-1, N):for j inrange(-1, N):sum=0for k inrange(-1, N): sum=sum+ mu[k+1+xe] * w[k+1] * Ji**2* J * l1d[i+1,k+1] * l1d[j+1,k+1] Ke[i+1, j+1] =sum xe += Nfor i inrange(-1,N):for j inrange(-1, N): K[i0+i, j0+j] += Ke[i+1, j+1]# --------------------------------------------------------------# Display stiffness matrix K# --------------------------------------------------------------plt.figure()plt.imshow(K)plt.title('Stiffness Matrix $\mathbf{K}$')plt.axis("off")plt.tight_layout()plt.show()
5. Finite element solution
Finally we implement the spectral element solution using the computed mass \(M\) and stiffness \(K\) matrices together with a finite differences extrapolation scheme