Source code for pyscal.scalrecommendation

"""SCALrecommendation, container for low, base and high WaterOilGas objects"""

import copy
from typing import Optional, Set, Type, Union

import numpy as np

from pyscal import GasWater, WaterOilGas, getLogger_pyscal
from pyscal.utils.interpolation import interpolate_go, interpolate_wo

logger = getLogger_pyscal(__name__)


[docs]class SCALrecommendation(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). Args: low: An object representing the low case base: An object representing the base case high: An object representing the high case tag: A string that describes the recommendation. Optional. """ def __init__( self, low: Union[WaterOilGas, GasWater], base: Union[WaterOilGas, GasWater], high: Union[WaterOilGas, GasWater], tag: Optional[str] = None, h: float = 0.01, ) -> None: """Set up a SCAL recommendation curve set from WaterOilGas objects Arguments: low: low case base: base case high: high case tag: Describes the recommendation. This string will be used as tag strings for the interpolants. """ self.h: float = h self.tag: Optional[str] = tag self.low: Union[WaterOilGas, GasWater] self.base: Union[WaterOilGas, GasWater] self.high: Union[WaterOilGas, GasWater] self.type: Type if ( isinstance(low, WaterOilGas) and isinstance(base, WaterOilGas) and isinstance(high, WaterOilGas) ): self.low = low self.base = base self.high = high self.type = WaterOilGas elif ( isinstance(low, GasWater) and isinstance(base, GasWater) and isinstance(high, GasWater) ): self.low = low self.base = base self.high = high self.type = GasWater else: raise ValueError("Wrong arguments to SCALrecommendation") self.fast: bool = False if all([self.low.fast, self.base.fast, self.high.fast]): self.fast = True elif any([self.low.fast, self.base.fast, self.high.fast]): self.fast = self.low.fast = self.base.fast = self.high.fast = False logger.warning( ( "One or more of the low/base/high objects are set to be run in " "fast mode, but not all. Fast mode set to false in all objects. " "Code is run in normal mode." ) ) # User should add capillary pressure explicitly by calling add** # on the class objects, or run the following method to add the # same to all curves:
[docs] def add_simple_J( self, a: float = 5.0, b: float = -1.5, poro_ref: float = 0.25, perm_ref: float = 100.0, drho: float = 300.0, g: float = 9.81, ) -> None: """Add (identical) simplified J-function to all water-oil curves in the SCAL recommendation set""" assert self.low.wateroil is not None assert self.base.wateroil is not None assert self.high.wateroil is not None self.low.wateroil.add_simple_J( a=a, b=b, poro_ref=poro_ref, perm_ref=perm_ref, drho=drho, g=g ) self.base.wateroil.add_simple_J( a=a, b=b, poro_ref=poro_ref, perm_ref=perm_ref, drho=drho, g=g ) self.high.wateroil.add_simple_J( a=a, b=b, poro_ref=poro_ref, perm_ref=perm_ref, drho=drho, g=g )
[docs] def interpolate( self, parameter: float, parameter2: Optional[float] = None, h: Optional[float] = None, ) -> Union[WaterOilGas, GasWater]: """Interpolate between low, base and high 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 WaterOilGas 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. Args: parameter: Between -1 and 1, inclusive. -1 reproduces low/ pessimistic curve, 0 gives base, 1 gives high/optimistic. parameter2: If not None, used for the gas-oil interpolation, enables having interpolation uncorrelated for WaterOil and GasOil. Ignored for GasWater (no warning). h: Saturation step length in generated tables. Does not need to be the same as the tables interpolation is done from. """ if parameter2 is not None: gasparameter = parameter2 else: gasparameter = parameter # Either wateroil or gasoil can be None in the low, base, high # If they are None, it is a two-phase problem and we # should support this. do_gaswater = False do_wateroil = False do_gasoil = False if self.type == GasWater: do_gaswater = True elif self.type == WaterOilGas: do_wateroil = ( self.base.wateroil is not None and self.low.wateroil is not None and self.high.wateroil is not None ) do_gasoil = ( self.base.gasoil is not None and self.low.gasoil is not None and self.high.gasoil is not None ) if parameter2 is not None: if not do_gasoil: logger.warning("parameter2 is meaningless for water-oil only") if do_gaswater: logger.warning("parameter2 is meaningless for gas-water") # Initialize wateroil and gasoil curves to be filled with # interpolated curves: interpolant: Union[WaterOilGas, GasWater] tags: Set[str] = set() if do_wateroil or do_gaswater: assert self.low.wateroil is not None assert self.base.wateroil is not None assert self.high.wateroil is not None tags = tags.union( set( [ self.base.wateroil.tag, self.low.wateroil.tag, self.high.wateroil.tag, ] ) ) if do_gasoil: assert self.low.gasoil is not None assert self.base.gasoil is not None assert self.high.gasoil is not None tags = tags.union( set([self.base.gasoil.tag, self.low.gasoil.tag, self.high.gasoil.tag]) ) tagstring = "\n".join(tags) if do_gaswater: interpolant = GasWater(h=h, tag=tagstring) else: interpolant = WaterOilGas(h=h, tag=tagstring) if do_wateroil or do_gaswater: tag = f"SCAL recommendation interpolation to {parameter}\n" + tagstring assert self.low.wateroil is not None assert self.base.wateroil is not None assert self.high.wateroil is not None if abs(parameter) > 1.0: raise ValueError( f"Interpolation parameter must be in [-1,1], got {parameter}" ) if np.isclose(parameter, 0.0): interpolant.wateroil = copy.deepcopy(self.base.wateroil) interpolant.wateroil.tag = tag elif np.isclose(parameter, -1.0): interpolant.wateroil = copy.deepcopy(self.low.wateroil) interpolant.wateroil.tag = tag elif np.isclose(parameter, 1.0): interpolant.wateroil = copy.deepcopy(self.high.wateroil) interpolant.wateroil.tag = tag elif parameter < 0.0: interpolant.wateroil = interpolate_wo( self.base.wateroil, self.low.wateroil, -parameter, h=h, tag=tag, ) elif parameter > 0.0: interpolant.wateroil = interpolate_wo( self.base.wateroil, self.high.wateroil, parameter, h=h, tag=tag, ) else: interpolant.wateroil = None if do_gasoil or do_gaswater: assert self.low.gasoil is not None assert self.base.gasoil is not None assert self.high.gasoil is not None tag = f"SCAL recommendation interpolation to {gasparameter}\n" + tagstring if abs(gasparameter) > 1.0: raise ValueError( "Interpolation parameter for gas must " f"be in [-1,1], got {gasparameter}" ) if np.isclose(gasparameter, 0.0): interpolant.gasoil = copy.deepcopy(self.base.gasoil) interpolant.gasoil.tag = tag elif np.isclose(gasparameter, -1.0): interpolant.gasoil = copy.deepcopy(self.low.gasoil) interpolant.gasoil.tag = tag elif np.isclose(gasparameter, 1.0): interpolant.gasoil = copy.deepcopy(self.high.gasoil) interpolant.gasoil.tag = tag elif gasparameter < 0.0: interpolant.gasoil = interpolate_go( self.base.gasoil, self.low.gasoil, -1 * gasparameter, h=h, tag=tag, ) elif gasparameter > 0.0: interpolant.gasoil = interpolate_go( self.base.gasoil, self.high.gasoil, gasparameter, h=h, tag=tag, ) else: interpolant.gasoil = None interpolant.fast = self.fast return interpolant