Dynamic Equipment Implementation
Use this skill when a NeqSim equipment class needs real dynamic simulation support instead of the default steady-state fallback or unsupported transient behavior.
This complements neqsim-dynamic-simulation, which explains how to run dynamic studies. This skill explains how to implement and test the Java code that makes equipment participate correctly in ProcessSystem.runTransient(dt, id).
Local Architecture
SimulationInterface.runTransient(double dt, UUID id) defaults to steady-state run(id) when getCalculateSteadyState() is true, otherwise throws UnsupportedOperationException.
ProcessSystem.runTransient(dt, id) steps each ProcessEquipmentInterface through a skip-aware wrapper and calls unit.runTransient(dt, id).
- Equipment that has real dynamic behavior must override
runTransient(double dt, UUID id).
- Existing reference implementations include
Separator, ThreePhaseSeparator, Tank, Compressor, ThrottlingValve, SafetyValve, BlowdownValve, PipeBeggsAndBrills, WaterHammerPipe, SimpleReservoir, and WellFlow.
- All dynamic equipment code must remain Java 8 compatible, use Log4j2 for logging, and pass Spotless formatting.
Implementation Decision Tree
Is the equipment purely algebraic?
- Examples: simple pressure drop, splitter ratio, ideal heater with no metal/fluid holdup.
- Implement only if needed to propagate controller or actuator dynamics; otherwise the steady-state fallback may be sufficient.
Does the equipment hold mass or energy inventory?
- Examples: separator, tank, column tray/section, adsorber bed, exchanger volume, pipe segment, reactor, accumulator.
- Add explicit volume/holdup state and integrate component moles and internal energy over
dt.
Does the equipment have actuator or mechanical state?
- Examples: compressor shaft speed, valve opening, pump ramp, fan speed, bed switching, recycle valve position.
- Add bounded state variables, ramp-rate limits, and controller hooks.
Does the equipment need thermal inertia?
- Examples: heat exchangers, heaters/coolers, fired heaters, reactors, pipes.
- Add fluid volume and optional wall/metal heat capacity. Preserve energy balance and avoid instant outlet jumps unless the equipment is intentionally algebraic.
Required Code Pattern
For any new transient implementation:
- Add serializable state fields with defaults that preserve existing steady-state behavior.
- Add public setters/getters for dynamic configuration such as volume, residence time, metal mass, ramp rate, or initial inventory.
- Override
runTransient(double dt, UUID id).
- Keep the steady-state branch first:
- If
getCalculateSteadyState() is true, call run(id), increaseTime(dt), set/finish the calculation identifier consistently with the class pattern, and return.
- Initialize transient state lazily on the first dynamic step from the last steady-state solution.
- Run controllers or actuator logic before solving the equipment response when the class exposes controller behavior.
- Integrate component and energy balances over the timestep:
- Accumulation = inlet rates minus outlet rates plus generation or consumption.
- Energy accumulation = inlet enthalpy rates minus outlet enthalpy rates plus heat/work terms.
- Clamp tiny negative mole inventories to zero; do not allow negative total volume, negative pressure, or invalid phase amounts.
- Re-flash the updated inventory with the appropriate flash operation (
TPflash, TVflash, PHflash, or VUflash) and call initProperties() or the class-equivalent physical-property initialization before reading transport properties.
- Update every outlet stream from the new state and preserve mass/energy consistency.
- Call
increaseTime(dt) and set the calculation identifier before returning.
Equipment Configuration Guidance
Dynamic behavior needs physical capacity. Prefer existing mechanical-design data when it already exists, but expose a simple runtime configuration path for simulations:
- Vessel-like equipment: volume, diameter, length, liquid level, gas headspace, max/min operating pressure.
- Heat-transfer equipment: process-side volume, optional utility-side volume, UA, metal mass, metal heat capacity, ambient heat loss.
- Rotating equipment: inertia, rated speed, ramp rates, driver power limit, recycle or minimum-flow logic.
- Valves and dampers: opening fraction, stroke time, fail action, Cv/characteristic, controller attachment.
- Pipes: length, diameter, roughness, elevation profile, segment inventory, heat-transfer environment.
- Reactors and beds: bed volume, void fraction, catalyst/solid heat capacity, reaction source terms, residence time.
Do not hide required dynamic capacity behind mechanical design only. Task notebooks and MCP workflows need direct, documented setters for dynamic parameters.
Testing Requirements
Create focused JUnit 5 tests under the matching package in src/test/java/neqsim/....
Minimum test coverage for each dynamic implementation:
- Steady-state compatibility:
runTransient(dt, id) with calculateSteadyState=true matches or preserves the existing run(id) behavior.
- Dynamic branch executes: with
calculateSteadyState=false, runTransient(dt, id) does not throw and advances equipment time.
- Inventory response: a flow, heat, pressure, valve-opening, speed, or level disturbance changes a physically relevant state in the expected direction.
- Mass balance: integrated inlet minus outlet accumulation matches inventory change within a reasonable tolerance.
- Energy or temperature response: when relevant, heat/work input changes internal energy, outlet temperature, or pressure consistently.
- Controller interaction: if the equipment has controllers, verify one timestep calls/runs controller logic and applies bounded output.
- Bounds and robustness: zero/low flow, empty inventory, small
dt, and repeated timesteps remain finite and non-negative.
- Serialization/copy safety: if new fields are non-serializable, mark them
transient; otherwise verify normal equipment copy behavior still works.
Use physical assertions, not private implementation details. Prefer monotonic or bounded assertions over fragile exact transient values unless a regression baseline is intentional.
Validation Commands
After editing Java files in the NeqSim repo, run:
mvnw.cmd spotless:apply
mvnw.cmd test "-Dtest=YourDynamicTest"
mvnw.cmd spotless:check
When public APIs or JavaDoc were added or changed, also run:
mvnw.cmd javadoc:javadoc
When only agent/skill files were edited, run:
python devtools/verify_skills_agents.py
python devtools/generate_agent_skill_map.py
python devtools/verify_skills_agents.py
Common Pitfalls
For TwoFluidPipe and related finite-volume code, include all three phase inventories in
rejection/positivity checks, even though the class name says two-fluid. Preserve phase identity
at exact water-cut endpoints and separate phase momenta through primitive/conservative recovery.
Distinguish explicit caller velocities from internal recovery: test legacy bulk updates after
recovery, recovered slip, and zero-to-positive phase appearance without overwriting donor momentum.
Pressure-correction face fluxes must share each donor phase's inventory budget. A positivity repair
that moves mass between phases can hide a phase-balance defect behind an exact total-mass balance.
Source-splitting helpers must not run primitive recovery on an invalid trial before its rejection
check; exercise the same negative-phase test with stiff source terms enabled.
Rollback must restore configured closures and their accepted diagnostics, pressure and phase
densities as well as the state vector; a generic clone may discard transient closure fields.
Pressure and density must use the same applied correction after bounds. Independent cell clipping
can reverse Newton face directions and make an upwind active set cycle; test bounded convergence.
A substep budget
must never enlarge a CFL-limited step or silently truncate a requested interval; compare accepted
time with both equipment and solver clocks. Tracker overlay inventory is separate from Eulerian
inventory, so dissolution must not return mass that initialization never withdrew. Read
docs/wiki/two_fluid_reporting_and_validation.md for the distinction between numerical regression
coverage and currently disabled experimental qualification gates.
Conservative slug/film coupling uses subcell reconstruction with the Eulerian seven-variable
state as the sole inventory. Assert that every reconstructed variable averages back to its cell
value, including energy after momentum redistribution. Enthalpy reference shifts must not change
the reconstruction. Recover positive trace-phase velocities from their exact momentum/mass
ratios so primitive recovery thresholds cannot alter the numerical flux. Use one shared face
flux per phase, with independent oil/water donors when slip is enabled, and cancel the pressure
part using the same phase face holdups and pressures. Test gas-free and one-cell limits explicitly.
Subcell face holdup can greatly exceed its cell average: include the phase-inventory draining
time in the CFL limit, not only reconstructed velocities. Test a thin body crossing a cell face.
Closed boundaries must constrain the external flux after reconstruction; zeroing cell velocities
alone does not prevent a reconstructed slug from leaking through a valve. Prescribed feeds
belong on external face states, not in the evolving cell density/momentum/holdup inventory.
For total energy, transport phase enthalpy plus kinetic energy, include gravitational work,
and do not add stationary-wall friction dissipation as an external energy source.
Independent experimental comparisons must retain source-cell provenance, source-definition
ambiguities, missing predictions and predeclared tolerances. A prescribed-flow marker experiment
tests a closure; label an actual time-marching pipe comparison separately. Coordinate source
freezes across agents before builds because desktop background compilers can modify target/classes.
Test initially volume-exact alternating pressure and velocity modes before claiming convective-CFL
stability of a collocated pressure correction. Include pressure-correction face transfers in
component/thermal transport after acceptance; global energy conservation alone does not establish
local EOS/pressure-work consistency. Record slug crossings during accepted motion and distinguish
union-occupancy changes from internal endpoints or instantaneous merge/birth geometry.
Reject nonfinite or negative raw phase predictors even when adaptive retry is disabled;
fixed stepping must fail at the last accepted state instead of using a positivity repair
to hide lost phase inventory. Separate clock-resolution/invalid-step failures from actual
attempt-budget exhaustion. A configured minimum timestep must never enlarge a CFL bound.
Pressure-solve convergence must include prescribed boundary pressure as well as volume
closure, and reset/failed steps must clear stale correction results and transfer ledgers.
Exercise five-/six-variable legacy inputs after a populated seven-variable state, including
zero and trace liquid. Do not suppress positive trace holdup in an interior flux while
external faces still transport it. For variable areas, match the momentum flux's face area
in the geometric pressure source. At either boundary, preserve each positive phase holdup
directly; subtracting a nearly unit gas fraction can round a trace liquid to zero. Evaluate
all tracked-interface kinematics against the
same accepted geometry before moving any marker; count reverse inlet exits separately
from downstream outlet arrivals without depositing overlay mass into Eulerian cells.
- Forgetting to set
calculateSteadyState=false in tests, so the test never exercises the new dynamic branch.
- Adding a dynamic branch that calls
run(id) internally every timestep and erases the inventory state.
- Reading viscosity, density, or thermal conductivity after a flash without physical-property initialization.
- Updating outlet streams but not the internal inventory, or updating inventory but leaving outlet streams stale.
- Re-flashing every outlet stream after an inventory flash. For phase-separating equipment, rebuild outlets from the
freshly flashed inventory phases, preserve configured valve-capacity flow rates, and initialize properties without
another equilibrium flash. This keeps outlets current and avoids multiplying expensive CPA flashes per timestep.
- Introducing Java 9+ syntax such as
var, List.of, Map.of, text blocks, or String.repeat.
- Adding
System.out.println in tests or examples; use assertions or Log4j2 logger output.
- Adding non-serializable fields to equipment without
transient.
Handoff Checklist
Before considering a dynamic equipment implementation complete, confirm:
- The equipment has a documented physical capacity model or a documented reason why it remains algebraic.
runTransient(dt, id) has both steady-state fallback and real dynamic branch behavior.
- New setters/getters have JavaDoc and Java 8-compatible signatures.
- Tests exercise the real dynamic branch and at least one process-level
ProcessSystem.runTransient(dt, id) path when practical.
- Spotless and the focused test pass.
1---2name: neqsim-dynamic-equipment-implementation3description: Implement and test NeqSim process-equipment runTransient support. USE WHEN: adding dynamic simulation behavior to any ProcessEquipmentInterface class, including inventory volume, pressure/level/temperature state, controller response, and JUnit regression tests for transient timesteps.4---56# Dynamic Equipment Implementation78Use this skill when a NeqSim equipment class needs real dynamic simulation support instead of the default steady-state fallback or unsupported transient behavior.910This complements `neqsim-dynamic-simulation`, which explains how to run dynamic studies. This skill explains how to implement and test the Java code that makes equipment participate correctly in `ProcessSystem.runTransient(dt, id)`.1112## Local Architecture1314- `SimulationInterface.runTransient(double dt, UUID id)` defaults to steady-state `run(id)` when `getCalculateSteadyState()` is true, otherwise throws `UnsupportedOperationException`.15- `ProcessSystem.runTransient(dt, id)` steps each `ProcessEquipmentInterface` through a skip-aware wrapper and calls `unit.runTransient(dt, id)`.16- Equipment that has real dynamic behavior must override `runTransient(double dt, UUID id)`.17- Existing reference implementations include `Separator`, `ThreePhaseSeparator`, `Tank`, `Compressor`, `ThrottlingValve`, `SafetyValve`, `BlowdownValve`, `PipeBeggsAndBrills`, `WaterHammerPipe`, `SimpleReservoir`, and `WellFlow`.18- All dynamic equipment code must remain Java 8 compatible, use Log4j2 for logging, and pass Spotless formatting.1920## Implementation Decision Tree21221. **Is the equipment purely algebraic?**23 - Examples: simple pressure drop, splitter ratio, ideal heater with no metal/fluid holdup.24 - Implement only if needed to propagate controller or actuator dynamics; otherwise the steady-state fallback may be sufficient.25262. **Does the equipment hold mass or energy inventory?**27 - Examples: separator, tank, column tray/section, adsorber bed, exchanger volume, pipe segment, reactor, accumulator.28 - Add explicit volume/holdup state and integrate component moles and internal energy over `dt`.29303. **Does the equipment have actuator or mechanical state?**31 - Examples: compressor shaft speed, valve opening, pump ramp, fan speed, bed switching, recycle valve position.32 - Add bounded state variables, ramp-rate limits, and controller hooks.33344. **Does the equipment need thermal inertia?**35 - Examples: heat exchangers, heaters/coolers, fired heaters, reactors, pipes.36 - Add fluid volume and optional wall/metal heat capacity. Preserve energy balance and avoid instant outlet jumps unless the equipment is intentionally algebraic.3738## Required Code Pattern3940For any new transient implementation:41421. Add serializable state fields with defaults that preserve existing steady-state behavior.432. Add public setters/getters for dynamic configuration such as volume, residence time, metal mass, ramp rate, or initial inventory.443. Override `runTransient(double dt, UUID id)`.454. Keep the steady-state branch first:46 - If `getCalculateSteadyState()` is true, call `run(id)`, `increaseTime(dt)`, set/finish the calculation identifier consistently with the class pattern, and return.475. Initialize transient state lazily on the first dynamic step from the last steady-state solution.486. Run controllers or actuator logic before solving the equipment response when the class exposes controller behavior.497. Integrate component and energy balances over the timestep:50 - Accumulation = inlet rates minus outlet rates plus generation or consumption.51 - Energy accumulation = inlet enthalpy rates minus outlet enthalpy rates plus heat/work terms.52 - Clamp tiny negative mole inventories to zero; do not allow negative total volume, negative pressure, or invalid phase amounts.538. Re-flash the updated inventory with the appropriate flash operation (`TPflash`, `TVflash`, `PHflash`, or `VUflash`) and call `initProperties()` or the class-equivalent physical-property initialization before reading transport properties.549. Update every outlet stream from the new state and preserve mass/energy consistency.5510. Call `increaseTime(dt)` and set the calculation identifier before returning.5657## Equipment Configuration Guidance5859Dynamic behavior needs physical capacity. Prefer existing mechanical-design data when it already exists, but expose a simple runtime configuration path for simulations:6061- Vessel-like equipment: volume, diameter, length, liquid level, gas headspace, max/min operating pressure.62- Heat-transfer equipment: process-side volume, optional utility-side volume, UA, metal mass, metal heat capacity, ambient heat loss.63- Rotating equipment: inertia, rated speed, ramp rates, driver power limit, recycle or minimum-flow logic.64- Valves and dampers: opening fraction, stroke time, fail action, Cv/characteristic, controller attachment.65- Pipes: length, diameter, roughness, elevation profile, segment inventory, heat-transfer environment.66- Reactors and beds: bed volume, void fraction, catalyst/solid heat capacity, reaction source terms, residence time.6768Do not hide required dynamic capacity behind mechanical design only. Task notebooks and MCP workflows need direct, documented setters for dynamic parameters.6970## Testing Requirements7172Create focused JUnit 5 tests under the matching package in `src/test/java/neqsim/...`.7374Minimum test coverage for each dynamic implementation:75761. **Steady-state compatibility:** `runTransient(dt, id)` with `calculateSteadyState=true` matches or preserves the existing `run(id)` behavior.772. **Dynamic branch executes:** with `calculateSteadyState=false`, `runTransient(dt, id)` does not throw and advances equipment time.783. **Inventory response:** a flow, heat, pressure, valve-opening, speed, or level disturbance changes a physically relevant state in the expected direction.794. **Mass balance:** integrated inlet minus outlet accumulation matches inventory change within a reasonable tolerance.805. **Energy or temperature response:** when relevant, heat/work input changes internal energy, outlet temperature, or pressure consistently.816. **Controller interaction:** if the equipment has controllers, verify one timestep calls/runs controller logic and applies bounded output.827. **Bounds and robustness:** zero/low flow, empty inventory, small `dt`, and repeated timesteps remain finite and non-negative.838. **Serialization/copy safety:** if new fields are non-serializable, mark them `transient`; otherwise verify normal equipment copy behavior still works.8485Use physical assertions, not private implementation details. Prefer monotonic or bounded assertions over fragile exact transient values unless a regression baseline is intentional.8687## Validation Commands8889After editing Java files in the NeqSim repo, run:9091```powershell92mvnw.cmd spotless:apply93mvnw.cmd test "-Dtest=YourDynamicTest"94mvnw.cmd spotless:check95```9697When public APIs or JavaDoc were added or changed, also run:9899```powershell100mvnw.cmd javadoc:javadoc101```102103When only agent/skill files were edited, run:104105```powershell106python devtools/verify_skills_agents.py107python devtools/generate_agent_skill_map.py108python devtools/verify_skills_agents.py109```110111## Common Pitfalls112113For `TwoFluidPipe` and related finite-volume code, include all three phase inventories in114rejection/positivity checks, even though the class name says two-fluid. Preserve phase identity115at exact water-cut endpoints and separate phase momenta through primitive/conservative recovery.116Distinguish explicit caller velocities from internal recovery: test legacy bulk updates after117recovery, recovered slip, and zero-to-positive phase appearance without overwriting donor momentum.118Pressure-correction face fluxes must share each donor phase's inventory budget. A positivity repair119that moves mass between phases can hide a phase-balance defect behind an exact total-mass balance.120Source-splitting helpers must not run primitive recovery on an invalid trial before its rejection121check; exercise the same negative-phase test with stiff source terms enabled.122Rollback must restore configured closures and their accepted diagnostics, pressure and phase123densities as well as the state vector; a generic clone may discard transient closure fields.124Pressure and density must use the same applied correction after bounds. Independent cell clipping125can reverse Newton face directions and make an upwind active set cycle; test bounded convergence.126A substep budget127must never enlarge a CFL-limited step or silently truncate a requested interval; compare accepted128time with both equipment and solver clocks. Tracker overlay inventory is separate from Eulerian129inventory, so dissolution must not return mass that initialization never withdrew. Read130`docs/wiki/two_fluid_reporting_and_validation.md` for the distinction between numerical regression131coverage and currently disabled experimental qualification gates.132133Conservative slug/film coupling uses subcell reconstruction with the Eulerian seven-variable134state as the sole inventory. Assert that every reconstructed variable averages back to its cell135value, including energy after momentum redistribution. Enthalpy reference shifts must not change136the reconstruction. Recover positive trace-phase velocities from their exact momentum/mass137ratios so primitive recovery thresholds cannot alter the numerical flux. Use one shared face138flux per phase, with independent oil/water donors when slip is enabled, and cancel the pressure139part using the same phase face holdups and pressures. Test gas-free and one-cell limits explicitly.140Subcell face holdup can greatly exceed its cell average: include the phase-inventory draining141time in the CFL limit, not only reconstructed velocities. Test a thin body crossing a cell face.142Closed boundaries must constrain the external flux after reconstruction; zeroing cell velocities143alone does not prevent a reconstructed slug from leaking through a valve. Prescribed feeds144belong on external face states, not in the evolving cell density/momentum/holdup inventory.145For total energy, transport phase enthalpy plus kinetic energy, include gravitational work,146and do not add stationary-wall friction dissipation as an external energy source.147Independent experimental comparisons must retain source-cell provenance, source-definition148ambiguities, missing predictions and predeclared tolerances. A prescribed-flow marker experiment149tests a closure; label an actual time-marching pipe comparison separately. Coordinate source150freezes across agents before builds because desktop background compilers can modify target/classes.151Test initially volume-exact alternating pressure and velocity modes before claiming convective-CFL152stability of a collocated pressure correction. Include pressure-correction face transfers in153component/thermal transport after acceptance; global energy conservation alone does not establish154local EOS/pressure-work consistency. Record slug crossings during accepted motion and distinguish155union-occupancy changes from internal endpoints or instantaneous merge/birth geometry.156157Reject nonfinite or negative raw phase predictors even when adaptive retry is disabled;158fixed stepping must fail at the last accepted state instead of using a positivity repair159to hide lost phase inventory. Separate clock-resolution/invalid-step failures from actual160attempt-budget exhaustion. A configured minimum timestep must never enlarge a CFL bound.161Pressure-solve convergence must include prescribed boundary pressure as well as volume162closure, and reset/failed steps must clear stale correction results and transfer ledgers.163Exercise five-/six-variable legacy inputs after a populated seven-variable state, including164zero and trace liquid. Do not suppress positive trace holdup in an interior flux while165external faces still transport it. For variable areas, match the momentum flux's face area166in the geometric pressure source. At either boundary, preserve each positive phase holdup167directly; subtracting a nearly unit gas fraction can round a trace liquid to zero. Evaluate168all tracked-interface kinematics against the169same accepted geometry before moving any marker; count reverse inlet exits separately170from downstream outlet arrivals without depositing overlay mass into Eulerian cells.171172- Forgetting to set `calculateSteadyState=false` in tests, so the test never exercises the new dynamic branch.173- Adding a dynamic branch that calls `run(id)` internally every timestep and erases the inventory state.174- Reading viscosity, density, or thermal conductivity after a flash without physical-property initialization.175- Updating outlet streams but not the internal inventory, or updating inventory but leaving outlet streams stale.176- Re-flashing every outlet stream after an inventory flash. For phase-separating equipment, rebuild outlets from the177 freshly flashed inventory phases, preserve configured valve-capacity flow rates, and initialize properties without178 another equilibrium flash. This keeps outlets current and avoids multiplying expensive CPA flashes per timestep.179- Introducing Java 9+ syntax such as `var`, `List.of`, `Map.of`, text blocks, or `String.repeat`.180- Adding `System.out.println` in tests or examples; use assertions or Log4j2 logger output.181- Adding non-serializable fields to equipment without `transient`.182183## Handoff Checklist184185Before considering a dynamic equipment implementation complete, confirm:186187- The equipment has a documented physical capacity model or a documented reason why it remains algebraic.188- `runTransient(dt, id)` has both steady-state fallback and real dynamic branch behavior.189- New setters/getters have JavaDoc and Java 8-compatible signatures.190- Tests exercise the real dynamic branch and at least one process-level `ProcessSystem.runTransient(dt, id)` path when practical.191- Spotless and the focused test pass.