INTEGRATIONS

Scripting SDK

Reference for Converge scripts - the converge object, every engine function, print, table and plot, units, limits and a worked example.

Last edited

Overview

Scripts let you run any Converge engine from JavaScript inside the app, loop over cases, and show the results as tables and plots. You write them on Integrations > Scripts. Each run executes in a fresh, sandboxed Web Worker in your browser, with no network access, and the worker is destroyed when the run ends.

The same engine functions are available over the REST API and to an MCP client, with the same inputs and outputs.

How a script runs

Your script is the body of an async function, so you can use await at the top level and return a value. These names are in scope:

Name What it is
converge The SDK: one namespace per module, plus helpers.
print(...args) Writes a line to the output. Same as console.log.
console log, info, warn, error, debug.
table(data, title?) Adds a table tab to the output.
plot(series, options?) Adds a plot tab to the output.

A return value is shown when the run ends. Every engine call is synchronous and returns plain data (objects, arrays, numbers).

Run with the run button or Ctrl/Cmd + Enter. Choose Time limit (5, 10, 30 or 60 s; 10 s by default). Save to project stores the script in the current project, up to 100 scripts per project; saved scripts are kept in project snapshots and exports.

The converge object

Engine functions

Every engine function has the form:

js
converge.<namespace>.<function>(input, options?)
  • input: one object. Unknown keys are an error that lists the valid keys.
  • options.units: 'field' (default) or 'metric', for this call only.

An invalid input throws an Error whose message names each bad field, for example converge.well.nodal: invalid input (fluid.api: ...).

Helpers

Signature Returns
converge.setUnits(units) Sets 'field' or 'metric' for the rest of the run. Returns the unit system.
converge.units() The current unit system for the run.
converge.functions() Array of { name, module, title } for every engine function.
converge.describe(name) { name, module, title, description, inputSchema, example }. inputSchema is JSON Schema.
converge.example(name) A copy of the documented example input for name, ready to modify and pass back.

describe and example take the full dotted name, for example converge.example('well.nodal'). They throw for an unknown name.

Units

Field units by default: psia, degF, ft, STB/d, Mscf/d, scf/STB. Pass { units: 'metric' } as the second argument, or call converge.setUnits('metric') once, to send and receive bara, degC, m, sm3/d and sm3/sm3. The main outputs come back in the same system.

js
converge.setUnits('metric');
const z = converge.pvt.gasProperties({ psia: 200, TF: 90, gasSG: 0.7 }); // 200 bara, 90 degC

A metric result carries a units object: units.system is 'metric' and units.converted lists the output keys that were converted, with their unit. Keys not listed are still in field units.

Function reference

Inputs listed are the main keys. Call converge.describe(name) for the full schema, defaults and limits.

pvt (Black-oil PVT)

Function Main inputs Returns
pvt.blackOilTable(input) fluid (black-oil: api, gasSG, gor scf/STB, reservoirT degF, optional waterSalinity ppm, pbMeasured psia); pressures psia or range { pMin, pMax, n }; optional TF pb psia, rows (Rs, Bo, oil viscosity, density, co, Bg, Z, gas viscosity, water properties, IFTs), warnings
pvt.gasProperties(input) psia, TF, gasSG Z (Sutton + Dranchuk-Abou-Kassem), Bg rb/scf, viscosity cp (Lee-Gonzalez-Eakin), density lbm/ft3
pvt.waterProperties(input) psia, TF, salinityPpm (default 30,000) McCain water FVF, viscosity, density

eos (Compositional PVT)

fluid is { kind: 'eos', eos, components, plus, split, ... } or { kind: 'spe5' } for the SPE5 benchmark oil. eos is 'PR', 'PR78' (default) or 'SRK'.

Function Main inputs Returns
eos.summary(input) fluid, reservoirTF, optional separator stages Saturation pressure, fluid type, separator test, CGR for gases, component table
eos.flash(input) fluid, psia, TF Vapour fraction, x/y, K-values, phase properties
eos.envelope(input) fluid, qualities (vapour fractions, default none) Bubble and dew branches, critical point, cricondenbar, cricondentherm, quality lines
eos.blackOilTable(input) fluid, TF, pMax, optional separator Separator-corrected black-oil table, pb, API, gas SG, separator GOR, warnings

well (Well performance)

Function Main inputs Returns
well.ipr(input) kind ('oil' or 'gas'), pr psia, Tres degF, ipr model, fluid, points (default 40) IPR curve, AOF, total skin and its parts for Darcy-type models
well.vlp(input) well case, fluid, optional rates Flowing bottomhole pressure per rate, VLP minimum
well.nodal(input) well case, fluid IPR, VLP, every intersection with stability, operating point, wellhead pressure, traverse

mbal (Material balance)

Function Main inputs Returns
mbal.analyse(input) fluid, tank, history rows Havlena-Odeh terms, aquifer influx We bbl, diagnostic plots, drive indices when N or G is given
mbal.historyMatch(input) fluid, tank, history, optional fitM, samples (default 150), seed Best-fit in-place volume (and m, aquifer), residuals, pressure match, P90/P50/P10

dca (Decline & RTA)

points are { t, q } with t in days since first production and q in the rate basis. Nominal declines are 1/day. rateUnit is 'STB/d' (default), 'Mscf/d' or 'sm3/d'.

Function Main inputs Returns
dca.fit(input) kind ('arps' default, 'modified-hyperbolic', 'duong', 'sepd'), points, optional excluded, autoOutliers, bBounds, terminalEffectiveAnnual, qEconomic, tMaxDays, rateUnit Fitted model, fit statistics; with qEconomic, eur with eurUnit and the time cap (default 50 years)
dca.compare(input) points, qEconomic, optional tMaxDays, rateUnit Every model ranked by AIC with its EUR, R2 and warnings
dca.forecast(input) model, times days, or tMaxDays (default 3,652.5) and stepDays (default 30.4375) Rate and cumulative at each time
dca.eur(input) model, qEconomic, optional tMaxDays (at most 100 years), t, rateUnit EUR, time at the limit, rate at t; a warning when the limit is at or above qi

welltest (Well test)

Function Main inputs Returns
welltest.synthetic(input) reservoir, pi psia, q STB/d, tpHr, shutInHr, pointsPerPeriod (default 40), boundary Drawdown and build-up pressure record, plus the build-up ready for the next two functions
welltest.horner(input) dtHr, pws, tpHr, pwf, fluid { q, B, mu, h, phi, ct, rw }, optional window k md, skin, p* and p1hr psia, skin pressure drop, flow efficiency, radius of investigation, r2
welltest.derivative(input) tHr, dp, smoothing (default 0.1) Pressure change and Bourdet derivative against time

sim (Simulation)

Function Main inputs Returns
sim.run(input) preset ('spe1', 'buckley-leverett', 'tank') or your own case; optional cells, days, timeLimitMs (default 20,000) Field and well rates and cumulatives per report step, average pressure, initial in-place, run statistics, warnings
sim.buckleyLeverett(input) sw, krw, kro, muW, muO cp Front saturation, average saturation behind the front, breakthrough pore volumes

network (Network)

Function Main inputs Returns
network.solve(input) Optional network { nodes, branches } (default: built-in four-well example), fluid, maxIter Node pressures, branch rates, constraint checks, totals

integrated (Integrated forecast)

Function Main inputs Returns
integrated.run(input) Optional case (default: built-in example), fluid, endDays Forecast per step, plateau length days, EUR, end reason

econ (Economics)

Function Main inputs Returns
econ.metrics(input) cashFlows per period, discountRates (default [0.1]), periodYears (default 1), discounting (default 'mid-period') NPV per rate, IRR (null when the cash flow never changes sign), payback years
econ.cashFlow(input) case: the Economics data model (production, prices, opex, capex, fiscal, discountRates, ...) Full cash flow, taxes, government take, metrics, warnings. See Economics.

data (Data)

These two run under the Integrations licence.

Function Main inputs Returns
data.parseTable(input) text (CSV, tab, semicolon or space separated), optional delimiter Row count, first 20 rows, detected columns, units, date order, frequency and layout
data.validate(input) asOf ISO date, wells (each with id, name, type, frequency, optional initialPressure, bubblePoint psia, and records) Validation report: each issue with severity and suggested fix

table and plot

table(data, title?)

data can be:

  • an array of objects (columns are the union of the keys),
  • an array of arrays (columns col1, col2, ...),
  • { columns, rows },
  • a plain object (shown as key / value rows).

Nested values are shown as compact JSON, cut at 120 characters.

plot(series, options?)

series is { name?, x, y } or an array of them; plot(x, y, options?) with two number arrays also works. x and y must have the same length. Pairs with a null or non-finite value are skipped.

options: title, xTitle, yTitle, logX, logY.

Limits

Limit Value
Time limit 10 s default, 60 s maximum
Script length 100,000 characters
Output lines 2,000 per run; each line cut at 20,000 characters
Tables 20 per run, 5,000 rows each
Plots 20 per run, 20,000 points in total per plot

When the time limit is reached, the worker is terminated, which stops even an endless loop.

Sandbox rules

  • No import anywhere in the script, not even inside a string or a comment. Everything you need is on converge.
  • No network or storage: fetch, XMLHttpRequest, WebSocket, indexedDB, navigator and similar are removed.
  • No code built from strings: eval, Function and string handlers for setTimeout and setInterval are removed.
  • Nothing survives between runs.

Seats

When you are signed in, the page takes the licence seat for each module a script uses before it runs, and returns the seats when the run ends. It finds them by reading converge.<namespace> in your code. Write calls out literally: a namespace reached another way (for example through a variable) throws converge.<ns> is not available in this run.

Worked example

This script fits a decline to two years of monthly rates, ranks every model, and plots a five-year forecast. It is one of the built-in examples.

js
// Monthly rates (days, STB/d). Replace with your own.
const points = [];
for (let i = 0; i < 24; i++) {
  points.push({ t: i * 30.4, q: 1200 / Math.pow(1 + 0.9 * 0.004 * i * 30.4, 1 / 0.9) * (1 + 0.03 * Math.sin(i)) });
}

const fit = converge.dca.fit({ kind: 'arps', points, qEconomic: 20 });
const m = fit.model;
print(`Arps: qi = ${m.qi.toFixed(0)} STB/d, Di = ${m.Di.toFixed(5)} 1/d, b = ${m.b.toFixed(2)}, R2 = ${fit.r2Rate.toFixed(3)}`);
print('EUR to 20 STB/d:', Math.round(fit.eur.eur).toLocaleString(), 'STB');

const ranked = converge.dca.compare({ points, qEconomic: 20 });
table(ranked.map((r) => ({ model: r.kind, 'EUR (STB)': Math.round(r.eur), AIC: r.aic, 'R2 rate': r.r2Rate })), 'Models ranked by AIC');

const f = converge.dca.forecast({ model: fit.model, tMaxDays: 5 * 365.25, stepDays: 30.4 });
plot(
  [
    { name: 'data', x: points.map((p) => p.t), y: points.map((p) => p.q) },
    { name: 'forecast', x: f.t, y: f.q },
  ],
  { title: 'Rate and forecast', xTitle: 'Time (days)', yTitle: 'Rate (STB/d)', logY: true },
);

A second pattern: start from a documented example and vary one input.

js
const base = converge.example('well.nodal');
const rows = [];
for (const wct of [0, 0.2, 0.4, 0.6, 0.8]) {
  const well = { ...base.well, ratios: { ...base.well.ratios, wct } };
  const op = converge.well.nodal({ well, fluid: base.fluid }).nodal.operating;
  rows.push({ 'water cut': wct, 'liquid (STB/d)': op ? op.q : null, 'pwf (psia)': op ? op.pwf : null });
}
table(rows, 'Operating point vs water cut');