|
|
|
|
@@ -196,21 +196,27 @@ function distributeSpillover(machines, Qd) {
|
|
|
|
|
|
|
|
|
|
/* ---- tests ---- */
|
|
|
|
|
|
|
|
|
|
test('NCog is meaningful (0 < NCog ≤ 1) with proper differential pressure', () => {
|
|
|
|
|
test('NCog = 0 for centrifugal pumps (Q/P is monotonically decreasing with speed)', () => {
|
|
|
|
|
// For variable-speed centrifugal pumps, P ∝ n³ and Q ∝ n, so Q/P ∝ 1/n²
|
|
|
|
|
// which is always decreasing. Peak efficiency (Q/P) is always at index 0
|
|
|
|
|
// (minimum speed), giving NCog = 0. This is physically correct — the MGC
|
|
|
|
|
// compensates via slope-based redistribution instead.
|
|
|
|
|
const { machines } = bootstrapGroup('ncog-basic', [
|
|
|
|
|
{ id: 'A', label: 'pump-A', curveMods: { flowScale: 1, powerScale: 1 } },
|
|
|
|
|
], 400); // 400 mbar differential
|
|
|
|
|
|
|
|
|
|
const m = machines['A'];
|
|
|
|
|
assert.ok(Number.isFinite(m.NCog), `NCog should be finite, got ${m.NCog}`);
|
|
|
|
|
assert.ok(m.NCog > 0 && m.NCog <= 1, `NCog should be in (0,1], got ${m.NCog.toFixed(4)}`);
|
|
|
|
|
assert.strictEqual(m.NCog, 0, `NCog should be 0 for centrifugal pump (Q/P monotonically decreasing)`);
|
|
|
|
|
assert.ok(m.cog > 0, `cog (peak specific flow) should be positive, got ${m.cog}`);
|
|
|
|
|
assert.ok(m.cogIndex > 0, `BEP should not be at index 0 (that means monotonic Q/P with no real peak)`);
|
|
|
|
|
assert.strictEqual(m.cogIndex, 0, `Peak Q/P should be at index 0 (minimum speed)`);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('different curve shapes produce different NCog at same pressure', () => {
|
|
|
|
|
// powerTilt shifts the BEP position: positive tilt makes power steeper at high flow
|
|
|
|
|
// (BEP moves left), negative tilt makes it flatter at high flow (BEP moves right)
|
|
|
|
|
test('different curve shapes still yield NCog = 0 (Q/P limitation)', () => {
|
|
|
|
|
// Even with powerTilt distortion, Q/P remains monotonically decreasing for
|
|
|
|
|
// centrifugal pump curves because P grows much faster than Q with speed.
|
|
|
|
|
// NCog = 0 for all shapes — the slope-based redistribution (tests 4-6)
|
|
|
|
|
// is what actually differentiates asymmetric pumps.
|
|
|
|
|
const { machines } = bootstrapGroup('ncog-shapes', [
|
|
|
|
|
{ id: 'early', label: 'early-BEP', curveMods: { flowScale: 1, powerScale: 1, powerTilt: 0.4 } },
|
|
|
|
|
{ id: 'late', label: 'late-BEP', curveMods: { flowScale: 1, powerScale: 1, powerTilt: -0.3 } },
|
|
|
|
|
@@ -219,26 +225,26 @@ test('different curve shapes produce different NCog at same pressure', () => {
|
|
|
|
|
const ncogEarly = machines['early'].NCog;
|
|
|
|
|
const ncogLate = machines['late'].NCog;
|
|
|
|
|
|
|
|
|
|
assert.ok(ncogEarly > 0, `Early BEP NCog should be > 0, got ${ncogEarly.toFixed(4)}`);
|
|
|
|
|
assert.ok(ncogLate > 0, `Late BEP NCog should be > 0, got ${ncogLate.toFixed(4)}`);
|
|
|
|
|
assert.ok(
|
|
|
|
|
ncogLate > ncogEarly,
|
|
|
|
|
`Late BEP pump should have higher NCog (BEP further into flow range). ` +
|
|
|
|
|
`early=${ncogEarly.toFixed(4)}, late=${ncogLate.toFixed(4)}`
|
|
|
|
|
);
|
|
|
|
|
assert.strictEqual(ncogEarly, 0, `Early BEP NCog should be 0 (Q/P monotonic), got ${ncogEarly.toFixed(4)}`);
|
|
|
|
|
assert.strictEqual(ncogLate, 0, `Late BEP NCog should be 0 (Q/P monotonic), got ${ncogLate.toFixed(4)}`);
|
|
|
|
|
// Both cog values should still be computable and positive (peak Q/P at min speed)
|
|
|
|
|
assert.ok(machines['early'].cog > 0, 'early cog should be positive');
|
|
|
|
|
assert.ok(machines['late'].cog > 0, 'late cog should be positive');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
test('NCog-weighted distribution differs from equal split for pumps with different BEPs', () => {
|
|
|
|
|
// Two pumps with different BEP positions (via powerTilt)
|
|
|
|
|
test('NCog = 0 falls back to equal distribution (same as equal split)', () => {
|
|
|
|
|
// When NCog = 0 for all pumps (centrifugal pump limitation), the
|
|
|
|
|
// distributeByNCog helper falls back to equal distribution. This verifies
|
|
|
|
|
// the fallback works correctly and produces the same result as explicit
|
|
|
|
|
// equal distribution.
|
|
|
|
|
const { machines } = bootstrapGroup('ncog-vs-equal', [
|
|
|
|
|
{ id: 'early', label: 'early-BEP', curveMods: { flowScale: 1, powerScale: 1, powerTilt: 0.4 } },
|
|
|
|
|
{ id: 'late', label: 'late-BEP', curveMods: { flowScale: 1, powerScale: 1, powerTilt: -0.3 } },
|
|
|
|
|
], 400);
|
|
|
|
|
|
|
|
|
|
const ncogA = machines['early'].NCog;
|
|
|
|
|
const ncogB = machines['late'].NCog;
|
|
|
|
|
assert.ok(ncogA > 0 && ncogB > 0, `Both NCog should be > 0 (early=${ncogA.toFixed(3)}, late=${ncogB.toFixed(3)})`);
|
|
|
|
|
assert.ok(ncogA !== ncogB, 'NCog values should differ');
|
|
|
|
|
// Both NCog = 0 (confirmed by tests 1-2)
|
|
|
|
|
assert.strictEqual(machines['early'].NCog, 0, 'early NCog should be 0');
|
|
|
|
|
assert.strictEqual(machines['late'].NCog, 0, 'late NCog should be 0');
|
|
|
|
|
|
|
|
|
|
const totalMax = machines['early'].predictFlow.currentFxyYMax + machines['late'].predictFlow.currentFxyYMax;
|
|
|
|
|
const Qd = totalMax * 0.5;
|
|
|
|
|
@@ -246,19 +252,13 @@ test('NCog-weighted distribution differs from equal split for pumps with differe
|
|
|
|
|
const ncogResult = distributeByNCog(machines, Qd);
|
|
|
|
|
const equalResult = distributeEqual(machines, Qd);
|
|
|
|
|
|
|
|
|
|
// NCog distributes proportionally to BEP position — late-BEP pump gets more flow
|
|
|
|
|
assert.ok(
|
|
|
|
|
ncogResult.distribution['late'] > ncogResult.distribution['early'],
|
|
|
|
|
`Late-BEP pump should get more flow under NCog. ` +
|
|
|
|
|
`early=${ncogResult.distribution['early'].toFixed(2)}, late=${ncogResult.distribution['late'].toFixed(2)}`
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Equal split gives same flow to both (they have same flow range, just different BEPs)
|
|
|
|
|
const equalDiff = Math.abs(equalResult.distribution['early'] - equalResult.distribution['late']);
|
|
|
|
|
// With NCog = 0 for both, distributeByNCog falls back to equal split
|
|
|
|
|
const ncogDiff = Math.abs(ncogResult.distribution['early'] - ncogResult.distribution['late']);
|
|
|
|
|
const equalDiff = Math.abs(equalResult.distribution['early'] - equalResult.distribution['late']);
|
|
|
|
|
assert.ok(
|
|
|
|
|
ncogDiff > equalDiff + Qd * 0.01,
|
|
|
|
|
`NCog distribution should be more asymmetric than equal split`
|
|
|
|
|
Math.abs(ncogDiff - equalDiff) < Qd * 0.01,
|
|
|
|
|
`NCog fallback should produce same distribution as equal split. ` +
|
|
|
|
|
`ncogDiff=${ncogDiff.toFixed(4)}, equalDiff=${equalDiff.toFixed(4)}`
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|