含断层的油藏 流动模拟
油藏里横着几条断层就像炒菜锅里突然出现裂缝——油气流动轨迹说变就变。搞数值模拟时最怕这种地质构造,常规的矩形网格遇到断层直接抓瞎。咱们今天不整虚的,直接撸代码看看怎么对付这些地质界的"路障"。
先整点基础数据热身。假设咱们有个500m×500m的二维油藏,中间斜插着一条断层,用Python画个示意图:
import numpy as np import matplotlib.pyplot as plt grid_size = 50 X, Y = np.meshgrid(np.linspace(0,500,grid_size), np.linspace(0,500,grid_size)) fault_line = Y > (0.6*X + 100) # 断层线方程 plt.figure(figsize=(8,6)) plt.imshow(fault_line.T, origin='lower', extent=[0,500,0,500], cmap='Greys') plt.colorbar(label='Fault Presence') plt.title('断层分布示意图') plt.xlabel('X方向/m') plt.ylabel('Y方向/m') plt.show()这段代码生成带斜断层的二值掩膜图,断层带显示为深色区域。注意0.6*X + 100这个参数控制断层走向,实操中得根据实际地质数据调整。
遇到断层最大的麻烦是渗透率突变。假设断层带渗透率骤降为周围岩石的1/1000,咱们得处理这种不连续场。用NumPy搞个渗透率矩阵:
perm = np.ones((grid_size, grid_size)) * 200 # 基础渗透率200mD perm[fault_line] = 0.2 # 断层带渗透率骤降但这么简单赋值会出问题——实际断层对相邻网格的影响需要特殊处理。这时候得上有限体积法,把断层当内部边界处理。举个压力方程的离散化例子:
for i in range(1, grid_size-1): for j in range(1, grid_size-1): if fault_line[i,j]: # 断层网格特殊处理 trans_x = 0 if fault_line[i+1,j] else 1 trans_y = 0 if fault_line[i,j+1] else 1 coeff_matrix[i,j] = - (trans_x + trans_y)这里的关键是判断相邻网格是否跨过断层,如果跨过就把传导率设为零。实际开发中得处理更复杂的连接列表,像Eclipse这类商用软件会在预处理阶段生成非相邻连接(NNC)表。
跑个瞬态模拟看看效果。用Scipy解压力方程:
from scipy.sparse import diags from scipy.sparse.linalg import spsolve # 构造系数矩阵 diag = -4 * np.ones(grid_size**2) adj = np.ones(grid_size**2 - 1) A = diags([adj, diag, adj], [-1, 0, 1], format='csr') # 处理断层影响 for idx in np.where(fault_line.flatten())[0]: A[idx, idx] *= 1000 # 放大对角线元素模拟低渗透 # 相邻元素连接调整... # 求解压力分布 pressure = spsolve(A, np.zeros(grid_size**2)).reshape(grid_size, grid_size)画出来的压力等值线在断层处会出现明显转折,就像水流遇到堤坝产生水位差。有趣的是,当断层部分开启时(渗透率不是完全为零),还能观察到"渗漏"现象——就像破损的水管虽然漏水但还能维持一定压力。
处理这类问题有个坑:过度简化断层为绝对隔挡。实际油藏中断层可能在不同位置呈现开启或封闭状态,得结合地质力学数据动态调整参数。下次如果遇到模拟结果与实际试井数据对不上,不妨查查断层模型是不是太"理想化"了。
最后给个忠告:别跟断层死磕结构网格。现在开源工具像MRST支持非结构网格,用PEBI网格(正交多边形)绕开断层走向,比硬调参数香多了。毕竟,跟地质构造较劲的最好方式,就是换个姿势拥抱它。