Skip to content

Add production history and a forecast

A history match holds each producer to its observed rates, month by month, and compares what the model then does (pressures, water cut, gas-oil ratio) with what the field did. In PetroScript an observed month is a ps.History control: every rate it states is an observation, and control= names the one the simulator holds the well to. ps.History.from_frame turns a table of observations into dated events.

import polars as pl
import petroscript as ps
m = ps.load("opm-tests/spe1/SPE1CASE2.DATA") # FIELD: stb/day, Mscf/day
observed = pl.DataFrame({ # or pl.read_csv("history.csv")
"date": ["2015-02-01", "2015-03-01", "2015-04-01"],
"well": ["PROD", "PROD", "PROD"],
"oil_rate": [19800.0, 19500.0, 19100.0], # stb/day
"gas_rate": [25000.0, 26000.0, 27500.0], # Mscf/day
})
for event in ps.History.from_frame(observed, control="oil_rate"):
m = m.at(event.date, *event.changes) # one dated event per month
# the forecast: from the first date after the history, a normal control again
m = m.at("2015-05-01", ps.Produce(well="PROD", oil_rate=15000.0, bhp_limit=1000.0))
# ask for the observed rates beside the simulated ones (Flow's WOPRH, WGPRH)
m = m.set("summary/history", {"wells": {"PROD": ["oil_production_rate", "gas_production_rate"]}})
deck = ps.compile(m).files["SPE1CASE2.DATA"]
assert "WCONHIST" in deck and "WOPRH" in deck
now = m.control("PROD", at="2015-03-15")
assert isinstance(now, ps.History) and now.oil_rate == 19500.0
  • The frame’s columns are date, well and any of oil_rate, water_rate, gas_rate, thp, bhp_limit and status, in the model’s units. A pandas frame or a plain mapping of columns works too.
  • A row with more than one rate needs control=: one word for every well, or a mapping by well name (control={"PROD": "oil_rate", "GAS1": "gas_rate"}).
  • A date that is not yet a report time becomes one. A date past the run’s end is refused: m = m.until(date) first.
  • A history control’s bhp_limit is the observed bottom-hole pressure, reported beside the rates; it limits nothing while the well is held to a rate.
  • One control per well per date: a ps.Produce at a history date replaces that month’s ps.History. Start the forecast at the next date.

After the run, the simulated rates read by name (run.wells("oil_production_rate")); the observed ones the summary asked for are in the long-form frame under Flow’s own names: run.frame().filter(pl.col("quantity") == "WOPRH").

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