git:20261027.56400d0 to git:20261027.5d03d13

46 added, 7 removed. Audit A to A.

---
name: neqsim-rotating-equipment-design
- description: "This skill guides agents through NeqSim compressor, expander and pump performance modeling and mechanical screening. USE WHEN: loading or calibrating vendor maps, checking chart extrapolation, modeling a compressor train or turboexpander, checking pump NPSH/API 610 inputs, or screening rotating-equipment mechanics. Anchors: CompressorChart, CompressorChartCalibrator, CompressorTrain, TurboExpanderMapIngestion, PumpChart, PumpApi610DesignCalculator."
- last_verified: "2026-10-03"
+ description: "NeqSim rotating-equipment studies. USE WHEN: dry gas seal or seal gas conditioning troubleshooting, regulator JT condensation, primary vent leakage, buffer/separation gas, API 692/ISO 6368 scope, startup and standby margins, compressor vendor maps/trains, turboexpanders, pump NPSH/API 610 or mechanical screening. APIs: DryGasSealAnalyzer, SealGasSupplyConditioning, DryGasSealMonitor, LabyrinthSealFlowCalculator, CompressorThermalModel."
+ last_verified: "2026-10-27"
---
# Rotating Equipment Design
## When to use this
Use this skill when an agent needs to create, correct, apply or interpret compressor, turboexpander or pump performance maps, operating envelopes, operating profiles, or first-pass mechanical checks. Load `neqsim-compressor-antisurge-recycle` for recycle-control topology and anti-surge dynamics. This skill covers machine performance/design API and data validity, not a vendor-certified selection.
## Class map
| Class | Package | What it does | Verified API |
|---|---|---|---|
| `CompressorChart`, `CompressorChartInterface` | `neqsim.process.equipment.compressor` | Stores speed curves and retrieves head/efficiency at flow and speed | `addCurve(double,double[],double[],double[])`, `getPolytropicHead(double,double)`, `getPolytropicEfficiency(double,double)` |
| `CompressorChartReader`, `CompressorChartJsonReader` | `neqsim.process.equipment.compressor` | Loads curve data and attaches chart curves to a compressor | `setCurvesToCompressor(Compressor)` |
| `CompressorChartCalibrator`, `SafeSplineSurgeCurve`, `SafeSplineStoneWallCurve` | `neqsim.process.equipment.compressor` | Fits measured surge points and provides safe surge/stonewall curve implementations | `fitSurgeCurve(double[],double[])`, `molarMassHeadCorrectionFactor(double,double)` |
| `CompressorCurveCorrections`, `CompressorCurveTemplate`, `CompressorChartMWInterpolation` | `neqsim.process.equipment.compressor` | Applies declared curve corrections/templates or interpolates maps by gas molecular weight | `calculateReynoldsEfficiencyCorrection(double,double)`, `addMapAtMW(double,double[],double[][],double[][],double[][])` |
| `CompressorChartAlternativeMapLookup`, `CompressorChartAlternativeMapLookupExtrapolate`, `CompressorChartKhader2015` | `neqsim.process.equipment.compressor` | Alternative map lookup, explicit extrapolation variant and Khader chart form | `getPolytropicHead(...)`, `getPolytropicEfficiency(...)`, `setCurves(...)` |
| `Compressor`, `CompressorTrain` | `neqsim.process.equipment.compressor` | Core machine and a separator-compressor-aftercooler composite | `setCompressorChart(...)`, `getChartFlowStatus()`, `isChartExtrapolated()`, `getValidatedDistanceToSurge()`, `getCompressor()` |
| `OperatingEnvelope`, `CompressorThermalModel`, `StartupProfile`, `ShutdownProfile` | `neqsim.process.equipment.compressor` | Surge/stonewall boundaries, compressor heat network and speed-versus-time profiles | `getSurgeMargin(double,double,double)`, `getStonewallMargin(double,double,double)`, `getNodes()`, `getTargetSpeedAtTime(double,double)` |
| `CompressorOperatingHistory`, `CompressorWashing`, `CompressorMechanicalLosses`, `RecycleFlowCoordinator`, `AdvancedAntiSurgeControlSystem` | `neqsim.process.equipment.compressor` | History, degradation/wash, seal and bearing losses, recycle and advanced controller calculations | `getCurrentFoulingFactor()`, `getTotalMechanicalLoss()`, `getLastRecycleFlow()`, `calculateValveCommand(double,double,double)` |
| `TurboExpanderCompressor`, `MapTurboExpanderCompressor`, `Expander` | `neqsim.process.equipment.expander` | Expander or coupled expander/compressor machine and power-balance state | `getPowerExpander("kW")`, `getPowerCompressor("kW")`, `getPowerBalanceResidual()`, `getOperatingStatus()` |
| `TurboExpanderMapIngestion`, `RadialExpanderGeometryMap`, `TurboExpanderOperatingEnvelope`, `ExpanderChartKhader` | `neqsim.process.equipment.expander` | Map anchors, geometry-derived curves, operating grid and Khader chart lookup | `addAnchorPoint(String,double,double,double)`, `setReferenceFluid(SystemInterface)`, `setGrid(double[],double[])`, `getFeasibility()` |
| `Pump`, `PumpChart`, `SuckerRodPump` | `neqsim.process.equipment.pump` | Centrifugal pump thermodynamics/map; sucker-rod pump specialty model | `setOutletPressure(double)`, `getPower(String)`, `getPumpChart()`, chart `getHead(double,double)` and `getNPSHRequired(double,double)` |
| `PumpHydraulicsNpshCalculator`, `PumpApi610DesignCalculator` | `neqsim.process.mechanicaldesign.pump` | Hydraulic power/NPSH calculation and evidence-based API 610 screening | `setDutyPoint(double,double,double,double)`, `setSuctionConditions(double,double,double,double,double,double)`, `calcHydraulics()`, `getNpshAvailable()`, API 610 `calculate()`, `getChecks()` |
| `AxialThrustScreening`, `CompressorCasingDesignCalculator`, `RotorUnbalanceAssessment`, `CompressorDesignFeasibilityReport` | `neqsim.process.mechanicaldesign.compressor` | Compressor thrust, casing, unbalance and feasibility screens | `getPredictedThrust()`, `getRequiredWallThicknessMm()`, `getUnbalanceRatio()`, `getVerdict()` |
| `TurboExpanderSealGasEnvelope` | `neqsim.process.mechanicaldesign.expander` | Coupled expander/compressor seal-gas and thrust envelope | `getNetAxialThrust()`, `getThrustUtilisation()`, `getRequiredHeaterDuty()` |
### Map workflow
1. Fix a reference gas, speed, inlet basis, flow unit and head unit from the vendor source. Keep the raw points and their source alongside derived points.
2. Choose the chart class by interpolation/extrapolation behavior. Load measured curves through `CompressorChartReader`/`CompressorChartJsonReader` or use `CompressorChart.addCurve(...)`/`setCurves(...)` for explicit points.
3. Attach via `Compressor.setCompressorChart(...)`; evaluate head and efficiency at representative vendor points before using a full process train.
4. Apply only corrections supported by documented inputs. `CompressorChartCalibrator.fitSurgeCurve(...)` installs a measured surge curve; its molecular-weight method returns a correction factor, it does not alter a map by itself.
5. At every design, sweep and optimizer point, inspect `getChartFlowStatus()` and `isChartExtrapolated()`. Use `getValidatedDistanceToSurge()` where distance-to-surge is consumed by a safety/optimization decision.
Turboexpander map work is separate: create `TurboExpanderMapIngestion`, add vendor/test anchor points with velocity ratio, IGV opening and measured efficiency, configure the reference fluid and impeller diameters, then validate against the expander/compressor operating envelope. Geometry-derived maps and `ExpanderChartKhader` are correlations/models, not converted vendor data.
- ## Seal gas supply and contamination (added after the Grane PEPR 80302027 study)
+ ## Dry gas seal systems, conditioning and troubleshooting
- Dry gas seals fail from liquid in the supply as often as from the machine. Two classes in `neqsim.process.equipment.compressor` split the problem:
+ Read [the engineering review](../../../docs/process/equipment/dry_gas_seal_engineering_review.md)
+ for the primary literature, source audit, standards boundaries, failure hypotheses
+ and validation priorities. Use public/synthetic data unless private inputs are supplied
+ and authorized. Retain OEM limits and parameter provenance rather than transferring
+ another machine's rules of thumb.
- - `DryGasSealAnalyzer`: leakage side, JT condensation in the primary vent and standpipes.
- - `SealGasSupplyConditioning`: supply side. `setSource(P, "barg", T, "C")`, `setSupplyPressure(P, "barg")`, optional `setHeaterOutletTemperature`, then `getSupplyTemperatureC()`, `getWaterDewPointC()`, `getHydrocarbonDewPointC()`, `getMinimumMarginK()`, `isMarginMet()` (API 692 practice 20 K) and `calculateRequiredInletTemperatureC(maxC)`. Static helpers `impliedLiquidLoadKgPerMSm3(...)` and `sumpFillTimeDays(...)` turn a filter sump filling rate into kg of liquid per MSm3 to compare with the filter datasheet contaminant basis (Grane CA-23-0026: 10-20 kg/MSm3).
+ Use these existing APIs after inspecting current constructors/methods:
- Rules of thumb checked on Grane: JT cooling over the supply valve of a dense lean gas is 15-20 K, so a seal gas taken at 45 C and 92 barg has about 0 K margin at 49 barg without a heater; a take-off at an impeller exit has 7-36 K depending on the head already produced; a take-off at the stage discharge has more than 40 K. Fuel gas used as seal gas (2nd/3rd stage on Grane) is cooled to about 20 C, where the model gives 370 kg/MSm3 of equilibrium condensate and none at 28 C, so check the fuel gas temperature before blaming the filters. Natural experiments (import gas as fuel, set-point steps) discriminate better than regressions on daily data.
+ - `DryGasSealAnalyzer`: configure an owned gas clone with `setSealGas`, cavity/vent
+ pressures, temperature, leakage and independent GCU supply throughput. Run
+ `runFullAnalysis` and inspect complete/error evidence in `getResults`.
+ `isSafeToOperate` is a legacy sampled-condensation screen, not an operating permissive.
+ - `SealGasSupplyConditioning`: `setSource`, `setSupplyPressure`, optional
+ `setHeaterOutletTemperature`, then supply temperature, dewpoints, margin and heater
+ search. The audit identifies single-liquid, failed/missing dewpoint, unit and caller
+ ownership concerns. Validate evidence independently; do not equate a passing
+ `isMarginMet` Boolean with demonstrated complete-path dryness. Use
+ `impliedLiquidLoadKgPerMSm3` and `sumpFillTimeDays` only with explicit input bases.
+ - `DryGasSealMonitor`: caller-defined `Limits`, quality-aware `Sample`,
+ `evaluate` and explicit simulator `reset`. Supply-process DP, primary vent,
+ intermediate buffer and bearing-side separation signals are distinct.
+ - `LabyrinthSealFlowCalculator`: process-side straight-through labyrinth demand,
+ choking and validity checks. This is not grooved dry-seal face leakage.
+ - `CompressorThermalModel`: calibrated lumped seal/shaft/metal temperatures;
+ this does not resolve local seal-film thermohydrodynamics.
+
+ Freeze source/cavity/process/vent pressures, seal arrangement, startup/running/trip/
+ standstill mode, composition including water/heavy ends, gas/metal/ambient temperatures,
+ normal-flow conditions, timestamp/quality and OEM limits. Evaluate regulator JT
+ cooling, wet supply, heater/booster capacity, filters/coalescers, sump/drains, vent
+ backpressure and buffer/separation loss. Primary vent flow may include nitrogen;
+ reconcile branch balances and flowmeter gas basis before diagnosing face damage.
+
+ Use API 692 and ISO 6368:2021 for dry-seal-system scope. API 614 sixth edition
+ excludes dry gas seal systems. The existing 17 K/20 K defaults are screening inputs,
+ not universal API 692 requirements. Full licensed clauses, project edition and OEM
+ instructions are needed for a conformity assessment.
+
+ Keep failed/missing thermodynamics unresolved. Compare competing hypotheses and
+ changes after interventions; a high vent signal alone does not uniquely identify
+ seal failure. Hand historian data to `@plant-data`, fault hypotheses to `@root-cause`,
+ fluid validity to `@thermo-fluid`, and regulator/controller dynamics to `@control-system`.
+ Reuse #4144/#4145 and roadmap #4149; do not claim that source discovery adds a curated
+ MCP operation. Keep expensive diagnostics optional so ordinary compressor runs do
+ not gain extra flashes.
## Build pattern
This Java 8 example follows the `CompressorTrain` usage shown in source. It creates a simple gas feed, runs a scrubber-compressor-aftercooler unit and reads shaft power. Replace the illustrative conditions and efficiencies with a controlled design basis.
```java
import neqsim.process.equipment.compressor.CompressorTrain;
import neqsim.process.equipment.stream.Stream;
import neqsim.thermo.system.SystemInterface;
import neqsim.thermo.system.SystemSrkEos;
SystemInterface gas = new SystemSrkEos(280.0, 30.0);
gas.addComponent("methane", 0.90);
gas.addComponent("ethane", 0.08);
gas.addComponent("propane", 0.02);
gas.setMixingRule("classic");
Stream feed = new Stream("Feed", gas);
feed.setFlowRate(5000.0, "kg/hr");
CompressorTrain train = new CompressorTrain("HP Train", feed);
train.getCompressor().setOutletPressure(85.0);
train.getCompressor().setPolytropicEfficiency(0.76);
train.getCompressor().setUsePolytropicCalc(true);
train.getAftercooler().setOutletTemperature(308.15);
train.run();
double powerKW = train.getPower("kW");
```
Equivalent Python class lookup:
```python
from neqsim import jneqsim
SystemSrkEos = jneqsim.thermo.system.SystemSrkEos
Stream = jneqsim.process.equipment.stream.Stream
CompressorTrain = jneqsim.process.equipment.compressor.CompressorTrain
gas = SystemSrkEos(280.0, 30.0)
gas.addComponent("methane", 0.90)
gas.addComponent("ethane", 0.08)
gas.addComponent("propane", 0.02)
gas.setMixingRule("classic")
feed = Stream("Feed", gas)
feed.setFlowRate(5000.0, "kg/hr")
train = CompressorTrain("HP Train", feed)
train.getCompressor().setOutletPressure(85.0)
train.getCompressor().setPolytropicEfficiency(0.76)
train.getCompressor().setUsePolytropicCalc(True)
train.getAftercooler().setOutletTemperature(308.15)
train.run()
power_kw = train.getPower("kW")
```
## Result extraction
| Quantity | API / unit |
|---|---|
| Compressor chart head and efficiency | `getPolytropicHead(flow,speed)` in chart head unit; `getPolytropicEfficiency(flow,speed)` as chart value |
| Chart-range status | `getChartFlowStatus()` returns `IN_RANGE`, `EXTRAPOLATED_LOW_FLOW`, `EXTRAPOLATED_HIGH_FLOW` or `NO_CHART` |
| Validated surge distance | `getValidatedDistanceToSurge()` returns the normal distance or `Double.NaN` when the chart point is extrapolated |
| Train power | `CompressorTrain.getPower("kW")` |
| Pump map | `PumpChart.getHead(flow,speed)`, `getEfficiency(flow,speed)`, `getNPSHRequired(flow,speed)` |
| Pump NPSH/hydraulics | `PumpHydraulicsNpshCalculator.getNpshAvailable()`, `getNpshMargin()`, `getHydraulicPower()`, `getBrakePower()` |
| API 610 review | `PumpApi610DesignCalculator.getAssessmentStatus()`, `getChecks()` and per-check unit/limit/status |
| Expander/compressor power | `getPowerExpander("kW")`, `getPowerCompressor("kW")`; coupled machine residual from `getPowerBalanceResidual()` |
| Mechanical screens | Check units on each result class; examples include casing thickness mm/pressure MPa, thrust N, and unbalance g·mm |
## Compressor trains on a common shaft (power-limited driver)
Use `CompressorShaft` (not one `Compressor` with `setSolveSpeed(true)` per body) whenever stages share a driver, also
when the stages sit in different platform models. Full code pattern: `neqsim-compressor-antisurge-recycle`, section
"Common-Shaft Trains Across Platforms, Power-Limited Drivers and Ambient Derating". Quick map from the Python
`ShaftGroup` idiom (Oseberg `eot_ofc_common.shaft`) to Java:
| Need | Java |
|---|---|
| members with gear ratio | `addCompressor(c, ratio)`, `setGearRatio`, `getMemberSpeed` |
| controlling member sets speed | `solveSpeedToTarget(member, DISCHARGE_PRESSURE / SUCTION_PRESSURE / INLET_FLOW, value, unit, run)` |
| per-member margins, recycle demand, flags | `getOperatingReport()` (`toJson()`), `setMemberRequiredDischargePressure` |
| train off / decoupled stage | `setMemberActive(c, false)`, `removeCompressor(c)`, status `SHAFT_IDLE` |
| shared driver power with ambient | `setGasTurbineUnitDriver(unit)` + `setAmbientTemperature(T, "C")`, `setMaxShaftPower`, `setElectricSupplyLimit` |
| max speed / max throughput at the driver limit | `findMaxSpeedWithinPowerLimit(run)`, `DriverLimitedThroughputSolver` |
| fuel, exhaust, waste heat | `updateDriverOperatingPoint()` then the `GasTurbineUnit` getters |
Not in Java: the Python snapshot evaluation without re-running the flowsheet (Java always re-runs through the
callback), and automatic application of the recycle demand to the anti-surge valves (read `requiredRecycleFraction`
and set the recycle yourself, or let the process anti-surge controller do it).
## Gotchas
- `AxialThrustScreening` rejects non-finite inputs/results and requires a positive fitted zero-crossing for a relative margin. An invalid case is not a safe margin or `NULL` thrust. Report the data range, extrapolation and OEM thrust sign convention.
- `ValveRangeabilityScreening` rejects missing trim and non-finite travel/rangeability. Finite travel is clamped to 0–1; a zero reference flow gives an undefined ratio (`NaN`). Its flow ratio assumes unchanged fluid conditions and pressure drop, not an installed-system response.
- Chart interpolation and extrapolation are not equivalent. `CompressorChartAlternativeMapLookupExtrapolate` explicitly permits extrapolation; outside available reference speeds, it can use a single nearest curve and scale head with speed. Treat that output as extrapolated, not vendor-supported operation.
- A chart can return finite, plausible-looking values outside its calibrated flow range. `getChartFlowStatus()` may report extrapolation even at an auto-generated nominal point. Gate every sweep point; `getValidatedDistanceToSurge()` is `NaN` outside the reported valid flow range.
- `getDistanceToSurge()` alone does not enforce the chart-range gate. Do not bisect or optimize on it without filtering extrapolated points and reporting the number excluded.
- `fitSurgeCurve` fits the control/surge boundary from measured flow/head; `molarMassHeadCorrectionFactor` only returns `mwReference / mwActual`. Neither certifies the rest of a vendor map.
- Use `getAftercooler()` on `CompressorTrain`; the earlier source Javadoc example calling `getCooler()` was corrected 2026-10.
- Do not infer chart units from array names. Flow points are inlet actual m3/hr in `CompressorChartCalibrator.fitSurgeCurve`; head must match the chart head unit; verify map reader metadata and reference conditions.
- `PumpApi610DesignCalculator` compares caller-supplied duty, BEP, NPSH, pressure, driver and mechanical evidence. It does not derive missing vendor data or certify API 610 compliance.
- NPSH margin is a screening result only when suction pressure, vapor pressure, static head, duty and NPSHr provenance are correct and use compatible units.
- A turboexpander anchor-point map is only as good as its reference fluid and anchor provenance. Confirm power balance, shaft mode/speed limits, cold-end temperature and hydrate margin.
- Compressor thermal/startup/shutdown models do not by themselves establish a stable controller or machine protection function; pair with dynamic process and anti-surge workflows as needed.
- Bypassing or removing one stage of a fixed-speed multi-stage centrifugal train (for example a GI 2nd stage whose gas is let down again): the machine delivers head from its curve, so the saving equals that stage's curve power per tonne (derive kW per t/h from live data and a benchmarked stage model), not a power recomputed from a changed system-pressure path. A model that recomputes head from a falling downstream pressure shows the remaining stage consuming less power as the system pressure falls, which a fixed-speed machine does not do (the excess pressure is throttled). Bound the two views and run the weight between them as an uncertainty. Clamp flow at the minimum stable flow per running train (recycle) in decline years.
- Chart-driven (head, efficiency) trains modelled outside `Compressor`: shaft power is `m_dot * Hp * g / eta_p / eta_mech`, not `m_dot * Hp * g`; the Schultz exponent is `n/(n-1) = kappa * eta_p / (kappa - 1)`. A sibling DX revamp model that dropped the `/eta_p` under-stated power about 25 % and reached only 71 % of the measured gas-turbine load; the corrected form reached 91-93 % (Oseberg DX, Oct 2026). Always check modelled train power against the driver load (rated shaft power x load %) and a measured interstage pressure before trusting savings. Stages that appear idle ("deep recycle", anti-surge flow close to the full train flow) still absorb power: derive it from measured suction/discharge pressures and the equivalent-flow tags (they were within 5 % of the model's actual flows) and run it through `Compressor` with a polytropic efficiency range.
## Validation / benchmarks
Use `CompressorTrainTest`, `CompressorChartGeneratorTest`, `CompressorChartSpeedInterpolationTest`, `TurboExpanderEnhancementsTest`, pump chart tests, and the corresponding design-calculator tests as API/regression evidence. Validate map values at vendor-rated points, hold out at least one point or curve, verify reference-gas conversion, and demonstrate the full intended operating envelope is in range. For API 610/API 617 screening, retain data provenance, assumptions, standard edition and unresolved checks. A generated map or a passing software test is not a vendor performance test.
## Related skills
- `neqsim-compressor-antisurge-recycle` — anti-surge recycle and minimum-speed coordination
- `neqsim-api-patterns` — compressor and pump process API patterns
- `neqsim-dynamic-equipment-implementation` — adding transient behavior to equipment
- `neqsim-troubleshooting` — operating-point and process convergence diagnostics
- `neqsim-power-generation` — gas-turbine/driver coupling
- `neqsim-dynamic-simulation` — transient controllers and process response
- `neqsim-vfp-lift-curves` — compressor capacity vs suction pressure as a VFP rate cap (scan the feasible flow band; a chart at low pressure ratio is not a bisection problem)