Assembling plants

A plant is a plant.json: a list of items (components placed in space) and lines (pipe joining one item's port to another's). <Plant> renders it, and deck-twin validate validates it.

{
  "name": "HP separation",
  "items": [
    { "tag": "DECK-1", "component": "deck-module", "position": [0, 0, 0], "props": { "length": 30, "width": 20 } },
    { "tag": "V-100", "component": "horizontal-separator", "position": [-6, 0, -3], "props": { "service": "3-phase", "pressure": 40 } },
    { "tag": "P-101A", "component": "centrifugal-pump", "position": [6, 0, 4], "rotation": 180 }
  ],
  "lines": [
    {
      "tag": "6\"-PO-1001-D6A-N",
      "from": "V-100.liquid",
      "to": "P-101A.suction",
      "service": "oil",
      "inline": [{ "component": "gate-valve", "at": 0.25 }, { "component": "y-strainer", "at": 0.75 }]
    }
  ]
}

This is the only plant format the registry and the CLI use. It is built from registry components, so an agent should take names, props and port ids from the CLI, never from memory. The JSON Schema at /r/plant.schema.json carries the full list of component names, and deck-twin validate is the final gate.

Coordinates

Rule Detail
Units Metres and degrees.
Axes +X is east, +Y is up, +Z is south. rotation turns about +Y, counter-clockwise seen from above.
position [x, y, z] of the component's origin. For equipment and structures (anchor base), that is the centre of the footprint at the level it stands on.
Decks A deck-module has its walking surface at y = 0 in its own frame. If it sits at position: [0, 10, 0], equipment on it goes at y = 10.
Inline items Valves, strainers and inline meters (anchor inline) go on lines, not in items.

Items

Field Meaning
tag Unique. Use the plant's equipment numbering, e.g. V-1100, P-1500A, K-2200, CR-A1.
component A registry name, such as horizontal-separator. npx deck-twin list shows them all.
props Overrides for the component's props. npx deck-twin info <name> lists them, with ranges.
position [x, y, z], defaulting to the origin.
rotation Degrees about +Y.

Lines

Field Meaning
tag Line number, SIZE"-SERVICE-SEQ-CLASS-INSULATION, e.g. 8"-PG-2101-D6A-N
from, to "TAG.port", e.g. "V-100.gas". Port ids come from info. A battery limit, where the line leaves the plot (the P&ID's off-page connector), is { "at": [x, y, z], "label": "TO HOST" }. It renders as a flanged, blinded tie-in with a yellow band and a tag.
service One of oil, gas, multiphase, water, seawater, firewater, steam, air, nitrogen, chemical, flare, drain, fuel-gas, diesel, potable, cooling. It sets the ISO 14726 colour bands, and firewater makes the whole line red.
nps Defaults to the size of the from port.
schedule Pipe schedule, STD by default
insulated Draws aluminium cladding
elevation Height of the horizontal run. By default it is chosen from the port directions: above top nozzles, below bottom nozzles.
order xz runs east-west first, zx runs north-south first
via Waypoints [[x,y,z], …] that replace auto-routing, e.g. to run along a pipe rack
supports Stanchions under horizontal runs, on by default. Set false for lines carried by a rack.
inline [{ component, at, tag?, props? }]. at is the fraction along the line, from 0 to 1. Inline items take the line's size and class.

Line numbers are data

When a line's tag follows SIZE"-SERVICE-SEQ-CLASS-INSULATION, the assembler reads it. For 8"-PG-2101-D6A-N:

Part Value Sets
8" NPS 8 Size
PG Process gas service
2101 Sequence Nothing
D6A Piping class; the digit gives Class 600 Pressure class of the pipe-side flanges
N Not insulated insulated (anything other than N, e.g. H or P, means insulated)

Explicit fields on the line override the tag. Sizes also work as 1-1/2" or DN200. Mismatches between the tag and the nozzle are reported. Service codes and the class-digit convention are in assembly/linetag.ts; edit them to match your project's line-numbering procedure.

The router leaves each flange straight along the port's axis for 0.6 m. It then climbs or drops to the run elevation, runs along X then Z (or Z then X), and drops into the other flange. Each corner gets a B16.9 long-radius elbow. Each end gets a gasket, a mating weld-neck flange and studs, sized to that end's nozzle. When the two ends differ in size, a B16.9 concentric reducer goes in before the far end. Inline items cut the pipe, with a flange pair on each side; wafer bodies (butterfly, dual-plate check) are clamped by through-bolts.

Rendering

import { Plant, type ComponentMap } from './components/deck-twin/assembly/plant'
import * as horizontalSeparator from './components/deck-twin/vessels/horizontal-separator'
import * as centrifugalPump from './components/deck-twin/rotating/centrifugal-pump'
import * as gateValve from './components/deck-twin/valves/gate-valve'
import spec from './plant.json'

const components: ComponentMap = {
  'horizontal-separator': horizontalSeparator,
  'centrifugal-pump': centrifugalPump,
  'gate-valve': gateValve,
}

<Plant spec={spec} components={components} labels onReport={(issues) => console.table(issues)} />

npx deck-twin assemble plant.json writes this file for you, as src/PlantScene.tsx, with every import in place.

Offshore: show the sea

Add "sea": { "level": -14, "depth": 50 } to the plant. <Plant> draws the animated ocean component at mean sea level, with foam rings where the jacket legs cross the surface, and a seabed depth metres below it. The water is slightly transparent, so the jacket shows through it. For a jacket at y = 0, level is minus its airGap.

Field What
level Mean sea level as plant y, m
depth Water depth to the seabed, m. Leave it out for no seabed.
waves Significant wave height Hs, m (default 1.5)

Pass night to <Plant> for darker water. Add the sky component for a matching day or night sky, lights and haze. The sea is visual only: it is not part of the checks. See the offshore water injection example, which has a Studio / Day / Night switch.

Checks

onReport receives these issues:

Level Issue
Error Unknown component
Error Unknown tag or port
Error Duplicate tag
Error Line ends too close for two flanges
Warning Unconnected process port
Warning No straight run long enough for an inline valve
Warning Items overlap (measured from the meshes)
Warning Item stands on nothing, meaning y > 0 with no structure under it

The playground shows the same report for any pasted plant.json.

Layout rules of thumb

  • Clearance. Leave at least 1 m between equipment for access, and 1.5–3 m in front of pumps and exchangers for maintenance (tube-bundle pulls need the bundle length clear).
  • Flow direction. Wells and inlet sit upstream (west), then separation, then treatment and export (east). Put utilities and power at one end, and accommodation away from hydrocarbons.
  • Pumps. Put them close to and below their suction vessel, so the suction line is short and drains towards the pump.
  • Pipe racks. Run them along the module's long axis at 4–6 m. Route long lines over the rack with via, and set supports: false.
  • Decks. A typical deck stack is a cellar deck at y = 0, the production deck at y = 8–10, and the weather deck 8–10 m above that. Put deck-module items at those heights, and put the equipment on each deck at the same y.
  • Cranes. Pedestal cranes go at deck edges and corners, with the laydown area within the boom radius.

See Examples for complete plants.