next up previous [pdf]

Next: Coding chains and arrays Up: FAMILIAR OPERATORS Previous: Smoothing with box and

Nearest-neighbor normal moveout (NMO)

Normal-moveout correction (NMO) is a geometrical correction of reflection seismic data that stretches the time axis so that data recorded at nonzero separation $x_0$ of shot and receiver, after stretching, appears to be at $x_0=0$. NMO correction is roughly like time-to-depth conversion with the equation $v^2 t^2 = z^2 + x_0^2$. After the data at $x_0$ is stretched from $t$ to $z$, it should look like stretched data from any other $x$ (assuming these are plane horizontal reflectors, etc.). In practice, $z$ is not used; rather, traveltime depth $\tau$ is used, where $\tau =z/v$; so $t^2 = \tau^2+x_0^2/v^2$. (Because of the limited alphabet of programming languages, I often use the keystroke z to denote $\tau$.)

Typically, many receivers record each shot. Each seismogram can be transformed by NMO and the results all added. This is called ``stacking'' or ``NMO stacking.'' The adjoint to this operation is to begin from a model which ideally is the zero-offset trace and spray this model to all offsets. From a matrix viewpoint, stacking is like a row vector of normal moveout operators and modeling is like a column. An example is shown in Figure 8.

cunha
Figure 8.
Hypothetical model, synthetic data, and model image.
cunha
[pdf] [png] [scons]

A module that does reverse moveout is hypotenusei. Given a zero-offset trace, it makes another at non-zero offset. The adjoint does the usual normal moveout correction.

user/gee/hypotenusei.c
void hypotenusei_set(float t0 /* time origin */, 
		     float dt /* time sampling */, 
		     float xs /* offset times slowness */)
/*< set up >*/
{
    int it;
    float t, z2;

    for (it=0; it < nt; it++) {  
	t = t0 + dt*it;
        z2 =  t * t - xs * xs;
	iz[it] = ( z2 >= 0.)? 0.5 + (sqrtf(z2) - t0) /dt: -1;
    }
}

void hypotenusei_lop(bool adj, bool add, 
		     int n1, int n2, float *zz, float *tt)
/*< linear operator >*/
{
    int  it;
    
    sf_adjnull(adj,add,n1,n2,zz,tt);

    for (it=0; it < nt; it++) {  
	if (iz[it] < 0) continue;

	if (adj) 
	    zz[iz[it]] +=  tt[it];
	else 
	    tt[it] +=  zz[iz[it]];
    }
}
(My 1992 textbook (PVI) illustrates many additional features of normal moveout.) A companion routine imospray loops over offsets and makes a trace for each. The adjoint of imospray is the industrial process of moveout and stack.
user/gee/imospray.c
void imospray_lop(bool adj, bool add, int n1, int n2, 
		  float *stack, float *gather)
/*< linear operator >*/
{
    int ix;
    float x;

    sf_adjnull(adj,add,n1,n2,stack,gather);

    for (ix=0; ix < nx; ix++) {
	x = x0 + dx*ix;

        hypotenusei_set (t0, dt, x);
	hypotenusei_lop (adj, true, nt, nt, stack, gather+ix*nt);
    }
}

This is the first time we have built an operator (moveout and stack) from a collection of other operators (moveout at various offsets) and there are new possibilities for confusion and program bugs. As earlier with the matrix multiplication operator, to use the imospray operator, there are two steps, one that we use to set things up

        imospray_init( slow, x0,dx, t0,dt, nt,nx);
and another step that we use a lot in the next chapter for analysis and data fitting.
        imospray_lop( adj, add, n1, n2, stack, gather);
Later we'll see programs that are not operators. Consider the the adjoint of spraying which is stacking. Here the occurance of the add=true in imospray assures we do not erase the stack each time we add in another trace.


next up previous [pdf]

Next: Coding chains and arrays Up: FAMILIAR OPERATORS Previous: Smoothing with box and

2012-01-12