test(sim): Phase 2 lockdown — guard BJT/diode model upgrades
Asserts the Phase 2 BJTs include Gummel-Poon junction caps (CJC/CJE) and forward transit time (TF), and the diode upgrades include reverse-recovery time (tt) and Schottky band-gap (Eg). If anyone simplifies the models in the future, these regress fail and surface the loss of AC/transient fidelity. Also guards the dedupe identity between the canonical diode-1n4148 emission and the relay flyback diode — they must serialise as the same string or ngspice will reject the netlist for duplicate .model lines. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
c476084e00
commit
7508423c6e
|
|
@ -0,0 +1,87 @@
|
|||
/**
|
||||
* Phase 2 lockdown — verify the BJT and diode model upgrades survived
|
||||
* any later edits to componentToSpice.ts. Asserts that the upgraded
|
||||
* models include the parameters that distinguish them from the old
|
||||
* truncated SPICE3F5 placeholders.
|
||||
*
|
||||
* If these fail, someone reasonably simplified the models again — that
|
||||
* regresses AC/transient fidelity. Either re-upgrade the model or
|
||||
* remove this test consciously.
|
||||
*/
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { componentToSpice } from '../simulation/spice/componentToSpice';
|
||||
import type { PlacedComponent } from '../simulation/spice/types';
|
||||
|
||||
function netLookupStub(_pin: string): string {
|
||||
return 'net_dummy';
|
||||
}
|
||||
|
||||
function emit(metadataId: string, properties: Record<string, unknown> = {}): string[] {
|
||||
const comp: PlacedComponent = { id: 't1', metadataId, properties };
|
||||
const result = componentToSpice(comp, netLookupStub, { vcc: 5 });
|
||||
if (!result) throw new Error(`no emission for ${metadataId}`);
|
||||
return Array.from(result.modelsUsed);
|
||||
}
|
||||
|
||||
describe('Phase 2 — BJT Gummel-Poon parameters present', () => {
|
||||
it.each([
|
||||
['bjt-2n2222', 'Q2N2222'],
|
||||
['bjt-bc547', 'QBC547'],
|
||||
['bjt-2n3055', 'Q2N3055'],
|
||||
['bjt-2n3906', 'Q2N3906'],
|
||||
['bjt-bc557', 'QBC557'],
|
||||
])('%s model includes Gummel-Poon junction caps and transit times', (id, modelName) => {
|
||||
const models = emit(id);
|
||||
const m = models.find((s) => s.includes(modelName));
|
||||
expect(m, `no model card found for ${modelName}`).toBeDefined();
|
||||
const text = (m ?? '').toUpperCase();
|
||||
// Junction caps — separates Phase 2 model from the old truncated one.
|
||||
expect(text).toMatch(/CJC=/);
|
||||
expect(text).toMatch(/CJE=/);
|
||||
// Forward transit time — required for switching/AC behaviour.
|
||||
expect(text).toMatch(/TF=/);
|
||||
// Bf must still be there too (sanity).
|
||||
expect(text).toMatch(/BF=/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Phase 2 — diode reverse-recovery and junction capacitance', () => {
|
||||
it('D1N4148 includes tt (storage time) and Cjo', () => {
|
||||
const models = emit('diode-1n4148');
|
||||
const m = models.find((s) => s.includes('D1N4148'));
|
||||
expect(m).toBeDefined();
|
||||
expect(m).toMatch(/tt=/i);
|
||||
expect(m).toMatch(/Cjo=/i);
|
||||
});
|
||||
|
||||
it('Schottky 1N5817 carries Eg (Schottky band-gap) and Cjo', () => {
|
||||
const models = emit('diode-1n5817');
|
||||
const m = models.find((s) => s.includes('D1N5817'));
|
||||
expect(m).toBeDefined();
|
||||
expect(m).toMatch(/Eg=/i);
|
||||
expect(m).toMatch(/Cjo=/i);
|
||||
});
|
||||
|
||||
it('Schottky 1N5819 carries Eg and Cjo', () => {
|
||||
const models = emit('diode-1n5819');
|
||||
const m = models.find((s) => s.includes('D1N5819'));
|
||||
expect(m).toBeDefined();
|
||||
expect(m).toMatch(/Eg=/i);
|
||||
expect(m).toMatch(/Cjo=/i);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Phase 2 — relay flyback keeps the canonical D1N4148 string', () => {
|
||||
it('relay emits the same D1N4148 .model string as a standalone diode', () => {
|
||||
const standaloneModels = emit('diode-1n4148');
|
||||
const relayModels = emit('relay');
|
||||
const standalone = standaloneModels.find((s) => s.includes('D1N4148'));
|
||||
const fromRelay = relayModels.find((s) => s.includes('D1N4148'));
|
||||
expect(standalone).toBeDefined();
|
||||
expect(fromRelay).toBeDefined();
|
||||
// Must match exactly — the netlist dedupe Set collapses them by string
|
||||
// identity. If they ever diverge, ngspice will see duplicate .model
|
||||
// definitions and error out.
|
||||
expect(fromRelay).toBe(standalone);
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue