Appearance
Intermediate Pumping Station — Worked Example
This page is the canonical reference for the Intermediate Pumping Station (IPS) template. It is the primary worked example of the full MBEP L0 → L3 pipeline, schedule generation, and the Engineering taskpane stack. All content is fully anonymised — no operator, site, vendor brand, or document-code identifiers appear anywhere in this page or the referenced source files.
Contents
- Overview
- L0 — CustomComponents (5)
- L1 — DrawingTemplate
- L2 — Model
- L3 — ViewDefinitions
- Schedule generation
- Engineering taskpane
- P&ID fidelity — how it was achieved
- Anonymisation reminder
Overview
The Intermediate Pumping Station template is a self-contained, coordinate-faithful P&ID representation of a generic intermediate (in-line booster) sewage pumping station. It ships as the single canonical Level-1 template in the live template library.
Key characteristics:
- Tag prefix:
IPS01(parametric — every tag can be re-prefixed without touching the template code; defaulttag_prefix = "IPS01"). - Topology: 3 submersible pump sets (duty / assist / standby), a single wet well with 3 level instruments, 3 discharge lines each containing a check valve plus an isolation gate valve, a manifold isolation valve, a valve chamber, 6 intrinsically safe (IS) barriers feeding a SCADA I/O signal row, and 2 inlet off-page connectors.
- Anonymisation: no proprietary site name, operator code, vendor brand, document code, or author name appears anywhere. The template is suitable for use across any project without additional scrubbing.
The template exercises every significant MBEP platform capability:
| Capability | Where exercised |
|---|---|
| L0 sub-assembly composition | 5 CustomComponents (see L0 section) |
| L1 coordinate-faithful layout | hand-authored [x, y] grid on 24 mm base unit |
| L2 placed model with engineering props | PidModelShowcaseSeeder (GUID B0000000-…-0004) |
| L3 ViewDefinitions (4 schedule sheets) | ViewDefinitionShowcaseSeeder (GUIDs B0000000-…-0020..0023) |
| Canvas → schedule derivation | ModelGraphParser + ScheduleDerivationService |
| Engineering taskpane (5 sections) | WgpuPropertiesPanel.tsx Engineering tab |
| Persisted element comments | ElementComment entity + /api/v1/element-comments |
L0 — CustomComponents (5)
Five Level-0 sub-assemblies form the building blocks. Each is a Template.define(…) with level: 0 that emits a deterministic payload of nodes and edges through the standard buildRegisteredTemplatePayload path.
submersible-pump-set
Source: packages/web/flow/templates/library/level-0/submersible-pump-set.tsTest: packages/web/flow/templates/library/level-0/__tests__/submersible-pump-set.test.tsBackend GUID: A0000002-0000-0000-0000-000000000001 (CustomComponentSeeder.SubmersiblePumpSetId)
Emits a pump-submersible (symbol from flow/libraries/bs-pumps.json) mechanically coupled to a variable-speed-motor (from flow/libraries/bs-prime-movers.json). The pump carries a full engineering-property block: operation (duty, vsdControlled, dutyRotation), dependencies (SCADA signals RNNG / TRPO / UNAV / STRF / STRR), and healthAndSafety (confinedSpace, liftingRequired, atex Zone 1).
| Parameter | Type | Default |
|---|---|---|
pump_tag | string | IPS01P_01 |
motor_tag | string | IPS01M01_01 |
duty | enum | duty |
duty_rotation | boolean | false |
Test coverage: AC1 (2 symbols: pump + motor), AC2 (tags, no labelPosition: center), AC3 (1 mechanical-coupling connection).
discharge-valve-pair
Source: packages/web/flow/templates/library/level-0/discharge-valve-pair.tsTest: packages/web/flow/templates/library/level-0/__tests__/discharge-valve-pair.test.tsBackend GUID: A0000002-0000-0000-0000-000000000002 (CustomComponentSeeder.DischargeValvePairId)
Emits a check-valve (non-return) upstream of a gate-valve (isolation) on a single discharge-riser pipe run. Both symbols are from flow/libraries/valves.json. Supports horizontal and vertical orientation.
| Parameter | Type | Default |
|---|---|---|
check_tag | string | IPS01V_13 |
iso_tag | string | IPS01V_16 |
line_direction | enum | horizontal |
Test coverage: AC1 (2 symbols), AC2 (horizontal ordering), AC3 (1 connection), AC4 (vertical ordering), AC5 (missing-tag validation error).
wetwell-level-set
Source: packages/web/flow/templates/library/level-0/wetwell-level-set.tsTest: packages/web/flow/templates/library/level-0/__tests__/wetwell-level-set.test.tsBackend GUID: A0000002-0000-0000-0000-000000000003 (CustomComponentSeeder.WetwellLevelSetId)
Emits three level instruments in a horizontal row:
- LT (Level Transmitter):
level-radarwhentech=ultrasonic-radar;level-ultrasonicwhentech=ultrasonic. CarriesinstrumentRange,processRange,signalOutput,tech,commsType,ipRating,atex Zone 1. - LSH (Level Switch High):
level-float-switch. CarriessignalOutput: DI,voltage: 24Vdc,ipRating: IP68,setpointDescription: Level Switch High. - LSHH (Level Switch High-High):
level-float-switch. Same properties,setpointDescription: Level Switch High-High,healthAndSafety.isEmergencyShutdown: true.
Symbols from flow/libraries/bs-instrumentation-misc.json, flow/libraries/bs-flow-instruments.json.
| Parameter | Type | Default |
|---|---|---|
lt_tag | string | IPS01LT01 |
lsh_tag | string | IPS01LS01 |
lshh_tag | string | IPS01LS02 |
tech | enum | ultrasonic-radar |
Test coverage: AC1 (tech=ultrasonic-radar → level-radar + 2x level-float-switch), AC2 (tech=ultrasonic → level-ultrasonic), AC3 (tags match params), AC4 (engineering props populated).
is-barrier
Source: packages/web/flow/templates/library/level-0/is-barrier.tsTest: packages/web/flow/templates/library/level-0/__tests__/is-barrier.test.tsBackend GUID: A0000002-0000-0000-0000-000000000004 (CustomComponentSeeder.IsBarrierId)
RISK-1 implementation. No dedicated IS-barrier / galvanic-isolator symbol exists in the shipped library JSON files. Two options were evaluated during design:
- (a)
ctx.boundary()with a rect+label — rejected becauseBoundaryOptionsdoes not accept apropsfield, so engineering metadata cannot flow through the payload pipeline. - (b)
locally-mounted(generic instrument balloon, confirmed inflow/libraries/bs-instruments.json) withprops.kind = 'is-barrier'— chosen because it is a real, verified library symbol that carries arbitraryprops.
Decision (b) is in use. The IS-barrier is placed as a locally-mounted symbol with props.kind = 'is-barrier'; a ctx.label() annotation emits the SCADA signal name alongside the glyph. The Engineering taskpane detects IS-barrier elements by reading properties.kind === 'is-barrier'. This decision is documented in docs/_archive/develop-tasks/task-ips-a5-plan.md.
| Parameter | Type | Default |
|---|---|---|
barrier_tag | string | IPS01IS01 |
signal_name | string | IPS01LS01AH |
Test coverage: AC1 (1 locally-mounted symbol + 1 __text label), AC2 (properties carry kind, barrierTag, signalName, atex, healthAndSafety), AC3 (missing-param validation errors).
offpage-connector
Source: packages/web/flow/templates/library/level-0/offpage-connector.tsTest: packages/web/flow/templates/library/level-0/__tests__/offpage-connector.test.tsBackend GUID: A0000002-0000-0000-0000-000000000005 (CustomComponentSeeder.OffpageConnectorId)
Emits a directional off-page connector flag. side=left → bs-connector-offpage-left; side=right → bs-connector-offpage-right (both from flow/libraries/bs-pipeline-connectors.json). Captions longer than 64 characters are truncated to 63 characters + … in the visible label; the full caption is preserved in props.caption.
| Parameter | Type | Default |
|---|---|---|
caption | string | Inlet PS (TBC) |
side | enum | left |
Test coverage: AC1 (side=left → bs-connector-offpage-left), AC2 (side=right), AC3 (caption truncation at 64 chars), AC4 (missing/invalid params throw errors).
L1 — DrawingTemplate
Source: packages/web/flow/templates/library/level-1/intermediate-pumping-station.tsTest: packages/web/flow/templates/library/level-1/__tests__/intermediate-pumping-station.test.tsBackend GUID: A0000000-0000-0000-0000-000000000100 (ParametricTemplateSeeder.IntermediatePumpingStationId) Backend seeder: packages/api/ErgoPlanner.Infrastructure/Seeding/ParametricTemplateSeeder.cs
Parameters
| Parameter | Type | Default | Range |
|---|---|---|---|
tag_prefix | string | IPS01 | — |
pump_count | integer | 3 | 2 – 3 |
level_tech | enum | ultrasonic-radar | ultrasonic-radar, ultrasonic |
include_chamber | boolean | true | — |
Topology (hand-authored coordinates, 24 mm grid)
The template places the following elements using ctx.compose() and ctx.place() at verified non-colliding integer grid coordinates:
| Area | Elements | Grid position |
|---|---|---|
| IS barriers | 6× is-barrier (IPS01IS01 – IS06) | y=0, x=2/8/14/20/26/32 |
| Level set | wetwell-level-set (IPS01LT01, IPS01LS01, IPS01LS02) | y=5, x=4 (LT)/7 (LSH)/10 (LSHH) |
| Inlet connectors | 2× offpage-connector (left) | y=8 x=0 / y=11 x=0 |
| Pump sets | 3× submersible-pump-set (IPS01P_01/02/03, IPS01M01_01/02/03) | y=16, x=4/10/16 |
| Discharge valve pairs | 3× discharge-valve-pair (check: IPS01V_13/14/15; iso: IPS01V_16/17/18) | y=12/16/20, x=22 |
| Manifold valve | gate-valve (IPS01V_20) | y=16, x=30 |
| Valve chamber valve | gate-valve (IPS01V_10) | y=12, x=40 |
| Boundaries | station boundary, valve chamber (IPS01CH02) | station [0,0] w=32 h=24; chamber [36,8] w=10 h=14 |
IS barriers and their SCADA signals:
| Barrier tag | Signal name | Description |
|---|---|---|
| IPS01IS01 | IPS01LS01AH | Level Switch High |
| IPS01IS02 | IPS01LS02AHH | Level Switch High-High |
| IPS01IS03 | IPS01LT01AFLT | Level Transmitter Fault |
| IPS01IS04 | IPS01M01RNNG | Pump 1 Running |
| IPS01IS05 | IPS01M02RNNG | Pump 2 Running |
| IPS01IS06 | IPS01M03RNNG | Pump 3 Running |
Inlet captions (as placed): "Inlet PS (TBC)" and "From Primary Biological Filter".
Seeder maturity status
The sewage-pumping-station template (GUID A0000000-…-0099) is retained in the database with MaturityLevel = "archived" to preserve the reconstruction-CI fixture. It is no longer visible to end-users. The intermediate-pumping-station (GUID A0000000-…-0100) is seeded as published.
Payload-assertion test (vitest)
The test file above (intermediate-pumping-station.test.ts) verifies the structural contract without requiring a live WASM canvas:
- AC1: symbol counts —
pump-submersible×3,variable-speed-motor×3,check-valve×3,gate-valve×5 (3 discharge pairs + manifold + chamber),level-radar×1,level-float-switch×2,locally-mounted≥6 (IS barriers),bs-connector-offpage-left×2. - AC2: mandatory tag subset present in payload labels.
- AC3: exactly 2
areaFrameelements (station boundary + valve chamber), bothboundaryType = "chamber". - AC4: no two real symbol elements share the same
[x, y]coordinate. - AC5: no element has
labelPosition = "center"(engine restriction). - AC6: every placed symbol id resolves against the shipped
flow/libraries/*.jsonfiles. - AC7:
pump_count=2produces 2 pump sets and 2 discharge valve pairs. - AC8: registry check —
intermediate-pumping-stationregistered;sewage-pumping-stationabsent from the registry.
L2 — Model
Seeder: packages/api/ErgoPlanner.Infrastructure/Seeding/PidModelShowcaseSeeder.csGUID: B0000000-0000-0000-0000-000000000004 (PidModelShowcaseSeeder.IpsShowcaseModelId) Fixture: packages/api/ErgoPlanner.Infrastructure/Seeding/TestData/showcase-ips-l2-placed.json
The L2 model is a pre-placed P&ID canvas seeded into the Models table under the Showcase Company (B0000000-…-0001) and Showcase Project (B0000000-…-0002).
Actual fixture node/edge counts (verified against showcase-ips-l2-placed.json):
| Metric | Count |
|---|---|
| Total nodes | 25 |
shapeNode (equipment / instruments) | 21 |
instrumentBubble (annotation overlays) | 4 |
| Edges (connections) | 24 |
Each instrument shapeNode carries a full engineering-property block under data.equipment.properties (camelCase keys). The three level instruments carry the engineering columns used by the schedule derivation service:
| Tag | Type | Voltage | CommsType | IpRating | SignalOutput | InstrumentRange | ProcessRange | MCC |
|---|---|---|---|---|---|---|---|---|
| IPS01LS01 | instrument-level-switch | 24Vdc | Conventional | IP68 | DI | — | — | MCC1 |
| IPS01LS02 | instrument-level-switch | 24Vdc | Conventional | IP68 | DI | — | — | MCC1 |
| IPS01LT01 | instrument-level-transmitter | 110Vac | Profibus DP | IP68 | Profibus | 0–6 m | 0–3 m | MCC1 HMI |
Equipment nodes (pump sets) carry operation, dependencies, and healthAndSafety property blocks derived from the FDS control narrative:
- operation —
duty(duty / assist / standby),vsdControlled: true, duty-start level setpoint. - dependencies — SCADA signal list (RNNG / TRPO / UNAV / STRF / STRR per pump), controlling instrument reference (
IPS01LT01). - healthAndSafety —
confinedSpace: true,liftingRequired: true,atex: "Zone 1".
L3 — ViewDefinitions
Seeder: packages/api/ErgoPlanner.Infrastructure/Seeding/ViewDefinitionShowcaseSeeder.cs
Four ViewDefinition rows are seeded over the IPS L2 Model (B0000000-…-0004):
| Name | ViewType | Sheet number | GUID |
|---|---|---|---|
| Instrument Schedule | instrument_schedule | IS-101 | B0000000-0000-0000-0000-000000000020 |
| I/O Schedule | io_schedule | IO-101 | B0000000-0000-0000-0000-000000000021 |
| Equipment Schedule | equipment_schedule | EQ-101 | B0000000-0000-0000-0000-000000000022 |
| Control Philosophy (FDS) | control_philosophy | CP-101 | B0000000-0000-0000-0000-000000000023 |
All four views have RevisionLetter = "A", StatusCode = "S0", CanUnfreeze = true.
Schedule generation
Endpoint: GET /api/v1/models/{id}/schedulesController: packages/api/ErgoPlanner.API/API/Controllers/PidModelSchedulesController.csService: ModelScheduleService → ModelGraphParser → ScheduleDerivationService
The endpoint parses Model.NodesJson + Model.EdgesJson via ModelGraphParser into a ProcessGraph, then runs the same ScheduleDerivationService that serves the GET /api/v1/parametric-templates/{id}/schedules endpoint.
Instrument Schedule output — 3 rows (verbatim from analysis.md)
For the seeded IPS model (B0000000-…-0004) the schedule derivation produces exactly 3 instrument rows:
| Tag | Description | Type | Voltage | Comms | IP | Range | Signal | MCC |
|---|---|---|---|---|---|---|---|---|
IPS01LS01 | Level Switch High | Level switch | 24Vdc | Conventional | IP68 | — | DI | MCC1 |
IPS01LS02 | Level Switch High-High | Level switch | 24Vdc | Conventional | IP68 | — | DI | MCC1 |
IPS01LT01 | Ultrasonic level transmitter | Level transmitter | 110Vac | Profibus DP | IP68 | 0–6 m (proc 0–3 m) | Profibus | MCC1 HMI |
These rows correspond to the 11 engineering columns in InstrumentScheduleRowDto: Tag, InstrumentType, ServiceDescription, IoType, SignalRange, Location, Voltage, CommsType, IpRating, InstrumentRange, ProcessRange, SignalOutput, Atex, Manufacturer, Indication, Mcc, Comments.
Validation summary
validation.summary.fail is 0 for this model.
Rules R4 (R4-MagmeterOnEachLine) and R5 (R5-ControlValveOnTankReturn) are SPS-topology rules designed for the equalisation-storage station pattern (EQ tank + magmeters + modulating return valve). The IPS has no vessel-atmospheric storage tank, so both rules detect the absence and return status: "Skipped" rather than "Fail". This is the IPS-B7 topology-aware guard.
R6 (R6-TagUniqueness) returns Pass — all three instrument tags are unique.
Integration test:packages/api/ErgoPlanner.IntegrationTests/Templates/IntermediatePsSchedulesIntegrationTests.cs (AC3 – AC8 verified; instrumentBubble nodes are skipped by ModelGraphParser — only shapeNode elements produce schedule rows).
Engineering taskpane
Clicking any equipment or instrument element in a drawing that was placed from this template opens the Engineering tab in WgpuPropertiesPanel.tsx. The tab presents five sections:
| Section | Content source | Notes |
|---|---|---|
| Engineering | element.properties (symbol type, tag, description, pipe / instrument spec) | Original section; classifies by classifyEngineeringKind(symbolId) |
| Operation | element.properties.operation | FDS narrative: duty, VSD control, duty-rotation, draw-down / odour modes |
| Dependencies | element.properties.dependencies | SCADA signals, controlling instrument, power source, interlocks |
| Health & Safety | element.properties.healthAndSafety | ATEX zone, IS-barrier requirement, confined-space entry, LOTO, lifting |
| Comments | Backend ElementComment entity, scoped to (ModelId, ElementTag) | Persisted across sessions via /api/v1/element-comments |
Comments backend:
- Entity:
packages/api/ErgoPlanner.Domain/Entities/Comments/ElementComment.cs - Controller:
packages/api/ErgoPlanner.API/API/Controllers/ElementCommentsController.cs—GET,POST,PUT {id},DELETE {id}underGET /api/v1/element-comments?modelId=&tag= - Web hooks:
useElementComments(modelId, tag)(TanStack Query), with create / update / delete mutations. - Auth: all endpoints require
[Authorize]; tenant isolation via global EF query filter onCompanyId.
P&ID fidelity — how it was achieved
Visual accuracy was pursued through three complementary methods:
Hand-authored coordinates. Every
[x, y]position in the L1 template is an explicit integer grid unit, manually traced from the decoded PID topology (seedocs/develop/planning/ intermediate-pumping-station/analysis.md). No algorithmic spacing is used; the coordinate table in the template file header documents the full layout.Real library symbols. Every placed symbol id (
pump-submersible,level-radar,level-float-switch,check-valve,gate-valve,locally-mounted,bs-connector-offpage-left) was verified against the shippedflow/libraries/*.jsonfiles before use. The payload-assertion vitest (AC6) re-verifies this on every CI run.Exact tags and dashed boundaries. Tag strings match the decoded naming convention exactly (e.g.
IPS01P_01,IPS01V_13,IPS01LS01). Station and valve-chamber boundaries usectx.boundary({ type: 'chamber', … })to produce dashed outlines.
Visual iteration (pixel-level spacing of labels, exact boundary dimensions) was tuned via user screenshot review after structural validation passed. A deferred item IPS-DEFER-VISUAL-TUNING tracks any follow-up pixel-fidelity passes that require live WASM canvas access.
Anonymisation reminder
MANDATORY: This template and all downstream artifacts (the L2 model, the L3 view definitions, and any screenshots or exports) must remain fully anonymous.
The following identifiers must never appear in any file on this branch:
| Category | Examples (prohibited) |
|---|---|
| Operator / client names | Any named water authority, utility, or contractor |
| Site names | Any geographic or named site reference |
| Document codes | K206-series, GU19-series, MMB-series, or similar |
| Vendor brands | Xylem, Seepex, or any equipment manufacturer |
The proprietary-refs guard scripts/check-proprietary-refs.sh enforces this at commit time. Any content derived from the proprietary source pack (docs/templates/Pumping Station 1/, which is untracked and gitignored) must be fully re-expressed as generic engineering descriptions before being committed.
If you are adding screenshots to this page, ensure they show only generic IPS01-prefixed tags and no real project data. Use placeholder blocks ([Screenshot: IPS P&ID layout]) until anonymised screenshots are available.
See also:
- Drawing Templates Overview
- Pumping Stations
- Instrumentation Arrangements
- Template Language Reference
- Planning analysis:
docs/develop/planning/intermediate-pumping-station/analysis.md