velxio/test/test_circuit/autosearch/06_ngspice_convergence.md

102 lines
5.1 KiB
Markdown
Raw Normal View History

feat: expand SPICE component catalog (fases 9 + 10) Adds 44 SPICE mappers, 58 custom metadata entries, and 12 visual Web Components covering logic gates, transistors, op-amps, regulators, sources, electromechanical parts and integrated-circuit packaging. Fase 9 — component catalog expansion ------------------------------------ - 7 logic gates (AND/OR/NAND/NOR/XOR/XNOR + NOT) as SPICE B-sources - 8 multi-input gates (AND/OR/NAND/NOR with 3 and 4 inputs) - 9 transistors: 5 BJTs (incl. PNP 2N3906/BC557) + 4 MOSFETs (incl. P-channel IRF9540/FQP27P06). NMOS refactored from Level=3 W=0.1 (hangs ngspice) to Level=1 with sane W/L - 5 op-amps: LM358, LM741, TL072, LM324 with per-chip saturation rails + opamp-ideal - 4 linear regulators (7805, 7812, 7905, LM317) with dropout - 3 batteries (9V, AA, coin-cell) with realistic ESR - Signal generator (sine / square / DC) - 2 Schottky diodes (1N5817, 1N5819) + photodiode (lux-driven current source) Fase 10 — electromechanical + ICs --------------------------------- - Relay (SPDT): coil + L + S-switch with native hysteresis + flyback diode, inverted-control trick for the NC contact - Optocouplers 4N25 and PC817 (LED + CCCS with CTR=0.5 / 1.0) - 7 74HC ICs as DIP-14 packages emitting 4 or 6 B-sources per component (first mapper pattern emitting multiple device cards) - 3 flip-flops (D, T, JK) — digital-sim only (edge detection is not representable in ngspice .op) - L293D dual H-bridge motor driver Infrastructure -------------- - scripts/component-overrides.json gains a _customComponents[] array that lets new Velxio-only parts survive metadata regeneration (previously applyOverrides() could only patch wokwi-elements components that had already been scanned) - scripts/generate-component-metadata.ts injects custom entries before the patch loop - New ComponentCategory values: 'logic', 'analog', 'electromech' - frontend/src/components/DynamicComponent.tsx PASSIVE tracing extended from just ['resistor','resistor-us'] to 9 two-terminal passives with per-part pin name maps - New CI workflow test-circuit.yml runs the sandbox on push/PR - frontend-tests.yml regenerates metadata and fails if committed JSON is stale - Documented 2 new ngspice gotchas in circuit-emulation-gotchas.md: unicode in netlist titles silently hangs the parser, and MOSFET Level=3 + W=0.1m causes .op to hang - 164/164 sandbox tests passing in ~9 s (was 88 pre-fase-9) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 06:44:18 +07:00
# ngspice — hallazgos de convergencia y gotchas (sesión 2026-04-15)
Documenta problemas numéricos observados al poblar el catálogo de componentes en fase 9. Complementa [`04_ngspice_findings.md`](04_ngspice_findings.md).
## 1. Caracteres no-ASCII en el título del netlist cuelgan ngspice
**Síntoma:** `runNetlist()` no resuelve ni falla — la promesa queda pendiente hasta que vitest la mata por timeout (30 s).
**Reproducción:**
```spice
3.3V GPIO → 2N7000 → 5V load
V_sys vsys 0 DC 5
Vgpio gate 0 DC 3.3
RL vsys drain 1k
M1 drain gate 0 0 M2N7000 L=2u W=200u
.model M2N7000 NMOS(Level=1 Vto=1.6 Kp=50u Lambda=0.01)
.op
.end
```
El `→` (U+2192) en la primera línea (título) es lo único que rompe el parser. Reemplazarlo por `to` o `->` hace que converja instantáneamente.
**Impacto:** silencioso — ngspice no emite error, no loggea nada, solo queda colgado. Un test que use unicode "decorativo" en el título parece un test roto a nivel numérico cuando en realidad es un problema de encoding.
**Mitigación:** regla de estilo — **solo ASCII en los títulos** `<header>` de los netlists. Los comentarios (`* ...`) y strings en B-sources (`V = ...`) sí aceptan unicode.
Añadir a `docs/wiki/circuit-emulation-gotchas.md`.
## 2. MOSFET Level=3 con W desmesurado causa hangs en `.op`
**Síntoma:** mismo que arriba — timeout sin error — pero por razones numéricas.
**Modelo problemático:**
```spice
.model M2N7000 NMOS(Level=3 Vto=1.6 Kp=0.1 Rd=1 Rs=0.5)
M1 drain gate 0 0 M2N7000 L=2u W=0.1
```
**Análisis:** `W=0.1` sin unidad en ngspice se interpreta como **0.1 metros** (100 mm de ancho de canal). Combinado con `L=2u` (= 2 µm) da un W/L absurdamente grande (50 000). Con `Kp=0.1 A/V²` y `V_ov=1.7 V`, la corriente teórica en saturación es de kiloamperios. Newton no converge; en algunas combinaciones de Vds bajo la resolución directa devuelve un resultado "por suerte" (los tests antiguos pasaban porque el drain estaba shunteado a 0 V y la corriente se limitaba por el resistor externo).
**Mitigación adoptada en fase 9.2:** migrar a `Level=1` (Shichman-Hodges) con W/L físicamente razonable:
```spice
.model M2N7000 NMOS(Level=1 Vto=1.6 Kp=50u Lambda=0.01) ; W=200u L=2u
.model MIRF540 NMOS(Level=1 Vto=3 Kp=20u Lambda=0.01) ; W=2m L=2u (power)
.model MIRF9540 PMOS(Level=1 Vto=-3 Kp=20u Lambda=0.01) ; W=2m L=2u
.model MFQP27P06 PMOS(Level=1 Vto=-2.5 Kp=50u Lambda=0.01) ; W=500u L=2u
```
Level-1 es menos preciso (sin short-channel effects, sin body-effect moderno) pero es **numéricamente robusto** y basta para circuitos didácticos: switches, drivers, inversores lógicos.
## 3. Salidas flotantes del B-source hacen matrix singular
**Síntoma:** ngspice logga "matrix is singular" (no cuelga — falla rápido).
**Reproducción:** una compuerta lógica cuya salida Y no esté conectada a ningún otro elemento:
```spice
Va a 0 DC 5
Vb b 0 DC 5
Bg1 y 0 V = 5 * u(V(a)-2.5) * u(V(b)-2.5)
.op
```
**Causa:** la B-source define `V(y) = f(...)` pero ngspice aún necesita ver al menos un elemento conectado a Y (caso contrario la ecuación KCL en Y es `0 = 0` → rango deficiente).
**Mitigación adoptada:** cada mapper de gate emite también una resistencia de pull-down de 1 MΩ:
```
R_${id}_load y 0 1Meg
```
1 MΩ es suficientemente alto como para no afectar la tensión en Y (la B-source la impone con impedancia cero) y suficientemente bajo como para que ngspice tenga camino DC.
## 4. Función `tanh()` puede o no estar disponible según build de ngspice-wasm
**Síntoma:** el test del Schmitt trigger con `V = 10 * tanh(1e5*V(p))` converge pero el output queda estático en cualquier rail — no refleja la entrada.
**Mitigación:** usar directamente la función step `u()` disponible en todos los builds:
```
V = 20 * u(V(p)) - 10 ; == +10 si V(p)>0, -10 si no
```
Evitamos `tanh`, `sin`, funciones hiperbólicas salvo que una prueba standalone confirme soporte.
## 5. Valores absolutos de Is para LEDs ajustados al Vf empíricamente
En `componentToSpice.ts` existen modelos LED_RED/LED_GREEN/etc. con `Is=1e-20 … 1e-28` y N=1.7..2.0. Estos valores **son correctos para dar Vf realista** a 10 mA (datasheets típicos), pero están al límite de la precisión float64: en `.op` con drive agresivo (p.ej. 5V directo con 40Ω de serie) el Newton puede cebar con `V_d > 5V``exp(V_d/(N·VT))` overflow → cuelgue.
**Mitigación:**
- Siempre incluir `Rs>0` en el modelo (limita `exp()` vía `V_d → V_d - I·Rs`).
- Alternativamente, subir Is a `1e-14` (diodo estándar) perdiendo tuning de Vf exacto — aceptable en sistemas donde "encenderse / no encenderse" importa más que el Vf medido.
## 6. Checklist de un mapper nuevo
Antes de fusionar un mapper SPICE:
- [ ] Título del netlist **solo ASCII**.
- [ ] Salida del componente tiene camino DC a 0 (resistencia pull-down, otro elemento, o canonicalización a `0`/`vcc_rail`).
- [ ] MOSFETs usan `Level=1` con W/L físicamente razonable (`W ≤ 10 mm`, `W/L` entre 10 y 10 000).
- [ ] Modelo no-lineal tiene `Rs>0` y `Rc>0` si aplica.
- [ ] `.op` converge en < 1 s en una topología mínima (V-source + resistencia + DUT).
- [ ] Truth table / behavior verificado con ngspice real en `test/test_circuit/test/`.