⌂ Project Home / 📚 Geology Course 🐍 GemPy Tutorial →
Module 1 of 2
// geology for modellers · a complete course

From Outcrop
to Implicit Model

Everything a programmer needs to know about geology to build correct, defensible 3-D models — organized around the decisions you'll actually make in software like GemPy, Leapfrog, or Petrel. No fieldwork required.

Tier 1 · Foundations Tier 2 · Structural Geology Tier 3 · Reading Real Data Tier 4 · Modelling Concepts Tier 5 · Expert Topics
Tier 1 — Foundations

What rock is, and how it remembers time

Before you can model anything you need the vocabulary: what rocks are made of, how they form, and the single rule (superposition) that lets us read geological history out of vertical position.

Rock types & the rock cycle

All rock falls into three families, defined by how they formed — this matters for modelling because each family produces a different kind of 3-D geometry.

FamilyForms byTypical geometryExamples
SedimentaryParticles deposited in layers, then compacted/cementedTabular, layered, follows superpositionSandstone, Shale, Limestone, Coal
IgneousMagma cooling (underground = intrusive, surface = extrusive)Cross-cutting blobs, sheets, or layered flowsGranite, Basalt, Obsidian
MetamorphicExisting rock altered by heat/pressure without meltingOften deformed, foliated, complex basement shapesMarble, Schist, Gneiss
▸ The rock cycle — click a stage
Modelling implication: sedimentary rock is the easy case — it obeys layering rules and is what most beginner GemPy tutorials use. Igneous intrusions and metamorphic basement complexes break the simple "stack of layers" assumption and need special handling (Tier 2).

Geologic time & the Law of Superposition

The Law of Superposition is the single most important rule for modelling: in an undisturbed sequence, depth = age. Three companion principles refine it:

PrincipleStates
Original horizontalitySediments are deposited as roughly horizontal layers — if you see tilted layers, something deformed them after deposition
Lateral continuityA layer extends in all directions until it thins out, hits a barrier, or erodes — useful for correlating boreholes that are far apart
Cross-cutting relationshipsAnything that cuts through a rock (fault, intrusion, erosion surface) is younger than the rock it cuts
🧑‍💻 Programmer analogy: these three principles are basically a sorting algorithm. Superposition gives you initial order (bottom = oldest). Cross-cutting relationships are like "this commit can't exist before its parent" — anything that disrupts a layer must postdate it. This is precisely the logic encoded in a modelling tool's series ordering.
▸ Relative age puzzle — order these events
click events in oldest→youngest order

The stratigraphic column

A stratigraphic column is simply a vertical list of layers at a location, oldest at the bottom. It's the direct ancestor of a borehole log and is exactly the data structure a model's surfaces list represents.

▸ Stratigraphic column ↔ code representation

Geological time itself is organized hierarchically — useful vocabulary when reading literature or maps:

Unit (largest→smallest)Example
EonPhanerozoic
EraMesozoic
PeriodJurassic
EpochLate Jurassic
You rarely need exact ages for modelling — what matters is relative order. Geological period names (Jurassic, Cretaceous, etc.) function like version tags: they tell you sequence, not precise dates you need to compute with.

Minerals vs. rocks (quick note)

A mineral is a naturally occurring, chemically-defined crystalline solid (e.g. quartz, feldspar, calcite). A rock is an aggregate of one or more minerals. For 3-D geological modelling you almost always work at the rock/lithology level, not individual minerals — mineral composition matters more for geochemistry and ore-grade estimation, which is a separate (though related) discipline from the geometric modelling this course focuses on.

Quick Check: A borehole log shows sandstone at 200m depth and shale at 150m depth (shallower) at the same location, undisturbed. Which is older?
Tier 2 — Structural Geology

How rocks deform, break, and bend

Real rock is rarely flat and undisturbed. This tier covers the deformation vocabulary that maps directly onto modelling primitives: orientations, folds, faults, unconformities, and intrusions.

Strike, dip & orientation

Every tilted surface has two measurements that fully describe its 3-D orientation:

MeasurementDefinitionRange
Dip angleSteepest angle of the surface from horizontal0°(flat)–90°(vertical)
Dip directionCompass direction the surface tilts toward (downslope)0°–360°
StrikeDirection of a horizontal line on the surface — always 90° from dip direction0°–360°
▸ Strike & dip — full 3D-style block diagram
🧑‍💻 Programmer analogy: dip + dip direction are exactly a surface normal vector in spherical coordinates. A flat layer's normal is straight up (0,0,1). Converting:
python
import numpy as np

def dip_to_normal(dip_deg, dip_dir_deg):
    """Convert dip angle + dip direction to a unit normal vector (Gx, Gy, Gz)."""
    dip = np.radians(dip_deg)
    dip_dir = np.radians(dip_dir_deg)
    gx = np.sin(dip) * np.sin(dip_dir)
    gy = np.sin(dip) * np.cos(dip_dir)
    gz = np.cos(dip)
    return (gx, gy, gz)

# Flat layer: dip=0 → (0, 0, 1) — points straight up
# Vertical layer dipping east: dip=90, dip_dir=90 → (1, 0, 0)

Folds

Folds form when layers bend under compressional stress rather than fracturing. The two basic shapes:

TypeShapeOldest rock atLimbs dip
AnticlineArches up (∩)Core/centreAway from the axis
SynclineSags down (∪)Outer edgesToward the axis
▸ Fold builder — amplitude & asymmetry

Fold geometry vocabulary

📐
Hinge
Point of maximum curvature
➡️
Axial plane
Plane bisecting the fold
📏
Limb
The relatively flat side of a fold
🔄
Plunge
Tilt of the fold axis itself
↕️
Wavelength
Distance between adjacent fold crests
📈
Amplitude
Height of the fold from mid-line to crest
Modelling implication: folds are not a separate object in implicit modelling software. They emerge automatically from interpolating orientation data points that point in different directions across a surface. The denser and more varied your orientation data, the more accurately the interpolator reconstructs the true fold shape — sparse or uniform orientation data will "round off" tight folds.

Faults

A fault is a fracture along which blocks of rock have moved relative to each other. Faults are classified by the direction of relative motion:

TypeMotionStress regimeHanging wall vs footwall
NormalExtensionalTension (pulling apart)Hanging wall moves down relative to footwall
ReverseCompressionalCompression (pushing together)Hanging wall moves up relative to footwall
ThrustCompressional, low angleStrong compressionReverse fault with dip < 45°
Strike-slipLateral / horizontalShearNo significant vertical offset
▸ Fault type comparison — click to switch

Key fault vocabulary

  • Hanging wall — the block of rock above an inclined fault plane
  • Footwall — the block below an inclined fault plane
  • Throw — vertical component of displacement
  • Heave — horizontal component of displacement
  • Fault zone / damage zone — fractured rock surrounding the main fault surface
🧑‍💻 Programmer analogy: a fault is a discontinuity in your interpolation domain — like a branch cut in complex analysis, or a seam in a 3-D mesh where two separately-meshed regions meet without blending. Modelling software handles this by giving fault surfaces their own series and explicitly marking them as a discontinuity the interpolator must respect rather than smooth over.

Unconformities

An unconformity is a buried surface representing a gap in time — older rock was deformed, uplifted, and eroded, then younger sediment was deposited on top of the erosion surface.

TypeBelow the surfaceAbove the surface
Angular unconformityTilted/folded layers, truncatedFlat-lying younger layers
DisconformityParallel layers with an erosional gapParallel younger layers (same orientation)
NonconformityIgneous or metamorphic basementSedimentary layers on top
▸ Angular unconformity — the classic modelling case
Modelling implication: an unconformity is exactly the boundary between two series in implicit modelling software. The younger series is given an "erode" relationship to the older one, meaning its surface acts as a hard clip that truncates whatever older geometry lies beneath it — this is how you encode "this part of the geological history doesn't matter anymore, only what's left after erosion."

Intrusions

Igneous intrusions form when magma pushes into existing rock and solidifies underground — these cut across stratigraphy rather than following it.

ShapeOrientationTypical scale
DikeSteep/vertical sheet, cuts across layeringMetres to kilometres long, thin
SillSub-horizontal sheet, parallel to layeringCan be very extensive laterally
Stock / PlutonIrregular, roughly equant blobKilometre-scale
BatholithMassive irregular body (many plutons merged)Tens to hundreds of km
▸ Dike vs. sill geometry relative to host layers
Modelling implication: like faults, intrusions get their own series with an "overprint" relationship — they're computed last and simply override whatever lithology they geometrically occupy, regardless of the surrounding stratigraphic order.

Joints & fractures (and why you usually skip them)

Joints are fractures with no appreciable displacement — unlike faults, the rock on either side hasn't moved relative to the other side. They're geologically important (control groundwater flow, rock strength, blasting patterns in mining) but are almost never modelled as discrete surfaces in a regional 3-D model.

Why we mostly skip this in implicit modelling: joints occur in dense networks at a scale far smaller than typical model resolution. Instead of modelling each one, their statistical density and orientation are usually captured as a property field (a "fracture intensity" attribute) rather than as explicit geometry — relevant in advanced/expert workflows (Tier 5) but skippable for standard stratigraphic models.
Quick Check: You see two sandstone blocks offset by 3 metres vertically across a planar break. Is this a fault or a joint?
Tier 3 — Reading Real Data

What raw geological data actually looks like

Before any data reaches your model, it comes from one of a handful of source formats. Knowing how to read these is what separates "I ran the interpolator" from "I understand what my model represents."

Geological maps

A geological map shows which rock unit is exposed at the surface across an area, using colour-coded polygons, plus strike/dip symbols and contact lines (boundaries between units).

▸ Reading a geological map — symbols explained
SymbolMeaning
Colour-filled polygonOne rock unit outcropping at surface
Solid lineObserved contact (boundary) between units
Dashed lineInferred/uncertain contact
Strike & dip tick (┳ rotated)Orientation measurement at that point — long line = strike, short tick = dip direction, number = dip angle
Thick line with teeth/barbsFault trace (barbs point toward hanging wall on thrust faults)
🧑‍💻 Programmer analogy: a geological map is a 2-D cross-section through your data at Z = topography. Digitizing the polygon boundaries and dip symbols from a geological map is one of the most common ways to generate surface_points and orientations input data for a 3-D model.

Boreholes & well logs

A borehole is a vertical (or deviated) hole drilled into the ground; the resulting well log records which rock type was encountered at which depth. This is the single most common and most reliable source of surface_points data.

▸ Well log → surface points table
python · parsing a well log into model input
# Typical well log: a list of (top_depth, bottom_depth, lithology)
well_log = [
    (0,   15,  'Topsoil'),
    (15,  120, 'Limestone'),
    (120, 280, 'Shale'),
    (280, 500, 'Sandstone'),
]

collar_elevation = 1450  # surface elevation at the well (metres)
well_x, well_y = 2400, 1180   # well location

# Convert each TOP of a unit into a (X, Y, Z, surface) input point
# Z must be ELEVATION → collar_elevation minus depth
surface_points = []
for top_depth, bottom_depth, lith in well_log[1:]:  # skip topsoil top = ground level
    z = collar_elevation - top_depth
    surface_points.append((well_x, well_y, z, lith))

print(surface_points)
# [(2400, 1180, 1435, 'Limestone'), (2400, 1180, 1330, 'Shale'), (2400, 1180, 1170, 'Sandstone')]
Common gotcha: well logs report depth below ground, but model coordinates need elevation above a datum. Forgetting to subtract depth from collar elevation is the single most common reason a model "doesn't look right" when imported from real well data.

Seismic sections

Seismic reflection surveys send sound waves into the ground and record echoes off rock boundaries, producing a 2-D (or 3-D volume of) image where horizontal bands roughly correspond to layer boundaries. Common in oil & gas exploration.

▸ Simplified seismic section — reflectors = layer boundaries
TermMeaning
ReflectorA bright/dark band corresponding to a strong contrast in rock properties — usually a layer boundary
Two-way time (TWT)Vertical axis is often in milliseconds (sound travel time), not metres — must be converted via a velocity model to get true depth
PickingThe manual or automated process of tracing a reflector across the section to extract its (X, Y, Z) geometry
Modelling implication: "picked" horizons from seismic interpretation software are exported as point clouds — these become dense surface_points input, often thousands of points per surface, far denser than borehole-derived data. They're an excellent complement to sparse borehole data: boreholes give precise depth control, seismic gives lateral shape.

Cross-sections

A geological cross-section is a vertical "slice" through the Earth showing rock layers as they would appear if you cut the ground open along a line. It's the standard way geologists communicate a 3-D interpretation in 2-D, and it's exactly what tools like plot_2d() in GemPy generate.

▸ Constructing a cross-section from map + dip data
🧑‍💻 Programmer analogy: a cross-section is a deterministic projection — given a surface trace on a map and a dip angle, you can geometrically extrapolate where that surface must be underground, the same way you'd extrapolate a line from a point and a slope. This "project the dip downward" logic is essentially a simplified, manual version of what an implicit interpolator automates and generalizes to 3-D with multiple constraints.

Outcrop & field measurements

An outcrop is a place where rock is exposed at the surface. Geologists visit outcrops with a compass-clinometer to directly measure strike and dip by placing the tool against the rock face — this is the most direct, ground-truth source of orientation data.

Field dataCaptured asBecomes in your model
GPS location + which unit is exposed(X, Y, Z, unit name)surface_points
Compass-clinometer reading(strike, dip) at a pointorientations
Hand-drawn field sketchAnnotated cross-sectionUsed for QA/validation against your model
Quick Check: Which data source typically gives the densest lateral coverage of a surface's shape?
Tier 4 — Modelling Concepts

How geology becomes a computable model

This tier is the translation layer — the geology-specific concepts you need to use any implicit 3-D modelling software correctly, including GemPy, Leapfrog, SKUA-GOCAD, or Petrel.

Implicit vs. explicit modelling

There are two fundamentally different approaches to building a 3-D geological model:

ApproachHow surfaces are definedProsCons
Explicit Geologist manually digitizes/draws each surface as a mesh, section by section Full manual control, exact match to interpretation Labour-intensive, hard to update, doesn't scale
Implicit An algorithm (e.g. co-kriging) fits a continuous scalar field through scattered data points; surfaces are extracted as isocontours of that field Fast, automatically consistent, easy to update with new data Less direct manual control; quality depends on input data density/quality
▸ Explicit (hand-drawn) vs implicit (interpolated) — same data, two approaches
GemPy, Leapfrog, and most modern modelling tools are implicit. Understanding this distinction explains why the software asks for points + orientations rather than letting you "draw" a surface directly — you're providing constraints to an interpolator, not authoring geometry by hand.

Interpolation behaviour — what the algorithm is actually doing

Most implicit modelling software (including GemPy) uses a form of kriging — specifically universal co-kriging with both surface points and orientation (gradient) constraints. You don't need the full mathematical derivation, but you do need the behavioural intuition:

  • The interpolator fits a smooth scalar field where surface points all sit on the same isovalue (e.g. value = 0)
  • Orientation data constrain the gradient (steepness/direction) of that field at given points
  • Away from data, the field interpolates smoothly — it does not know about geological features you haven't told it about
  • Sparse data + uniform orientations → smooth, "boring" surfaces; tight folds and abrupt features need denser sampling near them
▸ Effect of data density on interpolated surface
🧑‍💻 Programmer analogy: this is conceptually similar to fitting a spline or a Gaussian process regression through scattered data — more data near a feature of interest gives you tighter, more accurate fit; sparse data gets smoothed over with the interpolator's prior assumptions (usually: "as smooth as possible given the constraints").

Series, order & relations — encoding geological history

A series groups surfaces that share one continuous depositional/deformation event. The order of series, and the relation between adjacent series, is how you encode the geological history (Tier 1 & 2 concepts) into the software:

You observed…Encode as…
A normal stack of sedimentary layersOne series, surfaces ordered youngest→oldest
An angular unconformityTwo series; younger series set to "erode" relation
A fault offsetting layersSeparate fault series, ordered before the series it cuts, flagged is_fault
An igneous intrusionSeparate series, typically computed/overprinted last
Two faults where one offsets the otherTwo fault series with an explicit fault-fault relation matrix
This is the single highest-leverage skill in this whole course. Getting series order and relations right is what separates a model that "computes without errors" from one that's actually geologically correct. Most beginner mistakes live here, not in the interpolation math.

Anisotropy & range — tuning the interpolator

Geological layers usually extend much further laterally than vertically — a limestone bed might span 10 km horizontally but be only 50 m thick. Anisotropy settings tell the interpolator about this directional bias so it doesn't treat horizontal and vertical distances equally.

ParameterControlsEffect if too smallEffect if too large
RangeMaximum distance over which data points influence the interpolationOverfitting — noisy, patchy surfacesOver-smoothing — real features get washed out
Anisotropy ratioRelative horizontal vs. vertical influenceVertically "smeared" surfacesLayers appear unrealistically flat
Practical note: default range/anisotropy values are usually a reasonable starting point, but if your model looks unnaturally "bubbly" or "flat" compared to known geology, this is the first place to look — not the input data itself.

Uncertainty in geological models

Unlike a CAD model of a manufactured object, a geological model is always an interpretation of incomplete data — there is no ground truth to check against except more drilling. Understanding where uncertainty comes from is essential for using a model responsibly.

📍
Data sparsity
Far from any borehole/outcrop, the model is extrapolating
📏
Measurement error
GPS, depth, and dip readings all carry instrument error
🧩
Interpretive choice
Series order/relations encode a geologist's hypothesis, not certainty
⚙️
Parameter choice
Range/anisotropy settings shape the result
Bridging to Tier 5: expert workflows quantify this uncertainty explicitly — treating uncertain inputs (like a borehole pick's true depth) as probability distributions and running the model many times to get a distribution of plausible geological scenarios, rather than a single "answer." Covered in 5.3.
Quick Check: You have one borehole at the center of your model area and no other data. Where is your model's uncertainty highest?
Tier 5 — Expert Topics

Beyond the standard workflow

The topics that separate a working model from a defensible, production-grade one — structural restoration, complex fault networks, formal validation, and domain-specific practice.

Structural restoration — checking your model is physically possible

Structural restoration "undoes" deformation on a model — unfolding folds and closing faults — to check whether the pre-deformation state makes geological sense (e.g. layers should restore to roughly horizontal, without gaps or overlaps, which would indicate an impossible interpretation).

▸ Restoration check: deformed model → restored state
Why this matters: an implicit interpolator will happily produce a smooth surface even if the underlying geology it implies is physically impossible (e.g. layers that would have to stretch or overlap to restore to flat). Restoration is a sanity check used in expert workflows, particularly in petroleum structural geology, to validate fault geometries and interpretations before committing to a final model.

Complex fault networks

Real basins often have dozens of interacting faults rather than one isolated fault. Key concepts for handling this in software:

ConceptMeaning
Fault-fault relationsWhich fault offsets which — encoded as a directed relation/precedence matrix
Splay faultsSmaller faults branching off a main fault
Relay rampsZones where displacement transfers between two overlapping, non-connected faults
Horst & grabenUplifted block (horst) between two normal faults / down-dropped block (graben) between two normal faults
▸ Horst & graben structure
python · fault relation matrix (GemPy-style)
# Rows/columns = series, in computation order.
# A 1 at [i, j] means series i offsets/affects series j.
import numpy as np

fault_relations = np.array([
#                FaultA  FaultB  FaultC  Strat
    [0, 1, 1, 1],   # FaultA offsets B, C, and stratigraphy
    [0, 0, 1, 1],   # FaultB offsets C and stratigraphy
    [0, 0, 0, 1],   # FaultC offsets only stratigraphy
    [0, 0, 0, 0],   # Stratigraphy offsets nothing
])
geo_model.set_fault_relation(fault_relations)

Model validation, QA & uncertainty quantification

Expert practice treats a geological model as a hypothesis to be tested, not a final answer. Standard checks:

  1. Cross-validation against held-out data — remove some known points, run the model, check how close the prediction is to the held-out truth
  2. Thickness sanity checks — verify modelled layer thickness stays within geologically reasonable bounds across the model
  3. Stochastic/Monte Carlo runs — perturb uncertain inputs (e.g. borehole pick depths) within their error bounds and re-run many times to get a probability map of where each lithology likely occurs
  4. Geological plausibility review — a domain expert visually checks the model against regional geological knowledge that isn't captured in the raw data
python · simple Monte Carlo uncertainty sketch
import numpy as np

n_realizations = 100
results = []

for i in range(n_realizations):
    # Perturb each borehole pick within its measurement uncertainty
    perturbed_z = original_z + np.random.normal(0, measurement_error_std)
    geo_model.modify_surface_points(point_id, Z=perturbed_z)
    sol = gp.compute_model(geo_model)
    results.append(sol.lith_block)

# Probability of a given voxel being a particular lithology:
results = np.array(results)  # shape: (n_realizations, n_voxels)
prob_sandstone = (results == SANDSTONE_ID).mean(axis=0)

Domain case studies

Mining

Ore body modelling

Focus shifts from clean stratigraphic layers to irregular mineralized zones, often defined by grade thresholds (e.g. "anywhere with >0.5% copper") rather than lithological boundaries. Combines implicit geometric modelling with geostatistical grade estimation (a separate kriging problem on the property values themselves, not just the geometry).

Petroleum / Oil & Gas

Reservoir characterization

Heavy reliance on seismic-derived horizons (Tier 3.3) combined with sparse, expensive well data. Structural restoration (5.1) is routinely used to validate fault interpretations before committing to a reservoir model that informs multi-million dollar drilling decisions.

Hydrogeology

Aquifer mapping

Models focus on permeable vs. impermeable layer geometry to predict groundwater flow paths. Property fields (hydraulic conductivity) are often more important than precise lithological boundaries — a geological model here typically feeds into a separate groundwater flow simulator.

Geotechnical / Civil

Subsurface profiling for construction

Shallow, high-resolution models (tens of metres, not kilometres) focused on soil/rock competency for foundation design — dense borehole data over a small area is typical, with less reliance on inferred structural geology.

Full Glossary

TermDefinition
AnisotropyDirectional bias in spatial correlation (e.g. stronger horizontally than vertically)
AnticlineUpward-arching fold; oldest rock at the core
BoreholeA drilled hole used to sample subsurface rock at depth
Cross-cutting relationshipPrinciple: anything cutting through rock is younger than the rock it cuts
DikeSteep igneous sheet intrusion cutting across host rock layering
DipThe angle a surface makes with horizontal
Dip directionCompass direction toward which a surface tilts downward
FaultA fracture with measurable displacement of rock on either side
FoldA bend in rock layers from compressional stress without fracturing
FootwallThe rock block below an inclined fault plane
GrabenDown-dropped block between two normal faults
Hanging wallThe rock block above an inclined fault plane
HorstUplifted block between two normal faults
Igneous rockRock formed from cooled magma
Implicit modellingBuilding surfaces via interpolation of scattered constraint data, rather than manual drawing
JointA fracture with no measurable displacement
KrigingA geostatistical interpolation method using a spatial covariance model
Lateral continuityPrinciple: layers extend until they thin, are bounded, or eroded
Metamorphic rockRock altered by heat/pressure without melting
NonconformityUnconformity where sedimentary rock overlies igneous/metamorphic basement
OnlapRelation where younger sediment drapes over older topography without eroding it
Original horizontalityPrinciple: sediments are deposited roughly horizontal
OutcropRock exposed at the surface, not covered by soil
ReflectorA boundary visible in seismic data, usually a layer boundary
RestorationReversing deformation on a model to check physical plausibility
Sedimentary rockRock formed from deposited and compacted particles
SeriesA group of surfaces sharing one continuous geological event, in modelling software
SillSub-horizontal igneous sheet intrusion parallel to host layering
StrikeDirection of a horizontal line on a tilted surface, perpendicular to dip direction
StratigraphyThe study of rock layers — their sequence, composition, and age
SuperpositionPrinciple: in undisturbed strata, older rock lies below younger rock
SynclineDownward-sagging fold; oldest rock at the outer edges
ThrowVertical component of fault displacement
UnconformityA buried erosion surface representing a gap in geological time
Course complete. You now have the full vocabulary and conceptual model needed to build, interpret, and validate 3-D geological models — from raw field data through to expert-level uncertainty quantification. Pair this with the companion GemPy code tutorial to put it into practice.