Normal-moveout
correction (NMO) is a geometrical correction
of reflection seismic data
that stretches the time axis so that data recorded
at nonzero separation x0 of shot and receiver,
after stretching, appears to be at x0=0.
NMO correction is roughly like time-to-depth conversion
with the equation v2 t2 = z2 + x02.
After the data at x0 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 is used,
where
;so
.(Because of the limited alphabet of programming languages,
I often use the keystroke z to denote
.)
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 that is identical to the near-offset trace and spray this trace to all offsets. From a matrix viewpoint, stacking is like a row vector of normal moveout operators and modeling is like a column.
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.
module hypotenusei { # Inverse normal moveout
integer :: nt
integer, dimension (nt), allocatable :: iz
#% _init( nt, t0, dt, xs)
integer it
real t0, dt, xs, t, zsquared
do it= 1, nt { t = t0 + dt*(it-1)
zsquared = t * t - xs * xs
if ( zsquared >= 0.)
iz (it) = 1.5 + (sqrt( zsquared) - t0) /dt
else
iz (it) = 0
}
#% _lop( zz, tt)
integer it
do it= 1, nt {
if ( iz (it) > 0 ) { if( adj)
zz( iz(it)) += tt( it )
else
tt(it) += zz( iz(it))
}
}
}
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.
My 1992 textbook (PVI) illustrates many additional features of normal moveout.
do ix= 1, nx {
call hypotenusei_init ( nt, t0, dt, x0 + dx*(ix-1))
stat = hypotenusei_lop ( adj, .true., stack, gather(:,ix))
}
call hypotenusei_close ()
}
module imospray { # inverse moveout and spray into a gather.
use hypotenusei
real :: x0,dx, t0,dt
integer :: nx,nt
#% _init (slow, x0,dx, t0,dt, nt,nx)
real slow
x0 = x0*slow
dx = dx*slow
#% _lop( stack(nt), gather(nt,nx))
integer ix, stat