Skip to main content

Oil & gas — pipeline integrity

The problem. A pipeline leak is an environmental and financial emergency, and the two questions that follow are both urgent: is there a leak, and where. The signal for the first is a mass imbalance — what flows into a segment should equal what flows out, and a sustained gap with a pressure drop means product is escaping. The second is a graph question: given the leaking segment, which valves isolate it and what's downstream.

What we solve here is detecting a leak from the flow balance and locating it on the network so the right valves close fast.

Set up demo data

New workspace? Run this once (Python) to create the segment's flow series (with an inflow/outflow gap — a leak) and the pipeline graph the locate step walks. Safe to re-run.

import datahub_sdk, pandas as pd

client = datahub_sdk.DataHubClient.from_env()

# flow series: inflow 1000 vs outflow 940 m³/h → a 60 m³/h imbalance (leak)
for s, v in [("segment_pn_07_flow_in_m3h", 1000.0), ("segment_pn_07_flow_out_m3h", 940.0)]:
client.timeseries.create([datahub_sdk.TimeSeries(external_id=s, name=s, unit="m3h", value_type="float")])
client.timeseries.insert_from_lists(timestamps=[pd.Timestamp.now(tz="UTC")], values=[v], ts=s)

# the network: stations, the segment, and the valves that isolate it
client.resources.create(
[datahub_sdk.Resource(external_id=x, name=x, labels=[x]) for x in
["station_kollsnes", "segment_pn_07", "station_mongstad", "valve_v18", "valve_v19"]],
[datahub_sdk.RelForm.by_external_ids("station_kollsnes", "segment_pn_07", "connects_to"),
datahub_sdk.RelForm.by_external_ids("segment_pn_07", "station_mongstad", "connects_to"),
datahub_sdk.RelForm.by_external_ids("segment_pn_07", "valve_v18", "isolated_by"),
datahub_sdk.RelForm.by_external_ids("segment_pn_07", "valve_v19", "isolated_by")])

You'll also need small latest(id) and tolerance(flow) helpers — latest reads the last value of a series, tolerance returns an acceptable gap (say 1% of inflow).

1. Detect the imbalance

Each segment reports inlet and outlet flow and pressure. A sustained inlet-minus-outlet gap raises a leak_suspected event scoped to the segment. See Turn readings into events.

double inflow = latest("segment_pn_07_flow_in_m3h");
double outflow = latest("segment_pn_07_flow_out_m3h");

if (inflow - outflow > tolerance(inflow)) {
EventModel leak = new EventModel();
leak.setExternalId("leak_suspected_pn07_" + System.currentTimeMillis());
leak.setType("leak_suspected");
leak.setStatus("critical");
leak.setMetadata(Map.of("segment", "segment_pn_07",
"imbalance_m3h", String.valueOf(inflow - outflow)));
leak.setEventTime(ZonedDateTime.now());
client.events().create(List.of(leak));
}

2. Locate it and find the isolation valves

Model the pipeline as a graph — stations joined by segments, valves on the segments. Walk out from the leaking segment to find the valves that bound it and everything downstream that must be isolated. This is the connectivity traversal applied to a flow network.

RelatedResourcesForm form = new RelatedResourcesForm();
form.setExternalId("segment_pn_07");
form.setDepth(3);
form.setRelationshipTypes(List.of("connects_to", "isolated_by"));

ResourceNetwork around = client.resources().fetchRelated(form);
around.nodes().stream()
.filter(n -> n.getExternalId().startsWith("valve_"))
.forEach(v -> System.out.println("close: " + v.getExternalId()));

See the result

With the demo leak in place (inflow 1000 vs outflow 940), step 1 raises a leak_suspected event and step 2 prints the valves that bound the segment:

close: valve_v18
close: valve_v19

See also