Skip to content

Scripts

A script (*.js) is JavaScript that runs beside the editor with the engine at hand. It can build provinces of its own and live them for decades, put experiments and Monte Carlo runs on the worker pool, pool experience over many seeds, and read and write the workspace's files. Its tables and plots land in the Console.

scripts/ae-by-age.js
// Actual against expected deaths by age band, on the basis the province was built on.
// Five simulated years, about twenty seconds. One province is about 400 people, so the
// bands are noisy: scripts/pooled-experience.js pools seeds on the worker pool instead.

const p = await province({ seed: 'ae-by-age' });
p.run({ years: 5 });

const bands = {};
for (const r of p.experience({ ageWidth: 10 })) {
  const b = (bands[r.age] ??= { band: `${r.age}-${r.age + 9}`, person_years: 0, deaths: 0, expected: 0 });
  b.person_years += r.person_years;
  b.deaths += r.deaths;
  b.expected += r.expected_deaths;
}
const rows = Object.values(bands).map((b) => ({ ...b, ae: b.expected > 0 ? b.deaths / b.expected : null }));
table(rows, ['band', 'person_years', 'deaths', 'expected', 'ae']);
plot({ x: rows.map((b) => b.band), series: { 'A/E': rows.map((b) => b.ae) }, title: 'Actual / expected deaths by age band', yLabel: 'A/E' });

const D = rows.reduce((a, b) => a + b.deaths, 0);
const E = rows.reduce((a, b) => a + b.expected, 0);
print(`Pooled A/E ${(D / E).toFixed(3)}: ${D} deaths against ${E.toFixed(1)} expected.`);

await writeFile('results/ae-by-age.csv', csv(rows));
print('Saved results/ae-by-age.csv');

How a script runs

  • In a worker. A script runs in a worker of its own, beside the editor, so a long run never freezes the IDE. The province on screen is untouched: province() builds one of the script's own.
  • As an async function. The file is the body of an async function, so await works at the top level, and return ends it early.
  • Saved first. Ctrl+Enter (or Run) saves the file and runs it.
  • Stoppable. Stop (Ctrl+Shift+Enter) ends it, and cancels any job it started on the worker pool.

An error is reported with its line, in the Console and in Problems.

What a script can do

To Use
Build a province and live it const p = await province({ seed, basis, mortality, shocks }), then p.run({ years })
Read its state p.indicators(), p.people(), p.households(), p.events(kind), p.date, p.basisHash
Measure its experience p.experience({ ageWidth, group }): deaths, person-years and expected deaths by year, age and sex
Compare policies await experiment(spec), or await experiment(template('premium-adequacy', 10))
Run many seeds of one basis await monteCarlo({ basis, seeds, years })
Pool experience over seeds await pooledExperience({ basis, seeds, years, ageWidth })
Show results print(...), table(rows), plot({ x, series, title })
Read and write files await readFile(path), await writeFile(path, csv(rows))

The Script API documents every function, its options and what it returns. The editor completes and checks all of it as you type.

How long things take

A province lives a simulated year in about three to four seconds in a script's worker; p.run({ years: 30 }) is a couple of minutes. Work that needs many seeds belongs on the worker pool, which runs several provinces at once: experiment(), monteCarlo() and pooledExperience() all go there. An experiment and pooled experience may ask for at most 800 province-years, as an experiment file may; a Monte Carlo run takes up to 200 seeds of up to 60 years.

Examples

The indicators after two years, as the sample's first-look.js begins:

const p = await province({ seed: 'first-look' });
p.run({ years: 2 });
table(METRICS.map((m) => ({ indicator: m.label, value: p.indicators()[m.id] })));

Who lives where:

const p = await province({});
const people = p.people();
const byCity = {};
for (const r of people) byCity[r.city] = (byCity[r.city] ?? 0) + 1;
table(Object.entries(byCity).map(([city, n]) => ({ city, residents: n })));

A Monte Carlo of the burial society:

const mc = await monteCarlo({ seeds: 16, years: 10 });
print(`Probability of ruin: ${mc.ruinProbability}`);

The sample workspace has four complete scripts to start from.