Development instance — data may be reset at any time

Urethral cascade

Drop-in TS implementation. Given the bladder pressure and each gate's closing pressure, returns the flow rate and pressure profile along the urethra. For why the formula has its shape, see Urethral fluid dynamics.

A pure-physics function for the urinary cascade. Given the bladder pressure and the current closing pressure of each gate (internal sphincter, external sphincter, external obstruction), it returns the steady-state flow rate and the fluid pressure at every point along the chain:

BISU1ESU2EOAir\text{B} \to \text{IS} \to \text{U}_1 \to \text{ES} \to \text{U}_2 \to \text{EO} \to \text{Air}

The function makes no assumptions about what sets the closing pressures — autonomic tone, voluntary effort, external mechanical force are all caller concerns.

For why the formula has its particular shape (the closed-form min over branches, the recursion, the regime structure), see urethral-fluid-dynamics.

Inputs

  • bladderPressure — bladder pressure (cmH₂O).
  • closing[internal, external, obstruction] closing pressures (cmH₂O). Internal: bladder neck / internal sphincter. External: external sphincter + pelvic floor + passive urethral closure. Obstruction: external mechanical compression (a heel, a hand) — typically 0 if there's none.
  • resistances[internal, external, obstruction] per-segment flow resistances. Choose units consistent with the flow units you want out (see Returns).

Returns

  • flowRate — flow rate. mL/s when resistances are in cmH₂O · s / mL. Zero whenever any segment's closing pressure is at or above the bladder pressure.
  • upstream[u0, u1, u2] fluid pressure on the upstream side of each segment. upstream[1] is U₁ (between IS and ES); upstream[2] is U₂ (between ES and EO). The urethral-wall sensor sits in U₂, so that's the value the PMC sees as feedback.

Code

ts
export function urethralCascade(
  bladderPressure: number,
  closing: readonly [number, number, number],
  resistances: readonly [number, number, number],
): { flowRate: number; upstream: [number, number, number] } {
  const cumR: [number, number, number] = [
    resistances[0],
    resistances[0] + resistances[1],
    resistances[0] + resistances[1] + resistances[2],
  ];

  // Each segment caps flow at (bladder − closing_j) / cumR_j; cascade
  // picks the tightest. Negative cap → some segment holds, flow is 0.
  const flowRate = Math.max(0, Math.min(
    (bladderPressure - closing[0]) / cumR[0],
    (bladderPressure - closing[1]) / cumR[1],
    (bladderPressure - closing[2]) / cumR[2],
  ));

  const upstream: [number, number, number] = [0, 0, 0];
  if (flowRate > 0) {
    // Walk upstream from atmosphere applying the gate equation.
    let downstream = 0;
    for (let k = 2; k >= 0; k--) {
      upstream[k] = Math.max(closing[k], downstream) + flowRate * resistances[k];
      downstream = upstream[k];
    }
  } else {
    // Static regime: bladder pressure propagates forward until the first
    // segment strong enough to hold it; downstream of that is atmospheric.
    let p = bladderPressure;
    for (let k = 0; k < 3; k++) {
      upstream[k] = p;
      if (closing[k] >= p) p = 0;
    }
  }

  return { flowRate, upstream };
}

Wiring it into a tick step

The cascade is steady-state — it solves for the instantaneous flow given current pressures. To run the urethra each game tick, wrap it with the bladder mass balance (incoming urine from the kidney, outgoing flow through the urethra):

ts
function urethra(state: State, params: Params): void {
  const { flowRate, upstream } = urethralCascade(
    state.bladder.pressure,
    [
      state.sphincter.internalSegment,
      state.sphincter.externalSegment,
      state.sphincter.obstructionSegment,
    ],
    [
      params.urethra.internalSegmentResistance,
      params.urethra.externalSegmentResistance,
      params.urethra.obstructionSegmentResistance,
    ],
  );
  state.urethra = {
    flowRate,
    pressureU1: upstream[1],
    pressureU2: upstream[2], // PMC sensor input
  };
}

// The bladder reservoir owns its mass balance — it reads
// urethra.flowRate from this tick and updates state.bladder.volume.

State field names are illustrative — adapt to your own state shape.

Caller hints

  • During voluntary holding, set externalSegmentPressure equal to bladderPressure — that's the minimum effort that holds, per Principle 1 of the derivation.
  • During voluntary voiding, set externalSegmentPressure = 0 (and typically internalSegmentPressure low, since the micturition reflex relaxes both).
  • obstructionSegmentPressure is the externally-applied pressure (a hand between the legs, a heel on the perineum). Realistic values range from ~30 cmH₂O for light contact to 200+ for a firm heel press.
  • For PMC feedback (the brain's signal that flow is occurring), read upstream[2] (U₂).