Boundary conditions and source terms
PetroScript models external mass and heat exchange as reservoir concepts. A
boundary condition (ps.grid.BoundaryCondition) states a face of a box of cells
at the grid’s edge and what happens there — free flow, a signed phase rate, a
fixed Dirichlet pressure, or a constant temperature — and a source term
(ps.grid.SourceTerm) injects or withdraws a physical component in one cell. Compiler lowerers own the ECL/Flow keywords (BCCON, BCPROP, and
SOURCE).
From-scratch authoring
Section titled “From-scratch authoring”State boundary conditions in the model’s boundary_conditions=[...], and a cell
source term as a ps.grid.SourceTerm among the changes of a ps.Event:
import petroscript as ps
influx = ps.grid.BoundaryCondition( number=1, cells=ps.Box(i=(1, 1), j=(1, 3), k=(1, 1)), face="minus_i", kind="dirichlet", phase="water", pressure=3200.0,)open_edge = ps.grid.BoundaryCondition( number=2, cells=ps.Box(i=(3, 3), j=(1, 3), k=(1, 1)), face="plus_i", kind="free")
field = ps.Group(name="FIELD")model = ps.Model( name="BOUNDARY_CASE", units="field", start="2020-01-01", grid=ps.grid.Cartesian(shape=(3, 3, 1), dx=1000.0, dy=1000.0, dz=50.0, top=2500.0), rock=ps.Rock(porosity=0.2, permeability=100.0, vertical_ratio=1.0), fluid=ps.fluid.OilWater( oil=ps.pvt.DeadOil(pressure=[14.7, 3500.0], oil_fvf=[1.06, 1.0], oil_viscosity=[1.0, 1.2]), 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, 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])], initial=ps.initial.Equilibrium( datum_depth=2510.0, datum_pressure=3000.0, oil_water_contact=2600.0, gas_oil_contact=0.0 ), groups=[field], wells=[ps.Well(name="PROD1", i=3, j=3, group=field)], boundary_conditions=[influx, open_edge], schedule=ps.Schedule( report=[ps.Every(30.0)], events=[ ps.Event( "2020-01-01", ps.Perforate(well="PROD1", k=(1, 1), diameter=0.3), ps.Produce(well="PROD1", oil_rate=150.0, bhp_limit=2500.0), ps.grid.SourceTerm(cell=(2, 2, 1), component="water", rate=1500.0), ) ], ),)The boundary connections lower to BCCON in the GRID section, the control
behaviors lower to BCPROP in the SCHEDULE section, and each source term
lowers to a SOURCE record at its event’s position in the schedule timeline.
A control keeps the POSITION it was stated at. An imported BCPROP standing
after a time card is filed there rather than at the schedule head, because the
statement includes when it was made: a deck that runs a day and only then
imposes a Dirichlet pressure runs that day unconstrained on purpose, and
hoisting the card imposes the condition from t=0 instead. Controls stated at
the head still lower at the head, which is the ordinary case.
Only the placement travels on the timeline. What a control SAYS is written once, on the boundary itself, and the render reads it back by identifier — so the head block and the schedule position can never disagree about a record.
Boundary controls
Section titled “Boundary controls”kind="free"— keep the initial boundary state throughout the run.kind="rate"withphase=andrate=— constant influx (negative) or efflux (positive) mass rate per unit area for one phase.kind="dirichlet"withphase=,pressure=and an optionaltemperature=— fixed boundary pressure; the temperature requires an activeps.thermal.Package(...)in the model’sphysics=[...].kind="thermal"withtemperature=— constant-temperature boundary with no mass flow; requires an active thermal package. Omitting the temperature uses the simulator’s initial-state value.
Boundary conditions are unit-system agnostic: the same semantic objects lower for FIELD, METRIC, and LAB models.
Source terms
Section titled “Source terms”ps.grid.SourceTerm(cell=..., component=..., rate=..., heat_rate=..., temperature=...) defines one cell source (positive rate) or sink (negative
rate) of one component, stated in a ps.Event. Solvent, polymer, and
microbe/oxygen/urea components require their activating packages
(ps.solvent.Package, ps.polymer.Package, ps.micp.Package). heat_rate and temperature are
thermal-only and mutually exclusive.
Validation and common mistakes
Section titled “Validation and common mistakes”source_term.cell.bounds— the source cell must lie inside the grid.source_term.component.requires_solvent/...requires_polymer/...requires_micp— activate the matching package first.source_term.thermal.requires_thermal,lab_boundary.temperature.requires_thermal, andlab_boundary.control.requires_thermal— temperature-bearing boundary or source intents needps.thermal.Package(...).lab_boundary.identifier.uniqueandlab_boundary.cell_range.bounds— boundary identifiers must be unique and connections must stay in bounds.
A flux aquifer whose strength the schedule states
Section titled “A flux aquifer whose strength the schedule states”initial_water_flux_rate is optional, and a flux aquifer that omits it is a
complete statement: its connections declare it, and its strength arrives as a
schedule statement — ps.aquifers.Flux(...) changes in a ps.Event — at whatever position
the timeline gives it. Setting the rate at the first scheduled position is not
the same as an initial rate, and only the second writes a SOLUTION record;
opm-tests/aquifers/AQUFLUX-02 is a deck of the first shape and
AQUFLUX-01 of the second.
What is not a statement is a connection nothing claims. An aquifer declared by connections alone, with no initial rate and no schedule statement anywhere, is refused by name.
Thermal aquifer state
Section titled “Thermal aquifer state”Prescribed-flux aquifers accept thermal-only state alongside their
connections: ps.aquifers.ConstantFlux(..., temperature=..., pressure=...) requires
an active thermal package and lowers into the AQUFLUX optional record
slots. Finite aquifers take an explicit water PVT-table choice with
ps.aquifers.Fetkovich(..., pvt_region=...).
PetroScript is licensed under the Apache License 2.0. © 2026 Blauweiss EDV LLC. PetroScript by Blauweiss AI.For AI agents: llms.txt