Skip to content

ps.lift

ps.lift.InjectionTable(*, number: int, bhp_reference_depth: ScalarPropertyInput, source: str | None = None, phase: Literal['oil', 'water', 'gas'], units: Optional[Literal['field', 'metric', 'lab']] = None, rate: NDArray[numpy.float64], thp: NDArray[numpy.float64], bhp: NDArray[numpy.float64]) -> None

An injector’s lift table (VFPINJ): the bottom-hole pressure its tubing delivers.

ps.lift.InjectionTable(number=1, bhp_reference_depth=2500.0, phase="water", rate=[0.0, 1000.0], thp=[50.0, 100.0], bhp=[[300.0, 290.0], [350.0, 340.0]]) gives the bottom-hole pressure at two head pressures and two rates.

bhp holds one pressure per tubing-head pressure and rate, as an array of shape (thp, rate), at bhp_reference_depth; phase is the phase injected. InjectionTable.from_frame(frame) reads the long form, one row per combination with the columns thp, rate and bhp; table.frame() returns it.

number is the table’s number, and one number names one table at a time: a table that reuses a number comes into force at the date of the first control that names it (lift_table=), as a ps.lift.ProductionTable does.

A table is held in ps.Model(lift_tables=[...]), its address lift_tables/<number>, and an injector’s control names it with lift_table=. Its numbers are in the model’s units: the pressures, the reference depth, and rate the surface rate of phase.

ps.lift.ProductionTable(*, number: int, bhp_reference_depth: ScalarPropertyInput, source: str | None = None, rate: NDArray[numpy.float64], rate_kind: Literal['oil', 'liquid', 'gas', 'water'], thp: NDArray[numpy.float64], water_fraction: NDArray[numpy.float64] = <factory>, water_fraction_kind: Literal['water_cut', 'water_oil_ratio', 'water_gas_ratio'] = 'water_cut', gas_fraction: NDArray[numpy.float64] = <factory>, gas_fraction_kind: Literal['gas_oil_ratio', 'gas_liquid_ratio', 'oil_gas_ratio'] = 'gas_oil_ratio', artificial_lift: NDArray[numpy.float64] = <factory>, artificial_lift_kind: Optional[Literal['gas_lift_rate', 'injection_gas_liquid_ratio', 'total_gas_liquid_ratio', 'pump_rating']] = None, units: Optional[Literal['field', 'metric', 'lab']] = None, bhp: NDArray[numpy.float64], constant_pressure_drop_position: tuple[int, int, int, int] | None = None) -> None

A producer’s lift table (VFPPROD): the bottom-hole pressure its tubing delivers.

ps.lift.ProductionTable.from_frame({"thp": [20.0, 20.0, 40.0, 40.0], "rate": [100.0, 1000.0, 100.0, 1000.0], "bhp": [100.0, 140.0, 120.0, 160.0]}, number=1, bhp_reference_depth=2500.0, rate_kind="liquid") gives the bottom-hole pressure at two head pressures and two liquid rates.

bhp holds one pressure per combination of the axes, as an array of shape (thp, water_fraction, gas_fraction, artificial_lift, rate), the rate innermost, at bhp_reference_depth. rate_kind names the phase rate is a rate of, and water_fraction_kind and gas_fraction_kind the ratios those axes hold; an axis left out holds the one value 0. ProductionTable.from_frame(frame) reads the long form, one row per combination with the columns thp, water_fraction, gas_fraction, artificial_lift, rate and bhp; table.frame() returns it.

number is the table’s number, and one number names one table at a time. The deck writes a table where it comes into force: at the start for Model.lift_tables, and for a table that reuses a number at the date of the first control that names it (lift_table=), before that control. A number is reused only when no other well’s control and no network member still names the earlier table from that date on.

A table is held in ps.Model(lift_tables=[...]), its address lift_tables/<number>, and a producer’s control names it with lift_table=. Its numbers are in the model’s units: the pressures and the reference depth, and each axis in its own quantity’s unit (rate the surface rate of rate_kind’s phase).

ps.lift.Tubing(*, measured_depth: NDArray[numpy.float64], true_vertical_depth: NDArray[numpy.float64], inner_diameter: NDArray[numpy.float64], roughness: NDArray[numpy.float64], wellhead_temperature: float, temperature_gradient: float) -> None

A well’s tubing, for the lift tables made from it: its survey and its temperature.

ps.lift.Tubing(measured_depth=[0.0, 2560.0], true_vertical_depth=[0.0, 2560.0], inner_diameter=[0.124], roughness=[1.5e-5], wellhead_temperature=40.0, temperature_gradient=0.03) is a vertical string 2560 m long with a 0.124 m bore, 40 degC at the wellhead and 0.03 degC warmer per metre down.

measured_depth and true_vertical_depth are stations down the string, the first at the wellhead. The true vertical depths are in the model’s depth reference, as every depth it states (a wellhead above the origin has a negative one); the measured depths rise. Between each pair of stations lies a segment, and inner_diameter and roughness hold one value per segment; a segment drops at most its measured length. The temperature is stated, never defaulted: wellhead_temperature at the first station and temperature_gradient its change per unit of true vertical depth below (warmer with depth when positive, 0 for a string at one temperature). Its numbers are in the model’s units.

ps.lift.generate(pipe: Tubing, *, model: Model, well: Well | str, fluid: Compositional, composition: Composition | str, correlation, number: int, rate: ColumnIn, rate_kind, thp: ColumnIn | None = None, water_fraction: ColumnIn = (0.0,), water_fraction_kind='water_cut', gas_fraction: ColumnIn = (0.0,), gas_fraction_kind='gas_oil_ratio', bhp_reference_depth: float | None = None, simulator: Simulator | str | None = None, folder: str | Path | None = None) -> ProductionTable

A producer’s lift table NeqSim makes from its tubing and the fluid it flows.

NeqSim marches each axis point’s stream up the tubing with the correlation, and PetroScript’s own bracketed search finds the bottom-hole pressure that delivers the point’s tubing-head pressure. Every number is in the model’s units (METRIC or FIELD), the tubing’s too; the table holds them there. It needs a NeqSim runner (PETROSCRIPT_NEQSIM), and gives an ordinary ps.lift.ProductionTable: a control names it (ps.Produce(..., lift_table=table)) and the model holds it at lift_tables/<number>. The how-to docs/how-to/lift-table-neqsim.md builds one step by step.

pipe : ps.lift.Tubing The pipe the stream flows up: the well’s tubing, its true vertical depths in the model’s depth reference. model : ps.Model The model the table is for: its units, its well and what it states about that well. A complete model that checks clean (a fluid, an initial state, a control for every well), loaded or built; it may stay black-oil, since fluid alone describes the stream. well : ps.Well or str The model’s well, or its name. fluid : ps.fluid.Compositional The fluid the well flows, by its components. A water fraction above 0 needs water in the stream: a water component in the composition. composition : ps.pvt.Composition or str The stream’s overall composition, or the name of one the fluid declares (fluid.compositions); a surrogate is a stated composition, and the source names it. correlation : {“beggs_brill”, “hagedorn_brown”, “gray”, “mukherjee_brill”} NeqSim’s multiphase correlation; hagedorn_brown and gray take only segments within 15 degrees of vertical. number : int The table’s number. rate : list of float or 1-D array The flow rates, rising, of rate_kind’s phase at surface conditions. rate_kind : {“oil”, “liquid”, “gas”, “water”} The phase the rate is of. thp : list of float or 1-D array, optional The tubing-head pressures, rising. Left out, the axis is sized from the model: six nodes from the lowest tubing-head pressure it states for the well (a control’s limit or target, an adjustment) to its initial pressure (the equilibration’s datum pressure, the highest where there are several). water_fraction : list of float or 1-D array The water fractions, rising; the one value 0 when left out. water_fraction_kind : {“water_cut”, “water_oil_ratio”, “water_gas_ratio”} The ratio the water fractions are. gas_fraction : list of float or 1-D array The gas fractions, rising; the one value 0 when left out. gas_fraction_kind : {“gas_oil_ratio”, “gas_liquid_ratio”, “oil_gas_ratio”} The ratio the gas fractions are. bhp_reference_depth : float, optional The depth the table’s pressures refer to; the well’s own BHP reference depth when left out, so Flow adds no density correction between them. simulator : Simulator or str, optional The NeqSim runner; PETROSCRIPT_NEQSIM names it when left out. folder : str or Path, optional Where the request, NeqSim’s result and its log go: ./<model name>-lift<number>/ when left out.

ps.lift.ProductionTable The table, its source naming NeqSim, its version, the correlation, the composition, the datum and the THP axis’s origin.

InputError For a word correlation, rate_kind, water_fraction_kind or gas_fraction_kind does not take, refused before anything runs: each is written in lower case or in capitals ("beggs_brill" or "BEGGS_BRILL"), never mixed ("Beggs_Brill"). ValueError For a well the model does not hold, no datum (the well states no BHP reference depth and none is stated), no THP axis to size (the model states no THP for the well, or no equilibration pressure), a composition the fluid cannot flow, and a LAB model. RunError When no runner is found, NeqSim’s driver refuses the request, or it refuses any point: each is named by its axis values, since a table needs every entry.

The quickstart’s gas field (METRIC), its producer M-1 flowing a lean gas up 2420 m of tubing:

import petroscript as ps m = ps.load(“tests/results_fixtures/QUICKSTART/MERLE.DATA”) # any model that checks clean gas = ps.fluid.Compositional( … equation_of_state=“peng_robinson”, … components=ps.pvt.Components( … name=[“C1”, “C2”, “C3”], molecular_weight=[16.04, 30.07, 44.10], # g/mol … critical_temperature=[190.56, 305.32, 369.83], # K … critical_pressure=[45.99, 48.72, 42.48], # barsa … critical_volume=[0.0986, 0.1455, 0.2], acentric_factor=[0.011, 0.099, 0.152]), … compositions=[ps.pvt.Composition(name=“LEAN”, … mole_fraction={“C1”: 0.92, “C2”: 0.06, “C3”: 0.02})], … surface_temperature=15.0, surface_pressure=1.01325) tubing = ps.lift.Tubing(measured_depth=[0.0, 2420.0], true_vertical_depth=[0.0, 2420.0], … inner_diameter=[0.1], roughness=[1.5e-5], … wellhead_temperature=30.0, temperature_gradient=0.025) table = ps.lift.generate( # doctest: +SKIP … tubing, model=m, well=“M-1”, fluid=gas, composition=“LEAN”, … correlation=“beggs_brill”, number=1, … rate=[100_000.0, 300_000.0, 600_000.0], rate_kind=“gas”, # sm3/day … thp=[20.0, 50.0, 80.0], # barsa … gas_fraction=[0.0], gas_fraction_kind=“oil_gas_ratio”, # dry: no condensate … water_fraction=[0.0], water_fraction_kind=“water_gas_ratio”, … bhp_reference_depth=2420.0) # M-1 states none of its own table.bhp.shape # doctest: +SKIP (3, 1, 1, 1, 3)

PetroScript is licensed under the Apache License 2.0. © 2026 Blauweiss EDV LLC. PetroScript by Blauweiss AI.For AI agents: llms.txt