Exerciseschevron_rightChapter 7chevron_right7.2
fitness_center

Exercise 7.2

Non-Linear Vshale (Larionov Tertiary)

Level 2
Chapter 7: Well Log Analysis
descriptionProblem

The linear gamma-ray index reads too much shale through the transition zone: sands look dirtier than they are, and marginal pay gets cut. On the Tertiary clastics of OML 58 (wells OD-001/OD-003/OD-007) the Larionov (1969) Tertiary correction is the field standard, because it bends the index down in the mid-range where the linear method overshoots.

You have the OD-003 triple-combo log. Build both Vshale curves and prove the Larionov curve is the gentler one.

Write three functions:

  1. igr(gr, gr_clean, gr_shale): the linear gamma-ray index

IGR = (gr - gr_clean) / (gr_shale - gr_clean), clipped to 0..1. Works on a scalar or a numpy array.

  1. vshale_linear(igr_val): the linear model. Vshale is the (already

clipped) index, so this is the identity: return igr_val.

  1. vshale_larionov(igr_val): the Larionov Tertiary correction

Vsh = 0.083 (2 (3.7 IGR) - 1), clipped to 0..1. Must be vectorized (accept a numpy array and return an array).

Then, using clean/shale endpoints from the 5th / 95th percentiles of logs["GR"]:

  • compute igr_log = igr(logs["GR"].values, gr_clean_gapi, gr_shale_gapi),
  • vsh_linear_log = vshale_linear(igr_log),
  • vsh_larionov_log = vshale_larionov(igr_log).

The payoff: across the whole log, vsh_larionov_log <= vsh_linear_log elementwise. Larionov keeps the sands cleaner, so the wet and pay sands of OD-003 don't get over-shaled.

lightbulbHints (0/3)

Stuck? Reveal hints one at a time — they progress from nudge to near-solution.

codeYour solution
main.py
visibilityReveal reference solutionexpand_more

Try solving it yourself first — the hints walk you through it. The solution below is one valid approach; yours may differ and still be correct.

import numpy as np
import pandas as pd

np.random.seed(7)
depth = np.arange(7000.0, 7850.0, 0.5)
_cond = [depth < 7200, depth < 7400, depth < 7600, depth < 7700, depth >= 7700]
_gr   = np.select(_cond, [105.0, 40.0, 32.0, 110.0, 45.0])
_rt   = np.select(_cond, [4.0,   2.2,  35.0, 4.0,   2.5])
_rhob = np.select(_cond, [2.54,  2.31, 2.26, 2.55,  2.33])
_nphi = np.select(_cond, [0.33,  0.21, 0.17, 0.34,  0.22])
logs = pd.DataFrame({
    "DEPTH": depth,
    "GR":   _gr   + np.random.normal(0, 4.0,  len(depth)),
    "RT":   _rt   * np.exp(np.random.normal(0, 0.10, len(depth))),
    "RHOB": _rhob + np.random.normal(0, 0.02, len(depth)),
    "NPHI": np.clip(_nphi + np.random.normal(0, 0.015, len(depth)), 0.0, 1.0),
})
STEP_FT = 0.5


def igr(gr, gr_clean, gr_shale):
    """Linear gamma-ray index, clipped to 0..1. Scalar or array."""
    raw = (np.asarray(gr, dtype=float) - gr_clean) / (gr_shale - gr_clean)
    return np.clip(raw, 0.0, 1.0)


def vshale_linear(igr_val):
    """Linear model: Vshale is the (already clipped) index itself."""
    return igr_val


def vshale_larionov(igr_val):
    """Larionov (Tertiary): 0.083 * (2**(3.7*IGR) - 1), clipped 0..1."""
    igr_arr = np.asarray(igr_val, dtype=float)
    vsh = 0.083 * (2.0 ** (3.7 * igr_arr) - 1.0)
    return np.clip(vsh, 0.0, 1.0)


# Endpoints from the 5th / 95th percentile of GR.
gr_clean_gapi = logs["GR"].quantile(0.05)
gr_shale_gapi = logs["GR"].quantile(0.95)

igr_log = igr(logs["GR"].values, gr_clean_gapi, gr_shale_gapi)
vsh_linear_log = vshale_linear(igr_log)
vsh_larionov_log = vshale_larionov(igr_log)

print(f"gr_clean={gr_clean_gapi:.1f} gapi, gr_shale={gr_shale_gapi:.1f} gapi")
print(f"Larionov(IGR=1) = {vshale_larionov(np.array([1.0]))[0]:.4f}")
print(f"mean linear Vsh   = {vsh_linear_log.mean():.4f}")
print(f"mean Larionov Vsh = {vsh_larionov_log.mean():.4f}")

lockCopying code is a Full Access feature.