Skip to content

Add a rule that fires during the run

A rule (Flow’s ACTIONX) watches the run and changes the schedule when its condition holds: shut a well whose water cut passes 50 %, open a standby producer when the field rate drops. In PetroScript it is ps.actions.When(condition, then=[changes], name=...), dated like any other change with m.at(date, rule) or in a ps.Event. A rule dated at the start is in force from the first day.

A condition compares a quantity the run computes with a number:

ReadsSpelledExample
A field quantityps.actions.field().<name>ps.actions.field().water_cut > 0.9
A well’s quantityps.actions.well(<the ps.Well>).<name>ps.actions.well(producer).gas_oil_ratio > 2.0
A group’s quantityps.actions.group(<the ps.Group>).<name>ps.actions.group(north).oil_production_rate < 100.0
The dateps.actions.date()ps.actions.date() >= "2027-01-01"

The names are the summary’s (water_cut, gas_oil_ratio, oil_production_rate, bottom_hole_pressure; summary names). The threshold is a bare number in the model’s units, as the summary reports the quantity: a water cut is a fraction, a FIELD gas-oil ratio Mscf/stb (shut above 2,000 scf/stb is > 2.0), a METRIC one sm3/sm3 (units).

import petroscript as ps
m = ps.load("opm-tests/spe1/SPE1CASE2.DATA") # FIELD: GOR in Mscf/stb
producer = m.get("wells/PROD") # a rule reads the well itself
high_gor = ps.actions.When(
ps.actions.well(producer).gas_oil_ratio > 2.0, # 2,000 scf/stb
then=[ps.Adjust(well="PROD", oil_rate=5000.0)], # cut the oil target
name="CUT_GOR",
)
late = ps.actions.When(
ps.actions.field().oil_production_rate < 1000.0,
ps.actions.date() >= "2020-01-01", # every condition must hold
then=[ps.Shut(well="PROD")],
name="LATE_SHUT",
)
m = m.at(m.start, high_gor, late)
deck = ps.compile(m).files["SPE1CASE2.DATA"]
assert "WGOR 'PROD' > 2" in deck and "ENDACTIO" in deck
print(m.find("schedule/*/actions/*")) # the rules, by address

What goes wrong:

  • A well given by name: TypeError: ps.actions.well() takes a well handle, not the name 'PROD'; look it up first: m.get("wells/PROD").
  • A threshold with a unit: [units.wrong_kind] an expression's number takes a bare number in the model's units. State the number in the model’s units.
  • A rule fires once unless it says otherwise: runs= is how many times it may fire, and minimum_wait= the days between firings. A rule over every well that acts on well="?" (the wells whose condition fired) must state runs=, or only the first well to cross changes.
  • A threshold typed in the wrong unit runs without complaint: a FIELD GOR of 2000 means 2,000 Mscf/stb and never fires. Check the threshold against the summary’s unit (run.wells("gas_oil_ratio")["unit"]).

examples/water_cut_rule.py runs a water-cut rule in Flow and checks the date it fired against Flow’s own log. User quantities (UDQ) and lookup tables (UDT), which a rule’s condition can read, are in the user-defined reference.

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