Understanding the input parameters for the simulation and the plots that are generated
Allowing you to explore the finite-difference method with optimal operators
Optimising Operators
Geller and Takeuchi (1995) developed criteria against which the accuracy of frequency-domain calculation of synthetic seismograms could be optimised. This approach was transferred to the time-domain finite-difference method for homogeneous and heterogeneous schemes by Geller and Takeuchi (1998). Look at an optimal operator and compare with the classic scheme. The space-time stencils are illustrated below
\[Conventional-(1/dt^2)\]
t+dt
1
t
-2
t-dt
1
x-dx
x
x+dx
\[Optimal-(1/dt^2)\]
t+dt
1/12
10/12
1/12
t
-2/12
-20/12
-2/12
t-dt
1/12
10/12
1/12
x-dx
x
x+dx
\[Conventional-(1/dx^2)\]
t+dt
t
1
-2
1
t-dt
x-dx
x
x+dx
\[Optimal-(1/dx^2)\]
t+dt
1/12
-2/12
1/12
t
10/12
-20/12
10/12
t-dt
1/12
-2/12
1/12
x-dx
x
x+dx
The conventional 2nd order finite-difference operators for the 2nd derivative are compared with the optimal operators developed by Geller and Takeuchi (1998), see text for details.
Note that summing up the optimal operators one obtains the conventional operators. This can be interpreted as a smearing out of the conventional operators in space and time. The optimal operators lead to a locally implicit scheme, as the future of the system at \((x,t+dt)\) depends on values at time level “\(t+dt\), i.e., the future depends on the future. That sounds impossible, but it can be fixed by using a predictor-corrector scheme based on the first-order Born approximation.
The optimal operators perform in a quite spectacular way. With very few extra floating point operations an accuracy improvement of almost an order of magnitude can be obtained. The optimal scheme performs substantially better than the conventional scheme with a 5-point operator.
Code
# Import Libraries# ----------------------------------------------import numpy as npimport matplotlib# Show Plot in The Notebookmatplotlib.use("nbagg")import matplotlib.pyplot as plt# Sub-plot Configuration# ----------------------from matplotlib import gridspec # Ignore Warning Messages# -----------------------import warningswarnings.filterwarnings("ignore")
Code
# Parameter Configuration # -----------------------nt =501# number of time stepseps =1.# stability limitxs =250# source location in grid in x-directionxr =450# receiver location in grid in x-direction# Material Parameters# -------------------rho =2500.# densityc0 =2000.# velocitymu = rho*(c0**2) # elastic modulus # Space Domain# ------------nx =2*xs # number of grid points in x-directiondx =2.*nx/(nx) # calculate space increment# Calculate Time Step from Stability Criterion# --------------------------------------------dt =.5*eps*dx/c0
Code
# Source Time Function # --------------------f0 =1./(20.*dt) # dominant frequency of the source (Hz)t0 =4./f0 # source time shift# Source Time Function (Gaussian)# -------------------------------src = np.zeros(nt)time = np.linspace(0* dt, nt * dt, nt)# 1st derivative of a Gaussiansrc =-8.*(time-t0)*f0*(np.exp(-1.*(4*f0)**2*(time-t0)**2))
# Snapshot # -------------------------------------------# Initialize Pressure Fields# --------------------------p = np.zeros(nx) # p at time n (now)pnew = p # p at time n+1 (present)pold = p # p at time n-1 (past)d2p = p # 2nd space derivative of pmp = np.zeros(nx) # mp at time n (now)mpnew= mp # mp at time n+1 (present)mpold= mp # mp at time n-1 (past)md2p = mp # 2nd space derivative of mpop = np.zeros(nx) # op at time n (now)opnew= op # op at time n+1 (present)opold= op # op at time n-1 (past)od2p = op # 2nd space derivative of opap = np.zeros(nx) # op at time n (now)# Initialize model (assume homogeneous model)# -------------------------------------------c = np.zeros(nx)c = c + c0 # initialize wave velocity in model# Initialize Coordinate# ---------------------x = np.arange(nx)x = x * dx # coordinate in x-direction# Initialize Empty Seismogram# ---------------------------sp = np.zeros(nt)smp = np.zeros(nt)sop = np.zeros(nt)sap = np.zeros(nt)# Plot Position Configuration# ---------------------------plt.ion()fig2 = plt.figure(figsize=(10, 6))gs2 = gridspec.GridSpec(1, 1, hspace=0.3, wspace=0.3)# Plot 1D Wave Propagation# ------------------------# Note: comma is needed to update the variableax3 = plt.subplot(gs2[0])up31,= ax3.plot(p, 'r') # plot pressure update each time stepup32,= ax3.plot(mp, 'g') # plot pressure update each time stepup33,= ax3.plot(op, 'b') # plot pressure update each time stepup34,= ax3.plot(ap, 'k') # plot pressure update each time stepax3.set_xlim(0, nx)lim =12.* src.max() * (dx) * (dt**2) / rhoax3.set_ylim(-lim, lim)ax3.set_title('Time Step (nt) = 0')ax3.set_xlabel('nx')ax3.set_ylabel('Amplitude')error1 = np.sum((np.abs(p - ap))) / np.sum(np.abs(ap)) *100error2 = np.sum((np.abs(mp - ap))) / np.sum(np.abs(ap)) *100error3 = np.sum((np.abs(op - ap))) / np.sum(np.abs(ap)) *100ax3.legend((up31, up32, up33, up34),\('3 point FD: %g%%'% error1,\'5 point FD: %g%%'% error2,\'optimal FD: %g%%'% error3,\'analytical'), loc='lower right', fontsize=10, numpoints=1)plt.show()
Code
# 1D Wave Simulation # -------------------------------------------------------# Calculate Partial Derivatives# -----------------------------for it inrange(nt):# 3 Point Operator FD scheme# --------------------------for i inrange(2, nx -2): d2p[i] = (1.* p[i +1] -2.* p[i] +1.* p[i -1]) / (dx**2)# Time Extrapolation pnew =2.* p - pold + c**2* d2p * dt**2# Add Source Term at xs pnew[xs] = pnew[xs] + src[it] * (dx) * (dt**2) / rho# Remap Time Levels pold, p = p, pnew# Set Boundaries Pressure Free p[0] =0. p[nx-1] =0.# Seismogram sp[it] = p[xr]# 5 Point Operator FD scheme# --------------------------for i inrange(2, nx -2): md2p[i] = (-1./12.* mp[i +2] +4./3.* mp[i +1] -5./2.* mp[i]\+4./3.* mp[i -1] -1./12.* mp[i -2]) / (dx**2)# Time Extrapolation mpnew =2.* mp - mpold + c**2* md2p * dt**2# Add Source Term at xs mpnew[xs] = mpnew[xs] + src[it] * (dx) * (dt**2) / rho# Remap Time Levels mpold, mp = mp, mpnew# Set Boundaries Pressure Free mp[0] =0. mp[nx-1] =0.# Seismogram smp[it] = mp[xr]# Optimal Operator Scheme# -----------------------for i inrange(2, nx -2): od2p[i] = (1.* op[i +1] -2.* op[i] +1.* op[i -1]) / (dx**2)# Time Extrapolation opnew =2.* op - opold + c**2* od2p * dt**2# Calculate Corrector odp = op *0.# Corrector at x-dx, x and x+dxfor i inrange(2, nx -2): odp[i] = opold[i -1: i +2] * d0[:, 0]\+ op[i -1: i +2] * d0[:, 1]\+ opnew[i -1: i +2] * d0[:, 2] opnew = opnew + odp# Add Source Term at xs opnew[xs] = opnew[xs] + src[it] * (dx) * (dt**2) / rho# Remap Time Levels opold, op = op, opnew# Set Boundaries Pressure Free op[0] =0. op[nx-1] =0.# Seismogram sop[it] = op[xr]# Analytical Solution# ------------------- sig = f0 * dt x0 = xs * dx it0 = (4./sig)-1. ap = (np.exp(-(4*sig)**2* (x-x0 - c0*(it-it0)*dt)**2)\+ np.exp(-(4*sig)**2* (x-x0 + c0*(it-it0)*dt)**2)) ap = ap / np.max([np.abs(ap.min()), np.abs(ap.max())])\* np.max([np.abs(op.min()), np.abs(op.max())])# Seismogram sap[it] = ap[xr]# Update Data For Wave Propagation Plot# ------------------------------------- idisp =2# display frequencyif (it % idisp) ==0: ax3.set_title('Time Step (nt) = %d'% it) up31.set_ydata(p) up32.set_ydata(mp) up33.set_ydata(op) up34.set_ydata(ap) error1 = np.sum((np.abs(p - ap))) / np.sum(np.abs(ap)) *100 error2 = np.sum((np.abs(mp - ap))) / np.sum(np.abs(ap)) *100 error3 = np.sum((np.abs(op - ap))) / np.sum(np.abs(ap)) *100 ax3.legend((up31, up32, up33, up34),\ ('3 point FD: %g%%'% error1,\'5 point FD: %g%%'% error2,\'optimal FD: %g%%'% error3,\'analytical'), loc='lower right', fontsize=10, numpoints=1) plt.gcf().canvas.draw()