Initialization
Reservoir meaning
Section titled “Reservoir meaning”Initialization defines the starting pressure and saturation state before the
first timestep. A model’s initial is one of the ps.initial kinds: an
Equilibrium (the datum, the contacts and the depth tables), an ExplicitState
(the state cell by cell) or a Restart. One Equilibrium states every
equilibration region; {region: Equilibrium} states one per region, numbered
from 1, and region 1’s holds the statements of the whole model (the threshold
pressures, the whole-grid initial water saturation and the vaporized-oil
controls). The public API describes reservoir meaning; SOLUTION/RUNSPEC keyword
names remain compiler internals.
PetroScript API
Section titled “PetroScript API”Equilibrium
Section titled “Equilibrium”ps.initial.Equilibrium(...) is a standard equilibrium-contact initial state.
It requires an explicit datum depth and datum pressure, and states the contacts
the model has; PetroScript does not fabricate OPM defaults for those values.
import petroscript as ps
state = ps.initial.Equilibrium( datum_depth=8400.0, datum_pressure=4800.0, oil_water_contact=8450.0, gas_oil_contact=8300.0, # above the top (8325 ft): no gas cap)# ps.Model(..., initial=state)A model with gas and oil states its gas-oil contact even when it has no gas cap: above the reservoir’s top. OPM Flow places free gas where a depth is at or above the contact: each cell’s centre when EQUIL’s accuracy (item 9) is 0, and depths sampled within each cell with the default (-5) or any other non-zero accuracy. So at accuracy 0 a contact at a cell’s centre already puts gas there, and with sampling a contact inside the top cells can put gas in them though it lies below their centres. A contact above the top starts with no free gas at any accuracy.
A gas-water model — GASWAT, or CO2 storage into a saline aquifer — has no oil leg for a contact to sit against, and EQUIL carries one contact slot that OPM Flow reads as the gas-water contact when no oil phase is active. Say so:
storage_state = ps.initial.Equilibrium( datum_depth=2400.0, datum_pressure=250.0, gas_water_contact=2450.0,)gas_water_contact and oil_water_contact are two contacts that fill the one
EQUIL slot, each stored as itself, so state the one the model actually has.
Stating both is a contradiction and is refused. A gas-water contact placed below the grid base
initializes every cell in the gas leg — the aquifer disappears — and warns
under physics.contacts.gas_water_below_grid, which acknowledges like any
other physics warning when the gas-filled tank is the point.
The depth tables and the other equilibrium statements are its fields too:
state = ps.initial.Equilibrium( datum_depth=8400.0, datum_pressure=4800.0, oil_water_contact=8450.0, gas_oil_contact=8300.0, solution_gor_by_depth=ps.initial.RsVsDepth( depth=[8400.0, 8500.0], solution_gor=[700.0, 650.0] ), bubble_point_by_depth=ps.initial.PbVsDepth( depth=[8400.0, 8500.0], bubble_point_pressure=[4800.0, 4700.0] ), report_datum_depth=8400.0, threshold_pressures=( ps.initial.ThresholdPressure(from_region=1, to_region=2, pressure=25.0), ),)The report datum
Section titled “The report datum”report_datum_depth= states the depth pressures are reported at, and its shape
is the card: one depth writes DATUM (the whole model), one depth per
equilibration region in region order writes DATUMR, and one named
fluid-in-place set’s depths write DATUMRX. datum_depth stays EQUIL’s own
datum.
whole_model = state.replace(report_datum_depth=8400.0)per_region = state.replace(report_datum_depth=(2510.0, 2520.0))per_named_set = state.replace(report_datum_depth={"ABC": (2510.0, 2520.0)})The restart point
Section titled “The restart point”ps.initial.Restart(case=..., step=...) starts from a previous run’s
restart file: step= is the report-step number (the deck’s RSNUM, RESTART
item 2) or a date resolved against the schedule to the same item.
RsVsDepth
Section titled “RsVsDepth”ps.initial.RsVsDepth(depth=..., solution_gor=...) is the depth-indexed
solution-GOR table used by equilibrium initialization, held by column (depth,
solution_gor). The table is semantic input; the compiler lowers it internally
to the appropriate Flow deck rows. ps.initial.PbVsDepth, PdVsDepth and
RvwVsDepth hold the bubble-point pressure, the dew-point pressure and the
vaporized water-gas ratio against depth the same way.
RvVsDepth and VaporizationControls
Section titled “RvVsDepth and VaporizationControls”ps.initial.RvVsDepth(depth=..., vaporized_oil_gas_ratio=...) is the
depth-indexed vaporized-oil ratio table for equilibrium initialization.
ps.pvt.VaporizationControls is VAPPARS (its oil_vaporization_propensity and
oil_density_propensity), stated on the equilibrium as vaporization_controls=.
Initial reservoir temperature
Section titled “Initial reservoir temperature”ps.initial.TemperatureVsDepth(depth=..., temperature=...) states the initial
reservoir temperature against depth for one equilibration region, and the
equilibrium’s temperature_by_depth= attaches it.
Rows by name
Section titled “Rows by name”Every depth table (RsVsDepth, RvVsDepth, PbVsDepth, PdVsDepth,
RvwVsDepth, TemperatureVsDepth) is held by column, depth and the value
column named for the quantity: solution_gor, vaporized_oil_gas_ratio,
bubble_point_pressure, dew_point_pressure, vaporized_water_gas_ratio,
temperature. from_frame reads those columns by name from a polars or pandas
frame, or a mapping of column name to values:
profile = ps.initial.TemperatureVsDepth.from_frame( {"depth": [2500.0, 2600.0], "temperature": [80.0, 83.0]})A frame that lacks a column is refused, naming the table and the missing column.
Use it only where nothing else already holds the temperature. A thermal model
carries its own (physics.thermal.initial_temperature) and reads it for its
own physics; so does a CO2/H2 storage package
(physics.storage.temperature). What is left is the run that states a profile
and activates neither — opm-tests/diffusion/BO_DIFFUSE_CASE1 declares OIL,
GAS and DIFFUSE, states the profile, and the target runs it — and there the
temperature is an initial condition and nothing more.
A constant temperature with no thermal model and no storage package behind it has no such home and stays refused by name.
Direct initialization
Section titled “Direct initialization”ps.initial.ExplicitState(...) sets pressure and optional saturation/solution
fields directly instead of deriving pressure from contacts:
state = ps.initial.ExplicitState( pressure=3000.0, water_saturation=0.2, gas_saturation=0.0, oil_saturation=0.8,)Direct ratio fields such as solution_gor, solution_gas_water_ratio,
vaporized_oil_gas_ratio, and vaporized_water_gas_ratio require the matching
active phase/formulation semantics. Invalid combinations fail closed with stable
validation issue codes.
OILAPI in the SOLUTION section is intentionally not exposed through
ps.initial: OPM Flow (2025.10 and 2026.04) rejects that keyword (‘OILAPI: keyword not supported’). A deck’s
PROPS/GRAVITY, which states an oil API gravity, loads as the fluid system’s surface
densities.
Units and shapes
Section titled “Units and shapes”Under FIELD, depths are feet, pressure is psi, and gas/oil ratios are in the
model’s deck-native ratio units. Depth tables hold one value per depth, column
by column. Initialization property tensors use the same strict scalar-or
(nx, ny, nz) contract as grid and rock cell properties.
Validation and common mistakes
Section titled “Validation and common mistakes”Common mistakes are missing initial-state data, inconsistent contact depths,
non-positive datum pressure, non-finite scalar values, numeric strings, depth
tables with non-monotonic depth rows, and direct ratio fields supplied without
matching phase semantics. Use ps.testing.assert_issue(..., code=...) for
agent-friendly red/green diagnostics.
Lowers to
Section titled “Lowers to”Current status: emitted now for the implemented equilibrium and direct paths.
EQUILis emitted fromps.initial.Equilibrium(...).RSVD,RVVD,PBVD,PDVD, andRVWVDare emitted from semantic depth tables.RTEMPVDis emitted fromps.initial.TemperatureVsDepth(...)when neither a thermal model nor a storage package carries the temperature instead.DATUM,DATUMR, andDATUMRXare emitted fromreport_datum_depth.EQLOPTS THPRESandTHPRESare emitted from typed threshold-pressure semantics.PRESSURE,SWAT,SGAS,SOIL,RS,RSW,RV, andRVWare emitted fromps.initial.ExplicitState(...)when the corresponding semantic field is supplied.
Restart-based initialization is ps.initial.Restart; raw keyword-shaped
initialization helpers are not public PetroScript APIs.
PetroScript is licensed under the Apache License 2.0. © 2026 Blauweiss EDV LLC. PetroScript by Blauweiss AI.For AI agents: llms.txt