neqsim-dynamic-simulation
Skill167 starsChanged 7 months agoApache-2.0View on GitHub
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.
In this repository140 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- .github/copilot-instructions.mdCopilot instructions
- analyze-convergenceSkill
- analyze-gibbs-convergenceSkill
- book-creationSkill
- design-flash-benchmarkSkill
- design-reactor-benchmarkSkill
- figure-discussionSkill
- generate-publication-figuresSkill
- journal-formattingSkill
- neqsim-acid-gas-treatingSkill
- neqsim-advanced-control-mpc-and-virtual-sensingSkill
- neqsim-agent-handoffSkill
- neqsim-agentic-process-optimizationSkill
- neqsim-api-patternsSkill
- neqsim-autonomous-investigationSkill
- neqsim-capability-mapSkill
- neqsim-capacity-and-utilization-analysisSkill
- neqsim-capacity-increase-screeningSkill
- neqsim-ccs-hydrogenSkill
- neqsim-code-healthSkill
- neqsim-code-hygieneSkill
- neqsim-code-reviewSkill
- neqsim-column-internals-and-rate-basedSkill
- neqsim-compressor-antisurge-recycleSkill
- neqsim-consequence-analysisSkill
- neqsim-continuous-task-improvementSkill
- neqsim-controllability-operabilitySkill
- neqsim-cooling-waterSkill
- neqsim-cpa-water-boundariesSkill
- neqsim-depressurization-mdmtSkill
- neqsim-distillation-designSkill
- neqsim-document-intelligence-extractionSkill
- neqsim-dynamic-equipment-implementationSkill
- neqsim-electrolyte-systemsSkill
- neqsim-emulsion-separationSkill
- neqsim-energy-systems-and-electrificationSkill
- neqsim-engineering-design-packageSkill
- neqsim-eos-regressionSkill
- neqsim-equipment-cost-estimationSkill
- neqsim-exploration-strategy-selectionSkill
- neqsim-field-developmentSkill
- neqsim-field-economicsSkill
- neqsim-firewater-deluge-designSkill
- neqsim-flow-accelerated-corrosionSkill
- neqsim-flow-assuranceSkill
- neqsim-flow-assurance-study-reportSkill
- neqsim-forward-separator-trainSkill
- neqsim-future-operation-studySkill
- neqsim-hazid-fmea-eta-ftaSkill
- neqsim-heat-integrationSkill
- neqsim-hydrogen-productionSkill
- neqsim-input-validationSkill
- neqsim-integrated-production-and-lifecycleSkill
- neqsim-in-writingSkill
- neqsim-java8-rulesSkill
- neqsim-jurisdiction-regulatory-basisSkill
- neqsim-literature-searchSkill
- neqsim-lng-liquefactionSkill
- The first 60 shown.
The file
Show its rules and text
We split this skill into the rules it gives an agent
35 rules. About 0 are common, 16 are distinctive, and 19 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈16
Distinctive · estimate
This author's own choices. Worth a look.
19
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 13
Add transient behavior to the existing equipment class through its mode/configuration APIs.
- Very specific: line 14
Do not create a separate DynamicHeatExchanger, DynamicCompressor or other unit operation solely for a different execution mode.
- Very specific: line 102
Use AdaptiveTransientException reason (MINIMUM_STEP, ATTEMPT_LIMIT, or UNREPRESENTABLE_INTERVAL), attempted-trial count, attempted step, …
- Distinctive: line 107
Only this new overload uses start-of-substep event timing and caps the interval at the earliest future event across all area schedulers.
- Distinctive: line 175
Read the two-fluid model guide for unsupported combinations and qualification limits.
- Very specific: line 327
After the run, use ControllerPerformanceMetrics.fromEventLog(LC100.getEventLog()) (or LC100.getPerformanceMetrics()) to score the tuning …
- Distinctive: line 338
Put a gas pressure-control valve on the outlet with a lower declared downstream pressure, actuator lag and physical-step idempotency …
- Distinctive: line 343
Check each component inventory, the fixed-volume/internal-energy flash residual, pressure and temperature bounds, held scan order, valve …
- Very specific: line 363
Read getLastTransientEnergyBalanceReport() for consumed ordered enthalpy/heat terms, accepted-dt elapsed time, epoch, local VU defect and …
- Distinctive: line 369
Keep response PASS, energy FAIL/INCOMPLETE and qualification HOLD separate.
- Distinctive: line 376
Never reconstruct consumed inlet enthalpy from post-step ports after the mixer has been refreshed.
- Very specific: line 382
Use registered native held instruments with enabled transient sampling and SafetyFunctionChannel.highTripFromMeasurement(...) or …
- Distinctive: line 396
Use current held conditions and a separate explicit operator reopening, retaining each native trace epoch before reset.
- Very specific: line 408
Use actual finite-stroke ESDValve, native ESDLogic/TripValveAction, and ParallelActionGroup where simultaneous valve commands are required.
- Very specific: line 416
After canonical PID inputs/action configuration, definition IDs and evidence references are frozen, use …
- Very specific: line 472
Use ControlsBenchmarkSuite when a request needs a deterministic regression check across NeqSim's canonical level, pressure, cascade, …
- Distinctive: line 571
Use this application layer when the study needs stage coordination, startup/shutdown or trip states, hot/cold recycle split, operator …
- Very specific: line 598
When TwoFluidPipe.setIncludeMassTransfer(true) is enabled, flash-driven transfer is phase resolved.
- Very specific: line 626
Use HydrocarbonPoolTransfer.forHydrostaticPool(...) only for stationary or slowly draining stratified terrain pools.
- Distinctive: line 868
Use this pattern when evaluating actions such as closing an outlet valve, opening a bypass, tripping a shutdown valve, or opening a …
- Distinctive: line 871
Run and validate the steady-state base case.
- Very specific: line 874
Run process.runTransient(dt) for controller and inventory dynamics, or use neqsim-depressurization-mdmt for dedicated blowdown/MDMT cases.
- Distinctive: line 907
Before relying on a mixed transient flowsheet, inspect its state-ownership inventory separately from runtime activation and physics …
- Very specific: line 919
All built-in process-element declarations of runTransient(double, UUID) are mapped, and CI rejects a new built-in override unless it is …
- Very specific: line 968
Always run steady state first: Call process.run() before runTransient()
- Distinctive: line 969
Timestep size: Start with 1.0 s, reduce if oscillating (0.1-0.5 s)
- Very specific: line 970
Liquid-outlet level-controller direction: use setReverseActing(false) so level up drives the outlet valve further open.
- Very specific: line 972
Separator dimensions: Must set setInternalDiameter() and setSeparatorLength() for meaningful level dynamics.
- Distinctive: line 975
Physical-step UUIDs: never reuse one non-null UUID across multiple outer timesteps.
- Very specific: line 1104
Use AcousticPumpBoundary with upstream WaterHammerPipe.BoundaryType.PUMP for a declared single-liquid forward curve and actual …
- Distinctive: line 1115
Follow the physical inputs, equations, public example and limits.
- Distinctive: line 1116
Do not extrapolate clamped curves, turn stopped pumps into closed valves or claim a prescribed-speed adapter supplies shaft inertia, …
- Very specific: line 1129
Read the acoustic model, accepted histories and public evidence and hand specialist hydraulic cases to neqsim-water-hammer.
- Very specific: line 1136
Use the existing GeometryCalculator.StratifiedPressureMoments derived centre-referenced phase first moments [m3] and …
- Distinctive: line 1159
Do not use it as proof of dry/wet fronts, terrain pools, refill or physical evaporation.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Dynamic Simulation Guidance
Guide for transient/dynamic process simulation in NeqSim.
Extend the existing unit operation
Add transient behavior to the existing equipment class through its mode/configuration APIs.
Do not create a separate DynamicHeatExchanger, DynamicCompressor or other unit operation
solely for a different execution mode. Internal solver/state helpers are appropriate; a distinct
physical/model abstraction requires a written rationale. Keep the same streams, ports, tags,
design data, controllers and DEXPI/P&ID bindings. Preserve default steady behavior and test
initialization, both mode transitions, cache invalidation, conservation, repeated-step identity,
rollback and serialized continuation. For the heat exchanger automation workflow, configure the
existing HeatExchanger and call setDynamicModelEnabled(true) before its initial run. See
the coordinated workflow.
When to Use Dynamic Simulation
- Startup and shutdown sequences
- Controller tuning and loop analysis
- Pressure relief / blowdown scenarios
- Level and pressure dynamics
- Compressor surge analysis
- Pipeline transients (slug flow)
- Emergency depressurization (EDP/ESD)
- P&ID-derived valve actions where pressure, level, controller response, or inventory release changes with time
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.
Dynamic Simulation Architecture
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:
- Flowsheet-wide settings are applied. Each setter unit receives exactly one transient call, applying its specification and advancing its own clock once.
- Simulation time advances, then due events and field inputs are applied.
- Equipment updates, including its thermodynamic calculations, run in insertion order or dependency-aware graph levels when parallel transient execution is enabled. Setter units are excluded from these explicit, semi-implicit, and parallel equipment passes. Steady-state algebraic equipment still evaluates on every requested pass, but its local clock advances once per non-null timestep calculation identifier. Reusing an identifier may refine a recycle without advancing physical time; use a new identifier for the next physical timestep.
- Standalone controllers run; controllers actually executed inside equipment
retain their equipment-specific timing for compatibility. Execution is
coalesced by object identity and the timestep calculation identifier. Merely
attaching a controller to equipment that does not execute it must not suppress
the standalone update; repeated standalone registration runs once per step.
ControllerDeviceBaseClassalso makes repeated calls with one identifier idempotent, including semi-implicit equipment passes. Custom controllers integrated by equipment must implement the same identifier contract throughhasRunTransient(UUID). - Measurement devices are sampled, alarms are evaluated, and history is stored.
A non-null UUID therefore identifies one physical timestep, not an entire
outer time-marching simulation. Refinements of that physical step reuse the
same physical-step UUID; the next accepted physical step must use a different
UUID. For ordinary loops runTransient() is safe because it creates a fresh
UUID each call. Deterministic safety/OTS workflows can use
TransientStepIdentifier.deterministicPhysicalStep(scope, stepIndex).
The legacy two-argument ProcessSystem.runTransientAdaptive(dt, id) advances one
full step and recommends a following timestep using a temperature-change heuristic.
It does not reject/retry on a full-step versus two-half-step estimate. Its strict
qualification blocker remains; do not infer a different contract from its name.
For opt-in numerical acceptance, both ProcessSystem and ProcessModel provide
runTransientAdaptive(requestedDt, parentId, AdaptiveTransientOptions). This first
profile requires concrete process/model owners, exactly aligned finite area clocks
and no shared mutable participant across different areas. Use explicit
minimum/maximum timestep, bounded attempts, integration order and a
TransientErrorStateBinding for every unique rollback participant, including
attached controllers. Each read-only serializable provider returns a nonempty
vector with stable owner/variable IDs, explicit units, finite values, positive
absolute tolerances and nonnegative relative tolerances. Numerical coverage must
be complete and the schema/tolerances must remain fixed across all trial states.
The caller remains responsible for auditing that the declared variables cover
the relevant physical and control state.
The overload compares one full step with two half steps using maximum normalized
error, restores rejected trials in place, and commits the actual two-half-step
state. Distinct deterministic physical child UUIDs prevent idempotent controllers
from skipping the second half. Use a new parent UUID on the next call; preserve
result getAcceptedStepSeconds() versus getSuggestedNextStepSeconds() semantics.
Execution/provider/schema failures propagate after rollback; they are not accepted
as numerical progress. Exhausted bounds/attempts leave the step uncommitted.
Use AdaptiveTransientException reason (MINIMUM_STEP, ATTEMPT_LIMIT, or
UNREPRESENTABLE_INTERVAL), attempted-trial count, attempted step, normalized error
and limiting-variable evidence for numerical failure reports. A NaN error marks
an unavailable comparison; infinity marks an unbounded/overflowing error.
Only this new overload uses start-of-substep event timing and caps the interval at the earliest future event across all area schedulers. Shared due events execute before any area advances. Endpoint events remain pending until the following call, so they cannot alter the interval that precedes their trigger time. Event actions must have declared complete in-memory mutation scope; external effects are outside the rollback contract. Callbacks must terminate and avoid due-now cross-scheduler chains; the underlying scheduler has no callback budget. Newly inserted future events trigger bounded rollback/localization, while a leftover past event fails the interval. Keep exclusive ownership during adaptive execution.
Expect three physical evaluations per complete attempt plus snapshot/comparison and rejected-trial cost. Default/legacy paths gain no adaptive work. The synthetic quantitative tests establish the numerical acceptance/replay contract, while real process-control and safety studies still need method/parameter qualification, conservation/refinement evidence and explicit limits. Read the dynamic capability contract for the norm, provider contract, provenance, event and qualification boundaries.
Basic Dynamic Setup
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();TwoFluidPipe conservative thermal recovery
For blowdown or repressurization, the legacy post-step temperature update does not
recover temperature from transported total energy. The experimental
setUseConservativeEnergyRecovery(true) option must be selected before run();
it initializes named-component inventories automatically, including pure fluids.
Select coupled pressure-momentum, Euler, energy enabled and slug tracking disabled.
Read the two-fluid model guide
for unsupported combinations and qualification limits. Inspect
getLastConservativeEnergyReport() together with mass and component reports;
the legacy sensible thermal report is null in this mode. A successful interval
recovers common P/T from occupied volume and internal energy at each transported
phase composition. It does not qualify rich-fluid evaporation rates or asset
blowdown times. Regressions: TwoFluidPipeConservativeEnergyTest and
ConservativeThermodynamicRecoveryTest. Keep issue #4595 open until the complete
rich-phase-transition and refinement acceptance is demonstrated.
Adding Measurement Devices
// 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);Controller Configuration
PID Controller
// 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);Controller Tuning Guidelines
| 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 |
Controller Deadband (SP-PV)
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.
Dynamic level-loop recipe (get the sequence right)
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 fresh physical-step identity each step.
for (long step = 0; step < 600; step++) {
java.util.UUID physicalStepId =
neqsim.process.dynamics.TransientStepIdentifier.deterministicPhysicalStep("level-loop", step);
process.runTransient(1.0, physicalStepId); // dt = 1 s
}Gotchas:
- Level pinned at 0.5 — the vessel is still in steady-state mode; call
setCalculateSteadyState(false)on the separator (and its outlet valve). - Level runs away — the liquid-outlet level controller is reverse acting; it
must be
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). - Set
setLiquidLevelafterrun()— a steady-state solve resets the level, so set the starting level and geometry after the firstrun(). - One UUID per physical step — do not create one UUID before the outer time loop and reuse it. Built-in controllers/equipment intentionally treat repeated calls with one UUID as refinements of the same physical timestep.
- For an averaging level loop, use a loose
Kpand longTi(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.
Dynamic pressure-inventory loop recipe
A pressure loop needs a real compressible inventory. A valve between two imposed-pressure streams verifies action and hydraulics, but it cannot demonstrate pressure regulation. For the canonical separator chain:
- Configure separator diameter, length and starting level, then disable steady-state calculation.
- Put a gas pressure-control valve on the outlet with a lower declared downstream pressure, actuator lag and physical-step idempotency enabled.
- Set the PCV embedded controller to manual when an external PID owns the command.
- Register a physical-step-sampled
PressureTransmitteron the separator gas outlet and attach an engineering-unit, direct-acting PID to the PCV. - Check each component inventory, the fixed-volume/internal-energy flash residual, pressure and temperature bounds, held scan order, valve travel and timestep sensitivity.
SeparatorPressureControlChainTest is the public reference implementation. Its SRK/classic wet-gas
case uses a 2.2 m by 7.0 m separator, 55 bara initial pressure, 45 bara downstream boundary,
20 to 28 kg/s feed step, one-second scans, Kp = 6.0, Ti = 30 s and 4 s actuator lag. The
70 s regression limits peak pressure to 56.68 bara, pressure IAE to 84.6 bar-s and PCV travel to
21.38 percentage points; it also requires exact rollback/restart/repeatability and 1.0/0.5 s
refinement. These values qualify only the synthetic numerical chain. Do not reuse the tuning or
limits as a plant design basis.
Audit separator cumulative energy independently of response
Enable setTransientEnergyBalanceTrackingEnabled(true) on an exact concrete
Separator after steady/inventory initialization. Read availability and coverage
with getTransientEnergyBalanceTrackingStatus() and
getTransientEnergyBalanceCoverageIssue(); null last report means no measured
accepted update, not zero energy error. Reject uninstrumented subclasses,
including ThreePhaseSeparator, as unsupported.
Read getLastTransientEnergyBalanceReport() for consumed ordered enthalpy/heat
terms, accepted-dt elapsed time, epoch, local VU defect and final published
inventory energy. Compare getAcceptedVuEnergyJ() with getPublishedEnergyJ();
do not confuse a locally accepted flash with complete-window energy closure.
Apply an explicit frozen criterion through assessTransientEnergyBalance(limit);
it gates maximum absolute cumulative debt using the reported initial-U scale.
Keep response PASS, energy FAIL/INCOMPLETE and qualification HOLD separate.
Retain the original criterion during timestep refinement and report raw J
residuals, scale, extrema and sums of absolute local VU defects.
Require exact ordered external balance, on/off trajectory parity, reset/epoch, snapshot/restart and rejected-step evidence. Treat skipped steps, excluded nonzero liquid withdrawal and steady reevaluation as incomplete coverage. Never reconstruct consumed inlet enthalpy from post-step ports after the mixer has been refreshed. Read the native diagnostic contract for the bounded default-off API, scale and unsupported-mode limits.
Advanced ESD, voting and manual trip with actual inputs
Use registered native held instruments with enabled transient sampling and
SafetyFunctionChannel.highTripFromMeasurement(...) or
lowTripFromMeasurement(...). Derive channels from the actual executing process
copy; references captured from the source graph are rejected. For an unlinked
native PushButton, use manualTripFromPushButton(...). Legacy custom extractors
have unverified input identity and are not a substitute for actual configured
instrument readback. Duplicate channel objects and duplicate normalized channel
tags are rejected. Distinct software tags do not establish hardware independence.
Preserve native semantics explicitly: response delay is continuous-demand qualification on held samples; qualified channels latch until reset; bypass keeps fixed m-out-of-n voting. Test nonoverlapping qualified pulses, pre-vote pause with no actuation, post-actuation resume, failed-child preservation and reset/retrip. Reset clears bookkeeping and does not reopen equipment or authorize operation. Use current held conditions and a separate explicit operator reopening, retaining each native trace epoch before reset.
For quantitative cases use event-exact timing and whole-window
DynamicScenarioCriterion.EvaluationMode.MAINTAIN_BOUNDS for pressure envelopes
and no-trip invariants. Set its positive deadline to the full intended event
window and require start/end coverage. Default reach-by-deadline can pass an
initial normal sample despite a later violation. Neither mode proves behavior
between samples. Read actual accepted physical UUID and dt from
ProcessSystem.getCalculationIdentifier() and getTimeStep(); rounding
wall-time/nominal-dt fails on real short final alignment steps.
Use actual finite-stroke ESDValve, native ESDLogic/TripValveAction, and
ParallelActionGroup where simultaneous valve commands are required. Legacy
HIPPS immediate commands or stored closure-time metadata do not prove physical
travel. Retain exact native target identities and supported trip-action readback;
an energizing/opening/opaque action with the same target is not equivalent trip
configuration. Command issuance, each actual valve closure, pressure response and
failed/slow/inhibited negative controls need separate assertions.
After canonical PID inputs/action configuration, definition IDs and evidence
references are frozen, use EngineeringSimulationRunner.runStrict(project) for
direct typed project evidence. Do not attach unrelated same-name results. The
public AdvancedV101SafetyStudy fixture uses actual V100/V101 inventories, three
PI loops and 17 devices (16 held plus 1 discrete manual input); its 18 profiles plus
five half-dt cases retain expected native FAIL results. Keep per-vessel energy
coverage/debt and independent raw consumed-boundary audits separate from response.
The frozen 1e-5 energy limit and qualification HOLD remain even if whole-slice
residuals partly cancel. This is a proposed design study, not SIL, ISO/IEC or
construction approval. See the closed-loop verification contract
and public study resource README for the tested model and commands.
Loop-tuning KPIs (ControllerPerformanceMetrics)
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);- Integral criteria use trapezoidal integration over the sample intervals, so irregular time steps are handled correctly.
- A lower IAE/ISE/ITAE means tighter regulation; lower valve travel and reversals means gentler actuator duty. Tuning trade-offs usually pit the two against each other (tighter control costs more valve movement).
resetEventLog()on the controller before the disturbance so the KPIs cover only the window of interest.
Canonical controls qualification suite
Use ControlsBenchmarkSuite when a request needs a deterministic regression
check across NeqSim's canonical level, pressure, cascade, split-range,
anti-surge, and compressor speed/recycle control structures:
import neqsim.process.controllerdevice.ControlsBenchmarkSuite;
ControlsBenchmarkSuite.Report report = ControlsBenchmarkSuite.runCanonicalSuite();
boolean qualified = report.isPassed();Inspect report.getCases() for the uniform time/PV/SP/output traces,
ControllerPerformanceMetrics, physical acceptance detail, and final errors.
The report also exposes the dedicated AgentBenchmarkSuite verdict through
getAgentBenchmarkReport(). See
docs/benchmarks/controls_benchmark.md
for equations, declared challenges, numerical gates, and current CI reference
values.
The suite qualifies deterministic control execution and KPI reporting against transparent surrogate plants. It is not a substitute for field tuning, vendor compressor data, severe-slugging qualification, a safety-instrumented function, or commissioning validation.
Anti-Surge Control (dynamic)
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:
- A fixed-factor
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-surgeCalculatorpattern instead. - Once the operating point crosses left of the surge line,
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. - Aggressive proportional gain can slam the recycle valve to its minimum opening,
starve a stream, and trigger an SRK flash
NaN(PhaseSrkEos:molarVolume ... NaN). Keep gains moderate and the valve off hard minimum. - To demonstrate or tune the control law cleanly, prefer
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.
TwoFluidPipe Phase Appearance and Disappearance
Read the TwoFluidPipe evidence matrix before selecting a steady or transient configuration. It is the canonical distinction between implemented, numerically verified, experimentally qualified, failed, and unsupported behavior.
When TwoFluidPipe.setIncludeMassTransfer(true) is enabled, flash-driven transfer is phase
resolved. In conservative-energy Euler mode on a dry gas/oil grid, use conserved component
inventory differences from the local flashed targets, rather than assigning all new oil its final
equilibrium composition. Carry opposing species transfers with their actual donor momentum and
keep one total-energy owner. This remains an approximate relaxation, not predictive evaporation.
Positive aqueous inventory/equilibrium retains the preceding source contract below.
For a dry hydrocarbon finite-rate sensitivity, use HydrocarbonPoolTransfer only with
conservative-energy Euler. Supply component $k_i$ values (m/s), evaporation enthalpies (J/kg),
interface-width fraction and available heat (W/m); never infer them from the relaxation time.
The closure uses the same equilibrium component target, component inventory, donor momentum and
total-energy owners. Require zero-drive/area, donor/heat-limit, unseeded appearance, refinement and
ledger gates. Label results parameterized screening until coefficients pass independent public
calibration and validation. Do not call this elapsed pool-depletion, physical restart or pigging
qualification.
The 100 W/m finite-heat 780 s rich-gas cycle is numerical mesh/time evidence only. It crosses the cubic
EOS diagnostic $V/B=1.75$ label transition by explicitly retaining the requested root while the
positive-compressibility, Jacobian and energy-admissibility guards remain active. Do not clip
composition or change the configured root to manufacture a restart result.
For a conservative pressure-driven restart, select CONSTANT_PRESSURE, optionally set an explicit
reservoir with setInletBoundaryFluid(...), and ramp from getInletFacePressure("Pa"). The solver
reflashes the reservoir at the prescribed pressure and resolves signed phase speed from acoustic
impedance; it no longer treats pressure traction as zero-advection refill. The isolated-reservoir
route reuses one flash across internal stages at the same pressure; verify the
performance safeguard with getPressureInletFlashCount(). A live connected stream remains uncached.
The complete declared cycle closes mass, named-component and energy ledgers on 12/24 cells and
0.5/0.25 s intervals, but
the time-refined trace retains one small unfiltered opening reversal. Treat this as numerical
boundary/restart evidence, not independent surge, field, gelled-oil or repeated-cycle validation.
Use HydrocarbonPoolTransfer.forHydrostaticPool(...) only for stationary or slowly draining
stratified terrain pools. Use forStratifiedFlow(...) for a flowing circular-stratified interface:
its local holdup chord preserves integrated area and component sources under subdivision of a
uniform state, including nonuniform cells and finite heat limits. Independent per-cell horizontal
puddles introduce cell-length dependence during a flowing restart. The two area assumptions are
distinct models; retain the historical hydrostatic-cycle failure as evidence rather than claiming
the flowing closure validates static-pool convergence. Inspect interface-area, equilibrium-appearance and unresolved-trace-area
diagnostics. Representable long-cell trace wedges retain their leading-edge depth without a liquid
floor; any reported fallback remains a numerical-resolution warning. Configure deliberate wall
inventory with setInitialWallTemperatureProfile(...)
before run() and retain the wall profile through every event/replay. In multilayer mode, its one
temperature per cell initializes every radial layer uniformly; the layers evolve independently and
a new steady run clears the old radial inventory. Mesh refinement must preserve physical terrain
breakpoints and outlet length; a "last three cells" level-outlet rule compares different pipes.
The wall-coupled 12/24-cell cycle fixes the final 1250 m at -333.5 m on both meshes and retains
the 10% peak/final inventory gates. Consult its hydrocarbon-pool-transfer receipt for the executed
result. Maximum single-cell interface area is extensive and is not a convergence target.
A moving pig still needs separate distributed moving-volume, face-flux and pressure-work
conservation and qualified inlet states.
For a declared restart-opening window, use TwoFluidPipeReport.analyzePressureTrace(...) to report
unfiltered extrema, peak-to-peak pressure, maximum sample rate, total/excess variation and reversal
count with an explicit Pa gate. Preserve the sample interval and do not promote this diagnostic to a
surge qualification without mesh/time and independent evidence.
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 = 0For 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. With named-component transport, freeze component source compositions
and their partial-enthalpy latent source from the same RHS-stage equilibrium state as the
hydrodynamic phase source. A disappearing phase uses its conserved donor composition for the
property state; a receiving phase still uses only the equilibrium flash composition. Require
phase, total, named-component, interphase, bounded-fraction, and latent-inclusive energy closure.
The conservative Lagrangian slug/film + named-component + phase-transfer + thermal combination is
currently verified only with single-stage Euler. Reject a multi-stage selection before mutation
until intermediate phase appearance owns stage-local component inventories. Signed outlet backflow
with named-component transport requires setOutletBoundaryFluid(externalFluid) before
setAllowOutletPhaseBackflow(true); this explicitly owns reversed phase composition, density, and
enthalpy. The last interior composition is not a valid inflow boundary. Treat the synthetic
same-gas pressure-ramp regression as boundary-contract evidence only, not complete restart or
evaporation-rate qualification. When the physical outlet is a gas takeoff above retained liquid,
use setGasOnlyOutletTakeoff(true) with a constant-pressure outlet; do not zero liquid inventories
or manufacture a separator mixture. The mode closes external oil/water mass, component and
advected-energy flux while preserving their traction and internal drainage. None of these numerical contracts establishes equivalence with a
commercial transient multiphase simulator.
A negative prescribed inlet mass rate is pipe outflow: phase split, composition and enthalpy come
from the first finite-volume cell, not the external feed. Audit countercurrent boundary cases with
getLastBoundaryPhaseMomentumFluxes() and getLastBoundaryPhasePressureForces() so donor advection
and interior pressure traction remain distinct. These are boundary diagnostics, not a whole-domain
momentum-conservation claim or physical restart qualification.
TwoFluidPipe Regime-Transition Continuation
Transient wall-friction, interfacial-friction, interfacial-area, and entrainment sources must
consume the dimensionless normalized weights from FlowRegimeDetector.classify(...). Do not
reintroduce detectFlowRegime(...) as a hard source switch: the steady hold-up and transient
momentum paths would then see different closures at the same state. Keep stratified geometry active
whenever a stratified weight is non-zero. Pure-regime endpoints must reproduce the original closure
exactly.
Blend interfacial forces directly as the weighted sum of each regime's shear times area; separately averaging the two factors introduces spurious cross terms. Verify the actual momentum source and equal/opposite phase reactions in transition tests.
This continuation does not authorize changes to regime criteria, transition-band widths, hold-up correlations, or regime-specific friction models. It remains experimental until the public Tengesdal envelope, the liquid-rich 1,800 s inventory criterion, conservation, nonlinear convergence, nearby operating points, refinement, and runtime gates all pass. Continue to reject a completed time loop whose sticky coupled-solver diagnostics fire.
TwoFluidPipe Coupled Pressure-Momentum Gate
For a liquid-rich pressure outlet that physically permits phase fallback, the coupled path requires all four options. Keep the nonlinear controls explicit in reproducible studies:
pipe.setEnableInterfacialPressure(true);
pipe.setImplicitInterfacialPressureCoupling(true);
pipe.setEnableCoupledPressureMomentum(true);
pipe.setAllowOutletPhaseBackflow(true);
pipe.setCoupledPressureMomentumMaximumIterations(24); // default
pipe.setCoupledPressureMomentumRelativeVolumeTolerance(1.0e-7); // defaultThe previous budget of 12 stopped the public Tengesdal progress case near a 6e-7 relative
cell-volume residual, above the 1e-7 gate. The current 16-section Test 3 probe completes 50/50
calls of 0.1 s and a 24-section refinement completes 100/100 calls of 0.05 s. Neither rejects a
nonlinear substep, and phase and total discrete mass residuals remain below 1e-9.
A coupled call that exhausts its adaptive retries throws with accepted/requested elapsed time, residual/tolerance, iterations/cap, and limiter state. Do not catch that exception and advance the flowsheet clock. Successful completion still requires the sticky diagnostics to be inspected after the full window:
if (pipe.isTransientOutletBackflowClamped()
|| pipe.isTransientCoupledPressureMomentumFailureDetected()
|| pipe.isTransientCoupledPressureMomentumCorrectionLimited()
|| pipe.getTransientCoupledPressureMomentumRejectedSubsteps() > 0) {
throw new IllegalStateException("TwoFluidPipe transient is not qualified");
}These flags reset on the next steady run(). A pressure-limiter event is not itself a rejected
substep because the volume residual can converge while the bounded correction is active, but it is
mandatory qualification evidence. Use
isCoupledPressureMomentumPressureCorrectionLimited() only for the latest correction; use the
isTransient... form for the complete window. The current Tengesdal progress run still activates that flag and
spans -18.55 to 6.88 kg/s liquid outlet versus the stored 0.375 to 4.03 kg/s comparison. It is
therefore numerical-progress evidence only, not sustained-severe-slugging or commercial-parity
evidence. Never tune a public closure to the commercial trace; validate the next boundary-coupling
increment against the public Tengesdal experiment, conservation, nearby points, and mesh/time-step
refinement.
The unsplit transactional callback exposes directional gas/oil/water face diagnostics: separate forward/reverse mass transport, donor advective momentum, pressure traction, physical cell lengths, and phase mass sources. The experimental directional-complementarity option augments the nonlinear vector with signed mass magnitude, advective momentum and a donor multiplier per internal phase face, conservatively replaces the free flux, and carries solved faces into prepared-step verification. Exactly absent donors use exact donor-presence and zero-momentum rows; do not add an inventory floor. This is not yet a qualified phase-transition path: the six unchanged 0.1 s lanes pass. The nonlinear path may now land exactly on a zero phase/face bound, project only outward search components at an already active exact bound, select the donor from the unmodified signed free flux between frozen Jacobians, and release an exact donor row when the selected donor is present. Rejected line-search trials use independent active-set copies. These are conservative transition mechanics, not a tolerance, inventory clip, or fitted donor smear. Exact disappearance and opposite-donor unit cases pass, but the directly affected nine unchanged five-second lanes still fail line search at 0.63125--1.83515625 s and the complete 15-case gate remains failed. Retain all five-second, 180 s and 600 s gates.
Make every cycle-count assertion feasible for its sampled window. A detector that reports N
completed intervals needs N + 1 events, so the settled window must span at least N times the
largest admissible cycle period, plus phase and boundary margin. Keep shorter runs as numerical
characterization when they cannot contain the qualification cycle count; enforce the complete
cycle gate in the long-duration qualification run.
Running Dynamic Simulation
// 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. A worker exception propagates to the caller with its runtime type and message,
cancels later queued groups, prevents downstream dependency levels from being
submitted, and skips controller, measurement/alarm/history, timestep-counter,
and calculation-identifier commit phases. The process clock, due-event effects,
and equipment state already mutated by a same-level sibling or earlier unit are
not rolled back; treat that timestep as incomplete. 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 likewise aborts the remaining timestep phases. Graph ordering does
not define transient recycle convergence or transactional rollback, so keep
parallel execution off for recycle loops and other implicit couplings until
their transient contract is explicitly supported.
Python Dynamic Simulation
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()P&ID Valve-Action Dynamic Studies
Use this pattern when evaluating actions such as closing an outlet valve, opening a bypass, tripping a shutdown valve, or opening a drain/vent:
- Run and validate the steady-state base case.
- Define an event schedule with action type, affected valve, start time, and ramp duration.
- Map each P&ID valve to the correct NeqSim role: control valve, boundary switch, check-valve direction constraint, or blowdown/flare path.
- Run
process.runTransient(dt)for controller and inventory dynamics, or useneqsim-depressurization-mdmtfor dedicated blowdown/MDMT cases. - Save time series for pressure, level, temperature, valve position, flow, and any flare or vent stream.
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.
Depressurization / Blowdown
// 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 openingTransfer Function Blocks
For more advanced control logic:
import neqsim.process.controllerdevice.TransferFunctionBlock;
TransferFunctionBlock leadLag = new TransferFunctionBlock();
// Configure lead-lag, deadtime, filters as neededCommon Pitfalls
Before relying on a mixed transient flowsheet, inspect its state-ownership inventory separately from runtime activation and physics qualification:
neqsim.process.dynamics.DynamicCapabilityReport report =
neqsim.process.dynamics.DynamicCapabilityReport.from(process);
if (!report.isFullyAudited()) {
throw new IllegalStateException(report.getReviewItems().toString());
}
report.assertStrictTransientReady();All built-in process-element declarations of runTransient(double, UUID) are mapped, and CI rejects a new built-in
override unless it is classified or cites an explicit repository ADR. Custom/downstream overrides remain
UNCLASSIFIED_DYNAMIC until reviewed. isFullyAudited() is an inventory signal only: it does not certify numerical
stability, conservation, benchmark parity, controls performance, or safety suitability. In particular,
PipeBeggsAndBrills has distributed profile state but no conservative line-pack/storage term; do not use its capability
label to claim severe-slugging or liquid-rich transient validity. Use TwoFluidPipe only when the requested
configuration fits a numerically verified or experimentally qualified evidence-matrix row. Route failed or unsupported
severe-slugging, slug-statistics, and boundary-composition cases to a separately qualified model or controlled
experimental study.
For closed or near-static terrain initialization, the interior pressure operator is analytically well balanced for a
common mixture pressure: 90 flat/affine/curved, constant/smooth/alternating-density, 12--192-cell pure-gas/oil cases
and a separate 45-case pure-water extension remain below 1e-10 m/s2 with exactly zero local mass and energy rates.
Its signed energy correction is Q * deltaP
with the mass donor and its diagnostic is transaction-safe. This is numerical fixed-point evidence only. Do not use it
to qualify a two-density stationary pool, a moving interface, shutdown/restart, severe slugging, or pigging; those still
require their separate evidence-matrix gates.
For a gas/one-liquid circular section, the default-off
setStratifiedHydrostaticPressureMomentsEnabled(true) option adds the phase-density pressure moments to internal and
external momentum traction and the matching Q * F / A total-energy work with the mass donor. Exact phase endpoints,
closed-domain force telescoping, signed flow, rollback and default-off restoration are covered by
StratifiedPressureMomentCouplingTest. Simultaneous positive oil and water and diameter steps reject explicitly. This
completes an operator prerequisite only; do not route it as evidence of elapsed pool retention, shutdown/cooldown,
restart, severe slugging, hydraulic pigging or predictive evaporation.
For phase appearance, retain both face fractions in
GeometryCalculator.calculateStratifiedPressureMomentsFromHoldups(gas, liquid, diameter).
The minority-area inversion uses relative accuracy and algebraic reflection; do not pass the legacy
general holdup-to-depth approximation into pressure work, where its absolute area cutoff distorts traces.
The enabled historical 12/24-cell ramp currently stops at 0.25 s on the unchanged face-volume
closure guard. Keep the executable coupling rejection record attached; instantaneous operator
identities do not qualify full elapsed transient operation.
For an explicitly reduced-order dry-gas pig calculation, inspect
IdealGasPiggingModel. It advances immutable conservative upstream/downstream
gas inventories with PigMotion, moving-volume work and the existing
IdealGasReleaseModel bypass. Preserve the user-declared maximum time,
donor-mass fraction and pig-travel fraction controls, and inspect total mass,
combined energy, contact impact and timestep-refinement evidence. This is two
well-mixed calorically perfect gas chambers, not distributed pipe dynamics.
Launcher/receiver contact uses PigMotion.advanceToPosition; that API finds the
first crossing on either side of a stop/reversal and retains the analytical work
ledger, but the caller still owns the physical response to other position events.
The Hendrix 62 m/52 mm dimensions do not qualify synthetic pressures, friction
or bypass parameters. Do not infer TwoFluidPipe moving boundaries, liquid
sweeping, receiver surge capacity, physical restart, severe slugging, pig
trains or experimental/field validation.
- Always run steady state first: Call
process.run()beforerunTransient() - Timestep size: Start with 1.0 s, reduce if oscillating (0.1-0.5 s)
- Liquid-outlet level-controller direction: use
setReverseActing(false)so level up drives the outlet valve further open. Reverse acting on this configuration drives the loop the wrong way. - Controller windup: Large setpoint changes can cause integral windup
- Separator dimensions: Must set
setInternalDiameter()andsetSeparatorLength()for meaningful level dynamics. For dynamic simulation, set directly on the separator; for design purposes, configure viaSeparatorMechanicalDesign(see neqsim-api-patterns skill) - Measurement range: Set min/max on transmitters to match process range
- Enable dynamic (inventory) mode for level loops: after
process.run()(steady), callsetCalculateSteadyState(false)on the separator AND every valve, thenseparator.setLiquidLevel(startFraction), beforerunTransient. 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 valveCvis auto-derived from the steady solve. A liquid-outlet level valve issetReverseActing(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. - Physical-step UUIDs: never reuse one non-null UUID across multiple outer timesteps. Reuse is reserved for refinement/evaluation of one physical step.
Pluggable Integrator Strategies
ProcessSystem accepts a pluggable IntegratorStrategy, but this hook does not
by itself make the complete flowsheet a stiff vector ODE/DAE system. Implementations
live in neqsim.process.dynamics:
| Strategy | Class | Notes |
| Explicit Euler | ExplicitEulerIntegrator | Default; fast, conditionally stable |
| BDF-1 (Implicit Euler) | BDFIntegrator | Experimental local strategy. Newton + FD Jacobian; can fall back to explicit Euler when Newton diverges. Inspect lastStepFellBack() and do not treat fallback runs as qualified stiff/DAE evidence. |
import neqsim.process.dynamics.BDFIntegrator;
import neqsim.process.dynamics.ExplicitEulerIntegrator;
import neqsim.process.dynamics.IntegratorStrategy;
BDFIntegrator bdf = new BDFIntegrator();
process.setIntegratorStrategy(bdf);
// 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()).
A professional stiff-solver acceptance path must fail loudly or expose explicit fallback diagnostics; silent fallback is not an acceptable qualification result.
Event Scheduling (ESD, IOA, setpoint changes)
Time-stamped events (ESD trips, valve closures, setpoint ramps) are managed by
EventScheduler in neqsim.process.dynamics. ProcessSystem.runTransient(dt, id)
advances the process clock, then fires events with time <= currentTime 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); // fresh physical-step ID; due events fire automatically
}
int fired = events.getFiredEvents().size();
int pending = events.getPendingEvents().size();For event-aware/adaptive work, inspect event state without mutating it and checkpoint scheduler bookkeeping explicitly:
EventScheduler.Snapshot eventState = events.snapshot();
double nextEventTime = events.getNextEventTime();
java.util.List<EventScheduler.ScheduledEvent> due = events.getDueEvents(process.getTime());
// If a trial is rejected, restore pending/fired membership.
events.restore(eventState);restore(...) restores only scheduler pending/fired membership. It cannot undo
an already executed Runnable side effect. A rejected trial must defer external
actions until acceptance or restore every object those actions mutate as part of
the same transaction. This is required before safety/OTS event replay can be
called transactional.
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.
EventScheduler.Snapshot itself can be serialized when its event actions are
serializable, which is useful for explicit checkpoint/restart coordination.
New Measurement Devices (v3.11)
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);Use one physical pressure-, temperature- or flow-transmitter sample per control scan
For qualified controller or alarm studies, explicitly enable physical-step sampling on the concrete
PressureTransmitter, TemperatureTransmitter or VolumeFlowTransmitter and register it in the ProcessSystem. The
process samples once after events and field inputs and before equipment/controllers; controller, history and alarm
consumers share that held value. For a flow loop, use for example FT100.setUnit("kg/hr") followed by
FT100.setTransientSamplingEnabled(true). Declare the physical scan interval, configured phase/unit, filter time
constant, integer sample delay, noise basis and any sensor-fault rate. Flow conversions to kg/hr, m^3/hr,
Nm^3/day and Sm^3/day are frozen at the sample instant. Direct pressure/temperature reads retain the legacy
read-driven modifier path; direct flow reads remain raw. Treat a stuck or biased FT as a study failure mode, not an
independent safeguard. Vendor dynamics, alarm/trip integrity, SIL, voting and independence remain external evidence.
Safety Shutdown forward acoustic pump boundary
Use AcousticPumpBoundary with upstream WaterHammerPipe.BoundaryType.PUMP
for a declared single-liquid forward curve and actual positive-speed table.
Its constructor fits copied reference data through existing PumpChart, checks
monotonicity/domain/units and preserves Java restart state. Configure before
run(); changing the boundary requires explicit reinitialization. Actual-speed
and valve-travel knots use the same pipe clock. Use an explicit empty
eventSchedule for pump-only WaterHammerStudy JSON. Study output includes
aligned speed/inlet-flow/inlet-pressure/hydraulic-work histories. Unsupported
curve transitions return an error with last accepted state and internal outlet
history, never a successful truncated trip.
Follow the physical inputs, equations, public example and limits. Do not extrapolate clamped curves, turn stopped pumps into closed valves or claim a prescribed-speed adapter supplies shaft inertia, four-quadrant operation, check-valve disc/contact slam, NPSH/cavity physics, global rollback or integrated Safety Shutdown qualification. Existing canonical coordinator and ESD APIs own those integrations; do not create another scheduler or pressure-flow architecture.
Safety Shutdown ideal acoustic lift/contact handoff
The experimental AcousticLiftCheckValve couples declared translating mechanics
and signed jet/swept flow inside existing WaterHammerPipe substeps. Numerical
inelastic contact and local Java restart do not qualify continuous impact/grazing
timing, a full pump trip or global coordinator rollback. Keep the existing
physical-step/event identities and ESD chain; no parallel scheduler or solver.
Read the acoustic model, accepted histories and public evidence
and hand specialist hydraulic cases to neqsim-water-hammer. Asset/vendor,
phase-change, structural and integrated Safety Shutdown remain NOT_EVALUATED.
Cross-sectional gravitational energy budget
Use the existing GeometryCalculator.StratifiedPressureMoments derived centre-referenced phase
first moments [m3] and getGravityPotentialEnergyPerLength(gasDensity, liquidDensity, normalGravity)
[J/m] for an explicit static normal-gravity budget. No solver state or serialized field is added.
See the executed analytical/high-precision energy record.
This does not resolve the documented dry/wet pressure impulse or qualify dynamic pools, inlet refill
or hydrocarbon evaporation; those still require conservative phase-front transport/work and heat/mass transfer.
Optional Rusanov phase-flux contract
The default remains AUSM+. AUSMPlusFluxCalculator.calcRusanovFlux is an explicit local query,
and TwoFluidPipe.setRusanovInternalFluxEnabled(true) selects the qualified two-donor operator on
independent gas/oil/water internal faces after setEnableWaterOilSlip(true).
Its dissipated energy is alpha * (rho * H - p) with total specific enthalpy;
exact absence ignores unused properties and positive traces retain the same formula.
SRK gas/oil controls distinguish EOS-volume internal energy from physical-density mechanical
energy using explicit pressure-volume work; do not mix these API conventions in a reference.
See the executed conservative-flux evidence.
Forward and reverse donor streams remain separate through Runge-Kutta staging, pressure work and
transactional component transport; their difference must reproduce the hydrodynamic net. The
combined-liquid split, pressure interpolation, reconstructed slug faces and one-direction unsplit
complementarity route reject this option. Apply a wave-speed Courant bound and inspect component,
phase, total-energy and rollback reports.
Do not use it as proof of dry/wet fronts, terrain pools, refill or physical evaporation.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

