Add cavitation detection (NPSH-based) per pump #1

Open
opened 2026-05-06 14:48:47 +00:00 by znetsixe · 0 comments
Owner

Background

Cavitation occurs when the local pressure at the impeller eye drops below the liquid's vapour pressure: vapour bubbles form on the suction side, then collapse violently downstream — eroding the impeller, vibrating the shaft, and degrading flow. For wastewater pumping it's the dominant low-level failure mode (low water level → low static head → low NPSHa → cavitation → pump ruined).

This lives on rotatingMachine (per-pump concern), NOT on pumpingStation, because:

  • NPSHr is a pump-specific characteristic from the manufacturer curve.
  • Pump suction elevation and friction losses are pump-specific.
  • The station only contributes the basin water column + temperature; per-pump comparison is the right granularity.

Engineering formula

NPSHa (Available) at the pump suction:

NPSHa = (P_atm − P_vapour) / (ρ × g) + h_s − h_friction

Where:

Symbol Meaning Source
P_atm Atmospheric pressure (Pa) Config (default 101325, altitude-corrected if known)
P_vapour Saturation pressure of water at current T (Pa) coolprop.PropsSI('P', 'T', T_K, 'Q', 0, 'Water')
ρ Water density at current T (kg/m³) coolprop.PropsSI('D', 'T', T_K, 'P', P_atm, 'Water')
g Gravitational acceleration (m/s²) gravity.js (9.80665 default; use Somigliana if lat known)
h_s Static suction head (m) — waterLevel − pumpSuctionElevation; positive when basin is above pump (flooded suction) Station provides waterLevel; pump config provides suctionElevation
h_friction Suction-line friction losses (m) Per-pump config; default 0 for short submersible suctions; f × (L/D) × v²/(2g) for long suction lines

NPSHr (Required) — pump-specific, from manufacturer curve. Typically rises with flow². For MVP a single conservative value per pump is fine; a flow-dependent curve is the v2 follow-up.

Cavitation criterion:

cavitating = NPSHa ≤ NPSHr + safetyMargin

With safetyMargin typically 0.5–1.0 m or 10–20% of NPSHr (config, default 0.5 m).

Inputs needed

New rotatingMachine config fields

  • cavitation.npshRequired (m) — manufacturer NPSHr at design flow. Required to enable detection.
  • cavitation.suctionElevation (m, basin-floor reference) — height of pump suction inlet above basin floor. Often 0 for submersible. For dry-pit / suction-lift, this can equal the pump centerline elevation above the basin water surface (h_s becomes negative).
  • cavitation.frictionHead (m) — default 0. Optional approximation of suction-line losses.
  • cavitation.safetyMargin (m) — default 0.5.
  • cavitation.enabled (bool) — default false (opt-in until calibrated).

Inputs the pump pulls from its parent station

  • level.measured.atequipment or level.predicted.atequipment — basin water level (m above basin floor).
  • temperature.measured.atequipment — water temp (K). Default 15°C / 288.15 K if not present.
  • atmosphericPressurePa — station-level config (default 101325).

Implementation plan

Phase 1 — detection (this issue)

  1. Add nodes/generalFunctions/src/helper/npsh.js with one pure function:
    computeNPSHa({ waterLevelM, suctionElevationM, atmosphericPressurePa, temperatureK, frictionHeadM, gravityMs2 })  number  // metres
    
    Uses coolprop for vapour pressure + density. Pure helper, no Node-RED.
  2. In rotatingMachine/src/specificClass.js, on each tick (when cavitation.enabled):
    • Pull water level + temp from parent station's measurement stream.
    • Compute NPSHa via the helper.
    • Compare to NPSHr + safetyMargin.
    • Write npshAvailable.predicted.atequipment.default (m) and npshMargin.predicted.atequipment.default (m) measurements for telemetry.
    • Set boolean state this.cavitating and a severity enum this.cavitationState = 'normal' | 'margin-low' | 'cavitating'.
  3. Surface in getOutput(): output.cavitating, output.cavitationState, output.npshAvailable, output.npshMargin.
  4. Status update: red status badge when cavitating, amber when margin-low.

Phase 2 — action (separate issue, do NOT do here)

  • Auto-degrade pump speed/demand when cavitating, or signal the parent MGC to redistribute load.
  • Until Phase 2 lands, detection is observation-only — operator decides.

Test scenarios

Must cover:

  1. Sea level + 15 °C + flooded submersible: waterLevel=2 m, suctionElev=0, NPSHr=3 → NPSHa ≈ 12.3 m → margin ≈ 8.8 m → normal.
  2. Suction lift: pump centerline 5 m above water → h_s = −5 → NPSHa ≈ 5.3 m → with NPSHr=3 → margin ≈ 1.8 m → margin-low.
  3. Hot wastewater, 80 °C: P_vap ≈ 47.4 kPa → NPSHa drops ~5 m vs cold → cavitation triggers easily.
  4. High altitude, 2000 m: P_atm ≈ 79.5 kPa → NPSHa drops ~2.2 m vs sea level.
  5. Edge: no temperature available → fall back to 15 °C with warn log.
  6. Edge: no NPSHr configured → skip detection silently.

Acceptance criteria

  • npsh.js helper with unit tests in generalFunctions/test/.
  • rotatingMachine config schema updated; new cavitation.* block.
  • Per-tick NPSH computation with measurements written.
  • getOutput() exposes cavitating, cavitationState, npshAvailable, npshMargin.
  • Status badge changes (red/amber/normal).
  • All 6 test scenarios above pass.
  • Default config has cavitation.enabled: false so existing demos keep behaving identically until opted in.

Why deferred (not done now)

  • Out-of-scope for the pumpingStation volume-clamp work that surfaced this discussion (2026-05-06).
  • Wants pump-curve validation against a real station before auto-action goes in.
  • Phase 1 is ~1 day of work + tests; Phase 2 (auto-degrade) is a separate decision-gate.
## Background Cavitation occurs when the local pressure at the impeller eye drops below the liquid's vapour pressure: vapour bubbles form on the suction side, then collapse violently downstream — eroding the impeller, vibrating the shaft, and degrading flow. For wastewater pumping it's the dominant low-level failure mode (low water level → low static head → low NPSHa → cavitation → pump ruined). This lives on `rotatingMachine` (per-pump concern), NOT on `pumpingStation`, because: - NPSHr is a **pump-specific** characteristic from the manufacturer curve. - Pump suction elevation and friction losses are pump-specific. - The station only contributes the basin water column + temperature; per-pump comparison is the right granularity. ## Engineering formula **NPSHa (Available)** at the pump suction: ``` NPSHa = (P_atm − P_vapour) / (ρ × g) + h_s − h_friction ``` Where: | Symbol | Meaning | Source | |---|---|---| | `P_atm` | Atmospheric pressure (Pa) | Config (default 101325, altitude-corrected if known) | | `P_vapour` | Saturation pressure of water at current T (Pa) | `coolprop.PropsSI('P', 'T', T_K, 'Q', 0, 'Water')` | | `ρ` | Water density at current T (kg/m³) | `coolprop.PropsSI('D', 'T', T_K, 'P', P_atm, 'Water')` | | `g` | Gravitational acceleration (m/s²) | `gravity.js` (9.80665 default; use Somigliana if lat known) | | `h_s` | Static suction head (m) — `waterLevel − pumpSuctionElevation`; positive when basin is above pump (flooded suction) | Station provides waterLevel; pump config provides suctionElevation | | `h_friction` | Suction-line friction losses (m) | Per-pump config; default 0 for short submersible suctions; `f × (L/D) × v²/(2g)` for long suction lines | **NPSHr (Required)** — pump-specific, from manufacturer curve. Typically rises with flow². For MVP a single conservative value per pump is fine; a flow-dependent curve is the v2 follow-up. **Cavitation criterion:** ``` cavitating = NPSHa ≤ NPSHr + safetyMargin ``` With `safetyMargin` typically 0.5–1.0 m or 10–20% of NPSHr (config, default 0.5 m). ## Inputs needed ### New `rotatingMachine` config fields - `cavitation.npshRequired` (m) — manufacturer NPSHr at design flow. Required to enable detection. - `cavitation.suctionElevation` (m, basin-floor reference) — height of pump suction inlet above basin floor. Often 0 for submersible. For dry-pit / suction-lift, this can equal the pump centerline elevation above the basin water surface (h_s becomes negative). - `cavitation.frictionHead` (m) — default 0. Optional approximation of suction-line losses. - `cavitation.safetyMargin` (m) — default 0.5. - `cavitation.enabled` (bool) — default false (opt-in until calibrated). ### Inputs the pump pulls from its parent station - `level.measured.atequipment` or `level.predicted.atequipment` — basin water level (m above basin floor). - `temperature.measured.atequipment` — water temp (K). Default 15°C / 288.15 K if not present. - `atmosphericPressurePa` — station-level config (default 101325). ## Implementation plan ### Phase 1 — detection (this issue) 1. Add `nodes/generalFunctions/src/helper/npsh.js` with one pure function: ```js computeNPSHa({ waterLevelM, suctionElevationM, atmosphericPressurePa, temperatureK, frictionHeadM, gravityMs2 }) → number // metres ``` Uses `coolprop` for vapour pressure + density. Pure helper, no Node-RED. 2. In `rotatingMachine/src/specificClass.js`, on each tick (when `cavitation.enabled`): - Pull water level + temp from parent station's measurement stream. - Compute `NPSHa` via the helper. - Compare to `NPSHr + safetyMargin`. - Write `npshAvailable.predicted.atequipment.default` (m) and `npshMargin.predicted.atequipment.default` (m) measurements for telemetry. - Set boolean state `this.cavitating` and a severity enum `this.cavitationState` = `'normal' | 'margin-low' | 'cavitating'`. 3. Surface in `getOutput()`: `output.cavitating`, `output.cavitationState`, `output.npshAvailable`, `output.npshMargin`. 4. Status update: red status badge when `cavitating`, amber when `margin-low`. ### Phase 2 — action (separate issue, do NOT do here) - Auto-degrade pump speed/demand when cavitating, or signal the parent MGC to redistribute load. - Until Phase 2 lands, detection is **observation-only** — operator decides. ## Test scenarios Must cover: 1. **Sea level + 15 °C + flooded submersible**: waterLevel=2 m, suctionElev=0, NPSHr=3 → NPSHa ≈ 12.3 m → margin ≈ 8.8 m → `normal`. 2. **Suction lift**: pump centerline 5 m above water → h_s = −5 → NPSHa ≈ 5.3 m → with NPSHr=3 → margin ≈ 1.8 m → `margin-low`. 3. **Hot wastewater, 80 °C**: P_vap ≈ 47.4 kPa → NPSHa drops ~5 m vs cold → cavitation triggers easily. 4. **High altitude, 2000 m**: P_atm ≈ 79.5 kPa → NPSHa drops ~2.2 m vs sea level. 5. **Edge: no temperature available** → fall back to 15 °C with warn log. 6. **Edge: no NPSHr configured** → skip detection silently. ## Acceptance criteria - [ ] `npsh.js` helper with unit tests in `generalFunctions/test/`. - [ ] `rotatingMachine` config schema updated; new `cavitation.*` block. - [ ] Per-tick NPSH computation with measurements written. - [ ] `getOutput()` exposes `cavitating`, `cavitationState`, `npshAvailable`, `npshMargin`. - [ ] Status badge changes (red/amber/normal). - [ ] All 6 test scenarios above pass. - [ ] Default config has `cavitation.enabled: false` so existing demos keep behaving identically until opted in. ## Why deferred (not done now) - Out-of-scope for the pumpingStation volume-clamp work that surfaced this discussion (2026-05-06). - Wants pump-curve validation against a real station before auto-action goes in. - Phase 1 is ~1 day of work + tests; Phase 2 (auto-degrade) is a separate decision-gate.
Sign in to join this conversation.
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RnD/rotatingMachine#1