Interpretación automatizada de registros de pozos
Aplicación de evaluación petrofísica determinística a partir de archivos LAS, con soporte multiidioma (Español, English, Français).
🌐 Disponible en: aiwelllog.streamlit.app
- Lectura de LAS desde memoria, sin archivos temporales
- Saneamiento de valores nulos no declarados en la cabecera (
-9999,-999.25, ...) - Lectura de la unidad de profundidad declarada y conversión de metros a pies
- Detección automática de escalas erróneas: NPHI en unidades de porosidad, RHOB en kg/m³, DT en µs/m, CALI en mm
- Validación por rangos físicos de cada curva
- Control de calidad de hoyo: descarta la densidad donde el calibre indica derrumbe, y señala la costra de lodo (
CALI < BS) como indicio de permeabilidad - Mapeo flexible de curvas, incluidas las familias de resistividad AIT-H, AIT, laterolog e inducción
- Volumen de arcilla: Larionov (terciario y preterciario), lineal, Clavier, Stieber, triángulo neutrón-densidad y CGR sin uranio
- Porosidad: leída sobre la línea de matriz del crossplot neutrón-densidad (carta CP-1), con matriz de interpretación seleccionable por el usuario
- Saturación de agua: Archie, Simandoux e Indonesia (Poupon-Leveaux)
- Permeabilidad: Kozeny-Carman, Timur y Coates, con calibración de la constante contra datos de núcleo
- Litología: clasificación por densidad de matriz aparente y PEF
- Evaluación composicional: volúmenes minerales resolviendo
LOG = Σ Vmin·LOGmin + Φ·LOGfl - Net to Gross: jerarquía Gross → Net Rock → Reservorio → Net Pay, en espesor real
- Registro de 8 tracks con escalas según la matriz y ventana de profundidad ajustable
- Crossplots neutrón-densidad, RHOMAA–UMAA y M–N
- Perfil apilado de volúmenes minerales
- Exportación a PDF, Excel y CSV, individual y consolidada por lotes
- Procesamiento simultáneo de varios pozos con referencia de GR común al lote
La versión de referencia es Python 3.11: es sobre la que está verificado el conjunto de dependencias que documenta la cabecera de requirements.txt. En versiones más nuevas la resolución puede no ser la misma.
git clone https://github.com/ErickFMR777/AI_Well_Log.git
cd AI_Well_Log
pip install -r requirements.txtSi tu Python del sistema no es 3.11, la vía más limpia es uv, que instala un intérprete propio y lo aísla en un entorno virtual sin tocar el del sistema ni requerir permisos de administrador:
uv python install 3.11
uv venv --python 3.11
uv pip install -r requirements.txtstreamlit run app/main.pySe abre en http://localhost:8501.
Si usaste el entorno de
uv, actívalo antes (.venv\Scripts\activateen Windows,source .venv/bin/activateen Linux y macOS).Si el puerto 8501 está ocupado —lo reserva el devcontainer aunque no esté sirviendo—, añade
--server.port 8502.
El comando debe ejecutarse desde la raíz del repositorio: Streamlit añade el directorio del script (
app/) alsys.path, que es lo que permite resolvermodules.*.
También hay un devcontainer listo (.devcontainer/) que instala las dependencias y arranca la app automáticamente.
app/
main.py Interfaz: i18n, estilos, panel de control y renderizado
modules/
petrofisica.py Constantes, WellConfig, saneamiento y calculadores
las_io.py Lectura de LAS, unidades, nulos y mapeo de curvas
pipeline.py Orquestación del cálculo de un pozo
plotting.py Registro de 8 tracks y crossplots
pdf_export.py Reporte PDF individual y bloques reutilizables
pdf_batch_export.py Reporte PDF consolidado
.streamlit/config.toml Tema y ajustes de servidor
requirements.txt Dependencias con topes de versión mayor
CLAUDE.md Notas de arquitectura e invariantes del cálculo
El flujo es las_io.load_well → pipeline.process_loaded_well → renderizado y exportación. Toda la petrofísica vive en los módulos; main.py solo orquesta y presenta.
No hay redes neuronales, ni clustering, ni clasificadores entrenados. No existe scikit-learn, TensorFlow ni PyTorch en las dependencias, ni una sola llamada a fit, predict o train en el código.
Todo resultado sale de ecuaciones petrofísicas publicadas, evaluadas sobre las curvas del archivo. Esto es una ventaja, no una carencia: cada número es trazable hasta la fórmula que lo produjo y el parámetro que se le pasó, se reproduce exactamente con los mismos datos de entrada, y no depende de un conjunto de entrenamiento ajeno a tu cuenca. Un modelo entrenado no ofrece ninguna de las tres cosas.
Lo que sí se usa, y conviene nombrar con propiedad:
| Técnica | Dónde | Para qué |
|---|---|---|
| Ecuaciones deterministas | Archie, Simandoux, Indonesia, Larionov, Clavier, Stieber, Kozeny-Carman, Timur, Coates | Saturación, arcillosidad, permeabilidad |
| Estadística robusta | gr_reference (P2/P98), modal_matrix (moda), estimate_shale_point (P90), estimate_swirr (P10) |
Referencias tomadas del propio pozo, resistentes a picos de herramienta |
| Filtro de mediana | smooth_curve |
Suavizado que respeta los vacíos de la curva |
| Inversión lineal | solve_composicional |
Sistema 4×4 resuelto por profundidad para obtener volúmenes minerales |
| Geometría baricéntrica | calc_vsh_neutron_density |
Vsh por el triángulo neutrón-densidad |
| Clasificación por umbrales físicos | classify_lithology |
Litología según PEF y densidad de matriz aparente |
| Regresión por mínimos cuadrados | calibrate_kozeny |
Único ajuste contra datos: la constante de Kozeny frente a núcleos, en espacio logarítmico |
calibrate_kozeny es lo más cercano a "aprender de los datos" que hay en la app, y es una regresión de un solo parámetro contra mediciones de núcleo que aportas tú.
Las curvas no se leen por posición sino por nombre, y no se dibujan en crudo. Entre el archivo y el píxel hay seis pasos, en este orden exacto (las_io.load_well):
- Saneamiento de nulos — se anulan los centinelas que
lasiono reconoce. La librería solo honra elNULLdeclarado en la cabecera, así que un archivo que mezcle-999.25declarado con-9999sin declarar entregaría el segundo como dato válido y contaminaría cada promedio y percentil posterior. - Profundidad y su unidad real — se lee la unidad declarada en
~Curveo~Welly se convierte a pies, que es la unidad canónica interna. Un archivo métrico tratado como pies arrastra un error de factor 3,28 a espesores, ejes y tablas. - Mapeo de alias a nombres estándar —
CURVE_ALIASEStraduce más de 40 mnemónicos. La resistividad profunda puede llamarseRT,ILD,LLD,AHT90,AT90,RLA5… y si su familia falta, el pozo se queda sin saturación en silencio. - Corrección de escalas — se detectan por mediana los desfases de orden de magnitud: NPHI en unidades de porosidad, RHOB en kg/m³, DT en µs/m, CALI en mm.
- Rangos físicos — fuera del rango admisible el valor pasa a nulo, no a un número plausible.
- Control de calidad de hoyo — donde el calibre indica derrumbe se anulan RHOB, NPHI y PEF. La herramienta de densidad es de patín: en un hoyo derrumbado mide lodo, no roca.
Solo entonces se dibuja. Lo que ves en el registro es la curva saneada, no la del archivo, y por eso la app muestra en el Paso 1 qué correcciones aplicó.
Los 8 tracks:
| # | Track | Escala | Detalle |
|---|---|---|---|
| 1 | GR | 0 a máx(150, P99) API | Relleno amarillo por debajo de 75 API |
| 2 | RHOB | Según matriz, eje invertido | 2,95→1,95 en caliza · 2,65→1,65 en arenisca |
| 3 | NPHI | Según matriz, eje invertido | 0,45→−0,15 en caliza · 0,60→0,00 en arenisca |
| 4 | Resistividad | Logarítmica | RT, RM, RXOS, RMC y RMUD con estilo propio |
| 5 | PHI_E | v/v | Sombreado según el cutoff de porosidad |
| 6 | VSH | v/v | Sombreado según el cutoff de arcillosidad |
| 7 | Net Pay | Bandera | Distingue "no cumple" de "no evaluable" |
| 8 | Litología | Color | Paleta única en LITHO_COLORS |
Las escalas de RHOB y NPHI dependen de la matriz (ESCALAS_MATRIZ). Presentar un neutrón en unidades caliza sobre la escala de arenisca desplaza visualmente el cruce densidad-neutrón y hace que un intervalo de gas parezca agua, o al revés.
Una misma función, build_8track_figure, genera la figura de la pantalla y la de los dos PDF, y lee sus cutoffs de la configuración del pozo. No hay una segunda implementación que pueda desincronizarse.
Hay una circularidad que resolver: la litología necesita la porosidad para calcular la densidad de matriz aparente, y la porosidad necesita la densidad de matriz que determina la litología. Se resuelve iterando dos veces:
- Matriz provisional a partir de las medianas de PEF y RHOB, solo para obtener una porosidad con la que clasificar.
- Clasificación punto a punto por densidad de matriz aparente
ρma_ap = (ρb − φ·ρfl)/(1 − φ)combinada con PEF, cuyos umbrales son constantes físicas: arenisca 1,81 · dolomita 3,14 · anhidrita 5,05 · caliza 5,08 barns/electrón. - Matriz definitiva por la MODA de esa clasificación, no por la mediana de PEF. En una secuencia mixta la distribución de PEF es bimodal y su mediana cae entre los dos modos, eligiendo una roca que casi no existe en el pozo. Contar puntos evita ese artefacto.
- Porosidad final con la densidad de matriz y la calibración del neutrón que corresponden a la litología de cada profundidad.
La porosidad neutrón se lee sobre la línea de matriz del crossplot densidad-neutrón (carta CP-1), resolviendo φN = (NPHI − N_ma)/(N_fl − N_ma). Un neutrón grabado en unidades caliza y leído en una arenisca subestima la porosidad unas 4 unidades; en una dolomita la sobreestima unas 7.
En paralelo, la evaluación composicional resuelve en cada profundidad
RHOB = Σ Vi·RHOBi + Φ·RHOBfl
NPHI = Σ Vi·NPHIi + Φ·NPHIfl
U = Σ Vi·Ui + Φ·Ufl
1 = Σ Vi + Φ
y devuelve volúmenes minerales en lugar de una etiqueta: una arena arcillosa sale como 0,70 de cuarzo + 0,15 de arcilla + 0,15 de porosidad, no como "ARENISCA_ARCILLOSA". El fotoeléctrico entra como U y no como PEF, porque el PEF es una sección transversal por electrón y no se mezcla linealmente por volumen. La app informa cuánto volumen hubo que mover para que la solución fuera admisible: por encima de ~0,15 v/v el modelo no describe la formación, y lo dice.
Los crossplots N-D, RHOMAA–UMAA y M–N permiten verificar visualmente esa identificación mineralógica.
Cuatro niveles, cada uno subconjunto estricto del anterior, de modo que las relaciones N/G son acumulativas y comparables entre pozos:
GROSS intervalo completo
NET ROCK + Vsh <= cutoff descarta la arcilla
RESERVORIO + PHI_E >= cutoff con capacidad de almacenar
NET PAY + Sw <= cutoff con interés económico
Los tres cutoffs son ajustables desde el panel lateral (por defecto 6 % de porosidad, 50 % de Vsh y 70 % de Sw).
Lo que distingue este cálculo de uno ingenuo es que hay tres estados, no dos. Una muestra puede cumplir, no cumplir, o no ser evaluable porque le falta el dato para decidir. Las no evaluables quedan fuera del numerador y del denominador. Por eso se reportan dos porcentajes: pct_sobre_evaluable y pct_sobre_total. Rellenar los faltantes con ceros convertiría "sin dato" en un "no" definitivo y diluiría el net pay sobre tramos que ni siquiera tienen registro.
El resultado se da en espesor real (pies), obtenido del paso de muestreo mediano. Un conteo de muestras no significa nada sin él.
Cada decisión automática —matriz dominante, parámetros de Archie, constante de Kozeny, referencia de GR, matriz del neutrón— se registra con su procedencia y se muestra en la interfaz y en el PDF, de modo que lees los valores realmente usados, no los que muestran los deslizadores.
Algunas decisiones del cálculo que conviene conocer al leer los resultados:
- La corrección de arcilla se aplica una sola vez, de forma simétrica sobre densidad y neutrón.
- La porosidad total alimenta la ecuación de saturación; la efectiva alimenta el net pay y la permeabilidad.
- La matriz de interpretación se toma por defecto de la litología dominante del pozo, determinada por la moda de la clasificación punto a punto. Puede fijarse manualmente.
- El neutrón se calibra sobre una matriz (por defecto caliza, estándar de industria). La app lee esa matriz del archivo cuando está declarada.
- La constante de Kozeny no es universal: sin calibración contra núcleos, la permeabilidad es un orden de magnitud, no un valor absoluto.
- Los cálculos propagan el nulo: si falta un dato necesario el resultado es nulo, nunca un valor por defecto silencioso. El net pay distingue entre "no cumple" y "no evaluable".
Python 3.11 (versión de referencia, sobre la que está verificado el conjunto de dependencias). Las dependencias y sus topes de versión mayor están en requirements.txt.
La formulación sigue la práctica estándar de interpretación de registros. Los valores de referencia de minerales y los métodos de crossplot provienen del curso Análisis e Interpretación de Registros de Pozo (M. F. Barragán A., ACGGP / LITHoil, 2022).
Erick F. Mendoza Rivero (ErickFMR777)
Todos los derechos reservados © 2026