Ecuación de balance de materia: ejemplo 1, parte 1

Ejemplo de cálculo usando la ecuación de balance de materia con un modelo sin capa de gas ni acuífero. Método de Diagnóstico de Havlena-Odeh y Gráfico de Campbell


Author

Affiliation

Rigoberto Chandomi

 

Published

March 30, 2025

DOI


Un yacimiento con una presión inicial de 5200 psi y temperatura de 123 °F tiene las siguientes propiedades:

Porosidad = 0.2

Swc=0.15

cf=3.50×106 psi1

cw=7.00×106 psi1

API = 30°

GOR = 800 scf/stb

gg=0.8


📌 Carga de Datos de Producción y Propiedades de Fluidos

import pandas as pd

# Cargar datos del archivo CSV
file_path = "MB_ex.csv"
df = pd.read_csv(file_path)
print(df)   
          Date     Pavg     Np     Gp     Bo     Rs     Bg     Rp
0   01/01/2003  5200.00   0.00   0.00  1.376  0.800  0.545  0.800
1   02/04/2003  3940.89   3.89   3.11  1.389  0.800  0.613  0.800
2   02/07/2003  3512.38   6.07   4.85  1.364  0.747  0.651  0.800
3   01/10/2003  3322.18   7.74   6.17  1.336  0.696  0.673  0.798
4   31/12/2003  3178.82   9.22   7.51  1.316  0.658  0.692  0.814
5   31/03/2004  3058.58  10.57   8.98  1.299  0.627  0.709  0.850
6   30/06/2004  2952.10  11.79  10.63  1.285  0.600  0.727  0.901
7   29/09/2004  2854.95  12.90  12.47  1.272  0.576  0.745  0.966
8   29/12/2004  2764.87  13.91  14.50  1.261  0.554  0.763  1.042
9   30/03/2005  2680.77  14.83  16.70  1.250  0.533  0.781  1.126
10  29/06/2005  2602.16  15.65  19.04  1.241  0.515  0.800  1.216
11  28/09/2005  2528.94  16.39  21.47  1.232  0.497  0.820  1.310
12  28/12/2005  2461.18  17.05  23.94  1.224  0.482  0.839  1.404
13  29/03/2006  2399.03  17.62  26.40  1.216  0.467  0.858  1.498
14  28/06/2006  2342.59  18.13  28.79  1.210  0.455  0.877  1.588
15  27/09/2006  2291.91  18.56  31.07  1.204  0.443  0.895  1.674
16  27/12/2006  2246.93  18.94  33.20  1.199  0.433  0.912  1.753
17  28/03/2007  2207.46  19.26  35.14  1.195  0.424  0.928  1.825
18  27/06/2007  2173.21  19.53  36.89  1.191  0.417  0.943  1.889
19  26/09/2007  2143.79  19.76  38.44  1.188  0.410  0.956  1.945
20  26/12/2007  2118.77  19.95  39.79  1.185  0.405  0.967  1.994
21  26/03/2008  2097.66  20.11  40.95  1.183  0.400  0.977  2.037
22  25/06/2008  2080.00  20.24  41.94  1.181  0.397  0.986  2.072
23  24/09/2008  2065.32  20.35  42.77  1.179  0.393  0.993  2.102
24  24/12/2008  2053.19  20.43  43.47  1.178  0.391  0.999  2.127
25  25/03/2009  2043.21  20.51  44.05  1.177  0.389  1.004  2.148
26  24/06/2009  2035.03  20.56  44.52  1.176  0.387  1.008  2.165
27  23/09/2009  2028.36  20.61  44.91  1.176  0.385  1.012  2.179
28  23/12/2009  2022.92  20.65  45.23  1.175  0.384  1.015  2.190
29  24/03/2010  2018.51  20.68  45.50  1.174  0.383  1.017  2.200
30  23/06/2010  2014.94  20.71  45.80  1.174  0.383  1.019  2.212

📌 Ecuaciones del Balance de Materia

Las siguientes ecuaciones nos permiten crear los gráficos de diagnóstico de Campbell y Havlena-Odeh.

F=NEt

Donde:

F=Np(Bo+(RpRs)Bg)

Y:

Et=Boi[(BoBoi)+(RsiRs)Boi+m(BgBgi1)+(1+m)(cwSwc+cf1SwcΔp)]

Reordenando la ecuación:

FEt=N

En el caso del gráfico de Campbell, el yacimiento es volumétrico, por lo que:

We=0

El término independiente es el petróleo producido Np, mientras que el primer término es el petróleo original en sitio N.


📌 Reorganización de la Ecuación de Havlena-Odeh

En los métodos de diagnóstico de Havlena-Odeh, la ecuación (1) se reorganiza de la siguiente forma:

FWe=NEt

Por lo tanto, un gráfico de (FWe) contra Et producirá una línea recta que pasa por el origen, cuya pendiente representa el petróleo original en sitio N.


📌 Cálculo de F y Et en Python

import numpy as np

# Si el archivo fue cargado correctamente
# Extraer columnas relevantes
Np = df["Np"]  # Producción acumulada de petróleo (MMSTB)
Bo = df["Bo"]  # Factor de volumen de formación del petróleo
Bg = df["Bg"]  # Factor de volumen de formación del gas
Rp = df["Rp"]  # Razón gas-petróleo producida
Rs = df["Rs"]  # Razón gas disuelto
Pavg = df["Pavg"]  # Razón gas disuelto

#Parámetros iniciales
Sw = 0.15
cf = 0.0000035
cw = 0.000007
Boi = Bo.iloc[0]  # Bo inicial
Rsi = Rs.iloc[0]  # Rs inicial
Pi =  Pavg.iloc[0]  # Bg inicial

# Cálculo de F y Et
F = Np * (Bo + (Rp - Rs) * Bg)
Et = Boi*(((Bo-Boi)+(Rsi-Rs)*Bg)/Boi+(Pi-Pavg)*(cw*Sw+cf)/(1-Sw))

# Agregar los resultados al dataframe
df["F"] = F
df["Et"] = Et
df["F_Et"] = F/Et

# Mostrar los resultados calculados
print(df)
          Date     Pavg     Np     Gp  ...     Rp          F        Et        F_Et
0   01/01/2003  5200.00   0.00   0.00  ...  0.800   0.000000  0.000000         NaN
1   02/04/2003  3940.89   3.89   3.11  ...  0.800   5.403210  0.022274  242.577499
2   02/07/2003  3512.38   6.07   4.85  ...  0.800   8.488913  0.034933  243.002685
3   01/10/2003  3322.18   7.74   6.17  ...  0.798  10.871960  0.043823  248.085957
4   31/12/2003  3178.82   9.22   7.51  ...  0.814  13.128837  0.053151  247.008781
5   31/03/2004  3058.58  10.57   8.98  ...  0.850  15.401621  0.061430  250.718461
6   30/06/2004  2952.10  11.79  10.63  ...  0.901  17.730120  0.070957  249.870497
7   29/09/2004  2854.95  12.90  12.47  ...  0.966  20.156895  0.080153  251.480826
8   29/12/2004  2764.87  13.91  14.50  ...  1.042  22.719815  0.090634  250.675660
9   30/03/2005  2680.77  14.83  16.70  ...  1.126  25.405762  0.101083  251.336258
10  29/06/2005  2602.16  15.65  19.04  ...  1.216  28.198170  0.112135  251.466778
11  28/09/2005  2528.94  16.39  21.47  ...  1.310  31.119037  0.124134  250.688901
12  28/12/2005  2461.18  17.05  23.94  ...  1.404  34.058364  0.134975  252.330566
13  29/03/2006  2399.03  17.62  26.40  ...  1.498  37.012537  0.146345  252.912964
14  28/06/2006  2342.59  18.13  28.79  ...  1.588  39.952011  0.157612  253.483834
15  27/09/2006  2291.91  18.56  31.07  ...  1.674  42.794627  0.168935  253.320130
16  27/12/2006  2246.93  18.94  33.20  ...  1.753  45.509790  0.179455  253.599625
17  28/03/2007  2207.46  19.26  35.14  ...  1.825  48.056165  0.189970  252.967137
18  27/06/2007  2173.21  19.53  36.89  ...  1.889  50.369745  0.198463  253.798830
19  26/09/2007  2143.79  19.76  38.44  ...  1.945  52.471890  0.207351  253.058334
20  26/12/2007  2118.77  19.95  39.79  ...  1.994  54.295182  0.213660  254.119244
21  26/03/2008  2097.66  20.11  40.95  ...  2.037  55.953038  0.220651  253.581919
22  25/06/2008  2080.00  20.24  41.94  ...  2.072  57.330812  0.225339  254.420487
23  24/09/2008  2065.32  20.35  42.77  ...  2.102  58.527353  0.230240  254.201557
24  24/12/2008  2053.19  20.43  43.47  ...  2.127  59.497554  0.233769  254.513983
25  25/03/2009  2043.21  20.51  44.05  ...  2.148  60.361668  0.236896  254.802610
26  24/06/2009  2035.03  20.56  44.52  ...  2.165  61.026685  0.239616  254.685297
27  23/09/2009  2028.36  20.61  44.91  ...  2.179  61.655392  0.243341  253.370152
28  23/12/2009  2022.92  20.65  45.23  ...  2.190  62.117058  0.244641  253.910812
29  24/03/2010  2018.51  20.68  45.50  ...  2.200  62.492665  0.245523  254.529036
30  23/06/2010  2014.94  20.71  45.80  ...  2.212  62.911823  0.246383  255.341546

[31 rows x 11 columns]

📊 Gráfico de Campbell

El gráfico de Campbell muestra una línea horizontal con una intersección en 250 MMSTB, lo que indica un volumen original de petróleo en sitio aproximado de 250 MMSTB OOIP.

import matplotlib.pyplot as plt

# Gráfico de Np vs F_ET
plt.figure(figsize=(8,5))
plt.scatter(df["Np"], df["F_Et"], color='blue', label="Datos")

# Calcular la regresión lineal solo con datos limpios
df_clean = df.dropna(subset=["F_Et"])
coeficientes = np.polyfit(df_clean["Np"], df_clean["F_Et"], 1)  # Ajuste lineal (grado 1)
polinomio = np.poly1d(coeficientes)  # Crear función polinómica

# Evaluar la línea de ajuste en los valores de Np limpios
ajuste_y = polinomio(df_clean["Np"])

# Graficar la línea de ajuste
plt.plot(df_clean["Np"], ajuste_y, 'r--', label=f"Ajuste lineal: y = {coeficientes[0]:.2f}x + {coeficientes[1]:.2f}")

# Agregar línea horizontal en y=250 (similar a geom_hline)
plt.axhline(y=250, color='black', linestyle='-', linewidth=1)

# Agregar texto en (x=2, y=270) (similar a geom_text)
plt.text(5, 270, "N = 250 MMSTB", fontsize=12, color="black")

plt.xlabel("Np")
plt.ylabel("F_Et")
plt.ylim(-0, 400)
(0.0, 400.0)
plt.title("Gráfico de Campbell ")
plt.legend()
plt.grid()
plt.show()

📊 Gráfico de Havlena-Odeh

El gráfico de Havlena-Odeh muestra una línea recta con una pendiente de 255.67, lo que indica un volumen original de petróleo en sitio de aproximadamente 256 MMSTB OOIP.

# Gráfico de Et vs F
plt.figure(figsize=(8,5))
plt.scatter(df["Et"], df["F"], color='blue', label="Datos")

# Calcular la regresión lineal 
coeficientes = np.polyfit(df["Et"], df["F"], 1)  # Ajuste lineal (grado 1)
polinomio = np.poly1d(coeficientes)  # Crear función polinómica

# Evaluar la línea de ajuste en los valores de Np limpios
ajuste_y = polinomio(df["Et"])

# Graficar la línea de ajuste
plt.plot(df["Et"], ajuste_y, 'r--', label=f"Ajuste lineal: y = {coeficientes[0]:.2f}x + {coeficientes[1]:.2f}")

plt.xlabel("Et")
plt.ylabel("F")
plt.ylim(-0, 70)
(0.0, 70.0)
plt.title("Havlena–Odeh")
plt.legend()
plt.grid()
plt.show()

📈 Interpretación de los Gráficos

1️⃣ Si la relación F vs E_t es lineal, el yacimiento sigue un comportamiento volumétrico sin empuje de agua.
2️⃣ Si hay desviaciones, puede indicar presencia de empuje de agua o un comportamiento no volumétrico.
3️⃣ El valor de la pendiente de la recta es el volumen original de petróleo en sitio (N).

Este análisis es clave para estimar reservas y tomar decisiones de producción. 🚀