Fluids and PVT
Reservoir meaning
Section titled “Reservoir meaning”Black-oil simulation separates reservoir behavior into oil, water, and gas phases. PVT data explains how each phase’s volume and viscosity change with pressure and, for oil, solution gas. PetroScript models the implemented SPE1 style of single-region black-oil PVT with explicit semantic objects instead of raw table cards.
PetroScript API
Section titled “PetroScript API”Phases
Section titled “Phases”The phases a model holds, and their solution-gas coupling, are read off its
fluid system: the system’s class names the phase set (see Fluid systems below)
and its PVT tables the coupling. Dissolved gas (oil carrying solution gas) is a
ps.pvt.LiveOil oil table. Vaporized oil (gas carrying vaporized oil) is a
ps.pvt.WetGas gas table, and requires active oil and gas phases. Vaporized
water is a ps.pvt.VaporizedWaterGasPVT gas table in a gas-water system, and
requires an initial RVW state.
ps.pvt.Water(...) is water around a pressure anchor. The formation-volume factor maps surface
water volume to reservoir volume; compressibility and viscosibility describe the
local pressure response used by the simulator. Each value must be a finite
deck-native scalar (float/int) in the model’s units, and
reference_pressure also takes a ps.Quantity of pressure; numeric strings
are rejected instead of being silently coerced.
DryGas
Section titled “DryGas”ps.pvt.DryGas(...) is a dry-gas table, held by column: pressure, gas_fvf
and gas_viscosity, each a list, a tuple or a 1-D array, one value per row;
DryGas.from_frame reads them from a frame by name and table.frame() returns
them. Rows should be ordered by
increasing pressure. PVT table arrays must contain finite numeric values; numeric strings
and non-finite values are rejected.
import petroscript as ps
gas = ps.pvt.DryGas( pressure=[14.7, 264.7], gas_fvf=[166.666, 12.093], gas_viscosity=[0.008, 0.0096],)WetGas
Section titled “WetGas”ps.pvt.WetGas holds vaporized-oil gas PVT in long form: the columns
pressure, vaporized_oil_gas_ratio, gas_fvf and gas_viscosity, the rows of one
pressure together as its family, the saturated row first. This keeps the public
API semantic: users provide pressure-indexed wet-gas behavior, not backend
records.
Advanced vaporized-oil gas tables are currently exercised in executable
Flow-backed parity examples; ps.pvt.WetGas.from_frame reads the long-form
columns by name.
VaporizedWaterGasPVT
Section titled “VaporizedWaterGasPVT”ps.pvt.VaporizedWaterGasPVT holds its table
in long form: the columns pressure, vaporized_water_gas_ratio, gas_fvf and
gas_viscosity, the rows of one pressure together as its family. Use it only with the explicit gas-water formulation and
vaporized-water intent; PetroScript rejects a wet-water gas table in ordinary
dry-gas models so generated decks do not miss the required VAPWAT/RVW
context.
LiveOil
Section titled “LiveOil”ps.pvt.LiveOil holds live-oil PVT in long form: the columns solution_gor, pressure,
oil_fvf and oil_viscosity, the rows of one solution_gor together as its family. A
family’s first row is saturated, at its bubble-point pressure, and the rest are
undersaturated. Every column must contain finite numeric values; use numeric
arrays/list-like data, not deck-lexeme strings.
Advanced live-oil PVT families are covered by executable parity examples;
ps.pvt.LiveOil.from_frame reads the long-form columns by name.
DeadOil
Section titled “DeadOil”ps.pvt.DeadOil(...) is a dead-oil table, for oil without active dissolved
gas. Its columns are pressure, oil_fvf and
oil_viscosity.
Use it for oil/water cases where oil PVT is pressure-only rather than organized
by solution gas-oil-ratio families. Its table data follows the same finite
numeric-array contract as DryGas.
oil = ps.pvt.DeadOil(pressure=[14.7, 5014.7], oil_fvf=[2.0, 1.827], oil_viscosity=[0.2, 0.449])ConstantCompressibilityOil
Section titled “ConstantCompressibilityOil”ps.pvt.ConstantCompressibilityOil is a dead-oil constant-compressibility PVT
anchor. The semantic fields are reference pressure, oil FVF, oil compressibility,
oil viscosity, and viscosity compressibility. The lowerer emits PVCDO; callers
do not spell the keyword in public code.
CO2LiveOilPVT
Section titled “CO2LiveOilPVT”ps.solvent.CO2LiveOilPVT models bounded CO2-dynamic black-oil/EOR oil
PVT. Authors state its rows, the CO2 mass fraction first (each fraction’s rows
together, the fractions rising), plus the conventional live-oil baseline and
injected-gas density/PVT that OPM Flow requires to allocate the extended
black-oil model. PetroScript lowers those support facts to PVTO,
SDENSITY, and PVDS next to PVTSOL; it does not activate the conventional
SOLVENT/CO2SOL model or expose a general compositional framework.
SurfaceDensities
Section titled “SurfaceDensities”The fluid system’s fields oil_surface_density, gas_surface_density and
water_surface_density state finite scalar surface-condition phase
densities. These connect reservoir calculations to
stock-tank/surface units used by reports and controls.
A deck’s GRAVITY (the oil’s API gravity, the water’s and gas’s specific gravities)
loads as the three surface densities OPM Flow reads from it, by Flow’s own conversion. The
loaded deck writes its GRAVITY back while the densities are the ones it means, and DENSITY
once one is edited.
Fluid systems
Section titled “Fluid systems”The fluid system is the model’s fluid=. The class is the formulation: an
oil-water system is a ps.fluid.OilWater, a gas-water one a
ps.fluid.GasWater, and every other phase set a ps.fluid.BlackOil. A system
keeps the PVT tables required by its active phases together, with the surface
densities, so validation and lowering can reason about a coherent fluid
description.
water = ps.pvt.Water(reference_pressure=3000.0, fvf=1.0, compressibility=3.0e-6, viscosity=0.5, viscosibility=0.0)system = ps.fluid.OilWater(oil=oil, water=water, oil_surface_density=53.0, water_surface_density=64.0)A model with several PVT regions states one system per region,
{1: system, 2: system}. A system’s temperature is the temperature its tables
are stated at (degF in FIELD, degC in METRIC); an isothermal run writes no card
for it, and the implied z-factor check reads it.
Units and shapes
Section titled “Units and shapes”Under FIELD, pressure is psi, viscosity is cP, gas/oil ratios follow deck-native
surface-volume conventions, and formation-volume factors are deck-native ratios.
A scalar field that states a kind (a table’s reference pressure, a system’s
temperature) takes a number in the model’s units or a ps.Quantity of that
kind; the others (surface densities, viscosities, compressibilities,
formation-volume factors) take numbers in the model’s units and refuse a
ps.Quantity. Strings and non-finite values fail at model construction.
Every PVT table is held by column, each column 1-D and all of one length; the
family tables (LiveOil, WetGas, VaporizedWaterGasPVT) hold their families
in long form. Surface densities are in the active unit system.
Validation and common mistakes
Section titled “Validation and common mistakes”Common mistakes include pressure rows that are not monotonically increasing,
negative viscosity or formation-volume factors, enabling dissolved gas without an
oil table that represents solution-GOR families, enabling vaporized oil without a
wet-gas PVT table, enabling vaporized water without a vaporized-water gas table
and initial RVW, omitting the baseline/support gas data for CO2-dynamic oil
PVT, mixing dry-gas and wet-gas tables, and mixing unit systems inside a single
model. PetroScript validates table width and several monotonic/positive
constraints for the implemented tables.
Lowers to
Section titled “Lowers to”Current status: emitted now for the implemented black-oil PVT path.
OIL,GAS,WATER, andDISGASare emitted inRUNSPECfrom the fluid system’s phases when enabled.PVTWis emitted fromWater.PVDGis emitted fromDryGas.PVTGis emitted fromWetGaswhen vaporized oil is active.PVTGWis emitted fromVaporizedWaterGasPVTwhen vaporized water is active.PVTOis emitted fromLiveOil.PVDOis emitted fromDeadOil.PVCDOis emitted fromConstantCompressibilityOil.PVTSOLis emitted fromCO2LiveOilPVTtogether with its compiler-ownedPVTO/SDENSITY/PVDSsupport consequences.DENSITYis emitted from the system’s surface densities.GRAVITYis written back for a loaded deck that states it, while the densities are the ones it means.
Two PVT regions sharing one table
Section titled “Two PVT regions sharing one table”A ragged PVT keyword (PVTO, PVTG, PVTGW) states one table body per PVT
region. A deck says that a region uses the region before it by leaving that
region’s body empty — a bare terminator with no rows.
The semantic surface holds that as what it is: the two regions carry the same table object. Put one table into two regions’ systems and the restatement writes the rows once and a bare terminator for the repeat, the way the deck did; build two equal-but-separate tables and both bodies are written out. Importing a deck of this shape shares the table the same way.
The first region cannot be empty — there is nothing before it to repeat — and that stays refused by name.
Compositional EOS fluids are modelled (ps.fluid.Compositional with ps.pvt.Components):
ps.load reads a compositional deck into a model you can inspect and edit like any other, and
m.to_python() writes it back as Python that rebuilds it. Neither target compiles one yet:
ps.compile raises ps.CompileError [target.unsupported] for opm-flow (OPM Flow 2026.04 runs
no equation-of-state fluid) and for jutuldarcy (until its compositional support lands). A
compositional fluid also describes the stream of a NeqSim lift table
(how-to). Thermal PVT and broader multi-region PVT are
future/unsupported.
PetroScript is licensed under the Apache License 2.0. © 2026 Blauweiss EDV LLC. PetroScript by Blauweiss AI.For AI agents: llms.txt