import { describe, expect, it } from 'vitest'; import { buildSignalPlan } from '@/domain/plan/signalPlan'; import { createStandardIntersectionProject } from '@/domain/model/factory'; import { TrafficSimulation, type SimulationSnapshot } from '@/domain/simulation/simulation'; /** * Ankunftsschwankungen duerfen um die erfasste Verkehrsstaerke streuen, sie * duerfen sie nicht anheben (Befund 25, behoben in Fassung 5.9.0). * * Was der Anwender sonst erlebt: Er setzt den Haken "Ankunftsschwankungen * berücksichtigen", laesst eine Stunde laufen und liest in der Spalte * "Angekommen" fuer eine Gruppe mit 620 Fz/h rund 1800 Fahrzeuge ab. Rueckstau, * Laenge, mittlere Wartezeit und Qualitaetsstufe sind damit unbrauchbar, und der * Rueckstau waechst auch dort unbegrenzt, wo die Leistungsfaehigkeit desselben * Plans die Gruppe als deutlich unterausgelastet fuehrt. * * Ursache: Der Bruchteil eines Fahrzeugs wurde beim Feuern der Ziehung * verworfen, statt verbraucht zu werden - je Zeitschritt ein * Erwartungswert-Ueberschuss von rem * (1 - rem). */ const FESTES_DATUM = new Date('2026-01-01T00:00:00Z'); const PROJEKT = createStandardIntersectionProject('Ankunftsschwankungen', FESTES_DATUM); const PLAN = buildSignalPlan(PROJEKT); function laufen(sekunden: number, seed?: number): SimulationSnapshot { const sim = new TrafficSimulation(PLAN, PROJEKT, { timeStep: 0.5, stochastic: true, ...(seed === undefined ? {} : { seed }), }); for (let i = 0; i < sekunden / 60; i += 1) sim.advance(60); return sim.snapshot(); } describe('Simulation mit Ankunftsschwankungen', () => { it('laesst in einer Stunde etwa so viele Fahrzeuge ankommen wie die Verkehrsstaerke angibt', () => { const snapshot = laufen(3600); for (const group of snapshot.groups) { // Vor der Behebung kamen bei 620 Fz/h 1814 Fahrzeuge an - das Dreifache. const meldung = `${group.name}: ${group.arrived} statt ${group.demand}`; expect(group.arrived, meldung).toBeGreaterThan(group.demand * 0.9); expect(group.arrived, meldung).toBeLessThan(group.demand * 1.1); } }); it('haelt den Erwartungswert ueber mehrere Startwerte bei der erfassten Verkehrsstaerke', () => { // Eine Schwankung streut um den Mittelwert; ueber viele Startwerte gemittelt // muss die erfasste Verkehrsstaerke herauskommen. Vor der Behebung lag der // Mittelwert systematisch um ein Mehrfaches darueber - unabhaengig vom // Startwert, denn der Ueberschuss rem * (1 - rem) ist nie negativ. const startwerte = [1, 2, 3, 5, 8, 13, 21, 34]; const summe = new Map(); for (const seed of startwerte) { for (const group of laufen(1800, seed).groups) { summe.set(group.groupId, (summe.get(group.groupId) ?? 0) + group.arrived); } } for (const group of laufen(1800).groups) { const mittel = (summe.get(group.groupId) ?? 0) / startwerte.length; // Eine halbe Stunde: die halbe stuendliche Verkehrsstaerke. const soll = group.demand / 2; expect(Math.abs(mittel - soll), `${group.name}: ${mittel} statt ${soll}`).toBeLessThan( soll * 0.05, ); } }); it('erzeugt nie mehr Fahrzeuge, als die Fahrzeugbilanz traegt', () => { const snapshot = laufen(1800); for (const group of snapshot.groups) { expect(group.arrived).toBe(group.departed + group.queue); expect(group.queue).toBeGreaterThanOrEqual(0); } }); });