Skip to the content.

Pipeline Recipes

Copy-paste solutions for pipeline calculations and multiphase flow.

Table of Contents


Simple Pressure Drop

Adiabatic Pipe (Single Phase or Simple Multiphase)

from neqsim import jneqsim

AdiabaticPipe = jneqsim.process.equipment.pipeline.AdiabaticPipe

# Assume inlet_stream exists
pipe = AdiabaticPipe("Pipeline", inlet_stream)
pipe.setLength(10000)          # 10 km
pipe.setDiameter(0.3)          # 300 mm ID
pipe.setPipeWallRoughness(5e-5) # m (smooth steel)

process.add(pipe)
process.run()

# Results
inlet_P = inlet_stream.getPressure()
outlet_P = pipe.getOutletStream().getPressure()
dp = inlet_P - outlet_P

print(f"Inlet P: {inlet_P:.2f} bara")
print(f"Outlet P: {outlet_P:.2f} bara")
print(f"Pressure drop: {dp:.2f} bar")
print(f"ΔP/L: {dp / 10:.3f} bar/km")

Set Elevation Change

pipe = AdiabaticPipe("Riser", inlet_stream)
pipe.setLength(500)           # 500 m
pipe.setDiameter(0.2)
pipe.setInletElevation(0)     # Start at sea level
pipe.setOutletElevation(100)  # 100 m elevation gain

Multiphase Pipelines

Beggs and Brill Correlation

PipeBeggsAndBrills = jneqsim.process.equipment.pipeline.PipeBeggsAndBrills

pipe = PipeBeggsAndBrills("Export Pipeline", inlet_stream)
pipe.setLength(50000)          # 50 km
pipe.setDiameter(0.4)          # 400 mm
pipe.setAngle(0)               # Horizontal (degrees from horizontal)
pipe.setPipeWallRoughness(4.5e-5)

process.add(pipe)
process.run()

# Multiphase results
print(f"Outlet pressure: {pipe.getOutletStream().getPressure():.2f} bara")
print(f"Liquid holdup: {pipe.getPressureDrop():.2f} bar")  # Total DP

Two-Fluid Model (Detailed)

TwoFluidPipe = jneqsim.process.equipment.pipeline.TwoFluidPipe

pipe = TwoFluidPipe("Flowline", inlet_stream)
pipe.setLength(20000)
pipe.setDiameter(0.25)
pipe.setNumberOfSections(100)   # Computational cells

# Heat transfer
pipe.setSurfaceTemperature(5.0, "C")        # 5°C ambient
pipe.setHeatTransferCoefficient(25.0)       # W/(m²·K)

# Flat elevation profile (horizontal pipe)
elevations = [0.0] * 100
pipe.setElevationProfile(elevations)

process.add(pipe)
process.run()

# Get detailed profiles
positions = pipe.getPositionProfile()       # Section positions (m)
pressures = pipe.getPressureProfile()       # Pa
holdups = pipe.getLiquidHoldupProfile()      # Liquid fraction
temps = pipe.getTemperatureProfile("C")     # °C

Two-Fluid Model with Advanced Features

TwoFluidPipe = jneqsim.process.equipment.pipeline.TwoFluidPipe

pipe = TwoFluidPipe("Flowline", inlet_stream)
pipe.setLength(20000)
pipe.setDiameter(0.25)
pipe.setNumberOfSections(100)

# Add local loss coefficients (fittings, bends)
# addLocalLoss(position_m, kFactor)
pipe.addLocalLoss(5000.0, 0.9)   # K=0.9 at 5000m (tee junction)
pipe.addLocalLoss(15000.0, 2.0)  # K=2.0 at 15000m (check valve)
pipe.setNumberOf90DegreeBends(4)   # K=0.3 each
pipe.setNumberOf45DegreeBends(2)   # K=0.16 each
pipe.setInletLossCoefficient(0.5)  # Sharp entrance
pipe.setOutletLossCoefficient(1.0) # Exit to tank

process.add(pipe)
process.run()

# Pressure drop breakdown
dp_friction = pipe.getPressureDrop()
dp_local = pipe.calculateLocalLossPressureDrop()
dp_total = pipe.getTotalPressureDrop()
print(pipe.getLocalLossSummary())

Drift-Flux Model

The drift-flux model is a simplified multiphase flow approach that solves mixture equations with slip between phases. It’s faster than the full two-fluid model while capturing key multiphase phenomena.

Key Equations

The drift-flux model relates gas velocity to mixture velocity:

\[v_g = C_0 \cdot v_m + v_{drift}\]

Where:

TransientPipe with Drift-Flux

from neqsim import jneqsim

# Note: TransientPipe uses drift-flux internally with AUSM+ scheme
TransientPipe = jneqsim.process.equipment.pipeline.twophasepipe.TransientPipe

# Create fluid and stream
fluid = jneqsim.thermo.system.SystemSrkEos(273.15 + 50, 80.0)
fluid.addComponent("methane", 0.80)
fluid.addComponent("n-pentane", 0.20)
fluid.setMixingRule("classic")
fluid.setMultiPhaseCheck(True)

stream = jneqsim.process.equipment.stream.Stream("Feed", fluid)
stream.setFlowRate(50.0, "kg/sec")
stream.run()

# Create drift-flux pipe
pipe = TransientPipe("Subsea Flowline", stream)
pipe.setLength(10000)       # 10 km
pipe.setDiameter(0.3)       # 300 mm
pipe.setNumberOfSections(200)
pipe.setRoughness(4.5e-5)   # Steel roughness

# Run steady-state initialization
pipe.run()

# Get drift-flux results
pressures = pipe.getPressureProfile()
holdups = pipe.getLiquidHoldupProfile()

When to Use Drift-Flux

Scenario Recommended Model
Quick screening studies Drift-flux (TransientPipe)
Steady-state pressure drop PipeBeggsAndBrills
Detailed slug dynamics Two-fluid (TwoFluidPipe)
Three-phase flow Two-fluid (TwoFluidPipe)
Countercurrent flow Two-fluid
Long transient simulations Drift-flux (faster)

Further Reading: See Multiphase Transient Model for detailed drift-flux equations and numerical methods.


Dynamic Flow Simulation

Beggs and Brill - Dynamic Mode

PipeBeggsAndBrills can perform pseudo-transient calculations by iterating with updated inlet conditions:

PipeBeggsAndBrills = jneqsim.process.equipment.pipeline.PipeBeggsAndBrills

# Create process system
process = jneqsim.process.processmodel.ProcessSystem()

# Create dynamic inlet stream
fluid = jneqsim.thermo.system.SystemSrkEos(273.15 + 60, 100.0)
fluid.addComponent("methane", 0.85)
fluid.addComponent("ethane", 0.10)
fluid.addComponent("propane", 0.05)
fluid.setMixingRule("classic")
fluid.setMultiPhaseCheck(True)

inlet = jneqsim.process.equipment.stream.Stream("Inlet", fluid)
inlet.setFlowRate(25.0, "kg/sec")
process.add(inlet)

pipe = PipeBeggsAndBrills("Export Line", inlet)
pipe.setLength(30000)         # 30 km
pipe.setDiameter(0.4)         # 400 mm
pipe.setAngle(0)              # Horizontal
pipe.setPipeWallRoughness(4.5e-5)

# Enable detailed output
pipe.setNumberOfIncrements(100)  # Discretization for profiles

process.add(pipe)

# Initial steady-state
process.run()
print(f"Initial outlet P: {pipe.getOutletStream().getPressure():.2f} bara")

# Simulate flow rate ramp-up
flow_rates = [25.0, 30.0, 35.0, 40.0, 45.0]  # kg/s

for rate in flow_rates:
    inlet.setFlowRate(rate, "kg/sec")
    process.run()
    outlet_P = pipe.getOutletStream().getPressure()
    outlet_T = pipe.getOutletStream().getTemperature() - 273.15
    print(f"Flow: {rate:.0f} kg/s -> Outlet P: {outlet_P:.2f} bara, T: {outlet_T:.1f}°C")

Two-Fluid Model - Full Transient

TwoFluidPipe solves separate momentum equations for gas and liquid phases with transient time-stepping:

import uuid

TwoFluidPipe = jneqsim.process.equipment.pipeline.TwoFluidPipe

# Create two-phase fluid
fluid = jneqsim.thermo.system.SystemSrkEos(273.15 + 40, 60.0)
fluid.addComponent("methane", 0.75)
fluid.addComponent("n-pentane", 0.25)
fluid.setMixingRule("classic")
fluid.setMultiPhaseCheck(True)

inlet = jneqsim.process.equipment.stream.Stream("Feed", fluid)
inlet.setFlowRate(15.0, "kg/sec")
inlet.run()

# Create two-fluid pipe with fine discretization
pipe = TwoFluidPipe("Slugging Flowline", inlet)
pipe.setLength(5000)             # 5 km
pipe.setDiameter(0.2)            # 200 mm
pipe.setNumberOfSections(200)    # 200 cells = 25m each
pipe.setRoughness(4.5e-5)

# Heat transfer
pipe.setHeatTransferCoefficient(25.0)  # W/(m²·K)
pipe.setSurfaceTemperature(4.0, "C")   # Ambient

# Initialize steady-state
pipe.run()
print(f"Steady-state liquid inventory: {pipe.getLiquidInventory('m3'):.2f} m³")

# Run transient simulation
run_id = str(uuid.uuid4())
dt = 0.5        # 0.5 second time step
t_end = 300.0   # 5 minutes total

time = 0.0
while time < t_end:
    pipe.runTransient(dt, run_id)
    time += dt

    # Ramp up flow rate after 60 seconds
    if time > 60.0:
        inlet.setFlowRate(25.0, "kg/sec")
        inlet.run()

# Final results
print(f"Final liquid inventory: {pipe.getLiquidInventory('m3'):.2f} m³")
print(f"Outlet pressure: {pipe.getOutletStream().getPressure():.2f} bara")

Key Parameters for Dynamic Simulation

Parameter Method Description
Time step runTransient(dt, id) Transient step size (s)
Grid cells setNumberOfSections(n) Spatial discretization
Roughness setRoughness(ε) Wall roughness (m)
Heat transfer setHeatTransferCoefficient(U) Overall U-value (W/m²K)
Ambient temp setSurfaceTemperature(T, unit) External temperature

Cross-Validate TwoFluidPipe vs Beggs & Brill

Compare pressure drops between the two models to build confidence:

PipeBeggsAndBrills = jneqsim.process.equipment.pipeline.PipeBeggsAndBrills
TwoFluidPipe = jneqsim.process.equipment.pipeline.TwoFluidPipe

# Run both models on the same feed
bb = PipeBeggsAndBrills("BB", inlet)
bb.setLength(5000); bb.setDiameter(0.3)
bb.setAngle(0); bb.setPipeWallRoughness(4.5e-5)
bb.run()

tf = TwoFluidPipe("TF", inlet)
tf.setLength(5000); tf.setDiameter(0.3)
tf.setNumberOfSections(50); tf.setRoughness(4.5e-5)
tf.run()

dp_bb = inlet.getPressure() - bb.getOutletStream().getPressure()
dp_tf = inlet.getPressure() - tf.getOutletStream().getPressure()
print(f"BB dP: {dp_bb:.3f} bar, TF dP: {dp_tf:.3f} bar, ratio: {dp_tf/dp_bb:.2f}")
# Expect ratio 0.8-1.3 for typical two-phase horizontal flow

Note: For single-phase gas, the ratio should be ~0.98 (excellent agreement). For two-phase flow, agreement varies with gas fraction — ratios of 0.8–1.3 are within typical engineering accuracy for different multiphase correlations.


Three-Phase Flow and Slug Tracking

TwoFluidPipe supports three-phase flow (gas-oil-water) with built-in slug tracking capabilities.

Three-Phase Configuration

TwoFluidPipe = jneqsim.process.equipment.pipeline.TwoFluidPipe

# Create three-phase fluid (gas + oil + water)
fluid = jneqsim.thermo.system.SystemSrkCPAstatoil(273.15 + 50, 50.0)
fluid.addComponent("methane", 0.60)
fluid.addComponent("ethane", 0.10)
fluid.addComponent("n-heptane", 0.20)  # Oil phase
fluid.addComponent("water", 0.10)
fluid.setMixingRule("classic")
fluid.setMultiPhaseCheck(True)

inlet = jneqsim.process.equipment.stream.Stream("WellStream", fluid)
inlet.setFlowRate(30.0, "kg/sec")
inlet.run()

# Configure three-phase pipe
pipe = TwoFluidPipe("Subsea Tieback", inlet)
pipe.setLength(15000)            # 15 km
pipe.setDiameter(0.25)           # 250 mm
pipe.setNumberOfSections(300)    # 300 cells for slug resolution
pipe.setRoughness(4.5e-5)

# Run simulation
pipe.run()

# Get three-phase results
liquid_inventory = pipe.getLiquidInventory("m3")
water_holdup = pipe.getWaterHoldupProfile()  # Water-in-liquid fraction
oil_holdup = pipe.getOilHoldupProfile()      # Oil holdup
pressure_profile = pipe.getPressureProfile()

print(f"Total liquid inventory: {liquid_inventory:.2f} m³")
print(f"Average water holdup: {sum(water_holdup)/len(water_holdup):.4f}")
print(f"Average oil holdup: {sum(oil_holdup)/len(oil_holdup):.4f}")

Slug Tracking with Lagrangian Method

For detailed slug dynamics, use the Lagrangian slug tracker:

import uuid

# Configure pipe for slug tracking
pipe = TwoFluidPipe("Slugging Pipeline", inlet)
pipe.setLength(8000)             # 8 km
pipe.setDiameter(0.15)           # 150 mm (promotes slugging)
pipe.setNumberOfSections(400)    # Fine grid for slug resolution

# Enable Lagrangian slug tracking
pipe.setEnableSlugTracking(True)

# Set terrain to promote terrain-induced slugging
elevations = []
for i in range(400):
    x = i * 20.0  # 20m per section
    # Create V-shaped terrain (valley)
    elevation = -30.0 * (1 - abs(x/4000 - 1))
    elevations.append(elevation)

pipe.setElevationProfile(elevations)

# Initialize and run
pipe.run()

# Run transient to observe slug development
run_id = str(uuid.uuid4())
for step in range(600):  # 5 minutes @ 0.5s steps
    pipe.runTransient(0.5, run_id)

    # Monitor slugs every 30 seconds
    if step % 60 == 0:
        slug_count = pipe.getSlugTracker().getSlugCount()
        avg_slug_length = pipe.getSlugTracker().getAverageSlugLength()
        slug_frequency = pipe.getSlugTracker().getSlugFrequency()
        print(f"Time: {step*0.5:.0f}s - Slugs: {slug_count}, "
              f"Avg length: {avg_slug_length:.1f}m, "
              f"Frequency: {slug_frequency:.3f} Hz")

Three-Phase Capabilities Summary

Feature TwoFluidPipe PipeBeggsAndBrills
Gas-liquid flow
Three-phase (gas-oil-water) Limited
Water holdup profile No
Oil holdup profile No
Slug tracking ✅ (Lagrangian) No
Terrain-induced slugging Empirical
Transient dynamics ✅ Full Pseudo-steady

Further Reading: See TwoFluidPipe Tutorial for comprehensive examples including slug visualization and transient analysis.


Advanced Pipeline Profiles

For complex pipelines with varying geometry, use multi-leg or segmented configurations.

Multi-Leg Pipeline (OnePhasePipeLine)

OnePhasePipeLine = jneqsim.process.equipment.pipeline.OnePhasePipeLine

# Create gas stream
gas = jneqsim.thermo.system.SystemSrkEos(273.15 + 25, 200.0)
gas.addComponent("methane", 0.95)
gas.addComponent("ethane", 0.05)
gas.setMixingRule("classic")

stream = jneqsim.process.equipment.stream.Stream("Gas Feed", gas)
stream.setFlowRate(50000, "kg/hr")
stream.run()

# Create multi-segment pipeline
pipeline = OnePhasePipeLine(stream)

# Define leg geometry arrays
diameters = [1.0, 1.0, 0.8]           # meters (reducing pipe)
positions = [0, 70000, 150000]         # cumulative length (m)
elevations = [0, -200, -350]           # elevation profile (m)
roughnesses = [1e-5, 1e-5, 1e-5]       # wall roughness (m)
ambient_temps = [277.0, 280.0, 283.0]  # K (warming seabed)
U_outer = [15.0, 15.0, 15.0]           # outer U-value (W/m²K)
U_wall = [15.0, 15.0, 15.0]            # wall U-value (W/m²K)

# Configure legs
pipeline.setNumberOfLegs(len(diameters) - 1)
pipeline.setPipeDiameters(diameters)
pipeline.setLegPositions(positions)
pipeline.setHeightProfile(elevations)
pipeline.setPipeWallRoughness(roughnesses)
pipeline.setOuterTemperatures(ambient_temps)
pipeline.setPipeOuterHeatTransferCoefficients(U_outer)
pipeline.setPipeWallHeatTransferCoefficients(U_wall)

# Run
process = jneqsim.process.processmodel.ProcessSystem()
process.add(stream)
process.add(pipeline)
process.run()

# Results
outlet_P = pipeline.getOutletStream().getPressure()
outlet_T = pipeline.getOutletStream().getTemperature() - 273.15
print(f"Outlet: {outlet_P:.2f} bara, {outlet_T:.1f}°C")

Complex Elevation Profile (TwoFluidPipe)

import math

TwoFluidPipe = jneqsim.process.equipment.pipeline.TwoFluidPipe

# Subsea pipeline with realistic terrain
pipe = TwoFluidPipe("Subsea Tieback", inlet_stream)
pipe.setLength(25000)              # 25 km
pipe.setDiameter(0.3)              # 300 mm
pipe.setNumberOfSections(500)      # 50m per section

# Create complex elevation profile
num_sections = 500
dx = 25000 / num_sections

elevations = []
for i in range(num_sections):
    x = i * dx

    # Start at platform (-50m), descend to seabed, undulations, rise to FPSO

    # Riser down (0-500m)
    if x < 500:
        elev = -50 - (x / 500) * 300  # Descend 300m

    # Seabed section with undulations (500m - 24000m)
    elif x < 24000:
        base = -350  # Base seabed depth
        # Add hills and valleys
        undulation = 20 * math.sin(x / 2000 * 2 * math.pi)  # ±20m
        valley = -40 * math.exp(-((x - 12000) / 3000)**2)   # Deep valley mid-pipe
        elev = base + undulation + valley

    # Riser up (24000m - 25000m)
    else:
        elev = -350 + ((x - 24000) / 1000) * 340  # Rise to -10m

    elevations.append(elev)

pipe.setElevationProfile(elevations)

# Heat transfer zones
pipe.setHeatTransferCoefficient(25.0)   # W/(m²·K) for bare pipe
pipe.setSurfaceTemperature(4.0, "C")    # Seabed temperature

# Run simulation
pipe.run()

# Analyze liquid accumulation in valleys
liquid_inv = pipe.getLiquidInventory("m3")
print(f"Liquid inventory in pipeline: {liquid_inv:.2f} m³")

Variable Diameter Pipeline

# Create pipe sections with different diameters
# by chaining multiple TwoFluidPipe segments

process = jneqsim.process.processmodel.ProcessSystem()

# Add inlet stream
process.add(inlet_stream)

# Section 1: Large diameter trunk line
section1 = TwoFluidPipe("Trunk Line", inlet_stream)
section1.setLength(15000)          # 15 km
section1.setDiameter(0.4)          # 400 mm
section1.setNumberOfSections(150)
process.add(section1)

# Section 2: Reduced diameter spur
section2 = TwoFluidPipe("Spur Line", section1.getOutletStream())
section2.setLength(8000)           # 8 km
section2.setDiameter(0.25)         # 250 mm
section2.setNumberOfSections(80)
process.add(section2)

# Section 3: Riser
section3 = TwoFluidPipe("Riser", section2.getOutletStream())
section3.setLength(300)            # 300 m riser
section3.setDiameter(0.2)          # 200 mm
section3.setNumberOfSections(60)

# Vertical elevation for riser
riser_elevations = [-300 + i * 5 for i in range(60)]  # -300m to 0m
section3.setElevationProfile(riser_elevations)
process.add(section3)

# Run complete system
process.run()

# Get results at each section outlet
print(f"After trunk: {section1.getOutletStream().getPressure():.2f} bara")
print(f"After spur: {section2.getOutletStream().getPressure():.2f} bara")
print(f"At topside: {section3.getOutletStream().getPressure():.2f} bara")

Pipeline Configuration Summary

Configuration Method Use Case
Single pipe setLength(), setDiameter() Simple flowline
Elevation change setInletElevation(), setOutletElevation() Riser, inclined pipe
Detailed terrain setElevationProfile(array) Undulating seabed
Multi-leg setNumberOfLegs(), setLegPositions() Variable geometry
Pipe sections Chain multiple pipes Different diameters/materials
Grid resolution setNumberOfSections(n) Fine vs coarse simulation
Non-uniform mesh generateRefinedMesh(n, factor) Finer cells at risers/gradients
Manual mesh setSectionLengths(double[]) Full control over each cell

Non-Uniform Mesh (TwoFluidPipe)

Commercial simulators (OLGA, LedaFlow) use static non-uniform meshes to place finer cells where gradients are steepest — typically at risers, terrain undulations, and wellheads. NeqSim supports this via two approaches:

generateRefinedMesh(baseSections, refinementFactor) analyses the elevation profile and creates shorter cells where the gradient is steepest with longer cells where the pipe is flat.

pipe = TwoFluidPipe("Flowline-Riser", feed)
pipe.setLength(5400.0)
pipe.setDiameter(0.2032)

# Set elevation profile FIRST
elevation = [...]  # must be set before generateRefinedMesh
pipe.setElevationProfile(elevation)

# Generate refined mesh: 100 base sections, 4x refinement at steep sections
pipe.generateRefinedMesh(100, 4.0)

# Inspect resulting mesh
lengths = list(pipe.getSectionLengths())
print(f"dx range: [{min(lengths):.1f}, {max(lengths):.1f}] m")
# e.g. Flowline cells ~68 m, Riser cells ~17 m (3.9x ratio)

The refinementFactor controls the ratio between coarsest and finest cells (clamped to 1.5–10).

Manual Section Lengths

For full control, provide exact lengths for every section:

pipe = TwoFluidPipe("Custom Mesh", feed)
pipe.setLength(1000.0)
pipe.setDiameter(0.3)

# Fine at ends, coarse in middle — lengths must sum to total length
lengths = [50, 50, 100, 150, 200, 150, 100, 100, 50, 50]
pipe.setSectionLengths(lengths)

CFL Note

With explicit time integration (RK4), the global timestep is governed by the smallest cell: $\Delta t = \text{CFL} \cdot dx_{\min} / c_{\max}$. Finer riser cells improve accuracy but reduce the CFL timestep proportionally. For a 4x refinement ratio, expect ~4x more sub-steps per macro timestep.

Adaptive Timestepping (TwoFluidPipe)

OLGA-style adaptive timestepping provides robustness for challenging geometries (risers, S-bends):

pipe = TwoFluidPipe("Subsea Line", feed)
pipe.setLength(5400.0)
pipe.setNumberOfSections(100)
pipe.setElevationProfile(elevation)

# Enable adaptive timestepping
pipe.setEnableAdaptiveTimestepping(True)
pipe.setAdaptiveMaxPressure(200.0)  # bar — reject step if exceeded

# Run transient
import java.util.UUID as UUID
run_id = UUID.randomUUID()
for step in range(n_steps):
    pipe.runTransient(dt, run_id)
    dt_factor = pipe.getAdaptiveDtFactor()  # 1.0 = full CFL, <1 = reduced
Parameter Method Default Description
Enable setEnableAdaptiveTimestepping(bool) False Turn on/off
Pressure ceiling setAdaptiveMaxPressure(bar) 1000 Reject step if any section exceeds
Monitor getAdaptiveDtFactor() Current dt multiplier (1.0 = full CFL)
Check isAdaptiveTimesteppingEnabled() Query state

The algorithm: (1) per-step CFL recompute from current velocities, (2) NaN/negative mass detection with state rollback, (3) pressure/velocity ceiling checks, (4) gradual dt recovery (x1.02 growth per stable step back to 1.0).


Boundary Conditions

For transient TwoFluidPipe simulations, you can configure inlet and outlet boundary conditions to control how the solver handles flow rate and pressure.

Boundary Condition Types

Type Description
STREAM_CONNECTED Use properties from connected inlet stream (default for inlet)
CONSTANT_FLOW Fixed mass flow rate (set via setInletMassFlow())
CONSTANT_PRESSURE Fixed pressure (default for outlet)
CLOSED Zero flow velocity (blocked/shut-in) — pressure floats

Default Behavior

By default, TwoFluidPipe uses:

During transient simulation, the inlet pressure is computed from momentum balance (backward marching from the outlet boundary), while the inlet flow rate is fixed.

Setting Boundary Conditions

TwoFluidPipe = jneqsim.process.equipment.pipeline.TwoFluidPipe
BoundaryCondition = TwoFluidPipe.BoundaryCondition

pipe = TwoFluidPipe("Pipeline", inlet_stream)
pipe.setLength(10000)
pipe.setDiameter(0.25)
pipe.setNumberOfSections(50)

# Set outlet pressure (required for transient)
pipe.setOutletPressure(30.0, "bara")

# Option 1: Default - use stream flow rate
pipe.setInletBoundaryCondition(BoundaryCondition.STREAM_CONNECTED)

# Option 2: Explicit mass flow (independent of stream)
pipe.setInletBoundaryCondition(BoundaryCondition.CONSTANT_FLOW)
pipe.setInletMassFlow(50.0)            # kg/s
pipe.setInletMassFlow(180000, "kg/hr") # or with unit

# Option 3: Fixed inlet pressure (flow computed from momentum)
pipe.setInletBoundaryCondition(BoundaryCondition.CONSTANT_PRESSURE)
pipe.setInletPressure(60.0, "bara")

# Query current BC types
inlet_bc = pipe.getInletBoundaryCondition()
outlet_bc = pipe.getOutletBoundaryCondition()

Transient Simulation with Changing Flow Rate

import uuid

# Configure pipe with explicit flow BC
pipe = TwoFluidPipe("Flowline", inlet_stream)
pipe.setLength(10000)
pipe.setDiameter(0.25)
pipe.setNumberOfSections(50)

# Use constant flow BC so we can change flow directly
pipe.setInletBoundaryCondition(BoundaryCondition.CONSTANT_FLOW)
pipe.setInletMassFlow(20.0)  # Initial 20 kg/s
pipe.setOutletPressure(30.0, "bara")

# Initialize
pipe.run()

# Transient loop with flow ramp-up
run_id = str(uuid.uuid4())
for t in range(120):  # 2 minutes
    # Ramp flow from 20 to 50 kg/s over first 60s
    if t < 60:
        new_flow = 20.0 + (t / 60.0) * 30.0
        pipe.setInletMassFlow(new_flow)

    pipe.runTransient(1.0, run_id)  # 1s timestep

    # Monitor inlet pressure (computed by solver)
    pressures = pipe.getPressureProfile()
    inlet_P = pressures[0] / 1e5  # Pa to bara
    print(f"t={t}s, flow={new_flow:.1f} kg/s, inlet P={inlet_P:.2f} bara")

Fixed Pressure at Both Ends

For scenarios where both inlet and outlet pressures are known (e.g., connecting to a reservoir and topside), set both as constant pressure:

pipe.setInletBoundaryCondition(BoundaryCondition.CONSTANT_PRESSURE)
pipe.setInletPressure(65.0, "bara")   # Wellhead pressure
pipe.setOutletPressure(30.0, "bara")  # Receiving pressure

# Flow rate is computed from the pressure differential
pipe.run()

# Check computed flow rate from outlet stream
outlet = pipe.getOutletStream()
print(f"Computed outlet mass flow: {outlet.getFlowRate('kg/sec'):.2f} kg/s")

Boundary Condition Summary Table

Configuration Inlet BC Outlet BC Flow Inlet P Outlet P
Default STREAM_CONNECTED CONSTANT_PRESSURE From stream Computed Fixed
Explicit flow CONSTANT_FLOW CONSTANT_PRESSURE Fixed Computed Fixed
Both P fixed CONSTANT_PRESSURE CONSTANT_PRESSURE Computed Fixed Fixed
Shut-in outlet STREAM_CONNECTED CLOSED From stream Computed Floats
Blowdown CLOSED CONSTANT_PRESSURE Zero Floats Fixed
Blocked pipe CLOSED CLOSED Zero Floats Floats

Note: The most common setup for production pipelines is STREAM_CONNECTED inlet with CONSTANT_PRESSURE outlet, where the inlet stream defines the flow rate and the outlet represents the receiving facility pressure.

Shut-In and Surge Scenarios

Use the CLOSED boundary condition or convenience methods for shut-in and pressure surge analysis:

import uuid

pipe = TwoFluidPipe("Pipeline", inlet_stream)
pipe.setLength(10000)
pipe.setDiameter(0.25)
pipe.setNumberOfSections(100)
pipe.setOutletPressure(30.0, "bara")

# Initialize with normal flow
pipe.run()
initial_P_inlet = pipe.getPressureProfile()[0] / 1e5
print(f"Initial inlet pressure: {initial_P_inlet:.2f} bara")

# --- Shut-in scenario: close outlet valve ---
pipe.closeOutlet()  # Convenience method (or: BoundaryCondition.CLOSED)

run_id = str(uuid.uuid4())
for t in range(60):  # 1 minute transient
    pipe.runTransient(1.0, run_id)

    if t % 10 == 0:
        pressures = pipe.getPressureProfile()
        P_inlet = pressures[0] / 1e5
        P_outlet = pressures[-1] / 1e5
        print(f"t={t}s: inlet P={P_inlet:.2f} bara, outlet P={P_outlet:.2f} bara")

# --- Reopen outlet ---
pipe.openOutlet(30.0, "bara")

# Continue transient to observe pressure recovery
for t in range(60, 120):
    pipe.runTransient(1.0, run_id)

Blowdown / Depressurization

Close inlet, leave outlet open for blowdown simulation:

pipe = TwoFluidPipe("Pipeline", inlet_stream)
pipe.setLength(5000)
pipe.setDiameter(0.3)
pipe.setNumberOfSections(50)

# Initialize packed pipe at high pressure
pipe.setInletPressure(100.0, "bara")
pipe.setOutletPressure(100.0, "bara")
pipe.setInletBoundaryCondition(BoundaryCondition.CONSTANT_PRESSURE)
pipe.run()

# Blowdown: close inlet, open outlet to atmosphere
pipe.closeInlet()
pipe.openOutlet(1.0, "bara")  # Vent to atmospheric

# Monitor depressurization
run_id = str(uuid.uuid4())
for t in range(300):  # 5 minutes
    pipe.runTransient(1.0, run_id)

    if t % 30 == 0:
        pressures = pipe.getPressureProfile()
        avg_P = sum(pressures) / len(pressures) / 1e5
        inventory = pipe.getLiquidInventory("m3")
        print(f"t={t}s: avg P={avg_P:.2f} bara, liquid={inventory:.3f} m³")

Convenience Methods

Method Description
closeOutlet() Set outlet BC to CLOSED (zero velocity)
openOutlet() Restore outlet to CONSTANT_PRESSURE (uses last set pressure)
openOutlet(P, unit) Open outlet with specified pressure
closeInlet() Set inlet BC to CLOSED (zero velocity)
openInlet() Restore inlet to STREAM_CONNECTED
isOutletClosed() Returns true if outlet BC is CLOSED
isInletClosed() Returns true if inlet BC is CLOSED

Heat Transfer

Pipeline with Heat Loss

pipe = PipeBeggsAndBrills("Subsea Flowline", inlet_stream)
pipe.setLength(30000)
pipe.setDiameter(0.3)

# Heat transfer
pipe.setConstantSurfaceTemperature(4.0, "C")  # 4°C seawater
pipe.setHeatTransferCoefficient(10.0)  # W/m²K (overall U-value)

process.add(pipe)
process.run()

inlet_T = inlet_stream.getTemperature() - 273.15
outlet_T = pipe.getOutletStream().getTemperature() - 273.15
print(f"Temperature drop: {inlet_T - outlet_T:.1f} °C")

Insulated Pipeline

# Higher insulation = lower U-value
pipe.setHeatTransferCoefficient(2.0)  # W/m²K (well insulated)

Buried Pipeline

pipe.setConstantSurfaceTemperature(10.0, "C")  # 10°C soil temperature
pipe.setHeatTransferCoefficient(5.0)  # W/m²K (buried)

Terrain Effects

Pipeline with Elevation Profile

# For more complex terrain, use TwoFluidPipe with segments
# or set overall elevation change

pipe = PipeBeggsAndBrills("Mountain Pipeline", inlet_stream)
pipe.setLength(10000)
pipe.setDiameter(0.3)

# Set inclination angle (degrees from horizontal)
# Positive = uphill, Negative = downhill
pipe.setAngle(5)  # 5° uphill

# Or set specific elevations
pipe.setInletElevation(0)
pipe.setOutletElevation(870)  # ~870 m rise for 5° over 10 km

Riser (Vertical Section)

riser = PipeBeggsAndBrills("Production Riser", flowline_outlet)
riser.setLength(200)       # 200 m water depth
riser.setDiameter(0.25)
riser.setAngle(90)         # Vertical

process.add(riser)

Flow Regimes

Detect Flow Regime

# After running pipe calculation
pipe.run()

# Get flow pattern (Beggs & Brill)
flow_pattern = pipe.getFlowRegime()
print(f"Flow regime: {flow_pattern}")

# Common regimes:
# - "Segregated" (stratified)
# - "Intermittent" (slug/plug)
# - "Distributed" (bubble/mist)
# - "Transition"

Slug Flow Parameters

# For two-fluid model with slug tracking
pipe = TwoFluidPipe("Slugging Line", inlet_stream)
pipe.setLength(5000)
pipe.setDiameter(0.15)
pipe.setNumberOfSections(200)

# Enable slug tracking (Lagrangian OLGA-style)
pipe.setEnableSlugTracking(True)
pipe.setSlugTrackingMode(TwoFluidPipe.SlugTrackingMode.LAGRANGIAN)

# Configure Lagrangian tracker for terrain and hydrodynamic slugs
pipe.configureLagrangianSlugTracking(True, True, True)

process.add(pipe)
process.run()

# Check for slugging conditions
print(pipe.getFlowAnalysisSummary())

# Get slug statistics via JSON
print(pipe.getSlugTrackingStatisticsJson())

# Or use the slug summary
print(pipe.getSlugStatisticsSummary())

TwoFluidPipe Complete API Reference

A comprehensive reference for the TwoFluidPipe model covering all flow types, boundary conditions, time integration, and advanced features.

Enumerations

BoundaryCondition

Value Description
STREAM_CONNECTED Use properties from connected inlet stream (default inlet)
CONSTANT_FLOW Fixed mass flow rate (set via setInletMassFlow())
CONSTANT_PRESSURE Fixed pressure (default outlet)
CLOSED No flow — blocked/shut-in; pressure floats
CHARACTERISTIC Riemann invariant-based, reduces spurious wave reflections

SlugTrackingMode

Value Description
LAGRANGIAN Full Lagrangian tracking (OLGA-style) — default
SIMPLIFIED Simplified slug unit model
DISABLED No slug tracking

TimeIntegrator.Method

Value Description
EULER First-order forward Euler
RK2 Second-order (Heun)
RK4 Classical 4th-order Runge-Kutta — default
SSP_RK3 Strong Stability Preserving RK3
IMEX_PRESSURE_CORRECTION Implicit-explicit; allows 10-100× larger dt

InsulationType

Value U-Value (W/m²K)
NONE 150.0
UNINSULATED_SUBSEA 25.0
PU_FOAM 10.0
MULTI_LAYER 5.0
PIPE_IN_PIPE 2.0
VIT 0.5
BURIED_ONSHORE 3.0
EXPOSED_ONSHORE 75.0

Profile Getters

Method Unit Description
getPressureProfile() Pa Pressure at each section
getTemperatureProfile("C") °C Temperature, unit = “K”, “C”, or “F”
getLiquidHoldupProfile() - Total liquid holdup fraction
getWaterHoldupProfile() - Water holdup fraction
getOilHoldupProfile() - Oil holdup fraction
getWaterCutProfile() - Water in liquid ratio
getGasVelocityProfile() m/s Gas velocity
getLiquidVelocityProfile() m/s Liquid velocity
getOilVelocityProfile() m/s Oil velocity
getWaterVelocityProfile() m/s Water velocity
getOilWaterSlipProfile() - Oil-water slip ratio
getFlowRegimeProfile() - Flow regime per section
getPositionProfile() m Section positions
getWallTemperatureProfile() K Wall temperature

Mesh Configuration

Method Description
setNumberOfSections(int) Uniform mesh with N equal-length sections
setSectionLengths(double[]) Non-uniform mesh with explicit per-section lengths
generateRefinedMesh(int baseSections, double refinementFactor) Auto-refine based on elevation gradient
getSectionLengths() Returns per-section lengths (null if uniform)

Adaptive Timestepping

Method Description
setEnableAdaptiveTimestepping(boolean) Enable/disable OLGA-style adaptive dt
setAdaptiveMaxPressure(double) Pressure ceiling (bar) — reject step if exceeded
getAdaptiveDtFactor() Current dt multiplier (1.0 = full CFL)
isAdaptiveTimesteppingEnabled() Query state

Time Integration Method

Method Description
setTimeIntegrationMethod(TimeIntegrator.Method) Select time integrator (RK4, SSP_RK3, RK2, EULER, IMEX_PRESSURE_CORRECTION)
getTimeIntegrationMethod() Returns current time integration method

Oil-Water Flow Regime (Three-Phase)

Per-section oil-water classification is available via TwoFluidSection for three-phase (gas-oil-water) simulations. The OilWaterFlowRegimeDetector runs automatically when an aqueous phase is present.

Method (on TwoFluidSection) Returns Description
getOilWaterFlowRegime() OilWaterFlowRegime Detected regime (STRATIFIED, DISPERSED_OIL_IN_WATER, etc.)
getOilWaterResult() OilWaterResult Full result: regime, viscosity, inversion point, droplet size
isWaterWetting() boolean True if water wets the pipe wall (corrosion indicator)
isWaterDropoutRisk() boolean True if water may separate and accumulate
getOilWaterInterfacialTension() double Oil-water IFT in N/m
setOilWaterInterfacialTension(double) Override default IFT (0.03 N/m)
getOilWaterDetector() OilWaterFlowRegimeDetector Access detector for tuning
setOilWaterDetector(...) Set custom detector instance

OilWaterFlowRegimeDetector tuning:

Method Default Description
setCriticalWeber(double) 1.17 Hinze critical Weber number for droplet breakup
getCriticalWeber() Query current value
setInversionConstant(double) 0.5 Decarre-Fabre phase inversion constant
getInversionConstant() Query current value

Standalone usage (outside TwoFluidPipe):

import jpype
OilWaterFlowRegimeDetector = jpype.JClass(
    "neqsim.process.equipment.pipeline.twophasepipe.closure.OilWaterFlowRegimeDetector")

detector = OilWaterFlowRegimeDetector()
result = detector.detect(
    0.30,     # waterCut (volume fraction)
    2.0,      # mixtureVelocity (m/s)
    800.0,    # rhoOil (kg/m3)
    1025.0,   # rhoWater (kg/m3)
    0.005,    # muOil (Pa.s)
    0.001,    # muWater (Pa.s)
    0.03,     # sigmaOW (N/m)
    0.25,     # pipeDiameter (m)
    0.0       # inclination (radians)
)

print(f"Regime: {result.regime}")
print(f"Water wetting: {result.waterWetting}")
print(f"Effective viscosity: {result.effectiveViscosity:.4f} Pa.s")
print(f"Inversion water fraction: {result.inversionWaterFraction:.3f}")
print(f"Water dropout risk: {result.waterDropoutRisk}")

Steady-State Solver Tuning

Parameter Setter Default Description
Under-relaxation setSteadyStateUnderRelaxation(double) 0.5 Update damping (0 to 1)
Flash interval setSteadyStateFlashInterval(int) 3 Flash thermodynamics every N iterations
Max wall-clock time setSteadyStateMaxWallClockTime(double) 30 s Timeout for SS solver

Flow Assurance

# Set hydrate/wax appearance temperatures
pipe.setHydrateFormationTemperature(18.0, "C")
pipe.setWaxAppearanceTemperature(35.0, "C")

pipe.run()

# Check risk
print(f"Hydrate risk: {pipe.hasHydrateRisk()}")
print(f"Wax risk: {pipe.hasWaxRisk()}")
print(f"Distance to hydrate risk: {pipe.getDistanceToHydrateRisk():.0f} m")
print(f"Hydrate cooldown time: {pipe.calculateHydrateCooldownTime():.1f} hours")
print(pipe.getThermalSummary())

Erosion Assessment (API 14E)

pipe.run()

# Check erosional velocity
print(f"Erosional velocity: {pipe.getErosionalVelocity():.2f} m/s")
print(f"Above erosional limit: {pipe.isVelocityAboveErosionalLimit()}")
print(pipe.getErosionRiskAssessment(100.0))  # C-factor = 100

Pipeline Network Example

from neqsim import jneqsim

# Imports
SystemSrkEos = jneqsim.thermo.system.SystemSrkEos
ProcessSystem = jneqsim.process.processmodel.ProcessSystem
Stream = jneqsim.process.equipment.stream.Stream
PipeBeggsAndBrills = jneqsim.process.equipment.pipeline.PipeBeggsAndBrills
Mixer = jneqsim.process.equipment.mixer.Mixer

# Create fluids for two wells
fluid1 = SystemSrkEos(273.15 + 80, 150.0)
fluid1.addComponent("methane", 0.85)
fluid1.addComponent("ethane", 0.10)
fluid1.addComponent("propane", 0.05)
fluid1.setMixingRule("classic")

fluid2 = fluid1.clone()

# Build network
process = ProcessSystem()

# Well 1 stream
well1 = Stream("Well-1", fluid1)
well1.setFlowRate(30000, "kg/hr")
process.add(well1)

# Well 1 flowline
flowline1 = PipeBeggsAndBrills("FL-1", well1)
flowline1.setLength(5000)
flowline1.setDiameter(0.2)
process.add(flowline1)

# Well 2 stream
well2 = Stream("Well-2", fluid2)
well2.setFlowRate(20000, "kg/hr")
process.add(well2)

# Well 2 flowline
flowline2 = PipeBeggsAndBrills("FL-2", well2)
flowline2.setLength(8000)
flowline2.setDiameter(0.2)
process.add(flowline2)

# Manifold
manifold = Mixer("Manifold")
manifold.addStream(flowline1.getOutletStream())
manifold.addStream(flowline2.getOutletStream())
process.add(manifold)

# Export line
export = PipeBeggsAndBrills("Export", manifold.getOutletStream())
export.setLength(25000)
export.setDiameter(0.4)
process.add(export)

# Run
process.run()

# Results
print(f"Manifold P: {manifold.getOutletStream().getPressure():.2f} bara")
print(f"Export outlet P: {export.getOutletStream().getPressure():.2f} bara")
print(f"Total flow: {export.getOutletStream().getFlowRate('kg/hr'):.0f} kg/hr")

Pressure Drop Correlations

Correlation Best For NeqSim Class
Beggs & Brill General multiphase, empirical PipeBeggsAndBrills
Two-Fluid Detailed transient, slugging, three-phase TwoFluidPipe
Drift-Flux Transient, mixture equations TransientPipe
Adiabatic Single phase, quick estimates AdiabaticPipe
One-Phase Gas or liquid, multi-leg OnePhasePipeLine

See Also