"""Oil PVT properties."""
from __future__ import annotations
import math
import numpy as np
from numpy.typing import NDArray
from bluebonnet.fluids.gas import b_factor_DAK
[docs]
def b_o_Standing(
temperature: float,
pressure: NDArray | float,
api_gravity: float,
gas_specific_gravity: float,
solution_gor_initial: float,
) -> NDArray | float:
"""Calculate the oil formation volume factor (Bo) using Standing.
Parameters
----------
temperature : float
reservoir temperature in Fahrenheit.
pressure : float
reservoir pressure (psia)
api_gravity : float
Oil gravity in degrees API.
gas_specific_gravity : float
Gas gravity relative to air.
solution_gor_initial : float
Initial gas:oil ratio in scf/bbl.
Returns
-------
float
Bo, the formation volume factor (V/V)
Examples
--------
>>> b_o_Standing(200, 3_000, 35, 0.8, 650)
>>> b_o_Standing(200, 2_000, 35, 0.8, 650)
1.2820114682225974
"""
pressure_bubblepoint = pressure_bubblepoint_Standing(
temperature, api_gravity, gas_specific_gravity, solution_gor_initial
)
fvf_bubblepoint = b_o_bubblepoint_Standing(
temperature, api_gravity, gas_specific_gravity, solution_gor_initial
)
solution_gor = solution_gor_Standing(
temperature, pressure, api_gravity, gas_specific_gravity, solution_gor_initial
)
if np.ndim(pressure) != 0: # check for non-scalar
compressibility_undersat = oil_compressibility_undersat_Spivey(
temperature,
pressure[pressure >= pressure_bubblepoint],
api_gravity,
gas_specific_gravity,
solution_gor_initial,
)
fvf_oil = np.empty_like(pressure)
fvf_oil[pressure >= pressure_bubblepoint] = fvf_bubblepoint * np.exp(
compressibility_undersat
* (pressure_bubblepoint - pressure[pressure >= pressure_bubblepoint])
)
fvf_oil[pressure < pressure_bubblepoint] = b_o_bubblepoint_Standing(
temperature,
api_gravity,
gas_specific_gravity,
solution_gor[pressure < pressure_bubblepoint],
)
else:
if pressure >= pressure_bubblepoint:
compressibility_undersat = oil_compressibility_undersat_Spivey(
temperature,
pressure,
api_gravity,
gas_specific_gravity,
solution_gor_initial,
)
fvf_oil = fvf_bubblepoint * np.exp(
compressibility_undersat * (pressure_bubblepoint - pressure)
)
else:
fvf_oil = b_o_bubblepoint_Standing(
temperature, api_gravity, gas_specific_gravity, solution_gor
)
return fvf_oil
[docs]
def pressure_bubblepoint_Standing(
temperature: float,
api_gravity: float,
gas_specific_gravity: float,
solution_gor_initial: float,
) -> float:
"""Calculate the bubble point pressure using Standing.
Parameters
----------
temperature : float
reservoir temperature in Fahrenheit.
api_gravity : float
Oil gravity in degrees API.
gas_specific_gravity : float
Gas gravity relative to air.
solution_gor_initial : float
Gas: oil ratio in scf/bbl.
Returns
-------
p_b : float
Bubble point pressure in psia.
Examples
--------
>>> pressure_bubblepoint_Standing(200, 35, 0.8, 650)
2627.2017021875276
"""
pressure_bubble = 18.2 * (
(solution_gor_initial / gas_specific_gravity) ** 0.83
* 10 ** (0.00091 * temperature - 0.0125 * api_gravity)
- 1.4
)
return pressure_bubble
[docs]
def b_o_bubblepoint_Standing(
temperature: float,
api_gravity: float,
gas_specific_gravity: float,
solution_gor_initial: NDArray | float,
) -> NDArray | float:
"""Calculate the oil formation volume factor (Bo) at the bubble point using Standing.
Parameters
----------
temperature : float
reservoir temperature in Fahrenheit.
api_gravity : float
Oil gravity in degrees API.
gas_specific_gravity : float
Gas gravity relative to air.
solution_gor_initial : NDArray
Initial gas:oil ratio in scf/bbl.
Returns
-------
b_o : NDArray
Oil formation volume factor (rb/stb)
Example
-------
>>> b_o_bubblepoint_Standing(200, 35, 0.8, 650)
1.3860514623492897
"""
oil_specific_gravity = 141.5 / (131.5 + api_gravity)
fvf_bubblepoint = (
0.9759
+ 0.00012
* (
solution_gor_initial * np.sqrt(gas_specific_gravity / oil_specific_gravity)
+ 1.25 * temperature
)
** 1.2
)
return fvf_bubblepoint
[docs]
def db_o_dgor_Standing(
temperature: float,
api_gravity: float,
gas_specific_gravity: float,
solution_gor_initial: float,
) -> float:
"""Calculate the derivative of Bo per solution GOR at the bubble point.
Parameters
----------
temperature : float
reservoir temperature in Fahrenheit.
api_gravity : float
Oil gravity in degrees API.
gas_specific_gravity : float
Gas gravity relative to air.
solution_gor_initial : NDArray
Initial gas:oil ratio in scf/bbl.
Returns
-------
dbo_dgor: NDArray
Change in Oil formation volume factor per unit GOR (rb/stb / (scf/bbl))
Example
-------
>>> db_o_dgor_Standing(200, 35, 0.8, 650)
"""
oil_specific_gravity = 141.5 / (131.5 + api_gravity)
sqrt_gravity_ratio = np.sqrt(gas_specific_gravity / oil_specific_gravity)
dB_o_dgor = (
1.2
* 1.2e-4
* (solution_gor_initial * sqrt_gravity_ratio + 1.25 * temperature) ** 0.2
* sqrt_gravity_ratio
)
return dB_o_dgor
[docs]
def solution_gor_Standing(
temperature: float,
pressure: NDArray | float,
api_gravity: float,
gas_specific_gravity: float,
solution_gor_initial: float,
) -> NDArray | float:
"""Calculate the solution GOR partition using Standing.
Parameters
----------
temperature : float
reservoir temperature in Fahrenheit.
pressure : float or array
reservoir pressure (psia)
api_gravity : float
Oil gravity in degrees API.
gas_specific_gravity : float
Gas gravity relative to air.
solution_gor_initial : float
Gas: oil ratio in scf/bbl.
Returns
-------
gor : float
Solution GOR in scf/stb
Examples
--------
>>> solution_gor_Standing(200, 3_000, 35, 0.8, 650)
650
>>> solution_gor_Standing(200, 2_000, 35, 0.8, 650)
453.6099792270382
"""
pressure_bubblepoint = pressure_bubblepoint_Standing(
temperature, api_gravity, gas_specific_gravity, solution_gor_initial
)
def gor_belowbubble(pressure: NDArray | float) -> NDArray | float:
gor = gas_specific_gravity * (
(pressure / 18.2 + 1.4) * 10 ** (0.0125 * api_gravity - 0.00091 * temperature)
) ** (1 / 0.83)
return gor
if np.ndim(pressure) != 0:
solution_gor = np.full_like(pressure, solution_gor_initial)
solution_gor[pressure < pressure_bubblepoint] = gor_belowbubble(
pressure[pressure < pressure_bubblepoint]
)
else:
if pressure >= pressure_bubblepoint:
solution_gor = solution_gor_initial
else:
solution_gor = gor_belowbubble(pressure)
return solution_gor
[docs]
def dgor_dpressure_Standing(
temperature: float,
pressure: float,
api_gravity: float,
gas_specific_gravity: float,
solution_gor_initial: float,
) -> float:
r"""Calculate the instantaneous change in GOR with pressure using Standing.
Parameters
----------
temperature : float
reservoir temperature in Fahrenheit.
pressure : float
reservoir pressure (psia)
api_gravity : float
Oil gravity in degrees API.
gas_specific_gravity : float
Gas gravity relative to air.
solution_gor_initial : float
Initial gas:oil ratio in scf/bbl.
Returns
-------
dgor : float
Change in GOR per pressure ( scf / scf / psi). :math:`\partial R_s/\partial p`
Examples
--------
>>> dgor_dpressure_Standing(200, 3_000, 35, 0.8, 650)
0
>>> dgor_dpressure_Standing(200, 2_000, 35, 0.8, 650)
0.2824395998221715
"""
pressure_bubblepoint = pressure_bubblepoint_Standing(
temperature, api_gravity, gas_specific_gravity, solution_gor_initial
)
if pressure >= pressure_bubblepoint:
d_gor = 0.0
else:
d_gor = (
gas_specific_gravity
/ (0.83 * 18.2)
* (pressure / 18.2 + 1.4) ** (1 / 0.83 - 1)
* (10 ** ((0.0125 * api_gravity - 0.00091 * temperature) / 0.83))
)
return d_gor
[docs]
def oil_compressibility_undersat_Standing(
temperature: float,
pressure: float,
api_gravity: float,
gas_specific_gravity: float,
solution_gor_initial: float,
) -> float:
"""Calculate the oil compressibility (c_o) using Standing.
Parameters
----------
temperature : float
reservoir temperature in Fahrenheit.
pressure : float
reservoir pressure (psia)
api_gravity : float
Oil gravity in degrees API.
gas_specific_gravity : float
Gas gravity relative to air.
solution_gor_initial : float
Initial gas:oil ratio in scf/bbl.
Returns
-------
c_o : float
oil compressibility in 1/psi
Example
-------
>>> oil_compressibility_undersat_Standing(200, 3_000, 35, 0.8, 650)
1.478788544207282e-05
"""
oil_specific_gravity = 141.5 / (131.5 + api_gravity)
pressure_bubblepoint = pressure_bubblepoint_Standing(
temperature, api_gravity, gas_specific_gravity, solution_gor_initial
)
b_o_bubblepoint = b_o_bubblepoint_Standing(
temperature, api_gravity, gas_specific_gravity, solution_gor_initial
)
density_bubblepoint = (
62.37 * oil_specific_gravity + 0.0136 * gas_specific_gravity * solution_gor_initial
) / b_o_bubblepoint
oil_compressibility = 1e-6 * math.exp(
(density_bubblepoint + 0.004347 * (pressure - pressure_bubblepoint) - 79.1)
/ (7.141e-4 * (pressure - pressure_bubblepoint) - 12.938)
)
return oil_compressibility
[docs]
def oil_compressibility_undersat_Spivey(
temperature: float,
pressure: NDArray | float,
api_gravity: float,
gas_specific_gravity: float,
solution_gor_initial: float,
) -> NDArray | float:
"""Calculate the oil compressibility (c_o) using Spivey.
Parameters
----------
temperature : float
reservoir temperature in Fahrenheit.
pressure : float or array
reservoir pressure (psia)
api_gravity : float
Oil gravity in degrees API.
gas_specific_gravity : float
Gas gravity relative to air.
solution_gor_initial : float
Initial gas:oil ratio in scf/bbl.
Returns
-------
c_o : float
oil compressibility in 1/psi
Examples
--------
>>> oil_compressibility_undersat_Spivey(200, 3_000, 35, 0.8, 650)
9.177554347361075e-06
"""
pressure_bubblepoint = pressure_bubblepoint_Standing(
temperature, api_gravity, gas_specific_gravity, solution_gor_initial
)
# sometimes this is passed an empty pressure array when pressures are all
# above the bubblepoint
if np.size(pressure) == 0:
return np.array([], dtype=np.float64)
reduced_pressure = pressure / pressure_bubblepoint
# set up constants for quadratic formula
# C_labels = [
# 'api_gravity', 'gas_specific_gravity', 'pressure_bubblepoint',
# 'reduced_pressure', 'solution_gor_initial','temperature'
# ]
C0 = np.array([3.011, -0.835, 3.51, 0.327, -1.918, 2.52])
C1 = np.array([-2.6254, -0.259, -0.0289, -0.608, -0.642, -2.73])
C2 = np.array([0.497, 0.382, -0.0584, 0.0911, 0.154, 0.429])
if np.ndim(reduced_pressure) == 0:
X = np.log(
[
api_gravity,
gas_specific_gravity,
pressure_bubblepoint,
reduced_pressure,
solution_gor_initial,
temperature,
],
dtype="f8",
)
else:
X = np.log(
[
[
api_gravity,
gas_specific_gravity,
pressure_bubblepoint,
rp,
solution_gor_initial,
temperature,
]
for rp in reduced_pressure
]
)
z = np.sum(C0) + X @ C1 + (X**2) @ C2
compressibility_bubblepoint = np.exp(2.434 + 0.475 * z + 0.048 * z**2)
compressibility_derivative = (-0.608 + 0.1822 * np.log(reduced_pressure)) / pressure
compressibility_2nd_derivative = (
compressibility_bubblepoint * (0.475 + 0.096 * z) * compressibility_derivative
)
oil_compressibility = 1e-6 * (
compressibility_bubblepoint
+ (pressure - pressure_bubblepoint) * compressibility_2nd_derivative
)
return oil_compressibility
[docs]
def oil_compressibility_Standing(
temperature: float,
pressure: float,
api_gravity: float,
gas_specific_gravity: float,
solution_gor_initial: float,
temperature_pseudocritical: float,
pressure_pseudocritical: float,
temperature_standard: float = 60,
pressure_standard: float = 14.7,
) -> float:
"""Calculate the oil formation volume factor (Bo) using Standing.
Parameters
----------
temperature : float
reservoir temperature in Fahrenheit.
pressure : float
reservoir pressure (psia)
api_gravity : float
Oil gravity in degrees API.
gas_specific_gravity : float
Gas gravity relative to air.
solution_gor_initial : float
Initial gas:oil ratio in scf/bbl.
pressure_pseudocritical : float
Gas pseudocritical pressure (use gas module) in psia
temperature_pseudocritical : float
Gas pseudocritical temperature (use gas module) in F
temperature_standard : float
standard temperature in F (default: 60)
pressure_standard : float
standard pressure in psia (default: 14.70)
Returns
-------
c_o : float
oil compressibility in 1/psi
Examples
--------
>>> oil_compressibility_Standing(200, 3000, 35, 0.8, 650, -72.2, 653)
>>> oil_compressibility_Standing(200, 2000, 35, 0.8, 650, -72.2, 653)
"""
pressure_bp = pressure_bubblepoint_Standing(
temperature, api_gravity, gas_specific_gravity, solution_gor_initial
)
if pressure >= pressure_bp:
compressibility = oil_compressibility_undersat_Spivey(
temperature,
pressure,
api_gravity,
gas_specific_gravity,
solution_gor_initial,
)
else:
b_g = b_factor_DAK(
temperature,
pressure,
temperature_pseudocritical,
pressure_pseudocritical,
temperature_standard,
pressure_standard,
)
solution_gor = solution_gor_Standing(
temperature,
pressure,
api_gravity,
gas_specific_gravity,
solution_gor_initial,
)
dGOR_dpressure = (
gas_specific_gravity
/ 0.83
* (pressure / 18.2 + 1.4) ** (1 / 0.83 - 1)
/ 18.2
* 10 ** ((0.0125 * api_gravity - 0.00091 * temperature) / 0.83)
)
b_o_bubblepoint = b_o_bubblepoint_Standing(
temperature, api_gravity, gas_specific_gravity, solution_gor_initial
)
dBo_dGOR = db_o_dgor_Standing(temperature, api_gravity, gas_specific_gravity, solution_gor)
compressibility = (b_g - dBo_dGOR) * dGOR_dpressure / b_o_bubblepoint
return compressibility
[docs]
def density_Standing(
temperature: float,
pressure: float,
api_gravity: float,
gas_specific_gravity: float,
solution_gor_initial: float,
) -> float:
"""Calculate the oil density using Standing.
Parameters
----------
temperature : float
reservoir temperature in Fahrenheit.
pressure : float
reservoir pressure (psia)
api_gravity : float
Oil gravity in degrees API.
gas_specific_gravity : float
Gas gravity relative to air.
solution_gor_initial : float
Initial gas:oil ratio in scf/bbl.
Returns
-------
rho_o : float
oil density (lb/ft^3)
Examples
--------
>>> density_Standing(200, 2000, 35, 0.8, 650)
44.989365953905136
"""
oil_specific_gravity = 141.5 / (131.5 + api_gravity)
solution_gor = solution_gor_Standing(
temperature, pressure, api_gravity, gas_specific_gravity, solution_gor_initial
)
b_o = b_o_Standing(
temperature, pressure, api_gravity, gas_specific_gravity, solution_gor_initial
)
density = (62.37 * oil_specific_gravity + 0.0136 * gas_specific_gravity * solution_gor) / b_o
return density
[docs]
def viscosity_beggs_robinson(
temperature: float,
pressure: float,
api_gravity: float,
gas_specific_gravity: float,
solution_gor_initial: float,
) -> float:
r"""Calculate the oil viscosity using Beggs-Robinson.
Parameters
----------
temperature : float
reservoir temperature in Fahrenheit.
pressure : float
reservoir pressure (psia)
api_gravity : float
Oil gravity in degrees API.
gas_specific_gravity : float
Gas gravity relative to air.
solution_gor_initial : float
Initial gas:oil ratio in scf/bbl.
Returns
-------
mu_o : float
:math:`\mu_o`, the oil viscosity
"""
pressure_bubblepoint = pressure_bubblepoint_Standing(
temperature, api_gravity, gas_specific_gravity, solution_gor_initial
)
solution_gor = solution_gor_Standing(
temperature, pressure, api_gravity, gas_specific_gravity, solution_gor_initial
)
if pressure >= pressure_bubblepoint:
mu_o_dead = 10 ** (10 ** (3.0324 - 0.02023 * api_gravity) * temperature**-1.163) - 1
mu_o_live = _mu_dead_to_live_br(mu_o_dead, solution_gor_initial)
mu_o = mu_o_live * (pressure / pressure_bubblepoint) ** (
2.6 * pressure**1.187 * np.exp(-11.513 - 8.98e-5 * pressure)
)
else:
mu_o_dead = 10 ** (10 ** (3.0324 - 0.02023 * api_gravity) * temperature**-1.163) - 1
mu_o = _mu_dead_to_live_br(mu_o_dead, solution_gor)
return mu_o
def _mu_dead_to_live_br(
mu_dead: NDArray | float, solution_gor_initial: NDArray | float
) -> NDArray | float:
mu_live = (
10.715
* (solution_gor_initial + 100) ** -0.515
* mu_dead ** (5.44 * (solution_gor_initial + 150) ** -0.338)
)
return mu_live