← Natura·Docs/Modules/Source

traffic-test.js

54 lines · in natura-src-1.0.0.zip as natura/traffic-test.js · a test

// traffic-test.js -- assertions for TRAFFIC (the Intelligent Driver Model: free flow, phantom jams, the
// fundamental diagram). Run: node traffic-test.js
const N = require('./natura.js');
const { makeTraffic } = require('./traffic.js');
const { World, UNITS } = N;
const HOUR = UNITS.HOUR;
let pass = 0, fail = 0;
const ok = (c, m) => { if (c) pass++; else { fail++; console.log('  FAIL: ' + m); } };
const build = (cfg) => World({ seed: 9, systems: [makeTraffic(Object.assign({ roadLen: 500, seed: 9 }, cfg))] });
const run = (w, secs) => { const n = Math.round(secs / 60); for (let i = 0; i < n; i++) w.step(60); };   // step a minute at a time

console.log('traffic assertions:');

// 1. DETERMINISM
{
  const a = build({ N: 30 }), b = build({ N: 30 }); run(a, 1200); run(b, 1200);
  const pa = a.fields().traffic.positions, pb = b.fields().traffic.positions; let same = true;
  for (let k = 0; k < 30; k++) if (pa[k] !== pb[k]) { same = false; break; }
  ok(same, 'same seed -> identical traffic (reproducible)');
}

// 2. FREE FLOW at low density -- few cars settle near the desired speed, no jams
{
  const w = build({ N: 8 }); run(w, 1800); const f = w.fields().traffic;
  ok(f.meanSpeed > 18 && f.jamCount === 0, 'low density runs FREE (mean speed ' + f.meanSpeed + ' m/s, ' + f.jamCount + ' jammed)');
}

// 3. CONGESTION at high density -- the road clogs, mean speed collapses, stop-and-go appears
{
  const w = build({ N: 55 }); run(w, 2400); const f = w.fields().traffic;
  ok(f.meanSpeed < 12 && f.jamCount > 0, 'high density CONGESTS: speed collapses + jams form (mean ' + f.meanSpeed + ', jammed ' + f.jamCount + ')');
}

// 4. COLLISION-FREE -- cars never overlap, at any density (the model is safe)
{
  const w = build({ N: 55 }); let minG = Infinity; for (let i = 0; i < 40; i++) { run(w, 60); minG = Math.min(minG, w.fields().traffic.minGap); }
  ok(minG > -0.01, 'no car ever overlaps the one ahead (min gap ' + minG.toFixed(2) + ' m)');
}

// 5. THE FUNDAMENTAL DIAGRAM -- flow rises with density then FALLS (the jam regime): mid-density carries the most
{
  const q = (n) => { const w = build({ N: n }); run(w, 2400); return w.fields().traffic.flow; };
  const low = q(8), mid = q(28), high = q(60);
  ok(mid > low && mid > high, 'flow peaks at intermediate density then falls into congestion (low ' + low + ', mid ' + mid + ', high ' + high + ' veh/hr)');
}

// 6. PHANTOM JAM -- at an unstable density a jam nucleates from nothing and persists (no cause, no collision)
{
  const w = build({ N: 45 }); run(w, 600); const early = w.fields().traffic.jamCount; run(w, 2400); const late = w.fields().traffic.jamCount;
  ok(late > 0, 'a phantom jam emerges from smooth flow at an unstable density (jammed cars: ' + late + ')');
}

console.log(`\n${pass} passed, ${fail} failed`);
process.exit(fail ? 1 : 0);