Skip to the content.

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 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

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
}