Full well trajectory design and analysis library for oil & gas directional drilling.
- Minimum curvature method — industry-standard survey computation (MD/INC/AZI → TVD/N/E/VS)
- Survey interpolation — interpolate at any MD, TVD, or VS; forward/backward extrapolation
- Trajectory design — build complete trajectories from KOP, build rate, and target (TVD + VS)
- Segment builder — fluent API:
.vertical().build().hold().drop().turn() - Multi-objective Pareto optimization — minimize MD, tortuosity, and collision risk simultaneously; knee-point selection
- Survey vs plan comparison — left/right, above/below, ahead/behind, centre-to-centre distance
- Anti-collision — separation factor scanning, ISCWSA MWD uncertainty ellipses
- Constraint validation — max DLS, max inclination, max MD, target tolerance
- I/O — parse CSV, JSON, LAS 2.0, WITSML-style tab files; export CSV, JSON, Markdown reports
- Visualization — 2D/3D Plotly.js charts (plan view, vertical section, DLS profile, Pareto front) and Three.js 3D renderer
- ES6 modules — tree-shakeable ESM + CJS + UMD builds
npm install welltrajOptional visualization peer dependencies:
npm install plotly.js-dist # for PlotlyRenderer
npm install three # for ThreeRendererimport { Survey } from 'welltraj';
const survey = new Survey({ name: 'DEMO-01', vsAzimuth: 45 });
survey.setStations([
{ md: 0, inc: 0, azi: 45 },
{ md: 500, inc: 0, azi: 45 },
{ md: 1200, inc: 30, azi: 45 },
{ md: 2000, inc: 60, azi: 47 },
{ md: 3000, inc: 90, azi: 47 },
]);
// Computed coordinates
console.log(survey.get(-1)); // last station
console.log(survey.atMD(1600)); // interpolated at MD 1600
console.log(survey.stats()); // summary statistics
console.log(survey.toCSV()); // export| Option | Type | Default | Description |
|---|---|---|---|
name |
string | 'Unnamed Well' |
Well name |
vsAzimuth |
number | 0 |
Vertical section reference azimuth (°) |
origin |
{tvd, north, east} |
{0,0,0} |
Starting coordinates |
Methods:
| Method | Returns | Description |
|---|---|---|
setStations(stations) |
Survey |
Replace all stations (chainable) |
addStation(md, inc, azi) |
Survey |
Append one station |
get(i) |
SurveyResult |
Get result at index (negative = from end) |
getAll() |
SurveyResult[] |
All computed results |
atMD(md) |
SurveyResult |
Interpolate at measured depth |
atTVD(tvd) |
SurveyResult |
Find point at TVD |
atVS(vs) |
SurveyResult |
Find point at vertical section |
stats() |
Object |
Trajectory statistics |
toCSV(options?) |
string |
Export as CSV |
toJSON() |
Object |
Export as JSON |
Survey.fromCSV(text, options?) |
Survey |
Parse CSV |
Survey.fromJSON(data, options?) |
Survey |
Parse JSON |
SurveyResult fields: md, inc, azi, tvd, north, east, vs, dls, dl, closure, closureAzi
import { SegmentBuilder } from 'welltraj';
const survey = new SegmentBuilder({ azimuth: 45, mdStep: 10 })
.vertical(500) // drill straight to 500m MD
.build(90, 3) // build to 90° at 3°/30m
.hold(1500) // 1500m horizontal section
.toSurvey({ name: 'My Well' });Methods: .vertical(length) · .build(targetInc, buildRate) · .drop(targetInc, dropRate) · .hold(length) · .turn(targetAzi, turnRate) · .station(md, inc, azi) · .toSurvey(options?) · .getStations()
import { TrajectoryDesigner } from 'welltraj';
const survey = new TrajectoryDesigner({
kop: 500, azimuth: 45, buildRate: 3,
target: { tvd: 3200, vs: 1800 }
}).design();import { Optimizer } from 'welltraj';
const { front, knee, all } = new Optimizer({
baseOptions: { azimuth: 45, target: { tvd: 3200, vs: 1800 } },
buildRates: [2, 3, 4, 5],
kopValues: [300, 500, 700, 1000]
}).run();
console.log(`Knee point: KOP=${knee.params.kop}, buildRate=${knee.params.buildRate}`);import { compareSurveys, landingAccuracy } from 'welltraj';
const cmp = compareSurveys(actualSurvey, planSurvey);
// cmp[i].leftRight, .aboveBelow, .aheadBehind, .ctc
const accuracy = landingAccuracy(actualSurvey, { tvd: 3200, north: 1200, east: 1200 });import { scanAntiCollision, minSeparationFactor } from 'welltraj';
const records = scanAntiCollision(designSurvey, offsetWells);
const { sf } = minSeparationFactor(designSurvey, offsetWells);
console.log(`Min SF: ${sf.toFixed(3)}`); // >= 1.5 is safeimport { validate, passes } from 'welltraj';
const violations = validate(survey, {
maxDLS: 5, // °/30m
maxInc: 92, // °
maxMD: 6000,
targetTVD: 3200,
targetTolerance: 20
});import { parse, surveyTable, statsSummary } from 'welltraj';
// Auto-detect format
const survey = parse(csvOrJsonString, { name: 'My Well', vsAzimuth: 45 });
// Reports
console.log(surveyTable(survey));
console.log(statsSummary(survey));import Plotly from 'plotly.js-dist';
import { PlotlyRenderer } from 'welltraj';
const r = new PlotlyRenderer(Plotly);
r.plot3D('container-id', [survey]);
r.plotVerticalSection('vs-div', [actualSurvey, planSurvey]);
r.plotDLS('dls-div', [survey], { maxDLS: 5 });All core computations are also exported as standalone functions:
import { dogleg, doglegSeverity, ratioFactor, segment, compute } from 'welltraj';
const dl = dogleg(0, 45, 30, 47); // degrees
const dls = doglegSeverity(0, 45, 30, 47, 300); // °/30m
const seg = segment({ md: 0, inc: 0, azi: 0 }, { md: 300, inc: 30, azi: 45 });
// seg: { dTVD, dNorth, dEast, dls, dl }node examples/01-basic-survey.js
node examples/02-trajectory-design.js
node examples/03-anti-collision.js
node examples/04-pareto-optimization.jsnpm testnpm run build # produces dist/welltraj.esm.js, .cjs.js, .umd.jsMIT © Ismail Harkat "# welltraj"