├── .gitignore ├── README.md ├── abstract.tex ├── bibliography ├── .placeholder.md └── biblio.bib ├── data └── synthetic_cmp.npz ├── environment.yml ├── figures ├── .placeholder.md ├── nmo-application │ ├── caption.tex │ ├── config.yml │ ├── nmo-application.png │ └── nmo-application_original.png └── sketch-interpolation │ ├── caption.tex │ ├── config.yml │ ├── sketch-interpolation.png │ └── sketch-interpolation_original.png ├── header.tex ├── how-to-nmo.tex ├── intro.tex ├── layout.md ├── step-by-step-nmo.ipynb ├── thanks.tex ├── the-code-of-nmo.tex ├── title.md └── title.tex /.gitignore: -------------------------------------------------------------------------------- 1 | .ipynb_checkpoints 2 | .env 3 | *~ 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Step-by-step NMO correction 2 | 3 | by [Leonardo Uieda](http://www.leouieda.com) 4 | 5 | *This is a part of The Leading Edge [geophysical tutorials 6 | series](https://dx.doi.org/10.1190/tle35020190.1).* 7 | 8 | doi: [10.1190/tle36020179.1](http://dx.doi.org/10.1190/tle36020179.1) 9 | 10 | The tutorials are all **open access** and can be freely downloaded from the SEG 11 | website (follow the doi link). 12 | 13 | The manuscript was written in [Authorea](https://www.authorea.com). 14 | You can view and comment on the text online at https://www.authorea.com/users/1856/articles/142722/_show_article 15 | 16 | All source code used in the tutorial is in the Jupyter notebook [step-by-step-nmo.ipynb](http://nbviewer.jupyter.org/github/pinga-lab/nmo-tutorial/blob/master/step-by-step-nmo.ipynb) 17 | 18 | You can read it online or run it locally in your own computer (see [Running the code](#running-the-code) below for instructions). 19 | 20 | This repository contains the Latex source files for the tutorial text and the 21 | Jupyter notebook with the accompanying Python code. 22 | Both figures from the tutorial were generated by the code in the notebook. 23 | 24 | 25 | ## Abstract 26 | 27 | Open any text book about seismic data processing and you will inevitably find a 28 | section about the Normal Moveout (NMO) correction. 29 | When applied to a Common Mid Point (CMP) section, the correction is supposed to 30 | turn the hyperbola associated with a reflection into a straight horizontal 31 | line. 32 | What most text books won't tell you is *how, exactly, do you apply this 33 | equation to the data*? 34 | 35 | That is what this tutorial will teach you (hopefully). 36 | 37 | 38 | 39 | ## Running the code 40 | 41 | The code that accompanies the tutorial is in the `step-by-step-nmo.ipynb` 42 | [Jupyter notebook](http://jupyter.org/). 43 | 44 | You can run it in our own machine by following these steps: 45 | 46 | 1. Make sure you have Python 3.5 with the latest versions of numpy, scipy, 47 | matplotlib, and jupyter installed. The easiest way to do that is by 48 | installing the [Anaconda distribution](https://www.continuum.io/downloads#all). 49 | 2. Download a copy of this repository ([click here to get a zip 50 | file](https://github.com/pinga-lab/nmo-tutorial/archive/master.zip)). Unzip 51 | the repository, preferably in your Desktop folder. 52 | 3. Open a terminal (or `cmd.exe` in Windows) and start the Jupyter notebook 53 | server by running the command `jupyter notebook`. 54 | 4. In the Jupyter web interface, navigate to the repository and click to open 55 | the notebook. 56 | 5. Rejoice! 57 | 58 | ![](http://i.giphy.com/WIg8P0VNpgH8Q.gif) 59 | 60 | ## License 61 | 62 | All text and figures are licensed under a 63 | [CC-BY](http://creativecommons.org/licenses/by/4.0/deed.en_US). 64 | This means that you can use, copy, modify, and redistribute it provided you 65 | give attribution to the original authors. 66 | 67 | All source code is licensed under a [BSD 68 | 3-clause](https://opensource.org/licenses/BSD-3-Clause) license. 69 | This means that you can do whatever you want with the code provided you give 70 | attribution to the original authors and that you cannot blame the authors if 71 | something bad happens as a consequence of the code. 72 | -------------------------------------------------------------------------------- /abstract.tex: -------------------------------------------------------------------------------- 1 | Corresponding author: leouieda@gmail.com 2 | 3 | This is a part of The Leading Edge ``Geophysical Tutorials'' series. 4 | You can read more about it in \citet{Hall_2016a}. 5 | 6 | Open any textbook about seismic data processing and you will inevitably find a 7 | section about the normal moveout (NMO) correction. 8 | There you'll see that we can correct the measured travel-time of a reflected 9 | wave $t$ at a given offset $x$ to obtain the travel-time at normal 10 | incidence $t_0$ by applying the following equation 11 | 12 | \begin{equation} 13 | t_0^2=t^2-\dfrac{x^2}{v_\mathrm{NMO}^2} 14 | \label{eq:traveltime} 15 | \end{equation} 16 | 17 | in which $v_\mathrm{NMO}$ is the NMO velocity. 18 | There are variants of this equation with different degrees of accuracy, 19 | but we'll use this one for simplicity. 20 | 21 | When applied to a common midpoint (CMP) section, the equation above is 22 | supposed to turn the hyperbola associated with a reflection into a straight 23 | horizontal line. 24 | What most textbooks won't tell you is \textit{how, exactly, do you apply this 25 | equation to the data}? 26 | 27 | Read on and I'll explain step-by-step how the algorithm for NMO correction from 28 | \citet{Yilmaz_2001} works and how to implement it in Python. 29 | The accompanying Jupyter notebook \citep{Perez_2007} contains the full source 30 | code, with documentation and tests for each function. 31 | You can download the notebook at 32 | \href{https://github.com/seg}{github.com/seg} or 33 | \href{https://github.com/pinga-lab/nmo-tutorial}{github.com/pinga-lab/nmo-tutorial}. 34 | -------------------------------------------------------------------------------- /bibliography/.placeholder.md: -------------------------------------------------------------------------------- 1 | this is a placeholder for the directory 2 | -------------------------------------------------------------------------------- /bibliography/biblio.bib: -------------------------------------------------------------------------------- 1 | @article{2012, 2 | author = {CMS/CERN}, 3 | title = {{A New Boson with a Mass of 125 GeV Observed with the CMS Experiment at the Large Hadron Collider}}, 4 | volume = {338}, 5 | number = {6114}, 6 | pages = {1569-1575}, 7 | year = {2012}, 8 | doi = {10.1126/science.1230816}, 9 | eprint = {http://www.sciencemag.org/content/338/6114/1569.full.pdf}, 10 | journal = {Science}, 11 | } 12 | 13 | 14 | @article{Holstein_2009, 15 | doi = {10.1088/1742-6596/173/1/012019}, 16 | url = {http://dx.doi.org/10.1088/1742-6596/173/1/012019}, 17 | year = {2009}, 18 | month = {jun}, 19 | publisher = {{IOP} Publishing}, 20 | volume = {173}, 21 | pages = {012019}, 22 | author = {Barry R Holstein}, 23 | title = {{The mysterious disappearance of Ettore Majorana}}, 24 | journal = {J. Phys.: Conf. Ser.}, 25 | } 26 | 27 | 28 | @book{Yilmaz_2001, 29 | doi = {10.1190/1.9781560801580}, 30 | url = {http://dx.doi.org/10.1190/1.9781560801580}, 31 | year = {2001}, 32 | month = {jan}, 33 | publisher = {Society of Exploration Geophysicists}, 34 | author = {Öz Yilmaz}, 35 | title = {{Seismic Data Analysis}}, 36 | } 37 | 38 | 39 | @article{Perez_2007, 40 | doi = {10.1109/mcse.2007.53}, 41 | url = {http://dx.doi.org/10.1109/mcse.2007.53}, 42 | year = {2007}, 43 | publisher = {Institute of Electrical and Electronics Engineers ({IEEE})}, 44 | volume = {9}, 45 | number = {3}, 46 | pages = {21--29}, 47 | author = {Fernando Perez and Brian E. Granger}, 48 | title = {{{IPython}: A System for Interactive Scientific Computing}}, 49 | journal = {Computing in Science {\&} Engineering}, 50 | } 51 | 52 | 53 | @article{Hall_2016, 54 | doi = {10.1190/tle35040367.1}, 55 | url = {http://dx.doi.org/10.1190/tle35040367.1}, 56 | year = {2016}, 57 | month = {apr}, 58 | publisher = {Society of Exploration Geophysicists}, 59 | volume = {35}, 60 | number = {4}, 61 | pages = {367--369}, 62 | author = {Matt Hall}, 63 | title = {{Geophysical Tutorial: The function of interpolation}}, 64 | journal = {The Leading Edge}, 65 | } 66 | 67 | 68 | @article{Hall_2016a, 69 | doi = {10.1190/tle35020190.1}, 70 | url = {http://dx.doi.org/10.1190/tle35020190.1}, 71 | year = {2016}, 72 | month = {feb}, 73 | publisher = {Society of Exploration Geophysicists}, 74 | volume = {35}, 75 | number = {2}, 76 | pages = {190--191}, 77 | author = {Matt Hall}, 78 | title = {{A user guide to the geophysical tutorials}}, 79 | journal = {The Leading Edge}, 80 | } 81 | 82 | 83 | @article{van_der_Walt_2011, 84 | doi = {10.1109/mcse.2011.37}, 85 | url = {http://dx.doi.org/10.1109/MCSE.2011.37}, 86 | year = {2011}, 87 | month = {mar}, 88 | publisher = {Institute of Electrical and Electronics Engineers ({IEEE})}, 89 | volume = {13}, 90 | number = {2}, 91 | pages = {22--30}, 92 | author = {Ste{\'}fan van der Walt and S Chris Colbert and Gaël Varoquaux}, 93 | title = {{The {NumPy} Array: A Structure for Efficient Numerical Computation}}, 94 | journal = {Computing in Science {\&} Engineering}, 95 | } 96 | -------------------------------------------------------------------------------- /data/synthetic_cmp.npz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinga-lab/nmo-tutorial/8b3348010ba06ea0542628b930e0be6b149fb59e/data/synthetic_cmp.npz -------------------------------------------------------------------------------- /environment.yml: -------------------------------------------------------------------------------- 1 | name: nmo-tutorial 2 | dependencies: 3 | - python=3.5 4 | - numpy 5 | - scipy 6 | - matplotlib 7 | - jupyter 8 | -------------------------------------------------------------------------------- /figures/.placeholder.md: -------------------------------------------------------------------------------- 1 | this is a placeholder for the directory 2 | -------------------------------------------------------------------------------- /figures/nmo-application/caption.tex: -------------------------------------------------------------------------------- 1 | Figure 2: (Left) The $v_\mathrm{NMO}$ profile passed to 2 | \texttt{nmo\_correction}. 3 | The profile was interpolated on a line using the two picked velocities (black 4 | squares). (Middle) A synthetic CMP gather. (Right) The output 5 | from our \texttt{nmo\_correction} function. 6 | -------------------------------------------------------------------------------- /figures/nmo-application/config.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | multicolumn: false 3 | export-width: 0.7\columnwidth 4 | web-width: 500px 5 | align: center 6 | float: h! 7 | -------------------------------------------------------------------------------- /figures/nmo-application/nmo-application.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinga-lab/nmo-tutorial/8b3348010ba06ea0542628b930e0be6b149fb59e/figures/nmo-application/nmo-application.png -------------------------------------------------------------------------------- /figures/nmo-application/nmo-application_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinga-lab/nmo-tutorial/8b3348010ba06ea0542628b930e0be6b149fb59e/figures/nmo-application/nmo-application_original.png -------------------------------------------------------------------------------- /figures/sketch-interpolation/caption.tex: -------------------------------------------------------------------------------- 1 | Figure 1: Sketch of the algorithm for a single trace and $t_0$. To the left 2 | is a trace from the CMP and to the right the corresponding trace from the NMO 3 | corrected gather. The green square in the NMO is the amplitude at $t_0$ that 4 | we want to calculate. We apply the equation to find time $t$ in the CMP, then 5 | interpolate the amplitude using the four samples around $t$ (orange 6 | rectangles). This amplitude is then copied over to the NMO. 7 | -------------------------------------------------------------------------------- /figures/sketch-interpolation/config.yml: -------------------------------------------------------------------------------- 1 | version: 1 2 | multicolumn: false 3 | export-width: 0.7\columnwidth 4 | web-width: 500px 5 | align: center 6 | float: h! 7 | -------------------------------------------------------------------------------- /figures/sketch-interpolation/sketch-interpolation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinga-lab/nmo-tutorial/8b3348010ba06ea0542628b930e0be6b149fb59e/figures/sketch-interpolation/sketch-interpolation.png -------------------------------------------------------------------------------- /figures/sketch-interpolation/sketch-interpolation_original.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pinga-lab/nmo-tutorial/8b3348010ba06ea0542628b930e0be6b149fb59e/figures/sketch-interpolation/sketch-interpolation_original.png -------------------------------------------------------------------------------- /header.tex: -------------------------------------------------------------------------------- 1 | \usepackage{listings} 2 | \usepackage{natbib} 3 | \usepackage{hyperref} 4 | 5 | \lstset{ 6 | showstringspaces=true, 7 | backgroundcolor=\color{white}, % choose the background color 8 | % basicstyle=\footnotesize, % size of fonts used for the code 9 | breaklines=true, % automatic line breaking only at whitespace 10 | keywordstyle=\color{black}, % keyword style 11 | stringstyle=\color{black}, % string literal style 12 | language=python, % Set your language (you can change the language for each code-block optionally) 13 | frame=lrtb, % 14 | xleftmargin=\fboxsep, % 15 | xrightmargin=-\fboxsep, % 16 | } 17 | -------------------------------------------------------------------------------- /how-to-nmo.tex: -------------------------------------------------------------------------------- 1 | \section{Doing it backwards} 2 | 3 | It's surprisingly difficult to find a description of a method for calculating 4 | the amplitudes in the NMO correction. 5 | The only one I could find is a single paragraph in the book by 6 | \citet{Yilmaz_2001} (available on the SEG Wiki at 7 | \href{http://wiki.seg.org/wiki/NMO_for_a_flat_reflector}{wiki.seg.org/wiki/NMO\_for\_a\_flat\_reflector}): 8 | 9 | \begin{quotation} 10 | ``The idea is to find the amplitude value at A' on the NMO-corrected gather 11 | from the amplitude value at A on the original CMP gather. Given quantities 12 | $t_0$, $x$, and $v_\mathrm{NMO}$, compute $t$ from equation (1). [...] The 13 | amplitude value at this time can be computed using the amplitudes at the 14 | neighboring integer sample values [...] This is done by an interpolation scheme 15 | that involves the four samples.'' 16 | \end{quotation} 17 | 18 | This paragraph is telling us to do the calculation backwards. 19 | Instead of mapping where each point in the CMP goes in the NMO corrected 20 | gather, we should map where each point in the NMO gather comes from in the CMP. 21 | Figure 1 shows a sketch of the procedure to calculate the amplitude of a point 22 | ($t_0$, $x$) in the NMO gather. 23 | 24 | Here is the full algorithm: 25 | 26 | \begin{enumerate} 27 | \item Start with an NMO gather filled with zeros. 28 | \item For each point ($t_0, x$) in the NMO gather, do: 29 | \begin{enumerate} 30 | \item Calculate the reflection travel-time ($t$) given $v_\mathrm{NMO}$ using 31 | the equation in Figure 1. 32 | \item Go to the trace at offset $x$ in the CMP and find the two samples 33 | before and the two samples after time $t$. 34 | \item If $t$ is greater than the recording time or if it doesn't have 35 | two samples after it, skip the next two steps. 36 | \item Use the amplitude in these four samples to interpolate the 37 | amplitude at time $t$. 38 | \item Copy the interpolated amplitude to the NMO gather at ($t_0, x$). 39 | \end{enumerate} 40 | \end{enumerate} 41 | 42 | At the end of this algorithm, we will have a fully populated NMO gather with 43 | the amplitudes sampled from the CMP. 44 | Notice that we didn't actually use the equation for $t_0$. 45 | Instead we calculate the reflection travel-time ($t$). 46 | Good luck guessing that from the equation alone. 47 | -------------------------------------------------------------------------------- /intro.tex: -------------------------------------------------------------------------------- 1 | \section{What the equation doesn't tell us} 2 | 3 | Equation \ref{eq:traveltime} relates travel-times: the one we can measure ($t$) 4 | and the one we want to know ($t_0$). 5 | But the data in our CMP gather are actually a matrix of \textit{amplitudes} 6 | measured as a function of time ($t$) and offset. 7 | Our NMO corrected gather will also be a matrix of amplitudes as a function of 8 | time ($t_0$) and offset. 9 | So what we really have to do transform one matrix of amplitudes into the other. 10 | \textit{But the equation has no amplitudes}! 11 | 12 | This is a major divide between the formula we've all seen before and what 13 | actually goes on in the software that implements it. 14 | You have probably never thought about it -- I certainly hadn't -- 15 | so let's bridge this divide. 16 | Next, I'll explain an algorithm that maps the amplitudes in the CMP to 17 | amplitudes in an NMO corrected gather. 18 | -------------------------------------------------------------------------------- /layout.md: -------------------------------------------------------------------------------- 1 | abstract.tex 2 | intro.tex 3 | how-to-nmo.tex 4 | figures/sketch-interpolation/sketch-interpolation.png 5 | the-code-of-nmo.tex 6 | figures/nmo-application/nmo-application.png 7 | thanks.tex 8 | -------------------------------------------------------------------------------- /thanks.tex: -------------------------------------------------------------------------------- 1 | \section{Acknowledgments} 2 | 3 | My sincerest thanks to Evan Bianco, Gregorio Kawakami, Jesper Dramsch, and Matt Hall 4 | for comments and suggestions 5 | and to Öz Yilmaz for generously making the full text of the 6 | ``Seismic Data Analysis'' book available for free and in the open. 7 | -------------------------------------------------------------------------------- /the-code-of-nmo.tex: -------------------------------------------------------------------------------- 1 | \section{The code for NMO} 2 | 3 | Now I'll show how to implement the above algorithm in Python using the NumPy and SciPy libraries \citep{van_der_Walt_2011}. 4 | We'll split the algorithm into three functions. 5 | This is very important when programming any moderately complex code because it allows us to test each part of our code independently. 6 | It also reduces the amount of code we have to search through to find that bug that is messing up our results. 7 | Modular code is easier to understand and to reuse. 8 | 9 | The first function I'll define performs the NMO correction on a given CMP gather. 10 | We'll assume that the CMP gather is a 2D array of amplitudes and that the 11 | velocities are a 1D array with $v_\mathrm{NMO}$ for each time sample. 12 | 13 | \begin{lstlisting}[language=python] 14 | import numpy as np 15 | 16 | def nmo_correction(cmp, dt, offsets, velocities): 17 | nmo = np.zeros_like(cmp) 18 | nsamples = cmp.shape[0] 19 | times = np.arange(0, nsamples*dt, dt) 20 | for i, t0 in enumerate(times): 21 | for j, x in enumerate(offsets): 22 | t = reflection_time(t0, x, velocities[i]) 23 | amplitude = sample_trace(cmp[:, j], t, dt) 24 | if amplitude is not None: 25 | nmo[i, j] = amplitude 26 | return nmo 27 | \end{lstlisting} 28 | 29 | This function is essentially the algorithm above translated to Python with some 30 | of the details pushed into the \texttt{reflection\_time} and 31 | \texttt{sample\_trace} functions, which we will define below. 32 | 33 | First, the function that calculates the reflection travel-time: 34 | 35 | \begin{lstlisting}[language=python] 36 | def reflection_time(t0, x, vnmo): 37 | t = np.sqrt(t0**2 + x**2/vnmo**2) 38 | return t 39 | \end{lstlisting} 40 | 41 | For the \texttt{sample\_trace} function, we'll use cubic splines from the 42 | \texttt{scipy.interpolate} package. 43 | For more information on interpolation with scipy, see the tutorial by 44 | \citet{Hall_2016}. 45 | 46 | \begin{lstlisting}[language=python] 47 | from scipy.interpolate import CubicSpline 48 | 49 | def sample_trace(trace, time, dt): 50 | before = int(np.floor(time/dt)) 51 | N = trace.size 52 | samples = np.arange(before - 1, before + 3) 53 | if any(samples < 0) or any(samples >= N): 54 | amplitude = None 55 | else: 56 | times = dt*samples 57 | amps = trace[samples] 58 | interpolator = CubicSpline(times, amps) 59 | amplitude = interpolator(time) 60 | return amplitude 61 | \end{lstlisting} 62 | 63 | The Jupyter notebook contains the full code for these functions, including 64 | documentation through Python documentation strings or ``docstrings'' and code 65 | that tests that the functions work as expected. 66 | Also included is an application of our \texttt{nmo\_correction} function to a 67 | synthetic CMP (Figure 2). 68 | -------------------------------------------------------------------------------- /title.md: -------------------------------------------------------------------------------- 1 | Step-by-step NMO correction -------------------------------------------------------------------------------- /title.tex: -------------------------------------------------------------------------------- 1 | Step-by-step NMO correction --------------------------------------------------------------------------------