CamPetro

Inversion Endmember Parameters

On this page

Summary

An inversion endmember is the log response of a pure component, a mineral or a fluid. The endmembers are the columns of the matrix that the Mineral inversion solves, so every error in them is an error in the volumes. This page gives typical values and ranges for the common components. All values are starting points to be calibrated and are flagged for review.

Inputs and outputs

Item Units
Input Choice of component (mineral or fluid) and its source: chartbook, core, local calibration none
Input Bulk density, Neutron porosity, Compressional slowness, Photoelectric factor and Volumetric photoelectric cross section responses of the pure component g/cm³, v/v, µs/ft, barns/electron, barns/cm³
Output One column of the inversion matrix per component, with a range as above

Equations

Each endmember is a vector of responses. The common conversions are:

\[ \Uvol = \PEf\,\rhob \]

where \(U\) is used instead of PE because it mixes linearly with volume. For a fluid mixture of water and hydrocarbon, with water saturation \(S\) in the zone the tool reads, the density and sonic endmembers are the volume-weighted means:

\[ \rhoFl = S\,\rho_w + (1 - S)\,\rho_h \qquad \Delta t_f = S\,\Delta t_w + (1 - S)\,\Delta t_h \]

For a clay-bound-water mixture the clay endmember is defined for the wet clay, so that its density and neutron response include the bound water. This must match the definition of the clay volume that is passed in; see Total vs Effective Porosity.

Symbol Variable Units Typical range
\(\mathrm{PE}\) Photoelectric factor barns/electron 1.8 to 5.1
\(U\) Volumetric photoelectric cross section barns/cm³ 0 to 15
\(\rho_b\) Bulk density g/cm³ 1.8 to 3.0
\(\rho_f\) Pore fluid density g/cm³ 0.2 to 1.2

Single-value calculator

No calculator: this page is a reference table, not an equation. The inversion calculator is on the Mineral Inversion page.

Behavior

The endmember table below is the content of this page. On sensitivity: in the five-component example on this page, with the logs held fixed and the clay and kerogen volumes supplied as constraints, changing one clay or kerogen endmember at a time moves the inverted porosity by at most 0.009 and the quartz volume by at most 0.015, and the predicted logs still match the data to within 0.2 of their uncertainty. The error is therefore not visible in the misfit. The constraints make the porosity robust to these endmembers, but anything not constrained (quartz, calcite and the fluid) absorbs the error.

Parameter guidance

All values are typical, from memory of published chart books and from common practice, and need to be confirmed against a chartbook, core or a local calibration before use. Ranges show how much a value varies in nature or between sources. Neutron values are on the limestone scale and depend on the tool, borehole and environmental corrections.

Component Density (g/cm³) Neutron (v/v, limestone scale) Sonic (µs/ft) PE (barns/electron) U = PE × density (barns/cm³)
Quartz 2.65 (2.64 to 2.65) -0.02 (-0.04 to 0.00) 55.5 (51 to 56) 1.81 4.8
Calcite 2.71 0.00 47.5 (46 to 49) 5.08 13.8
Dolomite 2.87 (2.85 to 2.88) 0.02 (0.00 to 0.06) 43.5 (42 to 44) 3.14 9.0
Anhydrite 2.98 (2.95 to 2.98) 0.00 (-0.01 to 0.01) 50 5.05 15.0
Gypsum 2.35 (2.30 to 2.35) 0.50 52 4.0 9.4
Halite (salt) 2.04 (2.03 to 2.17) -0.02 (-0.03 to 0.00) 67 4.65 9.5
Pyrite 4.99 (4.9 to 5.0) -0.02 39 (38 to 40) 17 85
Siderite 3.89 (3.8 to 3.9) 0.05 44 to 47 14.7 57
Ankerite 2.86 to 3.1 0.00 to 0.05 47 9.3 27
Feldspar (K) 2.52 to 2.58 0.02 65 to 70 2.9 7.3
Clay (mixed) 2.2 to 2.8 0.15 to 0.45 70 to 140 1.8 to 6 4 to 14
Kerogen 1.1 to 1.4 (1.26) 0.4 to 0.8 140 to 180 about 0.2 about 0.25
Coal 1.2 to 1.8 0.3 to 0.6 100 to 140 about 0.2 about 0.25
Water 1.0 (fresh) to 1.2 (saturated brine) 1.0 185 to 200 (189 fresh) 0.36 0.4 to 1
Oil 0.7 to 0.9 0.8 to 1.1 210 to 240 about 0.12 about 0.1
Gas 0.1 to 0.4 0.0 to 0.5 several hundred about 0.1 about 0.02

How to choose.

  • Pure minerals (quartz, calcite, dolomite, anhydrite, halite, pyrite) are well known from chart books and laboratory values. Use the values as given and vary them only if core grain density shows a different mineral chemistry (for example iron-rich dolomite or ankerite).
  • Clay varies by type (kaolinite, illite, chlorite, smectite) and with the bound water it carries, so its responses cannot be taken from a table. Pick them in the thickest shale in the well, using the shale point on the crossplots, or from XRD and core. They must agree with the pick used for clay volume.
  • Kerogen density and neutron response rise with maturity. Use the density from the TOC work (Kerogen density) so that the volume and the endmember are consistent. Sonic and photoelectric responses of kerogen are poorly known.
  • Fluids depend on the zone the tool reads, which is the flushed zone. Use filtrate for water. The gas values vary strongly with pressure and temperature and are only an estimate.
  • Mixed-mineral groups. Feldspar, clay and ankerite are families. Pick the member or an average for the formation.

Worked example

The data below are generated from the base endmembers, then inverted with one endmember changed at a time. Only the solver's endmember changes; the data do not. Compare the inverted volumes and the average misfit (average absolute residual divided by uncertainty over the four logs) with the base case:

import itertools
import numpy as np

# Endmember responses. Illustrative mid-range values, not a recommendation.
#             density  neutron  sonic   U
#             g/cm3    v/v      us/ft   barns/cm3
END = {
    "quartz":  [2.65, -0.02,  55.5,  4.8],
    "calcite": [2.71,  0.00,  47.5, 13.8],
    "clay":    [2.55,  0.35, 110.0,  9.0],
    "kerogen": [1.26,  0.60, 160.0,  0.25],
    "water":   [1.00,  1.00, 189.0,  0.4],
}
LOGS = ["rhob", "nphi", "dt", "u"]
SIGMA = {"rhob": 0.03, "nphi": 0.03, "dt": 5.0, "u": 1.0, "vcl": 0.03, "vker": 0.01, "unity": 0.001}


def system(minerals, curves=LOGS, constraints=("vcl", "vker")):
    """Rows: the log curves, then the constraint rows, then the closure row."""
    rows, sig, labels = [], [], []
    for c in curves:
        rows.append([END[m][LOGS.index(c)] for m in minerals])
        sig.append(SIGMA[c]); labels.append(c)
    for c, name in (("vcl", "clay"), ("vker", "kerogen")):
        if c in constraints:
            rows.append([1.0 if m == name else 0.0 for m in minerals])
            sig.append(SIGMA[c]); labels.append(c)
    rows.append([1.0] * len(minerals)); sig.append(SIGMA["unity"]); labels.append("unity")
    return np.array(rows), np.array(sig), labels


def solve(A, sig, d):
    """Weighted least squares with V >= 0: try every active set, keep the best feasible one."""
    Aw, dw = A / sig[:, None], d / sig
    n = A.shape[1]
    best, best_cost = None, np.inf
    for k in range(1, n + 1):
        for subset in itertools.combinations(range(n), k):
            x = np.linalg.lstsq(Aw[:, subset], dw, rcond=None)[0]
            if (x < -1e-12).any():
                continue
            v = np.zeros(n)
            v[list(subset)] = x
            cost = np.sum((Aw @ v - dw) ** 2)
            if cost < best_cost:
                best, best_cost = v, cost
    return best, best_cost


truth = {"quartz": 0.45, "calcite": 0.20, "clay": 0.15, "kerogen": 0.05, "water": 0.15}
minerals = list(truth)
v_true = np.array([truth[m] for m in minerals])
A0, sig, _ = system(minerals)
d = A0 @ v_true            # data made with the base endmembers, no noise

print("Data are fixed. Only the endmember used in the inversion changes.")
print(f"{'endmember used':28s} {'quartz':>7s} {'calcite':>8s} {'clay':>6s} {'kerogen':>8s} {'water':>6s} {'avg|r|':>7s}")
base = {k: list(v) for k, v in END.items()}
tests = [("base (truth)", None, None, None),
         ("clay density 2.40", "clay", 0, 2.40),
         ("clay density 2.70", "clay", 0, 2.70),
         ("clay neutron 0.25", "clay", 1, 0.25),
         ("clay neutron 0.45", "clay", 1, 0.45),
         ("kerogen density 1.10", "kerogen", 0, 1.10),
         ("kerogen density 1.40", "kerogen", 0, 1.40),
         ("water density 1.10 (brine)", "water", 0, 1.10)]
for label, m, j, val in tests:
    for k in END:
        END[k] = list(base[k])
    if m:
        END[m][j] = val
    A, s, _ = system(minerals)
    v, _ = solve(A, s, d)
    r = (d - A @ v) / s
    print(f"{label:28s} " + " ".join(f"{x:7.3f}" for x in v) + f" {np.mean(np.abs(r[:4])):7.2f}")
for k in END:
    END[k] = list(base[k])

Output

Data are fixed. Only the endmember used in the inversion changes.
endmember used                quartz  calcite   clay  kerogen  water  avg|r|
base (truth)                   0.450   0.200   0.150   0.050   0.150    0.00
clay density 2.40              0.457   0.199   0.153   0.050   0.141    0.20
clay density 2.70              0.443   0.202   0.145   0.050   0.159    0.18
clay neutron 0.25              0.435   0.211   0.151   0.050   0.154    0.18
clay neutron 0.45              0.465   0.191   0.147   0.050   0.147    0.17
kerogen density 1.10           0.452   0.199   0.151   0.050   0.147    0.07
kerogen density 1.40           0.448   0.200   0.149   0.050   0.153    0.06
water density 1.10 (brine)     0.446   0.201   0.147   0.050   0.156    0.13

Assumptions and limitations

  • A single, fixed response applies to each component over the interval. In practice clay and kerogen responses change with depth and maturity, and a pure mineral can carry impurities.
  • The values come from laboratory or chartbook measurements of pure components. In situ responses depend on the tool, the environmental corrections and the lithology scale of the neutron.
  • Fluid responses are those of the invaded zone and depend on salinity, temperature and pressure.
  • PE has been converted to U before use, and the neutron is on one lithology scale for every component.
  • The values in the table are not endorsed values: all of them need review.

QC checks

  • Every endmember lies within its range in the table, or the reason it does not is documented.
  • Grain density from the inversion agrees with core grain density across the interval.
  • The inverted clay and kerogen endmembers plot at the shale point on the density-neutron and density-U crossplots.
  • A small change (a few percent) in any endmember does not change the volumes by more than the inversion uncertainty. If it does, the system is poorly conditioned.
  • The same endmember set is used for every well in the field unless there is a reason to vary it.

Going Deeper

Endmember values appear in service-company chart books, in compilations of mineral properties and in laboratory measurements, and the sources differ by small amounts. The differences matter less than the choice of components. A modest error in one endmember is mostly absorbed by the other components, which is why the misfit does not show it, and why endmembers need independent validation by core. The least certain are kerogen, clay and gas, which are precisely the components that matter most in unconventional and gas reservoirs. Where gas is present the correction for gas has to be treated as a model decision and not as an endmember.

References

  1. Schlumberger, Log Interpretation Charts. Schlumberger, Houston, Texas (updated periodically).
  2. Asquith, G. and Krygowski, D., 2004. Basic Well Log Analysis, 2nd edition. AAPG Methods in Exploration Series 16, American Association of Petroleum Geologists, Tulsa, OK.
  3. Doveton, J.H., 1994. Geologic Log Analysis Using Computer Methods. AAPG Computer Applications in Geology No. 2, American Association of Petroleum Geologists, Tulsa, OK.

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.