Skip to content

ps.grid

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

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

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

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