Working with the RelPerm class and equinor/pyscal

This tutorial describes two workflows that allow us to pass relative permeability and capillary pressure data between the resqpy library and the equinor\pyscal library.

We will be moving water-oil relperm data between resqpy’s resqpy.olio.relperm.RelPerm class and pyscal’s pyscal.WaterOil class.

Please note that similar workflows can be used for moving gas-oil data to/from the pyscal.GasOil class.

Importing the relperm and wateroil modules

In this tutorial we will be moving water-oil relperm data between the RelPerm object’s resqpy.olio.relperm.RelPerm.dataframe() method and the WaterOil object’s pyscal.WaterOil.table attribute.

from resqpy.olio.relperm import RelPerm
from pyscal import WaterOil
# import a plotting library for visual inspection of the data
import matplotlib.pyplot as plt
# import resqpy model module to interact with a resqpy model
import resqpy.model as rm

resqpy RelPerm.dataframe() to pyscal WaterOil.table

Initialize an instance of a RelPerm object that is stored in a resqpy Model instance. This can be done using the uuid of the existing Grid2dRepresentation object that acts as support for the dataframe of relperm data.

model = rm.Model('/path/to/my_file.epc')
relperm_wo = RelPerm(model = model, uuid = uuid)

The dataframe of water-oil relperm data is then accessed using the resqpy.olio.relperm.RelPerm.dataframe() method.

relperm_wo_df = relperm_wo_obj.dataframe()

To initialize a pyscal WaterOil object, we first need to define a water saturation end-point that is compatible with the RelPerm dataframe being inputted. In this case, we can define swl, which will be the first water saturation in the generated WaterOil table, and set it equal to the minimum water saturation value in the RelPerm dataframe.

swl = relperm_wo_df.min()['Sw']
pyscal_wo = WaterOil(swl = swl)

The pyscal pyscal.WaterOil.add_fromtable() method generates a relperm table by interpolating relative permeability and capillary pressure data from the inputted dataframe.

pyscal_wo.add_fromtable(dframe = relperm_wo_df, swcolname = 'Sw', krwcolname='Krw', krowcolname='Kro', pccolname='Pc')
pyscal_wo_df = pyscal_wo.table

We’ll generate some plots to visually compare the inputted RelPerm data to the WaterOil data.

fig, (ax1, ax2) = plt.subplots(nrows = 1, ncols = 2)
ax1.plot(relperm_wo_df['Sw'], relperm_wo_df['Krw'], label = 'resqpy_water', c = 'cyan')
ax1.plot(relperm_wo_df['Sw'], relperm_wo_df['Kro'], label = 'resqpy_oil', c = 'limegreen')
ax1.scatter(pyscal_wo_df['SW'], pyscal_wo_df['KRW'], label = 'pyscal_water', c = 'darkblue', s = 8)
ax1.scatter(pyscal_wo_df['SW'], pyscal_df['KROW'], label = 'pyscal_oil', c = 'darkgreen', s = 8)
ax1.legend( )
ax2.scatter(relperm_wo_df['Sw'], relperm_wo_df['Pc'], label = 'resqpy_Pc', c = 'magenta', s = 12)
ax2.plot(pyscal_wo_df['SW'], pyscal_wo_df['PC'], label = 'pyscal_Pc', linestyle = 'dotted', c = 'darkred' )
ax2.legend( )

The image below compares two sets of relperm and capillary pressure data:


pyscal WaterOil.table to resqpy RelPerm.dataframe()

Moving data in the opposite direction is simple, and involves reformatting the column names of the WaterOil table to be compatible with the RelPerm initialiser method. We reference the same WaterOil table instance, pyscal_wo_df, from the previous section.

model = rm.Model('/path/to/my_file.epc')
all_relevant_pyscal_cols = ['SW', 'SG', 'KRW', 'KRG', 'KROW', 'KROG']
cols = sorted(list(set(pyscal_wo_df.columns).intersection(set(all_relevant_pyscal_cols))), reverse=True)
if 'PC' in pyscal_wo_df.columns:
col_remap_dict = {k: (k.capitalize() if len(k) < 4 else k.capitalize()[0:3]) for k in cols}
pyscal_wo_df_processed = pyscal_wo_df[cols].rename(columns = col_remap_dict)
# intialize a new RelPerm object, write hdf5 and create xml for object
relperm_wo = RelPerm(model = model, df = pyscal_wo_df_processed)