Appearance
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.
| Template | Level | Slug | Role |
|---|---|---|---|
| Submersible Pump Arrangement | 0 | submersible-pump-arrangement | One pump + riser + NRV + isolation + motor signals |
| Valve Chamber Manifold | 0 | valve-chamber-manifold | Discharge header with one isolation branch per pump |
| Inlet Pumping Station | 1 | inlet-pumping-station | Full 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
| Parameter | Type | Default | Range / Options |
|---|---|---|---|
discharge_size | dimension | DN100 | DN50 – DN300 |
pipe_material | enum | DI | DI, SS316, HDPE |
include_nrv | boolean | true | swing check valve on the riser |
include_iso | boolean | true | gate isolation valve above the NRV |
motor_signals | enum | full | full, basic, minimal |
pump_index | integer | 1 | 1 – 6 (drives the tag suffix) |
tag_prefix | string | IPS01P | e.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
| Parameter | Type | Default | Range / Options |
|---|---|---|---|
pump_count | integer | 3 | 2 – 6 (one branch each) |
branch_size | dimension | DN100 | DN50 – DN300 |
header_size | dimension | DN150 | DN100 – DN600 |
header_material | enum | DI | DI, SS316, HDPE |
branch_spacing | number (GU) | 4 | 3 – 8 |
iso_valve_type | enum | gate | gate, butterfly, knife-gate |
chamber_type | enum | existing | existing, new, none |
tag_prefix | string | IPS01V | e.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
| Parameter | Type | Default | Range / Options |
|---|---|---|---|
pump_count | integer | 3 | 2 – 6 (duty + standby) |
design_flow | number | 100 m³/h | 10 – 2000 |
suction_size | dimension | DN100 | DN50 – DN300 |
discharge_size | dimension | DN150 | DN80 – DN600 |
pipe_material | enum | DI | DI, SS316, HDPE |
include_flowmeter | boolean | true | inline flow metering on the rising main |
include_storm | boolean | true | storm overflow weir + penstock |
level_type | enum | ultrasonic | ultrasonic, radar, … |
tag_prefix | string | IPS01 | station-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
| Scenario | Parameters | Result |
|---|---|---|
| Small duty/standby station | pump_count = 2, design_flow = 40, include_storm = false | 2 pumps, no storm weir |
| Large duty-assist station | pump_count = 6, discharge_size = DN450 | 6 pumps + sized header |
| Feasibility sketch | include_flowmeter = false, pumps motor_signals = minimal | Lean P&ID for early stage |
| Radar level (foaming/scum) | level_type = radar | Radar 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
- Templates → Gallery → Process Units / pumping → Inlet Pumping Station → Place
- In the parameter panel set
pump_count,design_flow,discharge_size, and toggleinclude_storm/include_flowmeter - The station materialises on the canvas with tags, isolation, NRVs, level control and metering already wired
- Connect the station's
inletanddischargeinterface points to the rest of your model
Related topics
- Drawing Templates Overview
- Template Language Reference — every
param.*andctx.*signature used above - Instrumentation Arrangements — the level cluster and flowmeter compound used here
- Building Process Diagrams
References
- BS 1553 / BS 1646 — pipework and instrument symbols
- Pump station duty/standby and storm-overflow arrangement conventions