ps.pvt
ps.pvt.Components
Section titled “ps.pvt.Components”ps.pvt.Components(*, name: tuple[str, ...], molecular_weight: NDArray[numpy.float64], critical_temperature: NDArray[numpy.float64], critical_pressure: NDArray[numpy.float64], critical_volume: NDArray[numpy.float64], acentric_factor: NDArray[numpy.float64], volume_shift: NDArray[numpy.float64] | None = None) -> None
The components of an equation-of-state fluid: one row per component, in named columns.
ps.pvt.Components(name=["CO2", "METHANE", "DECANE"], molecular_weight=[44.01, 16.04, 142.29], critical_temperature=[304.1, 190.6, 617.7], critical_pressure=[73.75, 46.0, 21.1], critical_volume=[0.0939, 0.0986, 0.6], acentric_factor=[0.225, 0.013, 0.49]) is 1D_COMP’s
three components in a METRIC model.
name is each component’s name, unique, which the binary interaction coefficients and the
compositions name it by. molecular_weight is its molar mass (g/mol in METRIC, lb/lb-mol in
FIELD: one number), critical_temperature its critical temperature, absolute (K in METRIC,
degR in FIELD), critical_pressure its critical pressure (barsa, psia), critical_volume
its critical molar volume (m3/kg-mol, ft3/lb-mol) and acentric_factor its acentric factor.
The molecular weight and the critical constants are positive. volume_shift is each
component’s dimensionless volume shift; None states none. Each is a bare number in the
model’s units.
It writes NCOMPS, CNAMES, MW, TCRIT, PCRIT, VCRIT and ACF, and SSHIFT where the volume shift
is stated. A deck’s ZCRIT loads as the critical volumes it means (Vc = Zc R Tc / Pc) and is
written back while they are its own. It is ps.fluid.Compositional(components=...), at
fluid/1/components.
Rows: (name, molecular_weight, critical_temperature, critical_pressure, critical_volume, acentric_factor, volume_shift). Each column is a field taking a list, a tuple or a 1-D array, one value per row; Components.from_frame(frame) reads them from a frame by name.
ps.pvt.Composition
Section titled “ps.pvt.Composition”ps.pvt.Composition(*, name: str, mole_fraction: dict[str, float]) -> None
A named composition: each component’s overall mole fraction.
ps.pvt.Composition(name="LEAN", mole_fraction={"C1": 0.9, "C2": 0.1}) is a lean gas. A
fluid or a storage package declares it once (ps.fluid.Compositional(compositions=),
ps.storage.Package(compositions=)) and an injector injects it by its name:
ps.wells.InjectedStream(well="INJ1", composition="LEAN") (WINJGAS).
name is the name an injection refers to it by (WELLSTRE item 1). mole_fraction is each
component’s mole fraction, by the component’s name, from 0 to 1; they sum to 1 within 1e-6.
A component left unstated is 0, and WELLSTRE writes every component in its holder’s order.
It sits at physics/storage/compositions/<n> on a storage package.
ps.pvt.ConstantCompressibilityOil
Section titled “ps.pvt.ConstantCompressibilityOil”ps.pvt.ConstantCompressibilityOil(*, reference_pressure: ScalarPropertyInput, fvf: ScalarPropertyInput, compressibility: ScalarPropertyInput, viscosity: ScalarPropertyInput, viscosibility: ScalarPropertyInput) -> None
Dead oil PVT of constant compressibility about one reference pressure.
ps.pvt.ConstantCompressibilityOil(reference_pressure=250.0, fvf=1.2, compressibility=1.5e-04, viscosity=1.0, viscosibility=0.0) is an oil of formation volume factor 1.2 and viscosity 1 at
250.
reference_pressure (a pressure, positive) is the pressure the other four are stated at;
fvf the oil’s formation volume factor there (reservoir volume per surface volume,
positive); compressibility its compressibility (per unit pressure, not negative), which
carries the FVF to other pressures; viscosity its viscosity there (positive); and
viscosibility the viscosity’s own compressibility (per unit pressure, not negative),
which carries the viscosity to other pressures. Each is a bare number in the model’s units;
reference_pressure also takes a ps.Quantity.
It writes PVCDO, one record per PVT region. It is the oil of a ps.fluid.BlackOil or
ps.fluid.OilWater, at fluid/1/oil (PVT region n’s at fluid/<n>/oil).
ps.pvt.DeadOil
Section titled “ps.pvt.DeadOil”ps.pvt.DeadOil(*, pressure: NDArray[numpy.float64], oil_fvf: NDArray[numpy.float64], oil_viscosity: NDArray[numpy.float64], source: str | None = None) -> None
Dead oil PVT: the oil’s formation volume factor and viscosity against pressure.
ps.pvt.DeadOil(pressure=[100.0, 300.0], oil_fvf=[1.2, 1.18], oil_viscosity=[1.0, 1.1]) gives
the oil’s formation volume factor and viscosity at 100 and at 300.
Each row’s pressure (a pressure), the oil’s formation volume factor there, oil_fvf
(reservoir volume per surface volume), and its viscosity, oil_viscosity, bare numbers in
the model’s units. source notes where the numbers come from (a loaded deck’s card, a
paper); the writer does not read it.
It writes PVDO, one table per PVT region. It is the oil of a ps.fluid.BlackOil or
ps.fluid.OilWater, at fluid/1/oil (PVT region n’s at fluid/<n>/oil).
Rows: (pressure, oil_fvf, oil_viscosity). Each column is a field taking a list, a tuple or a 1-D array, one value per row; DeadOil.from_frame(frame) reads them from a frame by name.
ps.pvt.Diffusion
Section titled “ps.pvt.Diffusion”ps.pvt.Diffusion(*, oil_molecular_weight: Optional[ScalarPropertyInput] = None, gas_molecular_weight: Optional[ScalarPropertyInput] = None, gas_in_gas: Optional[ScalarPropertyInput] = None, oil_in_gas: Optional[ScalarPropertyInput] = None, gas_in_oil: Optional[ScalarPropertyInput] = None, oil_in_oil: Optional[ScalarPropertyInput] = None) -> None
Molecular diffusion in one PVT region (DIFFUSE), with the coefficients DIFFC states.
ps.pvt.Diffusion() switches diffusion on (DIFFUSE) and states no coefficient, so Flow
asks the phases’ PVT for them: the CO2 and H2 storage PVT computes them, while a black-oil
table (PVDO, PVTO, PVDG, PVTG, …) does not, and Flow stops (“The PVT model does not
provide a diffusionCoefficient()”). Stated, the coefficients are the oil and gas molecular
weights, the gas-in-gas and oil-in-gas coefficients, and optionally the gas-in-oil and
oil-in-oil pair, stated together; each is positive.
oil_molecular_weight and gas_molecular_weight are the two components’ molecular
weights; gas_in_gas and oil_in_gas the diffusion coefficients of the gas and of the
oil component in the gas phase; gas_in_oil and oil_in_oil those in the oil phase
(DIFFC, one record per PVT region, bare numbers in the model’s units). Every PVT region
states its coefficients, or none does: the compiler refuses a model where only some do.
It is the diffusion of a fluid system, at fluid/1/diffusion (PVT region n’s at
fluid/<n>/diffusion).
ps.pvt.DissolutionLimits
Section titled “ps.pvt.DissolutionLimits”ps.pvt.DissolutionLimits(*, maximum_rate: Union[ScalarPropertyInput, dict[int, ScalarPropertyInput]], scope: Optional[Literal['all', 'free']] = None) -> None
How fast gas may dissolve into oil: ps.pvt.DissolutionLimits(maximum_rate=0.001).
maximum_rate is the most a cell’s dissolved gas-oil ratio may rise per unit time
(surface gas per surface oil, per day; per hour in lab units), a bare number in the model’s
units. One rate holds for every cell (DRSDT); a rate per PVT region, {region: rate} with
the regions numbered 1 to n, writes one DRSDTR record per region in region order.
It is a dated change: ps.Event(date, ps.pvt.DissolutionLimits(...)), at
schedule/<date>/dissolution_limits. Stated in the start’s ps.Event it is written at
the schedule’s head; in a later ps.Event, at its date.
ps.pvt.DryGas
Section titled “ps.pvt.DryGas”ps.pvt.DryGas(*, pressure: NDArray[numpy.float64], gas_fvf: NDArray[numpy.float64], gas_viscosity: NDArray[numpy.float64], source: str | None = None) -> None
Dry gas PVT: the gas’s formation volume factor and viscosity against pressure.
ps.pvt.DryGas(pressure=[100.0, 300.0], gas_fvf=[0.01, 0.004], gas_viscosity=[0.015, 0.025])
gives the gas’s formation volume factor and viscosity at 100 and at 300.
Each row’s pressure (psia in FIELD, barsa in METRIC), the gas’s formation volume factor
there, gas_fvf (reservoir volume per surface volume: rb/Mscf in FIELD, rm3/sm3 in
METRIC), and its viscosity, gas_viscosity (cP), bare numbers in the model’s units.
source notes where the numbers come from (a loaded deck’s card, a paper); the writer does
not read it.
It writes PVDG, one table per PVT region. It is the gas of a ps.fluid.BlackOil or
ps.fluid.GasWater, at fluid/1/gas (PVT region n’s at fluid/<n>/gas).
Rows: (pressure, gas_fvf, gas_viscosity). Each column is a field taking a list, a tuple or a 1-D array, one value per row; DryGas.from_frame(frame) reads them from a frame by name.
ps.pvt.LiveOil
Section titled “ps.pvt.LiveOil”ps.pvt.LiveOil(*, solution_gor: NDArray[numpy.float64], pressure: NDArray[numpy.float64], oil_fvf: NDArray[numpy.float64], oil_viscosity: NDArray[numpy.float64], source: str | None = None) -> None
Live oil PVT: the oil’s FVF and viscosity at each solution gas-oil ratio and pressure.
The table is in long form, one row per point. The rows that share a solution_gor are
its family: the first is the saturated row, at the bubble-point pressure; the rest are
undersaturated, at higher pressures. The last family must have undersaturated rows, or the
model is refused ([physics.pvt.undersaturated_missing]: OPM Flow extends each family
from a later one, and finds none past the last).
solution_gor: the dissolved gas-oil ratio, Mscf/stb in FIELD, sm3/sm3 in METRIC.pressure: psia, barsa.oil_fvf: the oil formation volume factor, rb/stb, rm3/sm3.oil_viscosity: cP.source: where the numbers come from (a loaded deck’s card, a paper); never written.
A live oil sits in a ps.fluid.BlackOil beside a gas table, and its initial state needs a
depth table (ps.initial.RsVsDepth or ps.initial.PbVsDepth) unless the datum is at the
gas-oil contact, where the datum pressure is the bubble point. It writes PVTO, one table
per PVT region, and switches the run’s dissolved gas on (DISGAS). It is the oil of a
ps.fluid.BlackOil (in a ps.fluid.OilWater it is refused,
[phases.dissolved_gas.requires_gas]), at fluid/1/oil (PVT region n’s at
fluid/<n>/oil).
Examples
Section titled “Examples”Two families, the last with an undersaturated row (FIELD: Mscf/stb, psia, rb/stb, cP):
import petroscript as ps oil = ps.pvt.LiveOil(solution_gor=[0.5, 1.0, 1.0], … pressure=[1500.0, 3000.0, 4500.0], … oil_fvf=[1.25, 1.40, 1.37], … oil_viscosity=[1.1, 0.8, 0.85]) oil.frame().height # rows 3
Rows: (solution_gor, pressure, oil_fvf, oil_viscosity); long form: the rows of one solution_gor together, its saturated row first. Each column is a field taking a list, a tuple or a 1-D array, one value per row; LiveOil.from_frame(frame) reads them from a frame by name.
ps.pvt.VaporizationControls
Section titled “ps.pvt.VaporizationControls”ps.pvt.VaporizationControls(*, oil_vaporization_propensity: ScalarPropertyInput = 0, oil_density_propensity: ScalarPropertyInput = 0) -> None
How oil vaporizes into undersaturated gas (VAPPARS): one statement for the whole model.
ps.pvt.VaporizationControls(oil_vaporization_propensity=0.5) slows the oil’s vaporization
into undersaturated gas.
oil_vaporization_propensity (item 1) slows the oil’s vaporization into undersaturated gas once
the oil saturation has fallen below its highest: Flow scales the gas’s saturated oil-gas ratio
by the oil saturation over its highest raised to this power, so a larger value slows
vaporization more and 0 does not slow it.
oil_density_propensity (item 2) scales the oil’s saturated solution gas-oil ratio by the
same saturation ratio raised to this power, so the remaining oil holds less gas as
vaporization goes on. Both are dimensionless and not negative; 0, the default, leaves
Flow’s ratios as the tables give them. The controls act on vaporized oil, so they need the
model’s gas stated as a ps.pvt.WetGas (VAPOIL): without one the model is refused,
initialization.vappars.inactive_phase, in the initial state and at a date alike.
It is stated in the initial state, ps.initial.Equilibrium(vaporization_controls=...), at
initial/1/vaporization_controls, and writes VAPPARS in SOLUTION. Stated at a date,
ps.Event(date, ps.pvt.VaporizationControls(...)), it is held at
schedule/<date>/vaporization_controls and writes VAPPARS in SCHEDULE there; a deck whose
VAPPARS stands in SCHEDULE loads as that statement.
ps.pvt.VaporizedWaterGasPVT
Section titled “ps.pvt.VaporizedWaterGasPVT”ps.pvt.VaporizedWaterGasPVT(*, pressure: NDArray[numpy.float64], vaporized_water_gas_ratio: NDArray[numpy.float64], gas_fvf: NDArray[numpy.float64], gas_viscosity: NDArray[numpy.float64], source: str | None = None) -> None
Gas PVT with vaporized water: the water-gas ratio, FVF and viscosity at each pressure.
The rows of one pressure are its family, in long form: the first is the saturated row, the ratio does not rise along a family, and the pressures rise strictly from one family to the next.
Each row’s pressure (a pressure); vaporized_water_gas_ratio, the water vaporized in
the gas (surface water per surface gas, not negative); and the gas’s formation volume
factor, gas_fvf (reservoir volume per surface volume), and viscosity, gas_viscosity,
both positive, at that pressure and ratio: bare numbers in the model’s units. source
notes where the numbers come from (a loaded deck’s card, a paper); the writer does not read
it. ps.pvt.water_content_of_gas estimates a saturated ratio.
It writes PVTGW, one table per PVT region, and switches the run’s vaporized water on
(VAPWAT). It is the gas of a fluid system, at fluid/1/gas (PVT region n’s at
fluid/<n>/gas).
Rows: (pressure, vaporized_water_gas_ratio, gas_fvf, gas_viscosity); long form: the rows of one pressure together, its saturated row first. Each column is a field taking a list, a tuple or a 1-D array, one value per row; VaporizedWaterGasPVT.from_frame(frame) reads them from a frame by name.
ps.pvt.Water
Section titled “ps.pvt.Water”ps.pvt.Water(*, reference_pressure: ScalarPropertyInput, fvf: ScalarPropertyInput, compressibility: ScalarPropertyInput, viscosity: ScalarPropertyInput, viscosibility: ScalarPropertyInput) -> None
Water PVT: the water’s formation volume factor and viscosity about a reference pressure.
ps.pvt.Water(reference_pressure=250.0, fvf=1.02, compressibility=4.5e-05, viscosity=0.5, viscosibility=0.0) is a water of formation volume factor 1.02 and viscosity 0.5 at 250.
reference_pressure (a pressure) is the pressure the other four are stated at; fvf
the water’s formation volume factor there (reservoir volume per surface volume);
compressibility its compressibility (per unit pressure), which carries the FVF to other
pressures; viscosity its viscosity there; and viscosibility the viscosity’s own
compressibility (per unit pressure), which carries the viscosity to other pressures. Each
is a bare number in the model’s units; reference_pressure also takes a ps.Quantity.
It writes PVTW, one record per PVT region. It is the water of a fluid system
(ps.fluid.BlackOil, OilWater or GasWater), at fluid/1/water (PVT region n’s
at fluid/<n>/water).
ps.pvt.WetGas
Section titled “ps.pvt.WetGas”ps.pvt.WetGas(*, pressure: NDArray[numpy.float64], vaporized_oil_gas_ratio: NDArray[numpy.float64], gas_fvf: NDArray[numpy.float64], gas_viscosity: NDArray[numpy.float64], source: str | None = None) -> None
Wet gas PVT: the gas’s vaporized-oil ratio, FVF and viscosity at each pressure.
The rows of one pressure are its family, in long form: the first is the saturated row, and the pressures rise strictly from one family to the next.
Each row’s pressure (psia in FIELD, barsa in METRIC); vaporized_oil_gas_ratio, the oil
vaporized in the gas (surface oil per surface gas, not negative: stb/Mscf in FIELD,
sm3/sm3 in METRIC); and the gas’s formation volume factor, gas_fvf (rb/Mscf, rm3/sm3),
and viscosity, gas_viscosity (cP), both positive, at that pressure and ratio: bare
numbers in the model’s units. source notes
where the numbers come from (a loaded deck’s card, a paper); the writer does not read it.
It writes PVTG, one table per PVT region, and switches the run’s vaporized oil on (VAPOIL).
It is the gas of a fluid system, at fluid/1/gas (PVT region n’s at
fluid/<n>/gas).
Rows: (pressure, vaporized_oil_gas_ratio, gas_fvf, gas_viscosity); long form: the rows of one pressure together, its saturated row first. Each column is a field taking a list, a tuple or a 1-D array, one value per row; WetGas.from_frame(frame) reads them from a frame by name.
ps.pvt.water_content_of_gas
Section titled “ps.pvt.water_content_of_gas”ps.pvt.water_content_of_gas(*, temperature: Any, pressure: Any, salinity: Any = 0.0, units: str) -> float
Saturated water content of sweet gas as a vaporized water-gas ratio (RVW).
Bukacek’s (1955) fit of the McKetta-Wehe chart —
W [lb/MMscf] = 47484 * Pw_sat/P + B with
log10 B = -3083.87/T[degR] + 6.69449 (Terrigeol, Coquelet & Chapoy,
GPA Europe 2015, eq. 1; ~5% of the chart for 15-238 degC, 1-960 bar) —
with the Katz brine factor 1 - 4.920e-3*S - 1.7672e-4*S**2 for
salinity in weight percent salt; the pure water’s vapour pressure is
the IAPWS (Wagner-Pruss) saturation curve. It never extrapolates: an input
outside that envelope is refused.
It writes no card and is held nowhere in a model: the number it returns is
one a model states, a row of the vaporized-water table
(ps.pvt.VaporizedWaterGasPVT) or the explicit start’s
vaporized_water_gas_ratio, which Flow does not compute.
Parameters
Section titled “Parameters”temperature : float or Quantity
The gas’s temperature: a bare number in units (degF in FIELD, degC
in METRIC), or a ps.Quantity of kind temperature.
pressure : float or Quantity
The gas’s absolute pressure: a bare number in units (psia in FIELD,
barsa in METRIC), or a ps.Quantity of kind pressure.
salinity : float, default 0.0
The brine’s salt content in weight percent, 0 for fresh water.
units : {“FIELD”, “METRIC”}
The unit system the bare numbers and the answer are in, spelled in
upper case (a model’s "field" or "metric" is refused).
Returns
Section titled “Returns”float Surface water per surface gas: stb/Mscf in FIELD, sm3/sm3 in METRIC.
Raises
Section titled “Raises”ModelError
fluids.gas.water_content.envelope when the temperature lies outside
15 to 238 degC, the pressure outside 1 to 960 bar or the salinity
outside 0 to 26 weight percent.
ValueError
When units is neither "FIELD" nor "METRIC", or the
temperature or the pressure is not finite.
TypeError
When a ps.Quantity is of another kind than its parameter’s.
PetroScript is licensed under the Apache License 2.0. © 2026 Blauweiss EDV LLC. PetroScript by Blauweiss AI.For AI agents: llms.txt