Skip to content

ps.actions

ps.actions.PyAction(name: str, path: str = '', run_mode: PythonHookRunModeChoice = 'once', script: str | None = None) -> None

A Python hook the run calls: ps.actions.PyAction(name="WTEST", path="wtest.py").

name names the hook (PYACTION record 1, item 1: upper-case letters, digits and _+.-, starting with a letter); path is the Python file Flow runs, read from the deck’s folder (record 2, written as a quoted word: Flow gives it no default, so a hook stating none is refused); run_mode is how often it runs: "once", "every_timestep" or "until_true" (record 1, item 2: SINGLE, UNLIMITED, FIRST_TRUE; an unwritten one is SINGLE); script is the file’s text, written beside the deck as path (ps.load reads it when the deck’s folder holds it as UTF-8 text; None holds no file, and the run’s folder must then hold it; a script written over the deck, or over another hook’s other script, is refused). Stated in ps.Event(date, ...), as a ps.actions.When is, and written there; its address is schedule/<date>/py_action. PetroScript never imports, evaluates or runs the file.

ps.actions.UserQuantity(*, name: str, statements, units: str | None = None, stated_units_position: int | None = None) -> None

A user quantity (UDQ): a name the run evaluates from a formula or a number.

ps.actions.UserQuantity(name="FU_CAP", expression=ps.actions.field().oil_production_rate.minimum(1000.0)) is the field’s oil rate capped at 1000, a quantity a rule’s condition or a control may read. Its formula reads every member of its own scope (every well of a WU…, every group of a GU…) or one value, as Flow does: a field quantity names its group (ps.actions.group(ps.Group(name="FIELD"))), and Flow evaluates field, group, well and segment quantities only.

expression= states one formula (a number is a constant); statements= states each record in order instead: a value for some wells or groups ({"expression": 4.0, "well": "PROD"}, written ASSIGN) or a switch of the name’s evaluation ({"evaluated": False}, written UPDATE). The quantity is itself an expression: a formula, a rule’s condition or a control’s rate reads it by its name. It is stated in ps.Event(date, ...) and written there in a UDQ card (DEFINE, ASSIGN, UPDATE, UNITS); its address is schedule/<date>/user_quantity.

ps.actions.UserTable(name: str, interpolation: InterpolationModeChoice, x: tuple[float, ...] | ColumnIn, y: tuple[float, ...] | ColumnIn) -> None

A user table (UDT): a one-dimensional lookup a user quantity’s formula reads.

ps.actions.UserTable(name="TU_FBHP", interpolation="linear_clamped", x=(100.0, 500.0), y=(100.0, 180.0)) reads 100 at argument 100 and 180 at 500, a straight line between and the end values beyond.

name is the table’s name, which a user quantity’s formula looks it up by (table.lookup(x), written TU_FBHP[x]): Flow evaluates a lookup only of a name that starts with TU_, and the constructor does not check the prefix. A name that starts with TU but no underscore loads, then aborts the run when Flow evaluates the quantity that reads it. x holds the argument values and y the value at each; Flow refuses a table whose arguments do not rise strictly, which the constructor does not check either. interpolation is how a value is read: "linear_clamped" (a straight line between points, the end values beyond), "linear_extrapolated" (the end lines carried on beyond) or "nearest" (the nearer point’s value). A table is stated in the schedule’s first ps.Event and written at the deck’s head; a later date is refused (schedule.event.start_only). Its address is schedule/<start>/user_table.

ps.actions.When(*conditions: Expression, name: str, condition: Expression, then: tuple[Flux | Output | Numerics | Efficiency | Well | Adjust | Regroup | History | InjectionHistory | ControlDefaults | HistoryControl | Open | Shut | Stop | Perforate | Scale | FaultMultiplier | VaporizationControls | WellTest | Economics | Economics | GuideRate | ProductivityIndex | ProductivityMultiplier | TargetMultiplier | InjectionMultiplier | InjectionDamage | InjectedStream | FilterCake | WellList | PressureAveraging | Survey | DamageFactorCorrection | Cycling | NonDarcyFactor | Produce | Inject | Group | ProductionTarget | InjectionTarget | GuideRates | PressureMaintenance | GasSales | FuelGas | Satellite | Branch | Node | Efficiency | SourceTerm | BoundaryCondition | When | DissolutionLimits | ConvectiveDissolution | PyAction | UserTable | UserQuantity | Segments | Valve | ICD | AICD | Lump | Skin | GasLiftOptimization | GasLift, ...], runs: int | None = None, minimum_wait: Optional[ScalarPropertyInput] = None, stated_body_scopes: tuple[tuple[str, int, tuple[int | None, ...] | None], ...] = (), stated_body_cards: tuple[tuple[str, int, int], ...] = ()) -> None

An operating rule (ACTIONX): when its conditions hold, its changes apply.

ps.actions.When(*conditions, then=[...], name=, runs=, minimum_wait=): the conditions are expressions over the run’s quantities (ps.actions.well(p1).water_cut > 0.85, ps.actions.date() >= "2026-03-01"), all of which must hold; then lists the changes, which may name the wells whose condition fired as well="?"; runs is how many times the rule may fire. A rule acting on "?" over every well must state runs: the simulator fires a rule once by default, so only the first well to cross would change.

name is the rule’s name: upper-case letters, digits, _ and -, a letter first, up to 32 characters, and never SKIP, SKIP100, SKIP300 or ENDSKIP. minimum_wait is the least time between two firings: a number of days in every unit system, or a ps.Quantity in "day", "hr" or "s" ("hour" or "days" passes here and is refused when the model is compiled). The rule is stated in ps.Event(date, ...) (or m.at(date, ...)) and written there, its changes between ACTIONX and ENDACTIO; its address is schedule/<date>/actions/<name>.

A rule is itself a schedule event and stands where the deck states it. Position is not a formatting choice: measured on both supported releases, a rule stated before further dated groups gives a different run from the same rule moved after the last one, and the moved run is byte-identical in its restart state to a run with no rule at all — the restart carries the rule definitions, and moving a rule past the last dated group leaves the run’s own record of itself with none. Rendering every rule at the end of the schedule is therefore a deletion that leaves the card visible, which is why the rule joins the event stream instead of a collection beside it.

A rule restated by name replaces the earlier rule of that name from where it stands, its firings counted afresh, as Flow does (opm-common Actions::add: the same slot, a new id the run count is kept by): model4/MOD4_UDQ_ACTIONX restates ACT01 and ACT01A at later dates.

ps.actions.date() -> Clock

The simulation date, compared with a date in a condition (YEAR, MNTH and DAY).

ps.actions.date() >= "2026-03-01" holds from that date on.

Clock The date, compared with a day (its text 'YYYY-MM-DD' or a datetime.date) by ==, <, <=, > and >=, or read by its terms .year, .month and .day, each compared with a number (a month also with its word).

ps.actions.field() -> Quantities

The field’s quantities, as a rule’s condition or a user quantity’s formula reads them.

ps.actions.field().oil_production_rate < 500.0 holds while the field produces less than 500.

Quantities The field’s quantities, each an attribute by its long name (.oil_production_rate, .pressure; dir() lists them), compared with a number in the model’s units or combined into a formula.

ps.actions.group(group: Any = None) -> Quantities

The quantities of group (a ps.Group), or of every group when none is named.

Every group is read in a user quantity’s formula only; a rule’s condition names its group (Flow has no every-group condition).

ps.actions.group(north).water_cut > 0.9 holds while the water cut of north is above 0.9.

group : ps.Group, optional The group whose quantities are read: its handle (m.get("groups/NORTH")), never its name. None reads every group’s in a user quantity’s formula; Flow reads a group’s quantity in a rule’s condition for a named group only, so a condition names its group.

Quantities The group’s quantities, each an attribute by its long name (.water_cut, .oil_production_rate; dir() lists them), compared with a number in the model’s units or combined into a formula.

TypeError When group is a name, or anything else that is not a ps.Group.

ps.actions.region(region: int, *, fluid_in_place_set: str | None = None) -> Quantities

The quantities of fluid-in-place region region: ps.actions.region(2).oil_in_place.

region is the region’s number (one or more) in the model’s fluid-in-place regions, or in the set fluid_in_place_set names (a key of ps.Regions(fluid_in_place_sets=), the three characters its vectors carry).

region : int The region’s number, 1 or more. fluid_in_place_set : str, optional The fluid-in-place set the number counts in, a key of ps.Regions(fluid_in_place_sets=); None counts in the model’s own fluid-in-place regions.

Quantities The region’s quantities, each an attribute by its long name (.oil_in_place, .pressure; dir() lists them), compared with a number in the model’s units or combined into a formula.

ValueError When region is not a whole number of 1 or more.

ps.actions.well(well: Any = None, *, lists: str | None = None) -> Quantities

The quantities of well (a ps.Well), of a set of wells, or of every well.

ps.actions.well().water_cut > 0.85 holds for each well whose water cut passes 0.85, which a rule’s changes name as well="?". A condition reads a set of wells on a comparison’s left side (Flow reads a list of wells there only), and the set may be narrowed: a string is a pattern over WELL names (ps.actions.well("OP0*"), ps.actions.well("*RO*"): * any run, ? one character), a ps.wells.WellList reads the wells of that declared list, and lists= reads the wells of every declared list whose name matches (lists="PR*") or of one list by its name (lists="NORTH"). A pattern and a list are matched at each check, against the wells and lists that stand then.

well : ps.Well, str or ps.wells.WellList, optional The well whose quantities are read, by its handle (m.get("wells/P1")); a pattern over well names; or a declared list. None reads every well’s: a rule’s condition writes the quantity over '*' (WWCT '*' > 0.85). lists : str, optional A list’s name or a pattern over list names, in place of well.

Quantities The well’s quantities, each an attribute by its long name (.water_cut, .bottom_hole_pressure; dir() lists them, and a name it does not list is an AttributeError naming the nearest), compared with a number in the model’s units or combined into a formula.

TypeError When well is one well’s name (its handle reads it), or none of the kinds above. ValueError When well and lists are both given, or a pattern is not one.

PetroScript is licensed under the Apache License 2.0. © 2026 Blauweiss EDV LLC. PetroScript by Blauweiss AI.For AI agents: llms.txt