A MATLAB raytracer for v (x, z)

2.12.2 A MATLAB raytracer for v (x, z)

The numerical solution of equations (2.83) and (2.84) can be done using well-tested and very general methods for solving first-order ordinary differential equations. An example is the fourth-order Runge- Kutta method (Press et al. (1992) chapter 16) that will be used here. This and other general methods are available in MATLAB; however, it is often useful to implement a specific Runge-Kutta scheme to gain improved flexibility.

To proceed, define the abstract rayvector #r = [#x, # p]. That is, in n dimensions #r is a 2n dimensional vector formed by concatenating the position and slowness vectors. In two dimensions, the ray vector is #r = [x z p x p z ] and the time derivative of #r is defined through equations (2.83) and (2.84) as

dr(1)

=v 2 r(3); dr(2) =v 2 dr(3)

Defining the vector #a = [v 2 p−# # ∇(ln v)], equation (2.89) becomes d#r

Then, given this prescription for d#r/dt and initial values for #r, the fourth-order Runge-Kutta (RK4) method is invoked to integrate equation (2.90) for #r(t).

The MATLAB implementation requires a velocity matrix giving v(x, z) on a grid with ∆x =

63 ∆z ≡ ∆g and uses the convention that (x = 0, z = 0) is in the upper left corner. Prior to

2.12. RAYTRACING FOR INHOMOGENEOUS MEDIA

raytracing, the function rayvelmod is invoked with arguments being the velocity matrix and the grid spacing ∆g. This function creates a number of global variables that will be used repeatedly

in the raytracing. These include matrices of v 2 ,∂ x ln v, and ∂ z ln v that are needed in equation (2.89). This precomputation speeds the raytracing and is especially beneficial if a great many rays are to be traced; however, the cost is three times the memory overhead of the simple velocity matrix. rayvelmod need only be called once at the beginning of the raytracing unless the velocity model is changed.

The function drayvec implements the computation of d#r/dt according to equation (2.89). This function is designed with the interface required by MATLAB’s built-in ODE solver ode45. This latter function implements an RK4 scheme by calling a user-designed function like drayvec that computes the time derivative of the solution vector. The general interface required by ode45 for such a function is that it must have two input arguments that are the current time and the current value of the solution vector #r. Thus, even though equation (2.90) does not require the value of t to compute d#r/dt, drayvec requires t as input but does not use it.

Code Snippet 2.12.1. This code builds a velocitymatrix representing v(x, z) = 1800 + .6z + .4x and then uses ode45 to trace a ray. It creates Figure 2.29.

1 dg=10; %grid spacing

2 tstep=0:.004:3; %time step vector

3 x=0:dg:10000;z=x’; %x and z coordinates

4 v=1800+.6*(z*ones(1,length(x)))+.4*(ones(length(z),1)*x);%velocity

5 rayvelmod(v,dg);clear v;%initialize the velocity model

6 theta=pi*45/180;%takeoff angle

7 r0=[0,0,sin(theta)/1800,cos(theta)/1800]’;%initial value of r

8 [t,r]=ode45(’drayvec’,tstep,r0);%solve for raypath

9 plot(r(:,1),r(:,2));flipy%plot

End Code

Code Snippet 2.12.1 illustrates the tracing of a single ray in a medium with both vertical and horizontal velocity gradients. The time-step vector is established on line 2 and a .004 second timestep is used. Smaller timesteps will improve accuracy but will also lengthen the computation. For a particular velocity model, some testing may be required to determine the optimal time step for the problem at hand. The velocity matrix is built on line 4 and then passed to rayvelmod on line 5. It is then cleared to save space because rayvelmod has established the required velocity information in global matrices. The takeoff angle and initial values for #r are calculated on lines 7 and 8. As mentioned in exercise 2.12.1, the components of # p are not independent of one another since # p·#p = v −2 and this is illustrated in the calculation of the initial values. Finally, ode45 is invoked to integrate equation (2.90) and thus compute #r for the vector of times established on line 2. The calculated ray is shown in Figure 2.29.

In the computation of d#r/dt it is generally true that the ray coordinates at a particular time will not coincide with grid points in the model. Thus the estimation of the velocity terms in equations (2.89) requires some form of interpolation. By default, drayvec uses nearest neighbor interpolation because this is the fastest method. For more accuracy, bilinear interpolation is also available and its use is controlled through a global variable that is explained in the drayvec help file.

A problem with the use of ode45, that is apparent upon close inspection of Figure 2.29, is that the ray has been traced beyond the bounds of the velocity model. To avoid indexing into the velocity array beyond its bounds, drayvec has been constructed to use the first (or last) column to represent

a simple v(z) medium for rays than go beyond the beginning (or end) of the model. A similar strategy is used to cope with rays that go beyond the minimum or maximum depths. Though this allows

64 CHAPTER 2. VELOCITY the solution to proceed, a better approach would be to detect when a ray has reached the boundary

of the model and stop the raytracing. For this purpose, an RK4 solver has been built as described in Press et al. (1992) and is incorporated in the functions shootrayvxz and shootrayvxz g . In these functions, the ray is tested as each time step to determine if it is within the model bounds and raytracing is halted before the maximum time if the ray leaves the model. The syntax to trace a ray with either program is essentially the same as with Code Snippet 2.12.1 except that the command [t,r]=shootrayvxz(tstep,r0) replaces [t,r]=ode45(’drayvec’,tstep,r0) on line 8. The functions shootrayvxz and shootrayvxz g differ in that the latter calls drayvec to compute d#r/dt while the former does this computation directly without the function call. The result is that shootrayvxz is much more efficient but does not offer bilinear interpolation as does shootrayvxz g . If nearest-neighbor interpolation is satisfactory, then shootrayvxz should be preferred because it is much faster.

Another useful raytracer is shootraytosurf . This function works similarly to shootrayvxz but the ray is traced until it reaches z = 0 or a maximum time limit is exceeded. This is useful in normal incidence raytrace modelling that will be discussed in chapter 4 in connection with normal incidence raytrace migration.

Chapter 3

Wave propagation