Skip to content

Use Case: Pumping Stations ​

Build a complete inlet pumping station from reusable, parametric drawing templates

Shipped-library note: The library ships one canonical pumping-station template — the self-contained Level-1 sewage-pumping-station (a main wetwell + EQ overflow storage; see the parameters and topology in the Overview and the Template Language Reference). The smaller Level-0 building blocks below (submersible pump arrangement, valve-chamber manifold) are kept here as illustrative authoring patterns — they show how to model each sub-assembly with the SDK, but are not separately shipped templates.

Pumping stations are the classic case for drawing templates: the arrangement (pumps on rising mains into a common discharge header, with isolation, non-return valves, level control and flow metering) is highly repeatable, but the numbers (duty count, flows, pipe sizes) change on every job. The patterns below show how the shipped station is built up from these sub-assemblies.

TemplateLevelSlugRole
Submersible Pump Arrangement0submersible-pump-arrangementOne pump + riser + NRV + isolation + motor signals
Valve Chamber Manifold0valve-chamber-manifoldDischarge header with one isolation branch per pump
Inlet Pumping Station1inlet-pumping-stationFull station composed from the two blocks + level + flow metering

Level 0: Submersible Pump Arrangement ​

A single pump "cell" — the unit that gets repeated. Drop one per pump, or let the Level-1 station repeat it for you.

Parameters ​

ParameterTypeDefaultRange / Options
discharge_sizedimensionDN100DN50 – DN300
pipe_materialenumDIDI, SS316, HDPE
include_nrvbooleantrueswing check valve on the riser
include_isobooleantruegate isolation valve above the NRV
motor_signalsenumfullfull, basic, minimal
pump_indexinteger11 – 6 (drives the tag suffix)
tag_prefixstringIPS01Pe.g. IPS01P1

Layout ​

         ┌── to discharge header (north)
         │
       [ISO]      ← include_iso
         │
       [NRV]      ← include_nrv
         │
       ╔════╗
       ║ P  ║     ← pump-submersible (in the wet well)
       ╚════╝
         │
   wet well (south)

What the code does ​

typescript
build: (p, ctx) => {
  const pump  = ctx.place('pump-submersible', { at: [0, 8], tag: `${p.tag_prefix}${p.pump_index}` });
  const riser = ctx.pipe('riser', { size: p.discharge_size, material: p.pipe_material });
  ctx.route(riser, [[0, 8], [0, 0]]);

  ctx.when(p.include_nrv, () => {
    ctx.place('valve-check-swing', { at: [0, 4], on: riser });   // non-return valve
  });
  ctx.when(p.include_iso, () => {
    ctx.place('valve-gate-standard', { at: [0, 2], on: riser }); // isolation valve
  });

  // Motor signal level controls how many discrete I/O instruments are drawn:
  ctx.when(p.motor_signals === 'full' || p.motor_signals === 'basic', () => {
    ctx.place('instrument-signal-discrete', { at: [3, 8], tag: `${p.tag_prefix}_RUN` });  // run status
  });
  ctx.when(p.motor_signals === 'full', () => {
    ctx.place('instrument-signal-discrete', { at: [3, 6], tag: `${p.tag_prefix}_FLT` });  // fault / trip
  });
};

Variation — signal density. motor_signals lets one template serve a feasibility sketch and a detailed-design sheet: minimal draws a single common-alarm signal, basic adds run status, full adds run + fault + (e.g.) seal/moisture monitoring.


Level 0: Valve Chamber Manifold ​

The discharge side: a common header with one isolation branch per pump, optionally inside a valve chamber.

Parameters ​

ParameterTypeDefaultRange / Options
pump_countinteger32 – 6 (one branch each)
branch_sizedimensionDN100DN50 – DN300
header_sizedimensionDN150DN100 – DN600
header_materialenumDIDI, SS316, HDPE
branch_spacingnumber (GU)43 – 8
iso_valve_typeenumgategate, butterfly, knife-gate
chamber_typeenumexistingexisting, new, none
tag_prefixstringIPS01Ve.g. IPS01V1

Parametric expansion ​

pump_count and branch_spacing drive both the geometry and the interface points — the manifold exposes one branch_N inlet per pump plus a single header_out:

typescript
interfaces: (p) => {
  const points = {};
  for (let i = 0; i < p.pump_count; i++) {
    points[`branch_${i + 1}`] = { position: [i * p.branch_spacing, 6], direction: 'south', size: p.branch_size, type: 'pipe' };
  }
  const headerWidth = (p.pump_count - 1) * p.branch_spacing;
  points.header_out = { position: [headerWidth, 0], direction: 'east', size: p.header_size, type: 'pipe' };
  return points;
},

build: (p, ctx) => {
  ctx.when(p.chamber_type !== 'none', () => {
    ctx.boundary({ at: [-1, -1], width: (p.pump_count - 1) * p.branch_spacing + 2, height: 8, type: 'chamber', label: 'VALVE CHAMBER' });
  });
  const header = ctx.pipe('header', { size: p.header_size, material: p.header_material });
  ctx.route(header, [[0, 0], [(p.pump_count - 1) * p.branch_spacing, 0]]);

  ctx.repeat(p.pump_count, (i) => {
    const branch = ctx.pipe(`branch-${i + 1}`, { size: p.branch_size });
    ctx.route(branch, [[i * p.branch_spacing, 6], [i * p.branch_spacing, 0]]);
    ctx.place(`valve-${p.iso_valve_type}-standard`, { at: [i * p.branch_spacing, 3], on: branch, tag: `${p.tag_prefix}${i + 1}` });
  });
  // outlet isolation valve on the header
  ctx.place(`valve-${p.iso_valve_type}-standard`, { at: [(p.pump_count - 1) * p.branch_spacing, 0], on: header });
},

Variation — valve family. Switching iso_valve_type from gate → butterfly → knife-gate swaps every isolation symbol in one move (the symbol id is interpolated: valve-${p.iso_valve_type}-standard).


Level 1: Inlet Pumping Station ​

The full station. It exposes a clean inlet / discharge contract and internally composes the pump arrangements and manifold, then adds level control and (optionally) flow metering and a storm overflow.

Parameters ​

ParameterTypeDefaultRange / Options
pump_countinteger32 – 6 (duty + standby)
design_flownumber100 m³/h10 – 2000
suction_sizedimensionDN100DN50 – DN300
discharge_sizedimensionDN150DN80 – DN600
pipe_materialenumDIDI, SS316, HDPE
include_flowmeterbooleantrueinline flow metering on the rising main
include_stormbooleantruestorm overflow weir + penstock
level_typeenumultrasonicultrasonic, radar, …
tag_prefixstringIPS01station-wide tag root

Interface contract ​

typescript
interfaces: (p) => ({
  inlet:     { position: [0, 20],  direction: 'south', size: 'DN300',          type: 'pipe' }, // gravity inlet to wet well
  discharge: { position: [55, 10], direction: 'east',  size: p.discharge_size, type: 'pipe' }, // rising main out
}),

Layout ​

   inlet (south, gravity sewer)
        │
        ▼
   ┌──────────── WET WELL ────────────┐         ┌─ storm overflow ─┐  (include_storm)
   │  [LT] level cluster              │         │   weir + penstock │
   │   P1   P2   P3   …  (pump_count) │         └───────────────────┘
   └───┬────┬────┬─────────────────────┘
       │    │    │   rising mains
     [ISO][ISO][ISO]
       └────┴────┴──→ [discharge header] ──[FM]──→ discharge (east)
                       valve-chamber-manifold   (include_flowmeter)

What the code does ​

typescript
build: (p, ctx) => {
  // 1. Level instrumentation in the wet well (composes the Level-0 level cluster)
  //    level_type selects ultrasonic vs radar primary element.

  // 2. One pump arrangement per duty — parametric repeat
  ctx.repeat(p.pump_count, (i) => {
    // place a submersible pump arrangement cell at the i-th bay
    ctx.place('pump-submersible', { at: [4 + i * 6, 12], tag: `${p.tag_prefix}P${i + 1}` });
  });

  // 3. Rising mains gather into the discharge header (valve-chamber-manifold pattern)
  ctx.repeat(p.pump_count, (i) => { /* riser + isolation into header */ });

  // 4. Flow metering on the rising main — optional
  ctx.when(p.include_flowmeter, () => {
    ctx.place('instrument-flow-element', { at: [48, 10], tag: `${p.tag_prefix}FE` });
  });
  ctx.when(!p.include_flowmeter, () => {
    const risingMain = ctx.pipe('rising-main', { size: p.discharge_size, material: p.pipe_material });
  });

  // 5. Storm overflow — optional
  ctx.when(p.include_storm, () => {
    const stormOverflow = ctx.pipe('storm-overflow', { size: 'DN300' });
    ctx.place('valve-gate-standard', { at: [50, 30] });   // penstock
  });
},

Variations from one template ​

ScenarioParametersResult
Small duty/standby stationpump_count = 2, design_flow = 40, include_storm = false2 pumps, no storm weir
Large duty-assist stationpump_count = 6, discharge_size = DN4506 pumps + sized header
Feasibility sketchinclude_flowmeter = false, pumps motor_signals = minimalLean P&ID for early stage
Radar level (foaming/scum)level_type = radarRadar primary element drawn instead of ultrasonic

Changing pump_count re-runs the ctx.repeat loops, re-spaces the bays, and re-derives the manifold's branch interface points — no manual redraw.


Try it ​

  1. Templates → Gallery → Process Units / pumping → Inlet Pumping Station → Place
  2. In the parameter panel set pump_count, design_flow, discharge_size, and toggle include_storm / include_flowmeter
  3. The station materialises on the canvas with tags, isolation, NRVs, level control and metering already wired
  4. Connect the station's inlet and discharge interface points to the rest of your model

References ​

  1. BS 1553 / BS 1646 — pipework and instrument symbols
  2. Pump station duty/standby and storm-overflow arrangement conventions