Skip to content

ps.pvt

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(*, 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(*, 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(*, 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(*, 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(*, 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(*, 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(*, 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).

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(*, 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(*, 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(*, 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(*, 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(*, 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.

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).

float Surface water per surface gas: stb/Mscf in FIELD, sm3/sm3 in METRIC.

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