Skip to main content

Oil & gas — refinery operations

The problem. A refinery turns crude into products through tightly-coupled units — distillation columns, crackers, reformers — each running a delicate balance of temperature, pressure and flow. Drift off that balance and the unit makes off-spec product (reprocessing cost), burns excess energy (the refinery's second-biggest expense after feed), or trips entirely. Operators need the process KPIs that reveal drift and an alert when a unit heads off-spec, early enough to nudge it back.

What we solve here is keeping units on-spec and energy-efficient by turning raw process tags into the few numbers that actually run the plant.

Set up demo data​

New workspace? Run this once (Python) to create the crude unit's energy series with a day of hourly readings, so the roll-up in step 1 has data. Safe to re-run.

import intellistream_datahub_sdk, numpy as np, pandas as pd

client = intellistream_datahub_sdk.DataHubClient.from_env()

client.timeseries.create([intellistream_datahub_sdk.TimeSeries(external_id="cdu_1_energy_gj", name="CDU-1 energy", unit="gj", value_type="float")])
idx = pd.date_range(end=pd.Timestamp.now(tz="UTC"), periods=24, freq="1h")
client.timeseries.insert_from_lists(timestamps=idx, values=np.random.uniform(40, 60, 24), ts="cdu_1_energy_gj")

1. Track the process KPIs​

Column temperatures, reflux and feed are series; the numbers operators steer by — energy per barrel, reflux ratio, separation quality — come from rolling these up. See Query & aggregate.

var filter = new RetrieveFilter();
filter.setExternalId("cdu_1_energy_gj");
filter.setStart(ZonedDateTime.now().minusDays(1));
filter.setEnd(ZonedDateTime.now());
filter.setAggregates(List.of("sum"));
filter.setGranularity("1h");

var request = new DataRetriever<RetrieveFilter>();
request.setItems(List.of(filter));

// divide hourly energy by hourly throughput for energy-per-barrel
client.timeseries().retrieve(request).getItems().get(0).getDatapoints()
.forEach(p -> recordEnergyIntensity(p.getTimestamp(), p.getValue()));

2. Catch a process upset early​

When a column's top temperature drifts out of its control band, raise a process_upset event so the board operator corrects before product goes off-spec or the unit trips. See Turn readings into events.

EventModel upset = new EventModel();
upset.setExternalId("process_upset_cdu1_" + System.currentTimeMillis());
upset.setType("process_upset");
upset.setStatus("open");
upset.setMetadata(Map.of("unit", "crude_unit_1", "tag", "column_top_temp", "value_c", "168"));
upset.setEventTime(ZonedDateTime.now());
client.events().create(List.of(upset));

See the result​

Chart the hourly energy you rolled up — the bar chart is the shape the board operator watches for drift:

import matplotlib.pyplot as plt

rf = intellistream_datahub_sdk.RetrieveFilter(ts="cdu_1_energy_gj",
start=pd.Timestamp.now(tz="UTC") - pd.Timedelta(days=1), end=pd.Timestamp.now(tz="UTC"),
aggregates=["sum"], granularity="1h")
vals = [dp.sum for dp in client.timeseries.retrieve_datapoints(rf)[0].get_datapoints()]
plt.bar(range(len(vals)), vals); plt.title("CDU-1 hourly energy (GJ)"); plt.xlabel("hour"); plt.show()

See also​