{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "coiled-tubing-unit",
  "type": "registry:component",
  "title": "Coiled-tubing unit (reel, cabin, power pack)",
  "description": "Offshore coiled-tubing spread sized on a 5.6 × 3.0 × 4.7 m reel skid with a 2.9 m core: spoked reel with wraps of tubing (`tubingOd`, `fill`) turning when `running`, A-frame stands and bearings, chain-guarded drive sprocket with twin hydraulic motors and brakes, HP swivel with isolation valve, gauge and a 2\" inlet flange on the axle, level wind with guide tubes, diamond screw and a traversing carriage (`wind`) with depth counter, tubing paying off towards the injector, crash/lifting frame. With `spread`, an elevated operator cabin skid with wraparound windows and an electric hydraulic power pack (pump stack, reservoir, fan cooler, hose reels) are placed alongside and hosed to the reel drive. The injector head is not included.",
  "dependencies": [
    "three",
    "@react-three/fiber",
    "@react-three/drei"
  ],
  "registryDependencies": [
    "https://deck-twin-library.demoareeb.workers.dev/r/core.json"
  ],
  "files": [
    {
      "path": "drilling/coiled-tubing-unit.tsx",
      "type": "registry:file",
      "target": "components/deck-twin/drilling/coiled-tubing-unit.tsx",
      "content": "// Offshore coiled-tubing spread (API RP 16ST / API 5ST): reel skid with level wind and hydraulic drive, operator control cabin, hydraulic power pack.\nimport { useEffect, useMemo } from 'react'\nimport { Kit, type MatKey, type V3 } from '../lib/kit'\nimport { paint } from '../lib/materials'\nimport { KitMesh, type GroupProps } from '../lib/mesh'\nimport { flange, gauge, ladder, liftingLug, motor, motorSize, skid, tagPlate } from '../lib/parts'\nimport { port, type Port } from '../lib/ports'\nimport { Rotors, fanRotor, type RotorSpec } from '../lib/rotors'\nimport { inch } from '../lib/standards'\n\nexport interface CoiledTubingUnitProps {\n  /** coiled tubing OD, inches */\n  tubingOd?: number\n  /** reel flange diameter, m */\n  flangeDia?: number\n  /** reel core diameter, m */\n  coreDia?: number\n  /** tubing on the reel, fraction of the core-to-flange depth */\n  fill?: number\n  /** level-wind carriage position across the reel, 0 … 1 */\n  wind?: number\n  /** include the control cabin and power pack skids */\n  spread?: boolean\n  color?: string\n  tag?: string\n  /** turn the reel and the cooler fan */\n  running?: boolean\n}\n\nexport const defaults = { tubingOd: 1.75, flangeDia: 4.3, coreDia: 2.9, fill: 0.7, wind: 0.4, spread: true, color: '#1f5fa8', tag: 'CTU-01', running: false }\n\ntype P = typeof defaults\n\n// typical offshore reel skid: 5.6 × 3.0 × 4.7 m, core 2.9 m (114\"), drum width 2.3 m\nconst RL = 5.6\nconst RW = 3.0\nconst DW = 2.3\n\nfunction layout(p: Omit<P, 'tag' | 'running' | 'color' | 'fill' | 'wind' | 'tubingOd'>) {\n  const Rf = Math.max(1.6, Math.min(2.4, p.flangeDia / 2))\n  const yA = Rf + 0.35\n  return { Rf, yA, inlet: [0, yA, -DW / 2 - 0.62] as V3 }\n}\n\nexport function ports(props: CoiledTubingUnitProps = {}): Port[] {\n  const p = { ...defaults, ...props }\n  const l = layout(p)\n  return [port('inlet', 'Reel swivel inlet', l.inlet, [0, 0, -1], 2, 2500)]\n}\n\nexport function CoiledTubingUnit({ tag = defaults.tag, ...rest }: CoiledTubingUnitProps & GroupProps) {\n  const { tubingOd, flangeDia, coreDia, fill, wind, spread, color, running, ...group } = { ...defaults, ...rest }\n  const out = useMemo(() => build({ tubingOd, flangeDia, coreDia, fill, wind, spread, color, tag, running }), [tubingOd, flangeDia, coreDia, fill, wind, spread, color, tag])\n  useEffect(() => () => [out.built, ...out.rotors.map((r) => r.built)].forEach((b) => b.geometry.dispose()), [out])\n  return (\n    <group {...group}>\n      <KitMesh data={out.built} />\n      <Rotors rotors={out.rotors} running={running} />\n    </group>\n  )\n}\n\nexport default CoiledTubingUnit\n\nfunction build(p: P) {\n  const k = new Kit()\n  const l = layout(p)\n  const { Rf, yA } = l\n  const C: MatKey = paint(p.color)\n  const rotors: RotorSpec[] = []\n  const od = inch(Math.max(1, Math.min(3.5, p.tubingOd)))\n  const Rc = Math.min(Rf - 0.3, Math.max(0.9, p.coreDia / 2))\n  const Rt = Rc + (Rf - 0.08 - Rc) * Math.max(0, Math.min(1, p.fill))\n\n  // reel skid: base frame, A-frame stands with pillow-block bearings\n  k.at([0, 0, 0], undefined, () => skid(k, RL, RW, 'HEB 300', 'structSteelDark'))\n  const top = 0.308\n  for (const s of [-1, 1]) {\n    const z = s * (DW / 2 + 0.38)\n    for (const sx of [-1, 1]) k.rod(C, [sx * 1.6, top, z], [sx * 0.12, yA - 0.2, z], 0.1, 12)\n    k.box(C, [3.4, 0.25, 0.25], [0, top + 0.125, z])\n    k.rbox(C, [0.5, 0.4, 0.3], 0.03, [0, yA - 0.1, z])\n    k.cyl('nut', 0.14, 0.34, [0, yA, z], 'z', 24)\n  }\n\n  // reel (rotor about Z): core, flanges with spokes, tubing wraps, drive sprocket\n  const rk = new Kit()\n  rk.cyl(C, Rc, DW, [0, 0, 0], 'z', 64)\n  const wraps = Math.max(8, Math.round(DW / od))\n  const prof: [number, number][] = [[Rc, -DW / 2]]\n  for (let i = 0; i < wraps; i++) {\n    const z0 = -DW / 2 + (DW * i) / wraps\n    prof.push([Rt - od * 0.15, z0], [Rt, z0 + DW / wraps / 2])\n  }\n  prof.push([Rt - od * 0.15, DW / 2], [Rc, DW / 2])\n  rk.lathe(paint('#2a2c2f', { metalness: 0.6, roughness: 0.35 }), prof, [0, 0, 0], 'z', 72)\n  for (const s of [-1, 1]) {\n    const z = s * (DW / 2 + 0.03)\n    rk.lathe(C, [[Rf - 0.08, z - 0.03], [Rf, z - 0.03], [Rf, z + 0.03], [Rf - 0.08, z + 0.03], [Rf - 0.08, z - 0.03]], [0, 0, 0], 'z', 72)\n    rk.cyl(C, Rc + 0.05, 0.05, [0, 0, z], 'z', 64)\n    for (let i = 0; i < 12; i++) {\n      const a = (i / 12) * Math.PI * 2\n      rk.rod(C, [Math.cos(a) * (Rc - 0.1), Math.sin(a) * (Rc - 0.1), z], [Math.cos(a) * (Rf - 0.06), Math.sin(a) * (Rf - 0.06), z], 0.035, 8)\n    }\n    rk.cyl(C, 0.25, 0.1, [0, 0, z + s * 0.05], 'z', 24)\n  }\n  rk.cyl('stainless', 0.1, DW + 0.9, [0, 0, 0], 'z', 20)\n  // drive sprocket on the +Z flange\n  rk.lathe('nut', [[Rc + 0.05, DW / 2 + 0.07], [Rc + 0.14, DW / 2 + 0.07], [Rc + 0.14, DW / 2 + 0.11], [Rc + 0.05, DW / 2 + 0.11]], [0, 0, 0], 'z', 96)\n  rotors.push({ built: rk.build(), axis: 'z', pos: [0, yA, 0], rpm: 6 })\n  // chain guard and twin hydraulic drive motors with brake on the +Z side\n  const zg = DW / 2 + 0.18\n  k.lathe(C, [[Rc + 0.02, zg - 0.06], [Rc + 0.22, zg - 0.06], [Rc + 0.22, zg + 0.08], [Rc + 0.02, zg + 0.08]], [0, yA, 0], 'z', 64, Math.PI * 1.15, Math.PI * 0.7)\n  for (const dx of [-0.35, 0.35]) {\n    const mp: V3 = [dx, yA - Rc - 0.45, zg + 0.1]\n    k.cyl(C, 0.12, 0.3, mp, 'z', 20)\n    k.cyl('actuatorYellow', 0.14, 0.12, [mp[0], mp[1], mp[2] + 0.2], 'z', 20)\n    k.box(C, [0.3, mp[1] - top, 0.2], [dx, (mp[1] + top) / 2 - 0.1, zg + 0.1])\n  }\n  // HP swivel and inlet piping on the -Z axle end\n  const zi = -DW / 2 - 0.5\n  k.cyl('stainless', 0.11, 0.2, [0, yA, zi + 0.05], 'z', 20)\n  k.cyl('nut', 0.13, 0.04, [0, yA, zi + 0.12], 'z', 20)\n  flange(k, 2, 2500, l.inlet, '-z', true)\n  k.cyl('stainless', 0.045, 0.06, [0, yA, zi - 0.06], 'z', 14)\n  k.rbox('valveBody', [0.12, 0.16, 0.12], 0.02, [0, yA - 0.2, zi])\n  k.cyl('stainless', 0.03, 0.2, [0, yA - 0.1, zi], 'y', 10)\n  gauge(k, [0.15, yA + 0.1, zi], 'x', 0.12, 'bar', 1000)\n  k.rod('stainless', [0, yA + 0.1, zi], [0.15, yA + 0.1, zi], 0.01, 8)\n  // level wind: twin guide tubes and diamond screw across the reel front, traversing carriage, depth counter\n  const xl = Rf + 0.25\n  const ylw = yA + Rt * 0.55\n  for (const s of [-1, 1]) {\n    k.rod(C, [xl - 0.25, top, s * (DW / 2 + 0.38)], [xl, ylw - 0.1, s * (DW / 2 + 0.38)], 0.07, 10)\n    k.rod(C, [-0.4, yA - 0.15, s * (DW / 2 + 0.38)], [xl, ylw - 0.1, s * (DW / 2 + 0.38)], 0.06, 10)\n  }\n  k.rod('stainless', [xl, ylw, -DW / 2 - 0.4], [xl, ylw, DW / 2 + 0.4], 0.035, 12)\n  k.rod('stainless', [xl, ylw + 0.22, -DW / 2 - 0.4], [xl, ylw + 0.22, DW / 2 + 0.4], 0.035, 12)\n  k.rod('machined', [xl, ylw + 0.11, -DW / 2 - 0.4], [xl, ylw + 0.11, DW / 2 + 0.4], 0.05, 12)\n  const zc = -DW / 2 + 0.1 + (DW - 0.2) * Math.max(0, Math.min(1, p.wind))\n  k.rbox('actuatorYellow', [0.3, 0.4, 0.3], 0.03, [xl, ylw + 0.11, zc])\n  for (const dy of [0.29, -0.07]) k.cyl('rubberBlack', 0.06, 0.18, [xl + 0.2, ylw + dy, zc], 'z', 16)\n  k.rbox('stainless', [0.15, 0.12, 0.12], 0.02, [xl + 0.15, ylw + 0.44, zc])\n  // tubing paying off the top of the reel through the carriage towards the injector (+X)\n  const ty = yA + Rt\n  k.tube('tubing', [[0.2, ty, zc], [xl - 0.1, ylw + 0.11 + od, zc], [xl + 0.8, ylw + 0.4, zc], [xl + 1.4, ylw + 0.8, zc]], od / 2, 10, 1.2)\n  k.cyl('nut', od * 0.8, 0.08, [xl + 1.4, ylw + 0.8, zc], [0.8, 0.6, 0], 12)\n  // crash frame / lifting frame over the reel\n  const yf = yA + Rf + 0.15\n  for (const sx of [-1, 1])\n    for (const sz of [-1, 1]) k.box(C, [0.15, yf - top, 0.15], [sx * (RL / 2 - 0.1), (yf + top) / 2, sz * (RW / 2 - 0.1)])\n  for (const sz of [-1, 1]) k.box(C, [RL, 0.15, 0.15], [0, yf, sz * (RW / 2 - 0.1)])\n  for (const sx of [-1, 1]) k.box(C, [0.15, 0.15, RW], [sx * (RL / 2 - 0.1), yf, 0])\n  for (const sx of [-1, 1]) for (const sz of [-1, 1]) liftingLug(k, [sx * (RL / 2 - 0.1), yf + 0.075, sz * (RW / 2 - 0.1)], 'x', 1.5, C)\n  tagPlate(k, p.tag, [-RL / 2 + 0.8, 0.15, RW / 2 + 0.002], 'z', 0.3)\n\n  if (p.spread) {\n    // control cabin skid (-X), elevated cab with wraparound windows and access stair\n    const xc = -RL / 2 - 0.4 - 1.6\n    k.at([xc, 0, 0], undefined, () => {\n      skid(k, 3.2, 2.6, 'HEB 200', 'structSteelDark')\n      const yc0 = 1.0\n      for (const sx of [-1, 1]) for (const sz of [-1, 1]) k.box('structSteelDark', [0.15, yc0 - 0.21, 0.15], [sx * 1.4, (yc0 + 0.21) / 2, sz * 1.15])\n      k.box('cladding', [3.0, 0.1, 2.4], [0, yc0 + 0.05, 0])\n      for (const sz of [-1, 1]) k.box('cladding', [3.0, 0.9, 0.05], [0, yc0 + 0.55, sz * 1.175])\n      k.box('cladding', [0.05, 2.5, 2.4], [-1.475, yc0 + 1.35, 0])\n      k.box('cladding', [0.05, 0.9, 2.4], [1.475, yc0 + 0.55, 0])\n      k.box('glass', [0.02, 1.2, 2.3], [1.48, yc0 + 1.6, 0])\n      for (const sz of [-1, 1]) k.box('glass', [2.9, 1.2, 0.02], [0, yc0 + 1.6, sz * 1.18])\n      k.box('cladding', [3.1, 0.4, 2.5], [0, yc0 + 2.4, 0])\n      k.rbox('cladding', [0.8, 0.3, 0.9], 0.04, [-0.6, yc0 + 2.75, 0])\n      k.box('plasticGrey', [0.6, 0.8, 1.8], [0.9, yc0 + 0.5, 0])\n      k.box('rubberBlack', [0.05, 0.35, 0.6], [1.2, yc0 + 1.2, 0])\n      ladder(k, [-1.2, 0.21, 1.3], yc0 + 0.1, [0, 0, 1], { cage: false })\n      tagPlate(k, 'CT CONTROL CABIN', [0, yc0 + 0.6, 1.205], 'z', 0.4)\n    })\n    // hydraulic power pack (-X of the cabin): electric motor, pump stack, reservoir, fan cooler, hose reels\n    const xp = xc - 1.6 - 0.4 - 1.5\n    const m = motorSize(150, 4)\n    k.at([xp, 0, 0], undefined, () => {\n      const t2 = skid(k, 3.0, 2.4, 'HEB 200', 'structSteelDark')\n      k.at([0.2, t2 + m.H, -0.5], undefined, () => motor(k, { R: m.R, L: m.L, H: m.H, tbox: 'top' }))\n      for (let i = 0; i < 3; i++) k.cyl('castIron', 0.15 - i * 0.02, 0.22, [0.45 + i * 0.24, t2 + m.H, -0.5], 'x', 20)\n      k.rbox(C, [1.4, 1.0, 0.9], 0.04, [0.2, t2 + 0.5, 0.65])\n      k.cyl('glass', 0.025, 0.5, [0.2, t2 + 0.5, 1.105], 'y', 12)\n      k.cyl('brass', 0.05, 0.08, [-0.2, t2 + 1.04, 0.65], 'y', 12)\n      k.box('structSteelDark', [1.0, 1.3, 0.2], [-1.0, t2 + 0.9, 0])\n      k.box('nut', [0.9, 1.1, 0.06], [-1.0, t2 + 0.95, 0.12])\n      rotors.push(fanRotor('flangeSteel', 0.45, [xp - 1.0, t2 + 0.95, 0.2], 'z', 400, { blades: 6 }))\n      k.cyl('nut', 0.47, 0.04, [-1.0, t2 + 0.95, 0.23], 'z', 32)\n      for (const dz of [-0.9, 0.9]) {\n        k.cyl(C, 0.35, 0.4, [1.1, t2 + 0.55, dz], 'z', 28)\n        k.cyl('rubberBlack', 0.3, 0.38, [1.1, t2 + 0.55, dz], 'z', 28)\n      }\n      tagPlate(k, 'HPU', [0.2, t2 + 0.6, 1.106], 'z', 0.2)\n    })\n    // hydraulic hoses: power pack → reel drive motors, cabin control umbilical\n    for (const dz of [-0.06, 0.06]) k.spline('rubberBlack', [[xp + 1.1, 0.6, 0.9 + dz], [xp + 2.5, 0.35, 1.4 + dz], [-RL / 2 + 0.3, 0.4, 1.6 + dz], [0, 0.45, 1.65 + dz], [0.35, yA - Rc - 0.45, zg + 0.32 + dz]], 0.025, 10, 80)\n    k.spline('cable', [[xc + 1.0, 1.0, -1.2], [xc + 1.8, 0.4, -1.4], [-RL / 2 + 0.5, 0.4, -1.4]], 0.02, 8, 30)\n  }\n  return { built: k.build(), rotors }\n}\n"
    }
  ],
  "categories": [
    "drilling"
  ],
  "meta": {
    "export": "CoiledTubingUnit",
    "anchor": "base",
    "standards": [
      "API RP 16ST",
      "API 5ST",
      "API 16A (stack, not modelled)",
      "DNV 2.7-3"
    ]
  }
}