Explorando datos de pozos petroleros mediante Welly

Una librería de Python que permite explorar archivos LAS de registros de pozos

David Castro
6 min readJul 17, 2023
Photo by Zbynek Burival on Unsplash

Recientemente, tuve la oportunidad de comenzar a trabajar en una empresa de consultoría en geomecánica, entre otros temas, para empresas de la industria del petróleo. Aunque mi misión inicial es aportar mi experiencia en el desarrollo de software, el contacto con esta temática y el análisis de datos relacionados con esta industria ha tenido un impacto directo en mi trabajo. He tenido que familiarizarme con nuevos términos y conceptos, adquiriendo un sin número de conocimientos nuevos.

En este nuevo camino he encontrado tecnologías que vale la pena revisar y detallar y una de estas es la librería Welly. Welly es una librería diseñada específicamente para el manejo y análisis de registros de pozos en la industria del petróleo y gas. Con Welly, puedo cargar y procesar datos de registros de pozos en formatos comunes como LAS (Log ASCII Standard) de manera eficiente y flexible. Esta librería me permite realizar una variedad de operaciones, desde el filtrado y cálculo de propiedades hasta la visualización de gráficos y curvas de registros.

La capacidad de Welly para acceder y manipular datos geomecánicos es una verdadera ventaja porque con unos pocos pasos de código, puedo explorar y analizar rápidamente los registros de pozos, identificar tendencias clave y descubrir patrones ocultos que ayudan a tomar decisiones informadas en la planificación y ejecución de proyectos geomecánicos.

En este breve tutorial vamos a explorar los usos de la librería Welly y sus diferentes capacidades.

👨🏼‍💻 Explorando los datos

Para iniciar con el análisis de los datos vamos a importar las librerías necesarias y a cargar nuestro archivo .las con la información de registros del pozo.

El archivo que comparto a continuación esta disponible en la documentación oficial de Welly.

import welly
# Cargar el archivo LAS
well = welly.Well.from_las('<https://geocomp.s3.amazonaws.com/data/P-129.LAS>')
well

🛢️ Información de archivos y pozos

Ahora que nuestros datos se han cargado, podemos comenzar a explorar los contenidos y los metadatos del pozo seleccionado. Si llamamos a nuestro well, se nos presentará una tabla de resumen que contiene el nombre del pozo, la ubicación, las coordenadas y una lista de mnemónicos de curvas.

well

También podemos recurrir a funciones específicas para acceder a cierta información . Por ejemplo, header devolverá información clave del encabezado, incluido el nombre del pozo, el Identificador único del pozo (UWI), el nombre del campo y el nombre de la empresa.

well.header

Ahora echemos un vistazo a la información de ubicación de este pozo. Para hacerlo, podemos llamar al locationmétodo para nuestro objeto de datos.

well.location
Location({'position': None, 'crs': CRS({}), 'location': 'Lat = 45* 12\\' 34.237" N', 'country': 'CA', 'province': 'Nova Scotia', 'latitude': '', 'longitude': '', 'datum': '', 'section': '45.20 Deg N', 'range': 'PD 176', 'township': '63.75 Deg W', 'ekb': 94.8, 'egl': 90.3, 'gl': 90.3, 'tdd': 1935.0, 'tdl': 1935.0, 'td': None, 'deviation': None})

O una lista con las keys disponibles dentro del archivo

well.data.keys()
dict_keys(['CALI', 'HCAL', 'PEF', 'DT', 'DTS', 'DPHI_SAN', 'DPHI_LIM', 'DPHI_DOL', 'NPHI_SAN', 'NPHI_LIM', 'NPHI_DOL', 'RLA5', 'RLA3', 'RLA4', 'RLA1', 'RLA2', 'RXOZ', 'RXO_HRLT', 'RT_HRLT', 'RM_HRLT', 'DRHO', 'RHOB', 'GR', 'SP'])

O incluso la cantidad total de curvas que tenemos dentro de la archivo del pozo

well.count_curves()

O información mas completa respecto a cada una de las variables con las cuales contamos en el dataset.

well.data
{'CALI': Curve(mnemonic=CALI, units=in, start=1.0668, stop=1939.1376, step=0.1524, count=[12718]),
'HCAL': Curve(mnemonic=HCAL, units=in, start=1.0668, stop=1939.1376, step=0.1524, count=[2139]),
'PEF': Curve(mnemonic=PEF, units=, start=1.0668, stop=1939.1376, step=0.1524, count=[12718]),
'DT': Curve(mnemonic=DT, units=us/ft, start=1.0668, stop=1939.1376, step=0.1524, count=[10850]),
'DTS': Curve(mnemonic=DTS, units=us/ft, start=1.0668, stop=1939.1376, step=0.1524, count=[10850]),
'DPHI_SAN': Curve(mnemonic=DPHI_SAN, units=m3/m3, start=1.0668, stop=1939.1376, step=0.1524, count=[12718]),
'DPHI_LIM': Curve(mnemonic=DPHI_LIM, units=m3/m3, start=1.0668, stop=1939.1376, step=0.1524, count=[12718]),
'DPHI_DOL': Curve(mnemonic=DPHI_DOL, units=m3/m3, start=1.0668, stop=1939.1376, step=0.1524, count=[12718]),
'NPHI_SAN': Curve(mnemonic=NPHI_SAN, units=m3/m3, start=1.0668, stop=1939.1376, step=0.1524, count=[12718]),
'NPHI_LIM': Curve(mnemonic=NPHI_LIM, units=m3/m3, start=1.0668, stop=1939.1376, step=0.1524, count=[12718]),
'NPHI_DOL': Curve(mnemonic=NPHI_DOL, units=m3/m3, start=1.0668, stop=1939.1376, step=0.1524, count=[12718]),
'RLA5': Curve(mnemonic=RLA5, units=ohm.m, start=1.0668, stop=1939.1376, step=0.1524, count=[12718]),
'RLA3': Curve(mnemonic=RLA3, units=ohm.m, start=1.0668, stop=1939.1376, step=0.1524, count=[12718]),
'RLA4': Curve(mnemonic=RLA4, units=ohm.m, start=1.0668, stop=1939.1376, step=0.1524, count=[12718]),
'RLA1': Curve(mnemonic=RLA1, units=ohm.m, start=1.0668, stop=1939.1376, step=0.1524, count=[12718]),
'RLA2': Curve(mnemonic=RLA2, units=ohm.m, start=1.0668, stop=1939.1376, step=0.1524, count=[12718]),
'RXOZ': Curve(mnemonic=RXOZ, units=ohm.m, start=1.0668, stop=1939.1376, step=0.1524, count=[12718]),
'RXO_HRLT': Curve(mnemonic=RXO_HRLT, units=ohm.m, start=1.0668, stop=1939.1376, step=0.1524, count=[2139]),
'RT_HRLT': Curve(mnemonic=RT_HRLT, units=ohm.m, start=1.0668, stop=1939.1376, step=0.1524, count=[12718]),
'RM_HRLT': Curve(mnemonic=RM_HRLT, units=ohm.m, start=1.0668, stop=1939.1376, step=0.1524, count=[12718]),
'DRHO': Curve(mnemonic=DRHO, units=g/cm3, start=1.0668, stop=1939.1376, step=0.1524, count=[12718]),
'RHOB': Curve(mnemonic=RHOB, units=g/cm3, start=1.0668, stop=1939.1376, step=0.1524, count=[12707]),
'GR': Curve(mnemonic=GR, units=gAPI, start=1.0668, stop=1939.1376, step=0.1524, count=[12718]),
'SP': Curve(mnemonic=SP, units=mV, start=1.0668, stop=1939.1376, step=0.1524, count=[12718])}

📈 Visualización de los datos

La visualización de datos de registros de pozos es parte importante del analisis de datos de registros de pozos, y los gráficos de registros son uno de los formatos de visualización más comunes. La biblioteca de welly permite la generación rápida y sencilla de gráficos de registros de pozos. Es tan sencillo como realizar la siguiente instrucción

well.plot(tracks=['GR', 'DTCO', 'IV', 'DTSM', 'RHOB', 'CAL', 'VSH'])

✅ Calidad de los datos

Tal vez una de las partes que más me llamo la atención de esta librería fue esta. Welly cuenta con un módulo que nos permite hacerle revisión de calidad a nuestros datos.

En ocasiones, el entorno en el que se encuentra un pozo puede ser extremadamente desafiante, con condiciones adversas como altas temperaturas, altas presiones y geometría irregular del pozo. Estos factores pueden tener un impacto significativo en las mediciones de registro realizadas. Como resultado, pueden surgir una variedad de problemas que afectan la calidad de los datos, como valores perdidos, valores atípicos, valores constantes o valores erróneos. Es esencial abordar estos problemas de manera rigurosa y aplicar técnicas de control de calidad para garantizar la fiabilidad y precisión de los datos obtenidos.

La biblioteca Welly viene con una serie de comprobaciones de control de calidad que nos permitirán comprobar todos los datos o curvas específicas en busca de problemas.

Los controles de calidad incluyen:

  • Comprobación de valores faltantes:.no_nans(curve)
  • Comprobación si toda la curva está vacía o no:.not_empty(curve)
  • Comprobación si la curva contiene valores constantes:.no_flat(curve)
  • Comprobación si los valores son todos positivos:all_positive(curve)
  • Comprobación si la curva está dentro de un rango:all_between(min_value, max_value)

Antes de comenzar, necesitaremos importar el módulo de calidad de la siguiente manera:

import welly.quality as wq

Antes de ejecutar cualquier control de calidad, primero debemos crear una lista de las pruebas que queremos ejecutar y sobre qué datos queremos ejecutar esas pruebas. Para hacer esto, podemos construir un diccionario, siendo la clave la(s) curva(s) en las que queremos ejecutar las comprobaciones. Si queremos ejecutarlo en todas las curvas necesitamos usar la clave Each. Para cada curva, verificaremos si hay valores constantes, espacios y nos aseguraremos de que la curva no esté vacía. Para las curvas de rayos gamma (GR), por ejemplo, vamos a verificar que todos los valores sean positivos, que estén entre rangos estándar y que las unidades sean las que esperamos que sean.

tests = {'Each': [wq.no_flat,
wq.no_gaps,
wq.not_empty],
'GR': [
wq.all_positive,
wq.all_between(0, 250),
wq.check_units(['API', 'GAPI']),
],
}

Después de ejecutar las pruebas, podemos ver que tenemos una tabla HTML coloreada devuelta.

from IPython.display import HTML

quality_table = well.qc_table_html(tests)
HTML(quality_table)

Si llegaste hasta el final, gracias por leerme, te invito a seguirme y a leer mis otros posts en mi perfil. Normalmente escribo sobre tecnología, programación y Python y actualmente por mi trabajo este es un nuevo tema sobre el cual voy a empezar a escribir.

Si quieres ver un Jupyter Notebook con todos los comandos que he explorado te invito al siguiente repositorio de GitHub:

🤓 Bibliografía

--

--