Skip to content

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 ​

  1. Overview
  2. L0 — CustomComponents (5)
  3. L1 — DrawingTemplate
  4. L2 — Model
  5. L3 — ViewDefinitions
  6. Schedule generation
  7. Engineering taskpane
  8. P&ID fidelity — how it was achieved
  9. 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; default tag_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:

CapabilityWhere exercised
L0 sub-assembly composition5 CustomComponents (see L0 section)
L1 coordinate-faithful layouthand-authored [x, y] grid on 24 mm base unit
L2 placed model with engineering propsPidModelShowcaseSeeder (GUID B0000000-…-0004)
L3 ViewDefinitions (4 schedule sheets)ViewDefinitionShowcaseSeeder (GUIDs B0000000-…-0020..0023)
Canvas → schedule derivationModelGraphParser + ScheduleDerivationService
Engineering taskpane (5 sections)WgpuPropertiesPanel.tsx Engineering tab
Persisted element commentsElementComment 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).

ParameterTypeDefault
pump_tagstringIPS01P_01
motor_tagstringIPS01M01_01
dutyenumduty
duty_rotationbooleanfalse

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.

ParameterTypeDefault
check_tagstringIPS01V_13
iso_tagstringIPS01V_16
line_directionenumhorizontal

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-radar when tech=ultrasonic-radar; level-ultrasonic when tech=ultrasonic. Carries instrumentRange, processRange, signalOutput, tech, commsType, ipRating, atex Zone 1.
  • LSH (Level Switch High): level-float-switch. Carries signalOutput: 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.

ParameterTypeDefault
lt_tagstringIPS01LT01
lsh_tagstringIPS01LS01
lshh_tagstringIPS01LS02
techenumultrasonic-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 because BoundaryOptions does not accept a props field, so engineering metadata cannot flow through the payload pipeline.
  • (b) locally-mounted (generic instrument balloon, confirmed in flow/libraries/bs-instruments.json) with props.kind = 'is-barrier' — chosen because it is a real, verified library symbol that carries arbitrary props.

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.

ParameterTypeDefault
barrier_tagstringIPS01IS01
signal_namestringIPS01LS01AH

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.

ParameterTypeDefault
captionstringInlet PS (TBC)
sideenumleft

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 ​

ParameterTypeDefaultRange
tag_prefixstringIPS01—
pump_countinteger32 – 3
level_techenumultrasonic-radarultrasonic-radar, ultrasonic
include_chamberbooleantrue—

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:

AreaElementsGrid position
IS barriers6× is-barrier (IPS01IS01 – IS06)y=0, x=2/8/14/20/26/32
Level setwetwell-level-set (IPS01LT01, IPS01LS01, IPS01LS02)y=5, x=4 (LT)/7 (LSH)/10 (LSHH)
Inlet connectors2× offpage-connector (left)y=8 x=0 / y=11 x=0
Pump sets3× submersible-pump-set (IPS01P_01/02/03, IPS01M01_01/02/03)y=16, x=4/10/16
Discharge valve pairs3× discharge-valve-pair (check: IPS01V_13/14/15; iso: IPS01V_16/17/18)y=12/16/20, x=22
Manifold valvegate-valve (IPS01V_20)y=16, x=30
Valve chamber valvegate-valve (IPS01V_10)y=12, x=40
Boundariesstation 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 tagSignal nameDescription
IPS01IS01IPS01LS01AHLevel Switch High
IPS01IS02IPS01LS02AHHLevel Switch High-High
IPS01IS03IPS01LT01AFLTLevel Transmitter Fault
IPS01IS04IPS01M01RNNGPump 1 Running
IPS01IS05IPS01M02RNNGPump 2 Running
IPS01IS06IPS01M03RNNGPump 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 areaFrame elements (station boundary + valve chamber), both boundaryType = "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/*.json files.
  • AC7: pump_count=2 produces 2 pump sets and 2 discharge valve pairs.
  • AC8: registry check — intermediate-pumping-station registered; sewage-pumping-station absent 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):

MetricCount
Total nodes25
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:

TagTypeVoltageCommsTypeIpRatingSignalOutputInstrumentRangeProcessRangeMCC
IPS01LS01instrument-level-switch24VdcConventionalIP68DI——MCC1
IPS01LS02instrument-level-switch24VdcConventionalIP68DI——MCC1
IPS01LT01instrument-level-transmitter110VacProfibus DPIP68Profibus0–6 m0–3 mMCC1 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):

NameViewTypeSheet numberGUID
Instrument Scheduleinstrument_scheduleIS-101B0000000-0000-0000-0000-000000000020
I/O Scheduleio_scheduleIO-101B0000000-0000-0000-0000-000000000021
Equipment Scheduleequipment_scheduleEQ-101B0000000-0000-0000-0000-000000000022
Control Philosophy (FDS)control_philosophyCP-101B0000000-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:

TagDescriptionTypeVoltageCommsIPRangeSignalMCC
IPS01LS01Level Switch HighLevel switch24VdcConventionalIP68—DIMCC1
IPS01LS02Level Switch High-HighLevel switch24VdcConventionalIP68—DIMCC1
IPS01LT01Ultrasonic level transmitterLevel transmitter110VacProfibus DPIP680–6 m (proc 0–3 m)ProfibusMCC1 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:

SectionContent sourceNotes
Engineeringelement.properties (symbol type, tag, description, pipe / instrument spec)Original section; classifies by classifyEngineeringKind(symbolId)
Operationelement.properties.operationFDS narrative: duty, VSD control, duty-rotation, draw-down / odour modes
Dependencieselement.properties.dependenciesSCADA signals, controlling instrument, power source, interlocks
Health & Safetyelement.properties.healthAndSafetyATEX zone, IS-barrier requirement, confined-space entry, LOTO, lifting
CommentsBackend 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} under GET /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 on CompanyId.

P&ID fidelity — how it was achieved ​

Visual accuracy was pursued through three complementary methods:

  1. Hand-authored coordinates. Every [x, y] position in the L1 template is an explicit integer grid unit, manually traced from the decoded PID topology (see docs/develop/planning/ intermediate-pumping-station/analysis.md). No algorithmic spacing is used; the coordinate table in the template file header documents the full layout.

  2. 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 shipped flow/libraries/*.json files before use. The payload-assertion vitest (AC6) re-verifies this on every CI run.

  3. 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 use ctx.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:

CategoryExamples (prohibited)
Operator / client namesAny named water authority, utility, or contractor
Site namesAny geographic or named site reference
Document codesK206-series, GU19-series, MMB-series, or similar
Vendor brandsXylem, 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: