Oil & gas — emissions & flaring (ESG)
The problem. Methane and routine flaring are now under hard scrutiny — from regulators with reporting mandates and penalties, and from investors tracking ESG performance. An operator has to measure flare volumes and methane, report them accurately, and catch excess flaring before it becomes a violation or a headline. The data exists across every platform; the job is turning it into auditable monthly numbers and timely alerts.
What we solve here is verifiable emissions reporting and early detection of excess flaring.
Set up demo data
New workspace? Run this once (Python) to create the platform's flare-volume series with a month of daily readings, so the monthly roll-up in step 1 has data. Safe to re-run.
import datahub_sdk, numpy as np, pandas as pd
client = datahub_sdk.DataHubClient.from_env()
client.timeseries.create([datahub_sdk.TimeSeries(
external_id="platform_north_flare_volume_m3", name="North platform flare volume", unit="m3", value_type="float")])
idx = pd.date_range(end=pd.Timestamp.now(tz="UTC"), periods=30, freq="1d")
client.timeseries.insert_from_lists(
timestamps=idx, values=np.random.uniform(3000, 5000, 30), ts="platform_north_flare_volume_m3")
1. Roll emissions up for reporting
Flare volume and methane rate are series per site. Monthly totals — and a CO₂-equivalent from them — are the numbers the regulatory submission and the ESG dashboard are built on. See Query & aggregate.
- Java
- Python
- Rust
var filter = new RetrieveFilter();
filter.setExternalId("platform_north_flare_volume_m3");
filter.setStart(ZonedDateTime.now().minusDays(30));
filter.setEnd(ZonedDateTime.now());
filter.setAggregates(List.of("sum"));
filter.setGranularity("1d");
var request = new DataRetriever<RetrieveFilter>();
request.setItems(List.of(filter));
double monthly = client.timeseries().retrieve(request).getItems().get(0)
.getDatapoints().stream().mapToDouble(p -> Double.parseDouble(p.getValue())).sum();
import pandas as pd
rf = datahub_sdk.RetrieveFilter(
ts="platform_north_flare_volume_m3",
start=pd.Timestamp.now(tz="UTC") - pd.Timedelta(days=30),
end=pd.Timestamp.now(tz="UTC"),
aggregates=["sum"], granularity="1d")
monthly = sum(dp.sum for dp in client.timeseries.retrieve_datapoints(rf)[0].get_datapoints())
use dataplatform_rust_sdk::generic::{DataWrapper, RetrieveFilter};
use chrono::Utc;
let filter = RetrieveFilter {
external_id: Some("platform_north_flare_volume_m3".into()),
start: Some(Utc::now() - chrono::Duration::days(30)),
end: Some(Utc::now()),
aggregates: Some(vec!["sum".into()]),
granularity: Some("1d".into()),
..Default::default()
};
let series = api.time_series
.retrieve_datapoints(&DataWrapper::from(vec![filter])).await?
.get_items().remove(0);
let monthly: f64 = series.datapoints.iter()
.filter_map(|p| p.sum).sum();
2. Catch excess flaring early
When a site's daily flaring crosses its permitted rate, raise a flaring_exceedance
event — with the volume and limit in metadata — so the operator corrects and the
compliance team has a dated record. See Turn readings into events.
- Java
- Python
- Rust
EventModel exceedance = new EventModel();
exceedance.setExternalId("flaring_exceedance_north_" + System.currentTimeMillis());
exceedance.setType("flaring_exceedance");
exceedance.setStatus("open");
exceedance.setMetadata(Map.of("site", "platform_north", "flared_m3", "142000", "permit_m3", "120000"));
exceedance.setEventTime(ZonedDateTime.now());
client.events().create(List.of(exceedance));
client.events.create([datahub_sdk.Event(
external_id=f"flaring_exceedance_north_{int(pd.Timestamp.now().timestamp())}",
type="flaring_exceedance", status="open",
event_time=pd.Timestamp.now(tz="UTC"),
metadata={"site": "platform_north", "flared_m3": "142000", "permit_m3": "120000"})])
use dataplatform_rust_sdk::events::Event;
use chrono::Utc;
let mut exceedance = Event::new(format!("flaring_exceedance_north_{}", Utc::now().timestamp()));
exceedance.r#type = Some("flaring_exceedance".into());
exceedance.status = Some("open".into());
exceedance.add_metadata("site".into(), "platform_north".into());
exceedance.add_metadata("flared_m3".into(), "142000".into());
exceedance.add_metadata("permit_m3".into(), "120000".into());
exceedance.set_event_time(Utc::now());
api.events.create(&vec![exceedance]).await?;
See the result
Print the month's total and chart the daily flaring — the bar chart is what the ESG dashboard shows:
import matplotlib.pyplot as plt
rf = datahub_sdk.RetrieveFilter(ts="platform_north_flare_volume_m3",
start=pd.Timestamp.now(tz="UTC") - pd.Timedelta(days=30), end=pd.Timestamp.now(tz="UTC"),
aggregates=["sum"], granularity="1d")
daily = [dp.sum for dp in client.timeseries.retrieve_datapoints(rf)[0].get_datapoints()]
print(f"monthly flaring: {sum(daily):,.0f} m³") # ~120,000 m³
plt.bar(range(len(daily)), daily); plt.title("North platform — daily flaring (m³)"); plt.show()
See also
- Query & aggregate — monthly emissions totals.
- Turn readings into events — the exceedance rule.
- Environmental — air quality — the same monitoring-and-report pattern.