#!/usr/bin/env python
# -*-coding:utf-8 -*-
'''
Batzle and Wang functionalities
'''
import numpy as np
import warnings
from rockphypy.Fluid import Fluid
[docs]class BW:
"""Effective CO2, natural gas, brine and oil property calculation using original and modified Batzle-Wang equations.
"""
[docs] @staticmethod
def dz_dp(P_pr,T_pr):
"""Values for dZ/dPpr obtained from equation 10b in Batzle and Wang (1992).
"""
# analytic
dzdp=(0.03+0.00527*(3.5-T_pr)**3)+0.109*(3.85-T_pr)**2*1.2*P_pr**0.2*-(0.45+8*(0.56-1/T_pr)**2)/T_pr*np.exp(-(0.45+8*(0.56-1/T_pr)**2)*P_pr**1.2/T_pr)
# numerical approximation
# dzdp= 1.938783*P_pr**0.2*(1 - 0.25974025974026*T_pr)**2*(-8*(0.56 - 1/T_pr)**2 - 0.45)*np.exp(P_pr**1.2*(-8*(0.56 - 1/T_pr)**2 - 0.45)/T_pr)/T_pr + 0.22595125*(1 - 0.285714285714286*T_pr)**3 + 0.03
return dzdp
#-----------------Pure Gas--------------------------#
[docs] @staticmethod
def pseudo_p_t(P,T,G):
"""Calculate the pseudoreduced temperature and pressure according to Thomas et al. 1970.
Parameters
----------
P : float or array-like
Pressure in MPa
T : float or array-like
Temperature in °C
G : float
Gas gravity
Returns
-------
float or array-like
Ta: absolute temperature
Ppr:pseudoreduced pressure
Tpr:pseudoreduced temperature
"""
# convert the temperature to absolute temperature
Ta=T+273.15
P_pr=P/(4.892-0.4048*G)
T_pr=Ta/(94.72+170.75*G)
return Ta,P_pr,T_pr
#-----------------Pure Co2--------------------------#
[docs] @staticmethod
def rho_K_co2(P,T,G):
"""Compute CO2 properties as a function of temperature and pressure using modified Batzle-Wang equations
Parameters
----------
P : float or array-like
Pressure in MPa
T : float or array-like
Temperature in °C
G : float
Gas gravity
Returns
-------
float or array-like
rho (g/cc): gas density
K (GPa): bulk modulus
References
----------
Xu, H. (2006). Calculation of CO2 acoustic properties using Batzle-Wang equations. Geophysics, 71(2), F21-F23.
"""
R=8.3145 # J.mol-1K-1 gas constant
Ta= T+273.15
P_pr= P/7.4
T_pr= Ta/(31.1+273.5)
E=0.109*(3.85-T_pr)**2*np.exp(-(0.45+8*(0.56-1/T_pr)**2)*P_pr**1.2/T_pr)
Z=(0.03+0.00527*(3.5-T_pr)**3)*P_pr+(0.642*T_pr-0.007*T_pr**4-0.52)+E
rho=28.8*G*P/(Z*R*Ta)
r_0=0.85+5.6/(P_pr+2)+27.1/(P_pr+3.5)**2-8.7*np.exp(-0.65*(P_pr+1))
#dz_dp=(0.03+0.00527*(3.5-T_pr)**3)+0.109*(3.85-T_pr)**2*1.2*P_pr**0.2*-(0.45+8*(0.56-1/T_pr)**2)/T_pr*np.exp(-(0.45+8*(0.56-1/T_pr)**2)*P_pr**1.2/T_pr)
dzdp=BW.dz_dp(P_pr,T_pr)
K=P/(1-P_pr*dzdp/Z)*r_0
return rho, K/1000
#-----------------Pure Gas--------------------------#
[docs] @staticmethod
def rho_K_gas(P, T, G):
"""Estimate the Gas density and bulk modulus at specific temperature and pressure.
Parameters
----------
P : float or array-like
Pressure in MPa
T : float or array-like
Temperature in °C
G : float
Gas gravity
Returns
-------
float or array-like
rho: Gas density (g/cm3)
K: Gas bulk modulus (GPa)
"""
R=8.3145 # J.mol-1K-1 gas constant
Ta,P_pr,T_pr=BW.pseudo_p_t(P,T,G)
E=0.109*(3.85-T_pr)**2*np.exp(-(0.45+8*(0.56-1/T_pr)**2)*P_pr**1.2/T_pr)
Z=(0.03+0.00527*(3.5-T_pr)**3)*P_pr+(0.642*T_pr-0.007*T_pr**4-0.52)+E
rho=28.8*G*P/(Z*R*Ta)
r_0=0.85+5.6/(P_pr+2)+27.1/(P_pr+3.5)**2-8.7*np.exp(-0.65*(P_pr+1))
#dz_dp=(0.03+0.00527*(3.5-T_pr)**3)+0.109*(3.85-T_pr)**2*1.2*P_pr**0.2*-(0.45+8*(0.56-1/T_pr)**2)/T_pr*np.exp(-(0.45+8*(0.56-1/T_pr)**2)*P_pr**1.2/T_pr)
dzdp=BW.dz_dp(P_pr,T_pr)
K=P/(1-P_pr*dzdp/Z)*r_0
return rho,K/1000
#-----------------Pure Oil--------------------------#
[docs] @staticmethod
def rho_K_oil(P,T,den):
"""Estimate the oil density and bulk modulus at specific temperature and pressure.
Parameters
----------
P : float or array-like
Pressure in MPa
T : float or array-like
Temperature in °C
den : float
oil density in g/cm3
Returns
-------
float or array-like
rho: oil density (g/cm3)
K: oil bulk modulus (GPa)
"""
rho_p=den+(0.00277*P-1.71*0.0000001*P**3)*(den-1.15)**2+3.49*0.0001*P
rho=rho_p/(0.972+3.81*0.0001*(T+17.78)**1.175)
v=2096*(den/(2.6-den))**0.5-3.7*T+4.64*P+0.0115*(4.12*(1.08/den-1)**0.5-1)*T*P
K=rho*v**2
return rho, K/1e6
#-----------------Gas satutrated with oil--------------------------#
[docs] @staticmethod
def rho_K_go(P, T, den,G,Rg):
"""compute density and bulk modulus of live oil.
Parameters
----------
P : float or array-like
Pressure in MPa
T : float or array-like
Temperature in °C
den : float
oil density in g/cm3
G : float
gas gravity
Rg : float
the volume ratio of liberated gas to remaining oil at atmospheric pressure and 15.6°C, Liter/Liter
Returns
-------
float or array-like
rho_g (g/cm3): true density of live oil at saturation
K (GPa): true bulk modulus of live oil at saturation
"""
if Rg == None:
Rg=0.02123*G*(P*np.exp(4.072/den-0.00377*T))**1.205
B=0.972+0.00038*(2.4*Rg*(G/den)**0.5+T+17.8)**1.175
rho_p=den*(1+0.001*Rg)**-1*B**-1 # pseudodensity
v=2096*(rho_p/(2.6-rho_p))**0.5-3.7*T+4.64*P+0.0115*(4.12*(1.08/rho_p-1)**0.5-1)*T*P
# true density of live oil at saturation
rho_g=(den+0.0012*G*Rg)/B
K=rho_g*v**2
# return v
return rho_g, K/1e6
#-----------------Pure Water--------------------------#
[docs] @staticmethod
def rho_K_water(T, P):
"""Compute the density and bulk modulus of pure water as a function of temperature and pressure using Batzle and Wang (1992).
Parameters
----------
T : float or array-like
Temperature in °C
P : float or array-like
Pressure in MPa
Returns
-------
float or array-like
rho_w (g/cm3): density of pure water
K_w (Gpa): bulk modulus of pure water
"""
#pressure=pressure*1e6
# Align with the symbols and units in Batzle & Wang.
#T, P = np.asanyarray(temperature), np.asanyarray(pressure)
rho_w = 1+1e-6*(-80*T- 3.3*T**2+0.00175*T**3+489*P-2*T*P+0.016*P*T**2-1.3e-5*T**3*P-0.333*P**2-0.002*T*P**2)
v_w= BW.v_water(T, P)
K_w= rho_w*v_w**2*1e-6
return rho_w, K_w
[docs] @staticmethod
def v_water(T, P):
"""Acoustic velocity of pure water as a function of temperature
and pressure using Batzle and Wang (1992).
Parameters
----------
T : float or array-like
Temperature in °C
P : float or array-like
Pressure in MPa
Returns
-------
float or array-like
v_w (m/s): acoustic velocity of pure water
"""
if np.any(P > 100):
warnings.warn(
'pressures above about 100 MPa-> inaccurate estimations'
)
w = np.array([[ 1.40285e+03, 1.52400e+00, 3.43700e-03, -1.19700e-05],
[ 4.87100e+00, -1.11000e-02, 1.73900e-04, -1.62800e-06],
[-4.78300e-02, 2.74700e-04, -2.13500e-06, 1.23700e-08],
[ 1.48700e-04, -6.50300e-07, -1.45500e-08, 1.32700e-10],
[-2.19700e-07, 7.98700e-10, 5.23000e-11, -4.61400e-13]])
v_w=sum(w[i, j] * T**i * P**j for i in range(5) for j in range(4))
return v_w
[docs] @staticmethod
def rho_K_brine(T,P,S):
"""Calculation of the density and bulk modulus of brine (NaCl) as a function of temperature, salinity and pressure using Batzle and Wang (1992).
Parameters
----------
T : float or array-like
Temperature in °C
P : float or array-like
Pressure in MPa
S : float
weight fraction of sodium chloride in ppm/1e6
Returns
-------
float or array-like
rho_b (g/cm3): the density of brine
K_b (GPa):bulk modulus of brine
"""
rho_w,_ = BW.rho_K_water(T, P)
x = 300*P-2400*P*S+ T*(80+ 3*T- 3300*S - 13*P + 47*P*S)
rho_b = rho_w + S*(0.668+ 0.44*S + 1e-6*x)
v_b = BW.v_brine(T, P, S)
K_b = rho_b*v_b**2*1e-6
return rho_b, K_b
[docs] @staticmethod
def v_brine(T, P, S):
"""Calculte the acoustic velocity of brine as a function of temperature, salinity and pressure using Batzle and Wang (1992).
Parameters
----------
T : float or array-like
Temperature in °C
P : float or array-like
Pressure in MPa
S : float
weight fraction of sodium chloride in ppm/1e6
Returns
-------
float or array-like
v_b (m/s): the velocity of brine
"""
v_w = BW.v_water(T, P)
s1 = 1170 - 9.6*T + 0.055*T**2 - 8.5e-5*T**3 + 2.6*P - 0.0029*T*P - 0.0476*P**2
s15 = 780 - 10*P + 0.16*P**2
s2 = -820
v_b = v_w + s1 * S + s15 * S**1.5 + s2 * S**2
return v_b
[docs] @staticmethod
def co2_brine(temperature, pressure, salinity, Sco2, brie_component=None, bw=False):
"""compute the effective properties of critical Co2 brine mixture depending on temperature, pressure and salinity of the brine, as well as the saturation state.
Args:
temperature (degree)
pressure (Mpa): pore pressure, not effective stress
salinity (ppm): The weight fraction of NaCl, e.g. 35e-3
for 35 parts per thousand, or 3.5% (the salinity of
seawater).
Sco2 (frac): Co2 saturation
brie_component (num): if None: uniform saturation. otherwise patchy saturation according to brie mixing
Returns:
den_mix (g/cc): mixture density
Kf_mix (GPa): bulk modulus
"""
G = 1.5349
if bw is True:
rho_co2, K_co2 = BW.rho_K_gas(pressure, temperature, G)
else:
rho_co2, K_co2 = BW.rho_K_co2(pressure, temperature, G)
rho_brine, K_b = BW.rho_K_brine(temperature, pressure, salinity)
den_mix = (1-Sco2)*rho_brine+Sco2*rho_co2
if brie_component == None:
Kf_mix = ((1-Sco2)/K_b+Sco2/K_co2)**-1 # Woods formula
else:
# patchy saturation
Kf_mix = Fluid.Brie(K_b, K_co2, 1-Sco2, brie_component)
# print('Kco2',K_co2,'K_b', K_b)
# print('density',den_mix,'moduli',Kf_mix)
return den_mix, Kf_mix