Siru en Simusol · Documentación · Manual

PLAN_UN_ARMADOR

Plan: un solo armador de ecuaciones (mdin llama a libdin)

30/09/2026. La decisión está en siruviejo/LLAVES.md 13. Esto es el plan para hacerla, escrito después de leer entero slex/din/din.c, Siru::Mdin, Siru::Red, Siru::Circuito y bin/siru. Integración y equilibrio van juntos: siru no se cambia de armador hasta que libdin sepa las dos cosas.

El preparador y el simulador

papel qué hace
el preparador prepara el modelo de un paso: de la configuración de ese paso, un din
el simulador integra, escribe la salida, y obedece RERUN, CONTINUE, SENSITIVITY, MONTE CARLO

El preparador prepara el modelo del paso exactamente antes de ejecutarlo. No se sabe de antemano cuántos din va a usar una corrida, y no se prepara ninguno que no se use: con 100 llaves hay 2^100 configuraciones y la corrida pasa por unas pocas. Antes de cada paso se mira la configuración de ese paso; si ya tiene din se usa ese, y si no, se prepara ahí.

Es una sola rutina, la misma para todos los casos, hecha de partes:

parte qué pone
ramas las ecuaciones de cada elemento, de su modelo de la biblioteca
topológicas voltajes de rama y corrientes de nodo; con lazos de C o cortes de L, la ley del lazo o del corte en derivadas
derivadas las etapas del método de Runge–Kutta y sus sumas
estado qué variables son estado, su valor inicial, y el equilibrio cuando la configuración tiene lazo o corte
propias las ecuaciones que trae el archivo cuando no hay circuito

Un sistema sin circuito usa sólo las propias, las derivadas y el estado; un circuito normal, todas menos la variante de lazos y cortes; un punto no usa derivadas. Que una corrida termine con un din o con varios no es algo que el preparador decida: sale de cuántas configuraciones distintas aparecieron.

El código de esas partes está en libdin; siru lo llama antes del paso y guarda lo ya preparado.

En este plan «armador» es el código del preparador que escribe las ecuaciones. Hoy hay dos, uno en C y uno en Perl; tiene que quedar uno.

RERUN y los otros tres no hacen que haya varios din: son del simulador (siruviejo/LLAVES.md 12).

Qué hay hoy

siru le pide a Siru::Mdin tres sistemas por configuración, y los tres los arma el Perl sin pasar por libdin:

sistema para qué quién lo arma hoy equivalente en libdin
integracion(bits, rk) un paso de Runge–Kutta Mdin::_sistema + Butcher din_ampliado — existe
instante(bits) evaluar las condiciones de las llaves al empezar el paso Mdin::_sistema + llave_x = condición din_base — existe, sin las llaves
equilibrio(bits) trasvase de carga o de flujo Mdin::equilibrio no existe

El formato de texto ya es el mismo (variables / ecuaciones, s<i>_, p_, q_). Lo que difiere es lo que sabe cada uno.

Lo que libdin no sabe y Mdin sí (hay que agregarlo a libdin):

  1. Llaves. Hoy libdin rechaza cualquier llave: valor de S1 no constante, porque el valor trae variables declaradas (V:T). Por eso los .tipo de llaves_equilibrio dicen din d2datdinc: NO.
  2. Lazos de C y cortes de L: detectarlos y poner la ley en derivadas.
  3. El sistema de equilibrio.
  4. El tiempo de cada etapa. Mdin escribe s<i>_t = t + c_i*dt. libdin deja el tiempo como parámetro, el mismo en todas las etapas: una fuente que depende de T la evalúa al comienzo del paso en vez de en t + c_i h.
  5. La derivada de una fuente en un lazo o un corte: la del modelo si la trae (DVOLTAGE), 0 si es constante, numérica si no.

Lo que Mdin no sabe y libdin sí (el camino mdin lo gana gratis): funciones del usuario, tablas, parámetros por nombre, salidas res:, SIGN, **, las marcas # @lineal, el chequeo de grados de libertad, las secciones de corrida (paso, adaptación, parada) y el aviso de PENDIENTE. Hoy un circuito con llaves que use una función o una tabla no simula.

Duplicaciones en Perl que desaparecen: el lector del .dat (Red::nuevo), los dos grafos (Red::_grafo_carga, _grafo_flujo), el orden de las ramas por expresión regular (Circuito::_orden), y el lector de las secciones del .din, que está dos veces (bin/siru y Mdin::_sin_ramas).

Dos cosas rotas que encontré al mirar, antes de empezar

La interfaz que queda

d2datdinc, dos opciones nuevas:

--llaves S1=1,S2=0     el estado de cada llave, por nombre
--topologia            no escribe nada: dice qué llaves hay y, para el
                       estado dado, qué lazos y qué cortes

Con --din <N> --llaves ... escribe en --directorio:

NOMBRE-de-d2datdinc.din          el instante: el circuito con los estados
                                 como datos, más  llave_<x> = condición
NOMBRE-de-d2datdinc-ED<N>.din    la integración
NOMBRE-de-d2datdinc-EQ.din       el equilibrio, sólo si hay lazo o corte

Todo lo que siru necesita saber de cada archivo va en secciones comentadas, como ya va la corrida: # parametros, # estados, y nuevas # llaves, # entradas, # salidas. siru deja de leer ramas.

Los nombres que ve el usuario no cambian: siru sigue escribiendo NOMBRE-<bits>.mdin y NOMBRE-<bits>E.mdin al lado de la fuente.

Los pasos

Cada paso se commitea solo. Del 1 al 6 son agregados a libdin que no cambian nada de lo que hoy funciona; siru recién cambia de armador en el 8.

0. Red de seguridad. Arreglar desde_din.t y llaves.t para que corran. Guardar en el temporal del trabajo —no en el repositorio— lo que produce hoy el Perl para los 22 archivos de caso 2 que tienen .patron y los 8 de llaves_equilibrio: los tres sistemas de cada configuración y el .simul. Es el oráculo.

1. libdin: la tabla de ramas. din_leer hoy recorre el JSON y emite texto, sin guardar nada por rama. Hace falta una tabla en struct din: nombre, clase (primera letra del tipo), los dos nodos, el índice de su ecuación v = ua - ub, el texto de su x1, su estado y su derivada; y por nodo, el índice de su ecuación de corrientes. Sin cambio de comportamiento: make check de slex tiene que dar igual.

2. libdin: las llaves. Una rama de clase S toma x1 de la configuración (1 o 0, parámetro numérico, como hace Mdin) y guarda su condición aparte. Sin configuración y con llaves, el error dice eso en vez de «valor no constante». De paso, un valor con variables declaradas (V:) deja de ser error: la expresión ya la traduce valor_expresion.

3. libdin: el tiempo de cada etapa. En din_ampliado, si el sistema usa el tiempo: s<i>_t = t + c_i*dt.

4. libdin: lazos y cortes en la integración. Los dos union-find de Red, y el reemplazo de las dos ecuaciones (Mdin::_derivadas_en_degeneraciones): la de voltaje de la rama que cierra el lazo, y la de nodo del supernodo hijo. Con la derivada de las fuentes. La cuenta de grados de libertad no cambia: se reemplaza una por una.

5. libdin: din_equilibrio. El sistema sin DT: potenciales w en la red de capacitores y baterías, potenciales de flujo phi en los supernodos, conservación por nodo. Es Mdin::equilibrio pasado a C.

6. libdin y d2datdinc: el instante, las secciones y las opciones. din_base más llave_<x> = condición; las secciones nuevas; --llaves y --topologia.

7. Paridad, antes de tocar siru. Para cada archivo del oráculo y cada configuración que usó: el sistema de libdin contra el del Perl. No por texto —cambian el orden y las marcas— sino por cantidad de variables y ecuaciones, y resolviendo los dos con los mismos datos. Lo que no coincida se arregla en libdin o se anota acá con el motivo.

8. siru cambia de armador, y queda un solo preparador. Mdin.pm queda en lo que es, la parte del preparador que está en siru: antes de cada paso, si la configuración de ese paso no tiene din, pedírselo a d2datdinc y leer sus secciones; si ya lo tiene, usarlo. Es el mismo para todos los casos, y nunca prepara de antemano. Hoy son dos caminos separados en bin/siru (din_directo y Circuito::correr). Qué llaves hay lo pregunta con --topologia. Un solo lector de secciones del .din (Siru::Din), usado por bin/siru y por Mdin. Y un solo escritor de la salida: UnPunto pasa a usar el de Integrador.

9. Borrar el armador en Perl. De Mdin.pm: _sistema, _derivadas_en_degeneraciones, _camino, _fuente, _derivada_fuente, equilibrio, condiciones, instante, _texto, _numero_rk, _sin_ramas. Red.pm entero. Circuito::_orden. Quedan unas 80 líneas de las 740.

10. Verificar y decir qué anda. prove en siru/tests, make check en slex, y los 53 archivos de caso 2 contra el oráculo, en el temporal. El informe es una tabla por archivo: igual, distinto (con cuánto), o no corre (con el mensaje). Los 31 de caso 2 que hoy no tienen .patron se informan aparte: hoy no andan, y no prometo que anden después.

11. Documentar. LLAVES.md 12 y 13, la cabecera de din.h (que todavía dice «ramas RB, LB, CB y EB con valor constante»), y los README.

Lo que cambia para afuera, y conviene saberlo antes

  1. Los números del camino din cambian donde hay dependencia del tiempo. Es el punto 3: hoy libdin evalúa el tiempo al comienzo del paso. Con el tiempo de etapa el resultado es más exacto, pero los .simul y .patron de los casos 1 y 3 que usan T van a dar otros números. Es necesario: sin eso el camino mdin empeora respecto de hoy.
  2. El orden de las columnas en el camino mdin pasa a ser alfabético, por nombre de rama, que es como ordena libdin (el .dat no trae ordenramas). Hoy es el orden del archivo fuente. Los valores son los mismos; cambia el orden de las columnas del .simul y, con varias llaves, el orden de los dígitos en NOMBRE-<bits>.mdin. Queda igual que el camino din.
  3. libdin es más estricto que Mdin: verifica grados de libertad y que todos los nodos lleguen a GND. Puede aparecer algún circuito que hoy corre por mdin y que libdin rechace; sale en el paso 7.

Lo que queda afuera

Cuidado con el árbol compartido

Hay otra sesión commiteando en siru/bin/siru y siru/corpus/clasificar (r1640 a r1648). Antes de cada edición de din.c o de bin/siru: svn status y svn diff del archivo; sacar un cambio propio es sacar esas líneas, nunca revertir el archivo.

Bitácora de la ejecución

Paso 0, hecho (r1655)

desde_din.t y llaves.t corren y pasan: 173 pruebas en los cinco archivos.

El oráculo está en el temporal del trabajo: una copia congelada de siru y de d2datdinc en r1655, y lo que dan para 22 archivos de caso 2 (los que tienen .patron; incluye los 8 de llaves_equilibrio). Los 22 corren.

Hallazgo: el camino din hoy rechaza casi todo lo que el Perl dejaba pasar. De los 18 archivos de casos 1, 3 y 4 que tienen un .simul en el corpus, hoy corren 3. Los otros 15 mueren con «El .din no tiene sección estados», porque libdin les encuentra algo sin traducir (# PENDIENTE) y entonces no escribe la corrida. Esos .simul son de cuando todo pasaba por el armador en Perl, que lo que no entiende lo ignora sin avisar.

Lo que libdin marca como pendiente en los 40 archivos mirados, y el Perl ignora:

qué cuántas veces qué es
condicion global: {} 7 una condición vacía: no hay nada que traducir
T<=10, T<=2, T<=7 10 la parada, con <= y un número; libdin sólo entiende T<TIEMPOFINAL y T<número
TICK<=1000 9 parada por cantidad de pasos
GLOBAL.constants: {"DT":"0.01"} 10 el paso, a la manera de Simusol
GLOBAL.variables en un circuito con ramas 11
ecuaciones escritas a mano (IL1+IL4-I3, DIL1-V1/1 * DT) 5 un circuito armado a mano, con el Euler escrito

Esto es el punto 3 de «lo que cambia para afuera», y es más grande de lo que escribí: al pasar a un solo armador, los 22 que hoy corren por mdin van a chocar con lo mismo. No es que el Perl los simule bien: los corre con paso 0.1 ignorando el DT y la parada que dice el archivo. Se resuelve en el paso 7, caso por caso; los dos primeros renglones son arreglos claros de libdin, y DT y TICK necesitan que se decida qué quieren decir.

Pasos 1 a 6, hechos: libdin (r1657 a r1663)

paso qué revisión
1 y 2 la tabla de ramas en struct din; las llaves (din_leer_con, --llaves) r1657
3 el tiempo de cada etapa, (t + c_i*dt) escrito en el lugar r1658
4 lazos de C y cortes de L: los dos grafos y la ley en derivadas r1659
5 y 6 din_equilibrio, din_instante, din_topologia; --topologia; las secciones r1660
una condición vacía no queda pendiente; la parada acepta T<=número r1661
T es el tiempo también como valor entero de un parámetro r1662
d2datdinc acepta un .dat como entrada r1663
dos mensajes de error que decían otra cosa r1666

slex/d2datdinc/probar pasó de 27 a 41 pruebas, todas bien.

Tres cosas que no estaban en el plan y aparecieron:

Y una que no toqué: hay un tercer detector de lazos y cortes, slex/degen (libdegen), que d2datdinc --din usa para escribir un NOMBRE-de-d2datdinc-madre.din. Nadie lo lee: siru no lo usa. Clasifica por lista de tipos (CB, EB, ESIN...) y no por la primera letra, así que un tipo propio como EV o LNL se le escapa. Queda como está.

Paso 7, hecho: la paridad

Contra el armador en Perl congelado en r1655, por nombre de columna. «Igual» es diferencia relativa menor que 1e-9; la peor fue 6e-16.

Los 22 de caso 2 con .patron:

cuántos
iguales tal cual 9 los 8 de llaves_equilibrio y circuitoORI
no corren por libdin 13 tienen cosas sin traducir (abajo)
esos 13, sobre una copia sin esas líneas 13 iguales

O sea las ecuaciones que arma libdin dan lo mismo que las del Perl en los 22. Lo que cambia es que 13 ya no corren, y es por lo que sigue.

Pasos 8 y 9, hechos: siru (r1664, r1665)

Lo que difiere del plan: la parte del preparador que está en siru no quedó en Mdin.pm sino en un módulo nuevo, Siru::Preparador, y Mdin.pm se borró entero junto con Red.pm. Y aparecieron dos decisiones chicas:

Paso 10: qué anda y qué no

prove en siru/tests: 173 pruebas bien. make check en slex: probar bien (41); compara-dat y compara-din fallan, como antes de empezar. compara-dat da lo mismo que antes (139 iguales, 55 distintos). Ya no fallan (08/10/2026): lo atrasado eran los congelados, no el código, y se rehicieron con siru/corpus/regenera (r2607). Las dos dan todo igual y el make check de slex pasa entero, 4/4 y 6/6. compara-din compara contra los .din congelados del corpus y ahora encuentra 72 archivos más distintos: miradas las 467 líneas de diferencia, son todas de lo que cambió a propósito —un PENDIENTE que ya no está, la cabecera de lazos y cortes, la ley en derivadas, el tiempo de etapa, la sección de corrida que aparece al irse el pendiente—. Los congelados están desactualizados; no los regeneré.

Lo que corría y ya no corre: 15 archivos. Los 13 de arriba y dos sin .patron (exprtk_varios_exprtkasades2015/circuito-T y circuito-T2). En todos es lo mismo: el archivo dice cosas que libdin no sabe traducir, el Perl las ignoraba en silencio y corría igual, y ahora siru se para y las nombra.

qué dice el archivo en cuántos de los 15
VARIABLES Z=0 y EQUATIONS Z+T+TICK+DTICK 9 una ecuación propia adentro de un circuito
TICK>1000 como parada 7 con TICK=0, DTICK=1 en DEFINED PARAMETERS
CONSTANTS DT=0.01 6 el paso, a la manera de Simusol

No es que el Perl los simulara bien: corría con paso 0.1 hasta T=1, sin mirar el DT ni la parada. Hace falta decidir qué quieren decir DT, TICK y DTICK, y si un circuito puede traer ecuaciones propias; con eso libdin los traduce y vuelven a correr.

Lo que no corría y ahora corre: 4 archivos. Simul_varios_Siru_perl_examples/circuitoCSLO4, sceptresimusol_share_sceptresimusol_examples/a06a, Simul_varios_Siru_perl_examples/circuitoBS y Simul_varios_Siru_perl_examples_otros/6elementos.

El resto, que no corría ni corre:

cuántos por qué
caso 2 sin .patron 27 de 31 cosas sin traducir (7); en el equilibrio, una capacidad o una fuente que depende de una variable del circuito (7); funciones o símbolos que no están (5); modelo con DT adentro (2); y otros (6: nodos inválidos, un valor complejo, dos valores donde el modelo pide uno, un capacitor sin la carga como estado)
casos 1, 3 y 4 con .simul 13 de 18 cosas sin traducir, las mismas de arriba (12); RK=[-1] (1)

Lo que mejoró sin buscarlo. stiffloli.ds con RK=[18] daba en T=1 y1 = 0.84133608; ahora da 0.84147098480, y sen(1) = 0.84147098481. El error bajó de 1.3e-4 a 3e-11: es el tiempo de etapa del paso 3.

Lo que queda

  1. Decidir DT, TICK, DTICK y las ecuaciones propias en un circuito (arriba). Es lo que devuelve los 15.
  2. Los .din, .simul y .patron congelados del corpus están desactualizados respecto de este libdin. No se regeneraron.
  3. slex/degen y el -madre.din, que nadie lee.
  4. siru/corpus/clasificar tiene su propia detección de topología.
  5. El oráculo —siru y d2datdinc congelados en r1655, y sus salidas— está en el temporal de esta sesión y se borra con ella.

Después del plan: los 15 que habían dejado de correr (r1669 a r1671)

No hacía falta decidir qué «significan» DT y TICK: alcanza con las ecuaciones, más saber que el archivo los recibe del simulador. Cuatro cosas en libdin y siru (r1669, r1670), escritas en siruviejo/LLAVES.md 13:

  1. un circuito puede traer ecuaciones propias;
  2. DT es el paso que usa el simulador, no lo fija el archivo;
  3. TICK es la cantidad de pasos dados, y la pone el simulador;
  4. una parada sobre otra variable (TICK>1000) se evalúa al empezar el paso.

Y en el corpus (r1671): se sacó DT= de los 53 archivos que lo declaraban sin usarlo, y DTICK de todos lados. Son 58 archivos.

Cómo quedó, con el árbol en r1671:

corrían con el Perl corren ahora
caso 2 con .patron (22) 22 22, y dan lo mismo en las filas que comparten
caso 2 sin .patron (31) 2 5
casos 1, 3 y 4 con .simul (18) 3 15

Los 15 que habían dejado de correr, corren. Y ahora llegan hasta la parada que dice el archivo (T>10: 101 filas) en vez de hasta T=1, que era lo que hacía el Perl.

prove: 182 pruebas bien (tests/propias.t es nueva). probar: 48 bien.

Los 29 que no corren, por qué:

por qué cuántos
en el equilibrio, un capacitor cuya capacidad depende de su voltaje (a02 y sus variantes) 6
Calcula no encuentra solución: jacobiano singular (circuito-S, -SE, -SE1, -SERRORRARO, -T3). Con el Perl daba lo mismo 5
una función o un símbolo que no está en el dato (a13, a13plot, g02), o un parámetro que no es un número (a13z, a13zcorto) 5
un modelo de la biblioteca vieja (11elem.ds, cutloop.ds, slex_viejos/circuitoCSLO) 3
un valor con dos partes donde el modelo pide una (circuitoCSLO, circuitoCSLO2) 2
nodos inválidos (circuitoCSLOCLO, circuito-SE2) 2
no cierran los grados de libertad con sus ecuaciones propias (engrane, girator) 2
la ecuación propia nombra una Z que no está en VARIABLES (circuitoBX, circuitoBloli) 2
un valor complejo, (1,0) (d01) 1
RK=[-1] (circuitoBBBB) 1

Para tener presente: