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.
- Its first line is
PO.SIM.OPM.SUMMARY(deck)spillsday, 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).
- a field vector is one column (
PO.SIM.OPM.RUN(deck)spills the end state askind | day | i | j | k | pressure | name | bhp:- a
cellrow per active cell, with its pressure; - a
wellrow per WELSPECS well, with its bottom-hole pressure; - where inactive cells hide a cell's position,
iis the active cell's number.
- a
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.DATAfile 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/stbfor 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.propertiesnames 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.daysnames 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'sPHASESpicks the family:single-phase(water),oil water, oroil water gas(black oil). Only black oil reads thepvtandscaltables; 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, orNo problems found.PO.SIM.DECK.RUNSPEC,GRID,PROPS,SUMMARYandSCHEDULEspill one section each, andPO.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,METRICorFIELD) andSPECkeys such asSYSTEM INJ.NETWORK: the graph.VERTEXandEDGE(from, to, length, diameter, roughness, elevations), plusPROPERTYrows 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 bindTRENDrows. SERVICEstations 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
- Rasmussen, A. F., et al. (2021). The Open Porous Media Flow reservoir simulator. Computers & Mathematics with Applications, 81, 159–185.
- Odeh, A. S. (1981). Comparison of Solutions to a Three-Dimensional Black-Oil Reservoir Simulation Problem. Journal of Petroleum Technology, 33(1), 13–25.