Skip to the content.

When a process run fails, the failure surfaces as a RuntimeException. For interactive use that is fine, but agents, optimizers, and batch drivers usually want a structured answer to the question “did the last run succeed, and if not, which unit failed and why?” without having to catch and parse the exception text.

ProcessSystem and ProcessModel expose a structured RunStatus that records a per-unit outcome for each run.

The Java fragments below assume a class-level Log4j2 logger, for example: private static final Logger logger = LogManager.getLogger(YourClass.class);.

Quick Start

import neqsim.process.processmodel.ProcessSystem;
import neqsim.process.processmodel.RunStatus;

ProcessSystem process = buildProcess();
try {
  process.run();
} catch (RuntimeException ex) {
  // run() still throws on failure — that behavior is unchanged.
}

RunStatus status = process.getRunStatus();
if (status.isSuccess()) {
  logger.info("All {} units ran.", status.getUnits().size());
} else {
  logger.info("Run failed at {}: {}", status.getFailedUnitName(),
      status.getFailedUnitError());
}

The status is always populated after a run — successful or not — so you can inspect it whether or not you wrapped run() in a try/catch.

RunStatus API

Method Description
isCompleted() True once a run has finished (successfully or not)
isSuccess() True if the run completed with no unit failure
getFailedUnitName() Name of the first unit that failed, or null
getFailedUnitError() Error message of the first failed unit, or null
getUnits() Unmodifiable list of per-unit UnitRunStatus entries
toJson() / toJsonObject() Schema-versioned JSON representation

UnitRunStatus

Each entry in getUnits() is a UnitRunStatus describing one unit operation:

Method Description
getUnitName() Unit operation name
getUnitType() Unit operation type (simple class name)
isSuccess() Whether this unit ran successfully
getErrorMessage() Error message if this unit failed, else null
getAreaName() Owning process area name (set by ProcessModel), else null

ProcessModel Aggregation

For a multi-area ProcessModel, getRunStatus() aggregates the per-unit status across all areas, tagging each UnitRunStatus with its area name via getAreaName(). This lets an agent pinpoint not just the failing unit but the area it lives in.

import neqsim.process.processmodel.ProcessModel;
import neqsim.process.processmodel.RunStatus;
import neqsim.process.processmodel.UnitRunStatus;

ProcessModel plant = new ProcessModel();
plant.add("Separation", separationProcess);
plant.add("Compression", compressionProcess);
plant.run();

RunStatus status = plant.getRunStatus();
for (UnitRunStatus u : status.getUnits()) {
  logger.info("{} / {} -> {}", u.getAreaName(), u.getUnitName(),
      u.isSuccess() ? "ok" : "FAILED: " + u.getErrorMessage());
}

JSON Form

getRunStatusJson() (on both ProcessSystem and ProcessModel) returns a schema-versioned payload suitable for logging or handing to an external agent:

String json = process.getRunStatusJson();
{
  "schemaVersion": "1.0",
  "completed": true,
  "success": false,
  "failedUnitName": "Compressor",
  "failedUnitError": "fluid is not converged",
  "unitCount": 3,
  "units": [
    { "unitName": "feed", "unitType": "Stream", "success": true, "errorMessage": null },
    { "unitName": "HP Sep", "unitType": "Separator", "success": true, "errorMessage": null },
    { "unitName": "Compressor", "unitType": "Compressor", "success": false,
      "errorMessage": "fluid is not converged" }
  ]
}

For a ProcessModel, each unit entry additionally carries an areaName field.

Solver Watchdog: Time Budget, Stall Detection and Cooperative Stop

A non-converging recycle flowsheet or a very slow unit can occupy a thread for minutes. ProcessSystem and ProcessModel therefore offer an opt-in watchdog. All features are off by default, so existing models run exactly as before.

import neqsim.process.equipment.stream.Stream;
import neqsim.process.processmodel.ProcessSystem;
import neqsim.process.processmodel.RunDiagnostics;
import neqsim.process.processmodel.RunStatus;
import neqsim.thermo.system.SystemSrkEos;

SystemSrkEos fluid = new SystemSrkEos(303.15, 10.0);
fluid.addComponent("methane", 1.0);
fluid.setMixingRule("classic");
Stream feed = new Stream("doc feed", fluid);
feed.setFlowRate(1000.0, "kg/hr");
ProcessSystem process = new ProcessSystem("doc");
process.add(feed);
process.setMaxRunTimeSeconds(30.0);   // wall-clock budget of one run() / runUntilConverged()
process.setStallDetection(8, 1.0e-3); // 8 iterations without 0.1 % residual improvement
process.run();                        // returns normally when the watchdog trips

RunStatus status = process.getRunStatus();
if (status.isTimedOut() || status.isStalled() || status.isStopped()) {
  logger.info("{} at unit {}: {}", status.getTerminationReason(), status.getBudgetUnitName(),
      status.getBudgetReason());
}
RunDiagnostics diag = process.getLastRunDiagnostics();
logger.info("elapsed {} s, outcome {}", diag.getElapsedSeconds(), diag.getOutcome());
Method Description
setMaxRunTimeSeconds(double) Wall-clock limit of one run; zero or negative disables it
setStallDetection(int window, double minRelImprovement) Stops with STALLED when the worst tolerance-normalised recycle or adjuster residual (outer boundary residual for ProcessModel) does not improve for window consecutive iterations
requestStop() Thread-safe cooperative cancellation from another thread; the run reports STOPPED
getRunStatus() getTerminationReason() is TIMED_OUT, STALLED or STOPPED; getBudgetUnitName() and getBudgetReason() give the unit that was running and why
getLastRunDiagnostics() RunDiagnostics with elapsed time, outcome, unit, pass count and residual history (toJson())

Notes:

Notes