Skip to main content

neqsim-dynamic-simulation

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.

소스 정보

저장소
equinor/neqsim
최근 소스 활동
2026년 11월 9일 03:42
감지된 SKILL.md 언어
영어
스타
168
포크
78

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
neqsim-dynamic-simulation
description
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.
last_verified
2026-10-27
# 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](../../../docs/process/equipment/heat_exchanger_automation.md). ## 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: 1. Flowsheet-wide settings are applied. Each setter unit receives exactly one transient call, applying its specification and advancing its own clock once. 2. Simulation time advances, then due events and field inputs are applied. 3. 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. 4. 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. `ControllerDeviceBaseClass` also makes repeated calls with one identifier idempotent, including semi-implicit equipment passes. Custom controllers integrated by equipment must implement the same identifier contract through `hasRunTransient(UUID)`. 5. 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](../../../docs/process/dynamic-capability-contract.md#opt-in-numerical-adaptive-acceptance) for the norm, provider contract, provenance, event and qualification boundaries. ## Basic Dynamic Setup ```java 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](../../../docs/process/TWOFLUIDPIPE_MODEL.md#opt-in-conservative-transient-thermodynamic-recovery) 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 ```java // 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 ```java // 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. ```java 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: ```java // 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 `setLiquidLevel` after `run()`** — a steady-state solve resets the level, so set the starting level and geometry after the first `run()`. - **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 `Kp` and long `Ti` (see the tuning table) and consider an SP-PV deadband only with care (see the limit-cycle note above). After the run, use `ControllerPerformanceMetrics.fromEventLog(LC100.getEventLog())` (or `LC100.getPerformanceMetrics()`) to score the tuning (IAE/ISE/ITAE, PV variability, valve travel and reversals, settling time) — see the KPI section below. ### 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: 1. Configure separator diameter, length and starting level, then disable steady-state calculation. 2. Put a gas pressure-control valve on the outlet with a lower declared downstream pressure, actuator lag and physical-step idempotency enabled. 3. Set the PCV embedded controller to manual when an external PID owns the command. 4. Register a physical-step-sampled `PressureTransmitter` on the separator gas outlet and attach an engineering-unit, direct-acting PID to the PCV. 5. 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](../../../docs/process/dynamic-capability-contract.md#opt-in-separator-cumulative-energy-diagnostics) 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
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기