Depth Reference and Sampling Checks
On this page
Summary
Every log is a function of depth, so an error in the depth scale is an error in every curve at once. This page covers the checks to make on a depth index (units, step, monotonicity, gaps, duplicates), the Depth reference the depths are measured from, and the depth shifts between runs and between logs and core. Use it before splicing, normalizing or comparing with any other depth-based data.
Inputs and outputs
| Item | Notes | |
|---|---|---|
| Input | The depth index of each curve or file | And its stated units |
| Input | The elevations of the depth references | Kelly bushing, drill floor, ground level, sea level |
| Input | A reference curve for depth matching | Normally gamma ray |
| Output | A list of findings about the depth index | Empty if it passes |
| Output | The depth reference and units of each data set | Stated explicitly |
| Output | The depth shifts needed between runs or against core | Recorded |
Equations
Most of the checks are comparisons. The step between samples is
and the index is acceptable when \(\Delta z_i > 0\) for all \(i\), and \(\Delta z_i\) equals the nominal step within a small tolerance. The nominal step is taken as the median of \(\Delta z_i\), which is not distorted by a few gaps. A \(\Delta z_i\) of \(m\) times the step marks \(m - 1\) missing samples, a value of zero marks a duplicate and a negative value marks a reversal.
A step of 0.1524 m is exactly 0.5 ft expressed in metres, which marks a file that was converted from feet.
Depth references. A depth \(z\) measured below a reference at elevation \(E\) above mean sea level lies at elevation \(E - z\). Its depth below mean sea level (positive down, for a vertical well) is
and depths move from one reference (elevation \(E_1\)) to another (elevation \(E_2\)) by
Both are written for a vertical well. In a deviated well, true vertical depth must be computed from the survey first, and the reference correction is applied to TVD.
The procedure is:
- Establish the units and the step. Read them from the header and confirm from the values.
- Check that the index is monotonic, without duplicates and without gaps, and report each finding.
- Establish the depth reference of every data set (logs, core, tops, perforations) and bring them to one.
- Depth-match runs to each other, and logs to core, with a feature-rich curve. See Curve Splicing and Merging.
- Resample to a common step only when needed, and not by interpolating to a finer step than the data support.
Single-value calculator
No calculator: this method is a procedure, not a single equation.
Behavior
The clean index (0.5 steps) passes. The damaged index has one reversal, one duplicate and two reported gaps, but the second gap, after 5003.5, is an artefact of the reversal (the sample before it went backwards), which shows that one error can produce several findings and that they should be read in order. A step of 0.5 is ambiguous between feet and metres, and the header must decide it, while a step of 0.1524 identifies a metric file written from feet. For the datum example, 8000 ft below a kelly bushing at 1250 ft elevation is 6750 ft below sea level, the same point is 7982 ft below a ground level at 1232 ft, and a core depth of 7982 ft below ground level is 8000 ft below the kelly bushing.
Parameter guidance
Tolerance. Use a small tolerance on the step (a fraction of a percent) so that floating-point and rounding noise do not trigger findings, and report any gap larger than one step.
Step and resolution. The step is not the resolution. A tool with a vertical resolution of 2 ft logged at 0.5 ft has samples that are not independent. Do not resample to a finer step than the data have, and when you resample to a coarser common step, filter or average first to avoid aliasing.
Depth reference. Take the elevations from the well header or the survey, not from memory. Check that logs, core, tops and completions use the same reference. Typical offsets between the kelly bushing and the ground are tens of feet, which is larger than the thin beds that the data resolve.
Measured and vertical depth. Logs are in measured depth. Anything compared with another well, a map, or a geological model needs true vertical depth, computed from a deviation survey.
Shifts between runs and against core. Use a shift search over a few feet with a feature-rich curve such as gamma ray, and apply the shift to the whole run. For core, use core gamma, and shift all core measurements by the same amount. A large shift (many feet) usually means a depth reference or units mismatch and not a depth error. The procedure is on the splicing page.
Worked example
A clean index and a damaged one, the units tests, and three datum conversions. The code returns findings, and an empty list would mean the index passed:
import numpy as np
COMMON_STEPS_FT = [0.1, 0.25, 0.5, 1.0, 2.0]
COMMON_STEPS_M = [0.1, 0.1524, 0.2, 0.25, 0.5, 1.0]
def depth_report(z, tol=1e-3):
"""Basic checks on a depth index. Returns a list of findings; an empty list means it passed."""
z = np.asarray(z, dtype=float)
d = np.diff(z)
out = []
step = float(np.median(d))
out.append(f"nominal step {step:g}")
if np.any(d < 0):
out.append(f"{int(np.sum(d < 0))} step(s) go backwards, first at index {int(np.argmax(d < 0))}")
if np.any(d == 0):
out.append(f"{int(np.sum(d == 0))} duplicated depth(s)")
gaps = np.where(d > step * (1 + tol) + tol)[0]
for i in gaps:
out.append(f"gap of {d[i]:g} after {z[i]:g} (about {int(round(d[i] / step)) - 1} missing sample(s))")
return out
def guess_units(z):
"""Feet or metres from the step size. 0.1524 m is exactly 0.5 ft, so it flags a metric file written from feet."""
step = float(np.median(np.diff(np.asarray(z, dtype=float))))
if abs(step - 0.1524) < 1e-4:
return "metres (a 0.5 ft step converted to metres)"
if abs(step - 0.5) < 1e-6:
return "ambiguous: 0.5 ft or 0.5 m, check the header"
return "feet" if step in COMMON_STEPS_FT and step not in COMMON_STEPS_M else "ambiguous: check the header"
def to_subsea(md, kb_elev):
"""Depth below mean sea level (positive down) of a vertical well: depth minus reference elevation."""
return np.asarray(md, dtype=float) - kb_elev
def datum_shift(z, ref_old, ref_new):
"""Move depths from one reference to another. Elevations are above mean sea level, in the units of z.
A point z below a reference at elevation ref_old is at elevation ref_old - z. Its depth below ref_new is
ref_new - (ref_old - z) = z + (ref_new - ref_old)."""
return np.asarray(z, dtype=float) + (ref_new - ref_old)
if __name__ in ("__main__", "worked_example"):
z = np.arange(5000.0, 5020.0, 0.5)
print("clean index:", depth_report(z) or "no findings")
# one gap (5002 to 5003), one duplicate (5003.5 twice) and one reversal (5004 then 5003.5)
bad = np.array([5000, 5000.5, 5001, 5001.5, 5002, 5003, 5003.5, 5003.5, 5004, 5003.5, 5004.5, 5005])
print("damaged index:")
for line in depth_report(bad):
print(" -", line)
print("units for a 0.5 step:", guess_units(z))
print("units for a 0.1524 step:", guess_units(np.arange(0, 10, 0.1524)))
kb, gl = 1250.0, 1232.0 # elevation of kelly bushing and ground level above sea level, ft
md = 8000.0
print(f"subsea depth of {md:g} ft MD (vertical well, KB {kb:g} ft): {to_subsea(md, kb):g} ft")
print(f"same point measured from GL: {datum_shift(md, kb, gl):g} ft")
# a log in feet below KB, a core in feet below GL: the core depths must be moved by the KB-GL height
print(f"a core depth of 7982 ft below GL is {datum_shift(7982.0, gl, kb):g} ft below KB")
Output
clean index: ['nominal step 0.5']
damaged index:
- nominal step 0.5
- 1 step(s) go backwards, first at index 8
- 1 duplicated depth(s)
- gap of 1 after 5002 (about 1 missing sample(s))
- gap of 1 after 5003.5 (about 1 missing sample(s))
units for a 0.5 step: ambiguous: 0.5 ft or 0.5 m, check the header
units for a 0.1524 step: metres (a 0.5 ft step converted to metres)
subsea depth of 8000 ft MD (vertical well, KB 1250 ft): 6750 ft
same point measured from GL: 7982 ft
a core depth of 7982 ft below GL is 8000 ft below KB
Assumptions and limitations
- The depth index is a column of the data and not a derived quantity, so its errors are in the file and not in the checks.
- The median step is the nominal step. This fails for a file that is mostly irregular (for example a merged file with several steps), which is handled by splitting it by run first.
- The well is vertical when using the depth reference formulas. Otherwise they apply to TVD.
- Elevations of the references are known and in the same units as the depth.
- A depth error is constant over the interval being compared. Stretch from cable tension or hole deviation makes errors that vary.
QC checks
- All curves of a data set share one index with a single step and no duplicates, gaps or reversals, or the exceptions are listed.
- The depth units are stated for every data set and agree with the step size.
- Logs, tops, core and completion data are on one reference, and the conversions are recorded.
- Features of a marker bed (a distinct gamma-ray peak, a casing shoe) appear at the same depth in all runs after shifting.
- Core gamma matches log gamma after a single shift, and the shift is small compared with the interval.
Going Deeper
Depth is deceptively hard. A wireline depth comes from the cable length at surface, corrected in good systems for stretch and tension, and different runs of the same hole can disagree by several feet. LWD depths come from the drilling depth with a different set of errors, and are often time-based and later corrected. The depth reference is also a source of errors that look like geology: a core or a top picked from a different reference puts a correct marker in the wrong place. Wells are also reported in different reference systems by different groups, and in some basins tops are given below sea level and logs below the kelly bushing. For this reason the depth reference of every data set should be stated in the data, not left to convention.
References
References will be added once verified.
Python reference implementation
Python reference implementation
The Python reference implementation is available to registered users with a verified email address. Register or sign in to view it.