Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Status

Everything planned is built. Every phase has landed, every validation tier has a harness, and the four decisions that were once open have been taken. The backlog is 85 items done, none open and none blocked.

That makes this page shorter than it used to be, and its job is now to say where the limits are rather than where the edge of construction is. Numbers here come from the current-state block of BACKLOG.org and the closing entries of LOG.org, and each names the iteration that measured it.

What exists

The plant model, the control layer, the public API, the command line, the Python wheel and the browser app are all complete and validated. tepsim-core ports the whole of TEFUNC (teprob.f:196-816); tepsim-control ports the twenty control loops and the driver's own scheduling; tepsim is the API most callers want; tep drives it from a terminal, including tep dataset for generating d00-d21 shaped files; the wheel carries no C dependency; and TEP Studio runs the whole simulator in a browser tab with no server.

The differential harness is tepsim-oracle, which compiles and links the unmodified Fortran, and tepsim-stats, which implements every statistic the ladder needs in Rust with known-answer tests, so no part of the validation depends on numpy or scipy.

All ten tiers have a harness and all ten have run. Tier 8, differential fuzzing with shrinking, and Tier 9, cross-platform determinism, were the last two and landed together. The validation chapters are generated from the suite's own output for the tiers that have a generator; the narrative in Validation is hand-written and transcribed from LOG.org, and each number there names the iteration that measured it so the transcription can be checked.

What is genuinely still missing

These are limits, not unfinished work, and most of them are limits of the machine this was built on rather than of the code.

Tier 9 has no x86-64 leg and no real-browser leg. Six committed digests are identical on aarch64 and on wasm32 under Node, across three build profiles, and the browser app's transport path reproduces them independently. Nobody has run them on x86-64, Windows or aarch64 Linux, and Node is not a browser. The table is committed constants rather than a value computed twice in one process, precisely so that running cargo xtask tier9 on another machine completes the claim with no code change.

Tier 8 has one open counterexample. In five million tuples, fuzz#863105 misses the 1e-12 gate at 4.607e-12 of the scale of its terms. It is recorded and attributed rather than fixed, because there is nothing to fix: it is an accepted one-ULP exp difference amplified by 973 simulated hours of IDV(13)'s kinetic drift, and it is bit-identical under libm-system.

Two IDV faults miss the Tier 5 margin under the vendored libm. IDV(14) and IDV(19), on exactly the valves those faults stick, and bit-identical under the platform libm. The cause is teprob.f:801, a discontinuous branch on a floating-point comparison. Those valves are judged on their distribution rather than on a mean, because a series of plateaux has no meaningful centre. No margin was widened.

Apache Arrow and Parquet sinks are not implemented. The recorder sinks that exist are Columnar, Csv, Ring, Decimating and Selecting, and Simulation::run_into streams into one rather than collecting. Arrow's dependency cost was judged not to be worth paying until someone has a dataset large enough that CSV is the bottleneck; the right home would be tepsim-cli, never tepsim, which is no_std and compiles to wasm32 under a size budget.

A historian export does not exist. Everything is wide tabular. A long-format tag,timestamp,value,quality record would need a caller-supplied epoch, since the core may not read a clock, a decision about what quality code a dead-time analyser and a frozen channel deserve, and units promoted from prose in measurements.rs to data on a channel.

The step size is not adapted. Three integrators exist as of B-0053: fixed step explicit Euler, classical RK4, and Dormand-Prince 5(4) with an embedded error estimate. Only Euler reproduces the original, and it is the default. A variable step changes when the discrete phases run, which is a decision about fidelity rather than about numerics, so it has not been taken.

That comparison produced a result about the original rather than about the port. RK4 and Dormand-Prince agree with each other to 1.5e-6 while both differ from Euler by about 1.1e-2, so the published Tennessee Eastman data carries roughly one percent of integration error against an accurate solution of the same equations. Reproducing that is the point, but it means "the TEP" names a particular discretisation and not only a set of differential equations.

The two Class C quirks, and what a default Scenario now does

Both were signed off on 2026-08-28, so a default Scenario fixes them and Scenario::faithful() reproduces them.

A trip ends the run (delta D-007, teprob.f:807-811). The original freezes the plant and keeps reporting, which is where four of the forty-four published files get their frozen tails: 1,832 rows in total, and 363 of d06.dat's 480. The fix is pure truncation, since every sample before the trip is bit-identical either way, and the argument for keeping the freeze was that it preserved an option it does not preserve, because the plant cannot be restarted in either case.

The driver does not force IDV(12) at hour eight (delta D-011, temain_mod.f:366-368). Tier 7 established that the published files were generated with that line replaced rather than kept: every dNN_te except d12_te sits at the nominal operating point straight across row 160.

Every comparison against the Fortran or against published data runs Scenario::faithful(), and tier5::run_port pins it so no differential can lose it. From the command line the flags are tep run --faithful, or --force-idv12 and --freeze-on-trip individually.