Web API Integration: JSON Process Builder and Session Management
NeqSim provides a complete set of classes for building process simulations declaratively from JSON, receiving structured error/success responses, and managing isolated multi-user sessions. These features are designed for web services and Python-based applications that call NeqSim remotely.
Architecture Overview
| Class | Purpose |
|---|---|
JsonProcessBuilder |
Parses JSON into a ProcessSystem with fluids, equipment, and stream wiring |
SimulationResult |
Structured success/error envelope with error codes, remediation hints, and report JSON |
ProcessSimulationSession |
Multi-user session manager with template-based copy-on-create and automatic expiry |
ProcessSystem.fromJson() |
Convenience entry points on ProcessSystem for JSON build and run |
ProcessSystem.resolveStreamReference() |
Resolves dot-notation stream references (e.g., "HP Sep.gasOut") |
ProcessSystem.wireStream() |
Connects equipment by name using the wiring API |
ProcessDiagramExporter |
Exports built processes to Graphviz DOT, SVG, PNG, or PDF visuals |
DexpiXmlWriter |
Exports built processes to DEXPI XML with P&ID layout data |
All classes are in the neqsim.process.processmodel package.
For the full schema reference, supported port aliases, advanced equipmentDesign metadata, and
ProcessModel area JSON, see
JSON Format for ProcessSystem and ProcessModel.
1. JSON Process Builder
JSON Format
A JSON process definition has three top-level sections:
{
"fluid": { ... },
"fluids": { ... },
"process": [ ... ],
"designCapacities": { ... },
"equipmentDesign": { ... },
"dataConnections": { ... },
"autoRun": false
}
Sections:
fluid(optional) — A single default fluid definition used by all streams unless overridden.fluids(optional) — A named map of fluid definitions for multi-fluid processes.process(required) — An ordered array of equipment definitions. Units are created and wired in order.designCapacities(optional) — Normalized capacity data applied to supported equipment.equipmentDesign(optional) — Advanced grouped sizing metadata; deterministic fields are applied and all fields are preserved in result metadata.dataConnections(optional) — Historian/API/tag mappings preserved in result metadata for downstream automation.autoRun(optional, defaultfalse) — Iftrue, the process is run immediately after building.
Fluid Definition
{
"fluid": {
"model": "SRK",
"temperature": 298.15,
"pressure": 50.0,
"mixingRule": "classic",
"multiPhaseCheck": true,
"components": {
"methane": 0.85,
"ethane": 0.10,
"propane": 0.05
}
}
}
Parameters:
| Field | Type | Default | Description |
|---|---|---|---|
model |
string | "SRK" |
Thermodynamic model: SRK, PR, CPA, GERG2008, PCSAFT, UMRPRU |
temperature |
number | 288.15 |
Temperature in Kelvin |
pressure |
number | 1.01325 |
Pressure in bara |
mixingRule |
string | "classic" |
Mixing rule name |
multiPhaseCheck |
boolean | false |
Enable multi-phase detection |
components |
object | — | Map of component name to mole fraction |
Multiple Named Fluids
Use the fluids map when different streams need different compositions:
{
"fluids": {
"rich_gas": {
"model": "SRK",
"temperature": 298.15,
"pressure": 50.0,
"components": { "methane": 0.85, "ethane": 0.10, "propane": 0.05 }
},
"lean_gas": {
"model": "SRK",
"temperature": 298.15,
"pressure": 50.0,
"components": { "methane": 0.95, "ethane": 0.05 }
}
},
"process": [
{ "type": "Stream", "name": "rich feed", "fluidRef": "rich_gas" },
{ "type": "Stream", "name": "lean feed", "fluidRef": "lean_gas" }
]
}
Equipment Definitions
Each entry in the process array defines one equipment unit:
{
"type": "Separator",
"name": "HP Sep",
"inlet": "feed",
"properties": {
"internalDiameter": 2.0
}
}
Fields:
| Field | Type | Required | Description |
|---|---|---|---|
type |
string | Yes | Equipment type name (matches EquipmentFactory types) |
name |
string | No | Unique name (auto-generated if omitted) |
tagName |
string | No | Process or instrument tag name preserved by JSON export/import |
inlet |
string | No | Stream reference for inlet (see Stream Wiring below) |
fluidRef |
string | No | Named fluid reference for Stream-type units |
properties |
object | No | Property setters applied via reflection |
Supported Equipment Types
Any type registered in EquipmentFactory is supported, including:
Stream, Separator, ThreePhaseSeparator, Compressor, Expander, Pump, Heater, Cooler, HeatExchanger, Mixer, Splitter, ThrottlingValve (Valve), Recycle, Adjuster, Tank, Flare, Ejector, and more.
Property Setting
Properties are applied via reflection using setter methods. Two formats are supported:
Simple value — calls setPropertyName(double), setPropertyName(String), or setPropertyName(boolean):
"properties": {
"outletPressure": 80.0,
"isentropicEfficiency": 0.75,
"usePolytropicCalc": true
}
Value with unit — calls setPropertyName(double, String):
"properties": {
"flowRate": [50000.0, "kg/hr"],
"outletPressure": [80.0, "bara"]
}
Compressor Driver Limits
Compressor properties may include a nested driver object. It is imported into
CompressorDriver and exported back from ProcessSystem.toJson(), so web clients can preserve
driver capacity constraints and speed-dependent maximum power curves.
"properties": {
"outletPressure": [120.0, "bara"],
"speed": 6000.0,
"driver": {
"type": "GAS_TURBINE",
"ratedPower": 40500.0,
"ratedSpeed": 7383.0,
"maxPowerSpeedCurve": {
"speeds": [4922.0, 6000.0, 7383.0],
"powers": [21.8, 32.0, 44.4],
"powerUnit": "MW"
}
}
}
For polynomial curves, use maxPowerCurveCoefficients with three coefficients [a, b, c].
Power is stored internally in kW; tabular input accepts kW, MW, or W.
Visualization and DEXPI Export
The JSON builder creates the simulation model; visualization is done from the resulting
ProcessSystem. The same JSON can be rendered as Graphviz/PFD output or exported as DEXPI XML for
P&ID-oriented tools.
SimulationResult result = ProcessSystem.fromJsonAndRun(jsonString);
if (!result.isSuccess()) {
throw new IllegalStateException(result.toJson());
}
ProcessSystem process = result.getProcessSystem();
// Lightweight topology/PFD visualization as Graphviz DOT text.
String dot = process.toDOT();
// DEXPI XML suitable for DEXPI/P&ID viewers or downstream exchange.
ByteArrayOutputStream dexpiXml = new ByteArrayOutputStream();
DexpiXmlWriter.write(process, dexpiXml);
String dexpi = new String(dexpiXml.toByteArray(), StandardCharsets.UTF_8);
For file outputs, use process.createDiagramExporter().exportDOT(...), exportSVG(...),
exportPNG(...), or exportPDF(...). SVG, PNG, and PDF export require Graphviz dot on the
server path. DEXPI export uses NeqSim’s built-in layout engine and does not require Graphviz.
For a multi-area ProcessModel, you can plot either a single common DOT graph or one DOT file per
contained ProcessSystem area. The common DOT uses Graphviz clusters for the areas and draws
cross-area edges when the areas share live stream objects. JSON round-trips preserve those links
through interAreaLinks, so a rebuilt ProcessModel can still show the process-system boundary
connections in the common DOT.
ProcessModel plant = ProcessModel.fromJsonAndRun(modelJsonString);
// One common plant-wide DOT graph.
String commonDot = plant.toDOT();
plant.exportToGraphviz("plant.dot");
// One detailed DOT file per area: separation.dot, compression.dot, etc.
Map<String, Path> areaDotFiles = plant.exportAreaDOT(Paths.get("plant-diagrams"));
See Process Flow Diagram Export for diagram styling options and DEXPI P&ID Import, Export and Visualization for DEXPI import/export details.
2. Stream Wiring API
Dot-Notation References
The inlet field on each equipment definition uses dot-notation to specify which outlet port of an upstream unit to connect:
| Reference | Resolves To |
|---|---|
"feed" |
The stream named "feed" (direct stream reference), or the default outlet of unit "feed" |
"HP Sep.gasOut" |
HPSep.getGasOutStream() |
"HP Sep.liquidOut" |
HPSep.getLiquidOutStream() |
"HP Sep.oilOut" |
HPSep.getOilOutStream() |
"HP Sep.waterOut" |
HPSep.getWaterOutStream() |
"Comp.outlet" |
Comp.getOutletStream() |
Port aliases are case-insensitive: gasOut, gas, liquidOut, liquid, oilOut, oil, waterOut, water, outlet.
Additional aliases accepted by the builder include out, outStream, gasOutStream,
liquidOutStream, split0, split1, splitStream_0, splitStream_1, outlet0, outlet1,
hx0, and hx1.
Programmatic Wiring on ProcessSystem
After building a ProcessSystem, you can also wire streams programmatically:
// Resolve a stream reference
StreamInterface gasStream = process.resolveStreamReference("HP Sep.gasOut");
// Wire a stream to a target unit
boolean success = process.wireStream("Compressor-1", "HP Sep.gasOut");
From Python via JPype:
from neqsim import jneqsim
process = jneqsim.process.processmodel.ProcessSystem.fromJson(json_string).getProcessSystem()
gas = process.resolveStreamReference("HP Sep.gasOut")
process.wireStream("Compressor-1", "HP Sep.gasOut")
3. Structured Error Responses
All build and run operations return a SimulationResult with a well-defined JSON structure.
Success Response
{
"status": "success",
"processSystemName": "json-process",
"report": { ... },
"metadata": {
"equipmentDesign": { ... },
"dataConnections": { ... },
"designDataApplied": { ... }
},
"warnings": [
"Property outletPressure not found on Stream (tried setOutletPressure)"
]
}
metadata is present only when the request included advisory sections such as equipmentDesign,
dataConnections, designCapacities, or caller-supplied metadata.
Error Response
{
"status": "error",
"errors": [
{
"code": "STREAM_NOT_FOUND",
"message": "Inlet reference 'HP Sep' not found for unit 'Compressor-1'",
"unit": "Compressor-1",
"remediation": "Ensure the referenced unit exists and was defined before this unit"
}
],
"warnings": []
}
Error Codes
| Code | Meaning |
|---|---|
JSON_PARSE_ERROR |
Input is null, empty, or malformed JSON |
MISSING_PROCESS |
JSON has no process array |
MISSING_TYPE |
A unit definition has no type field |
UNKNOWN_MODEL |
Unrecognized thermodynamic model name |
FLUID_ERROR |
Error creating a fluid (bad component, invalid parameters) |
NO_FLUID |
Stream has no fluid defined and no default fluid exists |
FLUID_NOT_FOUND |
fluidRef refers to a fluid name that does not exist |
STREAM_NOT_FOUND |
inlet refers to an unknown unit or port |
UNIT_ERROR |
Equipment creation or wiring failed |
SIMULATION_ERROR |
Process ran but threw an exception |
VALIDATION_* |
Validation issues from validateSetup() |
SESSION_NOT_FOUND |
Session ID not found or expired |
MAX_SESSIONS |
Maximum concurrent session limit reached |
Java API
// Build only
SimulationResult result = ProcessSystem.fromJson(jsonString);
if (result.isSuccess()) {
ProcessSystem process = result.getProcessSystem();
com.google.gson.JsonObject buildMetadata = result.getMetadata();
}
// Build and run
SimulationResult result = ProcessSystem.fromJsonAndRun(jsonString);
String reportJson = result.getReportJson();
// Validate and report
ProcessSystem process = ...;
SimulationResult validation = process.validateAndReport();
// Run and report
SimulationResult runResult = process.runAndReport();
String fullJson = runResult.toJson(); // Ready for HTTP response
4. Session Management
ProcessSimulationSession provides multi-user isolation for web services where each request or user gets their own ProcessSystem instance.
Core Concepts
- Templates — Pre-built
ProcessSystemobjects registered by name. Creating a session from a template deep-copies it so each session is independent. - Sessions — Isolated
ProcessSysteminstances identified by UUID. Each session has its own state and can be modified and run independently. - Expiry — Sessions are automatically cleaned up after a configurable timeout (default 30 minutes).
- Concurrency — Thread-safe with
ConcurrentHashMapbacking and configurable maximum session count (default 100).
Java Usage
// Create session manager
ProcessSimulationSession manager = new ProcessSimulationSession();
// Register a template
ProcessSystem template = buildGasProcessingProcess();
template.run();
manager.registerTemplate("gas_processing", template);
// Create a user session from the template
String sessionId = manager.createSession("gas_processing");
// Get the session and modify it
ProcessSystem userProcess = manager.getSession(sessionId);
// ... modify parameters ...
// Run and get results
SimulationResult result = manager.runSession(sessionId);
// Clean up
manager.destroySession(sessionId);
// Shut down (destroys all sessions, stops cleanup thread)
manager.shutdown();
JSON-Based Session Creation
// Create a session directly from JSON
SimulationResult createResult = manager.createSessionFromJson(jsonDefinition);
if (createResult.isSuccess()) {
// Session ID is in the first warning entry
String sessionInfo = createResult.getWarnings().get(0); // "sessionId:uuid-value"
String sessionId = sessionInfo.substring("sessionId:".length());
// Run the session
SimulationResult runResult = manager.runSession(sessionId);
}
Custom Configuration
// Custom timeout (60 min) and max sessions (200)
ProcessSimulationSession manager = new ProcessSimulationSession(60, 200);
// Modify at runtime
manager.setMaxSessions(500);
// Manual cleanup
int removed = manager.cleanupExpiredSessions();
// Session info
int active = manager.getActiveSessionCount();
Map<String, String> info = manager.getSessionInfo();
5. Python Integration Examples
Basic: Build and Run from JSON
from neqsim import jneqsim
ProcessSystem = jneqsim.process.processmodel.ProcessSystem
json_def = '''{
"fluid": {
"model": "SRK",
"temperature": 298.15,
"pressure": 50.0,
"components": {
"methane": 0.85,
"ethane": 0.10,
"propane": 0.05
}
},
"process": [
{
"type": "Stream",
"name": "feed",
"properties": { "flowRate": [50000.0, "kg/hr"] }
},
{
"type": "Separator",
"name": "HP Sep",
"inlet": "feed"
},
{
"type": "Compressor",
"name": "Gas Compressor",
"inlet": "HP Sep.gasOut",
"properties": { "outletPressure": 80.0 }
}
],
"autoRun": true
}'''
result = ProcessSystem.fromJsonAndRun(json_def)
if result.isSuccess():
print("Simulation succeeded")
print(result.getReportJson())
else:
for err in result.getErrors():
print(f"[{err.getCode()}] {err.getMessage()}")
print(f" Fix: {err.getRemediation()}")
Session Management from Python
from neqsim import jneqsim
SessionManager = jneqsim.process.processmodel.ProcessSimulationSession
manager = SessionManager()
# Create session from JSON
create_result = manager.createSessionFromJson(json_def)
if create_result.isSuccess():
session_info = str(create_result.getWarnings().get(0))
session_id = session_info.split(":", 1)[1]
# Modify parameters
process = manager.getSession(session_id)
feed = process.getUnit("feed")
feed.setFlowRate(60000.0, "kg/hr")
# Re-run
run_result = manager.runSession(session_id)
print(run_result.toJson())
# Clean up
manager.destroySession(session_id)
manager.shutdown()
Validate Before Running
result = ProcessSystem.fromJson(json_def)
if result.isSuccess():
process = result.getProcessSystem()
validation = process.validateAndReport()
if validation.isSuccess():
run_result = process.runAndReport()
print(run_result.toJson())
else:
print("Validation failed:")
print(validation.toJson())
6. Complete Example: Gas Processing Plant
{
"fluid": {
"model": "SRK",
"temperature": 323.15,
"pressure": 65.0,
"mixingRule": "classic",
"components": {
"nitrogen": 0.01,
"CO2": 0.02,
"methane": 0.80,
"ethane": 0.08,
"propane": 0.05,
"n-butane": 0.02,
"n-pentane": 0.01,
"n-hexane": 0.005,
"n-heptane": 0.005
}
},
"process": [
{
"type": "Stream",
"name": "well stream",
"properties": {
"flowRate": [75000.0, "kg/hr"]
}
},
{
"type": "Cooler",
"name": "inlet cooler",
"inlet": "well stream",
"properties": {
"outTemperature": [303.15]
}
},
{
"type": "ThreePhaseSeparator",
"name": "inlet separator",
"inlet": "inlet cooler.outlet"
},
{
"type": "Compressor",
"name": "1st stage compressor",
"inlet": "inlet separator.gasOut",
"properties": {
"outletPressure": 100.0,
"isentropicEfficiency": 0.78
}
},
{
"type": "Cooler",
"name": "aftercooler",
"inlet": "1st stage compressor.outlet",
"properties": {
"outTemperature": [308.15]
}
},
{
"type": "Separator",
"name": "scrubber",
"inlet": "aftercooler.outlet"
}
],
"autoRun": true
}
Related Documentation
- AI Validation Framework — Structured validation with remediation hints
- External Optimizer Integration — Python/SciPy optimization with NeqSim
- Python Extension Patterns — JPype integration patterns
- Process Simulation — Advanced simulation techniques