A CAUTIONARY TALE, THEN A PLAYBOOK

How close is too close?

Every operator knows parent–child interference is real: infill too tight and the children drain the parents’ rock. So why does a straightforward regression on public data conclude that crowded wells produce more? This project answers that with a measurement, builds a calibrated forecasting model on free Alberta data, and stops — honestly — exactly where public data runs out. Which is also where it gets interesting for anyone holding the proprietary pieces.

Three numbers

+1.9

The high-grading trap. Compare wells across an asset and more neighbouring withdrawal correlates with more production. That isn’t physics — it’s development behaviour. Operators high-grade: the best rock gets the tightest spacing, so crowding becomes a proxy for rock quality. Any spacing study built on cross-asset comparison inherits this and reports interference with the wrong sign.

−5.3

Same-pad siblings tell the truth. Wells on one pad share rock, vintage, and completion era — so comparing only pad-mates removes geology from the question. Across 4,858 wells on 1,416 multi-well pads, the slope flips negative [95% CI −11.4 to −0.5, cluster-bootstrapped by pad]. That is interference, isolated the way a field trial would isolate it.

+16.7

The what-if price. Ask the trained model “what would this well have done without its pad-mates?” and the median answer is a 16.7-point crowding penalty on first-year pace, relative to peers [p25/p75 +7.3/+31.4]. Material money, right sign — and, on public data alone, too uncertain to set spacing with. The report says so in as many words.

Crowding-vs-production slopes, in points of peer-median pace per unit of exposure, under two definitions of crowding: offset withdrawal (how much the neighbours produced) and offset count (how many neighbours). Both flip sign the moment geology is held fixed by comparing only same-pad wells; the withdrawal-weighted version excludes zero. Verbatim from reports/within_pad_contrast.md.

The geometry underneath

Everything rests on the same kind of 3-D wellbore reconstruction a drilling team runs for anti-collision — except province-wide, from the public record. Alberta publishes the directional surveys operators file (AER ST37), and from them this pipeline rebuilds 105,724 laterals across 65,082 horizontal wells deterministically: true closest approach in 3-D, TVD offset, lateral overlap length, and relative azimuth, computed well-to-well and leg-to-leg within multilaterals. That second part matters: in the Clearwater and other heavy-oil fairways, multi-leg fishbone wells drain against their own branches, and almost no public analysis quantifies it.

Here is one of those wells, live from the data — not a cartoon. Nine surveyed legs of a single Baytex multilateral in the Peace River heavy-oil area (licence 0318429), rebuilt from the 1,569 survey stations in its public ST37 record: one build section leaving surface, then a fan of laterals landing in the pay and running up to 1.7 km.

A real nine-leg fishbone, drawn from its AER ST37 directional surveys in true 3-D (2× vertical exaggeration; ticks drop to the landing plane). Drag to rotate. The question the model prices: how much do these legs steal from each other — and from the pad next door?

Every lateral in the province is drawn below.

Each line is one leg, heel to toe (straightened for display). Zoom into the Montney or Clearwater fairways and the pad architecture — parallel fans of sibling laterals — is the between-pad vs within-pad distinction made visible. Two-finger scroll passes through; use ⌘+scroll to zoom.

The model, in reservoir terms

Set aside the ML vocabulary for a moment and think about how an experienced engineer forecasts a new well: look at the offsets. Which wells are nearby, how close do they run, how much have they already withdrawn, how long have they been on. Then weigh them — a parallel lateral 150 m away that has produced hard for three years counts for a lot; a distant toe-to-toe neighbour counts for little. The model does exactly this, except the weighting is learned from 65,000 wells instead of hand-built.

The machinery that does the weighing is a set encoder — and the name is less exotic than it sounds. An offset list has no natural order and no fixed length: one well has two neighbours, another has thirty. A set encoder is a network built to accept exactly that kind of input — each offset described by its geometry (closest approach, TVD offset, overlap, azimuth), timing, and withdrawal history — and compress the list into a summary, learning which offsets deserve attention. Think of it as a drainage-weighted offset summary where the weights came from data rather than judgment. There are two of them: one over the well’s neighbours, one over its own legs, so a fishbone’s self-interference is priced by the same mechanism.

The output is deliberately in the industry’s native uncertainty language: P10/P50/P90 of first-year production pace, expressed relative to peer wells in the same play and vintage. Not a single-number type-curve multiplier — a band. And a band is only useful if it’s honest: on wells the model never saw, actuals fell below its P10 12% of the time, below its P50 53%, below its P90 88% — against the nominal 10/50/90. That’s the property reserves bands are supposed to have and often don’t.

What survived the gate

Before any of its output counts, the model had to pass a blind lookback — the ML term is a temporal holdout. The newest wells were sealed off during all development and opened exactly once, the way you’d grade last year’s forecast against this year’s actuals: no peeking, no second attempt, and a pre-registered rule that if the model lost to simpler methods, nothing downstream ships. It won — median forecast error of 71.6 points vs 78.1 for gradient boosting, the strongest of the conventional tabular-ML baselines — with the calibration above.

It also had to behave like a reservoir, not a curve-fit. The counterfactual machinery works like a what-if run: re-forecast the same well with its offsets’ withdrawal history erased, and the difference is the interference price. That price is near zero for isolated wells and grows monotonically with crowding — a hard physics gate the model passed.

Median depletion penalty by offset count: the drop in forecast pace when the what-if erases the offsets’ withdrawal history — from reports/counterfactual_summary.md. Monotone in crowding, near zero for isolated wells: the model behaves like drainage, not like a curve-fit.

And what failed is printed, not hidden. One probe walks a hypothetical offset in from 1,500 m to 100 m with no production history — an undrilled permit next door, in effect — and the forecast barely moves. Reservoir intuition says it shouldn’t move much (an undrained neighbour hasn’t taken your oil yet), but it also flags what the model can’t see: frac-hit risk from a future completion nearby, because public Alberta data carries no completion design at all. The model learned interference through depletion — the channel the data actually identifies — not through bare distance, and the evaluation gate records approach FAIL in the same table as its passes.

The playbook, as it was actually built

The repo is organized as the sequence it was developed in — each phase a runnable stage with its own artifact, so a team with proprietary data can replay it with their inputs:

  1. 0–2 · Cohort & geometry. Parse the ST37 shapefiles with a purpose-built PolyLineZ reader (no GDAL — the format is simple enough to read raw, and the pipeline stays dependency-light), keeping only bores flagged Surveyed, since the Calculated ones are two-vertex sticks with no real survey. Join geometry to Petrinex on the UWI built from the DLS label — where the details bite: the location exception is alphanumeric (F1, AA, W0…), and a digits-only parse silently dropped 11.7% of the province before a reconciliation check that refuses to lump “excluded” with “unexplained” caught it. Final parse: 532,553 of 532,623 wells (99.99%). Distances are computed in a local metre frame, never Web Mercator, which over-reads by ~1.44× at Alberta latitudes — enough to turn a 200 m spacing into a fictional 290. Each phase writes one Parquet artifact, so every downstream step is re-runnable and diffable.
  2. 3–5 · Offsets & timing. Candidate offset pairs come from a 2 km spatial grid index rather than an O(N²) sweep; each survives as a vectorized minimum segment-to-segment closest approach over every leg pair, both directions (overlap is asymmetric), plus TVD offset, overlap fraction, and relative azimuth — with the same math run within each multilateral for the fishbone self-spacing signal. Sanity anchor: pad siblings should read ~200–400 m apart at near-zero azimuth delta. Then the convention that keeps training honest: every feature describes the world as of the well’s own spud date — only offsets already on production count as parents, depletion is cumulative withdrawal before spud, and a later well never rewrites an earlier well’s row. Pace is normalized by a per-well EUR from an Arps decline fit, so the target is “% of expected recovery per year,” not raw barrels that reward long laterals.
  3. 6–7 · Dataset & baselines first. One training example per well: its own static description (lateral length, leg count, fan spread, vintage, TVD, play), its set of legs, and its set of offsets. The anti-fooling design does the heavy lifting: pad-mates are never split across train and validation (they share rock — a random split lets the model memorize geology and call it skill), the newest wells are sealed for the blind lookback, and normalization statistics come from training wells only. Before any neural network exists, a baseline ladder is fitted: global median (the floor), ridge regression on own-well features (roughly the geology-and-design share), and gradient boosting with the offset set flattened into aggregates — the honest competitor. If boosting can’t beat ridge, the offset signal isn’t there and no amount of architecture will conjure it.
  4. 8–9 · Model & the gate. The two set encoders with attention pooling — small by design, tens of thousands of parameters, trains on a laptop — output three monotone quantiles under pinball loss, the standard way to make a network forecast honest P10/P50/P90 bands rather than a single average. The empty-offset-list path (the counterfactual itself) is unit-tested to stay finite, along with permutation-invariance: shuffling the offset list must not change the forecast. Then the sealed wells are opened exactly once, and physics probes hold veto power over the MAE: isolated wells must price near zero, penalty must rise with depletion, thinning a fishbone’s legs must relieve its self-interference.
  5. 10–11 · What-ifs & the report. Score every well three ways: as drilled; offsets removed (the parent–child penalty); own legs thinned to a reference spacing (the fishbone penalty). Each score carries an in-domain flag — a well from a play-vintage combination the training data never saw gets its penalty marked untrustworthy rather than served anyway. And the report writes down everything, including the part that stung: the flattering single-run headline numbers shrank when training was repeated from different random starts and averaged, so the averaged numbers are the ones published here.

From playbook to well planner

The finish line is drawn honestly: an optimal-spacing prescription is not deliverable from public data alone. Three inputs that decide the answer are censored from the public record, and each maps to a specific failure you can see in the results above:

Here is the part worth saying plainly: none of these are exotic. They are standard tables in commercial well-data subscriptions — the kind of catalog a vendor like geoLOGIC has maintained for Western Canada for decades: complete production back to first oil, completion and frac detail, interpreted tops. This playbook’s gap list reads like a page from that catalog, and that is not a coincidence. Load those three inputs into the pipeline exactly as built — they slot into the same offset descriptions the set encoders already consume — retrain, and the cautionary tale becomes a working ML well planner: calibrated P10/P50/P90 for a proposed location, the parent–child penalty priced before permitting, spacing sensitivity run as a batch of what-ifs instead of a multi-well science experiment. WITH_PROPRIETARY_DATA.md is the drop-in spec, gap by gap. The machinery is finished; it is waiting on the data.