pyscal package

Submodules

pyscal.constants module

Constants used for pyscal modules:

  • SWINTEGERS: Number of different Sw values within [0,1] we allow This is used to create integer indices of Sw, since Floating Point indices are flaky in Pandas (and in general on computers)

  • EPSILON: Used as “a small number” for ensuring no floating point comparisons/errors pop up. You cannot have the h parameter less than this when generating relperm tables

  • MAX_EXPONENT: Maximal number for exponents in relperm parametrizations. Used to avoid numerical instabilities. It could probably be much higher than the chosen number in most circumstances, but such high numbers should not be relevant for relative permeability

pyscal.factory module

Factory functions for creating the pyscal objects

pyscal.factory.slicedict(dct, keys)[source]

Slice a dictionary for a set of keys. Keys not existing will be ignored.

class pyscal.factory.PyscalFactory[source]

Bases: object

Class for implementing the factory pattern for Pyscal objects

The factory functions herein can take multiple parameter sets, determine what kind of parametrization to be used, and set up the full objects based on these parameters, instead of explicitly having to call the API for each task.

Example:

wo = WaterOil(sorw=0.05)
wo.add_corey_water(nw=3)
wo.add_corey_oil(now=2)
# is equivalent to:
wo = factory.create_water_oil(dict(sorw=0.05, nw=3, now=2))

Parameter names to factory functions are case insensitive, while the add_*() parameters are not. This is becase the add_*() parameters are meant as a Python API, while the factory class is there to aid users when input is written in a different context, like an Excel spreadsheet.

static create_water_oil(params=None)[source]

Create a WaterOil object from a dictionary of parameters.

Parameterization (Corey/LET) is inferred from presence of certain parameters in the dictionary.

Don’t rely on behaviour of you supply both Corey and LET at the same time.

Parameter names in the dictionary are case insensitive. You can use Swirr, swirr, sWirR, swiRR etc.

NB: the add_LET_* methods have the names ‘l’, ‘e’ and ‘t’ in their signatures, which is not precise enough in this context, so we require e.g. ‘Lw’ and ‘Low’ (which both will be translated to ‘l’)

Recognized parameters:

swirr, swl, swcr, sorw, h, tag, nw, now, krwmax, krwend, lw, ew, tw, low, eow, tow, lo, eo, to, kromax, krowend, a, b, poro_ref, perm_ref, drho, a, b, poro, perm, sigma_costau

static create_gas_oil(params=None)[source]

Create a GasOil object from a dictionary of parameters.

Parameterization (Corey/LET) is inferred from presence of certain parameters in the dictionary.

Don’t rely on behaviour of you supply both Corey and LET at the same time.

NB: the add_LET_* methods have the names ‘l’, ‘e’ and ‘t’ in their signatures, which is not precise enough in this context, so we require e.g. ‘Lg’ and ‘Log’ (which both will be translated to ‘l’). Also note that in this factory context, kroend is an ambiguous parametre, krogend must be used.

Recognized parameters:

swirr, sgcr, “sorg, swl, krgendanchor, h, tag, ng, krgend, krgmax, nog, krogend, lg, eg, tg, log, eog, tog

static create_water_oil_gas(params=None)[source]

Create a WaterOilGas object from a dictionary of parameters

Parameterization (Corey/LET) is inferred from presence of certain parameters in the dictionary.

Check create_water_oil() and create_gas_oil() for lists of supported parameters (case insensitive)

static create_scal_recommendation(params, tag='', h=None)[source]

Set up a SCAL recommendation curve set from input as a dictionary of dictionary.

The keys in in the dictionary must be “low”, “base” and “high”.

The value for “low” must be a new dictionary with saturation endpoints and LET/Corey parameters, as you would feed it to the create_water_oil_gas() factory function;

Recognized parameters:

Lw, Ew, Tw, Low, Eow, Tow, Lg, Eg, Tg, Log, Eog, Tog, nw, now, ng, nog, swirr, swl, sorw, sorg, sgcr

For oil-water only, you may omit the LET parameters for gas and oil-gas

static create_scal_recommendation_list()[source]

Reserved for future implementation

static create_wog_list()[source]

Reserved for future implementation

pyscal.factory.check_deprecated(params)[source]

Check for deprecated parameter names

Parameters

params – Dictionary of parameters for which only the keys are used here.

pyscal.gasoil module

Representing a GasOil object

class pyscal.gasoil.GasOil(swirr=0, sgcr=0.0, h=0.01, swl=0.0, sorg=0.0, tag='', krgendanchor='sorg', fast=False)[source]

Bases: object

Object to represent two-phase properties for gas and oil.

Parametrizations available for relative permeability:

  • Corey

  • LET

or data can alternatively be read in from tabulated data (as Pandas DataFrame).

No support (yet) to add capillary pressure.

krgend can be anchored both to 1-swl-sorg and to 1-swl. Default is to anchor to 1-swl-sorg. If the krgendanchor argument is something else than the string sorg, it will be anchored to 1-swl.

Code duplication warning: Code is analogous to WaterOil, but with some subtle details sufficiently different for now warranting its own code (no easy inheritance)

Parameters
  • swirr (float) – Absolute minimal water saturation at infinite capillary pressure. Not in use currently, except for in informational headers and for consistency checks.

  • swl (float) – First water saturation point in water tables. In GasOil, it is used to obtain the normalized oil and gas saturation.

  • sgcr (float) – Critical gas saturation. Gas will not be mobile before the gas saturation is above this value.

  • sorg (float) – Residual oil saturation after gas flooding. At this oil saturation, the oil has zero relative permeability.

  • krgendanchor (str) – Set to sorg or something else, where to anchor krgend. If sorg, then the normalized gas saturation will be equal to 1 at 1 - swl - sgcr - sorg, if not, it will be 1 at 1 - swl - sgcr. If sorg is zero it does not matter.

  • h (float) – Saturation step-length in the outputted table.

  • tag (str) – Optional string identifier, only used in comments.

  • fast (bool) – Set to True if in order to skip some integrity checks and nice-to-have features. Not needed to set for normal pyscal runs, as speed is seldom crucial. Default False

resetsorg()[source]

Recalculate sorg in case it has table data has been manipulated

add_gasoil_fromtable(*args, **kwargs)[source]

Deprecated. Use add_fromtable()

add_fromtable(dframe, sgcolname='Sg', krgcolname='krg', krogcolname='krog', pccolname='pcog', krgcomment='', krogcomment='', pccomment='')[source]

Interpolate relpermdata from a dataframe.

The saturation range with endpoints must be set up beforehand, and must be compatible with the tabular input. The tabular input will be interpolated to the initialized Sg-table.

If you have krg and krog in different dataframes, call this function twice

Calling function is responsible for checking if any data was actually added to the table.

set_endpoints_linearpart_krg(krgend, krgmax=None)[source]

Set linear parts of krg outside endpoints.

Curve will be linear from [1 - swl - sorg, 1 - swl] (from krgend to krgmax) and zero in [0, sgcr]. Except special handling for krgendanchor:

If krgendanchor is set to sorg (default), then the normalized gas saturation sgn (which is what is raised to the power of ng) is 1 at 1 - swl - sgcr - sorg. If not, it is 1 at 1 - swl - sgcr.

krgmax is only relevant if krgendanchor is ‘sorg’

This function is used by add_corey/LET_gas(), and perhaps by other utility functions. It should not be necessary for end-users.

Parameters
  • krgend (float) – krg at 1 - swl - sgcr - sorg. Checkme.

  • krgmax (float) – krg at Sg=1-swl. Default 1.

set_endpoints_linearpart_krog(kroend, kromax)[source]

Set linear parts of krog outside endpoints.

Zero for sg above 1 - sorg - swl.

Linear from kromax to kroend from sg in [0, sgcr]

This function is used by add_corey/LET_oil(), and perhaps by other utility functions. It should not be necessary for end-users.

Parameters
  • kroend (float) – krog at sg=sgcr

  • kromax (float) – krog at Sg=0. Default 1.

add_corey_gas(ng=2, krgend=1, krgmax=None)[source]

Add krg data through the Corey parametrization

A column called ‘krg’ will be added. If it exists, it will be replaced.

If krgendanchor is set to sorg (default), then the normalized gas saturation sgn (which is what is raised to the power of ng) is 1 at 1 - swl - sgcr - sorg. If not, it is 1 at 1 - swl - sgcr.

krgmax is only relevant if krgendanchor is ‘sorg’

add_corey_oil(nog=2, kroend=1, kromax=None)[source]

Add kro data through the Corey parametrization

A column named ‘kro’ will be added to the internal DataFrame, replaced if it exists.

All values above 1 - sorg - swl are set to zero.

kromax is ignored if sgcr is close to zero

Parameters
  • nog (float) – Corey exponent for oil

  • kroend (float) – Value for krog at normalized oil saturation 1

  • kromax (float) – Value for krog at gas saturation 0.

Returns

None (modifies internal class state)

add_LET_gas(l=2, e=2, t=2, krgend=1, krgmax=None)[source]

Add gas relative permability data through the LET parametrization

A column called ‘krg’ will be added, replaced if it does not exist

If krgendanchor is set to sorg (default), then the normalized gas saturation sgn (which is what is raised to the power of ng) is 1 at 1 - swl - sgcr - sorg. If not, it is 1 at 1 - swl - sgcr

Parameters
  • l (float) – L parameter in LET

  • e (float) – E parameter in LET

  • t (float) – T parameter in LET

  • krgend (float) – Value of krg at normalized gas saturation 1

  • krgmax (float) – Value of krg at gas saturation 1

Returns

None (modifies internal state)

add_LET_oil(l=2, e=2, t=2, kroend=1, kromax=None)[source]

Add oil (vs gas) relative permeability data through the Corey parametrization.

A column named ‘krog’ will be added, replaced if it exists.

All values where sg > 1 - sorg - swl are set to zero.

kromax is ignored if sgcr is close to zero

Parameters
  • l (float) – L parameter

  • e (float) – E parameter

  • t (float) – T parameter

  • kroend (float) – The value at gas saturation sgcr

  • kromax (float) – The value at gas saturation equal to 0.

estimate_sorg()[source]

Estimate sorg of the current krg or krog data.

sorg is estimated by searching for a linear part in krg downwards from sg=1-swl. In practice it is impossible to infer sorg = 0, since we are limited by h, and the last segment from sg=1-swl-h to sg=1-swl can always be assumed linear.

If krgend is anchored to sorg, krg data is used to infer sorg. If not, krg cannot be used for this, and krog is used. sorg might be overestimated when krog is used if it very close to zero before reaching sorw.

If the curve is linear everywhere, sorg will be returned as sgcr + h

Parameters

None

Returns

The estimated sorg.

Return type

float

estimate_sgcr(curve='krog')[source]

Estimate sgcr of the current krog data.

sgcr is estimated by searching for a linear part in krog upwards from sg=0. In practice it is impossible to infer sgcr = 0, since we are limited by h, and we always have to assume that the first segment is linear.

If the curve is linear everywhere, sgcr will be returned as the right endpoint.

Parameters

curve (str) – Column name to use for search for linearity. Default is krog, if all of that is linear, you may try krg instead.

Returns

The estimated sgcr.

Return type

float

crosspoint()[source]

Locate and return the saturation point where krg = krog

Accuracy of this crosspoint depends on the resolution chosen when initializing the saturation range (it uses linear interpolation to solve for the zero)

Warning: Code duplication from WaterOil, with column names changed only

selfcheck()[source]

Check validities of the data in the table.

This is to catch errors that are either physically wrong or at least causes Eclipse 100 to stop.

Returns True if no errors are found, False if at least one is found.

If you call SGOF/SLGOF, this function must not return False.

SGOF(header=True, dataincommentrow=True)[source]

Produce SGOF input for Eclipse reservoir simulator.

The columns sg, krg, krog and pc are outputted and formatted accordingly.

Meta-information for the tabulated data are printed as Eclipse comments.

Parameters
  • header (bool) – Whether the SGOF string should be emitted. If you have multiple satnums, you should have True only for the first (or False for all, and emit the SGOF yourself). Defaults to True.

  • dataincommentrow (bool) – Whether metadata should be printed, defaults to True.

slgof_df()[source]

Slice out an SLGOF table.

This is a used by the SLGOF() function, it is extracted as a single function to facilitate testing.

SLGOF(header=True, dataincommentrow=True)[source]

Produce SLGOF input for Eclipse reservoir simulator.

The columns sl (liquid saturation), krg, krog and pc are outputted and formatted accordingly.

Meta-information for the tabulated data are printed as Eclipse comments.

Parameters
  • header – boolean for whether the SLGOF string should be emitted. If you have multiple satnums, you should have True only for the first (or False for all, and emit the SGOF yourself). Defaults to True.

  • dataincommentrow – boolean for wheter metadata should be printed, defaults to True.

SGFN(header=True, dataincommentrow=True)[source]

Produce SGFN input for Eclipse reservoir simulator.

The columns sg, krg, and pc are outputted and formatted accordingly.

Meta-information for the tabulated data are printed as Eclipse comments.

Parameters
  • header – boolean for whether the SGOF string should be emitted. If you have multiple satnums, you should have True only for the first (or False for all, and emit the SGOF yourself). Defaults to True.

  • dataincommentrow – boolean for wheter metadata should be printed, defaults to True.

GOTABLE(header=True, dataincommentrow=True)[source]

Produce GOTABLE input for the Nexus reservoir simulator.

The columns sg, krg, krog and pc are outputted and formatted accordingly.

Meta-information for the tabulated data are printed as Eclipse comments.

Parameters
  • header – boolean for whether the SGOF string should be emitted. If you have multiple satnums, you should have True only for the first (or False for all, and emit the SGOF yourself). Defaults to True.

  • dataincommentrow – boolean for wheter metadata should be printed, defaults to True.

plotkrgkrog(ax=None, color='blue', alpha=1, linewidth=1, linestyle='-', logyscale=False)[source]

Plot krg and krog on a supplied matplotlib axis

pyscal.scalrecommendation module

SCALrecommendation, container for low, base and high WaterOilGas objects

class pyscal.scalrecommendation.SCALrecommendation(low, base, high, tag='', h=0.01)[source]

Bases: object

A SCAL recommendation consists of three OilWaterGas objects, tagged low, base and high.

This container exists in order to to interpolation from -1 (low), through 0 (base) and to 1 (high).

Parameters
  • low (WaterOilGas) – An object representing the low case

  • base (WaterOilGas) – An object representing the base case

  • high (WaterOilGas) – An object representing the high case

  • tag (str) – A string that describes the recommendation. Optional.

add_simple_J(a=5, b=-1.5, poro_ref=0.25, perm_ref=100, drho=300, g=9.81)[source]

Add (identical) simplified J-function to all water-oil curves in the SCAL recommendation set

interpolate(parameter, parameter2=None, h=0.02)[source]

Interpolate between low, base and high parameter = -1 reproduces low curve parameter = 0 reproduces base curve parameter = 1 reproduces high curve

Endpoints are located for input curves, and interpolated individually. Interpolation for the nonlinear part is done on a normalized interval between the endpoints

Interpolation is linear in relperm-direction, and will thus not be linear in log-relperm-direction

This method returns an WaterOilGasTable object which can be realized into printed tables. No attempt is made to parametrize the interpolant in L,E,T parameter space, or Corey-space.

If a second parameter is supplied (“parameter2”) this is used for the gas-oil interpolation. This enables the gas-oil interpolant to be fully uncorrelated to the water-oil interpolant. CHECK how this affects endpoints and Eclipse consistency!!

static defaultshandling(key, value, dicts)[source]

Helper function for __init__ to fill out missing values in dicts with relperm parameter

This function IS DEPRECATED and will be removed when __init__ no longer supports dicts as arguments.

pyscal.utils module

Utility function for pyscal

pyscal.utils.estimate_diffjumppoint(table, xcol=None, ycol=None, side='right')[source]

Estimate the point where the y-data jumps from being linear in x to being nonlinear, or where it shift from one linear domain to another (for a piecewise linear function)

If xcol is sw, and ycol is krw, and side is ‘right’, this will typically estimate sorw for you. If side is ‘left’ it will give you swcr.

Parameters
  • table (pd.DataFrame) – A Dataframe with x and y data

  • xcol (string) – The name of the column in table containing x-data. If None (default) the first column in table will be used.

  • ycol (string) – The name of the column in table containing y-data. If None (default) the second column in the table will be used.

  • side (string) – Must be ‘left’ or ‘right’. Decides whether to look from the right side of the x-interval or from the left side for the linear domain.

Returns

The x value where the start-linear domain ends.

Return type

float

pyscal.utils.normalize_nonlinpart_wo(curve)[source]

Make krw and krow functions that evaluate only on the (potentially) nonlinear part of the relperm curves, and with a normalized argument (0,1) on that interval.

For a WaterOil krw curve, the nonlinear part is from swcr to sorw. swcr is mapped to zero, and 1 - sorw is mapped to 1. Then there is an assumed linear part from sorw to 1 which we ignore here.

For a WaterOil krow curve, the nonlinear part is from 1 - sorw (mapped to zero) to swcr (mapped to 1). If swcr > swl, there is a linear part from swcr down to swl, ignored here.

These endpoints must be known the the WaterOil object coming in (the object can determine them using functions ‘estimate_sorw()’ and ‘estimate_swcr()’

If the entire curve is linear, it will not matter for this function, because this function only deals with the presumably known endpoints.

Parameters

curve (WaterOil) – incoming oilwater curve set (krw and krow)

Returns

tuple of lambda functions. The first will evaluate krw on

the normalized Sw interval [0,1], the second will evaluate krow on the normalized So interval [0,1].

pyscal.utils.normalize_nonlinpart_go(curve)[source]

Make krg and krog functions that evaluates only on the (potentially) nonlinear part of the relperm curves, and with a normalized argument (0,1) on that interval.

For a GasOil krw curve, the nonlinear part is from sgcr to sorg. sgcr is mapped to sg=zero, and sg=1 - sorg - swl is mapped to 1. Then there is an assumed linear part from sorg to 1 which we ignore here.

For a GasOil krow curve, the nonlinear part is from 1 - sorg (mapped to zero) to sgcr (mapped to 1).

These endpoints must be known the the GasOil object coming in (the object can determine them using functions ‘estimate_sorg()’ and ‘estimate_sgcr()’

If the entire curve is linear, it will not matter for this function, because this function only deals with the presumably known endpoints.

Parameters

curve (GasOil) – incoming gasoil curve set (krg and krog)

Returns

tuple of lambda functions. The first will evaluate krg on

the normalized Sg interval [0,1], the second will evaluate krog on the normalized So interval [0,1].

pyscal.utils.normalize_pc(curve)[source]

Normalize the capillary pressure curve.

This is only normalized with respect to the smallest and largest saturation present in the table, not to the could-be-uncertain swirr that the object could contain, because we then have to make assumptions on the equations used to generate the data in the table.

Parameters

curve (WaterOil or GasOil) – An object with a table with a pc column

Returns

a lambda function that will evaluate pc on the normalized interval [0,1]

pyscal.utils.interpolate_wo(wo_low, wo_high, parameter, h=0.01)[source]

Interpolates between two water-oil curves.

The saturation endpoints for the curves must be known by the objects. They can be estimated by estimate_sorw() etc. or can be set manually for finer control.

The interpolation algorithm is different left and right for saturation endpoints, and saturation endpoints are interpolated individually.

Parameters
  • wo_low (WaterOil) – a “low” case

  • wo_high (WaterOil) – a “high” case

  • parameter (float) – Between 0 and 1. 0 will return the low case, 1 will return the high case. Any number in between will return an interpolated curve

  • h (float) – Saturation step-size in interpolant. If defaulted, a value smaller than in the input curves are used, to preserve information.

Returns

A new oil-water curve

pyscal.utils.interpolate_go(go_low, go_high, parameter, h=0.01)[source]

Interpolates between two gas-oil curves.

The saturation endpoints for the curves must be known by the objects. They can be estimated by estimate_sorg() etc. or can be set manually for finer control.

The interpolation algorithm is different left and right for saturation endpoints, and saturation endpoints are interpolated individually.

Parameters
  • go_low (GasOil) – a “low” case

  • go_high (GasOil) – a “high” case

  • parameter (float) – Between 0 and 1. 0 will return the low case, 1 will return the high case. Any number in between will return an interpolated curve

  • h (float) – Saturation step-size in interpolant. If defaulted, a value smaller than in the input curves are used, to preserve information.

Returns

A new gas-oil curve

pyscal.utils.interpolator(tableobject, wo_low, wo_high, parameter, sat='sw', kr1='krw', kr2='krow', pc='pc')[source]

Interpolates between two curves.

DEPRECATED FUNCTION!

The interpolation parameter is 0 through 1, irrespective of phases or low-base/base-high/low-high.

Parameters
  • tabjeobject (WaterOil or GasOil) – A partially setup object where relperm and pc columns are to be filled with numbers.

  • wo_low (WaterOil or GasOil) – “Low” case of interpolation (relates to interpolation parameter 0). Must be copies, as they will be modified.

  • wo_high – Ditto, relates to interpolation parameter 1

  • parameter (float) – Between 0 and 1, what you want to interpolate to.

  • sat (str) – Name of the saturation column, typically ‘sw’ or ‘sg’

  • kr1 (str) – Name of the first relperm column (‘krw’ or ‘krg’)

  • kr2 (str) – Name of the second relperm column (‘krow’ or ‘krog’)

  • pc (str) – Name of the capillary pressure column (‘pc’)

Returns

None, but modifies the first argument.

pyscal.wateroil module

Wateroil module

class pyscal.wateroil.WaterOil(swirr=0.0, swl=0.0, swcr=0.0, sorw=0.0, h=0.01, tag='', fast=False)[source]

Bases: object

A representation of two-phase properties for oil-water.

Can hold relative permeability data, and capillary pressure.

Parametrizations for relative permeability:
  • Corey

  • LET

For capillary pressure:
  • Simplified J-function

For object initialization, only saturation endpoints must be inputted, and saturation resolution. An optional string can be added as a ‘tag’ that can be used when outputting.

Relative permeability and/or capillary pressure can be added through parametrizations, or from a dataframe (will incur interpolation).

Can be dumped as include files for Eclipse/OPM and Nexus simulators.

Parameters
  • swirr (float) – Absolute minimal water saturation at infinite capillary pressure.

  • swl (float) – First water saturation point in generated table. Used for normalized saturations.

  • swcr (float) – Critical water saturation. Water will not be mobile before the water saturation is above this value.

  • sorw (float) – Residual oil saturation after water flooding. At this oil saturation, the oil has zero relative permeability.

  • h (float) – Saturation step-length in the outputted table.

  • tag (str) – Optional string identifier, only used in comments.

  • fast (bool) – Set to True if in order to skip some integrity checks and nice-to-have features. Not needed to set for normal pyscal runs, as speed is seldom crucial. Default False

add_oilwater_fromtable(*args, **kwargs)[source]

Deprecated, use add_fromtable()

add_fromtable(dframe, swcolname='Sw', krwcolname='krw', krowcolname='krow', pccolname='pcow', krwcomment='', krowcomment='', pccomment='', sorw=None)[source]

Interpolate relpermdata from a dataframe.

The saturation range with endpoints must be set up beforehand, and must be compatible with the tabular input. The tabular input will be interpolated to the initialized Sw-table

If you have krw and krow in different dataframes, call this function twice

Calling function is responsible for checking if any data was actually added to the table.

The relpermdata will be interpolated using a monotone cubic interpolator below 1-sorw, and linearly above 1-sorw. Capillary pressure data will be interpolated monotone cubicly over the entire saturation interval

The python package ecl2df has a tool for converting Eclipse input files to dataframes.

Parameters
  • dframe (pd.DataFrame) – containing data

  • swcolname (string) – column name with the saturation data in the dataframe df

  • krwcolname (string) – name of column in df with krw

  • krowcolname (string) – name of column in df with krow

  • pccolname (string) – name of column in df with capillary pressure data

  • krwcomment (string) – Inserted into comment

  • krowcomment (string) – Inserted into comment

  • pccomment (str) – Inserted into comment

  • sorw (float) – Explicit sorw. If None, it will be estimated from the numbers in krw (or krow)

add_corey_water(nw=2, krwend=1, krwmax=None)[source]

Add krw data through the Corey parametrization

A column named ‘krw’ will be added. If it exists, it will be replaced.

It is assumed that there are no sw points between sw=1-sorw and sw=1, which should give linear interpolations in simulators. The corey parameter applies up to 1-sorw.

krwmax will be ignored if sorw is close to zero

Parameters
  • nw (float) – Corey parameter for water.

  • krwend (float) – value of krw at 1 - sorw.

  • krwmax (float) – maximal value at Sw=1. Default 1

set_endpoints_linearpart_krw(krwend, krwmax=None)[source]

Set linear parts of krw outside endpoints.

Curve will be linear from [1 - sorw, 1] (from krwmax to krwend) and zero in [swl, swcr]

This function is used by add_corey_water(), and perhaps by other utility functions. It should not be necessary for end-users.

Parameters
  • krwend (float) – krw at 1 - sorwr

  • krwmax (float) – krw at Sw=1. Default 1.

set_endpoints_linearpart_krow(kroend, kromax=None)[source]

Set linear parts of krow outside endpoints

Curve will be linear in [swl, swcr] (from kromax to kroend) and zero in [1 - sorw, 1]

This function is used by add_corey_water(), and perhaps by other utility functions. It should not be necessary for end-users.

Parameters
  • kroend (float) – value of kro at swcr

  • kromax (float) – maximal value of kro at sw=swl. Default 1

add_LET_water(l=2, e=2, t=2, krwend=1, krwmax=None)[source]

Add krw data through LET parametrization

It is assumed that there are no sw points between sw=1-sorw and sw=1, which should give linear interpolations in simulators. The LET parameters apply up to 1-sorw.

krwmax will be ignored if sorw is close to zero.

Parameters
  • l (float) – LET parameter

  • e (float) – LET parameter

  • t (float) – LET parameter

  • krwend (float) – value of krw at 1 - sorw

  • krwmax (float) – maximal value at Sw=1. Default 1

add_LET_oil(l=2, e=2, t=2, kroend=1, kromax=None)[source]

Add kro data through LET parametrization

kromax will be ignored if swcr is close to swl.

Parameters
  • l (float) – LET parameter

  • e (float) – LET parameter

  • t (float) – LET parameter

  • kroend (float) – value of kro at swcr

  • kromax (float) – maximal value of kro at sw=swl. Default 1

Returns

None (modifies object)

add_corey_oil(now=2, kroend=1, kromax=None)[source]

Add kro data through the Corey parametrization

Corey applies to the interval between swcr and 1 - sorw

Curve is linear between swl and swcr, zero above 1 - sorw.

kromax will be ignored if swcr is close to swl.

Parameters
  • now (float) – Corey exponent

  • kroend (float) – kro value at swcr

  • kromax (float) – kro value at swl

Returns

None (modifies object)

add_simple_J(a=5, b=-1.5, poro_ref=0.25, perm_ref=100, drho=300, g=9.81)[source]

Add capillary pressure function from a simplified J-function

This is the ‘inverse’ or ‘RMS’ version of the a and b, the formula is

J = a S_w^b

J is not dimensionless. Doc: https://wiki.equinor.com/wiki/index.php/Res:Water_saturation_from_Leverett_J-function

poro_ref is a fraction, between 0 and 1 perm_ref is in milliDarcy drho has SI units kg/m³. Default value is 300 g has SI units m/s², default value is 9.81

add_normalized_J(a, b, poro, perm, sigma_costau)[source]

Add capillary pressure in bar through a normalized J-function.

\[p_c = \frac{\left(\frac{S_w}{a}\right)^{\frac{1}{b}} \sigma \cos \tau}{\sqrt{\frac{k}{\phi}}}\]

The Sw saturation used in the formula is normalized with respect to the swirr parameter.

Parameters
  • a (float) – a parameter

  • b (float) – b exponent (typically negative)

  • poro (float) – Porosity value, fraction between 0 and 1

  • perm (float) – Permeability value in mD

  • sigma_costau (float) – Interfacial tension in mN/m (typical value 30 mN/m)

Returns

None. Modifies pc column in self.table, using bar as pressure unit.

add_skjaeveland_pc(cw, co, aw, ao, swr=None, sor=None)[source]

Add capillary pressure from the Skjæveland correlation,

Doc: https://wiki.equinor.com/wiki/index.php/Res:The_Skjaeveland_correlation_for_capillary_pressure

The implementation is unit independent, units are contained in the input constants.

If swr and sor are not provided, it will be taken from the swirr and sorw. Only use different values here if you know what you are doing.

Modifies or adds self.table.pc if succesful. Returns false if error occured.

add_LET_pc_pd(Lp, Ep, Tp, Lt, Et, Tt, Pcmax, Pct)[source]

Add a primary drainage LET capillary pressure curve.

Docs: https://wiki.equinor.com/wiki/index.php/Res:The_LET_correlation_for_capillary_pressure

Note that Pc where Sw > 1 - sorw will appear linear because there are no saturation points in that interval.

add_LET_pc_imb(Ls, Es, Ts, Lf, Ef, Tf, Pcmax, Pcmin, Pct)[source]

Add an imbition LET capillary pressure curve.

Docs: https://wiki.equinor.com/wiki/index.php/Res:The_LET_correlation_for_capillary_pressure

estimate_sorw(curve='krw')[source]

Estimate sorw of the current krw data.

This is mostly relevant when add_fromtable() has been used. sorw is estimated by searching for a linear part in krw downwards from sw=1. In practice it is impossible to infer sorw = 0, since we are limited by h, and the last segment from sw=1-h to sw=1 can always be assumed linear. Expect sorw = h if the real sorw = 0, but do not depend that it might not return zero in the future (one could argue that sorw = h should be specially treated to mean sorw = 0)

If the curve is linear everywhere, sorw will be returned as swl + h

krow is not used, and should probably not be, as it can be very close to zero before approaching sorw.

Parameters

curve (str) – Colum name of column to use, default is krw. If this is all linear, but krow is not, you might be better off with krow

Returns

The estimated sorw.

Return type

float

estimate_swcr(curve='krow')[source]

Estimate swcr of the current krw data.

swcr is estimated by searching for a linear part in krw upwards from sw=swl. In practice it is impossible to infer swcr = 0, since we are limited by h, and the first segment is assumed linear anyways.

If the curve is linear everywhere, swcr can end up at the right end of your saturation interval.

Parameters

curve (str) – Colum name of column to use, default is krow. If this is all linear, but krw is not, you might be better off with krw

Returns

The estimated sgcr.

Return type

float

crosspoint()[source]

Locate and return the saturation point where krw = krow

Accuracy of this crosspoint depends on the resolution chosen when initializing the saturation range

selfcheck()[source]

Check validities of the data in the table.

An unfinished object will return False.

If you call SWOF, this function must not return False

This function should not throw an exception, but capture the error and give an error.

SWOF(header=True, dataincommentrow=True)[source]

Produce SWOF input for Eclipse reservoir simulator.

The columns sw, krw, krow and pc are outputted and formatted accordingly.

Meta-information for the tabulated data are printed as Eclipse comments.

Parameters
  • header (bool) – Indicate whether the SWOF string should be emitted. If you have multiple SATNUMs, you should set this to True only for the first (or False for all, and emit the SWOF yourself). Default True

  • dataincommentrow (bool) – Wheter metadata should be printed. Defualt True

SWFN(header=True, dataincommentrow=True)[source]

Return a SWFN keyword with data to Eclipse

WOTABLE(header=True, dataincommentrow=True)[source]

Return a string for a Nexus WOTABLE

plotpc(ax=None, color='blue', alpha=1, linewidth=1, linestyle='-', logyscale=False)[source]

Plot capillary pressure (pc) a supplied matplotlib axis

plotkrwkrow(ax=None, color='blue', alpha=1, linewidth=1, linestyle='-', logyscale=False)[source]

Plot krw and krow

If the argument ‘ax’ is not supplied, a new plot window will be made. If supplied, it will draw on the specified axis.

pyscal.wateroilgas module

Container object for one WaterOil and one GasOil object

class pyscal.wateroilgas.WaterOilGas(swirr=0, swl=0.0, swcr=0.0, sorw=0.0, sorg=0, sgcr=0, h=0.01, tag='', fast=False)[source]

Bases: object

A representation of three-phase properties for oil-water-gas

Use one object for each satnum.

One WaterOil and one GasOil object will be created, with compatible saturation ranges. Access the class members ‘wateroil’ and ‘gasoil’ directly to add curves.

All arguments can be defaulted, and all are zero except h.

Parameters
  • swirr (float) – Irreducible water saturation for capillary pressure

  • swl (float) – First water saturation point in outputted tables.

  • swcr (float) – Critical water saturation, water is immobile below this

  • sorw (float) – Residual oil saturation

  • sgcr (float) – Critical gas saturation, gas is immobile below this

  • h (float) – Saturation intervals in generated tables.

  • tag (str) – Optional text that will be included as comments.

  • fast (bool) – Set to True if you prefer speed over robustness. Not recommended, pyscal will not guarantee valid output in this mode.

selfcheck()[source]

Run selfcheck on both wateroil and gasoil.

Returns true only if both passes

SWOF(header=True, dataincommentrow=True)[source]

Return a SWOF string. Delegated to the wateroil object

SGOF(header=True, dataincommentrow=True)[source]

Return a SGOF string. Delegated to the gasoil object.

SLGOF(header=True, dataincommentrow=True)[source]

Return a SLGOF string. Delegated to the gasoil object.

SGFN(header=True, dataincommentrow=True)[source]

Return a SGFN string. Delegated to the gasoil object.

SWFN(header=True, dataincommentrow=True)[source]

Return a SWFN string. Delegated to the wateroil object.

SOF3(header=True, dataincommentrow=True)[source]

Return a SOF3 string, combining data from the wateroil and gasoil objects.

So - the oil saturation ranges from 0 to 1-swl. The saturation points from the WaterOil object is used to generate these

threephaseconsistency()[source]

Perform consistency checks on produced curves, similar to what Eclipse does at startup

Returns empty string if no errors catched. Alternatively an error description string is returned.

Possible variation of this function would be to throw Exceptions.

run_eclipse_test()[source]

Start the Eclipse simulator on a minimal deck in order to test the properties of the current WaterOilGas deck

Module contents

A module for creating relative permeability input curves for Eclipse and Nexus.