Tabular 2
=========

Lee tablas de texto ASCII, arma vistas sobre pares de columnas e interpola,
lineal o por spline. Se usa desde Siru, Simusol y Sceptre, y desde ExprTk
como la funcion Table() dentro de una expresion.

Funciones tipicas y procesamiento numerico (plan, 2026-10-08)
----------------------------------------------------------

El procesamiento numerico general requerido por Simplot se agregara a
Tabular2, no a un motor numerico duplicado en el graficador. El catalogo de
funciones tipicas, con estado actual y pendientes, esta en
doc/PLAN-PROCESAMIENTO-NUMERICO.md: carga/vistas, interpolacion, inversa,
remuestreo, filtros, algebra de columnas, estadistica, histogramas,
ventanas, derivadas, integrales, ajustes y FFT.

Hay que reutilizar lo existente: interpola ya tiene inversa, avg, rms,
integ y saltos; algunas operaciones viven en la consola y requieren una
interfaz reutilizable. El catalogo no anuncia que todas esten hechas.
Simplot conservara la presentacion y consumira los resultados de Tabular2.
La decision de mantenimiento de abajo queda ampliada por esta necesidad
concreta; este cambio solo documenta el plan, no modifica codigo ni API.

Fechas y horas
--------------

Las fechas completas se cargan como segundos epoch POSIX desde 1970-01-01T00:00:00Z, conservando hora y fracciones. Sin zona explicita se usa la de la maquina (o TZ); Z y los desplazamientos +/-HH:MM o +/-HHMM prevalecen sobre ella. No se cambia la zona del proceso. Una fecha sola usa medianoche; una hora sin fecha es una duracion en segundos desde medianoche.

Los cambios de horario estacional siguen la biblioteca del sistema: se rechazan horas locales inexistentes; para desambiguar una hora repetida se debe indicar un desplazamiento explicito. No se admiten segundos intercalares. Las marcas deben ocupar una sola celda: ISO con T en texto separado por blancos, o fecha y hora con blanco dentro de una celda CSV. Las celdas numericas comunes no cambian.

Esto requiere libmem-util0 2017.2.1.R1008-2 o posterior, que sale del proyecto summapack (siru/summapack). Con la anterior, 1.0-2 (de siru/mem_util, con la que se publico Tabular 2.0-13), las fechas pierden la hora y dan dias desde 2000-01-01. Las referencias del corpus usan TZ=UTC0 solo durante las pruebas.

Pendiente: texto y vecinos numericos
-----------------------------------

- Conservar texto en celdas junto con los datos numericos; la representacion interna ya distingue cadenas, pero falta completar la politica de tablas mixtas.
- Al interpolar, saltar las celdas no numericas y buscar, en la misma columna, los valores numericos validos anterior y posterior, usando sus abscisas originales cuando ambos existan. No convertir texto a cero ni a un prefijo numerico.
- No sobrescribir la celda de texto con el resultado de la consulta.
- Definir que hacer cuando falta uno o ambos vecinos, con una abscisa no numerica, con discontinuidades o con spline; distinguir interpolacion de extrapolacion.
- Agregar casos con huecos consecutivos, texto en extremos y columnas enteramente textuales. Este comportamiento es un pendiente, no una capacidad implementada.

Alcance y prioridad de desarrollo (decision del autor, 2026-10-06)
---------------------------------------------------------------

- Para las necesidades actuales, Tabular se considera funcionalmente completo y pasa a mantenimiento: correcciones, pruebas, documentacion y compatibilidad. Las capacidades nuevas se incorporaran cuando exista una necesidad concreta.
- La prioridad de desarrollo esta en el generador de modelos y Calcula: generacion conjunta de .din y fuente C, seleccion del evaluador al ejecutar y preparacion para mallas grandes con evaluacion por bloques y resolucion paralela.
- Tabular ya dispone de contextos independientes por hilo (tb_ctx_nuevo, tb_ctx_usar, tb_ctx_liberar). Un mismo contexto no debe usarse simultaneamente desde varios hilos.
- Solo se estudiara compartir tablas inmutables entre hilos si las mediciones muestran que la duplicacion de tablas por contexto representa un costo relevante de memoria o rendimiento.

Es la tercera generacion del mismo programa:

  viejos/interpola/  la primera, biblioteca simusol_table.
  viejos/tabular/    la segunda, con la biblioteca renombrada a tabular y
                     empaquetado Debian y RPM.
  tabular2/          esta, con autotools y la biblioteca partida en modulos:
                     cell, header, lines, sequence, table, tables, view,
                     gnuplot, interpolate (con su encabezado interpola.h)
                     y manual_lexer.

Esta es la linea viva. Las otras dos quedan guardadas en viejos/, adentro de
este mismo paquete, solo como referencia: no se compilan, no se instalan y
no forman parte de SUBDIRS. En viejos/ hay ademas:

  viejos/viejotabular/   otra copia anterior.
  viejos/ejemplo-01/     el ejemplo que recorria toda la API con un
                         analizador de flex y bison; compila contra el arbol
                         de fuentes pero no se sabe que entrada espera. Su
                         LEAME cuenta de donde salio y que se averiguo.
  viejos/flex/           el banco de pruebas de abril de 2024 donde se
                         compararon tres formas de leer una tabla: una
                         gramatica con flex y bison, un lexer escrito a mano
                         y expresiones regulares con PCRE2. Gano el escrito a
                         mano, y de ahi salio src/manual_lexer.c. Su LEAME
                         dice que compila y que no.


Instalar
--------

Con summapack (ver siru/summapack/PAQUETES.md), en Ubuntu 22.04 o 24.04,
Debian 12 o 13, Fedora, openSUSE Tumbleweed o Arch (amd64; los paquetes se
compilan en Ubuntu 22.04 y andan con glibc 2.35 o posterior), en una
terminal:

    curl -fsSLO https://www.simusol.org/downloads/installsumma.sh
    sh installsumma.sh tabular2

Pregunta que bajar (fuentes, para compilar en la maquina; deb para Ubuntu
y Debian; rpm para Fedora y openSUSE; tgz para Arch y las demas; o --para
fuentes de antemano) e instala summapack y Tabular con lo que necesita:
las bibliotecas de Simusol que falten (simusol-readline y simusol-pcre2
incluidas) y gnuplot (recomendado por el paquete tabular).
Solo el programa: sh installsumma.sh tabular2/tabular. Con summapack ya
instalado: bajar-paquetes --instalar tabular2.

Paquetes y fuentes: los paquetes (deb, rpm, tgz) se compilan en Ubuntu
22.04 con sus propias bibliotecas (readline, pcre2, gsl... en
/usr/lib/simusol: los paquetes simusol-* del proyecto comunes), y andan en
las distribuciones con glibc 2.35 o posterior. Compilar en cada
distribucion obligaba a recompilar cada seis meses (Fedora) o siempre
(Tumbleweed, Arch); asi se evita. Si en alguna maquina no andan, o se
quiere lo mas nuevo, se instala desde las fuentes (--para fuentes), que se
compilan con lo que esa maquina tiene.

Dependencias directas de los paquetes 2.0-15 para amd64 (compilados en Ubuntu 22.04):

| Paquete | Dependencias obligatorias |
| --- | --- |
| libtabular2-0 | libmem-util0 >= 2017.2.1.R1008-2, libslex0 >= 1.0-24, libunidades0 >= 1.0, libc6 >= 2.22 |
| libtabular2-dev | libtabular2-0 = 2.0-15, libmem-util-dev >= 2017.2.1.R1008-2, libslex-dev, libunidades-dev |
| tabular (interpola y sceptre-tabular) | libtabular2-0 >= 2.0, libslex0 >= 1.0-24, simusol-readline >= 1.0-1, libc6 >= 2.34, python3 |

libslex0 pide simusol-pcre2.

- Para graficar se recomienda gnuplot.
- Para ejecutar SCEPTRE mediante sceptre-tabular hacen falta SCEPTRE compilado y gfortran; no hacen falta para convertir resultados binarios existentes.
- Tabular no depende de Calcula ni de Siru: esos programas utilizan Tabular.

Armar los paquetes
------------------

Todo lo del paquete (dependencias, opciones, que va en cada uno) esta en
tabular2.summa; en debian/ queda solo el changelog. Se arman con
./hacer-pkg, que deja tar.gz, deb, tgz, rpm y sus md5 (ver
siru/summapack/PAQUETES.md). Hacen falta build-essential, debhelper-compat
(= 13), autoconf, automake, libtool, pkg-config, bison, python3,
libmem-util-dev, libslex-dev, libunidades-dev, libexprtk-dev y
libreadline-dev (los que falten, hacer-pkg los dice antes de compilar).
mem_util provee mem.h, time_mem.h, pathconv.h y libmem_util.

Compilar desde las fuentes
--------------------------

    autoreconf --install
    ./configure
    make
    sudo make install
    sudo ldconfig

Si mem_util esta instalado en un prefijo propio, hay que indicarselo:

    export PKG_CONFIG_PATH=/home/usuario/svn/siru/local/lib/pkgconfig
    ./configure CPPFLAGS="$(pkg-config --cflags mem_util)" \
                LDFLAGS="$(pkg-config --libs-only-L mem_util)"

El programa de prueba con ExprTk se construye aparte:

    cd src/exprtk && make && ./exprtk_table

exprtk ya no viene adentro del paquete: es libexprtk-dev, que instala la
unica cabecera en /usr/include y su archivo pkg-config. Tener una copia
propia obligaba a mantenerla al dia a mano en cada proyecto que la usa.


Pruebas
-------

    make check

Corre cinco programas, 232 comprobaciones en total, sobre tests/datos.txt, y
t_terminal.sh, 24 casos del programa interpola:

  t_tablas      ciclo de vida: crear, reusar la ya cargada, consultar,
                liberar y volver a cargar.
  t_vistas      busqueda de columnas por encabezado y armado de vistas,
                por nombre y por numero, tambien con la abscisa en una
                columna que no es la primera.
  t_interpola   interpolacion: en los nodos el valor tabulado exacto, entre
                nodos la recta calculada a mano, la spline, las tres formas
                de indicar el archivo y las columnas.
  t_saltos      discontinuidades: valor a izquierda, a derecha y en el
                punto; varios saltos; salto en el borde; tabla de dos
                columnas; abscisas decrecientes o desordenadas.
  t_errores     los caminos de error, uno por uno. Que este programa llegue
                al final es parte de lo que prueba: ninguna funcion debe
                terminar el proceso.
  t_terminal.sh el programa interpola, orden por orden, con -e.

Los ejemplos de examples/ se arman aparte, con

    make -C examples

y enlazan contra el arbol de fuentes, no contra la biblioteca instalada: un
cambio en src/ se nota en ellos sin hacer make install. El 02 interpola la
salida de una simulacion de Simusol y el 04 evalua Table() en una expresion de
ExprTk; los dos corren con make -C examples prueba.


Para medir fugas hay que pasar por el wrapper de libtool, si no se toma la
biblioteca instalada en vez de la recien compilada:

    cd tests && ../libtool --mode=execute valgrind --leak-check=full ./t_errores

Los cinco programas dan 0 bytes perdidos y 0 errores.


El programa interpola
---------------------

src/interpola (fuente src/interpola.c; paquete Debian tabular) es la
biblioteca como una terminal de comandos, con readline (historial en
~/.interpola_historial y completado de ordenes, archivos y nombres de
columna), al estilo de calcula. Lleva el nombre de la primera generacion
de esta biblioteca. La descripcion completa esta en su pagina de manual,
src/interpola.1 (man interpola una vez instalado):

    interpola [ARCHIVO...] [-e 'orden; orden'] [-f GUION]

Sin -e ni -f, y con la entrada en una terminal, pregunta; con la entrada
redirigida, lee las ordenes de ahi. Sin entrar a la terminal:

    interpola datos.txt -e 'metodo spline; saltos 0 1200 100 > fina.txt'
    interpola -f hacer.txt          (un archivo de ordenes, # comenta)
    echo 'UNO=450' | interpola datos.txt

Con -e van comillas simples: sin ellas el shell se queda con el ; y el >.
El estado de salida es 0 si todas las ordenes anduvieron, 1 si no.
En la terminal, Tab completa (una vez,
si hay una sola posibilidad) y Tab Tab lista todas las posibilidades:
archivos y directorios despues de leer, cd, dir o '>' (leer + Tab Tab
muestra los del directorio actual), y ordenes y nombres de columna en los
demas lugares. Las ordenes (varias en una linea con
';', # empieza un comentario):

  leer ARCHIVO              carga una tabla y la deja como actual
  tablas, usar N            las tablas cargadas; cambiar la actual
  columnas                  numero, nombre, minimo, maximo y orden de cada una
  mostrar [DESDE [HASTA]]   las filas
  x COLUMNA                 la columna de abscisas (nombre o numero desde 1)
  metodo lineal|spline      el metodo de interpolacion
  interpolar V [COL...]     las columnas en x = V
  COL=V                     lo mismo tomando COL como abscisa (x=V: la actual);
                            si COL no es creciente, busca al reves
  inversa COL V             los x donde COL vale V (todos, por tramos)
  avg A B [COL...]          el valor medio de cada columna entre x = A y x = B
  rms A B [COL...]          la raiz del valor medio cuadratico entre x = A y B
  integ A B [COL...]        la integral de cada columna entre x = A y x = B
  saltos A B P [COL...]     la tabla de x = A a B con paso P
  graficar [COL...]         gnuplot: los puntos y la curva interpolada
  cd [DIR], dir [DIR], pwd  cambiar de directorio, ver que hay, donde estoy
  ayuda                     (muestra tambien la ruta de las tablas de ejemplo)
  salir, q, quit, fin       termina

Los nombres de archivo son relativos al directorio actual; ~/ es el
personal. El paquete tabular instala tablas de ejemplo en
/usr/share/tabular/ejemplos (datos.txt, saltos.txt, otra_x.txt, simusol.txt),
que se leen desde cualquier directorio.

'> ARCHIVO' al final de mostrar, saltos o graficar escribe en el archivo;
graficar acepta .png, .svg, .pdf, o .gp para el guion de gnuplot con los
datos adentro. Fuera de la tabla se extrapola y se avisa. Por ejemplo:

    $ interpola tests/datos.txt -e 'UNO=450; metodo spline; saltos 0 1200 400 TRES'
    tabla 1: tests/datos.txt, 5 filas, 4 columnas: UNO DOS TRES CUATRO
    x = UNO
    UNO = 450
      DOS            31.945
      TRES           995.2
      CUATRO         1.26
    metodo spline
    #  UNO      TRES
         0  1000.100
       400   995.063
       800   995.086
      1200   983.300

La tabla de saltos sale en columnas alineadas, con los titulos arriba de
cada una y los mismos decimales en toda la columna (siete cifras
significativas para el mayor valor, sin los ceros que sobran en todos).

Las ordenes avg, rms e integ agregan cada columna entre x = A y x = B:
avg es el valor medio (la integral dividida por B - A), rms la raiz del
valor medio cuadratico e integ la integral. Las tres integran por
trapecios sobre la interpolante lineal: las filas con x dentro del rango
y los valores en A y B interpolados linealmente si caen entre filas, con
cualquier metodo, tambien spline. Como no extrapolan, A y B tienen que
quedar dentro del rango de la tabla (y A < B); si no, se informa el error
y no se imprime nada.

Una tabla hecha con saltos se vuelve a leer con leer: es un archivo de datos
como cualquier otro.


Como se usa
-----------

El camino corto es una sola llamada. Las columnas se nombran por su
encabezado, y el metodo es 1 para lineal y 2 para spline:

    #include "interpola.h"

    double y;
    int error = interpolate("datos.txt", "UNO", "DOS", 550.0, 1, &y);
    if (error < 0) { /* ver Codigos de error */ }

Si se prefiere indicar las columnas por numero, o el archivo por su numero
en etable.txt:

    interpolate_file_num("datos.txt", 0, 1, 550.0, 1, &y);
    interpolate_num(7, 0, 1, 550.0, 1, &y);

El camino largo, cuando se va a consultar muchas veces la misma tabla,
evita repetir la busqueda:

    unsigned int nt, nv;
    get_numbers("datos.txt", "UNO", "DOS", &nt, &nv);
    interpolate_view(550.0, 1, &y, nt, nv);

Una tabla que no esta en un archivo sino en una cadena de texto, con el
mismo formato (la linea de nombres con # y los datos), se registra con un
nombre y despues se usa por ese nombre, como si fuera un archivo:

    unsigned int nt;
    create_table_from_string("ntc", "#T R\n0 28704.3\n25 10000\n", &nt);
    interpolate("ntc", "T", "R", 12.5, 1, &y);

El nombre hace de nombre de archivo: identifica la tabla (interpolate busca
primero entre las ya cargadas) y su extension decide el tipo, como en un
archivo (.csv). Si el nombre ya estaba cargado queda la tabla anterior y el
texto no se lee, asi que quien registra tablas debe darles nombres propios.
Es lo que usa Calcula para las tablas escritas dentro de un .din.

Para saber que hay cargado:

    unsigned int n;            get_qty_tables(&n);
    const char *nombre; unsigned int lineas, columnas;
    get_table_info(0, &nombre, &lineas, &columnas);
    get_table_line_info(0, 2, &columnas, &ultima, &anterior);
    get_view_info(nt, nv);

Y para soltar todo, dejando la lista lista para volver a cargar:

    free_tablelist();

El programa que use la biblioteca tiene que definir dos globales, que ella
declara extern:

    int verbose = 0;                          // 0 callado, 10 todo
    char *archivoenuso = "main_string";

El formato del archivo de datos esta mas abajo, en su propia seccion.


El archivo de datos
-------------------

Texto ASCII: una linea con los nombres de columna precedida de #, y despues
las lineas de datos, separadas por tabuladores, espacios o comas. Ver
data/test.txt.

    #T	R
    0	28704.3
    25	10000.0
    100	987.0

Los nombres son opcionales. Sin ellos las columnas se piden por numero, con
interpolate_file_num() o interpolate_num(), contando desde 0.

**Comentarios.** Una linea que empieza con # es un comentario, en cualquier
parte del archivo: antes del encabezado, entre el encabezado y los datos, o
entre los datos. No cuenta como linea de la tabla.

    # Termistor NTC de 10 kilohm a 25 C, tabla del fabricante.
    # T en grados Celsius, R en ohm.
    #T	R
    ## la columna T no puede decrecer
    0	28704.3
    25	10000.0
    # entre los datos tambien se puede
    100	987.0

El encabezado tambien empieza con #, asi que hay que distinguirlo de los
comentarios. Se elige por la cantidad de palabras: el encabezado nombra las
columnas, de modo que tiene que tener tantas palabras como la primera linea
de datos. Entre las lineas con # anteriores a los datos se toma la ultima
que cumple eso; si ninguna cumple, la ultima, y con verbose>0 se avisa.
Una linea que empieza con ## es siempre comentario y nunca se considera
encabezado: es la forma de desempatar a mano.

Antes toda linea con # anterior a los datos se leia como encabezado, asi
que un comentario arriba del archivo se tomaba como los nombres de columna;
con las columnas corridas, buscar una por nombre terminaba en violacion de
segmento.

**Que se lee tal cual.** Estos formatos entran sin convertir nada:

  * Los .simul y .INTER de resultados de Simusol. Los .INTER son ya el
    formato de tabular. Los .simul que escribe lolisol_sceptre traen un
    bloque de encabezados anunciado por

        #  para Simusol: sceptrevars variables magnitudes unidades

    donde cada palabra despues de los dos puntos nombra, en orden, a una de
    las lineas con # que siguen; el orden cambia de un archivo a otro y por
    eso hay que leerlo del anuncio. Se usan los sceptrevars, que son los
    nombres con los que se pide una columna en todo Simusol (TIME, VCA,
    J1); si no estan, las variables. Las demas lineas del bloque quedan
    como comentarios: la de indices, por ejemplo, tiene numeros, y si
    contara como encabezado se colaria una fila 1 2 3 4 entre los datos.
    Los .simul sin bloque, con los nombres en una sola linea y con # o sin
    el, entran por la regla general.

  * Los archivos de datos de gnuplot: columnas separadas por espacios o
    tabuladores, comentarios con # y lineas en blanco entre bloques. Con
    una linea de nombres, que es la convencion de gnuplot, las columnas se
    piden por nombre; sin ella, por numero. Lo que NO es un archivo de
    datos es un script de gnuplot con los datos adentro (plot '-' ... y
    una e que cierra cada bloque, como data/gnudata.txt): eso es un
    programa de gnuplot, no una tabla.

  * CSV, por la extension .csv: ahi la coma separa siempre, y dos comas
    seguidas son un campo vacio.

  * Los listados que escribe ngspice (SPICE) con .print: un preambulo con
    titulos y fecha, el encabezado de columnas entre dos lineas de guiones,
    los datos, y el encabezado repetido en cada pagina detras de un avance
    de pagina, con un epilogo de estadisticas al final. De todo eso queda
    solo la tabla: el encabezado pasa con '#' adelante, asi que las
    columnas se piden por nombre (time, v(in), v(c)), y se tiran el
    preambulo, los guiones, las repeticiones del encabezado y el epilogo.
    Se reconoce el patron por la linea con letras entre dos de guiones; un
    renglon de datos nunca se toca porque sus unicas letras son las e/E de
    los exponentes. Se promueve el primer encabezado que cumple el patron:
    si el .tran no lleva UIC, ngspice imprime antes el bloque "Initial
    Transient Solution" (el punto de operacion, con encabezado
    Node/Voltage entre guiones) y la tabla queda con esas columnas en
    lugar de time y las v(...) -- los .cir de siru/corpus/spice llevan UIC por
    esto. Un '#' dentro del encabezado (v1#branch, la corriente de una
    fuente) cortaria la linea ahi, asi que al promoverlo se pasa a '_'
    (la columna se pide como v1_branch). Dos limitaciones: si el mismo
    listado trae las tablas de dos analisis distintos, los datos de la
    segunda se anexan a la primera (conviene un analisis por archivo, o
    wrdata); y como despues del encabezado se descarta toda linea con
    letras, un dato impreso como texto (nan, por ejemplo) saca su fila de
    la tabla.

Las lineas vacias o de solo blancos no son lineas de la tabla, en ningun
formato: antes cada una se colaba como una fila de ceros.

tests/comentada.txt, tests/simusol.txt, tests/simusol2.txt y
tests/spice.txt cubren esto.


Saltos: repetir una coordenada X
--------------------------------

Repetir un valor de X es intencional y es la forma de representar una
discontinuidad de salto: un escalon, algo que se enciende o se apaga. No es
un dato invalido y no hay que deduplicarlo.

Dos filas con el mismo X dan el valor por izquierda y el valor por derecha:

    #T	P
    0	0
    300	0
    600	0        <- valor por izquierda
    600	1000     <- valor por derecha
    900	1000

Con esa tabla, interpolando la columna P contra T:

    t < 600      vale 0
    t = 600      vale 500, el promedio de los dos
    t > 600      vale 1000

En el punto exacto se devuelve el promedio de los dos valores, que es la
convencion habitual para una discontinuidad de salto: es a lo que converge
una serie de Fourier en el punto de corte. La discontinuidad se conserva:
a un milesimo de cada lado los valores siguen siendo 0 y 1000, no hay
rampa.

Vale para cualquier cantidad de saltos en la misma tabla, en cualquier
sentido, y tambien si el salto esta en el primer o el ultimo punto.

Una limitacion que conviene conocer:

  * Solo dos filas por cada X. Con tres el resultado no tiene sentido: el
    valor intermedio se toma como extremo de los tramos vecinos. Si hace
    falta declarar ademas un valor en el punto, hoy no se puede.


La spline tambien sirve: no es una sola spline sobre toda la tabla sino
una secuencia, una por tramo continuo. En cada salto se impone condicion
natural a los dos lados --la derivada segunda se pone en cero y se corta
la recurrencia en ambas direcciones--, de modo que los tramos se ajustan
por separado y la discontinuidad se conserva. Con la tabla

    0,0  100,10  200,0   |   300,0 -> 300,100   400,110  500,100  600,100

los dos tramos tienen la misma forma desplazada 100, y la spline devuelve
7.25 en x=50 y 107.25 en x=350: cada tramo se ajusto solo. En el punto del
salto devuelve el promedio, igual que el metodo lineal.

Lo que si se comprueba es que X **no decrezca**. Repetir un X es valido, es
un salto; que baje, no: la busqueda binaria supone que no decrece, y con
una tabla al reves o desordenada devolvia resultados falsos informando
exito --la decreciente daba -nan, la desordenada devolvia siempre el primer
valor, y las dos con codigo de exito--. Hoy esas tablas se rechazan con SC
y no se toca el resultado del llamador.

Ordenar automaticamente no seria correcto: con X repetidos no hay forma de
saber cual fila es el valor por izquierda y cual el de la derecha, asi que
ordenar destruiria los saltos.

tests/t_saltos.c cubre todo esto.


Codigos de error
----------------

Cero es exito y los negativos son error. Estan en src/table.h y valen lo
mismo que en interpola y tabular:

    LT       -1   error de carga
    FO       -2   no se pudo abrir el archivo
    SC       -3   columnas mal especificadas
    MC       -4   x e y son la misma columna
    TB_BAD_DATA          -5
    TB_INVALID_ARGUMENT  -6
    TB_NO_DATA           -7
    TB_IO_ERROR          -8
    OOM     -15   sin memoria
    NOHEADER  -888  no hay columna con ese encabezado
    NOVIEW    -889  no se pudo obtener la vista de esas dos columnas

Ninguna funcion termina el proceso: informan con el valor de retorno, y
todas sin excepcion devuelven 0 cuando andan. La prueba a escribir es
`if (error < 0)`, nunca una comparacion contra un valor de exito.


Autoria y licencia
------------------

Diego Saravia <dsa@unsa.edu.ar>. GPL version 3 o posterior; ver COPYING.
exprtk.hpp es de Arash Partow, con licencia MIT; ver src/exprtk/mit.txt.

Lectura binaria de SCEPTRE2 (POS4), 2026-10-06
--------------------------------------------

Entrega Debian 2.0-13: libtabular2-0, libtabular2-dev y tabular. Se generan con ./hacer-pkg y no se instalan automáticamente. El paquete tabular incluye interpola y sceptre-tabular, con sus páginas de manual; ejecutar SCEPTRE requiere además una instalación compilada y gfortran. Las colecciones nuevas del corpus se conservan en el repositorio, no se incorporan a estos .deb.

Tabular reconoce por contenido los resultados POS4 de SCEPTRE2, tanto los .dirac como fort.7 o un archivo sin extensión. El lector carga los REAL*4 directamente en las celdas numéricas de Tabular como double, sin generar texto ni ejecutar SCEPTRE2 o SD2. El nombre de archivo y las operaciones de interpolación se usan con la API habitual.

| Entrada | Tabla disponible |
|---|---|
| resultado.dirac | Primera corrida disponible, ordenada por el número de RERUN. |
| resultado.dirac#2 | Segunda corrida disponible. |
| resultado.dirac#3 | Tercera corrida disponible. |

Cada corrida tiene su propio eje TIME o FREQ, ubicado en la primera columna. El sufijo #K es el ordinal del bloque (desde 1, con el primero sin sufijo), igual que en los demás formatos; no es el número original de RERUN. Así, un archivo que contiene RERUN 0 y 3 se abre como resultado.dirac y resultado.dirac#2. Se pueden pedir los bloques adicionales directamente, sin abrir antes el primero.

Desde este directorio, con la versión compilada:

    ./src/interpola resultado.dirac -e 'columnas; mostrar'
    ./src/interpola 'resultado.dirac#2' -e 'interpolar 10 VCE'

Dentro de la terminal, el nombre con # debe ir entre comillas:

    leer "resultado.dirac#2"
    columnas

Los resultados AC usan los mismos nombres que sceptre2simul: _RE/_IM para componentes cartesianas, _MOD/_DEG o _MOD/_RAD para módulo y fase. POS4 no aporta unidades físicas fiables: no se asignan unidades ni se cambia la escala de TIME/FREQ. Los dos valores finales de cada vector son sus extremos y no se incorporan como muestras.

Se admite el directorio POS4 de 2048 bytes y 64 entradas que escribe esta distribución, con REAL*4 IEEE de 32 bits y ambos órdenes de bytes. Se rechazan tipos incompatibles, vectores truncados o superpuestos, nombres duplicados, corridas sin un único eje, longitudes diferentes y muestras no finitas. Una carga fallida retira todas las tablas nuevas de esa carga, incluidas las vistas auxiliares, y conserva las anteriores.

Verificación: make check incluye t_pos4 (corridas, AC, precisión, ambos órdenes de bytes, errores y reintentos). La integración opcional compara todas las celdas de 17 ejemplos reales con su conversión .simul y prueba la terminal:

    make -C tests check-pos4-sceptre2

Para esta comparación, el texto se vuelve a redondear a REAL*4: las nueve cifras del .simul permiten recuperar el valor original, aunque su lectura como double no sea idéntica bit a bit a la promoción directa del binario.

Formatos binarios antiguos de SCEPTRE, 2026-10-06
-----------------------------------------------

El mismo lector también reconoce el directorio DIRECTOR de los paquetes sceptre-00.317, sceptre-2012.061 y sceptre-2013-0.6. Usa 51 entradas, nombres de 8 caracteres, arrays separados de tipos/longitudes/direcciones (enteros de 4 bytes), fechas de 19 caracteres y fin ZZZZZZZZ. POS4 conserva su detección por DIRECTORY, con 64 entradas de 32 bytes.

No hace falta indicar la versión ni convertir el archivo. Se aceptan .dirac, dirac.dat y otros nombres. Las corridas, componentes de AC, precisión y selección por #2, #3, etc. siguen el mismo contrato que POS4. Se admiten ambos órdenes de bytes con registros de 2048 bytes y datos REAL*4 IEEE; otras representaciones antiguas de máquina no se presuponen compatibles.

    ./src/interpola resultado.dirac -e 'columnas; mostrar'
    ./src/interpola 'dirac.dat#2' -e 'columnas'

El directorio antiguo se normaliza sólo en memoria; el archivo original no se modifica. Se aceptan huecos de vectores borrados. Los directorios incompletos, tipos no admitidos, vectores truncados, solapamientos y muestras no finitas se rechazan antes de publicar los datos.

make check cubre ambos formatos, corridas y componentes AC, errores, reintentos y un encabezado de texto llamado DIRECTOR. Para probar además los escritores históricos:

    make -C tests check-pos-antiguo

Esa prueba extrae POS000 de los tres archivos .tar.gz del directorio sceptre2novender, lo compila en temporales con gfortran y genera dos corridas de 1031 muestras (vectores que cruzan varios registros), en little-endian y big-endian. Compara todas las celdas con una tabla de referencia escrita desde los mismos arrays Fortran y verifica la terminal. Sólo se sustituyen las rutinas de fecha/hora por una fecha fija de prueba; no se modifica POS000 ni se compilan simuladores completos. Los seis casos pasaron.

Conversión común de SCEPTRE y SPICE a .simul, 2026-10-06
------------------------------------------------------

Tabular lee los dos formatos binarios de resultados de SCEPTRE y las salidas de texto de SPICE indicadas abajo. Todos se exportan a .simul con el mismo comando, usando los lectores de la biblioteca.

| Entrada | Formato admitido | Tratamiento |
| --- | --- | --- |
| SCEPTRE 00.317, 2012.061 y 2013-0.6 | POS antiguo: DIRECTOR, directorio de arrays de 51 posiciones | Detección automática; REAL*4 IEEE; registros de 2048 bytes; ambos órdenes de bytes |
| SCEPTRE2 SC1/SD2 | POS4: DIRECTORY, 64 entradas de 32 bytes | Detección automática; REAL*4 IEEE; registros de 2048 bytes; ambos órdenes de bytes |
| SPICE/ngspice .print | Listado de texto, incluso paginado | Descarta preámbulo, separadores y encabezados repetidos |
| SPICE/ngspice wrdata | Tabla de texto con o sin encabezado | Conserva las columnas; asigna col1, col2... si no hay nombres |
| SPICE/ngspice raw ASCII real | Flags: real y Values: | Conserva variables y puntos |
| SPICE/ngspice raw ASCII complejo | Flags: complex y Values: | Separa cada variable en nombre.re y nombre.im |
| SPICE/ngspice raw ASCII con varios análisis | Varios bloques Plotname/Variables/Values | Cada análisis queda en un bloque independiente del .simul |

El raw binario de SPICE (Binary:) no está admitido. Hay que pedir a ngspice set filetype=ascii antes de write. Para .print se mantienen las limitaciones del lector descritas arriba: conviene un análisis por archivo y evitar que el listado mezcle el punto de operación inicial con la tabla temporal. raw ASCII admite varios análisis en un archivo.

Desde tabular2:

    ./src/interpola --exportar-simul resultado.dirac > resultado.simul
    ./src/interpola --exportar-simul dirac.dat > antiguo.simul
    ./src/interpola --exportar-simul salida-spice.out > spice.simul
    ./src/interpola --exportar-simul salida-spice.raw > spice-raw.simul
    ./src/interpola --exportar-simul datos-wrdata.txt > wrdata.simul

--exportar-simul recibe un archivo y no se combina con órdenes de la terminal. Escribe exclusivamente el .simul en stdout y los diagnósticos en stderr; devuelve un estado distinto de cero si falla. También exporta otras tablas numéricas admitidas por Tabular. La redirección > pertenece al shell y reemplaza el destino si existe.

Un NaN o un Inf del calculador (los .simul de SCEPTRE los traen) se conserva tal cual en la exportación: para Gnuplot es un punto faltante, y cortar el archivo entero por una columna que quizá ni se dibuja era perder las demás. Un dato que no es número sigue cortando con error.

La exportación conserva leyendas, magnitudes y unidades declaradas de .simul. --unidad COLUMNA UNIDAD convierte una columna (índice desde 1) mediante libunidades/GNU units, sin modificar el original; --unidad-origen COLUMNA UNIDAD declara un origen ausente y rechaza contradicciones. --magnitud-origen COLUMNA MAGNITUD completa un rótulo ausente: no determina cómo se convierte. Las opciones se pueden repetir para distintas columnas.

Se usan las mismas unidades que en todo el proyecto: tempC y tempF para temperaturas absolutas, K para kelvin, degC para intervalos de temperatura, s para segundos, C para coulomb; también expresiones como km/hr. No hay alias ni conversiones particulares de Simplot. Los metadatos antiguos que usaban C para Celsius deben corregirse explícitamente a tempC; no se infiere de la palabra «temperatura».

    ./src/interpola --exportar-simul termico.simul --unidad 2 tempC > celsius.simul

| Propiedad del .simul | Comportamiento |
| --- | --- |
| Encabezado | Anuncio «para Simusol: sceptrevars variables magnitudes unidades» y sus cuatro líneas de metadatos |
| Corridas/análisis | Bloques separados por líneas en blanco; CORRIDA 1, 2... es el ordinal, no el número original de RERUN |
| Precisión | 17 cifras significativas conservan los double de SPICE; los REAL*4 de SCEPTRE se recuperan exactamente al redondearlos de nuevo a REAL*4 |
| SCEPTRE | TIME/FREQ primero; se excluyen los dos min/max finales de cada vector; componentes AC _RE/_IM o _MOD/_DEG/_RAD |
| SPICE | Conserva los nombres y columnas del lector, incluidos índices o escalas repetidas si aparecen |
| Unidades | Escribe las conocidas como metadatos y los valores ya convertidos por Tabular a SI; usa ?? si no hay información fiable |
| Datos | No remuestrea, interpola ni deduplica; requiere valores numéricos finitos y nombres de columnas sin blancos |

Para volver a leer la segunda corrida se usa archivo.simul#2. Como explica la sección de lectura .simul, las unidades del bloque de metadatos se leen como comentarios; los valores exportados ya tienen la escala indicada. No se inventan unidades para TIME/FREQ de SCEPTRE, porque pueden estar escalados por el modelo.

Lanzador sceptre-tabular
------------------------

src/sceptre-tabular ejecuta SCEPTRE, compila el Fortran generado y llama a interpola --exportar-simul. Tabular lee el binario y escribe los valores: el script no contiene otro decodificador POS. Se instala junto con interpola y requiere Python 3; para simular requiere además gfortran y SCEPTRE compilado.

| Instalación seleccionada | Ejecución |
| --- | --- |
| SC1/SD2 nuevo | exec1; enlace con exec2.o y libx2.a; ejecución; lectura de fort.7 |
| SCEPTRE 00.317/2012.061 | exec1; enlace con exec2.o, libx2.a y libx3.a; ejecución; lectura de dirac.dat |
| INENCO 2013-0.6 | sceptre1; enlace con sceptre2.o, libsceptre.a y libsceptreaux.a; ejecución; lectura de dirac.dat |
| Binario existente | Conversión directa; detecta DIRECTOR/DIRECTORY por contenido |

--sceptre-dir selecciona la raíz del árbol compilado o del prefijo instalado. Si se omite, se busca en este orden: SCEPTRE_HOME, el SC1/SD2 vecino de este repositorio y la instalación encontrada en PATH. --tabular selecciona otro ejecutable interpola compatible.

Desde tabular2:

    ./src/sceptre-tabular circuito.d
    ./src/sceptre-tabular circuito.d --sceptre-dir ../sceptre2novender/sceptre-00.317
    ./src/sceptre-tabular circuito.d --sceptre-dir ../sceptre2novender/sceptre-2012.061
    ./src/sceptre-tabular circuito.d --sceptre-dir ../sceptre2novender/sceptre-2013-0.6
    ./src/sceptre-tabular resultado.dirac
    ./src/sceptre-tabular dirac.dat --convertir --salida resultados/circuito.simul
    ./src/sceptre-tabular --help

| Opción o comportamiento | Detalle |
| --- | --- |
| -o/--directorio | Directorio de salida; por defecto, el actual |
| --salida | Nombre .simul; si está ocupado agrega -1, -2... sin reemplazar resultados |
| Archivos conservados | .simul, .dirac, .ls1, .ls2 y .log cuando los genera la simulación; al convertir un binario existente conserva el original |
| -m/--modelos, -u/--biblioteca | Seleccionan modellib.dat y usrlib.a |
| --fuente, --dato, --enlazar | Fuentes Fortran, datos auxiliares y opciones de enlace; repetibles |
| Fuentes automáticos | El .f90 vecino con el mismo nombre del circuito y funciones.for, si existen |
| --timeout | Límite por fase, en segundos |
| --conservar | Mantiene el temporal independiente de la corrida, también ante errores |
| Errores | No publica .simul parcial; conserva diagnósticos. Una corrida sólo de condiciones iniciales no genera series |

Fuentes históricos corregidos
------------------------------

Las tres versiones quedaron descomprimidas y compiladas en ../sceptre2novender/sceptre-00.317, sceptre-2012.061 y sceptre-2013-0.6. Los .tar.gz originales se conservan. doc/sceptre-historicos.patch registra las correcciones respecto de esos paquetes y la prueba numérica agregada.

| Corrección | Motivo |
| --- | --- |
| 00.317: argumentos de ETIME en qqqcpu.f | El orden antiguo impedía compilar con gfortran; el paquete describe el problema en ETime.bug |
| 2013: PREPLT desde MULPLT | PREPL dejaba un símbolo sin resolver al enlazar |
| 2013: comparaciones sin redondeo entero | NINT podía tratar un pivote de 0,1 como cero o confundir nombres almacenados en palabras |
| 2013: módulo/fase AC y comparación de autovalores | Conserva ambas componentes y precisión doble |
| 2013: listado sin puntos | Evita el bucle y la división por cero |
| 2013: nombre de matriz en LINSOL | Se declara CHARACTER, de acuerdo con sus llamadas |
| Las tres versiones: compilación | gfortran, modo legacy, almacenamiento estático y -fno-aggressive-loop-optimizations |
| asa.c de 00.317/2012.061 | Tipo int explícito para first_line |

-fno-aggressive-loop-optimizations es necesario para estos fuentes históricos: varias rutinas declaran una vista corta de un COMMON cuyo almacenamiento completo reserva el programa generado. Con -O2 sin esa opción se observó que sólo se actualizaba el primer elemento de una matriz y aparecían falsas singularidades. Se conserva -O2 con la opción de compatibilidad. Los paquetes siguen usando construcciones Fortran históricas y pueden emitir avisos de tipos o dimensiones; se corrigieron los fallos encontrados en la compilación y las simulaciones verificadas.

Para recompilar:

    make -j2 -C ../sceptre2novender/sceptre-00.317
    make -j2 -C ../sceptre2novender/sceptre-2012.061
    make -j2 -C ../sceptre2novender/sceptre-2013-0.6
    make -C ../sceptre2novender/sceptre-2013-0.6 check

Para reproducir las correcciones en otra copia, extraer cada paquete dentro de un directorio llamado sceptre-00.317, sceptre-2012.061 o sceptre-2013-0.6, quitando el primer componente del tar de 2013. Desde el directorio que contiene esos tres árboles, aplicar patch -p1 < /ruta/tabular2/doc/sceptre-historicos.patch y recompilar con make -B para regenerar los objetos incluidos en las distribuciones.

Verificación
------------

| Prueba | Cobertura |
| --- | --- |
| make check | 12 pruebas generales; incluye fechas, .print, wrdata, raw ASCII real/complejo y multianálisis, 12 bloques, precisión y metadatos de unidades |
| make -C tests check-pos-antiguo | POS000 original de tres paquetes, ambos órdenes de bytes y conversión por el lanzador; 4124 valores por caso |
| make -C tests check-sceptre-tabular | 17 ejemplos SC1/SD2, comparación de cada celda, nombres con espacios, preservación de salidas y rechazo de errores |
| make -C ../sceptre2novender/sceptre-2013-0.6 check | Pivotes fraccionarios, solución lineal y módulo/fase AC |

Para incluir también las tres versiones antiguas en la prueba del lanzador:

    make -C tests check-sceptre-tabular SCEPTRE_TEST_ARGS='--sceptre-dir /ruta/sceptre-00.317 --sceptre-dir /ruta/sceptre-2012.061 --sceptre-dir /ruta/sceptre-2013-0.6'

Se verificó a01 con tres corridas en las cuatro versiones: cada conversión conservó sus 53184 valores. Además se verificaron los finales normales de a02, a11 y a13 en 2013, después de corregir las comparaciones y la compilación. Cada versión se compara contra su propio binario; los simuladores pueden elegir pasos diferentes y no se exige igualdad entre sus trayectorias.
