neqsim-dynamic-equipment-implementation · git:20260709.4a016a7 · 2026-07-09 · sha256 2b3aaf97257c5efb

neqsim-dynamic-equipment-implementation git:20260709.4a016a7A

Immutable. This exact content is served forever at /api/v1/blob/2b3aaf97257c5efb.

---
name: neqsim-dynamic-equipment-implementation
description: "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."
last_verified: "2026-06-25"
---

# 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

1. **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.

2. **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`.

3. **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.

4. **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:

1. Add serializable state fields with defaults that preserve existing steady-state behavior.
2. Add public setters/getters for dynamic configuration such as volume, residence time, metal mass, ramp rate, or initial inventory.
3. Override `runTransient(double dt, UUID id)`.
4. 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.
5. Initialize transient state lazily on the first dynamic step from the last steady-state solution.
6. Run controllers or actuator logic before solving the equipment response when the class exposes controller behavior.
7. 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.
8. 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.
9. Update every outlet stream from the new state and preserve mass/energy consistency.
10. 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:

1. **Steady-state compatibility:** `runTransient(dt, id)` with `calculateSteadyState=true` matches or preserves the existing `run(id)` behavior.
2. **Dynamic branch executes:** with `calculateSteadyState=false`, `runTransient(dt, id)` does not throw and advances equipment time.
3. **Inventory response:** a flow, heat, pressure, valve-opening, speed, or level disturbance changes a physically relevant state in the expected direction.
4. **Mass balance:** integrated inlet minus outlet accumulation matches inventory change within a reasonable tolerance.
5. **Energy or temperature response:** when relevant, heat/work input changes internal energy, outlet temperature, or pressure consistently.
6. **Controller interaction:** if the equipment has controllers, verify one timestep calls/runs controller logic and applies bounded output.
7. **Bounds and robustness:** zero/low flow, empty inventory, small `dt`, and repeated timesteps remain finite and non-negative.
8. **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:

```powershell
mvnw.cmd spotless:apply
mvnw.cmd test "-Dtest=YourDynamicTest"
mvnw.cmd spotless:check
```

When public APIs or JavaDoc were added or changed, also run:

```powershell
mvnw.cmd javadoc:javadoc
```

When only agent/skill files were edited, run:

```powershell
python devtools/verify_skills_agents.py
python devtools/generate_agent_skill_map.py
python devtools/verify_skills_agents.py
```

## Common Pitfalls

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