Source code for bluebonnet.fluids.fluid

"""Calculate fluid PVT properties."""

from __future__ import annotations

from collections.abc import Mapping
from dataclasses import dataclass

import numpy as np
import pandas as pd
import scipy as sp
from numpy.typing import NDArray
from scipy.integrate import cumulative_trapezoid

from bluebonnet.fluids.gas import (
    b_factor_DAK,
    compressibility_DAK,
    density_DAK,
    make_nonhydrocarbon_properties,
    pseudocritical_point_Sutton,
    viscosity_Sutton,
    z_factor_DAK,
)
from bluebonnet.fluids.oil import (
    b_o_Standing,
    pressure_bubblepoint_Standing,
    viscosity_beggs_robinson,
)
from bluebonnet.fluids.water import b_water_McCain, viscosity_water_McCain

PRESSURE_STANDARD = 14.7
"""STP pressure (14.7 psi)"""
TEMPERATURE_STANDARD = 60
"""STP Temperature (60 degrees F)"""


[docs] def build_pvt_gas( gas_values: Mapping, gas_dryness: str, maximum_pressure: float = 14_000 ) -> pd.DataFrame: """Build a table of PVT properties for use in the flow module. Parameters ---------- gas_values : dictionary keys include 'N2','H2S','CO2', 'Gas Specific Gravity', 'Reservoir Temperature (deg F)' gas_dryness : str One of 'wet gas' or 'dry gas' maximum_pressure : float initial reservoir pressure to calculate up to. Returns ------- pd.DataFrame pvt_table """ non_hydrocarbon_properties = make_nonhydrocarbon_properties( gas_values["N2"], gas_values["H2S"], gas_values["CO2"] ) temperature_pc, pressure_pc = pseudocritical_point_Sutton( gas_values["Gas Specific Gravity"], non_hydrocarbon_properties, gas_dryness, ) pressure = np.arange(10.0, maximum_pressure, 10.0) temperature = gas_values["Reservoir Temperature (deg F)"] z_factor = np.array( [z_factor_DAK(temperature, p, temperature_pc, pressure_pc) for p in pressure] ) density = np.array( [ density_DAK( temperature, p, temperature_pc, pressure_pc, float(gas_values["Gas Specific Gravity"]), ) for p in pressure ] ) viscosity = np.array( [ viscosity_Sutton( temperature, p, temperature_pc, pressure_pc, float(gas_values["Gas Specific Gravity"]), ) for p in pressure ] ) compressibility = np.array( [compressibility_DAK(temperature, p, temperature_pc, pressure_pc) for p in pressure] ) pvt_gas = pd.DataFrame( data={ "temperature": np.full_like(pressure, temperature), "pressure": pressure, "Density": density, "z-factor": z_factor, "compressibility": compressibility, "viscosity": viscosity, } ) pseudopressure = 2 * cumulative_trapezoid( pvt_gas["pressure"] / (pvt_gas["viscosity"] * pvt_gas["z-factor"]), pvt_gas["pressure"], initial=0.0, ) pvt_gas["pseudopressure"] = pseudopressure return pvt_gas
[docs] @dataclass class Fluid: r"""Fluid PVT properties. Parameters ---------- temperature: float reservoir temperature (in F) api_gravity: float oil gravity in degrees API gas_specific_gravity: float Gas gravity relative to air solution_gor_initial: float initial reservoir gas-oil ratio in scf/bbl salinity: float water salinity in weight percent total dissolved solids water_saturation_initial: float initial water saturation (V/V) """ temperature: float """Temperature in F""" api_gravity: float """Oil API gravity""" gas_specific_gravity: float """Gas specific gravity""" solution_gor_initial: float """Initial gas-oil ratio""" salinity: float = 0.0 """Salinity (total dissolved solids)""" water_saturation_initial: float = 0.0 """Initial water saturation"""
[docs] def water_FVF(self, pressure: NDArray[float] | float) -> NDArray[float] | float: """Water formation volume factor (B-factor) from McCain. Parameters ---------- pressure: np.ndarray water pressure in psia Returns ------- b_w: ndarray b-factor in reservoir bbl / standard bbl """ b_w = np.array([b_water_McCain(self.temperature, p) for p in pressure]) return b_w
[docs] def water_viscosity(self, pressure: NDArray | float): """Water viscosity from McCain (1991). Parameters ---------- pressure: ndarray water pressure in psia Returns ------- mu_w : float viscosity in centipoise """ return viscosity_water_McCain(self.temperature, pressure, self.salinity)
[docs] def gas_FVF( self, pressure: NDArray | float, temperature_pseudocritical: float, pressure_pseudocritical: float, ) -> NDArray | float: """Gas formation volume factor (Bg) from Dranchuk and Abou-Kassem (1975). Parameters ---------- pressure : ndarray reservoir pressure in psia temperature_pseudocritical : float pseudocritical temperature in Fahrenheit. pressure_pseudocritical : float pseudocritical pressure in psia Returns ------- b_g: ndarray b-factor (reservoir barrels / scf) """ b_g = np.array( [ b_factor_DAK( self.temperature, p, temperature_pseudocritical, pressure_pseudocritical, ) for p in pressure ] ) return b_g
[docs] def gas_viscosity( self, pressure: NDArray | float, temperature_pseudocritical: float, pressure_pseudocritical: float, ) -> NDArray | float: """Calculate the viscosity for gas using Sutton's Fudamental PVT Calculations (2007). Parameters ---------- pressure : float reservoir pressure in psia temperature_pseudocritical : float pseudocritical temperature in Fahrenheit. pressure_pseudocritical : float pseudocritical pressure in psia Returns ------- mu_g : ndarray viscosity_gas (centipoise) Examples -------- >>> Fluid(400, 35, 0.65, 0).viscosity_gas(100, -102, 649) 0.01652719692109309 """ viscosity = np.array( [ viscosity_Sutton( self.temperature, p, temperature_pseudocritical, pressure_pseudocritical, self.gas_specific_gravity, ) for p in pressure ] ) return viscosity
[docs] def oil_FVF(self, pressure: NDArray | float) -> NDArray | float: """Calculate the oil formation volume factor (Bo) using Standing. Parameters ---------- pressure : float reservoir pressure (psia) Returns ------- b_o : ndarray :math:`b_o`, the formation volume factor (V/V) """ fvf_oil = b_o_Standing( self.temperature, pressure, self.api_gravity, self.gas_specific_gravity, self.solution_gor_initial, ) return fvf_oil
[docs] def oil_viscosity(self, pressure: NDArray | float) -> NDArray | float: r"""Calculate the oil viscosity using Beggs-Robinson. Returns ------- mu_o: ndarray :math:`\mu_o`, the oil viscosity """ visc_br = np.vectorize(viscosity_beggs_robinson) mu_o = visc_br( self.temperature, pressure, self.api_gravity, self.gas_specific_gravity, self.solution_gor_initial, ) return mu_o
[docs] def pressure_bubblepoint( self, ) -> float: """Calculate the bubble point pressure using Standing. Returns ------- p_bubble : float Bubble point pressure in psia. Examples -------- >>> Fluid(200, 35, 0.8, 650).pressure_bubblepoint() 2627.2017021875276 """ p_bubble = pressure_bubblepoint_Standing( self.temperature, self.api_gravity, self.gas_specific_gravity, self.solution_gor_initial, ) return p_bubble
[docs] def pseudopressure( pressure: NDArray[np.float64], viscosity: NDArray[np.float64], z_factor: NDArray[np.float64], ) -> NDArray[np.float64]: """Calculate the pseudopressure using Al-Hussainy's relation. Parameters ---------- pressure: NDArray, psi viscosity: NDArray, cp z_factor: NDArray, dimensionless Returns ------- m : NDArray pseudopressure, psia^2 / cp """ pp = 2 * pressure / (viscosity * z_factor) return sp.integrate.cumulative_trapezoid(pp, pressure, initial=0.0)