- name
- neqsim-root-cause-analysis
- version
- 1.3.0
- description
- Root cause analysis for process equipment - Bayesian-style diagnosis combining reliability priors (IOGP/SINTEF, CCPS, IEEE 493, Lees, OREDA), historian evidence and NeqSim verification, including the control-loop correlation trap (difference both series, resolve direction from the lag sign). USE WHEN: diagnosing compressor trips, vibration, efficiency loss, separator carryover, exchanger fouling or any operational anomaly. Anchors on neqsim.process.diagnostics.
- last_verified
- 2026-09-17
- requires
- {"java_packages":["neqsim.process.diagnostics","neqsim.process.equipment.failure","neqsim.process.automation"]}
# NeqSim Root Cause Analysis Skill
Systematic equipment-level root cause analysis integrating process simulation,
multi-source reliability data, plant historian time-series, and STID design conditions.
## When to Use
- Equipment trip investigation (compressor, pump, turbine)
- High vibration diagnosis
- Efficiency degradation analysis
- Separator liquid carryover or gas blow-by
- Heat exchanger fouling assessment
- Valve erosion or malfunction
- Any operational anomaly requiring structured root cause identification
## Already-closed incident with a "document learning" ask
When the failure is already resolved and recorded (for example a closed Blalys whose
only open subtask is to document learning), do not re-derive the cause. Cite the
recorded cause, test it for consistency against the historian and alarm evidence
(consistent, contradicted or not testable), then deliver a consequence and learning
screening: capacity impact (for a derated driver use `Compressor.getDriverLimitedEnvelope()`),
the barrier or early indicator that would have shortened the outage, and the cost of
recurrence. Mark causal claims beyond the recorded cause as hypotheses.
## Rapid Triage Mode (live disturbance)
A full RCA is a study. When the plant is **still down** and a response team needs a
direction within minutes, run the same framework in a reduced, explicitly labelled
mode rather than a different method:
| | Full RCA | Rapid triage |
|---|---|---|
| Prior | Multi-source reliability data | Reliability prior only, no source reconciliation |
| Likelihood | Full historian window, quality-screened | Last stable period to now, unscreened |
| Verification | NeqSim simulation of each hypothesis | Skipped; verification is deferred |
| Output | Ranked hypotheses with confidence | Top 3 candidates, confidence capped at `MODERATE` |
| Cause claim | Established when evidence supports it | `NOT_DETERMINED` unless a single candidate dominates |
Rules that make the reduced mode safe to act on:
1. **Label the mode in the output.** A triage result must never be presented, or
later quoted, as a completed RCA.
2. **Cap the confidence.** Without simulation verification the confidence ceiling is
`MODERATE`, regardless of how strongly the historian agrees.
3. **Time-box it.** State the box up front. When it expires, report the ranking as it
stands plus what is still unknown — restoration decisions do not wait for cause.
4. **Report single-source dependence.** If every piece of evidence for the leading
candidate comes from one instrument or one system, say so; that is the most common
way a triage conclusion turns out wrong.
5. **Promote, do not restart.** When the plant is stable, re-run the same hypotheses
in full mode with simulation verification; the triage ranking becomes the prior.
Rapid triage is the diagnose step of a live-disturbance response loop. The incident
coordination, phase gating, and restoration-option evaluation around it live in the
operations agents that call this skill.
## Architecture Overview
The RCA framework uses a Bayesian-inspired three-stage methodology:
```
Prior (Reliability Data) × Likelihood (Historian) × Verification (Simulation) = Confidence
```
### Reliability Data Sources
`ReliabilityDataSource` loads from multiple public databases automatically:
- **IOGP Report 434 / SINTEF** — offshore O&G equipment (free)
- **CCPS 1989** — process vessels, piping, valves (published handbook)
- **IEEE 493-2007** — industrial electrical equipment (purchasable standard)
- **Lees' Loss Prevention 2012** — generic process industry rates (textbook)
- **SINTEF PDS** — safety-instrumented systems (purchasable)
- **OREDA** — offshore equipment (optional, commercial)
No commercial license is required for the default data.
### Core Classes
| Class | Purpose |
|-------|---------|
| `RootCauseAnalyzer` | Main orchestrator — takes ProcessSystem + equipment name, runs full analysis |
| `Symptom` | Enum of 12 equipment symptoms (TRIP, HIGH_VIBRATION, etc.) |
| `Hypothesis` | Candidate root cause with expected signal fingerprints, evidence, prior/likelihood/verification scores |
| `HypothesisGenerator` | Generates candidate hypotheses from built-in libraries and reliability-data-adjusted priors per equipment type |
| `EvidenceCollector` | Analyzes historian/STID data and attaches only hypothesis-relevant supporting or contradictory evidence |
| `DiagnosisCase` | Captures equipment, window, provenance, process-model identity, context, and data quality |
| `SimulationVerifier` | Clones process and reports explicit status, KPI coverage, and magnitude-aware comparison |
| `RootCauseReport` | Ranked output with JSON and text report generation |
### Package Location
All classes in `neqsim.process.diagnostics`.
## Quick Start
### Java
```java
// 1. Create analyzer with running process and target equipment
RootCauseAnalyzer rca = new RootCauseAnalyzer(processSystem, "Compressor-1");
// 2. Set the observed symptom
rca.setSymptom(Symptom.HIGH_VIBRATION);
// 3. Provide historian data (from tagreader or CSV)
Map<String, double[]> historianData = new HashMap<>();
historianData.put("vibration_mm_s", vibrationTimeSeries);
historianData.put("discharge_temp_C", tempTimeSeries);
historianData.put("power_kW", powerTimeSeries);
rca.setHistorianData(historianData, timestamps);
// 4. Set design limits from STID
rca.setDesignLimit("vibration_mm_s", Double.NaN, 7.1);
rca.setDesignLimit("discharge_temp_C", Double.NaN, 180.0);
// 5. Optionally provide STID design values
Map<String, String> stidData = new HashMap<>();
stidData.put("design_flow_kg_hr", "50000");
stidData.put("design_efficiency_pct", "82");
rca.setStidData(stidData);
// 6. Run analysis
RootCauseReport report = rca.analyze();
// 7. Get results
String textReport = report.toTextReport();
String json = report.toJson();
Hypothesis topCause = report.getTopHypothesis();
```
### Python (Jupyter)
```python
RootCauseAnalyzer = ns.JClass("neqsim.process.diagnostics.RootCauseAnalyzer")
Symptom = ns.JClass("neqsim.process.diagnostics.Symptom")
rca = RootCauseAnalyzer(process, "Compressor-1")
rca.setSymptom(Symptom.HIGH_VIBRATION)
# Convert numpy arrays to Java
from jpype import JArray, JDouble
historian = ns.HashMap()
historian.put("vibration_mm_s", JArray(JDouble)(vibration_data.tolist()))
rca.setHistorianData(historian, JArray(JDouble)(timestamps.tolist()))
report = rca.analyze()
print(str(report.toTextReport()))
```
## Symptom Reference
| Symptom | Description | Related Categories |
|---------|-------------|-------------------|
| `TRIP` | Equipment trip/shutdown | MECHANICAL, CONTROL, EXTERNAL |
| `HIGH_VIBRATION` | Vibration above limits | MECHANICAL |
| `SEAL_FAILURE` | Seal leakage or failure | MECHANICAL |
| `HIGH_TEMPERATURE` | Temperature exceeding limits | MECHANICAL, PROCESS |
| `LOW_EFFICIENCY` | Performance below design | PROCESS, MECHANICAL |
| `PRESSURE_DEVIATION` | Unexpected pressure change | PROCESS, CONTROL |
| `FLOW_DEVIATION` | Unexpected flow change | PROCESS, CONTROL |
| `HIGH_POWER` | Power consumption above normal | PROCESS, MECHANICAL |
| `SURGE_EVENT` | Compressor surge | PROCESS, CONTROL |
| `FOULING` | Fouling or deposit buildup | PROCESS |
| `ABNORMAL_NOISE` | Unusual noise patterns | MECHANICAL |
| `LIQUID_CARRYOVER` | Liquid in gas outlet | PROCESS |
## Built-in Hypothesis Libraries
### Compressor
- **HIGH_VIBRATION**: Bearing wear, impeller imbalance, shaft misalignment, fouled impeller, loose foundation
- **HIGH_TEMPERATURE**: Seal degradation, process gas composition change, fouled intercooler, recycle valve malfunction
- **LOW_EFFICIENCY**: Internal leakage, fouled impeller, eroded impeller, gas composition change
- **SURGE_EVENT**: Low suction flow, blocked suction, anti-surge valve failure, gas composition change
- **TRIP**: Bearing failure, high vibration, high discharge temperature, low lube oil pressure, process upset
### Pump
- **HIGH_VIBRATION**: Cavitation, impeller wear, shaft misalignment
- **LOW_EFFICIENCY**: Impeller wear, internal recirculation
- **TRIP**: Bearing failure, seal failure, low suction pressure
### Separator
- **LIQUID_CARRYOVER**: Demister fouled, high liquid level, feed rate exceeded, foam formation
- **PRESSURE_DEVIATION**: Pressure control valve malfunction, blocked outlet, relief valve leaking
- **FOULING**: Wax deposition, scale buildup, sand accumulation
### Heat Exchanger
- **HIGH_TEMPERATURE**: Fouled tubes, loss of cooling, bypass valve stuck, air in cooling water
- **LOW_EFFICIENCY**: Fouling, tube leak, flow maldistribution
- **PRESSURE_DEVIATION**: Tube blockage, shell-side fouling
### Valve
- **FLOW_DEVIATION**: Trim erosion, actuator malfunction, positioner calibration drift
- **ABNORMAL_NOISE**: Cavitation, flashing, vibration from upstream piping
## Evidence Analysis Methods
Evidence is hypothesis-specific. Built-in hypotheses define expected signal
fingerprints such as `vibration|bearing` increasing, `lubeOilPressure` below
limit, `demisterDp` increasing, or `antiSurgeValve` not opening. Historian and
STID observations are matched by alias pattern. Matching observations become
supporting evidence; opposite observations become contradictory evidence;
unrelated trends are ignored instead of inflating all hypotheses.
Each evidence item includes:
- `supporting`: whether the observation supports or contradicts the hypothesis
- `weight`: the importance of the expected signal in the hypothesis fingerprint
- `sourceReference`: optional source/tag/document reference for traceability
### Trend Analysis
Linear regression on time-series data. Reports slope, R-squared, and percent change.
- **STRONG**: R-squared > 0.7 and > 10% change
- **MODERATE**: R-squared > 0.5
- **WEAK**: R-squared > 0.3
### Threshold Exceedance
Checks how many data points exceed design limits.
- **STRONG**: > 20% of data exceeding limits
- **MODERATE**: > 5%
- **WEAK**: 0-5%
### Rate-of-Change Detection
Identifies sudden step changes using 3-sigma detection on first differences.
Step changes produce STRONG evidence.
### Correlation Analysis
Pearson correlation between all parameter pairs. Reports correlations with |r| > 0.7.
- **STRONG**: |r| > 0.9
- **MODERATE**: 0.7 < |r| < 0.9
### Correlation when a controller closes the loop
The most common way a historian correlation misleads is that **a control loop
already connects the two signals being correlated**. The loop guarantees a
correlation with exactly the sign the wrong hypothesis predicts, so the raw
number looks like confirmation of a mechanism that is not there.
**1. Ask what the controller does before reading the correlation.**
Write the loop down first: *does the plant move B in response to A?* If it does,
a correlation between A and B is expected under the null hypothesis and proves
nothing. Do not run the correlation at all until this question is answered.
**2. Difference both series.**
First differences remove a shared trend — seasonal, production ramp, or
instrument drift — which is the other manufacturer of spurious correlation.
What survives is genuine short-term coupling.
**3. Use the sign of the lag to resolve direction.**
This is the part that discriminates. Scan lags in **both** directions and ask
which signal leads:
| Pattern | Interpretation |
|---------|----------------|
| Driver leads by the physical transport time | The causal mechanism under test |
| Response leads the driver | The controller acting — the loop read backwards |
| Peak at zero lag only | Shared common cause, or sampling too coarse to separate |
| Flat across all lags | No coupling at the resolution available |
State the expected lag **before** looking. A transport short-circuit has a
transit time of minutes; a controller responds in seconds; a seasonal common
cause has no lag structure at all. If the observed peak does not sit where the
hypothesis requires, the hypothesis fails regardless of the correlation
magnitude.
**Worked example.** An investigation suspected that a warm discharge stream was
short-circuiting back to a cold intake, and correlated intake temperature
against discharge flow:
```
raw correlation, intake temperature vs discharge flow: r = +0.37
```
Positive, moderate, right sign — it reads as confirmation. It is not. A
temperature controller opened the discharge valve when the intake ran warm, so
warm intake *caused* high flow rather than the reverse, and both series carried
the same seasonal ramp. Two mechanisms manufactured the correlation, neither of
them the hypothesis under test:
```
after differencing both series, every lag from -3 h to +3 h: |r| < 0.10
peak: r = +0.04
```
The hypothesis fails. The raw figure is dangerous precisely because it is
plausible rather than absurd.
A saturated controller is the mirror image of this failure: the loop that
*acts* fools the correlation, while a loop that has *stopped acting* lets
unchanged disturbances through and makes the process look like it has a new
external problem. Screen for it with `neqsim-control-authority-screening`
before accepting an external cause for "the controlled variable got worse".
### STID Cross-Reference
Compares current (latest) values to STID design values.
- **STRONG**: > 20% deviation from design
- **MODERATE**: 10-20% deviation
### Weather as Evidence (ambient/met-ocean conditions)
Some trips and performance shifts correlate with **weather at the equipment's
location**, not with anything in the historian: an air-cooler/fin-fan or a
gas-turbine driver losing margin on a hot day, a flare/dispersion or
fire-water-monitor case degraded by wind, a marine/topside motion event during
a storm, or a cold snap driving a hydrate/freeze-related valve or instrument
failure. When the hypothesis list includes an ambient-condition driver, pull
the historical weather for the equipment's location and the trip/event window
with the community `neqsim-weather-data` skill (`WeatherDataService.get_historical`
or the `installation-weather-agent`) and compare it against the equipment's
design/rated ambient envelope (ISO ambient for a gas turbine, design ambient
for an air cooler, etc.):
- **STRONG**: the ambient condition at the event time is outside the
equipment's design/rated envelope (e.g. ambient above ISO plus derate margin).
- **MODERATE**: inside the envelope but at a seasonal or historical extreme
for the site (compare against `design_site_conditions`'s percentiles).
- Treat this like the control-loop correlation trap above: a weather match is
circumstantial evidence, not proof. Score it, do not promote it to a
confirmed cause without a NeqSim verification run (e.g. re-flash the gas
turbine or air cooler at the observed ambient condition and check the
predicted margin against what was actually seen).
- There is no built-in ambient-temperature hypothesis in the compressor/pump/
separator/heat-exchanger/valve libraries above; add one as a custom
hypothesis (see Extending the Framework) when weather is a candidate driver.
### Incident and Recurrence History (prior layer, not likelihood)
Records of earlier incidents on the same equipment, its sister equipment or the same
unit (company incident database, maintenance failure history, earlier RCA reports) say
how often a failure mode has occurred here. That is a **reliability prior**, so it
enters the prior stage of `Prior × Likelihood × Verification`, never the likelihood
(the current event's own signals) and never as a confirmed cause.
1. Retrieve the history for the equipment tag, then the unit, over a stated window.
Use the installed incident-history evidence agent when the plugin provides one;
otherwise ask the user for the case list. Record `no_access` or `not_applicable`
in `agent_workflow_plan.evidence_sources` rather than silently skipping.
2. Map each record to a hypothesis by failure mode (seal leak, bearing, fouling,
valve sticking, instrument fault, utility loss ...). Count only records that match
the failure mode, not every record on the unit. Keep the case numbers.
3. Pass the counts to the analyzer before `analyze()`:
```java
rca.addIncidentHistoryPrior("Seal degradation", 3, "cases 1001, 1007, 1012");
rca.addIncidentHistoryPrior("Bearing", 1, "case 1020");
```
Each matching hypothesis prior is multiplied by `1 + 0.15 × count`, capped at 1.75,
and a weak supporting evidence item with the case references is attached for the
audit trail. The likelihood score is not touched. Confidence is renormalised, so the
effect is relative. Unmatched names are logged, not applied.
4. Rules that keep it honest:
- Recurrence is a screening signal, not a demonstrated common cause. Same tag, same
failure mode and a comparable operating regime strengthen it; same unit alone does not.
- No recorded incidents never lowers a prior (under-reporting is common).
- Closed cases with a recorded cause are stronger than open ones; quote the recorded
cause as a hypothesis to test against the historian, not as the answer.
- A hypothesis raised only by history and contradicted by the historian stays low:
the likelihood term dominates once there is measured evidence.
- Report the history window, record count, matching count and the data gap
(system coverage, reporting culture) in `assumptions` / `data_gaps`.
- Remove personal data from case text before it enters a task folder.
## Simulation Verification
The verifier clones the process system, applies a perturbation matching each
hypothesis, runs the modified process, and compares both direction and normalized
response magnitude to the historian data pattern where a defensible KPI match exists.
Every likelihood and verification stage has an explicit `EvaluationStatus`:
`UNKNOWN`, `EVALUATED`, `UNSUPPORTED`, or `FAILED`. Unknown, unsupported, and failed
stages are neutral in confidence multiplication. An `EVALUATED` zero correctly drives
the raw hypothesis score to zero. Reports also expose verification coverage and
comparison count; do not present low-coverage verification as conclusive.
### Perturbation Examples
| Hypothesis | Perturbation Applied |
|-----------|---------------------|
| Seal degradation | Reduce polytropic efficiency by 20% |
| Fouled impeller | Reduce polytropic efficiency by 15% |
| Surge condition | Reduce polytropic efficiency by 30% |
| Heat exchanger fouling | Increase outlet temperature by 10°C |
| Loss of cooling | Set outlet = inlet temperature |
### Verification Score
The score (0-1) combines direction and the ratio of normalized simulated/observed
response magnitudes, then qualifies it by comparable-KPI coverage. Opposite directions
score zero. No defensible comparison yields `UNKNOWN`, not an invented midpoint score.
For operational decisions, attach a `DiagnosisCase` with the historian query/reference,
process-model ID and revision, operating mode, and known quality qualifications. It is
View on GitHub