Live safety source terms from process models
SourceTermSession connects a caller-owned NeqSim process to the
release-flow models and neutral exchange contract.
It supports a single ProcessSystem and an area-based ProcessModel without an external safety
software runtime. New APIs are additive. The independent legacy gas blowdown integration was
corrected for inventory/energy conservation in #3905; see the
compatibility note.
Single process and live capture
These calls are executed in SourceTermSessionTest.singleProcessDocumentationAndExternalLiveCapture.
The example assumes a configured process containing a stream named feed.
SourceTermSession session = new SourceTermSession("study-1", process);
session.addSource("feed-opening", "feed", 0.01, 0.62, 101325.0,
new HomogeneousEquilibriumReleaseModel());
List<SourceTermFrame> delivered = new ArrayList<SourceTermFrame>();
Consumer<SourceTermFrame> listener = delivered::add;
session.subscribe(listener);
List<SourceTermFrame> initial = session.runSteadyState();
// An external owner can update inputs and solve before capture.
UUID externalId = UUID.randomUUID();
feed.setPressure(4.5, "bara");
process.run(externalId);
List<SourceTermFrame> current = session.capture(
Collections.singletonMap(SourceTermSession.SINGLE_AREA, externalId));
session.unsubscribe(listener);
The short registration overload samples the named unit’s getFluid(): a stream’s flowing fluid,
or a separator’s vessel fluid. Check the particular equipment API before interpreting its fluid
as inventory. Registration verifies area/unit ownership and opening geometry. Replacing an area or
unit object after registration requires a new session; the previous binding becomes stale.
Use toNdjson() on each delivered frame for a self-contained live record. A receiver can write to
a file, application queue or authenticated transport. Delivery is synchronous: slow receivers
slow the caller and no unbounded background queue is created. A receiver exception is logged and
counted by getDeliveryFailureCount(); other receivers and the returned batch remain available.
Delivery is attempted once, with no hidden retry. Use sequence numbers to detect gaps and define
retry/deduplication in the application. For long-running use, replace the example’s growing list
with a bounded queue or streaming sink and choose an explicit overflow policy.
Callbacks must not mutate the process. Session operations cannot be reentered from callbacks. The caller must serialize all access to the process, including field-input updates and other references to the same model. Synchronizing the session cannot make external model mutation safe.
ProcessModel and dynamic operation
The area-qualified overload takes outletIndex = -1 for equipment fluid, or a zero-based index
from getOutletStreams() for an explicitly selected outlet. The following APIs are exercised in
processModelDocumentationPreservesAreaIdentityAndStepsComposition.
SourceTermSession session = new SourceTermSession("study-1", model);
session.addSource("a-opening", "train-a", "feed", -1, 0.01, 0.62, 101325.0,
new HomogeneousEquilibriumReleaseModel());
session.addSource("b-opening", "train-b", "feed", -1, 0.01, 0.62, 101325.0,
new HomogeneousEquilibriumReleaseModel());
List<SourceTermFrame> initial = session.runSteadyState();
List<SourceTermFrame> next = session.step(0.25);
Steady operation calls ProcessModel.run(), preserving its cross-area convergence algorithm.
Each area may have a different actual calculation UUID. The frame’s calculation UUID correlates
the sampling batch; areaCalculationId and equipmentCalculationId preserve underlying execution
identity. Transient operation calls the native runTransient(dt, id) and requires aligned area
clocks and completed area IDs. All sources are cloned before any release model is evaluated.
Frames are returned and delivered in registration order, with a monotonically increasing sequence
across the session.
Configure each equipment’s actual transient behavior before stepping. The session does not
turn an algebraic equipment model into a dynamic one. The real-vessel regression
realSeparatorInventoryDynamicsReachSourceFramesForBothContainers uses a separator with
setCalculateSteadyState(false), applies heat, advances its volume/internal-energy dynamics and
checks that vessel and gas-outlet source frames reproduce the changed thermodynamic state.
It runs for both containers. Another regression changes feed composition between steps.
Simulation time is read from the process, never inferred from wall-clock elapsed time. Positive finite timesteps are required. Wall-clock pacing, deadlines and transport timeouts belong to the application; this API makes no hard real-time guarantee.
Failure and freshness semantics
| Condition | Result |
|---|---|
| Successful process and release calculation | Current source payload; inspect model diagnostics and evidence level. |
| Release model fails for one source | That source is invalid; other registered sources can succeed. |
| Process run or transient step fails | Diagnostic frames without source payload; session becomes faulted. |
| External UUID, area set, ownership, activity or clocks mismatch | Stale frames without source payload. |
| Direct stream inputs changed since its calculation | That source is stale until solved again. |
| Hypothetical opening disabled | Disabled frame; no numeric source payload. |
| Receiver fails | Delivery-failure count increases; other receivers continue. |
After an owned process failure, step and capture refuse further work.
Native transient execution can mutate some equipment and clocks before failing; this adapter
does not claim transactional rollback. Failure-frame simulation time is the last coherent
session time, not the partially advanced process time. Restore/reinitialize the process state,
then call runSteadyState() to recover, or create a new session after validated restoration.
A steady rerun alone is not a rollback of dynamic inventory.
External capture requires the UUID of each successfully completed area calculation. For a model, supply a map keyed by its exact area names. The external owner remains responsible for successful execution, aggregate convergence and synchronization. Never pass a retained old UUID after an external failure: arbitrary in-place equipment mutation cannot always be detected. Equipment-owned outlet freshness follows its owner’s calculation contract; an outlet’s own UUID is recorded where available but is not assumed to equal the area UUID. Some native units use separate outlet flash identifiers. When an already captured area UUID is reused, current stream inputs are compared against the captured inputs. Native transient pressure feedback is retained in that captured state; it is not mistaken for a later external input edit. Direct source-stream input-cache changes are additionally checked. Zero-flow direct streams without a current calculable state produce stale frames.
Consumers must also enforce their own maximum wall-clock age and delivery timeout. A schema status of valid does not guarantee a recently received frame is still current on a remote receiver.
Physical meaning and isolation
Each source registered with addSource is an instantaneous hypothetical opening evaluated from the sampled fluid.
Use addLongPipeSource for a hypothetical one-sided pipe segment; it retains explicit length
and Darcy friction in every immutable request and exported frame. Select a finite-pipe model
explicitly—short-opening models reject this geometry.
The source does not remove mass or energy from the connected process. Its provenance explicitly
records HYPOTHETICAL_OPENING_NO_INVENTORY_FEEDBACK. To calculate a depleting release, model the
physical discharge and inventory balance in the process; sample the resulting trajectory and
check component/energy conservation and timestep sensitivity. Do not apply this source rate as
an additional loss when the process already includes that discharge.
For opt-in two-way depletion, use ReleaseInventory and addInventorySource.
The inventory removes component mass and stagnation enthalpy during native process stepping;
the session exports the updated instantaneous source and cumulative balance provenance.
The compatibility path reports COUPLED_RIGID_ADIABATIC_GAS_INVENTORY. The explicit
phase-selected overload reports COUPLED_RIGID_ADIABATIC_PHASE_SELECTED_INVENTORY plus
inventoryWithdrawalPhase. Both are bounded to a rigid adiabatic equilibrium inventory and
have no automatic entrainment, phase or release-model fallback.
A sampled mass rate is not a timestep average or an integrated release mass. Retain the initial frame, integrate only over valid intervals using a documented quadrature, and refine timesteps around openings, closures and phase transitions. Do not interpolate across failed, stale or disabled intervals as if their missing values were zero.
setEnabled(sourceId, false) disables source export only, including for a coupled inventory.
Use ReleaseInventory.setReleaseEnabled(false) to stop its physical withdrawal. An upstream shutdown or
isolation valve closing does not empty trapped inventory or necessarily stop an existing leak.
Represent those actions in the process model and use its event scheduling/control facilities.
Specify source position/orientation through SourceTermFrame.withLocation when spatial boundary
conditions are needed. No coordinates, isolation assumptions, weather or consequence models
are inferred.
Validation and limits
Run SourceTermSessionTest, SourceTermFrameTest, ReleaseFlowModelTest and LeakModelTest,
then devtools/validate_source_term_contract.py. The tests cover both containers, real vessel
transients, evolving composition, per-area provenance, external capture, fail-closed lifecycle,
invalid steps, disabled openings and callback isolation. These are synthetic software regressions,
not independent engineering qualification.
The selected homogeneous-equilibrium model retains its documented applicability and limitations, including unsupported solid-risk cases and invalid unresolved mixture flashes. No automatic fallback to a different model is performed. This implementation supplies an auditable foundation; relaxation/non-equilibrium discharge models, multiphase inventory coupling and experimental qualification require separately reviewed extensions. Uncertainty ensembles evaluate complete joint inputs after the corresponding process states have been solved. The architecture and foundational APIs have merged; that does not establish qualification. The completion status tracks remaining work, including the Colab demonstration.