Skip to content

Chapter 1 — The shortest model that writes a deck

One import, one value, one compile, no configuration:

import petroscript as ps
model = ps.Model(
name="HELLO",
units="field",
start="2020-01-01",
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=0.1,
compressibility=4.0e-6, reference_pressure=3000.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.2, 0.8], krw=[0.0, 1.0], krow=[1.0, 0.0],
pcow=[0.0, 0.0])],
initial=ps.initial.Equilibrium(datum_depth=2520.0, datum_pressure=3000.0,
oil_water_contact=2600.0),
wells=[ps.Well(name="PROD1", i=1, j=1)],
schedule=ps.Schedule(
end="2020-01-31",
events=[ps.Event("2020-01-01",
ps.Perforate(well="PROD1", k=(1, 1), diameter=0.3),
ps.Produce(well="PROD1", oil_rate=220.0, bhp_limit=1200.0))],
),
)
ps.compile(model).write("hello") # hello/HELLO.DATA

ps.Model is the whole case as one frozen value, stated by keyword in the model’s unit system. ps.compile(model) makes the deck in memory and .write(folder) writes it. Everything the package publishes is reachable as ps.<name> or ps.<namespace>.<name>: there is one import and it is the one above.

Nothing is edited in place. A change to a case that already exists is a verb that returns a new model, m.at(date, ...) for a dated change and m.set(address, value) for a fact, and the same verbs edit a deck read with ps.load.

The value is also the short path to a test: read what the model states, by address, rather than the deck text.

assert model.get("wells/PROD1").i == 1
assert model.find("schedule/*/PROD1/*") == [
"schedule/2020-01-01/PROD1/perforation/1",
"schedule/2020-01-01/PROD1/control",
]

Three habits carry over from here into every other chapter:

  • Look names up rather than guess them. ps.help(ps.Produce) prints a class’s fields, the card it writes and where the model holds it; ps.help("WCONPROD") names the class that writes a keyword; ps.help("FOPR") the name a summary result is read by; ps.help() lists the classes, namespaces and topics.
  • A wrong name answers with the nearest one. ps.Produce(bhp_limt=...) is refused with did you mean 'bhp_limit'?. You do not have to be right the first time; you have to read the refusal.
  • A deck answers too. Where ps.help names no class for a keyword, ps.load a deck that states it and m.to_python(folder) prints the statement that holds it.

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