Facilities and networks
PetroScript models surface production systems as semantic objects: named network nodes backed by groups, directed branches routed through typed vertical-flow-performance (VFP) tables, node efficiency factors, and a network-balancing convergence policy. The compiler owns every keyword layout; authors describe the production system, not simulator records.
OPM Flow’s network balancing requires all three phases (oil, water, gas) to be
active — the model gate rejects a scheduled network on two-phase fluids with
issue code schedule.networks.requires_three_phases instead of letting the
simulator abort.
import petroscript as ps
field = ps.Group(name="FIELD")plat_a = ps.Group(name="PLATA", parent=field)plat_b = ps.Group(name="PLATB", parent=field)injectors = ps.Group(name="INJE", parent=field)
table = ps.lift.ProductionTable( number=1, bhp_reference_depth=2510.0, rate_kind="liquid", rate=[100.0, 1000.0, 6000.0], thp=[100.0, 300.0], artificial_lift=[0.0, 100.0], artificial_lift_kind="gas_lift_rate", # one pressure per (thp, water fraction, gas fraction, artificial lift, rate) bhp=[ [[[[1200.0, 1700.0, 2300.0], [1200.0, 1700.0, 2300.0]]]], [[[[1300.0, 1800.0, 2400.0], [1300.0, 1800.0, 2400.0]]]], ],)
field_node = ps.groups.Node(name="FIELD", group=field, terminal_pressure=200.0)plat_a_node = ps.groups.Node(name="PLATA", group=plat_a, efficiency=0.95)plat_b_node = ps.groups.Node(name="PLATB", group=plat_b)network = ps.groups.Network( name="production-network", nodes=[field_node, plat_a_node, plat_b_node], branches=[ ps.groups.Branch( name="plata-to-field", from_node=plat_a_node, to_node=field_node, lift_table=table, artificial_lift_quantity=50.0, ), ps.groups.Branch( name="platb-to-field", from_node=plat_b_node, to_node=field_node, uses_no_pressure_loss=True, ), ],)
model = ps.Model( name="PLATFORM_NETWORK", units="field", start="2020-01-01", capacity=ps.Capacity(wells=3, connections_per_well=1, groups=3, wells_per_group=3), grid=ps.grid.Cartesian(shape=(2, 2, 1), dx=500.0, dy=500.0, dz=40.0, top=2500.0), rock=ps.Rock(porosity=0.25, permeability=100.0, vertical_ratio=1.0), fluid=ps.fluid.BlackOil( oil=ps.pvt.DeadOil(pressure=[14.7, 3500.0], oil_fvf=[1.06, 1.0], oil_viscosity=[1.0, 1.2]), gas=ps.pvt.DryGas(pressure=[14.7, 3500.0], gas_fvf=[200.0, 0.9], gas_viscosity=[0.008, 0.02]), water=ps.pvt.Water( reference_pressure=3000.0, fvf=1.0, compressibility=3.0e-6, viscosity=0.5, viscosibility=0.0, ), oil_surface_density=53.0, gas_surface_density=0.053, water_surface_density=64.0, ), relperm=[ ps.relperm.OilWater(sw=[0.20, 0.80], krw=[0.0, 1.0], krow=[1.0, 0.0], pcow=[0.0, 0.0]), ps.relperm.GasOil( sg=[0.0, 0.75, 0.80], krg=[0.0, 0.9, 1.0], krog=[1.0, 0.0, 0.0], pcog=[0.0, 0.0, 0.0] ), ], initial=ps.initial.Equilibrium( datum_depth=2510.0, datum_pressure=3000.0, oil_water_contact=2600.0, gas_oil_contact=2400.0 ), numerics=ps.Numerics( network_balance_interval=-1, network_pressure_tolerance=1.0, maximum_network_iterations=10, network_thp_tolerance=0.01, maximum_network_thp_iterations=10, ), groups=[field, plat_a, plat_b, injectors], wells=[ ps.Well(name="PRODA", i=1, j=2, group=plat_a), ps.Well(name="PRODB", i=2, j=2, group=plat_b), ps.Well(name="INJ1", i=1, j=1, group=injectors), ], lift_tables=[table], network=network, schedule=ps.Schedule( report=[ps.Every(30.0)], events=[ ps.Event( "2020-01-01", ps.Perforate(well="PRODA", k=(1, 1), diameter=0.3), ps.Perforate(well="PRODB", k=(1, 1), diameter=0.3), ps.Perforate(well="INJ1", k=(1, 1), diameter=0.3), ps.Produce( well="PRODA", oil_rate=150.0, bhp_limit=1000.0, thp_limit=150.0, lift_table=table, artificial_lift_quantity=0.0, ), ps.Produce(well="PRODB", oil_rate=120.0, bhp_limit=1000.0), ps.Inject(well="INJ1", phase="water", rate=300.0, bhp_limit=4500.0), ) ], ),)Network topology
Section titled “Network topology”ps.groups.Node(name=..., group=..., ...)is a node for a group, or an explicit junction by its name alone. Terminal nodes carry a fixedterminal_pressure; non-terminal nodes leave it unset.ps.groups.Branch(name=..., from_node=..., to_node=..., ...)connects nodes downstream-to-upstream. Route through a production lift table (lift_table=), declare a lossless route withuses_no_pressure_loss=True, or take a route out of service withis_disabled=True.ps.groups.Network(name=..., nodes=..., branches=...)assembles the extended network, held by the model’snetwork=;kind="standard"builds the standard group network instead (no branches — routing follows the group tree).- The compiler sizes the network dimensions from the declared topology (node count first, then branch count), with each node and branch a dated branch adds.
Network changes on dates
Section titled “Network changes on dates”The model’s network is the start’s. A ps.groups.Branch or ps.groups.Node
stated in a later ps.Event changes it at that date, as OPM Flow applies the
card there:
- a dated branch is a BRANPROP record. It adds a branch, replaces the branch
above its
from_node(a re-route), or drops one (is_disabled=True, item 3 = 0). A node it names that the network does not hold yet is added; - a dated node is a NODEPROP record in an extended network, and a GRUPNET record
in a standard one, restating that node from its date. A standard network’s
pipeline follows a dated
ps.Groupthat moves the group, as Flow’s does.
The start’s network says which card a dated node is, so a dated change needs
ps.Model(network=...). A branch in a standard network, and a dated node’s
efficiency (NEFAC is the start’s), are refused
(schedule.networks.dated_change).
Branch lift quantities
Section titled “Branch lift quantities”artificial_lift_quantity on a branch models a pump or compressor working the
route: it is interpolated on the routing table’s artificial-lift axis, so it
requires lift_table=... and a table whose lift axis spans the value. OPM Flow
can also read the branch lift value as a surface-density equivalence (BRANPROP
item 5); PetroScript does not map that yet, and refuses a deck stating it.
Node efficiency
Section titled “Node efficiency”ps.groups.Node(name=..., group=..., efficiency=0.95) declares an
extended-network node efficiency factor (1.0 minus downtime fraction, in
(0, 1]). Standard networks reject node efficiencies — state a dated
ps.groups.Efficiency(group=..., factor=...) for group efficiency factors instead.
Standard-network pipelines
Section titled “Standard-network pipelines”On a standard network, a group node may carry a pipeline
artificial_lift_quantity (pump/compressor duty on the pipeline to the parent
group) and add_gas_lift=True to include well lift gas in the pipeline flow.
uses_no_pressure_loss=True states a pipeline with no pressure drop (GRUPNET
item 3 = 9999): the group’s node is at its parent’s pressure. An extended
network states that on the branch instead.
The tubing-pressure manifold balancing option and pipeline density resets are
rejected by OPM Flow for non-default values and stay non-public.
Network balancing
Section titled “Network balancing”ps.Numerics(...) states when the network is balanced and how it converges
(NETBALAN): network_balance_interval (0 at the start of each time step, a
number of days, or -1 in each step’s first NUPCOL Newton iterations);
network_pressure_tolerance and maximum_network_iterations bound the
nodal-pressure iteration, while network_thp_tolerance and
maximum_network_thp_iterations bound the tubing-head-pressure iteration for
wells balancing against a common manifold and for automatic chokes. The model’s
numerics write it at the schedule’s head; a ps.Numerics in a later
ps.Event balances the network differently from that date. The branch-error
timestep controls are flagged unsupported by OPM Flow and are not exposed.
Typed VFP tables
Section titled “Typed VFP tables”ps.lift.ProductionTable(...) states what each fraction axis holds:
water_fraction_kind is water cut ("water_cut"), water-oil ratio
("water_oil_ratio"), or water-gas ratio ("water_gas_ratio" — gas-rate flow
axis only); gas_fraction_kind is gas-oil ratio ("gas_oil_ratio"), gas-liquid
ratio ("gas_liquid_ratio"), or oil-gas ratio ("oil_gas_ratio" — gas-rate flow
axis only). The compiler derives the table’s fraction tokens from the axis
kinds. artificial_lift_kind declares what the lift axis quantifies
("gas_lift_rate", "injection_gas_liquid_ratio", "total_gas_liquid_ratio" or
"pump_rating"): the kinds OPM Flow reads on VFPPROD. Flow 2026.04 refuses a compressor
(COMP), choke (BEAN) or surface-density (DENO, DENG) lift axis, so none is written.
Units and shapes
Section titled “Units and shapes”Terminal pressures, tolerances, and lift quantities use the active unit system;
the branch lift quantity’s unit follows the routing table’s artificial-lift
axis. Node efficiency is a dimensionless fraction in (0, 1]. THP tolerance is
a dimensionless fraction of the group rate target.
PetroScript is licensed under the Apache License 2.0. © 2026 Blauweiss EDV LLC. PetroScript by Blauweiss AI.For AI agents: llms.txt