- wiki/functional-description.md: rename Overfill Protection → High-volume
Safety; tighten basin-ordering chain; relocate level-based mode
diagrams under wiki/diagrams/modes/level-based/; document the new
flow.predicted.overflow.default position (replaces the previous
child='overflow' under position 'out'); add underflowVolume +
predictedUnderflowVolume entries.
- wiki/modes/{levelbased,powerbased}.md: paragraph cleanups.
- wiki/diagrams: move level-linear basin diagram under modes/level-based/
alongside a new level-log variant.
- simulations/run.js: add max_demand_gt expectation.
- simulations/scenarios/*: minor fixture updates.
- test/basic/nodeClass-config.test.js: new config-shape coverage.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
62 lines
2.7 KiB
JavaScript
62 lines
2.7 KiB
JavaScript
// Steady sewer inflow, level-based control, pumps should settle.
|
||
//
|
||
// Expectation: with a stable inflow of 0.008 m³/s and a pump bank with
|
||
// max capacity 0.012 m³/s, the level settles in the RAMP zone (between
|
||
// inflowLevel and maxLevel while filling) at roughly the point where demand matches
|
||
// inflow. No safety trips should fire.
|
||
|
||
module.exports = {
|
||
name: 'levelbased-steady',
|
||
description: 'Constant sewer inflow below pump capacity; level converges inside the RAMP zone with demand matching inflow.',
|
||
durationSec: 3600,
|
||
|
||
config: {
|
||
general: { name: 'EvalSteady', id: 'eval-steady', unit: 'm3/h',
|
||
logging: { enabled: false, logLevel: 'error' } },
|
||
functionality: { softwareType: 'pumpingStation', role: 'stationcontroller', positionVsParent: 'atEquipment' },
|
||
basin: { volume: 50, height: 5, inflowLevel: 3, outflowLevel: 0.2, overflowLevel: 4.5, inletPipeDiameter: 0.4, outletPipeDiameter: 0.3 },
|
||
hydraulics: { refHeight: 'NAP', basinBottomRef: 0, minHeightBasedOn: 'outlet' },
|
||
control: {
|
||
mode: 'levelbased',
|
||
allowedModes: new Set(['levelbased']),
|
||
levelbased: { minLevel: 1, startLevel: 2, maxLevel: 4, curveType: 'linear', logCurveFactor: 9 },
|
||
},
|
||
safety: {
|
||
enableDryRunProtection: true,
|
||
dryRunThresholdPercent: 2,
|
||
enableHighVolumeSafety: true,
|
||
highVolumeSafetyThresholdPercent: 98,
|
||
timeleftToFullOrEmptyThresholdSeconds: 0,
|
||
},
|
||
},
|
||
|
||
setup: async (ps) => {
|
||
// Stub MGC: its pumps collectively deliver (demand/100) × MAX_OUTFLOW.
|
||
const MAX_OUTFLOW = 0.012; // m³/s
|
||
ps.machineGroups['mgc1'] = {
|
||
config: { general: { name: 'mgc1' } },
|
||
turnOffAllMachines: () => {
|
||
ps.measurements.type('flow').variant('predicted').position('out').child('mgc1').value(0, Date.now(), 'm3/s');
|
||
},
|
||
handleInput: async (_source, demand) => {
|
||
const d = Math.max(0, Math.min(100, Number(demand) || 0));
|
||
const outflow = (d / 100) * MAX_OUTFLOW;
|
||
ps.measurements.type('flow').variant('predicted').position('out').child('mgc1').value(outflow, Date.now(), 'm3/s');
|
||
},
|
||
};
|
||
ps.calibratePredictedLevel(2.0); // start at the mode start level, below the rising ramp
|
||
},
|
||
|
||
inputs: (t, ps) => {
|
||
ps.setManualInflow(0.008, Date.now(), 'm3/s'); // ≈ 29 m³/h
|
||
},
|
||
|
||
expectations: [
|
||
{ name: 'no safety trips', type: 'safety_trips_eq', value: 0 },
|
||
{ name: 'level stays below overflow', type: 'max_level_bounded', value: 4.5 },
|
||
{ name: 'level stays above outflow', type: 'min_level_bounded', value: 0.2 },
|
||
{ name: 'rising ramp engages after inlet level', type: 'max_demand_gt', value: 0 },
|
||
{ name: 'no threshold issues on init', type: 'threshold_issues_eq', value: 0 },
|
||
],
|
||
};
|