# Swivel-ring flange

Pipeline and riser swivel-ring flange. A forged weld hub with a bevelled weld end carries the gasket face, RTJ with the B16.5 octagonal groove for the class or RF. A loose drilled ring sits on a tapered seat behind the hub head. `swivel` turns the ring so the bolt holes can be aligned with the mating flange without rolling the pipeline. Drilling and OD follow ASME B16.5, so the flange mates a standard flange; the head, seat and neck proportions are typical.

**Standards:** ASME B16.5 (drilling), MSS SP-44, ASME B16.20, ASME B16.25

## Installation

```bash
npx -y https://decktwin.com/cli/deck-twin-0.2.2.tgz add swivel-ring-flange
```

Copies `piping/swivel-ring-flange.tsx`, any helper files it imports and the shared `lib/` into `src/components/deck-twin/`. Needs `three`, `@react-three/fiber` and `@react-three/drei`.

## Usage

```tsx
import { Canvas } from '@react-three/fiber'
import { OrbitControls } from '@react-three/drei'
import { Stage } from './components/deck-twin/lib/stage'
import { SwivelRingFlange } from './components/deck-twin/piping/swivel-ring-flange'

export default function Scene() {
  return (
    <Canvas shadows camera={{ position: [8, 5, 9], fov: 35 }}>
      <Stage>
        <SwivelRingFlange />
      </Stage>
      <OrbitControls makeDefault />
    </Canvas>
  )
}
```

## Props

| Prop | Type | Default | Range |
| --- | --- | --- | --- |
| `nps` | `number (NPS, in)` | `12` |  |
| `rating` | `150 \| 300 \| 600 \| 900 \| 1500 \| 2500` | `600` |  |
| `facing` | `"RTJ" \| "RF"` | `"RTJ"` |  |
| `schedule` | `"10" \| "20" \| "40" \| "STD" \| "80" \| "XS" \| "160" \| "XXS" \| "10S" \| "40S" \| "80S"` | `"80"` |  |
| `swivel` | `number (°)` | `0` | 0 – 45 |
| `stub` | `number (m)` | `0` | 0 – 1 |
| `tag` | `string` | `""` |  |
| `...group` | position, rotation, scale… |  |  |

## Ports

Flange faces in the component's local frame, metres. Origin: pipe centreline, flow along X.

| Id | Label | Position | Direction | Size | Class |
| --- | --- | --- | --- | --- | --- |
| `A` | Hub face | 0.000, 0.000, 0.000 | -1, 0, 0 | 12" | 600 |

## P&ID

In section: a weld hub carrying the gasket face with a separate loose ring that can rotate around it to align bolt holes; drawn with a curved arrow on some pipeline tie-in details. On P&IDs a flange bar, often at a riser or spool tie-in.

tag SRF · ASME B16.5 flange types drawn in half-section (GA / isometric detail convention); on P&IDs every flange is a short bar; MSS SP-44 / pipeline convention

Found at subsea and riser tie-ins where bolt-hole alignment is hard. On P&IDs it is indistinguishable from other flanges; read the tie-in detail.

Pipeline flange datasheet (ASME B16.5 drilling, MSS SP-44) fields and the props they set:

| Field | Prop |
| --- | --- |
| Nominal size | `nps` |
| Pressure class | `rating` |
| Facing | `facing` |
| Pipe schedule | `schedule` |
| Tag number | `tag` |

## Typical suppliers

Oil States. The geometry is generic and sized from public standards. It is not vendor CAD.

## Source

`piping/swivel-ring-flange.tsx`. This is the component file only: it imports shared code, so install it with the command above rather than pasting it alone. https://decktwin.com/r/swivel-ring-flange.json has every file.

```tsx
// Swivel-ring flange: pipeline / riser weld hub with a loose drilled swivel ring for bolt-hole alignment, ASME B16.5 / MSS SP-44 drilling, RTJ or RF hub face.
import type { Kit, Profile } from '../lib/kit'
import { KitMesh, useKit, type GroupProps } from '../lib/mesh'
import { port, type Port } from '../lib/ports'
import { flange as flangeDims, pipeOD, pipeWall, type Rating, type Schedule } from '../lib/standards'
import { BEVEL_TAN, LAND, boltHole, buttWeld, drilledPlate, facingHeight, facingLathe, pipeStub, ringGroove, seg, stamp, type Facing } from './_flanges'

export interface SwivelRingFlangeProps {
  /** nominal pipe size, inches */
  nps?: number
  rating?: Rating
  facing?: Facing
  schedule?: Schedule
  /** swivel ring rotation about the axis, degrees (bolt-hole alignment) */
  swivel?: number
  /** pipe stub welded to the hub, m */
  stub?: number
  tag?: string
}

export const defaults = { nps: 12, rating: 600 as Rating, facing: 'RTJ' as Facing, schedule: '80' as Schedule, swivel: 0, stub: 0, tag: '' }

// Port A is the hub face at the origin, pointing -X; hub and pipeline run along +X.
export function ports(p: SwivelRingFlangeProps = {}): Port[] {
  const { nps, rating } = { ...defaults, ...p }
  return [port('A', 'Hub face', [0, 0, 0], [-1, 0, 0], nps, rating)]
}

export function SwivelRingFlange({ nps = defaults.nps, rating = defaults.rating, facing = defaults.facing, schedule = defaults.schedule, swivel = defaults.swivel, stub = defaults.stub, tag = defaults.tag, ...group }: SwivelRingFlangeProps & GroupProps) {
  const hub = useKit((k) => buildHub(k, nps, rating, facing, schedule, Math.max(0, stub), tag), [nps, rating, facing, schedule, stub, tag])
  const ring = useKit((k) => buildRing(k, nps, rating, facing), [nps, rating, facing])
  return (
    <group {...group}>
      <KitMesh data={hub} />
      <KitMesh data={ring} rotation-x={(swivel * Math.PI) / 180} />
    </group>
  )
}

export default SwivelRingFlange

/** typical: hub head, neck and swivel-ring proportions from pipeline swivel-ring practice. */
function dims(nps: number, rating: Rating, facing: Facing) {
  const f = flangeDims(nps, rating)
  const ro = pipeOD(nps) / 2
  const h = facingHeight(facing, nps, rating)
  const rFace = facing === 'RTJ' ? ringGroove(nps, rating).K / 2 : f.raisedFace / 2
  const rMax = f.boltCircle / 2 - boltHole(f.boltDia) / 2 - 0.005
  const Rn = Math.max(f.hubDia / 2, ro * 1.06) // neck under the ring
  const Rh = Math.min(Math.max(rFace + 0.006, Rn + 0.012), rMax - 0.0015) // head
  const t = f.thickness * 1.05 // ring thickness
  const th = Math.max(t * 0.42, h + 0.012) // head length
  const k = Math.tan((20 * Math.PI) / 180) // head-to-ring seat taper (typical)
  const yc = -th - (Rh - Rn) * k
  const yRing = -0.003 // ring front sits just behind the head face
  return { f, ro, h, Rn, Rh, t, th, k, yc, yRing, yBack: yRing - t }
}

function buildHub(k: Kit, nps: number, rating: Rating, facing: Facing, schedule: Schedule, stub: number, tag: string) {
  const d = dims(nps, rating, facing)
  const { ro, h, Rn, Rh, th, yc } = d
  const ri = ro - pipeWall(nps, schedule)
  const base: [number, number, number] = [h, 0, 0]
  const dir: [number, number, number] = [-1, 0, 0]
  const n = seg(Rh * 2)
  const { rFace, yb } = facingLathe(k, facing === 'FF' ? 'RF' : facing, nps, rating, ri, base, dir, n)
  // neck runs past the ring, then tapers to the pipe OD at the weld bevel
  const yN = d.yBack - Math.max(0.015, d.t * 0.3)
  const y0 = yN - Math.max(0.03, (Rn - ro) * 3.5)
  const bev = Math.max(0, ro - ri - LAND) * BEVEL_TAN
  const outer: Profile = [[ro, y0 + bev], [ro, y0 + bev + 0.004], [Rn, yN], [Rn, yc], [Rh, -th], [Rh, 0]]
  if (rFace < Rh) outer.push([rFace, 0])
  k.lathe('flangeSteel', outer, base, dir, n)
  k.lathe('machined', [[ri, y0], [ri + LAND, y0], [ro, y0 + bev]], base, dir, n)
  k.lathe('pipeBore', [[ri, yb], [ri, y0]], base, dir, n)
  stamp(k, tag || `${nps}" ${rating}# SWIVEL ${facing === 'RTJ' ? ringGroove(nps, rating).ring : 'RF'}`, base, dir, Rn, d.yBack - 0.003, Math.max(yN + 0.003, d.yBack - 0.003 - (Rn * 0.2)))
  if (stub > 0) {
    pipeStub(k, [h - y0, 0, 0], [1, 0, 0], stub, nps, schedule, 'open', 'bevel')
    buttWeld(k, [h - y0, 0, 0], [1, 0, 0], ro, ro - ri)
  }
}

function buildRing(k: Kit, nps: number, rating: Rating, facing: Facing) {
  const d = dims(nps, rating, facing)
  const { f, h, Rn, Rh, k: tk, th, yRing, yBack } = d
  const base: [number, number, number] = [h, 0, 0]
  const dir: [number, number, number] = [-1, 0, 0]
  const rc = Rh + 0.0015
  const rr = Rn + 0.0015
  const n = seg(f.od)
  drilledPlate(k, 'flangeSteel', 'flangeSteel', [h - yBack, 0, 0], dir, d.t, rc, f.od / 2, { n: f.bolts, bc: f.boltCircle, hole: boltHole(f.boltDia) }, n)
  // counterbore over the head, tapered seat, bore over the neck, back face
  const ySeat = -th - (rc - rr) * tk
  k.lathe('machined', [[rc, yRing], [rc, -th], [rr, ySeat], [rr, yBack], [rc, yBack]], base, dir, n)
}
```
