This post introduces the Reservoir Opportunity Index (ROI) method, covers the reservoir model setup, runs the simulation using OPM Flow, and walks through the Python workflow for computing and visualising the results. The entire stack — simulation, data loading, and visualisation — is open source.
The ROI method was introduced by Varela-Pineda et al. in SPE-172231-MS (2014) and has since been discussed in numerous subsequent SPE publications. The concept is to combine rock quality, remaining oil volume, and reservoir pressure into a single normalised index that highlights areas of concentrated remaining oil that could be potential targets for future wells.
The method has formulas that build on each other:
Rock Quality Index (RQI) — a purely static measure of rock quality:
RQI = 0.0314 × sqrt(PERMX / PORO)
This is the effective pore throat radius in micrometres. The constant 0.0314 is a unit conversion factor converting sqrt(mD/fraction) to micrometres.
Saturable Oil Pore Volume (SOMPV) — how much movable oil remains in each cell:
SOIL = 1 - SWAT - SGAS
SOMPV = DX × DY × DZ × PORO × SOIL
Cells swept by water or gas get near zero automatically so no manual filtering is required.
ROI with pressure (Eq. 4 — the full version):
ROI = cbrt(PRESSURE_norm × RQI_norm × SOMPV_norm)
All three components are normalised to 0–1 before combining. The pressure term matters because a cell with good rock and remaining oil but depleted pressure simply will not flow at commercial rates. The final index is dimensionless, ranging from 0 to 1, with high values pointing to the best combination of rock quality, saturation and reservoir energy.
The model used here is a publicly available synthetic black-oil reservoir from the Eclipse test suite. The grid is 20 × 15 × 8 corner-point cells with 1,639 active cells, three-phase fluids with dissolved gas, water injection and gas injection support. The model has three equilibration regions with oil-water contacts at 7,500 ft, 7,550 ft and 7,600 ft respectively. The well count was reduced to one producer and one injector per region to simplify the well controls for OPM Flow compatibility.
The simulation was run using OPM Flow — no commercial licence required. It produces standard Eclipse binary output (EGRID, INIT, UNRST) that feeds directly into the Python libraries used in this workflow.
If you are on Windows, WSL (Windows Subsystem for Linux) is the cleanest way to run OPM. Install Ubuntu 24.04 from the Microsoft Store, then:
sudo apt-add-repository ppa:opm/ppa
sudo apt update
sudo apt install libopm-simulators-bin
flow --version
You should see flow 2026.04 or newer. No compilation required.
The post-processing stack is built on four libraries:
xtgeo handles the grid geometry — it reads the EGRID corner-point grid and builds a PyVista-compatible mesh. Properties are deliberately not loaded through xtgeo on this model: its internal property mapping produces a checkerboard pattern due to a conflict with the ACTNUM ordering in the Eclipse output. Instead, properties are loaded separately using resfo.
resfo reads the Eclipse binary files (EGRID, INIT, UNRST) directly and returns raw numpy arrays. The ACTNUM array from the EGRID file is used to map active-only property arrays to the correct PyVista cell ordering, bypassing the xtgeo mapping entirely.
PyVista builds the 3D visualisation. The grid is constructed from xtgeo geometry and populated with properties from resfo, then exported as an interactive HTML file.
Plotly produces the 2D contour maps. Cell values are averaged across all K layers within each IJ column and interpolated onto a regular grid, giving a plan-view map suitable for potential well placement decisions.
The 3D view below shows oil saturation at the final timestep (December 2050) after 60 years of production. The central crest retains the highest oil saturation, while the flanks and aquifer-supported edges have been swept.
Figure 1: Oil Saturation — 3D Grid Property (final timestep, Dec 2050)
The RQI map shows a clear east-to-west gradient where permeability increases toward the eastern part of the model, giving higher RQI values around the LU1, U2 and I2 area. This is a purely static property, reflecting the original reservoir rock distribution before any production.
Figure 2: Rock Quality Index (RQI) Map — averaged across all K layers
The ROI map combines rock quality, remaining oil and pressure into a single index. High values concentrate in the central part of the reservoir around LU1, U1 and L3 — the area that has the best rock, has not been fully swept, and still has pressure support from the surrounding injection pattern.
Figure 3: Reservoir Opportunity Index (ROI) Map — averaged across all K layers
These high-ROI cells represent the strongest candidates for future infill drilling. In a real field study the ROI index provides the initial screening layer.
The full workflow — simulation, data loading, ROI computation, and visualisation — runs entirely on open source tools. OPM Flow handles the simulation and produces standard Eclipse binary output. Python reads those files directly with xtgeo and resfo, computes the ROI index cell by cell, and produces interactive maps with Plotly and PyVista.
The notebook and OPM DATA file are available on GitHub.
Varela-Pineda, A., Hutheli, A. H., & Mutairi, S. M. (2014). Identification of Infill Drilling Opportunities: Application to Saudi Arabian Fields. SPE-172231-MS. Society of Petroleum Engineers.