Gas Gathering System
An upstream gas field, over Enron Modbus. Four well pads, a compressor station, a tank battery and a custody transfer meter run, 121 registers across eight flow computers.
Operator view | port 4019 |
|---|---|
Protocol server | port 5029 |
Devices and tags | 8 flow computers, 121 registers |
Litmus Edge driver | Enron Modbus |
Image | oil-gas-production-demo |
Scenarios
Four scenarios at once, each on a different part of the field, with healthy neighbours beside it so the comparison is on screen rather than remembered.
- A marginal well loading up with liquid, and dying. A gas well carries its own produced water up the tubing as mist, but only while the gas moves fast enough. Below the Turner critical velocity the droplets fall back and stand in the tubing. That column is weight, so the flowing bottomhole pressure rises, and inflow goes as the square of the pressure difference, so the rate falls, so the velocity falls further. The well spirals down from 570 to about 120 Mcf/d and nothing alarms, because it is still flowing the whole way. The tell is not the rate: gas in the annulus cannot get past the standing column, so casing pressure climbs while tubing pressure sags, and two readings that normally sit 6 psi apart separate to nearly 400.
- The sales meter quietly drifting. The orifice differential transmitter loses its zero. AGA-3 makes flow go as the square root of differential pressure, so a 4 percent error in inches of water is about 2 percent on volume, in one direction, on every hour of a measurement the gas is invoiced against. The check meter in series is what makes it visible.
- A flare pilot that has gone out. The thermocouple goes cold while gas is still routed to the stack, so it leaves unburned. That is both a loss and a reportable event, and methane is roughly 28 times CO2 over a hundred years, so venting a unit of gas is not the same as selling it cheap.
- A separator dump valve stuck shut. Level climbs to the high level switch and liquid begins to carry over into the gas leg, which is what damages the meter run downstream.
One cheap fault becoming an expensive one
A loading well does not flow smoothly, it slugs. Those slugs arrive at the tank battery two miles downstream as liquid the level controller did not plan for, so the separator floods and carries over with no fault injected there at all. Nothing links the two in the model; the well pad produces slugs and the separator has to deal with what arrives.
Tag names
Enron Modbus is the gas measurement dialect of Modbus, and its defining feature is that the address block declares the type. A real flow computer lays itself out this way and a real polling host relies on it:
Addresses | What lives there | Register type |
|---|---|---|
1000-1999 | booleans | 1x |
3000-3999 | 16-bit integers | 3x |
5000-5999 | 32-bit integers | 5x |
7000-7999 | 32-bit floats | 7x |
So 7011 is a flow rate because it is a float in the 7000 block, and every reading on the operator view is labelled with the register it arrived as. What is on screen and what is in DeviceHub are the same address.
One listener serves all eight stations and Litmus Edge tells them apart by RTU station id, which is how a gathering system is really polled: one host, many outstations on one line.
What it creates on Litmus Edge
Eight DeviceHub devices on the Enron Modbus driver, 121 registers. Four Digital Twin models across eight instances, which is a better account of this field than one would be: the four pads share a WellPad model, the sales and check meters share a MeterRun because they are the same measurement made twice, and a separator is not a well pad. Four Analytics groups:
Group | What it computes, on the Edge |
|---|---|
Liquid Loading Risk | gas velocity against the Turner critical velocity |
Custody Measurement Check | the sales meter against its check meter in series |
Flare Combustion Check | pilot thermocouple against gas routed to the stack |
Separator Containment | separator level against the dump valve |
Requirements
- Litmus Edge 4.0.x, reachable over HTTPS from the host running the solution
- An API token for that Edge, or a username and password, or a client id and secret
- Docker, and Docker Compose for the compose deployment
- A network path in both directions: the solution reaches the Edge's API, and the Edge reaches this host on the protocol port
Deploy
The solution ships two ways. Use the registry if the host can reach Google Artifact Registry, and the downloaded archive if it cannot.
Option 1: pull from the registry
Requires a read-only registry credential, supplied by Litmus separately.
cat key.json | docker login -u _json_key --password-stdin https://us-docker.pkg.devSave this as docker-compose.yml:
services:
oil-gas-production:
image: us-docker.pkg.dev/litmus-customer-facing/litmus-solutions/oil-gas-production-demo:0.3.0
container_name: oil-gas-production-demo
restart: unless-stopped
ports:
- target: 4019
published: 4019
protocol: tcp
- target: 5029
published: 5029
protocol: tcp
environment:
# Fill these in.
EDGE_URL: "${EDGE_URL:-}"
EDGE_API_TOKEN: "${EDGE_API_TOKEN:-}"
SIM_HOST: "${SIM_HOST:-auto}"
APPLY_ON_START: "${APPLY_ON_START:-1}"
EDGE_VERIFY_TLS: "${EDGE_VERIFY_TLS:-0}"
HTTP_PORT: "4019"
ENRON_PORT: "5029"
SEED: "11"
LOG_LEVEL: "INFO"EDGE_URL=https://10.0.0.5
EDGE_API_TOKEN=your-token
SIM_HOST=10.0.0.9docker compose up -dOpen http://localhost:4019.
Option 2: load the downloaded archive
Download oil-gas-production-demo-0.3.0-amd64.tar.gz from the solution's page on portal.litmus.io. The archive is the container image, not a source bundle, and needs no registry access.
docker load -i oil-gas-production-demo-0.3.0-amd64.tar.gzdocker load prints the image it added:
Loaded image: us-docker.pkg.dev/litmus-customer-facing/litmus-solutions/oil-gas-production-demo:0.3.0That reference is the one the compose file above already names, so the same docker-compose.yml and .env now work offline with no docker login:
docker compose up -dTo run it without Compose, pass the same three values on the command line:
docker run -d \
--name oil-gas-production-demo \
--restart unless-stopped \
-p 4019:4019 \
-p 5029:5029 \
-e EDGE_URL=https://10.0.0.5 \
-e EDGE_API_TOKEN=your-token \
-e SIM_HOST=10.0.0.9 \
us-docker.pkg.dev/litmus-customer-facing/litmus-solutions/oil-gas-production-demo:0.3.0Configuration
Variable | Default | What it does |
|---|---|---|
EDGE_URL | the Docker host | Litmus Edge to configure |
EDGE_API_TOKEN | unset | token for that Edge. Or a client id and secret |
SIM_HOST | auto | address the Edge polls to reach this solution |
APPLY_ON_START | 1 | configure the Edge on startup |
EDGE_VERIFY_TLS | 0 | 1 checks the Edge's certificate |
SEED | 11 | makes a run repeatable |
Operator view
Across the top: the field rate, and then three costs stated as a run rate per day rather than a running total. That is deliberate. A total is worth pennies in the first minute of a scenario and reads as "nothing is wrong" while gas is actively venting; a rate is visible the moment something breaks, and "$509 a day" is the number that decides whether to send somebody tonight. The amount accumulated so far sits underneath as the evidence.
Below, the gathering system left to right: the four pads with their wellbores drawn, so the liquid standing in the tubing is something you watch rise; then compression, the separator with its level and dump valve, the flare with a flame that is there or is not, and the two meter runs in series with the difference between them printed underneath.
Faults are the four scenarios above, injected per station, each with a card naming the DataHub topics to watch and what changes about them.
Apply and remove
The header carries both controls. Apply to Edge pushes the devices, twins and analytics; it greys out once the solution is on the Edge. Remove from Edge previews first, listing exactly what would be removed and from which Edge, and only then offers to remove it.
The solution removes only what it created. It also records which Edge its apply landed on and removes from that one, so repointing at a different Edge in between cannot leave the first one carrying a solution nobody can see.
The operator view stays on its waiting panel until Litmus Edge is verified to be carrying the field. Every value on the page is read back from the Edge, so a page showing data is itself evidence the chain is working.
Reports
Export report produces one self-contained HTML file: the shift's money figures, production by pad against each well's own baseline, the sales meter against the check meter, the separator and the flare. No scripts and no external requests, so it opens offline and prints.
Stop the solution
docker compose downUse Remove from Edge on the operator view, or Clean up in the Solutions provisioner, to remove what it created on the Edge.