Métodos potenciales de prospección, FCAG, 2024.
Utilizamos el archivo datos2D.dat de anomalías de Bouguer dispuestos en una cuadrícula. Para comenzar, podemos emplear algunos comandos GNU/Linux para observar la estructura del dato en la terminal.
$> head "datos2D.dat"
# x[km] y[km] AB[mGal]
0 0 8.36
1 0 8.46
2 0 8.51
3 0 8.56
4 0 8.72
5 0 8.67
6 0 8.79
7 0 8.78
Viendo el encabezado, notamos que el dato está presentado en la forma
$x$ (km) | $y$ (km) | $g_z$ (mGal) |
---|---|---|
... | ... | ... |
... | ... | ... |
... | ... | ... |
Ahora procedemos a leer el archivo para procesar. Para ello podemos escribir:
data = np.loadtxt('datos2D.dat') # las líneas que comienzan con '#' en el archivo son ignoradas
x, y, gz = data[:,0], data[:,1], data[:,2] # columnas del dato en cada vector
N = len(gz) # número de observaciones de AAB [mgal]
nx = len(np.unique(x)) # número de coordenadas únicas en x
ny = len(np.unique(y)) # número de coordenadas únicas en y
print(f"Contamos con {N} observaciones.")
print(f"nx={nx}, ny={ny}, (nx * ny = {nx*ny}).")
GZ = gz.reshape(ny,nx) # genero matriz con el vector de mediciones de gravedad (sólo para graficar)
Contamos con 176 observaciones. nx=16, ny=11, (nx * ny = 176).
Ajustamos al dato $g_z$ polinomios de grado $M$,
$$ \hat{g}_z(x,y;M) = \begin{cases} c, & \; M=0;\\ ax + by + c, & \; M=1;\\ ax^2 + by^2 + cxy + dx + ey + f, & \; M=2. \end{cases} $$$\newcommand{\B}[1]{\mathbf{#1}}$ Consideramos $M=1$ y calculamos la matriz de diseño $\B{A}= [\B{x}\: \B{y}\: \B{1}]$:
x = x.reshape(N,1)
y = y.reshape(N,1)
ones = np.ones((N,1))
A = np.hstack((x, y, ones)) # A = [ x y 1 ]
print(f"Tamaño de A: {np.shape(A)}")
Tamaño de A: (176, 3)
Resolvemos el sistema $\mathbf{A}\mathbf{m}=\mathbf{g^o}$, para $\mathbf{m}$ por cuadrados mínimos. Aquí $\mathbf{g^o}$ es el dato observado dispuesto en un vector columna. La solución dada por cuadrados mínimos la denotamos $\mathbf{m}^*$ y en código m_estimated
:
from scipy.linalg import lstsq
m_estimated,_,_,_ = lstsq(A,gz)
Grado del polinomio de ajuste, M = 1 Vector de coeficientes, m* = [-0.022066 -0.209915 8.734554]
Como valor agregado, podemos verificar el resultado si utilizamos un algoritmo de gradientes conjugados (CG) programado en una clase preliminar:
def CG(A,b):
...
return m
m_estimated = CG(A.T @ A,A.T @ gz)
CG: Según la tolerancia 1e-10, CG converge en la iteración 4 vector de coeficientes, m* = [-0.022066 -0.209915 8.734554]
Con el vector de coeficientes $\mathbf{m^*}$, obtenemos por medio del problema directo la superficie de tendencia en los puntos observados:
$$\mathbf{\hat{g}} = \mathbf{A}\mathbf{m^*},$$y el campo residual,
$$\Delta\mathbf{g} = \mathbf{g^o}-\mathbf{\hat{g}}.$$En python podemos generar la superficie de tendencia y el campo residual así:
# Superficie de tendencia:
GZ_fit = (A @ m) # en python 3, '@' es producto matricial
GZ_fit = GZ_fit.reshape(ny,nx) # construyo cuadrícula del ajuste
# Campo residual (resta de cuadrículas):
diff = GZ - GZ_fit
Visualizamos el dato original $g_z$, la superficie ajustada según mínimos cuadrados $\hat{g}_z$ y el residuo $\Delta g_z=g_z-\hat{g}_z$. En los gráficos, utilizamos la misma escala vertical para el dato original y la superficie de tendencia. Para visualizar el campo residual, utilizamos una escala diferente centrada en 0 mGal. Observamos que las líneas de contorno no son suaves debido a que los valores no han sido interpolados en una cuadrícula más densa.
Eso es todo por hoy.
Leemos el archivo datos2D_interpolado.dat de anomalías de Bouguer dispuestas en una cuadrícula e interpoladas con un método específico para los métodos potenciales. Procedemos de la misma manera, realizando el ajuste para $M=1$ y graficando los resultados.
Podemos interpolar el archivo original utilizando GMT (Generic Mapping tools) y escribiendo unas pocas líneas en la terminal. El dato original tiene $n_x=16$ y $n_y=11$. El método de interpolación utilizado es el continuous curvature splines in tension. Generamos el dato cuadrículado e interpolado en binario (formato NetCDF) y luego lo convertimos a formato ASCII para utilizarlo en esta clase. Utilizamos el parámetro $T=0.25$ recomendado para datos de métodos potenciales.
$> which GMT # si esta instalado, arroja el path al ejecutable