Reservoir Simulation in Excel


Introduction

Petroleum Office runs reservoir simulation models from worksheet functions. A model is an ECLIPSE deck written as a table in the workbook. The PO.SIM.OPM functions hand it to OPM Flow, the open-source reservoir simulator installed with Petroleum Office, and spill the results back: summary series per report step, and cell and well states.

The engine runs outside Excel, in its own program. Excel stays responsive while a model runs, a crash in the engine can't take a workbook down, and 32-bit Excel can use it too.

The Deck table

A deck is a range with six columns under a header row:

Section Keyword Statement Record Parameter Value
RUNSPEC TITLE 1 1 1 SPE1 - CASE 1
RUNSPEC DIMENS 1 1 1 10
RUNSPEC DIMENS 1 1 2 10
RUNSPEC DIMENS 1 1 3 3
RUNSPEC OIL
RUNSPEC FIELD
SCHEDULE WELSPECS 1 1 1 'PROD'
SUMMARY FOPR
  • Section is the ECLIPSE section: RUNSPEC, GRID, EDIT, PROPS, REGIONS, SOLUTION, SUMMARY or SCHEDULE.
  • Keyword is the keyword, as the deck writes it.
  • Statement numbers repeats of a keyword within its section (1 when blank).
  • Record and Parameter count the keyword's records and items from 1.
  • Value is the item as the deck writes it: 10, 8400.0, 'PROD', 300*1000, 1*.

A flag keyword such as OIL, FIELD, or a field SUMMARY vector is a row with no record, parameter or value. Blank rows are skipped. A deck that names no unit keyword is METRIC, as ECLIPSE and Flow read it.

Excel takes a leading apostrophe as its own text prefix, so typing 'PROD' into a cell stores PROD'. The functions put the quote back.

The functions

  • PO.SIM.OPM.CHECK(deck) reads the deck in Excel and starts nothing, so it works without the engine installed.
    • Its first line is No problems found. or a count of problems.
    • Then the deck's title, units, grid size, wells, number of SUMMARY series and rows.
    • Then each problem on its own line: no DIMENS, no TSTEP or DATES to march, no SUMMARY series, or a SUMMARY well vector naming a well WELSPECS doesn't define.
  • PO.SIM.OPM.SUMMARY(deck) spills day, then the SUMMARY series in the section's order, a row per report step. Each kind of vector gets its own column or columns:
    • a field vector is one column (FOPR);
    • a well vector gets one per well (WBHP:PROD), or every WELSPECS well when the record names none;
    • a group or region vector gets one per group or region;
    • a block vector gets one per i j k record (BPR:1,1,1);
    • a connection vector gets one per well and connection (COPR:PROD:10,10,3).
  • PO.SIM.OPM.RUN(deck) spills the end state as kind | day | i | j | k | pressure | name | bhp:
    • a cell row per active cell, with its pressure;
    • a well row per WELSPECS well, with its bottom-hole pressure;
    • where inactive cells hide a cell's position, i is the active cell's number.
  • PO.SIM.OPM.STATE0(deck) spills the same at day 0. Before the first step a well under bottom-hole-pressure control shows its scheduled target. Use it to check an initialisation before a long run.
  • PO.SIM.OPM.DATA(deck) spills the deck as the ECLIPSE text the engine's own parser writes, one line per cell. Copy it into a .DATA file to run the same model elsewhere.
  • PO.SIM.OPM.VECTORS(deck) spills SUMMARY's series long, ready to filter, pivot or chart: vector | identifier | unit | day | value, a row per series per report step. The identifier is what the vector is of (PROD, 1,1,1), blank for a field vector. The unit follows the deck's system (stb/day, psia, Mscf/stb for FIELD), and is blank for a fraction or a vector it doesn't know. It reuses SUMMARY's run.
  • PO.SIM.OPM.GRID(deck, properties, days) spills cell properties long: property | i | j | k | unit | day | value.
    • properties names any of PRESSURE, SWAT, SGAS, SOIL, RS and RV. By default it is PRESSURE plus what the deck's phases carry: SWAT and SGAS with more than one phase, RS with DISGAS.
    • days names report days. Day 0 is the initial state, and the default is day 0 and the last report day.
    • A request that would overflow a sheet is refused before the run, with a hint to name fewer days or properties.

Results are in the deck's own units: a FIELD deck spills psia and stb/day, a METRIC deck bar and sm3/day, a LAB deck atm.

A deck from input tables

Typing every keyword, record and item is slow. The PO.SIM.DECK functions build the Deck table from a few small tables instead, in FIELD units, and run in Excel without starting the engine:

  • PO.SIM.DECK.BUILD(spec, grid, pvt, scal, wells, schedule) spills a complete deck. The spec's PHASES picks the family: single-phase (water), oil water, or oil water gas (black oil). Only black oil reads the pvt and scal tables; leave them empty otherwise.
  • PO.SIM.DECK.CHECK(...) takes the same tables and lists what is wrong with each, or what the built deck's checks find, or No problems found.
  • PO.SIM.DECK.RUNSPEC, GRID, PROPS, SUMMARY and SCHEDULE spill one section each, and PO.SIM.DECK.COMBINE(a, b) joins two fragments. It refuses an item the two give different values, and fragments of different phases.
  • PO.SIM.DECK.MAPGRID(spec, maps, wells, schedule) builds a single-phase corner-point deck between a top and a base map given at the pillar nodes.

The tables:

Table Columns
Spec key and value, one row per value: PHASES, an optional TITLE, DIMENS.NX, DX, TOPS, the fluids by phase (WATER.MU, WATER.RHO, OIL.C, GAS.RHO), the rock (ROCK.C, ROCK.PREF), and the start (INIT.PRESSURE, INIT.SW)
Grid i, j, k, property, value, a row per cell and property; or poro, permx, permy, permz (and ntg), one row for every cell or one per cell with i, j, k
Pvt kind, pressure, rs, bo, muo, bg, mug: oil rows (saturated), undersaturated branch rows, gas rows
Scal saturation, krw, krow, krg, krog
Wells name, type, i, j, k1, k2
Schedule day, well, control, target, limit: a row per control a well takes from a day on (bhp, rate with an optional bottom-hole pressure limit, or gasrate for a gas injector), and report rows for the days a report step ends; without them, the Spec's TSTEP.DAYS and TSTEP.COUNT set uniform steps

Every family reads the same Grid and Schedule, and names a quantity by the same Spec key, so a book changes family by changing PHASES and adding what the new family reads. A key only another family reads is kept, and PO.SIM.DECK.CHECK says which family reads it. TITLE names the deck's cases in the OPM Flow Simulator pane.

The tables began as the wildcat simulator's, whose Spec keys and Schedule columns differed by family. Its keys still read: FLUID.MUW and PVTW.MUW as WATER.MU, DENSITY.OIL as OIL.RHO, PVTO.CO as OIL.C, ROCK.CR as ROCK.C, and so on, and PO.SIM.DECK.CHECK names each one's new key. Its Schedule columns step, name, mode and bhplimit are refused, with the column that replaced each.

A built deck feeds the PO.SIM.OPM functions as it is: =PO.SIM.OPM.SUMMARY(PO.SIM.DECK.BUILD(B2:C30, E2:I3, , , K2:P4, R2:V12)).

A .DATA file as the deck

Every function also takes one cell naming an ECLIPSE .DATA file, such as C:\Models\SPE1CASE1.DATA, in place of a Deck range. The engine reads the file and the files it INCLUDEs. A model built in another simulator then runs without being typed in as a table.

An existing model's SUMMARY section often names series the engine doesn't report: a user-defined region set, or a count of wells. For a .DATA deck those are left out of SUMMARY and VECTORS. A Deck range is held to every series it names, so a typo there shows as an error rather than a missing column.

How runs work

A cell that runs a deck shows its progress while it waits: received once Excel has read the deck, validated once Excel's checks and Flow's own reading of it have passed, then queued, starting, running step k of N, and reading the result before it spills. A deck either of them refuses shows why instead. The pane names each stage the same way. Runs go one at a time, in order, whichever Excel or workbook asked. The OPM Flow Simulator pane has four tabs. Cases lists every case on this computer by the workbook that asked for it (another Excel's under its own heading), running, waiting and finished, with the selected case's diagnostics. Summary plots a finished case's vector over time, its history vector as dots. Grid draws a grid property in 3D or by layer, stepping through the report steps the run kept. Teaching books opens worked examples. Cancel run stops a waiting or running case; its cells say it was cancelled, and a recalc runs it again.

Runs are kept by the deck's content. SUMMARY, RUN and STATE0 on one deck share a single run, and recalculating a sheet whose deck hasn't changed returns the kept result at once. Change any cell of the deck and the next calculation runs it again.

A deck the engine refuses, or a run that fails, shows the reason in the cell as text. When the engine can't be reached the cell shows an error value, and a recalculation tries again.

Pipes and networks in Marlim3

Marlim3, Petrobras's open-source multiphase flow simulator for wells, pipelines and networks, runs from the same kind of range. Its deck uses the network dialect, which has the same six columns but different sections:

  • RUNSPEC: the phases, units (SI, METRIC or FIELD) and SPEC keys such as SYSTEM INJ.
  • NETWORK: the graph. VERTEX and EDGE (from, to, length, diameter, roughness, elevations), plus PROPERTY rows for materials and cross-sections.
  • PROPS: the fluid.
  • SOLUTION: the initial pressure.
  • SCHEDULE: the terminals (TERMSPEC) and their rate or pressure (TCON).
Function What it returns
PO.SIM.MR3.CHECK Whether Marlim3 can run the deck: its title, units and run kind, its problems, and every value the binding supplies or replaces. Starts nothing.
PO.SIM.MR3.SUMMARY One row per branch: branch, day, liquid rate, the pressure where the branch starts and ends, and the developed friction gradient
PO.SIM.MR3.PROFILE Long form, branch property duct cell length unit day value: pressure, hydrostatic and friction gradients and elevation along the pipe, a cell value at the cell's centre and a face value at its upstream face
PO.SIM.MR3.TREND Long form, station length day property unit value: the variables a TREND station records at one point on the line
PO.SIM.MR3.RUN How the run went: the engine, the run kind, the time it took, how a network converged and its branches, and the binding's notes
PO.SIM.NET.BUILD The network Deck from input tables: spec, vertices, edges and terminals, with properties, schedule, groups and connections when the case has them. Runs in Excel and starts nothing.
PO.SIM.NET.CHECK The input tables' problems, naming the table and the row, or No problems found.

Building the deck from tables. Most cases are easier to write as tables than as keyword rows. The vertices and edges of the graph go one row each, the terminals hold the boundary rates and pressures, and long element | kind | key | value rows hold the materials, cross-sections and equipment. =PO.SIM.MR3.SUMMARY(PO.SIM.NET.BUILD(...)) runs them as they are.

What happens to the deck. The deck is turned into Marlim3's input by the same binding Team 76's network tools use, and the binding fills what the deck leaves out. It also replaces a few stated values the engine can't take. An injection branch, for example, runs at a fixed 50 kgf/cm² wellhead pressure whatever its outlet says. CHECK and RUN list every such value, so read them before trusting a pressure.

How runs work. Marlim3.exe runs on this computer as a separate program, one case at a time, and a case takes tens to hundreds of milliseconds. SUMMARY, PROFILE and RUN on one deck share a run. A recalc with the deck unchanged answers from memory. The Marlim3 pane (the Simulators group on the ribbon) lists this Excel's runs: waiting, running, finished, failed or cancelled. Each shows its deck, the input the engine ran, what the binding supplied, and the engine's log. Cancel stops a run, and a recalc runs it again. The pane's Teaching books open worked pipes as new workbooks.

Results. Results come back in the deck's units: pascals and metres under SI, bar under METRIC, psia and feet under FIELD. Liquid rate is in sm³/d in every system.

Networks of branches. A deck with DOCUMENT rede is a network: PROPERTY rows put each edge on a branch file (edge | branch | upstream.json), and CONNECTION rows join the branches, a collector and the branches that feed it. Marlim3 solves the branches together as one REDE run and iterates the pressure at each join until both sides agree within its convergence limit. SUMMARY and PROFILE spill a row per branch, named by its file (upstream), and a single-branch deck's one branch is main. RUN says whether the network converged; a network that does not solve says so in words instead of spilling. The Two-branch network teaching book is a worked case.

Trend stations. A profile is the whole line at one time; a trend is one point on the line over time. A TREND row in the NETWORK section names a station: its id, the line (PROD), an edge and a distance from that edge's start, or a length along the branch, a label, the sampling interval dt in seconds, and the variables to record, such as OUT PROD E4 200 1* Outlet 60 'pressure frictionPressureGradient' /. PO.SIM.MR3.TREND spills each station's samples, and its station argument picks one by label or by length. A trend has these limits today:

  • Marlim3 3.8.1 records a station only on a production branch (SPEC SYSTEM PROD). On an injection branch the engine writes no trend file, and TREND says so; CHECK and RUN carry the binding's note about it.
  • A case is run at t = 0, so a trend holds one sample until the binding runs a case over time.
  • A network of branches (DOCUMENT rede) does not bind TREND rows.
  • SERVICE stations are written to the engine's input but have not yet been checked against a run.

Limits. A case is solved at steady state, t = 0. A network runs as one steady REDE solve; transient networks are not supported.

References

  1. Rasmussen, A. F., et al. (2021). The Open Porous Media Flow reservoir simulator. Computers & Mathematics with Applications, 81, 159–185.
  2. Odeh, A. S. (1981). Comparison of Solutions to a Three-Dimensional Black-Oil Reservoir Simulation Problem. Journal of Petroleum Technology, 33(1), 13–25.
Tags:
reservoir-simulationopm-flowdecksummarymarlim3pipeline