Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

SO₂ Virtual Analyzer: Predictive Emissions Monitoring (PEMS)

A machine-learning virtual analyzer that predicts stack SO₂ concentration for a sulfuric-acid plant from ordinary process instrumentation, so emissions monitoring survives an outage of the physical CEMS (Continuous Emissions Monitoring System).

Test R² = 0.908, RMSE = 11.7 ppm SO₂ on a strictly held-out, chronologically later time window, with a deployment-faithful, leakage-safe evaluation and an explicitly outage-safe feature set. Deployed in a live plant and passing its Relative Accuracy Test Audit (RATA).

When a regulatory CEMS goes offline for maintenance, calibration, or a fault, a plant still needs a defensible emissions number. This project trains a gradient-boosted model on temperatures, flows, and pressures that stay available during a CEMS outage, and serves it as a live prediction inside the plant historian on a 15-minute cadence, with monthly automatic retraining.

Production status

This is not a notebook experiment. The analyzer is deployed in a live sulfuric-acid plant, where it runs continuously on SEEQ Data Lab, scores every 15 minutes, and has been validated against the certified CEMS in production. It passes the Relative Accuracy Test Audit (RATA), the field validation against a reference method that a predictive monitor must clear under EPA Performance Specification 16 to serve as a CEMS backup. Plant engineering leadership adopted it as the outage-monitoring solution.

Why this is hard, and what makes the result trustworthy

A model that scores well offline but leans on signals that vanish during an outage is worthless in production. Most of the engineering here is about earning a number you can actually deploy:

  • Outage-safe by construction. The prediction target is the CEMS SO₂ analyzer, so every CEMS/analyzer channel and status flag is excluded from the feature set. The model may only use process instrumentation that keeps reporting when the CEMS is down. There are no target-history / autoregressive features either, because the target is unavailable during an outage by definition.
  • Leakage-safe evaluation. Data is split into chronological calendar-day blocks (train is oldest, validation is middle, test is newest). All tuning and early stopping use the validation block; the test block is scored exactly once. No shuffled or stratified splits that would let the model peek at neighboring timestamps.
  • Forward-chaining cross-validation simulates monthly-retrain deployment with expanding-window folds that always predict the future, reported alongside the single test score so the headline number is not a lucky window.
  • A real scoring contract, not just a metric. Predictions are emitted only when the plant is running and feature coverage clears a threshold, and negative predictions are clipped. These are the same rules the live scorer enforces.

The learned feature importances land exactly where process chemistry says they should, on converter-bed and boiler outlet temperatures and sulfur feed flow, a good sign the model captured conversion physics rather than an artifact.

Results

Real model performance: a continuous 48-hour trace of the deployed model tracking actual stack SO₂, a predicted-vs-actual parity plot on the held-out test set (R² 0.908), and the residual distribution.

SO₂ is shown as a relative magnitude derived from the real CEMS ppm signal, with absolute values and dates withheld to protect confidentiality. That rescaling is a single affine transform applied to actual and predicted alike, so R² is unchanged; error is quoted as a percent of the operating range.

Split R² RMSE (ppm) MAE (ppm)
Validation (chronological, middle) 0.954 9.2 7.0
Test (chronological, newest, scored once) 0.908 11.7 10.6
Forward-chaining CV (mean of 4 expanding folds) 0.873 14.5 n/a

Trained on a 6-month lookback at a 15-minute grid, with 180 features selected by mutual information from about 120 candidate process signals. In production scoring over roughly 17k plant-running intervals, 99.97% met the coverage bar and produced a prediction.

What the model learned

The most telling check on a virtual analyzer is whether it learned the physics or just fit noise. Aggregating the model's feature importance by process stage shows it concentrates almost entirely on the sulfur furnace, waste-heat boiler, and converter-bed outlet temperatures, plus sulfur feed flow, and puts near-zero weight on the downstream acid towers.

Feature importance mapped onto the contact process. Importance concentrates on the sulfur furnace, waste-heat boiler, and converter beds, and is near zero on the downstream absorption towers.

That is exactly right for the contact process. Stack SO₂ is governed by how much SO₂ is generated (sulfur feed and furnace) and how completely it is catalytically converted to SO₃ across the beds, where bed outlet temperatures track conversion efficiency. The absorption end does not change how much SO₂ escaped conversion, and the model gives it almost no weight. Recovering that structure on its own is strong evidence the model captured real process behavior rather than a spurious correlation. See docs/tag_glossary.md for the full tag map.

Architecture

flowchart LR
    subgraph Historian["Plant historian (SEEQ)"]
        A[Process signals<br/>temps, flows, pressures]
        C[CEMS SO2 analyzer<br/>training label only]
    end
    A --> B[Ingest<br/>tag discovery + gridded pull]
    C --> B
    B --> P[Preprocess<br/>causal fill, quality masks<br/>run/outage gating]
    P --> F[Feature build<br/>lags, deltas, rolling means<br/>+ cyclic time features]
    F --> S[MI feature selection<br/>top 180]
    S --> T[CatBoost<br/>chronological split<br/>inner-val early stopping]
    T --> E[Evaluate<br/>held-out test + forward CV]
    E --> M[(Model + scoring config<br/>+ model card)]
    M --> L[Live scorer<br/>every 15 min]
    L --> W[Prediction signal<br/>pushed to historian]
Loading

Two deployment loops run as scheduled SEEQ Data Lab jobs.

Monthly retrain (notebooks/01_monthly_model_pipeline.ipynb) pulls the latest 6 months, rebuilds features, retrains, re-evaluates, and republishes the model plus scoring artifacts. This keeps the model current as catalyst age and operating regime drift.

15-minute live scoring (notebooks/02_push_current_predictions.ipynb) pulls a short feature history, scores the latest interval, and pushes the SO₂ prediction back into the historian as a signal. It is optimized so scheduled runs never overlap (minimal tag pull, 3-hour history window, cached worksheet setup).

Repository layout

src/
  train_pipeline.py     End-to-end training: ingest, preprocess, features, select, train, evaluate, export
  score_and_push.py     Live 15-minute scorer: pull, feature-coverage gate, predict, push
  paths.py              Artifact path resolution
notebooks/
  01_monthly_model_pipeline.ipynb    Scheduled monthly retrain (SEEQ Data Lab)
  02_push_current_predictions.ipynb  Scheduled 15-minute live scoring
docs/
  methodology.md        Design decisions: outage-safety, leakage-safe splits, RATA, scoring contract
  model_card.md         Model card (intended use, data, metrics, limitations)
  tag_glossary.md       Synthetic tag codes to physical meaning
results/
  model_card.json       Machine-readable metrics, leakage audit, promotion checks
  model_metrics.json    Full metrics + selected feature list
  scoring_config.json   Frozen scoring contract used by the live scorer
  tag_reference.csv     Full tag glossary

Tech stack

Python, CatBoost (gradient-boosted trees, GPU-trained), scikit-learn (mutual-information feature selection, metrics), pandas and NumPy, and SEEQ SPy (historian data access and deployment).

A note on data and confidentiality

This is a sanitized portfolio version. It contains no proprietary data, credentials, real instrument-tag names, plant identity, or server details. Every instrument tag has been replaced with a synthetic ISA-style code (for example TT-011, FT-015) whose meaning is documented generically in docs/tag_glossary.md. Those descriptions reference only standard double-contact double-absorption sulfuric-acid-plant chemistry, which is textbook. The metrics and figures shown are the model's real evaluation results, but SO₂ is normalized to a relative magnitude with absolute ppm values and all timestamps withheld. Only those normalized, dateless series are included; raw time-series datasets and trained model binaries are not.

License

MIT. The code and methodology here are free to reuse.

About

ML virtual analyzer predicting stack SO2 for a sulfuric-acid plant as a CEMS-outage backup. CatBoost, leakage-safe chronological evaluation, outage-safe features, deployed on SEEQ Data Lab. Test R2 0.91.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages