ps.grid
ps.grid.Add
Section titled “ps.grid.Add”ps.grid.Add(target: ArrayName | GridArrayReference, value: float, source: ArrayName | GridArrayReference | None = None, cells: Box | InRegion = <factory>, form: ArithmeticForm | None = None, stage: MultiplierStageChoice | None = None, fluid_in_place_set: str | None = None, work: int | None = None, source_work: int | None = None) -> None
Add a value to an array over its cells (writes ADD; OPERATE ADDX; ADDREG).
ps.grid.Add("top_depth", value=10.0): target, the array, is named by its name, as
m.array(name) reads it; cells is a ps.Box (the whole grid when unstated) or a region
(ps.grid.InRegion). form is in_place (ADD), operate when it adds the value to
another array, source (OPERATE ADDX; OPERATER over a region), or region (ADDREG).
stage is the section a deck stated the statement in ("grid", "edit",
"props", "regions" or "solution"); unstated, the array’s own section.
fluid_in_place_set="ABC" writes the fluid-in-place set ABC (FIPABC), a key of
ps.Regions(fluid_in_place_sets=), where the target is “fluid_in_place_region”.
work=1 writes a deck’s work array WORK1 (a scratch array of the grid program), where
the target is “work”, and source_work=1 reads it, where the source is “work”;
“initial_water_saturation” is SWATINIT.
value is a bare number in the units of the target array (a ps.Quantity is refused).
It is a statement of the grid program, ps.Model(edits=[...]), at edits/<n>, written
in its stage’s section in program order.
ps.grid.Assign
Section titled “ps.grid.Assign”ps.grid.Assign(target: ArrayName | GridArrayReference, value: float | NDArray[np.float64], cells: Box | InRegion = <factory>, form: AssignForm | None = None, stage: MultiplierStageChoice | None = None, fluid_in_place_set: str | None = None, work: int | None = None) -> None
Set a grid array over its cells: one value (EQUALS, EQUALREG) or the array’s own card.
ps.grid.Assign("porosity", value=0.2, cells=ps.Box(k=(1, 2))); target, the array, is
named by its name, as m.array(name) reads it. form is in_place (EQUALS),
region over a region (EQUALREG), or array: the array’s own card (PORO, PORV,
…), between BOX and ENDBOX when the cells are a box narrower than the grid, one value written
for every cell, or a value per cell. stage is the section a deck stated the statement in
("grid", "edit", "props", "regions" or "solution"); unstated, the array’s
own section. A value per cell takes the array form unstated: ps.grid.Assign("porosity", value=np.full((2, 1, 3), 0.25), cells=ps.Box(i=(1, 2), j=(4, 4), k=(1, 3))). On a
transmissibility multiplier stated in the edit stage, the array form multiplies the grid
stage’s values rather than replacing them (Flow composes a face multiplier’s two stages).
fluid_in_place_set="ABC" writes the fluid-in-place set ABC (FIPABC), a key of
ps.Regions(fluid_in_place_sets=), where the target is “fluid_in_place_region”.
work=1 writes a deck’s work array WORK1 (a scratch array of the grid program), where
the target is “work”; “initial_water_saturation” is SWATINIT.
value is bare numbers in the units of the target array (a ps.Quantity is refused);
in the array form a face’s transmissibility multiplier is zero or more. It is a statement of
the grid program, ps.Model(edits=[...]), at edits/<n>, written in its stage’s
section in program order.
ps.grid.BoundaryCondition
Section titled “ps.grid.BoundaryCondition”ps.grid.BoundaryCondition(*, number: int, cells: Box, face: Literal['minus_i', 'plus_i', 'minus_j', 'plus_j', 'minus_k', 'plus_k'], kind: Optional[Literal['free', 'rate', 'dirichlet', 'thermal']], phase: Optional[Literal['oil', 'water', 'gas']] = None, rate: Optional[ScalarPropertyInput] = None, pressure: Optional[ScalarPropertyInput] = None, temperature: Optional[ScalarPropertyInput] = None) -> None
A condition on one face of a box of cells at the grid’s edge (Flow’s BCCON and BCPROP).
ps.grid.BoundaryCondition(number=1, cells=ps.Box(i=(1, 1), j=(1, 10), k=(1, 5)), face="minus_i", kind="dirichlet", phase="water", pressure=250.0). A model’s
boundary_conditions holds each boundary and its condition from the start of the run. Stated
in a ps.Event (m.at("2022-01-01", bc.replace(pressure=200.0))), a boundary
condition is the condition of the boundary of its number from that date on: its cells
and face are the ones boundary_conditions states for that number, and the date is one after
the run’s start, whose condition boundary_conditions states [lab_boundary.dated.start]. OPM
Flow applies a later condition only in the phase of the boundary’s first (none, for
"free" and "thermal"); one of another phase is written, read and ignored, and the
compile says so [lab_boundary.dated.phase].
Attributes
Section titled “Attributes”number : int
The boundary’s number, positive and one per boundary condition of the model.
cells : ps.Box
The cells whose face the condition stands on; an axis the box leaves out spans the
grid. The deck states all three ranges, so ps.load reads such a box back as the
same cells with each range stated.
face : str
Which face of those cells: "minus_i", "plus_i", "minus_j", "plus_j",
"minus_k" or "plus_k".
kind : str or None
What the face does: "free" lets fluid cross it at the state the face started in;
"rate" moves phase across it at rate; "dirichlet" holds phase at
pressure (and temperature, where stated) beyond it; "thermal" holds
temperature beyond it and moves no mass. None, in boundary_conditions: the boundary
states no condition from the start (its BCCON record and no BCPROP) and takes its
first where a ps.Event states one. Until then its face has no condition only while
no boundary of the model has one: once one has, OPM Flow gives the face the first
condition the model states (measured on 2026.04).
phase : str or None
The phase a "rate" or "dirichlet" condition moves: "oil", "gas" or
"water".
rate : float or None
A "rate" condition’s mass rate of phase per unit area of the face: negative into
the grid, positive out of it.
pressure : float, ps.Quantity or None
A "dirichlet" condition’s pressure beyond the face.
temperature : float or None
A "dirichlet" or "thermal" condition’s temperature beyond the face; None leaves
a thermal condition at the initial temperature. It needs an active thermal package.
rate is a mass per unit time per unit area, pressure a pressure and temperature
a temperature, bare numbers in the model’s units (pressure also a ps.Quantity).
The boundary’s cells and face write a BCCON record in GRID and its condition a BCPROP
record in SCHEDULE. From the start it is held in ps.Model(boundary_conditions=[...]),
at boundary_conditions/<n>; at a date it is at schedule/<date>/boundary_condition.
ps.grid.Cartesian
Section titled “ps.grid.Cartesian”ps.grid.Cartesian(*, shape: tuple[int, int, int], dx, dy, dz, top, active=None, coordinate_unit: Optional[Literal['metres', 'feet', 'centimetres']] = None, coordinate_reference: Optional[Literal['local_origin', 'map_coordinates']] = None, map_axes: MapAxes | None = None, map_units: Optional[Literal['metres', 'feet', 'centimetres']] = None, grid_orientation: GridOrientation | None = None, grid_system: GridSystem | None = None, minimum_pore_volume: MinPoreVolume | None = None, pinch_policy: Pinch | None = None, net_to_gross=None, pore_volume_scale: PoreVolumeScale | None = None, transmissibility_scheme: Optional[Literal['corner_point']] = None) -> None
A Cartesian grid: its shape, the cells’ widths and thicknesses, and the depth of its top.
ps.grid.Cartesian(shape=(10, 10, 3), dx=100.0, dy=100.0, dz=[20.0, 30.0, 50.0], top=2000.0)
is a 10 by 10 grid of three layers, 20, 30 and 50 thick, its top at 2000.
shape is (nx, ny, nz). dx, dy, and dz each take a single value, one value per
column, row or layer along their own axis (dz=[20.0, 30.0, 50.0]: DXV, DYV, DZV), or a
full (nx, ny, nz) array; net_to_gross and active a single value, one value per layer
(held in every cell of its layer) or a full array; top accepts a scalar or (nx, ny)
top-surface tensor.
dx, dy, dz and top are lengths (a bare number in the model’s units, or a
ps.Quantity): dx and dy positive; dz positive as one value per layer (DZV), and
zero (a pinched-out cell) or more as a single value or a full array. With coordinate_unit
stated and not the model’s own length unit, OPM Flow reads dx, dy, dz and top in
that unit (GRIDUNIT rescales the grid) while every other length stays in the model’s units;
the numbers are written as given, a ps.Quantity converted to the model’s units, so a
quantity is then read in the wrong unit.
active marks each cell active (1) or not (0): a mask with inactive cells is written as
EQUALS records setting ACTNUM to 0 over boxes of them, one with none as ACTNUM, every cell 1.
net_to_gross is a fraction.
Whether the run writes the INIT file is the model’s output (ps.Output(static_file=)).
It writes DIMENS in RUNSPEC and DX, DY, DZ (DXV, DYV, DZV for widths along an axis) and
TOPS in GRID, with ACTNUM (in the form active above says), NTG and the cards of its
policies (MINPV, PINCH, MULTPV, MAPAXES, …). It is ps.Model(grid=...), at grid,
each field below it: grid/shape.
Examples
Section titled “Examples”import petroscript as ps grid = ps.grid.Cartesian(shape=(10, 10, 3), dx=100.0, dy=100.0, # m: metric … dz=[20.0, 30.0, 50.0], top=2000.0) # layer by layer grid.shape (10, 10, 3) m = ps.load(“tests/results_fixtures/QUICKSTART/MERLE.DATA”) # 10 x 10 x 5 m.get(“grid/dz”) # m 20.0
ps.grid.CellDepthOverride
Section titled “ps.grid.CellDepthOverride”ps.grid.CellDepthOverride(value: float, cells: Box = <factory>) -> None
The depth of each cell’s centre over a box of cells (DEPTH).
ps.grid.CellDepthOverride(value=2000.0, cells=ps.Box(k=(1, 1))): value is the depth and
cells the ps.Box it is stated over (the whole grid when left out).
ps.grid.CellTransmissibilityField
Section titled “ps.grid.CellTransmissibilityField”ps.grid.CellTransmissibilityField(*, direction: Literal['x', 'y', 'z'], values) -> None
An explicit transmissibility across one axis’s positive faces, per cell.
TRANX overwrites the +X-face transmissibility of EVERY cell with a value the deck states outright (manual 7.3.69; TRANY 7.3.70 and TRANZ 7.3.71 say the same for the +Y and +Z faces). It is how a modeller pins flow across a surface without touching permeability, and the corpus states it as a field that varies cell by cell — a barrier is one column of zeros inside an otherwise uniform sheet.
A UNIFORM OVERRIDE IS A DIFFERENT STATEMENT and keeps its own surface: one
value over the whole grid is a set-value edit and renders as EDIT-section
EQUALS, which is what the deck that states it says. The field is for
what a scalar cannot say. Collapsing a varying field to any one value —
its first, its mean, its mode — states a grid the deck did not: on
mult/MULT2D_TRANS it would open five sealed faces.
The values are a full cell array, never a box: the deck’s card carries one number per cell and the field restates exactly that.
ps.grid.Clip
Section titled “ps.grid.Clip”ps.grid.Clip(target: ArrayName | GridArrayReference, min: float | None = None, max: float | None = None, cells: Box = <factory>, form: ClipForm | None = None, stage: MultiplierStageChoice | None = None, fluid_in_place_set: str | None = None, work: int | None = None) -> None
Hold an array between bounds over its cells (MINVALUE, MAXVALUE).
ps.grid.Clip("porosity", min=0.01); target, the array, is named by its name, as
m.array(name) reads it; min raises the cells below it, max lowers the cells above
it, and a statement stating both writes both cards, the lower bound first. cells is a
ps.Box (the whole grid when unstated); form is in_place. stage is the section a
deck stated the statement in ("grid", "edit", "props", "regions" or
"solution"); unstated, the array’s own section.
fluid_in_place_set="ABC" writes the fluid-in-place set ABC (FIPABC), a key of
ps.Regions(fluid_in_place_sets=), where the target is “fluid_in_place_region”.
work=1 writes a deck’s work array WORK1 (a scratch array of the grid program), where
the target is “work”; “initial_water_saturation” is SWATINIT.
min and max are bare numbers in the units of the target array (a ps.Quantity is
refused), min no greater than max. It is a statement of the grid program,
ps.Model(edits=[...]), at edits/<n>, written in its stage’s section in program
order.
ps.grid.ConnectionTransmissibilityEdit
Section titled “ps.grid.ConnectionTransmissibilityEdit”ps.grid.ConnectionTransmissibilityEdit(*, first: tuple[typing.Annotated[int, Strict(strict=True)], typing.Annotated[int, Strict(strict=True)], typing.Annotated[int, Strict(strict=True)]], second: tuple[typing.Annotated[int, Strict(strict=True)], typing.Annotated[int, Strict(strict=True)], typing.Annotated[int, Strict(strict=True)]], mode: Literal['multiply', 'override'], value: ScalarPropertyInput) -> None
A transmissibility edit of the connection two cells share, multiplied or replaced.
ps.grid.ConnectionTransmissibilityEdit(first=(1, 1, 1), second=(5, 1, 1), mode="multiply", value=0.1) cuts the transmissibility of the connection between those two cells to a tenth.
The connection is one the run builds (a non-neighbour connection of a fault, of the
geometry or of a ps.grid.NonNeighbor). mode="multiply" writes EDITNNC, value a
dimensionless multiplier; "override" writes EDITNNCR, value the transmissibility in
the model’s units; either zero or more. It is written in EDIT and held in
ps.Model(non_neighbor_connections=[...]), at non_neighbor_connections/<n>.
ps.grid.Copy
Section titled “ps.grid.Copy”ps.grid.Copy(target: ArrayName | GridArrayReference, source: ArrayName | GridArrayReference, cells: Box | InRegion = <factory>, from_cells: Box | None = None, to_cells: Box | None = None, form: CopyForm | None = None, fluid_in_place_set: str | None = None, source_fluid_in_place_set: str | None = None, work: int | None = None, source_work: int | None = None) -> None
Copy one array into another over its cells (COPY; COPYREG over a region).
ps.grid.Copy("permeability_y", source="permeability_x"): target, the array written,
and source, the array read, are named by their names, as m.array(name) reads them.
cells is a ps.Box (the whole grid when unstated) or a region (ps.grid.InRegion);
from_cells and to_cells, stated together, copy one box of an array into another box
of the same array, the two of one shape (COPYBOX). form is copy (COPY), or
region over a region (COPYREG).
fluid_in_place_set="ABC" writes the fluid-in-place set ABC (FIPABC), a key of
ps.Regions(fluid_in_place_sets=), where the target is “fluid_in_place_region”;
source_fluid_in_place_set="ABC" reads that set where the source is “fluid_in_place_region”.
work=1 writes a deck’s work array WORK1 (a scratch array of the grid program), where
the target is “work”, and source_work=1 reads it, where the source is “work”;
“initial_water_saturation” is SWATINIT.
It is a statement of the grid program, ps.Model(edits=[...]), at edits/<n>, written
in the target array’s section in program order.
ps.grid.CornerPoint
Section titled “ps.grid.CornerPoint”ps.grid.CornerPoint(*, shape: tuple[int, int, int], pillars: NDArray[numpy.float64], corner_depths: NDArray[numpy.float64], active: NDArray[numpy.int64] | None = None, coordinate_unit: Optional[Literal['metres', 'feet', 'centimetres']] = None, coordinate_reference: Optional[Literal['local_origin', 'map_coordinates']] = None, map_axes: MapAxes | None = None, map_units: Optional[Literal['metres', 'feet', 'centimetres']] = None, grid_orientation: GridOrientation | None = None, grid_system: GridSystem | None = None, minimum_pore_volume: MinPoreVolume | None = None, pinch_policy: Pinch | None = None, net_to_gross=None, transmissibility_scheme: Optional[Literal['corner_point']] = None, provenance: SourceProvenance | None = None) -> None
Corner-point grid geometry: pillars and the depths of every cell’s corners.
ps.grid.CornerPoint.from_box(box) is the corner-point grid of the Cartesian grid box.
shape is (nx, ny, nz). pillars holds the (nx + 1) * (ny + 1) pillars, each as
the x, y and z of its top point then of its bottom point (six values per pillar, COORD’s
order); corner_depths holds the depth of each cell’s eight corners (ZCORN’s order:
for each layer the top plane then the bottom plane). active optionally marks the
active cells, an (nx, ny, nz) array of 0 and 1. A GRDECL file reads into one through
ps.io.read_grdecl; from_box and from_surface build one from a Cartesian grid and
from a top surface. The pillar coordinates and corner depths are lengths, bare numbers in
the model’s units; with coordinate_unit stated and not the model’s own length unit, OPM
Flow reads them in that unit instead (GRIDUNIT rescales the grid) while every other length
stays in the model’s units.
It writes DIMENS in RUNSPEC and SPECGRID, COORD and ZCORN in GRID, with ACTNUM and the cards
of its policies. It is ps.Model(grid=...), at grid, each field below it:
grid/pillars.
ps.grid.DirectionalTransmissibilityMultiplier
Section titled “ps.grid.DirectionalTransmissibilityMultiplier”ps.grid.DirectionalTransmissibilityMultiplier(*, direction: Literal['plus_i', 'minus_i', 'plus_j', 'minus_j', 'plus_k', 'minus_k', 'outward_radial', 'inward_radial', 'plus_theta', 'minus_theta'], factor, cells: Box = <factory>, stage: Optional[Literal['grid', 'edit', 'props', 'regions', 'solution']] = None) -> None
Multiply transmissibility across one semantic grid-face family.
ps.grid.FaultMultiplier
Section titled “ps.grid.FaultMultiplier”ps.grid.FaultMultiplier(*, fault: str, factor: ScalarPropertyInput) -> None
The transmissibility multiplier of a fault, named as the model’s faults declare it.
ps.grid.FaultMultiplier(fault="F1", factor=0.1) writes one MULTFLT record in GRID, from
the start of the run. Stated in ps.Event(date, ...) (or a ps.actions.When body) it
multiplies the fault’s transmissibility from that date: a run of them standing together is
one MULTFLT card in SCHEDULE, and successive dates multiply as Flow reads them (0.5 then 0.5 is
0.25; 1.0 changes nothing; measured on the pinned image, the cards spaced by DATES and by TSTEP
alike).
factor is dimensionless, zero or more. From the start it is held beside the faults,
ps.Model(faults=[ps.Fault(...), ps.grid.FaultMultiplier(...)]), at faults/<n> (or in
the grid program, edits/<n>); at a date it is at schedule/<date>/fault_multiplier.
ps.grid.GridOrientation
Section titled “ps.grid.GridOrientation”ps.grid.GridOrientation(*, i_order: Literal['increasing', 'decreasing'], j_order: Literal['increasing', 'decreasing'], k_order: Literal['increasing', 'decreasing'], depth: Literal['up', 'down'], handedness: Literal['left', 'right']) -> None
The grid’s axis ordering, depth direction and handedness, as a deck records them (GDORIENT).
ps.grid.GridOrientation(i_order="increasing", j_order="increasing", k_order="increasing", depth="down", handedness="right") is the usual frame, recorded for the grid file.
The five items match OPM Flow’s own EGRID header record: the property ordering in each of I, J and K, the direction the Z axis runs, and the handedness of the frame. Post-processing metadata, not a flow control: OPM Flow states it has no effect on the results.
It writes GDORIENT in GRID. It is a grid’s grid_orientation, at
grid/grid_orientation.
ps.grid.GridSystem
Section titled “ps.grid.GridSystem”ps.grid.GridSystem(*, layer_range: tuple[int, int], circle: Optional[Literal['complete', 'incomplete']] = None, connection: Optional[Literal['join', 'separate']] = None, lower_reservoir: int | None = None, upper_reservoir: int | None = None) -> None
A deck’s reservoir grid system over a range of layers (COORDSYS), which OPM Flow ignores.
ps.grid.GridSystem(layer_range=(1, 3)) states one reservoir over layers 1 to 3.
The items are what COORDSYS states: layer_range is the inclusive K-direction bound pair
the system spans (from 1); circle (“complete” or “incomplete”) says whether a radial
grid’s THETA direction closes; connection (“join” or “separate”) whether the reservoir
below is joined or isolated, and lower_reservoir/upper_reservoir number the units it
joins (from 1). Absent optional fields keep the deck’s own defaulted slots. OPM Flow
supports one grid system and ignores the card (“COORDSYS is ignored”), so none of these
changes a run: the class keeps what a loaded deck states.
It writes COORDSYS in GRID. It is a grid’s grid_system, at grid/grid_system.
ps.grid.InRegion
Section titled “ps.grid.InRegion”ps.grid.InRegion(map: RegionMap, region: int) -> None
The cells of one region of a region map, as a grid statement’s cells.
ps.grid.InRegion("operation", 3) is the cells the model’s operation map
(ps.Regions(operation=...)) puts in region 3. map names the map, one of
"flux", "transmissibility" or "operation", in lower case or in capitals; region
is the region number, from 1. The statement reads the map the model holds where the
statement stands: the map stated on ps.Regions, as the grid program’s earlier statements
left it. A statement that edits a region map itself takes a box, not a region: Flow applies no
region-scoped arithmetic to a region map.
It is no card of its own. The statement holding it takes its region form (form="region",
or "operate" when it reads another array), whose card names the map and the region. It
is the cells of the statement that holds it, which is at edits/<n> (an address names
the statement, not its fields).
ps.grid.MapAxes
Section titled “ps.grid.MapAxes”ps.grid.MapAxes(*, y_axis, origin, x_axis) -> None
The map frame a grid’s coordinates sit in: the origin and a point on each map axis.
ps.grid.MapAxes(y_axis=(0.0, 100.0), origin=(0.0, 0.0), x_axis=(100.0, 0.0)) is the frame
whose axes are the map’s own.
The three coordinate pairs are the points MAPAXES states: one point on the map y-axis, the map origin, and one point on the map x-axis, each an (x, y) on the map (bare numbers, map coordinates). It places the grid on the map for the files a run writes; it does not change the flow. The three points must differ: a frame that repeats a point spans no axes.
It writes MAPAXES in GRID. It is a grid’s map_axes, at grid/map_axes.
ps.grid.MinPoreVolume
Section titled “ps.grid.MinPoreVolume”ps.grid.MinPoreVolume(*, threshold: Optional[ScalarPropertyInput] = None, per_cell_thresholds=None) -> None
The pore volume below which a cell is made inactive (MINPV, MINPVV).
ps.grid.MinPoreVolume(threshold=0.001) makes every cell of a pore volume under 0.001
inactive.
threshold and per_cell_thresholds are reservoir volumes, positive bare numbers in
the model’s units; at least one is stated. It writes MINPV and MINPVV in GRID. It is a
grid’s minimum_pore_volume, at grid/minimum_pore_volume.
ps.grid.NonNeighbor
Section titled “ps.grid.NonNeighbor”ps.grid.NonNeighbor(*, first: tuple[typing.Annotated[int, Strict(strict=True)], typing.Annotated[int, Strict(strict=True)], typing.Annotated[int, Strict(strict=True)]], second: tuple[typing.Annotated[int, Strict(strict=True)], typing.Annotated[int, Strict(strict=True)], typing.Annotated[int, Strict(strict=True)]], transmissibility: ScalarPropertyInput) -> None
The transmissibility between two cells that are not neighbours (NNC).
ps.grid.NonNeighbor(first=(1, 1, 1), second=(3, 1, 2), transmissibility=5.0): each
cell its 1-based (i, j, k).
transmissibility is a positive transmissibility, a bare number in the model’s units; the
two cells differ and lie inside the grid. It writes one NNC record in GRID. It is held in
ps.Model(non_neighbor_connections=[...]), at non_neighbor_connections/<n> (or in the
grid program, edits/<n>).
ps.grid.NonNeighborConnectionPolicy
Section titled “ps.grid.NonNeighborConnectionPolicy”ps.grid.NonNeighborConnectionPolicy(*, ignore_simulator_connections: Annotated[bool, Strict(strict=True)] = False) -> None
Whether the run builds non-neighbour connections at all (NONNC).
ps.grid.NonNeighborConnectionPolicy(ignore_simulator_connections=True) makes the run build
no non-neighbour connection.
True writes NONNC in RUNSPEC; False writes nothing. It is held in
ps.Model(non_neighbor_connections=[...]), at non_neighbor_connections/<n>.
ps.grid.Operate
Section titled “ps.grid.Operate”ps.grid.Operate(target: ArrayName | GridArrayReference, operator: EquationFormChoice, source: ArrayName | GridArrayReference | None = None, alpha: float | None = None, beta: float | None = None, cells: Box | InRegion = <factory>, form: OperateForm | None = None, stage: MultiplierStageChoice | None = None, fluid_in_place_set: str | None = None, source_fluid_in_place_set: str | None = None, work: int | None = None, source_work: int | None = None) -> None
Write an operator applied to another array into an array (OPERATE; OPERATER over a region).
ps.grid.Operate("permeability_z", operator="affine", source="permeability_x", alpha=0.1, beta=1.0): target, the array written, and source, the array read, are named by
their names, as m.array(name) reads them. alpha and beta are the operator’s two
parameters, each stated where the operator uses it. cells is a ps.Box (the whole grid
when unstated) or a region (ps.grid.InRegion); form is operate. stage is the
section a deck stated the statement in ("grid", "edit", "props", "regions"
or "solution"); unstated, the arrays’ own.
fluid_in_place_set="ABC" writes the fluid-in-place set ABC (FIPABC), a key of
ps.Regions(fluid_in_place_sets=), where the target is “fluid_in_place_region”;
source_fluid_in_place_set="ABC" reads that set where the source is “fluid_in_place_region”.
work=1 writes a deck’s work array WORK1 (a scratch array of the grid program), where
the target is “work”, and source_work=1 reads it, where the source is “work”;
“initial_water_saturation” is SWATINIT.
operator is one of "affine", "power_law", "accumulate_power",
"reciprocal", "absolute", "log10", "natural_log", "scaled_log",
"cap", "floor", "clone" or "product"; alpha is required by affine,
power_law, accumulate_power, scaled_log, cap and floor, beta by the first four; an
alpha its operator does not use is refused, and so is a beta other than 1.0. They
are bare numbers (a ps.Quantity is refused). It is a statement of the grid program,
ps.Model(edits=[...]), at edits/<n>, written in its stage’s section in program
order.
ps.grid.Pinch
Section titled “ps.grid.Pinch”ps.grid.Pinch(*, minimum_thickness: Optional[ScalarPropertyInput] = None, gap_mode: Literal['gap', 'no_gap'], maximum_gap: Optional[ScalarPropertyInput] = None, transmissibility_mode: Optional[Literal['adjacent_active_cells', 'all_pinched_cells']] = None, multiplier_scope: Optional[Literal['top_active_cell', 'all_pinched_cells']] = None) -> None
How cells connect vertically across layers that pinch out (PINCH).
ps.grid.Pinch(gap_mode="no_gap", maximum_gap=5.0) connects the cells above and below layers
that pinch out, across an empty gap of at most 5.
minimum_thickness is the thickness below which a cell counts as pinched out, and
maximum_gap the gap a connection may cross; each is a positive length, a bare number in
the model’s units or a ps.Quantity. minimum_thickness=None leaves the threshold to
Flow (1*): 0.001 m in every unit system, which is not 0.001 of a deck’s own length unit.
It writes PINCH in GRID. It is a grid’s pinch_policy, at grid/pinch_policy.
ps.grid.PoreVolumeOverride
Section titled “ps.grid.PoreVolumeOverride”ps.grid.PoreVolumeOverride(value: float, cells: Box = <factory>) -> None
The pore volume of each cell over a box of cells (PORV).
ps.grid.PoreVolumeOverride(value=10.0, cells=ps.Box(i=(1, 1))): value is the pore
volume and cells the ps.Box it is stated over (the whole grid when left out).
ps.grid.PoreVolumeScale
Section titled “ps.grid.PoreVolumeScale”ps.grid.PoreVolumeScale(factor: float, cells: Box = <factory>) -> None
A multiplier of each cell’s pore volume over a box of cells (MULTPV).
ps.grid.PoreVolumeScale(factor=0.5, cells=ps.Box(i=(1, 2))): factor multiplies the pore
volume of the cells of cells, a ps.Box (the whole grid when left out).
ps.grid.Radial
Section titled “ps.grid.Radial”ps.grid.Radial(*, shape: tuple[int, int, int], inner_radius: ScalarPropertyInput, radial_widths, angular_widths, dz, top, active=None, coordinate_unit: Optional[Literal['metres', 'feet', 'centimetres']] = None, coordinate_reference: Optional[Literal['local_origin', 'map_coordinates']] = None, map_axes: MapAxes | None = None, map_units: Optional[Literal['metres', 'feet', 'centimetres']] = None, grid_orientation: GridOrientation | None = None, grid_system: GridSystem | None = None, minimum_pore_volume: MinPoreVolume | None = None, pinch_policy: Pinch | None = None, net_to_gross=None, transmissibility_scheme: Optional[Literal['corner_point']] = None) -> None
Radial grid geometry described with semantic radial/angular extents.
ps.grid.Radial(shape=(4, 1, 2), inner_radius=0.1, radial_widths=[1.0, 5.0, 20.0, 100.0], angular_widths=[360.0], dz=10.0, top=2000.0) is four rings round a wellbore of radius 0.1, two
layers deep.
shape is (nr, ntheta, nz). radial_widths describes the radial cell
widths after inner_radius; angular_widths describes each theta sector
in degrees. dz, active and net_to_gross each take a single value, one value per
layer (dz=[5.0, 10.0], held in every cell of its layer) or a full (nr, ntheta, nz)
array, and top a single value or a (nr, ntheta) array, letting authors name geometry
rather than write radial deck cards directly. inner_radius (positive), radial_widths,
dz and top are lengths: inner_radius, dz and top a bare number in the model’s
units or a ps.Quantity, radial_widths bare numbers. With coordinate_unit stated and
not the model’s own length unit, OPM Flow reads these four in that unit (GRIDUNIT rescales
the grid) while every other length stays in the model’s units; the numbers are written as
given, a ps.Quantity converted to the model’s units, so a quantity is then read in the
wrong unit.
It writes RADIAL in RUNSPEC and INRAD, DRV, DTHETAV, DZ and TOPS in GRID. It is
ps.Model(grid=...), at grid, each field below it: grid/inner_radius.
ps.grid.RegionPairMultiplier
Section titled “ps.grid.RegionPairMultiplier”ps.grid.RegionPairMultiplier(*, from_region: Optional[Annotated[int, Strict(strict=True)]] = None, to_region: Optional[Annotated[int, Strict(strict=True)]] = None, factor: ScalarPropertyInput, directions: tuple[typing.Literal['x', 'y', 'z'], ...], connections: Literal['all', 'neighbor_only', 'non_neighbor_only', 'except_aquifer_non_neighbor'] = 'all', map: Optional[Literal['flux', 'transmissibility', 'operation']] = None, stage: Optional[Literal['grid', 'edit', 'props', 'regions', 'solution']] = None) -> None
A transmissibility multiplier between two regions of a region map (MULTREGT).
ps.grid.RegionPairMultiplier(from_region=1, to_region=2, factor=0.0, directions=["x", "y"], map="flux") seals the connections across x and y between flux regions 1 and 2.
A region id left UNSTATED means every region. Manual 6.3.138 gives items 1
and 2 no default value, and a deck that writes 2* there is coupling the
whole map rather than naming a pair — opm-tests/editnnc/ NNC_OVERLAPPING_EDITNNCR_MULTREGT states exactly that. The absence is
carried as an absence rather than resolved into a list of pairs: which
pairs it covers depends on what the region map holds when the card is read,
and restating one number where the deck wrote none would name a coupling
the deck did not.
factor is dimensionless, zero or more. It writes one MULTREGT record, in GRID unless a
deck stated it in another section (stage: two stages on one pair do not compose, the
later replaces the earlier). It is held in ps.Model(transmissibility=[...]), at
transmissibility/<n> (or in the grid program, edits/<n>).
ps.grid.RegionalPoreVolumeMultiplier
Section titled “ps.grid.RegionalPoreVolumeMultiplier”ps.grid.RegionalPoreVolumeMultiplier(*, region: Annotated[int, Strict(strict=True)], factor: ScalarPropertyInput, map: Literal['flux', 'transmissibility', 'operation']) -> None
Scale pore volume for cells in a typed semantic region-map family.
ps.grid.RockCompaction
Section titled “ps.grid.RockCompaction”ps.grid.RockCompaction(*, model: Literal['reversible', 'irreversible'], water_induced: bool, loading, unloading, water_saturation_nodes, overburden) -> None
Water-induced rock compaction (ROCKCOMP, ROCK2D, ROCK2DTR, ROCKWNOD, OVERBURD).
Pore-volume and transmissibility multipliers tabulated against pressure and water saturation, one table of each per rock region.
model is the compaction’s response when pressure rises again: "reversible" or
"irreversible". water_induced states the water-induced option (ROCKCOMP item 3), True:
the tables below are read only with it.
Each table field holds one table per rock region, in region order, and every one of them
the same number of regions:
loading(ROCK2D) holds the pore-volume multipliers andunloading(ROCK2DTR) the transmissibility multipliers, whatever their names say: neither is a loading or an unloading branch. Both are rows of a pressure, then one multiplier per water-saturation node of the region, in node order:(pressure, *multipliers), or a mapping{"pressure": ..., "multipliers": [...]};water_saturation_nodes(ROCKWNOD): the region’s water saturations, rising, each a number or a mapping{"water_saturation": ...};overburden(OVERBURD): rows of(depth, overburden_pressure), or mappings by those names.
ps.grid.RockCompaction(model="irreversible", water_induced=True, loading=[[(1000.0, 0.9, 0.95), (3000.0, 1.0, 1.0)]], unloading=[[(1000.0, 0.8, 0.9), (3000.0, 1.0, 1.0)]], water_saturation_nodes=[[0.2, 0.8]], overburden=[[(8000.0, 500.0), (9000.0, 900.0)]]) states one region with two nodes.
The pressures, depths and overburden pressures are bare numbers in the model’s units, the
multipliers dimensionless and the nodes water saturations (fractions); water_induced
must be True and every region holds two nodes or more (OPM Flow interpolates between them).
It writes ROCKCOMP in RUNSPEC and ROCK2D, ROCK2DTR, ROCKWNOD and OVERBURD in PROPS. It is
the rock’s compaction, ps.Rock(compaction=...), at rock/compaction.
ps.grid.RockPressureCompaction
Section titled “ps.grid.RockPressureCompaction”ps.grid.RockPressureCompaction(*, tables, reversibility: Literal['reversible', 'irreversible'] = 'reversible', store_initial_pressure: bool = False, table_regions: Literal['pvt_regions', 'rock_regions'] = 'pvt_regions') -> None
Pore-volume and transmissibility multipliers against pressure (ROCKCOMP, ROCKTAB, ROCKOPTS).
tables holds one table per compaction region, in region order; a table’s rows are
(pressure, pore_volume_multiplier, transmissibility_multiplier), or mappings by those
names: ps.grid.RockPressureCompaction(tables=[[(2000.0, 0.96, 0.965), (4500.0, 1.0, 1.0)]]). reversibility selects whether pore volume recovers on repressurization,
store_initial_pressure references the multipliers at the initial grid pressures, and
table_regions selects the region family that allocates the tables to cells
("pvt_regions", PVTNUM, or "rock_regions", ROCKNUM).
Each row’s pressure is a bare number in the model’s units and its two multipliers are
dimensionless. It writes ROCKCOMP in RUNSPEC and ROCKTAB (with ROCKOPTS where its options
ask) in PROPS. It is the rock’s compaction, ps.Rock(compaction=...), at
rock/compaction.
ps.grid.Scale
Section titled “ps.grid.Scale”ps.grid.Scale(target: ArrayName | GridArrayReference, by: float | NDArray[np.float64], source: ArrayName | GridArrayReference | None = None, cells: Box | InRegion = <factory>, form: ArithmeticForm | None = None, stage: MultiplierStageChoice | None = None, fluid_in_place_set: str | None = None, work: int | None = None, source_work: int | None = None) -> None
Multiply an array by a factor over its cells (MULTIPLY; OPERATE MULTX; MULTIREG).
ps.grid.Scale("permeability_x", by=0.5, cells=ps.Box(k=(1, 3))): target, the array, is
named by its name, as m.array(name) reads it (“porosity”, “permeability_x”,
“net_to_gross”, “transmissibility_multiplier_z”, …; a miss names the nearest);
cells=ps.grid.InRegion("operation", 3) scales region 3 of the operation map. form
is in_place (MULTIPLY), operate when it multiplies another array, source, into
the target (OPERATE MULTX; OPERATER over a region), or region (MULTIREG), the form a
statement over a region takes when it states none. The pore volume scaled over a region in
the grid stage, ps.grid.Scale("pore_volume", by=0.5, cells=ps.grid.InRegion("flux", 2), stage="grid"), is the regional pore-volume multiplier (MULTREGP): one for each region
number, whatever its map. At a date (in a ps.Event) it scales a face’s transmissibility
multiplier from that date on (MULTX … MULTZ- in SCHEDULE; successive ones compose by
multiplication): m.at("2021-03-01", ps.grid.Scale("transmissibility_multiplier_x", by=0.1, cells=ps.Box(i=(1, 9)))), by one number, or one per cell shaped as its box (along i, j
and k).
fluid_in_place_set="ABC" writes the fluid-in-place set ABC (FIPABC), a key of
ps.Regions(fluid_in_place_sets=), where the target is “fluid_in_place_region”.
work=1 writes a deck’s work array WORK1 (a scratch array of the grid program), where
the target is “work”, and source_work=1 reads it, where the source is “work”;
“initial_water_saturation” is SWATINIT.
stage is the section a deck stated the statement in ("grid", "edit",
"props", "regions" or "solution"); unstated, the array’s own section.
by is a dimensionless factor (a ps.Quantity is refused): zero or more where it scales
a region’s pore volume in the grid stage, and at a date. In the grid program it is a
statement of ps.Model(edits=[...]), at edits/<n>; at a date it is at
schedule/<date>/scale.
ps.grid.SourceTerm
Section titled “ps.grid.SourceTerm”ps.grid.SourceTerm(*, cell: tuple[int, int, int], component: Literal['oil', 'gas', 'water', 'solvent', 'polymer', 'microbes', 'oxygen', 'urea'], rate: ScalarPropertyInput, heat_rate: Optional[ScalarPropertyInput] = None, temperature: Optional[ScalarPropertyInput] = None) -> None
A source or sink of one component in one grid cell (Flow’s SOURCE), from a date on.
ps.Event("2021-06-01", ps.grid.SourceTerm(cell=(5, 5, 1), component="water", rate=10.0)): the cell takes rate of component from that date. Each source term
is one SOURCE record, written at the event’s place in the schedule.
Attributes
Section titled “Attributes”cell : tuple of int
The cell (i, j, k), 1-based.
component : str
What the cell takes or gives: "oil", "gas", "water", "solvent",
"polymer", "microbes", "oxygen" or "urea".
rate : float
The mass rate of component: positive into the cell, negative out of it.
heat_rate : float or None
The heat the source brings with it, per unit time; it needs an active thermal package.
temperature : float or None
The temperature of what the source brings, in place of heat_rate; it needs an
active thermal package.
rate is a mass per unit time, heat_rate an energy per unit time and
temperature a temperature, bare numbers in the model’s units. It is a dated change, at
schedule/<date>/source_term.
ps.grid.Spider
Section titled “ps.grid.Spider”ps.grid.Spider(*, shape: tuple[int, int, int], inner_radius: ScalarPropertyInput, radial_widths, angular_widths, dz, top, active=None, coordinate_unit: Optional[Literal['metres', 'feet', 'centimetres']] = None, coordinate_reference: Optional[Literal['local_origin', 'map_coordinates']] = None, map_axes: MapAxes | None = None, map_units: Optional[Literal['metres', 'feet', 'centimetres']] = None, grid_orientation: GridOrientation | None = None, grid_system: GridSystem | None = None, minimum_pore_volume: MinPoreVolume | None = None, pinch_policy: Pinch | None = None, net_to_gross=None, transmissibility_scheme: Optional[Literal['corner_point']] = None) -> None
A spider-web radial grid: rings and sectors round a well, with the fields of Radial.
ps.grid.Spider(shape=(4, 4, 2), inner_radius=0.1, radial_widths=[1.0, 5.0, 20.0, 100.0], angular_widths=[90.0, 90.0, 90.0, 90.0], dz=10.0, top=2000.0) is four rings of four sectors,
two layers deep.
It writes SPIDER in RUNSPEC, and in GRID the cards a ps.grid.Radial writes. A radial grid
states RADIAL where this one states SPIDER. It is ps.Model(grid=...), at grid; its
replace is ps.grid.Radial’s.
PetroScript is licensed under the Apache License 2.0. © 2026 Blauweiss EDV LLC. PetroScript by Blauweiss AI.For AI agents: llms.txt