Skip to content

Passive tracers

PetroScript models passive tracers as reservoir concepts. A model states its water or gas tracers as ps.tracer.Tracer values in passive_tracers=, and a dated ps.wells.InjectedStream sets the concentration a well injects, naming each tracer by its name. Compiler lowerers own the ECL/Flow keywords (TRACERS, TRACER, initial tracer tables, and WTRACER).

State the tracer on the model and its injected concentration in the schedule:

import petroscript as ps
field = ps.Group(name="FIELD")
wt1 = ps.tracer.Tracer(
name="WT1",
phase="water",
units="g",
concentration_by_depth=[(2500.0, 0.0), (3300.0, 0.0)],
)
model = ps.Model(
name="TRACER_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.2, 0.8], 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),
passive_tracers=[wt1],
groups=[field],
wells=[
ps.Well(name="PROD1", i=3, j=3, group=field),
ps.Well(name="INJ1", i=1, j=1, group=field),
],
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.Perforate(well="INJ1", k=(1, 1), diameter=0.3),
ps.Produce(well="PROD1", oil_rate=150.0, bhp_limit=2500.0),
ps.Inject(well="INJ1", phase="water", rate=300.0, bhp_limit=4500.0),
ps.wells.InjectedStream(well="INJ1", tracers={"WT1": 12.5}),
),
ps.Event("2020-01-31", ps.wells.InjectedStream(well="INJ1", tracers={"WT1": 0.0})),
],
),
)

The public API describes reservoir meaning: a passive water tracer, optional initial concentration by depth, and a well’s injected concentration from a date. The generated deck syntax is a compiler concern.

A gas tracer is carried by the gas phase. Its initial concentration is stated for free gas and for the gas in solution, either as one value each or against depth:

x11 = ps.tracer.Tracer(
name="X11",
phase="gas",
units="g",
initial_free_gas_concentration=0.0,
initial_solution_gas_concentration=0.0,
)
gt1 = ps.tracer.Tracer(
name="GT1",
phase="gas",
units="g",
free_gas_concentration_by_depth=[(1000.0, 0.0), (2500.0, 0.0)],
solution_gas_concentration_by_depth=[(1000.0, 0.0), (2500.0, 0.0)],
)

Gas tracers are phase-aware. A reservoir model that enables a gas tracer without a gas phase fails with structured validation issue tracer.phase.inactive. Duplicate tracer names fail with tracer.name.duplicate.

ps.wells.InjectedStream(well=..., tracers={name: concentration}) sets the concentration of each named tracer in the selected wells’ injected stream from its date; a concentration of 0.0 stops it. The well is named as every dated change names it, and each tracer by the name its ps.tracer.Tracer states. A concentration is a number, a quantity, or a user-defined quantity.

For a concentration driven by a user-defined quantity, state the quantity over the well’s rate and name it as the concentration:

gas_injector = ps.Well(name="R-1H", i=1, j=1, group=field, preferred_phase="gas")
wucon_x11 = ps.actions.UserQuantity(
name="WUCONX11",
expression=25000.0 / ps.actions.well(gas_injector).gas_injection_rate,
)
stream = ps.wells.InjectedStream(well="R-1H", tracers={"X11": wucon_x11})

PetroScript intentionally does not expose raw keyword/table-shaped APIs for partitioned tracer or group-source tracer controls. Measured, OPM Flow rejects PARTTRAC, TRACERKP, partitioned TRACER fields, and the TRACERKM boundary probe. The manual marks WTRACER cumulative concentration and group-source concentration as unsupported; the current Flow binary accepts the probed cards, but without result-effect proof they remain non-public.

The compiler currently emits passive tracer declarations, initial concentration tables, and well tracer controls from semantic objects. The tracer examples under tests/fixtures/examples/g9/ are complete Flow-runnable generated decks anchored to the local OPM tracer fixtures; they document comparator gaps where the generated deck is a semantic reconstruction rather than a byte-for-byte clone of the fixture.

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