# Manual de uso de siru

Referencia central del lenguaje: [Gramática .ds y .d](../../slex/d2datdinc/doc/GRAMATICA.md), con modelos, ecuaciones, integradores, FORMAS, PARAPLOT y ejemplos descargables. [Versión web](https://www.simusol.org/?Gram%C3%A1tica-.ds).

Cómo describir un circuito —o un sistema de ecuaciones— en un archivo `.d` o
`.ds`, y cómo correrlo con `siru`. Cubre todo lo que hoy se puede hacer;
lo que no se puede está al final. El porqué de cada cosa está en
`siruviejo/LLAVES.md`, `slex/din/README.md` y `slex/d2datdinc/README.md`; lo
hecho y lo que falta, en [HECHO.md](HECHO.md).

**El `.ds` es la descripción completa del problema que se entrega a Siru:** reúne las ecuaciones, las incógnitas y sus semillas, los datos conocidos, las constantes, las funciones y, cuando corresponde, las ramas y sus modelos. Siru construye con esa información el sistema de Calcula; el usuario no necesita escribir otro archivo de ecuaciones para Calcula. Si todo está definido dentro del `.ds`, el archivo es autosuficiente. Si usa `include`, tablas externas o funciones Fortran, esos archivos referenciados también forman parte del problema y deben acompañarlo.

**Con `RK=[-1]` en `RUN_CONTROLS`, Siru entrega el sistema a Calcula, le pide una sola evaluación y escribe los resultados en `.simul`.** No hay integración temporal, etapas Runge–Kutta ni avance de estados. Las iteraciones internas que Calcula necesite para resolver el sistema siguen siendo tarea de Calcula. Véanse la [gramática de `.ds`](#31-estructura-y-reglas-de-escritura), la sección [FORMAS](#38-formas-el-dibujo-dentro-del-ds) y el [modo de un punto](#12-un-punto-sin-integración-rk-1).

## 1. Qué hace falta

El paquete opcional `dibuja` genera esquemas SVG cuando existe la sección `FORMAS`, incluso vacía. Para leer esa sección desde `.ds` hace falta `d2datdinc` 1.0-26 o posterior. El dibujante también funciona por separado, sin Siru ni Calcula; véanse [FORMAS](#38-formas-el-dibujo-dentro-del-ds) y [su manual de instalación y uso](../../dibuja/LEEME.md).

Para dibujar los resultados, instalar también **Simplot** (con la opción `--stdin`). Siru lo llama al final de una ejecución correcta, incluyendo un solo punto, entrada `.din` y `--continuar`. `siru -nosimplot archivo.ds` o `siru --nosimplot archivo.din` ejecuta solamente el cálculo. Si falta Simplot o falla el gráfico, Siru avisa sin invalidar los resultados. Los mensajes del graficador quedan en el `.log` de la corrida y los gráficos en `NOMBRE-simplot/`, junto a su tabla de salida. Con pantalla usa el comportamiento habitual de Simplot; sin DISPLAY ni WAYLAND_DISPLAY produce archivos sin ventanas. No llama al graficador si la simulación falla o si solo se solicita ayuda.

Las instrucciones RESULTADOS/PARAPLOT se leen de la fuente `.d`, `.ds` o `.dat`, vinculadas a la tabla recién escrita, aunque tenga un sufijo numérico o se haya indicado `--salida`. Las fuentes externas de una comparación se conservan. Para un `.din` independiente, sin descripción gráfica, Simplot usa la primera columna como abscisa. Siru envía la tabla recién escrita y la descripción gráfica por la entrada estándar de Simplot, también cuando el resultado es CSV; el archivo original se conserva. Las limitaciones del propio Simplot siguen vigentes: por ejemplo, un modelo gráfico v2 con varias corridas debe seleccionar el bloque de su fuente explícitamente.

Hay dos maneras de tenerlo.

**Instalado.** El paquete `siru` trae `/usr/bin/siru`, su página de manual
(`man siru`) y este manual, en `/usr/share/doc/siru`. Depende de los paquetes
`d2datdinc` (Slex, 1.0-27 o posterior), `calcula` (6.0-37 o posterior) y `tabular` (2.0-17 o posterior, incluido `preparar-resultados`). De Perl usa sólo módulos de la distribución base; el preparador de Tabular usa Python 3. Hay dos maneras de instalarlo:

- `sudo ~/svn/siru/instalar` arma e instala todos los paquetes de este
  repositorio en orden, siru al final. `calcula` es de otro repositorio y se
  instala con el suyo (`sudo ~/svn/calcula/instalar`): si todavía no está,
  `instalar` no falla, deja a siru armado y dice las dos órdenes que faltan.
- A mano: `siru/hacer-pkg` deja el `.deb` al lado, y se instala con
  `sudo dpkg -i` de ese archivo, con `d2datdinc` y `calcula` ya instalados.

`~/svn/siru/instalar --que-hay` dice qué hay instalado y en qué versión.

**Desde el árbol de fuentes**, sin instalar siru:

- `calcula` (`~/svn/calcula/calcula-6.0`, `make` en `src`), 6.0-26 o
  posterior. Instalado, `siru` lo toma del `PATH`; desde el árbol de
  fuentes se le indica `--calcula` y `--bibliotecas`.
- `slex` compilado (`~/svn/siru/slex`, `make`): `siru` usa
  `slex/d2datdinc/d2datdinc` y su biblioteca `slex/d2datdinc/biblioteca`.
- Perl 5 con `JSON::PP` (viene con Perl).

`siru` sabe de cuál de las dos maneras corre por dónde está (`Siru::Donde`):
instalado usa el `d2datdinc` del `PATH` y los catálogos de su biblioteca
(`/usr/share/slex/biblioteca`); desde el árbol, los de `slex/d2datdinc`.

## 2. Correr

Instalado, `siru circuito.d`. Desde el árbol:

```sh
cd ~/svn/siru/siru
perl bin/siru circuito.d
perl bin/siru circuito.ds
perl bin/siru --salida resultado.csv circuito.d
perl bin/siru --calcula ~/svn/calcula/calcula-6.0/src/calcula \
                --bibliotecas ~/svn/calcula/calcula-6.0/src/.libs circuito.d
```

También acepta un `.dat` (el JSON de Siru) y un `.din` ampliado ya generado.
`--plazo SEGUNDOS` es cuánto se espera cada respuesta de Calcula (un paso, un
punto): 30 s por omisión en una corrida, 10 s en un punto; un circuito que
Newton tarda en resolver necesita más.
`--paso-factor F` corre con el paso multiplicado por F; con paso adaptivo,
con las tolerancias por F elevado al orden. Sirve para ver si el resultado
cambia al achicar el paso (`siru/corpus/convergencia`).

**Qué deja**, al lado del archivo:

| archivo | qué es |
|---|---|
| `NOMBRE.simul` | la salida, en el formato de Simusol: una columna por cada nombre de `OUTPUTS` (sin `OUTPUTS`, los estados), una fila por paso. Con `--salida X.csv`, un CSV. Si ya existe no se pisa: la nueva es `NOMBRE-1.simul`, después `-2`... |
| `NOMBRE.informe` | el informe: `resultado:` (llega al final, para en una condición, no arranca…), hasta dónde llegó y el error si lo hubo |
| `NOMBRE.log` | todo lo que `d2datdinc` y `calcula` dijeron por su salida de errores, cada línea con quién la dijo, y al final el resultado. Es donde se busca el motivo de una falla (sección 13) |
| `NOMBRE[-bits][-rkN].mdin`, `NOMBRE-[bits]I.mdin`, `NOMBRE-[bits]E.mdin`, `NOMBRE-[bits]D.mdin` | sólo si el circuito tiene llaves, lazos de capacitores, cortes de bobinas o varios métodos: los sistemas de cada configuración (integración, instante, equilibrio, punto de operación DC). Sirven para mirar qué ecuaciones se resolvieron. |

Los `.din` de un circuito común no quedan: viven en un temporal. Para
verlos, `d2datdinc --din circuito.d` los escribe al lado
(`NOMBRE-de-d2datdinc.din` y `-ED<N>.din`); desde el árbol de fuentes hay que
darle `-c slex/d2datdinc/d2datdinc.slex -b slex/d2datdinc/biblioteca`.

`siru` no simula un archivo que tenga algo que no sabe traducir: lo dice
(`PENDIENTE`) y termina. Mejor que simular un modelo a medias.

## 3. `.d` o `.ds`: cuál usar

Los dos describen lo mismo con las mismas secciones y las mismas líneas de
elementos; el lector es uno solo (`d2datdinc`) y el sistema que sale es el
mismo. Difieren en lo que sigue.

| | `.d` (SCEPTRE) | `.ds` (Siru) |
|---|---|---|
| de dónde viene | lo que Simusol genera para SCEPTRE; el manual de SCEPTRE | el formato propio de Siru |
| tipo de rama | por la primera letra del nombre: `R1` es `RB`, `C3` es `CB`, `E1` es `EB`, `J1` es `JB`, `L2` es `LB`, `S1` es `SB`; un tipo propio que sea prefijo del nombre (`ESIN1`, `MW1`) | explícito, con guión: `RB-R1`, `CB-C3`, `SB-S1` |
| biblioteca de elementos | se incluye sola | hay que escribir `include .../libraryori.siru` (ruta relativa al archivo) |
| comentarios | `#`, y `;` | `#`; `;` separa sentencias |
| secciones | con espacio: `RUN CONTROLS` | con espacio o con `_`: `RUN_CONTROLS` |
| fin de la corrida | `STOP TIME = 5` o `T>5` en `RUN CONTROLS` | `TIEMPOFINAL=5` en `DEFINED_PARAMETERS` y `T<TIEMPOFINAL` en `CONTROL_EQUATIONS` |
| método de integración | `INTEGRATION ROUTINE = TRAP` (12), `IMPLICIT` o `XPO` (13), `RUK` (7); sin ella, 13. `RK=[...]` no se entiende en un `.d` | `RK=[12]` en `RUN_CONTROLS`, un índice definido en el catálogo; sin ella, Euler (0) |
| paso | `STARTING STEP SIZE = 0.05` | `STARTING STEP SIZE = 0.05` (desde r1890; también `MINIMUM` y `MAXIMUM`), o `SIRU_PASO=[0.05]` |
| semilla de calcula | `SEMILLA VL2 = 0.1` | `SEMILLA VL2 = 0.1`, o `VL2=0.1` en `VARIABLES` si es de una rama |
| condiciones iniciales | `INITIAL CONDITIONS` (o `DEFINED PARAMETERS`) | `DEFINED_PARAMETERS` |
| modelos de SCEPTRE (`MODEL 101M (B-C-E)`, transistores de `modellib.siru`) | sí | no |
| tablas adentro del archivo (`TABLE 1`) | sí | sí (desde r1950), o por archivo aparte, `Table('tabla.txt',...)` |
| incluir otro archivo | SCEPTRE no tiene `include`, pero siru lo acepta: `include parte.d` mete otro `.d`, que se lee como `.d` (desde r2066); una biblioteca (`.siru`, `.ds`) se lee como `.ds`. Un `.d` con `include` ya no corre en SCEPTRE | `include otro.ds` (o `input otro.ds`): el otro archivo entra en ese lugar, con su ruta relativa al que lo incluye o al directorio actual; sirve para la biblioteca y para partes de un circuito |
| cadenas entre comillas | no | sí (`Table('...')`) |

**Cuál usar:** `.ds` para lo nuevo —dice el tipo de cada rama, no lo
adivina por la letra, y es el formato de Siru—; `.d` para correr lo que
viene de SCEPTRE o de Simusol, o lo que usa sus modelos de transistor y sus
tablas incorporadas. Lo que sigue muestra las dos formas donde difieren.

### 3.1 Estructura y reglas de escritura

La siguiente referencia describe el formato `.ds` que lee `d2datdinc`, el lector basado en slex. Las secciones explican qué significa cada sentencia; una misma expresión escrita bajo otro encabezado puede significar otra cosa. El recorrido es:

```text
problema.ds → d2datdinc → dato del problema (.dat)
                              ├─ sistema (.din) → Calcula → resultados (.simul)
                              └─ FORMAS + ramas → dibuja → esquema (.svg)
```

Siru coordina ese recorrido con `siru problema.ds`. Los archivos intermedios pueden ser temporales: no hace falta prepararlos a mano. `FORMAS` aporta información de dibujo; las ecuaciones se construyen con la descripción del sistema, sin depender de que exista el dibujo.

| Regla | Escritura y significado |
|---|---|
| Encabezado | En una línea propia: `DEFINED_PARAMETERS` o `DEFINED PARAMETERS`. En encabezados se admiten mayúsculas/minúsculas, espacios, tabuladores y `_` entre palabras. Puede seguir un comentario. |
| Cuerpo | Desde el encabezado hasta el siguiente encabezado o `END`. Se pueden omitir las secciones que no se necesitan y volver a abrir una sección; esto no permite redefinir arbitrariamente los mismos nombres. |
| Fin | `END`, en mayúsculas, termina la fuente actual. También se acepta el fin del archivo. En una biblioteca incluida, su `END` permite volver al archivo que la incluyó. El texto posterior a `END` no es parte de ese problema. |
| Sentencias | Una por línea, o varias separadas por `;`. En `.ds`, `;` **no** introduce un comentario. |
| Continuación | Una barra inversa `\` al final une la línea siguiente. Terminar en `+`, `,` o `=` sin `\` no pide continuación en `.ds`. |
| Comentarios | `#` o `//` hasta fin de línea; también `/* ... */`. Dentro de cadenas entre comillas no comienzan comentarios. |
| Inclusión | `include ruta` o `input ruta`; se acepta `#include ruta` por compatibilidad. La ruta va sin espacios ni comillas y se busca relativa al archivo que incluye o al directorio de trabajo. La sección activa puede continuar a través de la inclusión: conviene escribir encabezados explícitos al volver. |
| Nombres | Para parámetros e incógnitas: letra seguida de letras, dígitos o `_`, por ejemplo `TEMP`, `Y1`, `P_T`. Para tipo, rama y nodo se aceptan letras, dígitos y `_`, incluso un nodo numérico. No usar blancos dentro del nombre. |
| Mayúsculas | La tolerancia de los encabezados no se extiende a todas las claves. Escribir `RK`, `SIRU_PASO`, `V:` y `D:` como se documentan y mantener la misma escritura de los nombres. Los nombres del sistema generado pueden aparecer en minúsculas. |
| Números | Decimal con punto, signo opcional y exponente `e`/`E`: `12`, `-0.5`, `.001`, `2e-6`. La coma separa campos; no es separador decimal. |
| Cantidades con unidad | En expresiones: número, blanco y unidad GNU units, por ejemplo `10 kohm`, `100 uF`, `25 tempC`. El lector convierte la cantidad a SI. `10k` no es esta sintaxis; `C` es coulomb, `tempC` es Celsius absoluto. `CONSTANTS` y `DATA` tienen sus propias reglas indicadas más abajo. |
| Expresiones | Operadores explícitos: `+ - * / ^`, paréntesis, funciones y condiciones de ExprTk. Ejemplos: `2*(X+1)`, `sin(T)`, `if(T<1,0,1)`. No hay multiplicación implícita. Las comillas simples o dobles delimitan cadenas, como las rutas de `Table(...)`. |

### 3.2 Secciones del archivo

| Sección | Contenido | Desarrollo |
|---|---|---|
| `CIRCUIT_DESCRIPTION` | Texto que describe el problema. | §4 |
| `ELEMENT_DESCRIPTION`, alias `ELEMENT_DEFINITION` | Tipos de rama y sus ecuaciones; también pueden llegar mediante `include`. | §3.3 y §5 |
| `ELEMENTS` | Ramas, nodos, valores y acoplamientos. Se omite en un sistema de ecuaciones sin circuito. | §3.3, §5–8 |
| `CONSTANTS` | Constantes numéricas con nombre. | §3.4 |
| `DEFINED_PARAMETERS` | Datos conocidos, expresiones auxiliares y valores iniciales de estados. | §3.4 y §10 |
| `INITIAL_CONDITIONS` | Encabezado de compatibilidad con SCEPTRE. En un `.ds` nuevo escribir las condiciones en `DEFINED_PARAMETERS`. | §3 y §10 |
| `VARIABLES` | Incógnitas y semillas para Calcula; también derivadas propias. | §3.4 y §10 |
| `EQUATIONS` | Residuos que Calcula debe anular. | §3.4 y §10 |
| `FUNCTIONS` | Funciones, tablas incorporadas o declaraciones `EXTERNAL`. | §3.5 y §9 |
| `RUN_CONTROLS` | Selección del modo de resolución y opciones numéricas. | §3.6, §11 y §12 |
| `CONTROL_EQUATIONS` | Condiciones para continuar una integración. | §3.6 y §11 |
| `OUTPUTS` | Variables que se quieren observar y abscisa opcional. | §3.6 y §12 |
| `FORMAS` | Símbolos de las ramas e información conservada del dibujo de Dia. Su presencia solicita el esquema. | §3.8 |
| `DATA` | Datos estructurados de compatibilidad, como tablas de métodos. | §3.7 |
| `DATABLOB` | Texto de compatibilidad con Siru antiguo; no es una sección general para programar el Siru actual. | §3.7 |
| `RESULTADOS`, `UNIDADES` | Preparación común de entradas y salidas, aun sin gráficos. | §3.9 |
| `PARAPLOT`, `GRAFICOS`, `ACEPTAR_GRAFICOS`, `GRAFICOS_ADICIONALES` | Descripción de gráficos para Simplot. | §3.9 |
| `SENSITIVITY`, `MONTE_CARLO`, `WORST_CASE` / `WORST-CASE` | Conjuntos de variables para los análisis DC correspondientes. | §11 ter–quinquies |

El lector reconoce además encabezados históricos como `FORCED_VARIABLES`, `MODELS`, `MODEL_DESCRIPTION`, `MULTIELEMENT_DEFINITION`, `CONTROLS`, `ECUACIONES`, `OPTIMIZATION`, `CLEAR`, `RERUN_DESCRIPTION`, `CONTINUE` y `RE-OUTPUT`. Reconocer el encabezado no implica implementar su contenido en `.ds`: los modelos de SCEPTRE, reruns y continuaciones descritos en este manual usan su sintaxis `.d`. `FORCED_VARIABLES` no tiene traducción general en `.ds`; usar parámetros y ecuaciones explícitos. Las órdenes de la terminal antigua (`EXECUTE`, `PREPARE`, `SHOW`, `solve`, `plot`, etc.) no se ejecutan al leer un `.ds`.

### 3.3 Gramática de ramas y modelos

En las reglas siguientes, las comillas indican texto literal, `{ ... }` significa repetición y `[ ... ]` significa parte opcional. `expresion` representa una expresión del lenguaje numérico, no un nombre literal. Los separadores se interpretan fuera de paréntesis y cadenas.

```text
rama       = tipo "-" nombre "," nodo "-" nodo "=" valores [ "=" controles ]
valores    = pieza { "&" pieza }
controles  = pieza { "&" pieza }
pieza      = expresion [ "V:" nombre { "," nombre } ]
mutua      = nombre_M "," bobina "-" bobina "=" expresion
modelo     = tipo "(" cantidad ")" "=" campo { "," campo }
campo      = expresion | marca_modelo
```

| Parte | Ejemplo y regla |
|---|---|
| Rama | `RB-R1, entrada-salida = 1000`. `RB` es el tipo; `R1`, el nombre usado en `VR1`, `IR1` y `FORMAS`. |
| Varios valores | En `.ds` se separan con `&`: `ESIN-E1, GND-1 = 5 & 2*pi*T`. Son `x1`, `x2`, etc., en el modelo. Las comas dentro de una llamada separan los argumentos de esa llamada. |
| Control de rama | El segundo `=` introduce el control histórico de la rama. Para una llave corriente usar el tipo `SB` y su valor de apertura/cierre (§6); otros controles pueden no tener traducción al motor actual. |
| Masa | Un circuito `.ds` usa `GND`. No sustituirla por `0`; esa compatibilidad pertenece a `.d`. Un sistema sin ramas no necesita masa. |
| Mutua | `M12, L1-L2 = 0.2`; las dos bobinas deben estar declaradas. El valor es la inductancia mutua, no el coeficiente de acoplamiento (§8). |
| Modelo algebraico | `RB(1) = VOLTAGE - x1*CURRENT`. Cada expresión del modelo es un residuo que se anula. |
| Estado de un modelo | `IC: CURRENT:CURRENT:DCURRENT` declara la asociación de estado/derivada del modelo de bobina; véanse los modelos completos de §5 y `libraryori.siru`. |
| Otras marcas de modelo | `IF: expresion`, `INV: expresion`, `OP: parte:parte`, `VARS: nombre:nombre`. Son campos del modelo; no encabezados del archivo. Las dos primeras intervienen en la definición y el signo de las fuentes. `OP` y `VARS` conservan información del modelo para su traducción. |

La definición de los tipos debe estar disponible antes de construir el sistema. Se pueden copiar sólo los modelos utilizados dentro del `.ds`, como en los ejemplos de `dibuja`, o incluir la biblioteca. El valor de una rama no es su símbolo gráfico: la forma eléctrica, térmica o genérica se declara por separado en `FORMAS`.

### 3.4 Parámetros, incógnitas, constantes y ecuaciones

```text
parametro  = nombre "=" expresion [ "D:" derivada ]
variable   = nombre "=" semilla   [ "D:" derivada ]
constante  = nombre "=" numero
ecuacion   = expresion { marca_ecuacion }
```

| Sección y ejemplo | Significado |
|---|---|
| `DEFINED_PARAMETERS` / `PRES=101325` | Dato conocido: se entrega a Calcula con ese valor. |
| `VARIABLES` / `PV=1500` | Incógnita: 1500 es una semilla, no un valor impuesto. Calcula la determina mediante las ecuaciones. |
| `CONSTANTS` / `TKO=273.15` | Constante numérica. Esta declaración acepta un número, no una expresión general ni el sufijo de unidad de las cantidades. |
| `DEFINED_PARAMETERS` / `X=1 D:DX` | Valor inicial de `X` y nombre de su derivada. La derivada debe quedar determinada por el sistema. También se reconoce la relación convencional entre `X` y `DX` descrita en §10. |
| `DEFINED_PARAMETERS` / `POT=V*I` | Definición mediante expresión; no es un dato numérico independiente. Se traduce en una incógnita con su ecuación. |
| `EQUATIONS` / `PV-PW(TEMP)` | Calcula busca que el residuo sea cero: equivale a `PV=PW(TEMP)`. Escribir el residuo evita confundir una ecuación con la asignación de un parámetro. |

Cada incógnita necesita la relación que la determina; las semillas ayudan a resolver el sistema pero no agregan ecuaciones. No repetir la misma declaración de parámetro, variable o constante: el lector avisa y descarta la repetida. En cambio, una sección reabierta puede agregar otros nombres.

Las marcas opcionales van **después** de la expresión y no se repiten en una sentencia:

| Marca | Forma y uso |
|---|---|
| `V:` | `V:X,Y` declara los nombres asociados a la ecuación. Se permite `V: X, Y`; escribir la coma pegada al nombre anterior. |
| `LINEAL:` | Sin valor. La parte inicial pasa a ser una lista de coeficientes, alineada con `V:`. |
| `INDEP:` | Un valor: término independiente de la forma lineal. `1,-1 V:X,Y LINEAL: INDEP:2` representa `X-Y=2`. |
| `NONUCLEO:` | Sin valor. Marca una ecuación fuera del núcleo del sistema. Es información avanzada de compatibilidad; no usarla para una ecuación que Calcula deba resolver como parte del sistema. |
| `RES:` | Un nombre asociado al resultado de una expresión. |
| `TIPO:` | Un valor que clasifica la ecuación en el dato histórico; por ejemplo `TIPO:POST`. Su lectura no garantiza que cualquier clasificación tenga implementación en el motor actual. |

### 3.5 Funciones y tablas

```text
funcion    = nombre_funcion "(" cantidad_o_argumentos ")" "=" expresion
externa    = "EXTERNAL" nombre_funcion "(" cantidad ")" ruta
tabla      = "TABLE" nombre_tabla
             numero "," numero { "," numero "," numero }
```

| Forma | Ejemplo y alcance |
|---|---|
| Función por cantidad | `PW(1)=exp(x)`, `COC(2)=x/y`. Los argumentos formales son `x`, `y`, `z`, etc. |
| Función por nombres | `COC(A,B)=A/B`. El lector admite la lista explícita de argumentos; los nombres de función llevan letras y dígitos, sin `_`. |
| Uso | `COC(PV,PRES)` dentro de una expresión. Los argumentos van separados por comas y entre paréntesis. |
| Tabla incorporada | En `FUNCTIONS`, `TABLE 1` y después `0,0, 1,10, 2,20`. También puede ir todo en una línea: `TABLE 1, 0,0, 1,10, 2,20`. Se usa como `TABLE 1(T)`; §9 detalla las formas equivalentes y los saltos. |
| Tabla externa | `Table('tabla.txt','TIEMPO','VALOR',T,1)`. La tabla referenciada debe acompañar al `.ds`; §9 explica columnas e interpolación. |
| Función externa | `EXTERNAL FGEN (2) fgen.f90`. Acepta entre 1 y 12 argumentos y requiere el fuente y las herramientas de compilación indicadas en §9. |

### 3.6 Controles, condiciones y salidas

```text
control    = clave "=" "[" valor { "," valor } "]"
condicion  = expresion_logica { marca_ecuacion }
salidas    = [ abscisa ":" ] nombre { "," nombre }
```

| Escritura | Efecto |
|---|---|
| `RUN_CONTROLS` / `RK=[-1]` | Un único punto resuelto por Calcula. No integra. Véase §12. |
| `RK=[12]` | Integración con el método 12. Los métodos y sus límites están en §11. |
| `RK=[18,18,11]` | Secuencia de métodos; no mezclar `-1` con métodos de integración. |
| `SIRU_PASO=[0.01]` y `SIRU_ADAPT=[0]` | Paso fijo de 0.01. Sin la segunda opción, declarar un paso puede activar adaptación; véase §11. |
| `STARTING STEP SIZE = 0.01` | Forma compatible admitida también en `.ds`; `MINIMUM STEP SIZE`, `MAXIMUM STEP SIZE` y `SEMILLA VL2 = 0.1` son otras excepciones a la lista entre corchetes. |
| `GLM=[...]`, `PGLM=[...]`, `ROS=[...]`, `SPLIT=[...]` | Seleccionan otras familias. Son alternativas a `RK`, no controles para combinar con `RK=[-1]`; sus contratos están en §11. |
| `CONTROL_EQUATIONS` / `T<TIEMPOFINAL` | Condición para **seguir**, no para terminar. Se detiene al dejar de cumplirse. Con `RK=[-1]` no provoca una marcha temporal. |
| `OUTPUTS` / `T:VC1,IR1` | Abscisa `T` y variables solicitadas para una corrida. No son ecuaciones ni valores impuestos. Conviene poner la lista completa en una línea, o usar `\` para continuarla. |
| `OUTPUTS` / `TEMP,TBH,PRES,PV` | Lista de observables sin inventar una variable de tiempo. En modo de un punto la tabla de Siru contiene todas las variables del sistema de Calcula, incluidos los datos (§12). |

Los valores de los controles no admiten listas anidadas; cada clave valida además sus opciones. Las repeticiones de una clave pueden acumular sus valores: declarar una sola vez el selector de método. Usar exactamente `RK=[-1]`, con corchetes y `RK` en ese orden; `RK=-1` y `KR=[-1]` no son la escritura de este modo.

### 3.7 DATA y DATABLOB

`DATA` conserva datos estructurados; no ejecuta Perl. Su gramática es:

```text
asignacion = camino "=" valor
camino     = clave { [ "->" ] ( clave | indice ) }
clave      = "{" cadena "}"
indice     = "[" entero_sin_signo "]"
valor      = cuenta | cadena | "undef" | "[" [ valor { "," valor } ] "]"
```

| Elemento | Regla |
|---|---|
| Ejemplo | `{'rungekutta'}->[3]->{'a'}=[[0,0],[1/2,0]]`. Se conservan listas, objetos, números y textos. |
| Cuentas | `+ - * / **`, paréntesis, `sqrt`, `exp`, `log`, `sin`, `cos`, `abs`, `int`, `atan2`. Aquí la potencia se escribe `**`. |
| Índices | `[0]`, `[3]`: enteros no negativos, sin signo. No confundir un índice con un valor: `[-1]` sí puede ser el **valor** de una lista. |
| Compatibilidad | `{'lnumrk'}=[-1]` aparece en archivos antiguos. Para pedir un punto al Siru actual declarar explícitamente `RK=[-1]` en `RUN_CONTROLS`; no depender de ese dato histórico. |
| Errores | No se acepta código, un índice negativo, una cadena sin cerrar ni cambiar un objeto ya creado por una lista al recorrer un camino. |

`DATABLOB` transporta el texto histórico y sólo tiene traducciones específicas, entre ellas el selector antiguo de método `numrk`. No constituye un lenguaje general admitido por el motor actual. Para un archivo nuevo usar ecuaciones, funciones y controles declarativos; el [documento del lector](../../slex/d2datdinc/README.md#5-datablob-a-exprtk) precisa el patrón heredado que reconoce.

### 3.8 FORMAS: el dibujo dentro del .ds

`FORMAS` permite incluir en el propio `.ds` la información de `NOMBRE.formas`, el archivo que `dia2formas` extrae de un dibujo de Dia. No sustituye `ELEMENTS`: las ramas y sus conexiones siguen siendo la fuente del circuito. Puede aparecer entre las demás secciones; termina al comenzar otra sección o al llegar a `END`.

El formato completo tiene **siete campos separados por tabuladores reales**, uno por renglón:

```text
nombre<TAB>forma<TAB>orientacion<TAB>x<TAB>y<TAB>ancho<TAB>alto
```

`<TAB>` representa un tabulador, no los cinco caracteres de esa indicación ni la secuencia literal `\t`. Los espacios interiores pertenecen al campo. En este ejemplo las separaciones son tabuladores reales:

```text
FORMAS
# nombre	forma	orientacion	x	y	ancho	alto
R1	Electrico - hresistor	horizontal	4	1	3	1.5
masa	Electrico - tierra	fija	8	6	2	2
nodo 1	Electrico - nodo	fija	3	1	1	1
END
```

| Campo | Contenido |
|---|---|
| `nombre` | Nombre de rama sin el prefijo de tipo (`R1`, no `RB-R1`), `masa` o el nombre de nodo de Dia (`nodo 1`). |
| `forma` | Nombre de la forma, por ejemplo `Electrico - hresistor`, `Termico - acumulador` o `Termico - 0gradosc`. Se conserva como texto. |
| `orientacion` | Texto de Dia, por ejemplo `horizontal`, `vertical`, `fija`, `hacia la izquierda` o con indicaciones de volteo. No es un código numérico. |
| `x`, `y` | Coordenadas de la esquina de la forma en Dia, en cm; `y` crece hacia abajo. |
| `ancho`, `alto` | Dimensiones en las mismas unidades de Dia. |

Los siete campos deben tener contenido y los cuatro últimos deben ser números finitos. Campos de más, campos vacíos o coordenadas no numéricas hacen fallar la lectura sin publicar un DAT parcial. Como formato abreviado también se admite `R1 Electrico - hresistor`, o dos campos separados por tabulador: sólo `nombre` y `forma`, sin geometría. No escribir siete campos separados por espacios: se interpretarían como la forma abreviada.

El lector conserva el orden, los nodos y las masas repetidas. En el `.dat` el bloque queda en `GLOBAL.formas`:

```json
{"GLOBAL":{"formas":[{"nombre":"R1","forma":"Electrico - hresistor","orientacion":"horizontal","x":4,"y":1,"ancho":3,"alto":1.5}]}}
```

Es un fragmento del dato, no un circuito completo. `FORMAS` vacía produce `"formas":[]`; sin la sección, la clave no existe. Esa diferencia es deliberada:

| Entrada | Acción de Siru |
|---|---|
| Sin `FORMAS` | Resuelve el problema sin solicitar esquema. |
| `FORMAS` vacía | Invoca `dibuja` para construir el esquema a partir de las ramas y conexiones, con símbolos automáticos. |
| `FORMAS` con registros | Invoca `dibuja` y le entrega las formas del dato ya leído. |
| `FORMAS` y `RK=[-1]` | Solicita el esquema y resuelve un punto con Calcula; dibujar no implica integrar. |
| `dibuja` ausente | Continúa el cálculo y deja constancia en `.log`. |
| Error de `dibuja` | Avisa y continúa el cálculo. |

Para pedir el dibujo automático basta poner, antes de `END`:

```text
FORMAS
END
```

`dibuja` elige símbolos con esta información y escribe `NOMBRE.svg` junto al circuito. Las formas internas prevalecen, por nombre, sobre las del archivo externo `NOMBRE.formas`; los nombres no cubiertos internamente pueden seguir tomando la forma externa. Las ramas restantes usan el conjunto predominante o los símbolos automáticos. Para nombres repetidos se toma la primera forma; los registros de nodos se conservan pero todavía no seleccionan símbolos de nodo. `--electrico` permite pedir explícitamente la representación eléctrica al ejecutar `dibuja` por separado.

**Alcance actual de la geometría:** slex transporta orientación, posición y tamaño, pero `dibuja` usa ELK para distribuir el esquema. Todavía no reproduce las coordenadas, tamaños ni volteos originales de Dia. Siru activa este recorrido para `.d`, `.ds` y `.dat`, también al continuar una corrida; un `.din` no transporta `FORMAS`.

| Ejemplo completo | Contenido |
|---|---|
| [automatico.ds](../../dibuja/ejemplos/automatico.ds) | `FORMAS` vacía: esquema automático. |
| [divisor.ds](../../dibuja/ejemplos/divisor.ds) | Divisor de tensión con formas eléctricas y modelos incorporados. |
| [termico.ds](../../dibuja/ejemplos/termico.ds) | Circuito con formas térmicas. |

Los tres tienen sus `.dat` y `.svg` de ejemplo al lado. Para trasladar la información de Dia: ejecutar `dia2formas circuito.dia`, insertar el contenido de `circuito.formas` bajo `FORMAS` antes de `END` y conservar sus tabuladores. Para dibujar por separado: `dibuja circuito.ds` o `dibuja circuito.dat`; la segunda forma no necesita Calcula, Siru ni el lector `.ds`.

### 3.9 Resultados y unidades comunes

`UNIDADES` y `RESULTADOS` pertenecen al cálculo y a la preparación común de tablas, no al motor gráfico. Slex interpreta ambos al generar el DAT. Siru calcula en SI y aplica el pedido de salida con Tabular2 antes de publicar la tabla, también con `-nosimplot`. PARAPLOT describe cómo dibujar la tabla preparada; FORMAS describe el esquema y referencia su modelo físico.

| Información | Aplicación |
|---|---|
| `UNIDADES`, `FORMAS`, `DEFAULTDOMAIN` | Slex resuelve el origen y convierte las entradas numéricas a las unidades del cálculo. |
| `RESULTADOS` | Tabular2 selecciona y ordena columnas y aplica nombres, magnitudes y unidades de salida. |
| `PARAPLOT` | Simplot dibuja; consume los valores y metadatos preparados, sin repetir el procesamiento común. |
| `RESULTADO GRAFICO` | Propuesta futura, no implementada ni necesaria para esta cadena. |

La sintaxis se mantiene en una sola [gramática común, §3.9](../../slex/d2datdinc/doc/GRAMATICA.md#39-unidades-y-resultados-comunes-paraplot-para-el-dibujo). El [contrato del preparador](../../tabular2/doc/RESULTADOS.md) explica su uso por archivos o stdin, las columnas seleccionadas y la marca que impide volver a convertir. Un ejemplo: 25 tempC de entrada se calcula como 298.15 K; pedir la salida en tempC publica 25, sin alterar las ecuaciones ni los estados del integrador.

### 3.10 Cómo comprobar la lectura

Con el lector instalado, `d2datdinc -v problema.ds -o problema.dat` genera el dato y muestra advertencias de sintaxis o compatibilidad. Desde la raíz del árbol de fuentes:

```sh
slex/d2datdinc/d2datdinc -v \
  -c slex/d2datdinc/d2datdinc.slex \
  -b slex/d2datdinc/biblioteca problema.ds -o problema.dat
perl siru/bin/siru problema.ds
```

Algunas construcciones históricas mal escritas se descartan con aviso; por eso generar un `.dat` no prueba por sí solo que todo lo escrito haya sido utilizado. Revisar los avisos de `-v`. Siru rechaza un sistema con traducciones pendientes. Para comprobar una regla en el código, la gramática léxica está en [d2datdinc.slex](../../slex/d2datdinc/d2datdinc.slex); las gramáticas de sentencias, en `parametros.y`, `ecuacion.y`, `declaraciones.y`, `control.y`, `data.y`, `edef.y` y `piezas.y` del mismo directorio. `dato.c` implementa la interpretación de secciones y de `FORMAS`.

## 4. Un archivo mínimo

Un RC alimentado por una batería, con la resistencia dependiendo del voltaje
del capacitor. Da lo mismo en los dos formatos.

`.d`:

```
CIRCUIT DESCRIPTION
  Un RC con resistencia no lineal
ELEMENTS
 E1, GND-1 = 1
 R1, 1-2 = 1+0.1*VC1
 C1, 2-GND = 1
OUTPUTS
VC1, IR1
INITIAL CONDITIONS
QC1 = 0
RUN CONTROLS
INTEGRATION ROUTINE = TRAP
STOP TIME = 2
END
```

`.ds`:

```
include ../slex/d2datdinc/biblioteca/libraryori.siru
CIRCUIT_DESCRIPTION
  Un RC con resistencia no lineal
ELEMENTS
 EB-E1, GND-1 = 1
 RB-R1, 1-2 = 1+0.1*VC1
 CB-C1, 2-GND = 1
OUTPUTS
T:VC1,IR1
DEFINED_PARAMETERS
T=0
TIEMPOFINAL=2
QC1=0
RUN_CONTROLS
RK=[12]
CONTROL_EQUATIONS
T<TIEMPOFINAL
END
```

La línea de un elemento es `NOMBRE, NODO1-NODO2 = VALOR`. La masa es `GND`.
Un `.ds` tiene que tenerla: sin `GND`, o con un nodo `0`, `d2datdinc` se
para y pide ponerla. Un `.d` es la entrada de SCEPTRE, que no pide masa, y
se acepta sin `GND`: la masa es el nodo `0`, o si no lo hay el nodo más chico
(los números por su valor, antes que los nombres), y se avisa por la salida
de errores (`el .d no tiene nodo GND: la masa es el nodo 1`). La corriente de la rama va del primer nodo al segundo y su tensión
es `u(NODO1) - u(NODO2)`. Una fuente de tensión `E1, GND-1 = 1` pone el nodo
1 a +1 V.

## 5. Los elementos

### 5.1 La biblioteca (`slex/d2datdinc/biblioteca/libraryori.siru`)

| tipo | qué es | modelo | estado |
|---|---|---|---|
| `RB` | resistencia | `V = x1·I` | |
| `CB` | capacitor, `x1` la capacidad **total** `Q/V` | `dQ/dt = I`, `Q = x1·V` | la carga `Q` |
| `CINC` | capacitor, `x1` la capacidad **incremental** `dQ/dV`; `CINC1, 1-GND = …` | `I = x1·dV/dt` | el voltaje `V` |
| `LB` | bobina | `V = x1·dI/dt` | la corriente `I` |
| `EB` | fuente de tensión | `V = x1` (`E1, GND-1 = 1` pone 1 V en el nodo 1) | |
| `JB` | fuente de corriente | `I = x1` | |
| `SB` | llave | `V·x1 + I·(1-x1) = 0`; `x1` es 1 cerrada, 0 abierta | |
| `ESIN` | fuente senoidal, `ESIN1, GND-1 = A, T` | `V = A·sin(x2)`, con su derivada | |
| `MW` | devanado de un transformador con núcleo, `MW1, 2-GND = N, DPHI1` | `V = N·dPHI/dt` | |
| `LNL` | bobina no lineal, `LNL1, 2-3 = L, n` | `L·dI/dt = V^n` | `I` |
| `RC`, `EC`, `JC` | lo mismo que `RB`, `EB`, `JB` | | |

**Los dos capacitores.** Con capacidad constante `CB` y `CINC` dan lo mismo.
Con una capacidad que depende del voltaje (o de una corriente que depende
del voltaje) no, porque el número significa otra cosa:

- en `CB` es la capacidad total, `Q = C·V`; la corriente es `dQ/dt`, que
  incluye el término `V·dC/dt`;
- en `CINC` es la capacidad incremental, `I = C·dV/dt`. Es el capacitor de
  SCEPTRE, y es lo que dan las fórmulas de un transistor: la capacidad de
  juntura y la de difusión (`CE = 5 + 70·J1`) son `dQ/dV`.

Un `.d` escrito para SCEPTRE con capacidades variables hay que correrlo con
`CINC` para que dé lo mismo: en un `.d` es `CINC` toda rama cuyo nombre
empiece con `CINC` (para SCEPTRE es un capacitor más); las demás `C…` son
`CB`. En un `.ds`, `CINC-C1, 1-GND = …`. La misma capacidad en los dos:
`c(V) = 1 + 0.2·V²` cargada a 10 V por 1 Ω da, en t = 4, `V = 4.74` con `CB`
y `6.17` con `CINC` (y con SCEPTRE). `CINC` no puede estar en un lazo de
capacitores: no tiene la carga como estado. Ejemplos:
`slex/d2datdinc/pruebas/sceptre/dos-capacitores.d` (los dos lado a lado) y
`a01-cinc.d` (el inversor a01 con `CE` y `CC` como `CINC`: igual a SCEPTRE;
con `CB`, `a01-tabla2.d`, difiere de 6 a 15 %).

Los nombres `R`, `E`, `J`, `S`, `L`, `C` sin la `B` son de una biblioteca
vieja por incrementos y no sirven para `siru`: una `C` o una `L` así en
un `.ds` da el mensaje «modelo por incrementos». Usar `CB`, `LB`.

### 5.2 Lo que un elemento puede nombrar en su valor

| en el archivo | qué es |
|---|---|
| un número | el valor, constante |
| `PL` | un parámetro de `DEFINED PARAMETERS` (`PL = 22`); dos ramas que nombren `PL` comparten el valor |
| `VC1`, `IR1`, `QC1`, `IL1` | la tensión, la corriente, la carga, la corriente de otra rama (o de la misma): así se escriben los elementos no lineales y las fuentes controladas |
| `T` | el tiempo |
| `DT`, `TICK` | el paso en uso y la cantidad de pasos dados (los pone el simulador, el archivo no los fija) |
| `QC(PC,VC1)` | una función de `FUNCTIONS` |
| `T1(T)`, `TABLE 1 (T)`, `Table('tabla.txt','X','Y',T,1)` | una tabla (sección 9) |
| `sin`, `cos`, `exp`, `log`, `abs`, `sqrt`, `if(c,a,b)`, `<`, `>`, `and`, `or`… | lo que entiende ExprTk |

Las expresiones van en notación de ExprTk: la multiplicación es `*` y no se
sobreentiende (`2*(x+1)`, no `2 (x+1)`); `^` es la potencia; una condición
vale 1 o 0, así que `(T<1)+(T>3)` es «antes de 1 o después de 3».

Ejemplos:

```
 R1, 1-2 = 1+0.1*VC1              resistencia que depende del voltaje del capacitor
 C1, 2-GND = 1+0.5*VC1            capacidad que depende de su propio voltaje
 E2, GND-3 = 2e5*VR1              fuente de tensión controlada (amplificador)
 J2, GND-3 = 0.98*IR1             fuente de corriente controlada
 E3, GND-4 = 0.5+0.2*sin(3*T)     fuente que varía en el tiempo
 ESIN1, GND-1 = 1, 6.28*T         la senoidal de la biblioteca: amplitud y fase
 J3, GND-5 = 1e-13*(exp(38*VJ3)-1)  un diodo: fuente de corriente función de su tensión
```

### 5.3 Tipos propios

Un modelo nuevo se declara en `ELEMENT DEFINITION` (`.d`) o
`ELEMENT_DESCRIPTION` (`.ds`, que es la sección de una biblioteca): el
nombre con la cantidad de parámetros, y las ecuaciones en `VOLTAGE`,
`CURRENT`, sus derivadas `DVOLTAGE`, `DCURRENT`, y la carga `CHARGE`,
`DCHARGE`; `x1`, `x2`… son los valores que da la línea del elemento, separados por `&` en `.ds` y por comas en `.d`.

```
ELEMENT DEFINITION
   EV(2) = IF: VOLTAGE, VOLTAGE + x1, DVOLTAGE + x2     fuente con su derivada
   MZER(1) = 0                                           elemento nulo
   RQ(1) = VOLTAGE - x1 * CURRENT^3                      resistencia cúbica
```

- La derivada que nombra el modelo dice cuál es el estado: `DCURRENT` hace
  de la corriente un estado (una bobina), `DCHARGE` de la carga (un
  capacitor), `DVOLTAGE` del voltaje. Con dos ecuaciones, la segunda *da* la
  derivada y no hay estado (es lo que hace `ESIN`: una fuente con su
  derivada conocida).
- `IF: VOLTAGE` identifica una fuente de tensión en el dato. El signo de la ecuación y la marca `INV:` forman parte de su definición; para conservar la convención de EB copiar su modelo completo, incluido `INV:VOLTAGE`.
- Un tipo cuyo modelo es `0` no aporta ecuación: su tensión y su corriente
  quedan libres para que las fijen ecuaciones propias (un transformador
  ideal, un girador: `slex/d2datdinc/pruebas/nulo.d`).
- En un `.d` el tipo se reconoce por ser prefijo del nombre (`EV1`, `MZER2`).

### 5.4 Modelos de SCEPTRE (sólo `.d`)

`MODEL 101M (B-C-E)` bajo `MODEL DESCRIPTION`, o los permanentes de
`slex/d2datdinc/biblioteca/modellib.siru` (`T1, 2-3-1 = MODEL 2N2222
(PERM)`): subcircuitos con patas que se expanden donde se usan; cada
elemento interno lleva el nombre de la instancia como sufijo (`CET1`).
Diodos: `DIODE EQUATION (Is, T)` es `Is*(exp(T*VJ)-1)`. Ejemplos en
`slex/d2datdinc/pruebas/sceptre/modelo.d` y `diodos.d`.

### 5.5 Convolución: una caja descripta por su respuesta (`K`)

Una parte del circuito se puede dar por cómo responde en lugar de por sus
elementos: su **respuesta al impulso** `h(t)`, por tabla o por ecuación. Es la
convolución de SCEPTRE (manual, volumen 1, apéndice F), con la diferencia de
que la respuesta va en el mismo archivo y no en el «Disk 12».

```
.d                                          .ds
ELEMENTS                                    ELEMENTS
 K1, 1-GND = FCONVE (H1)    impedancia       KE-K1, 1-GND = H1
 K2, 3-4 = FCONVJ (H2)      admitancia       KJ-K2, 3-4 = H2
FUNCTIONS                                   FUNCTIONS
 TABLE H1                   por tabla        TABLE H1
  0, 2,  0.05, 1.9025, ...                    0, 2,  0.05, 1.9025, ...
 H2 (T) = EXP(-2*T)         por ecuación     H2(1) = exp(-2*x)
```

- **Impedancia** (`FCONVE`, `KE`): la tensión del elemento es la convolución
  de su corriente, `v(t) = ∫₀ᵗ h(t−τ)·i(τ) dτ`.
- **Admitancia** (`FCONVJ`, `KJ`): la corriente es la convolución de la
  tensión.

Una caja de varias puertas se arma, como en SCEPTRE, con un `K` entre cada
par de nodos. Por dentro, como SCEPTRE, el elemento es una fuente (la
historia, lo ya calculado) en serie con una resistencia (el último tramo,
que depende de la corriente desconocida): `v = e + r·i`. siru guarda la
historia en cada paso aceptado y calcula `e` y `r` para cada etapa del
Runge-Kutta, suponiendo `h` e `i` lineales entre puntos (la fórmula F.9 del
manual, exacta para esa suposición): el error es del orden del cuadrado del
paso. Si el circuito no tiene otros estados, el `.din` lleva un estado reloj
(`conv_reloj`, derivada 1) para que haya algo que integrar.

Cuidados:

- El **paso adaptativo no ve el error de la convolución** (sólo mira los
  estados): con un `K` conviene un paso fijo (`SIRU ADAPTIVE STEP = 0`
  en un `.d`, `SIRU_ADAPT=[0]` en un `.ds`). siru lo avisa al arrancar.
- **Llaves.** Una llave en serie con un `K` de impedancia que se abre, o en
  paralelo que se abre, anda. Una que lo **cortocircuita** (o que abre la
  rama de un `K` de admitancia) no: en el comienzo del paso el `K` es una
  fuente que vale su historia, y la llave le impone algo contra ella, como
  cortocircuitar un capacitor cargado. Haría falta un impulso; calcula dice
  que el sistema no se determina y siru agrega esta explicación.
- Un `.din` con elementos `K` no se corre solo (`siru circuito.din`): la
  historia la guarda siru al correr el `.d` o el `.ds`, y lo dice.
- Por tabla, fuera de la tabla vale el último valor; para un tiempo
  negativo, 0 (la respuesta es causal). Por ecuación, la función tiene un
  solo argumento y usa `exp`, `log`, `sqrt`, `sin`, `cos`, `tan`, `abs`,
  `min`, `max`, `pow` y las hiperbólicas.
- Sólo con Runge-Kutta, no con GLM.
- **El costo.** La convolución tiene memoria, y siru hace la cuenta de dos
  maneras:
  - **Recursiva**, si `h` por ecuación es una suma de exponenciales (`exp`,
    `cos`, `sin`, `cosh`, `sinh` de `a·x`, sumadas y multiplicadas: por
    ejemplo la impedancia de una caja R, L, C, como en `a06b`). siru lo
    reconoce solo y no guarda historia: el costo por paso es fijo, y la
    cuenta es exacta para la corriente lineal entre puntos.
  - **Directa**, para cualquier otra `h` (por tabla, o una ecuación que no es
    suma de exponenciales): guarda la historia y cada paso la suma entera.
    La historia se **comprime** como el `CMPAC` de SCEPTRE: cada `cuenta`
    pasos se sacan los puntos que sobran (donde la corriente es casi una
    recta) mientras el área perdida no pase de `criterio` por el área de la
    historia. Por omisión, cada 50 pasos con criterio 10⁻⁶; se cambia con los
    controles de SCEPTRE, en un `.d` o un `.ds`:
    `COMPRESSION COUNT = 100` (0 la apaga) y `COMPRESSION CRITERION = 1E-5`.
    La suma se corta también en los puntos de `h`, como SCEPTRE: con `h` por
    tabla el costo por paso queda acotado por la tabla y la compresión ahorra
    tiempo (diez veces con una corriente constante); con `h` por ecuación que
    no es exponencial ahorra memoria, no tiempo.

Ejemplos, con su prueba en `tests/convolucion.t`: en `siru/corpus/corpus/inventados`,
la caja RC escrita (`caja-rc.ds`) y como `K` por ecuación y por tabla
(`caja-k-ecuacion.ds`, `caja-k-tabla.ds`); la caja RL escrita (`caja-rl.d`) y
como `FCONVJ` (`caja-kj.d`). El plan y las fórmulas: `PLAN_CONVOLUCION.md`.

## 6. Llaves

Una llave es un elemento `SB` (`S1` en un `.d`) cuyo valor es su condición:
1 (o verdadero) cerrada, 0 abierta.

```
 S1, 2-3 = T<1                       .d
 SB-S1, 2-3 = (T<1)+(T>3)            .ds
 S2, 3-GND = VC1>0.5                 por una variable del circuito
```

La condición puede usar cualquier variable del circuito, los parámetros y
`T`. Se evalúa **al empezar cada paso**; si cambia en medio de un paso, la
llave cambia en el siguiente. Cada combinación de llaves es una
configuración, y su sistema se arma la primera vez que se entra en ella
(quedan los `.mdin`). Una llave abierta puede dejar nodos sin camino a masa
(una isla): se les fija potencial cero y no es error. Ejemplos:
`slex/d2datdinc/pruebas/isla.d`, `llave-lazo.ds`, `llave-corte.ds`.

### 6.1 Llaves o `if(...)`: la diferencia

Las dos eligen entre ecuaciones según una condición. La diferencia es
**cuándo** se decide y **qué pasa en el salto**.

| | llave (`SB`, `S1`) | `if(c, a, b)` en una ecuación |
|---|---|---|
| cuándo se evalúa la condición | una vez, al empezar cada paso (el sistema del instante) | en cada iteración de Newton y en cada etapa del Runge-Kutta |
| durante el paso | la configuración queda fija: un sistema suave, sin saltos | puede cambiar de rama en medio del paso, o ir y volver entre dos iteraciones |
| el salto | siru lo resuelve: trasvase de carga o flujo y equilibrio (`-I.mdin`, `-E.mdin`) | no hay tratamiento: los estados siguen como estaban |
| para qué sirve | cambios de topología, donde los estados pueden saltar | leyes por tramos con los estados continuos |
| robustez | buena: cada configuración es su propio sistema | más frágil: cerca del cambio, con paso grande, Newton puede oscilar entre las ramas |

Un ejemplo de `if` es el cambio de fase
(`siru/corpus/corpus/perl/cambiofase.ds`): la entalpía
es continua y sólo cambia la ley, calor sensible fuera del intervalo de
fusión y latente adentro. Las llaves de hoy son ramas de un circuito, así que
un sistema sin circuito, como ese, sólo puede usar `if`.

`if`, `and`, `or`, `not` y `xor` pasan tal cual al `.din` (desde r1899) y
Calcula los evalúa con ExprTk. Un estado se puede declarar `TEMP=0 D:DTEMP`
en `DEFINED_PARAMETERS` con la derivada dada por una ecuación de `EQUATIONS`
(desde r1901), como en `cambiofase.ds`: la temperatura sube hasta la de
fusión, se queda ahí mientras funde y después sigue subiendo.

## 7. Lazos de capacitores y cortes de bobinas

Dos capacitores en paralelo, un capacitor en paralelo con una batería, o una
malla de capacitores y baterías, es un **lazo**: los voltajes no son
independientes. Bobinas en serie en un nodo sin otra salida, o con una
fuente de corriente, es un **corte**: las corrientes no son independientes.
No hay nada que escribir: `siru` los encuentra, integra con la ley del lazo
(o del corte) en derivadas, y cuando una llave hace aparecer un lazo o un
corte con los estados desparejos hace el **equilibrio** —el trasvase
instantáneo de carga o de flujo— y escribe una fila más con el mismo `t`.
En el `.simul` se ve como un salto.

**Al arrancar**, si una fuente impone un estado —una bobina en serie con una
fuente de corriente, un capacitor en paralelo con una batería— y el archivo
no da su condición inicial, el estado arranca con lo que impone la fuente y
no hay salto (`arranque: il44 = 10 (lo impone una fuente y el archivo no da
su condicion inicial)`); es lo que hace SCEPTRE. Si el archivo la da (`IL44
= 0`), hay equilibrio como en cualquier otro caso: la bobina salta a 10 y
las acopladas con ella reciben el golpe.

Una fuente que varía adentro de un lazo o un corte entra con su derivada: la
del modelo si la trae (`EV`, `ESIN`), y si no la numérica. En un lazo la
capacidad puede ser constante o depender del voltaje de capacitores:

- del **propio** (`C1, 2-GND = 1+0.5*VC1`);
- del de **otro capacitor** (`C2, 1-2 = X1(1.+0.2*VC1**2)`; `CC2 = f(VCC1)`
  en el transistor JCNTRAN de `d04.d`);
- de la **corriente de una fuente** cuyo valor es función de un voltaje en
  paralelo con un capacitor (`CE = f(JE)`, con `JE` el diodo de la juntura:
  siru cambia `JE` por su ecuación y el voltaje del diodo por el de `CE`).

Si depende de otra cosa (el tiempo, una corriente cualquiera), en un lazo es
un error que lo dice. Ejemplos: `siru/corpus/corpus/llaves/*.d`,
`slex/d2datdinc/pruebas/capacidad-variable.d`,
`slex/d2datdinc/pruebas/sceptre/lazo-c-ajeno.d`.

**El estado de un capacitor es su carga**, `Q = C·V`, y la corriente es
`dQ/dt`: con una capacidad variable eso incluye el término `V·dC/dt`. SCEPTRE
hace otra cosa: integra `I = C·dV/dt`, con `C` evaluada en el punto (la
capacidad incremental). Con capacidad constante es lo mismo; con capacidad
variable **los resultados difieren** y no es un error de ninguno de los dos:
en `slex/d2datdinc/pruebas/sceptre/c-variable.d` (`C1 = 1 + 0.2·VC1²`
cargado a 10 V por 1 Ω) SCEPTRE da `VC1 = 6.17` en `t = 4` y siru `4.74`.
Para escribir en siru la capacidad incremental de SCEPTRE `c(V)` hay que dar
`C = (1/V)·∫c dV` (para `c = 1 + 0.2·V²`, `C = 1 + 0.2·V²/3`): con eso
siru da lo mismo que SCEPTRE (`c-variable-q.d`: 6.166829 contra 6.166827).

Con lazos o cortes, el punto de operación (11 bis) se resuelve en un sistema
aparte, `NOMBRE-[bits]D.mdin`: el instante con las ecuaciones originales en
lugar de las leyes derivadas, que con las derivadas en cero no atan nada.

## 8. Acoplamientos magnéticos

**Inductancia mutua**, como en SCEPTRE: una línea `M, L1-L2 = valor` entre
dos bobinas `LB` que ya existen. Agrega `M·dI2/dt` a la tensión de `L1` y
viceversa. Si una llave abre la rama de una de las dos, la otra recibe el
golpe: `ΔI2 = (M/L2)·I1` (se conserva el flujo concatenado).

```
 L1, 2-GND = 1
 L2, 3-GND = 4
 M,  L1-L2 = 1.8
```

**Transformador con núcleo**: cada devanado es un `MW` con sus vueltas y la
derivada del flujo del núcleo; el flujo es un estado propio y la fuerza
magnetomotriz una ecuación propia (sección 10).

```
ELEMENTS
    MW1, 2-GND = 1, DPHI1          N1 = 1 vuelta
    MW2, 3-GND = 2, DPHI1          N2 = 2 vueltas
VARIABLES
    DPHI1 = 0
EQUATIONS
    1*IMW1 + 2*IMW2 - REL*PHI1     N1·I1 + N2·I2 = reluctancia · flujo
DEFINED PARAMETERS
    REL = 1
    PHI1 = 0
```

`REL` puede ser una expresión (un núcleo que satura). Ejemplos:
`slex/d2datdinc/pruebas/mutua.d`, `nucleo.d`, `siru/corpus/.../trafo*.d`.

## 9. Funciones y tablas

**`FUNCTIONS`**: `NOMBRE(args) = expresión`. En un `.d` los argumentos llevan nombre. En un `.ds` se puede declarar la cantidad y llamarlos `x`, `y`, `z`…; también se admite la lista explícita de nombres de §3.5.

```
FUNCTIONS                              .d
QC(PC,V) = PC*(1+0.1*V)

FUNCTIONS                              .ds
QC(2) = x*(1+0.1*y)
```

Hasta 6 argumentos las compila Calcula; con más, `d2datdinc` reemplaza cada
llamada por el cuerpo de la función, así que andan igual. Las constantes de
`CONSTANTS` se pueden usar adentro.

**Tablas**: una función de una variable dada por puntos, interpolada
(lineal; un `x` repetido es un salto). Si la tabla se evalúa en el tiempo,
como `TABLE 1(TIME)`, cada salto es un punto de quiebre de la corrida.
siru corta el paso justo antes del salto y lo cruza con un paso mínimo,
así ningún paso promedia el valor de antes con el de después. La salida
muestra el salto en dos filas, una a cada lado (Integrador, r2104;
`siru/corpus/EXACTAS.md`). Vale también para una tabla en archivo aparte, y para
funciones que saltan en el tiempo, aunque el tiempo esté transformado de
forma lineal: `if(T<1,...)`, `if(4*T-2 < 2,...)`, `T < TCORTE` con un
parámetro, `sgn(T-1)`, `mod(T,2)`, `T % 3`, `floor(T/2)`, una tabla evaluada
en `2*T+1`, o una función propia evaluada en `T` que haga eso. Vale con
Runge-Kutta, con GLM y con PGLM. En un método multipaso, como BDF2, el paso
que cruza el salto y el siguiente se dan como el primero de la corrida. Lo
que no se reconoce, como `sin(T) > 0.5` o un salto de una variable del circuito,
pierde medio paso en cada salto. Ejemplos: `siru/corpus/corpus/inventados/salto-*.ds`.

```
FUNCTIONS                              .d: adentro del archivo
TABLE 1
  0,0, 1,1, 2,0
ELEMENTS
 E1, GND-1 = TABLE 1 (T)              o T1(T), o XTABLE(T1, VC1)

ELEMENTS                               .ds: en un archivo de dos columnas
 EB-E1, GND-1 = Table('tabla.txt','TIEMPO','DATO',T,1)
```

Desde el 02/10/2026 un `.ds` también acepta la forma de SCEPTRE, con la
tabla adentro del archivo: en `FUNCTIONS` `TABLE 1` y debajo los pares (o
`TABLE 1, 0,0, 1,1, 2,0` en una línea), y en el valor `TABLE 1 (T)` o
`TABLE 1(TIME)`. La forma corta `T1(T)` en un `.ds` sólo vale si la tabla
ya se leyó (antes de `ELEMENTS`); si no, se toma por una función.
Ejemplo: `siru/corpus/.../needsderiJ.ds`, que da lo mismo que su `.d`.

El archivo `tabla.txt` es de tabular2: una línea `#TIEMPO DATO` y las filas;
su ruta es relativa al `.ds` (o al directorio de arriba, como corría Siru).
Las `x` tienen que ir creciendo. `Table()` también se puede usar adentro de
una función. Ejemplos: `slex/d2datdinc/pruebas/tablas.d`,
`siru/corpus/.../tabla.ds`.

**Funciones en Fortran: `EXTERNAL`.** Una función escrita en Fortran, en un
`.f90`, `.f` o `.for` aparte (la "External Function" del manual de SCEPTRE,
2.2.2 y 2.7), se declara en `FUNCTIONS` con su nombre, su cantidad de
argumentos (de 1 a 12) y la ruta del fuente, relativa al `.d` o al `.ds`:

```
ELEMENTS                               .d
 E1, GND-1 = FGEN (0., 5., 20., 40., 100., 100., 300., 2., TIME)
 L2, 2-GND = FCOR (IL2, IR1, 25., 100.)
FUNCTIONS
 EXTERNAL FGEN (9) fgen.f90
 EXTERNAL FCOR (4) fcor.for
```

El fuente tiene que ser una `FUNCTION` de doble precisión, con los argumentos
por referencia, que es lo normal en Fortran:

```
      FUNCTION FCOR(IL, IR, L0, ISAT)
      DOUBLE PRECISION FCOR, IL, IR, L0, ISAT
      FCOR = L0 / (1D0 + ((IL + IR)/ISAT)**2)
      END FUNCTION
```

`d2datdinc` lleva la declaración al `.din` (sección `externas`, con la ruta
absoluta); Calcula compila el fuente con `gfortran -shared -fPIC -O2` a un
`.so` en `~/.cache/calcula/externas`, lo vuelve a compilar si el fuente
cambia, lo carga y llama a `fgen_`, el nombre en minúsculas con el `_` que
le pone gfortran. También acepta un `.so` ya compilado como ruta. Hace falta
`gfortran` donde se corre, y **`CALCULA_EXTERNAS=1` en el entorno**: es
código de afuera, y Calcula no lo carga sin ese permiso. Por eso Puyuspa,
que corre archivos de cualquiera, no ejecuta un ejemplo con funciones
externas salvo que se lo instale en el servidor. Los mensajes de error están
en la sección 13. Ejemplo: `siru/corpus/corpus/fortran/subprograma.d`, con
`fgen.f90` y `fcor.f90`.

**`ETABLE`: una tabla en un archivo aparte, por número.** Es la forma de
SCEPTRE y Simusol: `ETABLE(N, X, Y, V, M)` es la tabla número `N`,
interpolada en `V`, con las abscisas en la columna `X` y los valores en la
columna `Y` (contadas desde 0) y el método `M` (1 lineal, 2 spline). Qué
archivo es cada número lo dice `etable.txt`, junto al archivo, con una línea
`número archivo` por tabla:

```
DEFINED PARAMETERS                     con_FOR.d
PP = X1(ETABLE(2,0,2,TIME,2))

2 con_FOR.etable                       etable.txt
```

siru la tiene de dos maneras. **La normal**: no hay que declarar nada; libdin
la traduce a `tabla('con_FOR.etable','0','2',t,2)` de Calcula (desde calcula
6.0-34, que acepta las columnas por número). **La de Fortran**: si el archivo
la declara `EXTERNAL ETABLE (5) RUTA/tabular2/fortran/etable.f90`, se llama a
esa función —la misma que enlaza SCEPTRE—, con `CALCULA_EXTERNAS=1`. Las dos
dan lo mismo. Ejemplos: `siru/corpus/corpus/fortran/con_FOR_1.d` (la normal) y
`con_FOR_1.ds` (la de Fortran).

Un archivo sin circuito ni estados que pide una corrida —una tabla o una
función a lo largo del tiempo, como esos— se resuelve punto por punto, con el
paso de `SIRU_PASO`, o el de `MAXIMUM STEP SIZE` en un `.d`, o la milésima
parte del tiempo final (11); la salida lleva el tiempo y los `OUTPUTS`.

## 10. Ecuaciones propias, estados propios, parámetros

Un circuito puede traer ecuaciones además de las suyas, y un archivo puede
no tener circuito y ser sólo ecuaciones.

- **`EQUATIONS`**: cada línea es una expresión igualada a cero. Puede
  nombrar las variables del circuito (`VC1`, `IMW1`), los parámetros, `T`,
  `DT`, `TICK`.
- **`VARIABLES`**: las incógnitas que esas ecuaciones agregan, con su
  semilla: `Z=0`. Una que se llame `D<X>` con `X` definida en `DEFINED
  PARAMETERS` es la derivada de `X`, y `X` pasa a ser un **estado** que se
  integra. Cada ecuación propia necesita una incógnita propia: si no cierran
  los grados de libertad, `siru` lo dice.
- **`DEFINED PARAMETERS`**: `P = 22` es un parámetro; `P = expresión` es una
  incógnita propia con su ecuación (`PDIS = PX1*2`); `DPX1 = PX2` junto con
  `PX1 = 1` hace de `PX1` un estado con derivada `PX2` (la mecánica de
  `a13.d`). También van las condiciones iniciales (`QC1 = 0`, `IL1 = 0`) y,
  en un `.ds`, `T=0` y `TIEMPOFINAL`.
- **`CONSTANTS`**: `K=1e-3`, números con nombre para usar en expresiones y
  funciones.

Un sistema sin circuito:

```
CIRCUIT_DESCRIPTION
  y'' = -y
VARIABLES
DY1=0
DY2=0
EQUATIONS
DY1-Y2
DY2+Y1
OUTPUTS
T:Y1,Y2
DEFINED_PARAMETERS
T=0
TIEMPOFINAL=3
Y1=1
Y2=0
RUN_CONTROLS
RK=[12]
CONTROL_EQUATIONS
T<TIEMPOFINAL
END
```

Ejemplos: `slex/d2datdinc/pruebas/propias.d`, `parametros-propios.d`,
`nucleo.d`, `nulo.d`.

## 11. La corrida

### Composición de subflujos (SPLIT)

Se declara cómo avanzar cada subproblema, no solamente su derivada. Para el oscilador `Q'=P`, `P'=-Q`, A mueve Q manteniendo P y B mueve P manteniendo Q. En `RUN_CONTROLS` de un `.ds`:

```text
SPLIT=[2]
SPLIT_AUTONOMO=[1]
SPLIT_EXACTOS=[1]
SPLIT_A=[Q,Q+SPLIT_H*P,P,P]
SPLIT_B=[Q,Q,P,P-SPLIT_H*Q]
SIRU_PASO=[0.1]
SIRU_ADAPT=[0]
```

| Selector | Orden de ejecución de subavances | Orden esperado con subflujos exactos suaves |
| --- | --- | --- |
| `SPLIT=[1]` | A(h), luego B(h) | 1 (Lie–Trotter) |
| `SPLIT=[2]` | A(h/2), luego B(h), luego A(h/2) | 2 (Strang) |

Cada lista debe incluir una expresión para **todos** los estados, incluso los que se conservan. Las expresiones de un mismo mapa se evalúan simultáneamente sobre su estado de entrada. `SPLIT_H` es la duración de ese subavance: no es el tiempo físico ni el paso completo. Se permite aritmética `+ - * / ^` y paréntesis con estados y parámetros constantes. No se admiten referencias al tiempo, derivadas, variables algebraicas, funciones o tablas en estos mapas iniciales.

`SPLIT_AUTONOMO=[1]` y `SPLIT_EXACTOS=[1]` son declaraciones del autor: los mapas deben ser flujos exactos de las partes del modelo y valer la identidad con duración cero. El programa verifica forma, nombres y capacidades, **no demuestra esas propiedades matemáticas**. Reemplazarlos por un Euler aproximado puede perder el orden dos de Strang. El modelo residual completo se conserva y Calcula lo resuelve en los estados intermedios; debe ser válido allí.

En `.d` se escriben `SPLIT AUTONOMO`, `SPLIT EXACTOS`, `SPLIT A`, `SPLIT B` (con espacios), y los controles habituales `STARTING STEP SIZE` y `SIRU ADAPTIVE STEP = 0`. El ejemplo [lc-strang.d](../corpus/corpus/SPLIT/lc-strang.d) declara mapas de **QC1**, la carga que libdin usa como estado, aunque pide **VC1** como salida; C=L=1. También están [oscilador-1.ds](../corpus/corpus/SPLIT/oscilador-1.ds) y [oscilador-2.ds](../corpus/corpus/SPLIT/oscilador-2.ds).

La cadena es `.d/.ds → d2datdinc --din → NOMBRE-de-d2datdinc-SPLIT1.din` o `-SPLIT2.din → siru`. El `.din` contiene las ecuaciones de ambos mapas y un descriptor `# split` v1 con su secuencia, estados y salidas; no requiere fuente, `.dat` ni catálogo. `split_h` y `split_flujo` son datos internos de Calcula, `split_p_estado` es la salida del mapa y `p_estado` es la salida virtual del paso completo. Los prefijos `split_` y `p_` están reservados.

Solo se confirma el reloj y se escribe una fila al terminar toda la secuencia. Los tiempos internos no se suman: A(h/2), B(h), A(h/2) representan un único paso físico h, no 2h. Un fallo de subflujo aborta sin publicar trayectoria parcial. El informe cuenta pasos completos, subavances y consultas; se aceptan último paso acortado y `--paso-factor`.

Límites: un selector, paso fijo positivo, modelo autónomo, dos flujos declarados exactos, sin adaptación, `.mdin`, llaves, eventos, convolución, DT/TICK, DC/AC, RERUN/CONTINUE, funciones propias o externas ni tablas internas o externas. Quedan para otra ampliación los subflujos aproximados, sus tolerancias, pasos negativos, más particiones y el tiempo no autónomo. El núcleo genérico es `Siru::Composicion`, y `Siru::SPLIT` lo conecta con el conductor transaccional y Calcula. Pruebas: `prove -Ilib tests/split_nucleo.t tests/split.t`.

### GLM fijo o adaptativo

`GLM=[1]` selecciona Euler explícito, `[20]` BDF2 efectivo, `[21]` BDF2 directo y `[30]` Adams–Bashforth 2. Son IDs propios del catálogo GLM, no índices RK. Solo se admite uno por corrida, con `.din` simple, sin llaves, lazos/cortes, convoluciones ni RERUN. El artefacto generado contiene el descriptor y se ejecuta sin fuente ni `.dat`.

En `RUN_CONTROLS` de un `.ds`, por ejemplo:

```text
GLM=[21]
SIRU_ADAPT=[1]
SIRU_PASO=[0.1]
SIRU_PASO_MIN=[1e-8]
SIRU_PASO_MAX=[0.2]
SIRU_ABSOLUTO=[1e-5]
SIRU_RELATIVO=[0]
SIRU_ORDEN_ERROR=[2]
```

En `.d` se usan los controles con espacios de la tabla de abajo. `[0]` en `SIRU_ADAPT` conserva el modo fijo. En adaptación, el orden del estimador debe ser 2: se compara BDF2/AB2 con un paso Euler desde el mismo estado confirmado; el arranque/Euler se comparan con dos medios pasos Euler. Se acepta siempre el candidato principal. Es una primera política conservadora, no BDF de orden variable ni una garantía de error global. Un fallo de Calcula aborta; el rechazo por tolerancia no mueve historia ni tiempo. No se admite `DT`/`TICK` dentro del modelo adaptativo.

Los `.din` nuevos usan contrato GLM v2; los v1 archivados conservan el paso fijo y deben regenerarse para activar adaptación. `estadisticas ultima integracion:` en el `.informe` registra intentos, aceptaciones, descartes y consultas del conductor, incluyendo las de comparación. En RERUN/CONTINUE de otros motores describe la última integración, no la suma de corridas. ROS conserva su registro `estadisticas ROS:`. Fórmulas, límites y ejemplos en [GLM_CONTRATO.md](GLM_CONTRATO.md).

### Rosenbrock (extension ROS)

`ROS=[1]` en RUN CONTROLS (`.d`) o RUN_CONTROLS (`.ds`) selecciona ROS2 de orden dos. Es excluyente con RK, GLM, PGLM e IMEX_EXPLICITA. `SIRU ADAPTIVE STEP = 0` en `.d` o `SIRU_ADAPT=[0]` en `.ds` selecciona paso fijo; con 1 usa el estimador embebido ROS2 2(1), las tolerancias SIRU y los limites de paso existentes. El orden del error es 2. No admite todavia llaves, lazos/cortes, convoluciones, DT/TICK, paradas adicionales, inicializacion algebraica, analisis DC, sensibilidad, Monte Carlo, peor caso, RERUN ni CONTINUE.

El archivo generado termina en `-ROS1.din` y se puede ejecutar sin su fuente; incluye OUTPUTS si se pidieron. El motor consulta el campo diferencial en Calcula, calcula un Jacobiano por diferencias y resuelve las etapas lineales. No usa la numeracion RK ni supone que el Jacobiano de Newton sea el de la ecuacion diferencial. Detalles y precauciones de escalas y precision en [ROS.md](ROS.md).

Los din nuevos usan descriptor ROS v2 fijo o v3 adaptativo, con escalas configurables y comprobacion de diferencias a dos perturbaciones; los v1 archivados conservan el comportamiento anterior. En .ds: `ROS_ESCALAS=[X,1e-6,Z,1e6]`, `ROS_ESCALA_TIEMPO=[1]`, `ROS_DELTA=[1e-4]`, `ROS_CONTROL_JACOBIANO=[1e-6,1e-3,3]`. En .d los nombres llevan espacios en lugar de guiones bajos. Estas opciones controlan la diferenciacion numerica, no activan adaptacion temporal ni cambian la precision interna de Calcula. v3 agrega la seccion adaptacion y los errores virtuales e_estado; solo publica pasos aceptados. Fallos del campo/Jacobiano abortan, no se ocultan reduciendo el paso temporal.

Con adaptacion activa, `ROS_TOLERANCIAS=[X,1e-11,1e-4,Z,10,1e-4]` declara grupos estado, absoluta, relativa; en .d se llama `ROS TOLERANCIAS`. Los estados omitidos usan las tolerancias globales. Se guarda el mapa completo en descriptor v4, de modo que el din sigue siendo autosuficiente. No confundir estas tolerancias temporales con ROS_ESCALAS. El informe `.informe` incluye `estadisticas ROS:` en JSON: pasos aceptados/rechazados, intentos, consultas a Calcula y estado de finalizacion; las consultas del campo y de OUTPUTS se cuentan por separado. Detalles en [ROS.md](ROS.md).

Para ROS tambien se puede elegir `ROS_JACOBIANO=[SENSIBILIDADES]` (en .d, `ROS JACOBIANO`): genera descriptor v5 y requiere un Calcula con capacidad SENS1. Este resuelve el punto y obtiene el Jacobiano reducido mediante las sensibilidades de los residuos, sin resolver otro punto por cada perturbacion. `ROS_ESCALAS_RESIDUO=[Z,1]` permite escalas de cualquier variable, incluidas algebraicas. Es optativo: los archivos anteriores mantienen campo_central. Funciona con paso fijo o adaptativo; sus fallos abortan sin cambio silencioso de proveedor. Limites y controles numericos en [ROS.md](ROS.md#sensibilidades-del-residuo-v5).

### Controles generales y Runge-Kutta

| qué | `.d` | `.ds` |
|---|---|---|
| hasta cuándo | `STOP TIME = 5`, o `T>5` | `TIEMPOFINAL=5` y `T<TIEMPOFINAL` en `CONTROL_EQUATIONS` |
| desde cuándo | `START TIME = 0` (0 si no) | `T=0` en `DEFINED_PARAMETERS` |
| otra parada | `TICK>1000`, `VC1>0.9` en `RUN CONTROLS` | `TICK<=1000`, `VC1<=0.9` en `CONTROL_EQUATIONS` (se sigue mientras vale) |
| método | `INTEGRATION ROUTINE = TRAP` (sólo `TRAP`, `IMPLICIT`, `XPO`, `RUK`) | `RK=[12]`, cualquiera de la tabla |
| varios métodos, rotando | no | `RK=[18,18,11]` |
| paso | `STARTING STEP SIZE = 0.05` | `STARTING STEP SIZE = 0.05` (desde r1890; también `MINIMUM` y `MAXIMUM`), o `SIRU_PASO=[0.05]` |
| semilla de calcula | `SEMILLA VL2 = 0.1` | `SEMILLA VL2 = 0.1`, o `VL2=0.1` en `VARIABLES` si es de una rama |
| paso adaptativo | `MINIMUM/MAXIMUM STEP SIZE`, `SIRU ABSOLUTE TOLERANCE`, `SIRU RELATIVE TOLERANCE`, `SIRU ERROR ORDER`, `SIRU ADAPTIVE STEP = 1` | `SIRU_PASO_MIN=[..]`, `_PASO_MAX`, `_ABSOLUTO`, `_RELATIVO`, `_ORDEN_ERROR`, `SIRU_ADAPT=[1]` |
| qué sale | `OUTPUTS` con `VC1, IR1` | igual, o `T:VC1,IR1` |

Hasta el 05/10/2026 los nombres que empiezan con `SIRU` empezaban con
`SIMUL2` (`SIMUL2_PASO=[0.05]`, `SIMUL2 ADAPTIVE STEP = 0`). Un circuito que
todavía los trae así corre igual: `d2datdinc` los lee como los de ahora, y
con `-v` lo avisa.

Un `.d` sin `INTEGRATION ROUTINE` usa 13, como SCEPTRE; un `.ds` sin `RK=`
usa Euler (0) y `siru` lo dice.

**El paso, si el archivo no lo dice**: la milésima parte del tiempo final, y
**adaptativo**, que es lo que hace SCEPTRE —el programa para el que se
escribieron estos archivos— cuando no le dan controles de paso. Hasta el
06/10/2026 era 0.1 fijo, un número que no tiene nada que ver con la escala de
cada circuito: con él los planetas (`STOP TIME = 0.1`) hacían toda la corrida
en un paso, `auditorio` (120 horas) pedía 4,3 millones de pasos y los rígidos
(λ = −25, escala 0.04) no convergían. Un paso fijo hay que pedirlo:
`SIRU ADAPTIVE STEP = 0` (`.d`) o `SIRU_ADAPT=[0]` (`.ds`), y conviene dar
también el paso, porque si no es esa misma milésima parte. **Cualquier control
de paso o de tolerancia prende la adaptación.** Con adaptación y sin
`MAXIMUM STEP SIZE`, el paso máximo es el de SCEPTRE: 2 % del tiempo final.
Los métodos de `rk.siru`:

| N | método | | N | método |
|---|---|---|---|---|
| 0 | Euler | | 10 | Bogacki–Shampine |
| 1 | Euler implícito | | 11 | Punto medio implícito |
| 2 | Punto medio explícito | | 12 | Lobatto IIIA (trapecio; `TRAP`) |
| 3 | Heun | | 13 | Lobatto IIIC, stiff (`IMPLICIT`, `XPO`) |
| 5 | Ralston | | 14 | Lobatto IIIC* |
| 6 | Kutta | | 15 | Runge–Kutta–Fehlberg |
| 7 | Runge–Kutta clásico (`RUK`) | | 16 | Cash–Karp |
| | | | 17 | Dormand–Prince |
| | | | 18 | Gauss–Legendre 6 |

Para un circuito con diodos o constantes de tiempo muy distintas, 12 o 13.
`RK=[-1]` pide resolver un punto sin integrar, incluso si el modelo tiene estados: se fijan en sus valores iniciales. Véase §12.

**`OUTPUTS`** puede pedir cualquier variable del circuito (`VC1`, `IR1`,
`QC1`, `IL1`, `VS1`), un estado propio, un parámetro, `T`; lo que no se
pueda traducir queda anotado con `-` y no impide simular. Sin `OUTPUTS`
salen los estados.

**Las semillas.** Calcula resuelve cada paso con Newton, y arranca de la
semilla de cada incógnita: la solución del paso anterior, o al principio el
valor del `.din`, que para las variables de las ramas es 0. Si el modelo no
se puede evaluar ahí --una bobina `L2 = 1/VL2` da 1/0 con `VL2 = 0`
(`circuito-T3.ds`)--, calcula falla antes de iterar. `SEMILLA VL2 = 0.1` en
`RUN CONTROLS` (un `.d` o un `.ds`, una línea por variable) le da otro punto
de partida; en un `.ds`, también un valor en `VARIABLES` para la tensión o la
corriente de una rama. No cambia el problema, sólo de dónde arranca Newton.
Si el nombre no es una incógnita (un parámetro, un estado), se avisa por la
salida de errores y se ignora.

**`T`, `DT` y `TICK`** los pone el simulador en cada paso: el tiempo, el
paso en uso y la cantidad de pasos dados. Lo que el archivo les ponga
(`CONSTANTS DT=0.01`) no fija nada.

## 11 bis. El punto de operación (`RUN INITIAL CONDITIONS`)

Como en SCEPTRE: con `RUN INITIAL CONDITIONS` en `RUN CONTROLS` (sólo en un
`.d`), siru calcula los estados en t = 0 con las derivadas en cero
—capacitores abiertos, bobinas en corto— partiendo de las condiciones
iniciales del archivo como semilla, y arranca la corrida desde ahí. Con `RUN
INITIAL CONDITIONS ONLY` escribe ese punto, una fila, y termina. Es lo que
SCEPTRE llama «initial conditions calculation» (manual 2.2.5, 2.2.7), y la
base de la sensibilidad, Monte Carlo y el peor caso (pendientes,
`PLAN_DC_RERUN.md`). Ejemplo: `siru/corpus/corpus/samples/a03.d`.

**Cuando Newton no llega al punto de operación** (varios transistores,
`exp(38·v)`), o cuando el archivo dice `RUN IC VIA IMPLICIT`, siru lo busca
**integrando**, como SCEPTRE: integra el circuito con el tiempo quieto en el
inicial, desde las condiciones iniciales del archivo, en tramos del largo de
la corrida (hasta 20), y desde el estado de cada tramo prueba Newton sobre el
sistema del punto de operación. En la pantalla: `punto DC por integracion:
tramo 1 (50 de tiempo), el estado se mueve 3.52 por tramo; Newton llega`. El
primer paso de la corrida, el instante de cada fila de salida y el
equilibrio arrancan Newton de esa solución y no de las semillas del archivo.
Ejemplo: `siru/corpus/corpus/samples/d04.d`.

## 11 ter. Sensibilidad (`RUN SENSITIVITY`)

Como en SCEPTRE (manual 2.2.8), sólo en un `.d`: bajo `CIRCUIT DESCRIPTION`
una subsección `SENSITIVITY` con conjuntos `(dependientes / independientes)`,
y `RUN SENSITIVITY` en `RUN CONTROLS`:

```
SENSITIVITY
   (VC1, PABC / R1, E1)
RUN CONTROLS
   RUN SENSITIVITY
```

siru calcula el punto de operación DC (11 bis) y, para cada dependiente y
cada independiente, la derivada parcial por diferencias centradas y la
normalizada `(dy/dx)(x/y)`, y las escribe en `NOMBRE.dc`, una tabla de texto.
Dependientes: `VC`, `IR`, `VR`, `IL`, `IE`, una fuente de corriente (su
corriente) o un parámetro definido (`PABC`). Independientes: un valor que sea
un dato —una resistencia, una fuente, un parámetro definido con un número—; una
rama que vale una expresión no lo es, y hay que pedir la derivada respecto de
sus parámetros. Ojo: la tabla de sensibilidad de SCEPTRE no coincide con sus
propias corridas (`PLAN_DC_RERUN.md`, paso 3); la de siru sí.

## 11 quater. Monte Carlo (`RUN MONTE CARLO`)

Como en SCEPTRE (manual 2.2.9), sólo en un `.d`: una subsección `MONTE
CARLO` con conjuntos `(dependientes / independientes)`, cotas en los
independientes —`R1 = 100 (90, 110)` mínimo y máximo, o `R1 = 100 (10)` en
porcentaje— y `RUN MONTE CARLO = n` en `RUN CONTROLS` (10 si no se da `n`):

```
MONTE CARLO
   (VC1, IR2 / R1, E1)
RUN CONTROLS
   RUN MONTE CARLO = 50
   INITIAL RANDOM NUMBER = 127263527
   DISTRIBUTION = UNIFORM
```

siru hace exactamente lo que SCEPTRE: `n` puntos de operación DC (11 bis) con
cada independiente en `media + sigma·g`, media `(max+min)/2`, sigma
`(max−min)/6`, `g` gaussiana (suma de 12 uniformes menos 6) o uniforme; el
generador es el de SCEPTRE (RANDU) con su semilla, así que con la misma
semilla salen las mismas muestras. Escribe en `NOMBRE.mc` una tabla con, por
independiente y dependiente, el nominal, el mínimo, el máximo, la media y el
sigma muestral (con `N−1`). Ojo, dos cosas heredadas de SCEPTRE: la
"uniforme" cae en `[media, media+sigma)`, no entre las cotas; y tras `RUN
MONTE CARLO` los OUTPUTS que lista SCEPTRE son los de la última muestra, los
de siru los nominales. `siru/corpus/sceptre` compara `NOMBRE.mc` con las
OBSERVED STATISTICS de SCEPTRE (`NOMBRE.sceptre-mc`); en a07 coinciden en
ocho cifras.

## 11 quinquies. Peor caso (`RUN WORST CASE`)

Como en SCEPTRE (manual 2.2.7), sólo en un `.d`: una subsección `WORST CASE`
con conjuntos `(dependientes / independientes)`, cotas en los independientes
(como en Monte Carlo) y `RUN WORST CASE` en `RUN CONTROLS`:

```
WORST CASE
   (VCET1, VCCT1 / P1T1, P1T2, R3)
RUN CONTROLS
   RUN WORST CASE
```

Por cada dependiente, siru calcula el gradiente respecto de los
independientes en el punto de operación nominal, lo normaliza y avanza por
esa recta hasta la primera cota que toca, a favor del gradiente (HIGH) y en
contra (LOW); en esos dos puntos resuelve el punto de operación. Es
exactamente el método de SCEPTRE (`worst.f`): no pone cada independiente en
su cota, sólo el más cercano llega a la suya. Escribe `NOMBRE.wc`: por
(dependiente, independiente) el nominal, HIGH y LOW del dependiente, y el
valor y el gradiente del independiente en los tres puntos; en comentarios,
la distancia y la cota que toca cada extremo. Ojo: los extremos de siru no
son los de SCEPTRE, porque el gradiente de SCEPTRE está mal (11 ter); en a09
los de siru son más extremos, y SCEPTRE corrido en los puntos de siru da lo
mismo que siru (`PLAN_DC_RERUN.md`, paso 5). Con `RUN WORST CASE = LOW |
HIGH` la corrida en el tiempo sigue con los valores de ese extremo del último
dependiente (y desde su punto de operación); con `NOMINAL`, por omisión, con
los nominales. Ojo: SCEPTRE integra con los valores contrarios (con HIGH deja
los LOW y al revés; `worst.f` calcula HIGH y después LOW y queda lo último):
siru hace lo que dice el manual.

## 11 sexies. Reruns (`RERUN DESCRIPTION`)

Como en SCEPTRE (manual 2.4), sólo en un `.d`: después de `RUN CONTROLS`,
`RERUN DESCRIPTION (N)` con las secciones que cambian y, en cada línea, los
N valores, uno por rerun:

```
RERUN DESCRIPTION (2)
ELEMENTS
   R1 = 9.3, 9.5
DEFINED PARAMETERS
   PF = 500, 600
INITIAL CONDITIONS
   VC1 = 1, 2
FUNCTIONS
   TABLE 1 = 0, 0, 0
            40, 1.2, .4
RUN CONTROLS
   STOP TIME = 600, 700
   INTEGRATION ROUTINE = XPO, RUK
   IC FOR RERUNS = PRECEDING RESULTS
```

siru corre la maestra y después cada rerun con sus cambios (elementos,
parámetros, condiciones iniciales, tablas, `STOP TIME`, `START TIME`,
`INTEGRATION ROUTINE`; lo demás se avisa y no se aplica). Cada rerun parte
de las condiciones iniciales del archivo (`IC FOR RERUNS = MASTER RESULTS`,
por omisión) o del estado final de la corrida anterior (`PRECEDING
RESULTS`). Todo va al mismo `NOMBRE.simul`, un bloque por corrida con su
cabecera y una línea `#  RERUN k/N (R1 = 9.3, STOP TIME = 600)` (0 es la
maestra). Ojo: `interpola` y tabular leen el archivo entero como una tabla;
para una corrida sola hay que cortar su bloque (empieza en `#   Archivo`).
`siru/corpus/sceptre` compara bloque por bloque; a03 y tanque_par_1 coinciden con
SCEPTRE (`PLAN_DC_RERUN.md`, paso 6).

## 11 septies. Seguir una corrida (`CONTINUE`, `--continuar`)

Una corrida terminada se sigue desde donde quedó:

```
siru --continuar --hasta 40 circuito.d
```

siru lee el estado final que dejó en `circuito.informe` (`estado final: t=20
qc1=6.87`; si no está, la corrida no terminó bien y hay que correrla de
nuevo), integra de ahí hasta 40 y anexa el bloque `#  CONTINUE (desde t = 20,
STOP TIME = 40)` al mismo `circuito.simul`; el `.informe` queda con el nuevo
estado final, así que se puede volver a continuar. No se recalcula el punto
de operación ni las opciones DC; los valores que dejó el peor caso (`WORST
CASE = LOW | HIGH`) se heredan. En un `.d`, la sección `CONTINUE` de SCEPTRE
(manual 2.5) con sus `RUN CONTROLS` (`STOP TIME = 2000`) hace lo mismo al
final de la corrida, siguiendo la última (la maestra o el último rerun):

```
CONTINUE
RUN CONTROLS
   STOP TIME = 2000
```

## 11 octies. El análisis en frecuencia (`RUN AC`)

`RUN AC` (manual de SCEPTRE 2.2.4) no integra en el tiempo: linealiza el
circuito alrededor de su punto de operación y resuelve en frecuencia. Es un
análisis de pequeña señal.

```
ELEMENTS
  EDC, GND-1 = 0.6                 la polarización, como siempre
  EAC, 1-2 = (0.001, 0.)           la excitación: magnitud y fase en grados
  ...
RUN CONTROLS
  RUN AC
  INITIAL FREQUENCY = 100
  FINAL FREQUENCY = 10000
  NUMBER FREQUENCY STEPS = 2       dos intervalos, o sea tres frecuencias
  TYPE FREQUENCY RUN = LOG         o LINEAR (el que se toma si no se dice)
```

Una fuente que vale `(magnitud, fase)` es la excitación. En el tiempo vale
0: es una perturbación, no una fuente de la corrida. Pueden ser varias, y
sus respuestas se suman.

**El punto de operación se calcula siempre**, aunque el archivo no diga
`RUN INITIAL CONDITIONS`: el análisis es alrededor de él. Si el archivo no
trae `STOP TIME`, el análisis es toda la corrida y no hay nada en el
tiempo, como con `RUN INITIAL CONDITIONS ONLY`.

**La salida es `NOMBRE.ac`**: una fila por frecuencia y, por cada variable
de `OUTPUTS`, cuatro columnas, su parte real, su imaginaria, su módulo y su
fase en grados. El encabezado dice la excitación y los estados.

**Cómo lo hace** (`Siru::AC`): el circuito es `dq/dt = f(q, u)` con las
salidas `y = g(q, u)`, y hace falta `A = df/dq`, `B = df/du`, `C = dg/dq` y
`D = dg/du`. Esas derivadas salen por diferencias centradas del sistema del
punto de operación, el que tiene las derivadas como datos y los estados
como incógnitas: una corrida por cada dato que se mueve. No se deriva
ninguna expresión. Después, para cada frecuencia, `(sI − A) X = B U` y
`Y = C X + D U`, con `s = j·2πf` y `U` el fasor; el sistema complejo se
resuelve por eliminación gaussiana con pivoteo.

No se usa el sistema del instante, el que tiene los estados como datos,
porque en un circuito con transistores no converge: dada la carga de una
juntura, hallar su tensión es el problema difícil.

**Probado** (`tests/ac.t`, los circuitos en
`corpus/corpus/inventados/ac-*.d`): un pasabajos RC coincide con su
transferencia `1/(1 + jωRC)` en 6·10⁻¹¹, y un diodo polarizado, que no
tiene fórmula, coincide con la corrida en el tiempo del mismo circuito
excitado con `0.001·sin(2πft)` en 2,5·10⁻⁵, que es el error del paso.

**Lo que no hace:**

| | |
|---|---|
| `PRINT EIGENVALUES`, `PRINT EIGENVECTORS` | los valores propios del circuito linealizado; se avisa |
| `COMPLEX`, `NYQUIST`, `DEGREES`, `RADIANS` en `OUTPUTS` | el formato de columnas de SCEPTRE; el `.ac` trae siempre las cuatro |
| `(10,4), COMPLEX` en el valor | el fasor dado por parte real e imaginaria en lugar de magnitud y fase |
| parámetros y funciones complejas | lo que prueba `complex.d`: aritmética compleja en las expresiones |

## 11 nonies. Relajación de forma de onda (`siru-relaja`)

`siru-relaja` simula un circuito **partido en subcircuitos, cada uno en su
proceso**, por relajación de forma de onda (*waveform relaxation*;
Lelarasmee, Ruehli y Sangiovanni-Vincentelli, 1982). Es el camino para
repartir una simulación entre varios procesos, o máquinas, cuando el
circuito es grande. Pedido del autor, 06/10/2026; el porqué está en
`calcula-6.0/PENDIENTE-iterativos.md`, método 4.

```sh
perl bin/siru-relaja --partes 4 --ventana 5 grilla-rc-10x10.d
perl bin/siru-relaja --partes 3 relaja-cadena.d
```

**Cómo parte.** Los nodos (sin la masa) se reparten en `--partes` partes por
bisección del grafo de ramas, o como diga `--particion ARCHIVO` (una línea
`NODO PARTE` por nodo, las partes desde 1). Cada rama va a la parte de sus
nodos. Una rama **cortada**, que une un nodo de una parte con uno de otra, va
a las dos: en cada una, el nodo ajeno se maneja con una fuente de tensión
`EWR<nodo>` cuyo valor es una `TABLE` con la forma de onda de ese nodo de la
iteración anterior. Así cada nodo tiene su ley de corrientes completa en su
parte. La parte dueña de cada nodo de frontera lo mide con una fuente de
corriente de cero, `JWR<nodo>`, que no cambia el circuito.

**Cómo itera.** En cada iteración las partes corren a la vez (`--procesos`,
por omisión una por parte), cada una con las formas de onda de la iteración
anterior (Jacobi), y se para cuando ninguna forma de onda de frontera cambia
más que `--tolerancia` (1e-6, relativo a la mayor). La primera vez las
fronteras valen 0. Con `--ventana T` el tiempo se parte en ventanas: cada una
se itera hasta converger, y la siguiente arranca de los estados finales de
cada parte (los del `.informe`). `--iter-max` (50) es el tope por ventana;
`--charla` dice cada iteración.

**Qué deja.** En `NOMBRE-relajacion/`, al lado del archivo: el `.d` de cada
parte (`parte01.d`...) con su `.csv`, `.informe` y `.log` de la última
iteración, y `NOMBRE.csv` con las salidas pedidas de todas las partes
(`--salida` para otro nombre).

**Cuándo converge rápido.** Cuando las partes están débilmente acopladas: la
cadena RC de `corpus/corpus/inventados/relaja-cadena.d` (las etapas unidas
por una resistencia veinte veces la de entrada) converge en 9 iteraciones con
una sola ventana, y la de diodos (`relaja-diodos.d`, no lineal) en 18. Con
acoplamiento fuerte hacen falta ventanas cortas: la grilla RC de 10×10
(`grilla-rc-10x10.d`) en cuatro partes y ventanas de 5 converge en 15 a 24
iteraciones por ventana. En los tres casos da la curva de la corrida entera:
3e-9, 2e-9 y 6e-6 de diferencia como mucho.

**Lo que cuesta.** Cada iteración corre las partes enteras otra vez, así que
en una sola máquina tarda más que la corrida directa (la grilla: 296 s contra
8 s; la cadena: 43 contra 3,5). Lo que da es que cada parte es un proceso
independiente, que podría ir a otra máquina, y que solo se intercambian las
formas de onda de la frontera.

**Lo que no hace todavía.** Solo `.d` (SCEPTRE), con ramas de dos nodos (no
`MODEL DESCRIPTION`). Una rama que nombra en su valor a otra (una fuente
controlada, `VC1` en el valor de `R2`) tiene que quedar en la misma parte: si
la partición las separa, se avisa y no se corre; con `--particion` se las
junta. El reparto es entre procesos de la misma máquina; a otras máquinas,
falta.

## 11 decies. GPU: muchas copias de un circuito (`ejemplos/montecarlo-gpu`)

siru y calcula resuelven un circuito paso a paso, y eso **no** le conviene a
una GPU: cada paso depende del anterior, la matriz es dispersa e irregular y
una consulta dura alrededor de un milisegundo. `siru-relaja` (11 nonies)
tampoco, porque son pocas tareas grandes. Lo que le conviene a una GPU es
**muchas copias independientes de un circuito chico**: Montecarlo, barridos
de parámetros, peor caso.

`ejemplos/montecarlo-gpu` es ese caso, listo para probar:

- `rectificador.d` es el circuito (un rectificador de media onda), que siru
  simula como cualquier otro.
- `montecarlo.c` simula 100.000 copias con los componentes dentro de su
  tolerancia, con el mismo método que siru. Es un solo fuente con OpenMP,
  sin CUDA: corre en el procesador (100.000 copias en 6,2 s con 16 hilos),
  en una GPU NVIDIA (`make gpu`) o AMD (`make gpu-amd`). Su copia nominal da
  la curva de siru (`make compara`).
- Para probarlo en una GPU sin comprarla: `cuaderno-gpu.ipynb` se sube a
  Kaggle (P100) o Colab (T4) y se corre entero; en una máquina alquilada,
  `make paquete` y `./prueba-gpu.sh`, que detecta la placa, compila, corre
  obligando a usar la GPU y compara con el procesador.

`INFORME-GPU.md`, en el mismo directorio, tiene las conclusiones: que la
doble precisión decide la placa (solo las de cálculo científico la tienen
rápida), que no hace falta CUDA, qué sirve también para IA, precios del
06/10/2026, y los pasos para usar una GPU gratis o alquilada. No se probó
todavía en una GPU real.

## 12. Un punto, sin integración: RK=[-1]

El `.ds` contiene la información necesaria para Calcula: incógnitas, semillas, datos conocidos, constantes, funciones y ecuaciones; si hay circuito, también sus ramas y modelos. Con `RK=[-1]`, **Siru construye el sistema, llama a Calcula para resolverlo una vez y pone sus resultados en `.simul`**. No hace falta escribir un `.din` aparte, preparar una simulación temporal ni declarar un tiempo ficticio.

| Aspecto | Comportamiento con `RK=[-1]` |
|---|---|
| Lugar y escritura | En `RUN_CONTROLS` o `RUN CONTROLS`, una línea `RK=[-1]`. Es sintaxis `.ds`; no escribir `KR`, `rk=-1` ni quitar los corchetes. |
| Resolución | Una evaluación del sistema de Calcula. Calcula puede hacer las iteraciones internas necesarias para converger. |
| Incógnitas | Los valores de `VARIABLES` son semillas. Los valores finales salen de resolver las ecuaciones. |
| Datos | Los parámetros numéricos quedan fijados en los valores dados. |
| Estados | Si existen, se fijan en sus valores iniciales; se pueden calcular sus derivadas, pero no se avanza en el tiempo. |
| Controles temporales | No se hace un barrido aunque permanezcan `T`, `TIEMPOFINAL`, un paso o condiciones de continuación en el archivo. |
| Otros análisis | Este modo no lanza los barridos/análisis DC adicionales. Para esos análisis usar el modo y los controles de §11 bis–quinquies. |
| Salida | Sin RESULTADOS, una fila con todas las variables del sistema, incluidos sus datos. RESULTADOS selecciona y ordena columnas; OUTPUTS no recorta esta tabla de un punto. |
| Precisión | Los números se copian tal como los devuelve el protocolo de Calcula, sin un segundo redondeo ni sustituir resultados ausentes por cero. Algunos números pueden mostrar muchos decimales: ésa puede ser la escritura que entrega Calcula. |
| Nombres | Se usan los nombres del sistema generado, habitualmente en minúsculas. Si `OUTPUTS` conserva nombres originales distintos, se agrega la fila `sceptrevars` para que Simplot pueda relacionarlos con la fuente. No se duplica una fila de nombres idénticos. |
| Unidades | Se escribe la fila de unidades sólo cuando el sistema declara alguna. Para columnas sin unidad, dentro de esa fila, se usa `-`; si ninguna tiene unidad, se omite toda la fila. No se inventan `??`, magnitudes ni unidades a partir del nombre de una variable. |
| Dibujo | Si hay ramas y una sección `FORMAS`, incluso vacía, también se solicita el SVG al paquete opcional `dibuja`. |
| Limitación | El modo de un punto con llaves `SB` se rechaza: requiere un tratamiento de las conmutaciones que este modo no implementa. |

Para entradas `.d`, `.ds` o `.dat`, Siru conserva las unidades y magnitudes conocidas en `GLOBAL.unidades_calculo`. Las conversiones pedidas por RESULTADOS se aplican antes de publicar, mediante Tabular2, que exporta con 17 cifras significativas. Un `.din` independiente conserva sus propias unidades declaradas; no se inventan magnitudes desconocidas ni se toma tempC como origen de un valor que ya está en K.

Ejemplo completo, autosuficiente, sin bibliotecas externas, circuito ni tiempo. Guardarlo como `punto.ds`:

```text
CIRCUIT_DESCRIPTION
Un dato y una incognita: X al cuadrado es A.
DEFINED_PARAMETERS
A=4
VARIABLES
X=1
EQUATIONS
X^2-A
OUTPUTS
X,A
RUN_CONTROLS
RK=[-1]
END
```

Al ejecutar `siru punto.ds`, Calcula resuelve `X^2=A` desde la semilla `X=1` y entrega la raíz positiva, aproximadamente 2. `punto.simul` contiene los nombres `a`, `x`, sus nombres originales `A`, `X` y una fila numérica con 4 y el resultado de Calcula. No hay columna de tiempo ni fila de unidades porque no se declararon. Si ya existe la salida, Siru conserva la anterior y usa `punto-1.simul`, luego `punto-2.simul`, etc.

La llamada automática a Simplot descrita en §1 puede presentar la tabla al terminar. Para ejecutar exclusivamente el cálculo y escribir la tabla, usar `siru -nosimplot punto.ds`; esto no cambia el significado de `RK=[-1]` ni sus resultados.

Un ejemplo real es [psicro_r_srk.ds](../corpus/corpus/lolipmp/psicro_r_srk.ds), copia de `psicro_5.ds` con `RK=[-1]` explícito. `TEMP`, `TBH` y `PRES` son datos; `XXX`, `WWW` y `PV` son incógnitas. Sus ecuaciones y funciones están en el mismo archivo. Se ejecuta desde la raíz del repositorio con:

```sh
siru siru/corpus/corpus/lolipmp/psicro_r_srk.ds
```

**Sin `RK=[-1]`:** un circuito sin estados y sin corrida temporal también se resuelve automáticamente en un punto, como `slex/d2datdinc/pruebas/nulo.d`. Si no tiene estados pero sí pide una corrida temporal, Siru puede resolver un punto por cada `T` de la grilla, usando la solución anterior como semilla del siguiente. Eso es un barrido y produce varias filas; no es el modo explícito `RK=[-1]`.

## 13. Los mensajes, y qué hacer

**Dónde aparece un error.** En tres lugares, del más corto al más completo:

1. En la pantalla, la última línea: `siru: NO CONVERGE en t=78.3 (paso
   783)`, o `siru: no arranca en t=0 (paso 0)`.
2. En `NOMBRE.informe`: `resultado:`, `hasta:` (el paso y el tiempo, contra
   el `TIEMPOFINAL`) y `error:`, la primera línea del motivo.
3. En `NOMBRE.log`: todo, sin cortar. Cada línea dice quién la escribió:
   `d2datdinc: ...` (la traducción), `calcula: ...` (el sistema y el
   solver, y adentro de él tabular2 y las funciones externas) y al final
   `siru: resultado: ...`.

`siru/corpus/regenera` deja el mismo `NOMBRE.log` al lado de cada fuente, con lo
que dicen `d2datdinc` y `clasificar` al regenerarla, y `siru/corpus/clasificar`
copia el resultado de `NOMBRE.informe` al `.tipo` (campo `siru`).

**Los resultados** del `.informe`:

| resultado | qué quiere decir |
|---|---|
| `llega al final` | integró hasta `TIEMPOFINAL` (o resolvió el punto) |
| `NO CONVERGE` | arrancó y en algún paso posterior al primero el solver no encontró solución; el `hasta:` dice dónde. Las filas hasta ahí son válidas |
| `no arranca` | falló antes de integrar o en el paso 0: la traducción, la carga del sistema, o el solver en el punto inicial |

**Las tablas.** Un error de `tabla()` lo detecta tabular2 y lo informa
`calcula`, con el código, su motivo y el dato concreto:

```
calcula: tabla('jx','X','Y'): error -3 de tabular2: columnas mal especificadas:
la columna de abscisas (la X) tiene que crecer de una fila a la siguiente
(puede repetirse, no bajar), y las columnas pedidas tienen que existir
(la columna 0 de la tabla (la X) baja en la linea de datos 3: 1 despues de 2)
```

Los códigos de tabular2 (`tabular2/src/table.h`; el motivo lo da
`tb_mensaje()`, el dato `tb_detalle()`, desde tabular2 2.0-10):

| código | motivo | qué hacer |
|---|---|---|
| −1 | no se pudo leer la tabla: no tiene el formato de tabular2 | una línea `#` con los nombres de columna y después los datos |
| −2 | no se pudo abrir el archivo | ver la ruta: una relativa se busca junto al archivo que la usa |
| −3 | la X no crece de una fila a la siguiente, o las columnas pedidas no existen | ordenar la tabla; el detalle dice en qué línea baja |
| −4 | la misma columna como X y como Y | pedir dos columnas distintas |
| −5 | datos no válidos (algo que no es número, un número de tabla repetido en `etable.txt`) | corregir el dato |
| −6 | argumento no válido (método distinto de 1 o 2, un número negativo) | método 1 lineal o 2 spline |
| −7 | la tabla no tiene datos en esas columnas | revisar la tabla |
| −8 | error de entrada o salida | el disco, los permisos |
| −15 | sin memoria | |
| −888 | no hay una columna con ese nombre en el encabezado | usar el nombre de la línea `#` |
| −889 | no se pudieron relacionar esas dos columnas | la X no crece, o faltan datos |

**Las funciones externas** (`EXTERNAL NOMBRE (N) RUTA`, sección 9):

| mensaje | qué pasa | qué hacer |
|---|---|---|
| `el .din declara funciones externas (codigo de afuera); calcula las carga solo con CALCULA_EXTERNAS=1` | calcula no carga código de afuera sin permiso | `CALCULA_EXTERNAS=1` en el entorno |
| `gfortran no pudo compilar RUTA` | el fuente tiene un error, o no hay `gfortran` | compilarlo a mano: `gfortran -shared -fPIC RUTA` |
| `la biblioteca de RUTA no tiene la funcion nombre_` | el nombre del `EXTERNAL` no es el de la `FUNCTION` | que coincidan |
| `NOMBRE pide N argumentos y le dieron M` | la cantidad del `EXTERNAL` no es la del llamado | corregir la `N` |

**Los demás:**

| mensaje | qué pasa | qué hacer |
|---|---|---|
| `PENDIENTE ...` | algo del archivo no se pudo traducir; no se simula | mirar qué es: un símbolo que no está, un control que Simul no tiene |
| `tipo X de R: no tiene modelo en el dato` | un `.ds` sin `include` de la biblioteca, o un tipo que no existe | agregar el `include` o declarar el tipo |
| `modelo por incrementos` | `L` o `C` de la biblioteca vieja | usar `LB`, `CB` |
| `valor de R: no se puede traducir el simbolo X` | `X` no es rama, parámetro, función ni `T` | declararlo |
| `la funcion F no esta en el dato` | se usa sin definir en `FUNCTIONS` | definirla |
| `grados de libertad ...` | las ecuaciones propias no cierran con sus incógnitas | una `VARIABLE` por ecuación, o una ecuación menos |
| `aviso: los nodos 1 2 no tienen camino a la masa (GND)` | una parte del circuito no toca la masa (una isla) | no es un error: en la isla un nodo va a potencial cero, de referencia; las tensiones y corrientes de las ramas no cambian. Si es un descuido, conectarla |
| `los datos dados no determinan el sistema: ... incognitas que ninguna ecuacion libre siente` | Calcula: hay una incógnita que ninguna ecuación fija (un nodo que flota, una ecuación repetida) | mirar las que nombra |
| `no se obtuvo una solucion valida ... not making progress` | Newton no converge desde la semilla (diodos con mucha ganancia) | semillas razonables en `DEFINED PARAMETERS`; método 13 |
| `la capacidad de C depende de X: en un lazo de capacitores` | en un lazo sólo puede depender del voltaje de capacitores (el propio u otro) o de una fuente en paralelo con uno (sección 7) | sacar el lazo o la dependencia |
| `vale (1,0), un fasor: es el analisis AC de SCEPTRE` | Diagnóstico de versiones antiguas sin AC | Actualizar y usar el análisis AC de §11 octies; respetar sus límites. |
| `se usa el metodo 0` | un `.ds` sin `RK=` | `RK=[12]` |
| para en `TICK<=1000` | la parada que puso el archivo | es lo pedido |

## 14. Lo que no se puede

- `OPTIMIZATION` no se implementa. `RUN AC` sí está implementado con las restricciones de §11 octies; no implica admitir aritmética compleja general en las expresiones.
- Los controles de error de SCEPTRE (`MINIMUM/MAXIMUM ABSOLUTE/RELATIVE
  ERROR`): no son las tolerancias de siru.
- Los modelos por incrementos de la biblioteca vieja (`L`, `C`, `R`, `E`
  sin `B`).
- Tablas con `x` no creciente.
- Expresiones en FORTRAN o con multiplicación sobreentendida: van en ExprTk.
- Un salto de corriente en una bobina acoplada por `M` no se transmite a la
  otra si no está en un corte.
- Varias ramas con el mismo nombre.
