« Subsurface Analytics

Empirical Lithology Classifier (Phase 1) -- Full Database

Deterministic lithology classifier with ZERO fitted parameters. GR is a prerequisite curve, via a Clavier Vshale gate. A tiered rule then splits clean samples into two checks, then applies optional refinements:

  • Evaporite check: DT and GR against Halite/Anhydrite matrix endpoints. DT is physically reliable here, since evaporites are essentially non-porous.
  • Reservoir check: GR alone against Sandstone/Limestone. DT and NPHI are deliberately EXCLUDED here, since porosity dilution makes the pure-mineral matrix value misleading for porous rock without an independent porosity input.
  • PEF, RHOB, NPHI, resistivity, and SP are used as optional refinement axes wherever present, never required.
Clavier Vshale gate
-- an estimate of shale content (Vshale, 0-1) computed from gamma ray, normalised against each well's own 5th/95th percentile GR (clean vs. shale baseline) via the Clavier et al. (1971) formula. Only samples at or below a Vshale of 0.15 are treated as clean enough for the lithology checks below -- a shaly sample is not a fair test of a pure-mineral matrix value.

What "accuracy" means here: Every number on this page -- coverage, accuracy, the confusion matrix -- is measured against labels that are not themselves verified geological ground truth. Sandstone, Limestone, Halite, and Anhydrite labels are derived from Lloyd's Register's interpreted lithology picks, an OGA-commissioned product whose own classification method is undocumented. Igneous, Metamorphic, Tuff, and Volcanics rows also come from Lloyd's Register but are never reachable here: they are compositionally variable rock categories with no single published pure-mineral matrix value, so the classifier has no rule that can ever output them as a prediction. Shale combines two independent sources: the WellStrat dataset's own shale picks, and any depth falling in a gap of at least 1 metre between two consecutive Lloyd's Register picks (their scheme has no Shale code, so an unassigned gap counts, by their own convention, as "none of the above"). This was checked against real gamma-ray contrast at 21,712 Sandstone-pick/gap boundaries project-wide before use: the gap side reads higher gamma ray 89.5% of the time, by a median of 11.4 API. "Accuracy" here means agreement with these provisional, third-party or derived labels, not confirmed-correct geology.

wells
2567
n total
22900975
coverage pct
37.5
clean vsh max
0.15
accuracy overall
0.299
shale vsh confident
0.5
max normalized distance
2.0
accuracy when classified
0.799

Last run: 2026-08-02 17:46:49 UTC

Confusion Matrix

Each row is a real, known rock type. Each column is what the classifier guessed. Reading across the "Sandstone" row shows: of all the samples that really are Sandstone, what did the classifier call them?

Darker cells = higher count within that row (row-normalised shading). "Unclassified" is a declined answer, not a wrong guess -- shaded separately.

Tuning Crossplot -- PEF / RHOB

The black star is the reference mineral point: the published, textbook value for 100% pure, zero-porosity rock (e.g. pure quartz for Sandstone) -- not measured from any of our own wells. The dashed ellipse around it is the actual tolerance boundary at the current slider setting; real rock always sits off the star due to porosity and impurity, so the question the ellipse answers is "how far off is still a plausible match?"

Client-side preview only -- dragging the slider does not change the classifier's actual behaviour, it recomputes against the sample points already loaded on this page. To change the real tolerance, edit FEATURE_TOLERANCE in pipeline/lithology_phase1_classifier.py and Recalculate.

How this result was generated

graph TD curves["GR (required)
+ optional PEF/RHOB/DT/NPHI/Resistivity/SP"] despike["Within-Run Despike"] vsh["Clavier Vshale (from GR)"] gate{"Vsh > 0.5?"} shale["Shale"] ambiguous{"0.15 < Vsh <= 0.5?"} unclass1["Unclassified
(ambiguous)"] evap["Evaporite check:
DT + GR (required)
+ optional PEF/RHOB/NPHI/Res/SP
vs Halite / Anhydrite"] res["Reservoir check:
GR alone (required)
+ optional PEF/RHOB/Res
vs Sandstone / Limestone
(DT and NPHI excluded -- porosity)"] within{"Within Tolerance?"} mineral["Best-matching class"] unclass2["Unclassified
(no match)"] curves --> despike --> vsh --> gate gate -->|yes| shale gate -->|no| ambiguous ambiguous -->|yes| unclass1 ambiguous -->|no| evap ambiguous -->|no| res evap --> within res --> within within -->|yes| mineral within -->|no| unclass2

Hover a step for a plain-language explanation.