Best for
- Startup and shutdown sequences
- Controller tuning and loop analysis
- Pressure relief / blowdown scenarios
equinor/neqsim/.github/skills/neqsim-dynamic-simulation/SKILL.md
Dynamic simulation guidance for NeqSim. USE WHEN: running transient simulations, modeling startup/shutdown, tuning PID controllers, analyzing pressure/level dynamics, performing blowdown/depressurization, or setting up measurement devices and control loops. Covers runTransient, DynamicProcessHelper, controller tuning, and dynamic equipment configuration.
Decision brief
Guide for transient/dynamic process simulation in NeqSim.
Compatibility matrix
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/equinor/neqsim --skill ".github/skills/neqsim-dynamic-simulation"Inspect the Agent Skill "neqsim-dynamic-simulation" from https://github.com/equinor/neqsim/blob/9e8d44a141bba600026d2229969b49af50f34237/.github/skills/neqsim-dynamic-simulation/SKILL.md at commit 9e8d44a141bba600026d2229969b49af50f34237. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
Review the “Basic Dynamic Setup” section in the pinned source before continuing.
When TwoFluidPipe.setIncludeMassTransfer(true) is enabled, flash-driven transfer is phase resolved. Condensation must use equilibrium hydrocarbon-liquid and aqueous-liquid mass contributions; never use the current cell water cut to identify a phase that is not yet present. For e…
Review the “Build process (same pattern as Java)” section in the pinned source before continuing.
For valve-action studies that start from P&ID symbols and plant data, also load neqsim-pid-process-operations to define the process graph, valve semantics, historian tag mapping, and event schedule before running runTransient.
NeqSim dynamic simulation advances a ProcessSystem with runTransient() (using the configured timestep) or runTransient(double dt, UUID id) (using an explicit timestep and calculation identifier). Pass a finite, positive timestep. Process and model entry points reject zero, negat…
Permission review
No configured static risk pattern was detected
This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.
Evidence record
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 95/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 136 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Guide for transient/dynamic process simulation in NeqSim.
For valve-action studies that start from P&ID symbols and plant data, also load
neqsim-pid-process-operations to define the process graph, valve semantics,
historian tag mapping, and event schedule before running runTransient.
NeqSim dynamic simulation advances a ProcessSystem with runTransient()
(using the configured timestep) or runTransient(double dt, UUID id) (using an
explicit timestep and calculation identifier).
Pass a finite, positive timestep. Process and model entry points reject zero,
negative, NaN, and infinite values before any area or equipment state changes.
Adaptive stepping rejects invalid requests rather than clamping them.
Each timestep:
ControllerDeviceBaseClass also makes repeated calls with one identifier
idempotent, including semi-implicit equipment passes. Custom controllers
integrated by equipment must implement the same identifier contract through
hasRunTransient(UUID).import neqsim.process.processmodel.ProcessSystem;
import neqsim.process.equipment.stream.Stream;
import neqsim.process.equipment.separator.Separator;
import neqsim.process.equipment.valve.ThrottlingValve;
import neqsim.process.controllerdevice.ControllerDeviceInterface;
import neqsim.process.controllerdevice.ControllerDeviceBaseClass;
import neqsim.process.measurementdevice.LevelTransmitter;
import neqsim.process.measurementdevice.PressureTransmitter;
// Build steady-state process first
SystemInterface fluid = new SystemSrkEos(273.15 + 25.0, 50.0);
fluid.addComponent("methane", 0.80);
fluid.addComponent("ethane", 0.10);
fluid.addComponent("propane", 0.05);
fluid.addComponent("n-pentane", 0.05);
fluid.setMixingRule("classic");
Stream feed = new Stream("feed", fluid);
feed.setFlowRate(10000.0, "kg/hr");
Separator sep = new Separator("HP Sep", feed);
sep.setInternalDiameter(2.0); // m — for dynamic simulation, set directly for level dynamics
sep.setSeparatorLength(6.0); // m — for design purposes, use SeparatorMechanicalDesign instead
ThrottlingValve gasValve = new ThrottlingValve("gas valve", sep.getGasOutStream());
gasValve.setOutletPressure(20.0, "bara");
ThrottlingValve liqValve = new ThrottlingValve("liq valve", sep.getLiquidOutStream());
liqValve.setOutletPressure(10.0, "bara");
ProcessSystem process = new ProcessSystem();
process.add(feed);
process.add(sep);
process.add(gasValve);
process.add(liqValve);
// Run steady state first
process.run();
// Pressure transmitter
PressureTransmitter PT100 = new PressureTransmitter("PT-100", sep);
PT100.setUnit("bara");
PT100.setMaximumValue(100.0);
PT100.setMinimumValue(0.0);
process.add(PT100);
// Level transmitter
LevelTransmitter LT100 = new LevelTransmitter("LT-100", sep);
LT100.setUnit("m");
process.add(LT100);
// Temperature transmitter
TemperatureTransmitter TT100 = new TemperatureTransmitter("TT-100", sep);
TT100.setUnit("C");
process.add(TT100);
// Flow transmitter
VolumeFlowTransmitter FT100 = new VolumeFlowTransmitter("FT-100", feed);
FT100.setUnit("kg/hr");
process.add(FT100);
// Level controller on liquid OUTLET valve — DIRECT acting (reverseActing = false):
// level up (error > 0) -> output up -> outlet valve opens -> level falls. Setting
// reverseActing(true) here inverts the sign and makes the loop unstable (runaway).
ControllerDeviceInterface LC100 = new ControllerDeviceBaseClass();
LC100.setControllerSetPoint(1.0); // Target level = 1.0 m
LC100.setTransmitter(LT100); // Controlled variable
LC100.setReverseActing(false); // liquid-outlet level valve is direct acting
LC100.setControllerParameters(0.5, 100.0, 10.0); // Kp, Ti (s), Td (s)
// Attach controller to valve
liqValve.addController("LC-100", LC100);
// Pressure controller on gas valve
ControllerDeviceInterface PC100 = new ControllerDeviceBaseClass();
PC100.setControllerSetPoint(50.0); // Target pressure = 50 bara
PC100.setTransmitter(PT100);
PC100.setReverseActing(false); // Pressure up -> valve opens more
PC100.setControllerParameters(1.0, 50.0, 0.0);
gasValve.addController("PC-100", PC100);
| Loop Type | Typical Kp | Typical Ti (s) | Typical Td (s) |
|---|---|---|---|
| Level (averaging) | 0.5-2.0 | 60-300 | 0 |
| Level (tight) | 2.0-5.0 | 30-60 | 0-10 |
| Pressure (gas) | 0.5-2.0 | 20-100 | 0-5 |
| Flow | 0.3-1.0 | 5-30 | 0 |
| Temperature | 0.5-2.0 | 60-600 | 10-60 |
ControllerDeviceBaseClass supports a native SP-PV deadband via
setDeadBand(double) / getDeadBand(). While the absolute control error stays
inside the band the controller output is frozen (holds the last valve
position) and the integral term does not accumulate; default 0 disables it.
The deadband is in the controller error unit (percent in the default percent
mode, else the configured engineering unit). This is the standard DCS averaging-
level deadband used to stop valve cycling.
levelController.setDeadBand(0.5); // hold the valve while |PV - SP| <= 0.5 %
Beware the deadband limit cycle. On an integrating (level) process a
deadband delays correction until the level reaches the band edge; the delayed
correction then overshoots and the cycle repeats, giving a square-wave valve
trace. Removing (or shrinking) the deadband is the usual fix. If the installed
pip neqsim predates setDeadBand, emulate it by toggling controller mode
each step: setMode(ControllerMode.MANUAL) while |PV%-SP%| <= deadband (holds
output) and setMode(ControllerMode.AUTO) otherwise (bumpless resume) - this is
numerically identical to the native deadband.
A dynamic separator level loop only responds if the vessel is switched out of steady-state mode and the liquid outlet valve is direct acting. The exact, easy-to-get-wrong sequence is:
// 1. Build and solve the steady state first (sets inventory, flows, holdup).
process.run();
// 2. Switch the vessel (and its outlet valve) to dynamic mode. If this is left
// on, the separator recomputes steady state every step and the level is
// pinned at its default (0.5 fraction) no matter what the controller does.
sep.setCalculateSteadyState(false);
liqValve.setCalculateSteadyState(false);
// 3. Set the physical geometry and the starting liquid level (as a 0..1 fraction
// of the vessel). Do this AFTER run() so it is not overwritten by steady state.
sep.setInternalDiameter(2.0); // m — drives holdup volume / level dynamics
sep.setSeparatorLength(6.0); // m
sep.setLiquidLevel(0.30); // start at 30 %
// 4. Direct-acting level controller on the liquid OUTLET valve (see above).
LevelTransmitter LT100 = new LevelTransmitter("LT-100", sep);
LT100.setUnit("m");
ControllerDeviceInterface LC100 = new ControllerDeviceBaseClass();
LC100.setTransmitter(LT100);
LC100.setControllerSetPoint(0.30 * sep.getInternalDiameter()); // SP in the LT unit
LC100.setReverseActing(false); // liquid-outlet level valve = direct acting
LC100.setControllerParameters(1.0, 300.0, 0.0); // averaging level: loose Kp, long Ti, no Td
liqValve.addController("LC-100", LC100);
// 5. Advance the transient with a fixed time step.
java.util.UUID id = java.util.UUID.randomUUID();
for (int i = 0; i < 600; i++) {
process.runTransient(1.0, id); // dt = 1 s
}
Gotchas:
setCalculateSteadyState(false) on the separator (and its outlet valve).setReverseActing(false) (direct acting). A gas-outlet pressure valve
is also direct acting (false); reverse acting is for cases where more output
reduces the measured value (e.g. a controller manipulating an inlet/feed valve).setLiquidLevel after run() — a steady-state solve resets the level,
so set the starting level and geometry after the first run().Kp and long Ti (see the tuning
table) and consider an SP-PV deadband only with care (see the limit-cycle note
above).After the run, use ControllerPerformanceMetrics.fromEventLog(LC100.getEventLog())
(or LC100.getPerformanceMetrics()) to score the tuning (IAE/ISE/ITAE, PV
variability, valve travel and reversals, settling time) — see the KPI section below.
ControllerPerformanceMetrics
(neqsim.process.controllerdevice.ControllerPerformanceMetrics) computes the
standard loop-tuning KPIs from a controller event log (or from raw time / PV / SP
/ output arrays) so tuning studies report consistent numbers without
re-implementing the definitions. It is the preferred way to compare two PID
tunings on the same disturbance.
Metrics: getIntegralAbsoluteError() (IAE), getIntegralSquaredError() (ISE),
getIntegralTimeAbsoluteError() (ITAE, time referenced to the first sample),
getProcessValueStandardDeviation() (PV variability), getPeakAbsoluteError(),
getControllerOutputTravel() (total valve travel), getControllerOutputReversals()
(valve direction reversals), and getSettlingTime() (time of the last sample
outside the settling band, default 2 % of max(|SP|, 1)).
// After a runTransient loop with a logging controller:
ControllerPerformanceMetrics kpi = LC100.getPerformanceMetrics(); // from getEventLog()
// or, explicitly / with a custom settling band:
ControllerPerformanceMetrics kpi2 =
ControllerPerformanceMetrics.fromEventLog(LC100.getEventLog(), 0.05); // 5 % band
double iae = kpi.getIntegralAbsoluteError();
double valveTravel = kpi.getControllerOutputTravel();
int reversals = kpi.getControllerOutputReversals();
double settlingTime = kpi.getSettlingTime();
logger.info("IAE={} travel={} reversals={} settle={} s", iae, valveTravel, reversals, settlingTime);
// Or build directly from arrays (e.g. PV/OP pulled from a historian):
ControllerPerformanceMetrics kpi3 =
ControllerPerformanceMetrics.fromArrays(time, pv, sp, op);
resetEventLog() on the controller before the disturbance so the KPIs cover
only the window of interest.AntiSurgeController (neqsim.process.controllerdevice.AntiSurgeController) is a
purpose-built reverse-acting PI controller that reads the compressor
getDistanceToSurge() and drives a recycle (spill-back) ThrottlingValve open
when the margin falls below the set point, then closes it again on recovery.
import neqsim.process.controllerdevice.AntiSurgeController;
// distance to surge ~ (operating flow / surge flow - 1); only meaningful once a
// compressor chart with an active surge curve exists.
AntiSurgeController asc = new AntiSurgeController("anti-surge", compressor, recycleValve);
asc.setSurgeMarginSetPoint(0.10); // protect a 10% distance-to-surge margin
asc.setProportionalGain(400.0); // percent opening per unit margin error
asc.setIntegralTime(20.0); // s
asc.setOpeningRange(0.0, 100.0); // valve opening clamp (%) with anti-windup
asc.setActive(true);
recycleValve.addController("anti-surge", asc);
Control law each transient step: error = setPoint - distanceToSurge,
integral += Kp/Ti * error * dt, opening = clamp(Kp*error + integral) with
anti-windup; the controller applies the opening directly to the recycle valve.
Reproducible benchmark. AntiSurgeDynamicBenchmark
(neqsim.process.util.scenario.AntiSurgeDynamicBenchmark) drives the real
controller against a transparent first-order gas-path surrogate
m_{k+1} = m_k - d*dt + a*(u/100)*dt (m = distance to surge, d = disturbance
rate, a = recycle authority, u = valve opening %). It is deterministic and
always converges, so it is the preferred way to verify or tune the control law:
import neqsim.process.util.scenario.AntiSurgeDynamicBenchmark;
AntiSurgeDynamicBenchmark bench = new AntiSurgeDynamicBenchmark();
bench.setInitialMargin(0.30);
bench.setDisturbanceRate(0.020); // flow loss erodes the margin (/s)
bench.setRecycleAuthority(0.060); // fully open recycle restores margin (/s)
bench.setTimeStep(1.0);
bench.setNumberOfSteps(120);
bench.getController().setSurgeMarginSetPoint(0.10);
bench.run(false); // open loop -> surges (margin < 0)
bench.run(true); // closed loop -> margin held at set point
boolean safe = bench.isSurgeAvoided();
Critical gotchas when wiring a full dynamic recycle flowsheet:
Splitter (setSplitFactors([0.97, 0.03])) pins the recycle
fraction in dynamic mode, so the anti-surge valve has no authority over the
actual recycle flow — the controller can hit 100% with no effect. Let the
recycle flow be set by the valve (setCv/resistance), or use the steady-state
anti-surge Calculator pattern instead.getDistanceToSurge()
clamps at -1.0 and the steady solver cannot climb back out; a flowsheet
driven into deep surge will not self-heal even after the inlet is reopened.
Apply gradual/ramped disturbances and keep the machine off deep surge.NaN
(PhaseSrkEos:molarVolume ... NaN). Keep gains moderate and the valve off hard
minimum.AntiSurgeDynamicBenchmark (or a transparent gas-path surrogate) over a full
recycle flowsheet that can stick in deep surge.For coordinated compressor-train studies, use
CompressorAntiSurgeApplication (neqsim.process.equipment.compressor) as the
supervisory scan layer. Each StageApplication can bind directly to real NeqSim
topology objects with bindTopology(process, compressor, hotRecycleValve, coldRecycleValve, recycleCooler, suctionMixer, hotRecycle, coldRecycle). A scan
then writes hot/cold recycle valve openings and optional compressor speed
runback to the real units, and runDynamicStep(scanInput, dt) advances the
bound ProcessSystem with runTransient().
Use this application layer when the study needs stage coordination,
startup/shutdown or trip states, hot/cold recycle split, operator diagnostics,
commissioning checks, or speed runback in one executable dynamic model. Keep
Recycle blocks algebraic unless they have explicit transient inventory
support; valve, compressor, cooler, mixer, and volume-capable equipment should
carry the dynamic response. The application layer reports
NOT_CERTIFIED_FOR_PROTECTION and is for simulation/advisory studies, not a
certified machinery-protection package.
For production-readiness evidence, pass the actual compressor cases and transient response
limits to CompressorProtectionQualificationCalculation; it checks map margins,
extrapolation, driver/start-up/rundown, response time, rotor separation, settle-out and
vendor acceptance without certifying the protection system. For piping, pass ordered
TwoFluidPipe, water-hammer, or externally governed solver samples to
TransientPipingQualificationCalculation. That module checks acoustic resolution and
line-pack balance before pressure, slug, velocity and stress limits, and deliberately does
not treat a quasi-steady time series as distributed-transient evidence. Production mode
requires the controlled context attribute distributedTransientModel=approved.
When TwoFluidPipe.setIncludeMassTransfer(true) is enabled, flash-driven transfer is phase
resolved. Condensation must use equilibrium hydrocarbon-liquid and aqueous-liquid mass
contributions; never use the current cell water cut to identify a phase that is not yet present.
For evaporation, withdraw from the actual oil and water conservative inventories and bound each
withdrawal by phase mass / relaxation time. An absent phase must have exactly zero evaporation
source.
Transferred momentum follows donor velocity. During condensation, gas loses mass and momentum at gas velocity and the receiving oil/water phases gain that momentum. During evaporation, each liquid loses momentum at its own velocity and gas receives their sum. Validate both invariants:
gas mass source + oil mass source + water mass source = 0
gas momentum source + oil momentum source + water momentum source = 0
For a phase-transition regression, cross a real SRK/CPA dew point in both directions without finite
oil or water seeding. Check gas, oil, water, liquid, and total closure with
TwoFluidMassBalanceReport; sweep nearby temperatures, refine time step and mesh, repeat the run,
and compare rigorous flash with FlashTable. The flash table must retain the oil/aqueous liquid mass
split. Record EOS, mixing rule, composition, absolute pressure, temperature, mass-transfer
relaxation time, and units. The current hydrodynamic state transports bulk phase inventories, not a
full component-composition vector per cell, and does not establish OLGA or LedaFlow equivalence.
// Timestep loop
double dt = 1.0; // seconds
int nSteps = 3600; // 1 hour
// Storage for time history
double[] time = new double[nSteps];
double[] pressure = new double[nSteps];
double[] level = new double[nSteps];
for (int i = 0; i < nSteps; i++) {
time[i] = i * dt;
// Introduce disturbance at t = 300 s
if (i == 300) {
feed.setFlowRate(15000.0, "kg/hr"); // Step change +50%
}
process.runTransient(dt);
pressure[i] = PT100.getMeasuredValue();
level[i] = LT100.getMeasuredValue();
}
For large flowsheets with independent branches, enable
process.setParallelTransientEnabled(true) and set the maximum worker count
with process.setTransientThreadPoolSize(n). The per-process worker pool is
created lazily and reused across timesteps; changing the worker count or
disabling the option retires it. Execution follows cached process-graph levels,
so upstream groups complete before downstream equipment is submitted while
independent groups within a level remain parallel. If the caller is interrupted
while waiting, NeqSim restores the interrupt status, cancels queued work without
interrupting equipment already updating state, does not submit downstream
levels, and aborts the remaining timestep phases. Treat that timestep as
incomplete. Graph ordering does not define transient recycle convergence or
rollback, so keep parallel execution off for recycle loops and other implicit
couplings until their transient contract is explicitly supported.
from neqsim import jneqsim
import numpy as np
import matplotlib.pyplot as plt
# Build process (same pattern as Java)
# ... create fluid, equipment, controllers ...
process.run() # Steady state
dt = 1.0
n_steps = 3600
times = np.zeros(n_steps)
pressures = np.zeros(n_steps)
levels = np.zeros(n_steps)
for i in range(n_steps):
times[i] = i * dt
if i == 300:
feed.setFlowRate(15000.0, "kg/hr")
process.runTransient(dt)
pressures[i] = PT100.getMeasuredValue()
levels[i] = LT100.getMeasuredValue()
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 8))
ax1.plot(times / 60, pressures)
ax1.set_ylabel("Pressure (bara)")
ax1.set_xlabel("Time (min)")
ax1.grid(True)
ax2.plot(times / 60, levels)
ax2.set_ylabel("Level (m)")
ax2.set_xlabel("Time (min)")
ax2.grid(True)
plt.tight_layout()
Use this pattern when evaluating actions such as closing an outlet valve, opening a bypass, tripping a shutdown valve, or opening a drain/vent:
process.runTransient(dt) for controller and inventory dynamics, or use neqsim-depressurization-mdmt for dedicated blowdown/MDMT cases.Minimum result keys: max_pressure_bara, max_level_m, min_temperature_C,
peak_flare_flow_kg_s, time_to_alarm_s, and time_to_new_steady_state_s.
// For vessel depressurization, use the safety/depressuring agent
// Key pattern: open a blowdown valve at t=0 and track P, T vs time
ThrottlingValve bdv = new ThrottlingValve("BDV", sep.getGasOutStream());
bdv.setOutletPressure(1.0, "bara"); // Vent to atmosphere
bdv.setCv(500.0); // Valve Cv
// Controller: fully open at t=0
// Or use step change in valve opening
For more advanced control logic:
import neqsim.process.controllerdevice.TransferFunctionBlock;
TransferFunctionBlock leadLag = new TransferFunctionBlock();
// Configure lead-lag, deadtime, filters as needed
process.run() before runTransient()setInternalDiameter() and setSeparatorLength() for meaningful level dynamics. For dynamic simulation, set directly on the separator; for design purposes, configure via SeparatorMechanicalDesign (see neqsim-api-patterns skill)process.run() (steady), call setCalculateSteadyState(false) on the separator AND every valve, then separator.setLiquidLevel(startFraction), before runTransient. If steady-state mode is left on, the separator liquid level stays pinned at its default (0.5) and the level controller never acts. The valve Cv is auto-derived from the steady solve. A liquid-outlet level valve is setReverseActing(false) (level up -> valve opens); put a pressure controller on the gas-outlet valve so the vessel pressure is held and the level loop is isolated.Beyond the default fixed-step explicit-Euler loop, ProcessSystem accepts a
pluggable IntegratorStrategy. Implementations live in neqsim.process.dynamics:
| Strategy | Class | Notes |
|---|---|---|
| Explicit Euler | ExplicitEulerIntegrator | Default; fast, conditionally stable |
| BDF-1 (Implicit Euler) | BDFIntegrator | Newton + FD Jacobian (tol 1e-8, maxIter 25). Falls back to explicit Euler if Newton diverges; check lastStepFellBack() |
import neqsim.process.dynamics.BDFIntegrator;
import neqsim.process.dynamics.ExplicitEulerIntegrator;
import neqsim.process.dynamics.IntegratorStrategy;
process.setIntegratorStrategy(new BDFIntegrator()); // stiff dynamics
// process.setIntegratorStrategy(new ExplicitEulerIntegrator()); // explicit default
// process.setIntegratorStrategy(null); // reset to default ExplicitEulerIntegrator
IntegratorStrategy current = process.getIntegratorStrategy();
For multi-area plants the strategy is propagated to every child area:
plant.setIntegratorStrategy(new BDFIntegrator()).
Time-stamped events (ESD trips, valve closures, setpoint ramps) are managed by
EventScheduler in neqsim.process.dynamics. Every call to
runTransient(dt, id) fires events with time <= currentTime at the top of
the step, before equipment runs.
import neqsim.process.dynamics.EventScheduler;
EventScheduler events = new EventScheduler();
events.scheduleEvent(120.0, "ESD trip", new Runnable() {
public void run() { esdValve.setPercentOpen(0.0); }
});
events.scheduleEvent(300.0, "Setpoint ramp", new Runnable() {
public void run() { pressureController.setControllerSetPoint(45.0); }
});
process.setEventScheduler(events);
for (int i = 0; i < nSteps; i++) {
process.runTransient(dt); // due events fire automatically
}
int fired = events.getFiredEvents().size();
int pending = events.getPendingEvents().size();
For multi-area plants install the scheduler once on the ProcessModel; it is
propagated to every child area, and plant.runTransient(dt, id) advances all
areas:
plant.setEventScheduler(events);
plant.runTransient(dt, java.util.UUID.randomUUID());
Note: EventScheduler is declared transient on ProcessSystem because
event Runnable payloads (lambdas, anonymous classes) are usually not
serializable. Re-install the scheduler after deserialising a saved process.
Three new measurement devices in neqsim.process.measurementdevice complement
the existing PT/TT/LT/FT family:
| Class | Reads | Unit |
|---|---|---|
DifferentialPressureTransmitter(name, high, low) | pHigh - pLow across two streams | bar |
CompositionAnalyzer(name, stream, component, phase) | Mole fraction; phase OVERALL / GAS / LIQUID | mole/mole |
FlowRatioMeter(name, num, den, basis) | Flow ratio; basis MASS / MOLE / VOLUME | dimensionless |
import neqsim.process.measurementdevice.DifferentialPressureTransmitter;
import neqsim.process.measurementdevice.CompositionAnalyzer;
import neqsim.process.measurementdevice.FlowRatioMeter;
DifferentialPressureTransmitter dpdt = new DifferentialPressureTransmitter("dPT-1", upstream, downstream);
CompositionAnalyzer ax = new CompositionAnalyzer("AX-1", sweetGas, "methane",
CompositionAnalyzer.AnalyzerPhase.GAS);
FlowRatioMeter rxn = new FlowRatioMeter("FR-1", recycleStream, feedStream,
FlowRatioMeter.FlowBasis.MASS);