How it is built
Architecture overview
A digital twin of a plant is a running simulation model that is kept in step with a real installation (a refinery unit, a factory line, a power station) by a stream of measurements. The plant's sensors publish values over industrial protocols; a gateway turns them into a clean, time-aligned stream; the twin feeds that stream into a state estimator that corrects the model's internal state and slowly re-fits its parameters. Because the model now mirrors the plant, it can be asked questions the plant cannot answer safely: what happens if this valve closes, what should the setpoints be for the next hour, why does the real heat exchanger perform worse than the model says.
The one idea that makes it work is the residual: the difference between what the model predicts and what the sensor reads. Everything useful comes from it. A small, noise-like residual means the twin is trustworthy and can be used for prediction and optimisation; a persistent residual in one place means something in the plant has drifted (fouling, wear, a failing sensor), and that is the anomaly signal. Without the estimator the model drifts away from reality within hours; without the model the sensor stream is just a pile of numbers.
Parts:
- Ingest and time base — industrial protocols in, one aligned clock, cleaned values — MQTT protocol (no page yet), OPC UA (no page yet), Clock synchronization, Robust statistics, Phase-locked loop
- Telemetry store — cheap, compressed history of every tag — LSM tree, Time-series chunk storage, Gorilla XOR compression, Delta encoding, Bit-packing with PFOR, Varint (LEB128)
- Twin registry — one record per asset with its latest state, desired state and links — Event sourcing, Write-ahead log, Bitstream serialization
- Simulation core — the plant model as coupled units advanced in time — FMI co-simulation (no page yet), Adaptive Runge-Kutta, Stiff solvers (implicit BDF), Discrete-event simulation (no page yet), Finite differences
- Calibration — fit model parameters to measured data — System identification (no page yet), Levenberg-Marquardt (no page yet), Linear and logistic regression, Fast Fourier transform, L-BFGS
- State estimation — keep the model's state aligned with the plant — Kalman filter, Extended Kalman filter
- Control and what-if — optimise setpoints over a horizon on the twin — Model predictive control, PID control, Quadratic programming (no page yet), Interior-point method, Sparse direct solver
- Anomaly detection — read the residual — Time-series anomaly detection, STL decomposition, Isolation forest, Exponential smoothing
sensors / PLCs ──OPC UA, MQTT──► edge gateway ──► align clock, clean ──► telemetry store (compressed history)
(plant, hard real time) (buffers, QoS) (outlier filter) │
│ ▼
▼ calibration (offline, nightly)
state estimator ◄──params──────┘
(Kalman filter)
│ state
▼
twin registry ◄──────── simulation core (FMUs + solver) ──► what-if / MPC ──setpoints──► operator or PLC
(asset, state, │ (advice, never direct)
relations) ▼
residual = measured − predicted ──► anomaly detector ──► alerts
There are two clocks. The plant loop is the control system the plant already has; it runs in hard real time on PLCs and never depends on the twin. The twin loop runs seconds to hours behind and advises. Keeping that separation is the safety design: the twin can be wrong, slow, or down, and the plant still runs.
How the parts fit together
Ingest and time base
Plants speak OPC UA (a structured address space of nodes, with subscriptions and monitored items that report only on
change or deadband) and, increasingly, MQTT (lightweight publish/subscribe with three delivery guarantees).
OPC UA (no page yet) is the source of truth for tags and units; MQTT protocol (no page
yet) is the transport from the edge to the cloud, because a retained message and a persistent session make a
flaky uplink invisible. The contract with the next part: every value leaves the gateway as (tag id, timestamp, value, quality), timestamped at the source, not at arrival.
Source timestamps are only useful if the sources agree on time. Clock synchronization (NTP, or PTP with hardware timestamps where tags from different PLCs must be compared within microseconds) fixes the time base; a Phase-locked loop is what a sampling clock uses to follow it. Cleaning is deliberately boring: Robust statistics (median, MAD, Huber) discard spikes and stuck values before they poison the estimator. A Kalman filter fed an outlier trusts it, so cleaning is a precondition, not a nicety.
Telemetry store
A plant has tens of thousands of tags sampled at about 1 Hz and up, written once and almost never changed. The
write path is an LSM tree: buffer in memory, flush sorted immutable files, merge in the background,
which turns the endless stream of small writes into sequential I/O; the read path is range scans by tag and time,
so the sort key is (tag, time). Inside a file, Time-series chunk storage keeps one
column per tag, and the codecs do the compression: Delta encoding for timestamps,
Gorilla XOR compression for floats that change slowly, Bit-packing with PFOR
and Varint (LEB128) for integers and counters. Slow-moving process values compress very well; that
is why keeping years of history of every tag is affordable. B+ tree storage wins instead when the
workload is mostly point lookups and updates in place; a twin's history is not that.
Twin registry
The registry answers "what is asset A12 right now, and what is it connected to". Each asset has a record: static description (type, parameters, units), the latest estimated state, the desired state (setpoints, limits) and relations (A12 feeds B3). Writers append changes to a log (Write-ahead log, Event sourcing), so the record can be rebuilt for any past instant: "what did the twin believe at 03:10" is a replay, not a guess. This is the same desired-versus-reported split as a device shadow in an IoT device fleet (system page iot-fleet), applied to equipment. The wire format between processes is compact and fixed (Bitstream serialization); the registry itself is usually JSON for the model and a time-series store for the values.
Simulation core
The plant is a network of components (tanks, pumps, heat exchangers, controllers) written by different vendors in
different tools. The standard way to couple them is FMI: each component is exported as an FMU (a zip with a
modelDescription.xml and a shared library with a C interface), and a master algorithm steps them in time,
exchanging outputs and inputs at communication points. FMI co-simulation (no page yet) covers
step negotiation, rollback and algebraic loops. The seam to remember is the communication step: inside it each
FMU integrates alone with its own solver, between steps data is held constant or extrapolated, so a large step
means a coupling error and a small step means cost. Rollback (re-doing a step with a smaller size) needs the FMU
to support saving and restoring its state; check that capability before choosing a tool.
Inside one FMU, continuous dynamics are ODEs. Adaptive Runge-Kutta resizes its step by an error estimate and wins on smooth dynamics; process plants are usually stiff (fast chemical or thermal modes next to slow flows), where an explicit method is forced to a tiny step, so Stiff solvers (implicit BDF) win. For fixed-step real-time models (hardware-in-the-loop) the simple Runge-Kutta 4, Explicit Euler or Semi-implicit Euler are chosen for their predictable cost. Spatial effects (a long pipe, a heated plate) are discretised with Finite differences into many coupled ODEs. Discrete behaviour (batch steps, valve sequences, queue of jobs on a line) is modelled with Discrete-event simulation (no page yet), driven by a time-ordered event queue (d-ary heap) and seeded generators (PCG and xoshiro) so a run is reproducible bit for bit. The solver cores are tight numeric kernels where SIMD programming pays.
Calibration
A model is only as good as its parameters: heat-transfer coefficients, valve curves, time constants. Offline, System identification (no page yet) fits them from recorded input/output data, using nonlinear least squares (Levenberg-Marquardt, no page yet) for mechanistic models and Linear and logistic regression as the baseline every fitted model must beat. The Fast Fourier transform shows the dominant frequencies in the data, which tells you whether the experiment excited the dynamics you want to identify; if nothing excited a mode, its parameter cannot be fitted. L-BFGS is the fallback for large parameter sets with a smooth loss (Loss functions: use Huber rather than squared error when sensors misbehave). The contract with the estimator: calibration writes a versioned parameter set; the online estimator reads it and may only adjust it slowly, within bounds.
State estimation
The simulation core predicts; the estimator corrects. A Kalman filter fuses the model's prediction with each noisy measurement, weighted by their uncertainties; plant models are nonlinear, so the usual choice is the Extended Kalman filter, which linearises the model at the current estimate. Two practical points decide whether it works. First, only a subset of states is measured (temperatures yes, compositions rarely): observability decides which hidden states can be inferred at all. Second, the measurement noise covariance must reflect sensor quality, otherwise the filter either ignores real change or follows noise. The filter's innovation (measurement minus prediction) is exactly the residual that the anomaly detector reads.
Control and what-if
Model predictive control solves, every cycle, a small optimisation over a predicted horizon, applies only the first move, and repeats. Constraints (valve limits, temperature ceilings) are what make it worth the effort compared with PID control, which stays on the PLC as the fast inner loop. The optimisation is a Quadratic programming (no page yet) problem, solved by the Interior-point method, whose inner linear systems go to a Sparse direct solver with a Fill-reducing ordering; Sparse matrices (CSR), LU and Cholesky and Graph partitioning sit underneath. The twin's job here is the prediction model. A what-if is the same machinery run without applying the result: copy the registry state, change one input, step the simulation forward faster than real time.
Fixed-rate control loop discipline matters for the part that touches hardware: absolute deadlines and a fixed step, so jitter does not leak into the model. The twin's output goes to an operator or to the PLC as a setpoint suggestion with limits enforced on the PLC side.
Anomaly detection
Residuals are time series with seasonality (shift patterns, ambient temperature). Time-series anomaly detection predicts the expected value from a seasonal baseline (STL decomposition, Exponential smoothing), scales the residual with a robust spread, and alarms on a persistent excess. Isolation forest catches multivariate oddities that no single residual shows. Alarm on the residual of the twin, not on the raw tag: the twin already knows that a temperature rise is expected because load went up.
Data and control flow
One measurement through the twin (online loop, seconds):
- A PLC value changes; the OPC UA server reports it to the gateway (a monitored item, deadband applied) with a source timestamp.
- The gateway publishes it over MQTT with at-least-once delivery; if the uplink is down it buffers.
- The cleaning stage drops stuck values and spikes, aligns the sample to the common time base, and appends it to the telemetry store.
- The simulation core advances the plant model to the sample time (FMUs stepped by the master, each with its own solver) and produces a prediction.
- The Kalman filter compares prediction and measurement, updates the state estimate and covariance, and writes the new state to the twin registry.
- The residual goes to the anomaly detector; an alarm is raised if it stays out of range. Time goes to step 4 (stiff solves, many FMUs); steps 1-3 are bound by the network and by write throughput.
One what-if or optimisation (on demand, minutes):
- The user (or MPC) picks a scenario: an initial state snapshot from the registry, a change of inputs, a horizon.
- The simulation runs faster than real time, either one scenario or an ensemble over uncertain parameters.
- MPC wraps this in a QP per cycle; the solver returns the move sequence; the first move is the suggestion.
- Results are stored with the scenario inputs so they can be replayed and compared with what really happened.
One calibration cycle (nightly):
- Pull a window of history for the tags that matter from the telemetry store.
- Fit parameters with least squares; keep the previous set if the new fit is not clearly better on held-out data.
- Publish a new versioned parameter set; the online estimator picks it up at a safe moment.
Scaling
Limits in the order you meet them:
- Model fidelity versus cost. A rigorous model of every pump is too slow to run faster than real time. Lever: simplify (reduced-order models, linearisation around the operating point), run detailed models only for the assets you are asking about.
- Ingest and storage. Many tags at high rates. Levers: deadband at the source, compression codecs on the way to disk, downsampled rollups for long-range queries.
- Simulation time. Stiff systems and tight coupling. Levers: larger communication steps for weakly coupled FMUs, partition the plant graph (Graph partitioning) so each partition runs on its own core or machine, implicit solvers where stability is the limit.
- Estimator size. An EKF's cost grows quickly with the state dimension. Levers: estimate per subsystem, keep the state to what is observable.
- Many plants. The same twin design per site: the registry and models are parameterised, the telemetry store shards by site and tag.
Failure modes
| Failure | Symptom | Cause | Fix |
|---|---|---|---|
| Clock skew between PLCs | residuals correlate with which PLC the tag came from; strange lags | sources timestamped on unsynchronised clocks | PTP or NTP at the source, monitor offsets, reject samples with wild offsets |
| Filter divergence | state estimate drifts off, covariance collapses | model error larger than the process noise assumed, linearisation too crude | inflate process noise, use a better model, reset the state from a trusted snapshot |
| Sensor fault trusted | model "learns" a wrong parameter | stuck or biased sensor fed to the estimator and the calibration | quality flags, robust cleaning, cross-check redundant sensors |
| Co-simulation instability | oscillation at coupling points | communication step too large for a tight algebraic loop | smaller step, rollback, iterate the coupling, or merge the two FMUs |
| Stiff problem on an explicit solver | step size collapses, simulation slower than real time | fast modes limit the stable step | implicit BDF solver, or a reduced model |
| Model drift unnoticed | residuals slowly grow, alarms ignored | parameters not re-calibrated since plant changes (retrofit, fouling) | scheduled calibration, versioned parameters, alarm on the residual's trend |
| Twin treated as control | plant tripped by a bad suggestion | twin output wired to actuators without limits | twin advises, PLC enforces limits and interlocks |
| Unreproducible run | cannot explain last week's alarm | no snapshot of inputs, parameters, solver seeds | event-sourced registry, versioned parameters, seeded generators |
Real implementations
| name | what it is | repo | licence | notes |
|---|---|---|---|---|
| Eclipse Ditto | digital-twin framework: a registry of "things" with features, connectors and access policies | eclipse-ditto/ditto | EPL-2.0 | the registry and state-synchronisation part; no simulation inside |
| FMPy | Python tools to simulate and validate FMUs | CATIA-Systems/FMPy | — | the simulation-core seam, via the FMI standard (see fmi-standard.org) |
| OpenModelica | open Modelica environment that can export FMUs | OpenModelica/OpenModelica | — | a way to author the plant model |
| open62541 | C implementation of OPC UA | open62541/open62541 | MPL-2.0 | the ingest seam for OPC UA servers and clients |
| Eclipse Mosquitto | MQTT broker | eclipse-mosquitto/mosquitto | — | the edge-to-cloud transport |
| Digital Twins Definition Language (DTDL) | the model language for twins on Azure Digital Twins: typed properties, telemetry, relationships | Azure/opendigitaltwins-dtdl | CC-BY-4.0 | a documented way to describe the registry's asset graph |
Commercial platforms for this exist (for example from Siemens); their internals are not public.
Sources
- E. Glaessgen, D. Stargel, The Digital Twin Paradigm for Future NASA and U.S. Air Force Vehicles, AIAA, 2012. doi
- R. E. Kalman, A New Approach to Linear Filtering and Prediction Problems, J. Basic Engineering, 1960. doi
- Functional Mock-up Interface standard: fmi-standard.org
- OPC UA overview: opcfoundation.org
- Eclipse Ditto documentation: eclipse.dev/ditto