Skip to content

Well identities and groups

The PetroScript well model is split across three semantic objects:

  • ps.Well — a well’s identity: its name, its head column and its group. Whether it produces or injects is the control a dated change gives it.
  • ps.Group — an operating group, in the model’s groups=.
  • ps.Perforate — a dated connection between a wellbore and a run of layers in one grid column.

For scheduled controls (ps.Produce(...), ps.Shut(...), ps.wells.Regroup(...), ps.Schedule(...), etc.) see wells-schedule.md. This page focuses on the well/group/completion shapes themselves.

A well’s group is a ps.Group, and the model’s groups= declares every group its wells and policies name. Reuse the same group values when building wells and scheduling group policies:

import petroscript as ps
field_group = ps.Group(name="FIELD")
operating_group = ps.Group(name="OP1", parent=field_group)
prod = ps.Well(name="PROD1", i=10, j=10, group=operating_group)
start = ps.Event(
"2020-01-01",
ps.Perforate(well="PROD1", k=(3, 3), diameter=0.5),
ps.Produce(well="PROD1", oil_rate=20_000.0, bhp_limit=1000.0),
ps.wells.Regroup(well=prod, group=operating_group.name),
)

Group is a frozen pydantic value object with two fields: name: str and parent: Group | None. Two distinct Group instances with the same name and parent compare equal, so validation can compare handles safely whether or not the same Python object was reused.

Well.group: Group | None = None — passing None is valid for cases that have no group structure.

bhp_reference_depth is optional on ps.Well. When it is omitted the render DEFAULTS WELSPECS item 5 — it emits 1* and lets OPM Flow derive the BHP datum from the mid-point of the shallowest connection. Pass bhp_reference_depth=... only when the problem statement gives a deliberate datum.

PetroScript does not write that mid-point itself, even where the grid family makes it computable: stating the same value does not say the same thing. On two FIELD decks the lowered depth is correct to every digit the restart file prints and the runs still diverge, because a stated depth crosses the unit conversion as one multiplication while the target accumulates its own in SI from converted grid geometry. A value the target would derive is not ours to state; agreement to every printed digit is not agreement.

The model still knows the depth — the physics guards ask for it, and the warning about a datum far from the grid reads it. Only the deck leaves it unstated.

ps.Perforate(...) states a connection’s semantics. A missing connection factor or skin factor means “use the simulator default”; the lowerer owns any deck-token representation. Beside the interval and the diameter it takes the connection factor, kh, the skin factor, the non-Darcy factor, the direction and the saturation region when the problem states them:

perforation = ps.Perforate(well="PROD1", k=(1, 1), diameter=0.5, skin_factor=2.0)

Well-dimension limits are semantic capacity metadata. The compiler sizes ordinary well and group capacity from the wells and groups the model declares; ps.Model(capacity=ps.Capacity(wells=..., connections_per_well=..., ...)) states a larger allocation when a scenario needs one, by name rather than as a raw positional tuple.

Core wellbore authoring is typed. Users should not memorize or pass simulator control/status/phase tokens; each choice is a field with listed words:

  • ps.Produce(well=..., oil_rate=..., ...) and ps.Inject(well=..., phase=..., rate=..., ...) name control intent without spelling backend control tokens.
  • ps.Open, ps.Shut and ps.Stop change lifecycle state; a control’s status is "open", "shut" or "stop".
  • An injector’s phase, and a well’s preferred_phase, is "oil", "water" or "gas".
  • A group control is control="group", a well test’s reasons are among "physical", "economic" and "group_economic", a workover’s scope is workover_scope= on ps.wells.Economics, a guide rate’s phase is one of its listed words, and the injection multiplier and damage policies take their reversibility and geometry the same way.

A word outside a field’s choices is refused at construction, and a numeric string is refused where a number is expected. The compiler owns the eventual deck token mapping.

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