import { describe, expect, it } from 'vitest'; import { computeDelay, sperrzeitenAusFenstern } from '@/domain/rilsa/delay'; /* * Fassung 5.10.0 - die Grundwartezeit. * * `computeDelay` bildet den Zaehler der Grundwartezeit seit Befund 26 aus * Summe(r_j^2) / tU ueber die Sperrzeiten des Umlaufs * (`summeSperrzeitenQuadrate` in src/domain/rilsa/delay.ts); ein Nachtrag hat * die Uebergabe der Sperrzeiten aus dem Planaufbau nachgezogen, seitdem kommt * die allgemeine Form auch im laufenden Programm an. * * Die Unterlagen fuehrten die Groesse weiter ausschliesslich in der * geschlossenen Einfensterform tW,G = tU * (1 - fA)^2 / (2 * (1 - min(1; x) * fA)). * Wer damit nachrechnet, was das Programm bei mehreren Freigabezeitfenstern * ausweist, kommt auf das Doppelte und haelt die Zahl des Programms fuer * falsch. Die Faelle hier messen an `computeDelay` nach, dass die allgemeine * Form gilt - und dass die Einfensterform genau der Sonderfall ist. * * NACHTRAG: Die r_j der Formel sind nicht die Rot-Abschnitte, * die `sperrzeitenAusFenstern` liefert - deren Summe ist tU - tF. Erst nachdem * `summeSperrzeitenQuadrate` je Freigabezeitfenster den Zeitzuschlag der * Abflusszeit abgezogen hat, gilt Summe(r_j) = tU - tA. Der letzte Fall misst * diesen Unterschied. * * Die beiden Rechenfaelle sind der Beleg, dass die allgemeine Form stimmt, und * werden rot, sobald jemand die Rechnung wieder auf die Einfensterform * zurueckdreht. */ /** * Umlauf mit zwei gleich langen Sperrzeiten. * * tU = 100 s, tA = 20 s: Die wirksame Sperrzeit betraegt 80 s. Uebergeben * werden 45 s + 45 s - die Sperrzeiten aus den Freigabezeitfenstern tragen den * Zeitzuschlag der Abflusszeit noch; `summeSperrzeitenQuadrate` zieht ihn je * Fenster ab und rechnet mit 40 s + 40 s. * * Zaehler allgemein: (40² + 40²) / 100 = 32 s. Zaehler geschlossen: 100 · (1 − * 0,2)² = 64 s - genau das Doppelte. */ const ZWEI_FENSTER = { cycleTime: 100, greenTime: 19, abflusszeit: 20, capacity: 1000, demand: 500, verfahren: 'HBS', mode: 'kfz', } as const; describe('Die Grundwartezeit rechnet in der allgemeinen Form', () => { it('rechnet bei zwei Fenstern nach der allgemeinen Form', () => { const zwei = computeDelay({ ...ZWEI_FENSTER, sperrzeiten: [45, 45] }); // 32 s / (2 · (1 − 0,5 · 0,2)) = 32 / 1,8 = 17,78 s expect(zwei.rechenweg.abflussanteil).toBeCloseTo(0.2, 6); expect(zwei.degreeOfSaturation).toBeCloseTo(0.5, 6); expect(zwei.uniformDelay).toBeCloseTo(32 / 1.8, 1); }); it('fällt ohne brauchbare Sperrzeiten auf die Einfensterform zurück', () => { // Genau der Sonderfall der Einfensterform: 64 s / 1,8 = 35,56 s, // das Doppelte des Wertes darueber. const ohne = computeDelay(ZWEI_FENSTER); const unbrauchbar = computeDelay({ ...ZWEI_FENSTER, sperrzeiten: [0, Number.NaN] }); expect(ohne.uniformDelay).toBeCloseTo(64 / 1.8, 1); expect(unbrauchbar.uniformDelay).toBe(ohne.uniformDelay); // Ein Fenster: Die allgemeine Form ergibt denselben Wert wie der Rueckfall. expect(computeDelay({ ...ZWEI_FENSTER, sperrzeiten: [81] }).uniformDelay).toBeCloseTo( ohne.uniformDelay, 1, ); }); it('unterscheidet in der Rechnung die Rot-Abschnitte von den wirksamen r_j', () => { // Zwei Freigabezeitfenster von je 5 s im 100-s-Umlauf: tF = 10 s. const rotAbschnitte = sperrzeitenAusFenstern( [ { start: 0, duration: 5 }, { start: 50, duration: 5 }, ], 100, ); expect(rotAbschnitte).toEqual([45, 45]); // Die Summe der gelieferten Abschnitte ist tU - tF, nicht tU - tA. const summeRot = rotAbschnitte.reduce((wert, r) => wert + r, 0); expect(summeRot).toBeCloseTo(100 - 10, 6); expect(summeRot).not.toBeCloseTo(100 - 12, 6); // tA = tF + 1 s je Fenster = 12 s. Erst die wirksamen Sperrzeiten // 44 s + 44 s summieren sich zu tU - tA = 88 s. const ergebnis = computeDelay({ ...ZWEI_FENSTER, abflusszeit: 12, sperrzeiten: rotAbschnitte }); expect(ergebnis.rechenweg.abflussanteil).toBeCloseTo(0.12, 6); // Zaehler (44² + 44²) / 100 = 38,72 s, Nenner 2 · (1 − 0,5 · 0,12) = 1,88. expect(ergebnis.uniformDelay).toBeCloseTo(38.72 / 1.88, 1); // Mit den Rot-Abschnitten selbst waeren es (45² + 45²) / 100 = 40,5 s - // genau der Nachrechenfehler, den die alte Gleichsetzung ausloeste. expect(ergebnis.uniformDelay).not.toBeCloseTo(40.5 / 1.88, 1); }); });