stellar-test.js
53 lines · in natura-src-1.0.0.zip as natura/stellar-test.js · a test
// stellar-test.js -- assertions for the Stellar system (a star's life over billions of years).
const N = require('./natura.js');
const { makeStellar, evolve } = require('./stellar.js');
const { World, UNITS } = N;
const EON = UNITS.EON;
let pass = 0, fail = 0;
const ok = (c, m) => { if (c) pass++; else { fail++; console.log(' FAIL: ' + m); } };
const L = (age, mass) => evolve(age, mass || 1, 1).L;
console.log('stellar assertions:');
// 1. MAIN-SEQUENCE BRIGHTENING: a sun-like star slowly brightens across its main-sequence life
ok(L(1) < L(8) && L(8) < 1.5, 'main sequence brightens slowly (L ' + L(1).toFixed(2) + ' at 1Gyr -> ' + L(8).toFixed(2) + ' at 8Gyr)');
// 2. RED-GIANT SPIKE: near the end the star swells and its luminosity climbs by ~1000x
{
let maxL = 0; for (let a = 0; a < 13; a += 0.02) maxL = Math.max(maxL, L(a));
ok(maxL > 500, 'red-giant luminosity spike (peak L ' + maxL.toFixed(0) + ' vs ~1 on the main sequence)');
const rg = evolve(10.9, 1, 1);
ok(rg.phase === 'red-giant' && rg.R > 50, 'the star swells into a giant near the RGB tip (R ' + rg.R.toFixed(0) + ' solar radii)');
}
// 3. WHITE-DWARF COOLING: after the giant, the ember cools for billions of years
ok(evolve(12.5, 1, 1).L < evolve(11.5, 1, 1).L && evolve(11.5, 1, 1).phase === 'white-dwarf',
'the white dwarf cools over time (L ' + evolve(11.5, 1, 1).L.toFixed(3) + ' -> ' + evolve(12.5, 1, 1).L.toFixed(3) + ')');
// 4. HABITABLE-ZONE MARCH: a fixed planet's insolation goes frozen(early) -> temperate(now) -> scorched(giant)
{
const early = evolve(0.5, 1, 1).insol, now = evolve(4.6, 1, 1).insol;
let scorch = 0; for (let a = 10; a < 11; a += 0.02) scorch = Math.max(scorch, evolve(a, 1, 1).insol);
ok(early < 0.85 && now > 0.9 && now < 1.15 && scorch > 100, 'insolation: cool birth (' + early.toFixed(2) + '), temperate now (' + now.toFixed(2) + '), scorched giant (' + scorch.toFixed(0) + ')');
}
// 5. MASS -> LIFETIME: a heavier star burns out FASTER (shorter main sequence)
ok(evolve(5, 1, 1).phase === 'main-sequence' && evolve(5, 2, 1).phase !== 'main-sequence',
'a heavier star leaves the main sequence sooner (at 5Gyr: 1 Msun still burning, 2 Msun already dead)');
// 6. EVENTS fire over the star's life, and DETERMINISM
{
const run = () => {
const w = World({ seed: 1, systems: [makeStellar({ age0Gyr: 0 })] });
const evs = [], track = [];
for (let i = 0; i < 280; i++) { const r = w.step(0.05 * EON); for (const e of r.events) evs.push(e.kind); if (i % 40 === 0) track.push(+w.fields().stellar.luminosity.toFixed(4)); }
return { evs, track };
};
const a = run(), b = run();
ok(a.evs.includes('leaveMainSequence') && a.evs.includes('whiteDwarf'), 'the star leaves the main sequence and becomes a white dwarf (events fired)');
ok(JSON.stringify(a.track) === JSON.stringify(b.track), 'same star -> identical life track (reproducible)');
}
console.log(`\n${pass} passed, ${fail} failed`);
process.exit(fail ? 1 : 0);