1D DC inversion of Schlumberger array

This is an example for 1D DC Sounding inversion. This 1D inversion usually use analytic foward modeling, which is efficient. However, we choose different approach to show flexibility in geophysical inversion through mapping. Here mapping, \(\mathcal{M}\), indicates transformation of our model to a different space:

\[\sigma = \mathcal{M}(\mathbf{m})\]

Now we consider a transformation, which maps 3D conductivity model to 1D layer model. That is, 3D distribution of conducitivity can be parameterized as 1D model. Once we can compute derivative of this transformation, we can change our model space, based on the transformation.

Following example will show you how user can implement this set up with 1D DC inversion example. Note that we have 3D forward modeling mesh.

from SimPEG import (
    Mesh, Maps, DataMisfit, Regularization, Optimization,
    InvProblem, Directives, Inversion
)
from SimPEG.Utils import plotLayer
try:
    from pymatsolver import Pardiso as Solver
except ImportError:
    from SimPEG import SolverLU as Solver
from SimPEG.EM.Static import DC
import numpy as np
import matplotlib.pyplot as plt

Step 1

Generate mesh

cs = 25.
npad = 11
hx = [(cs, npad, -1.3), (cs, 41), (cs, npad, 1.3)]
hy = [(cs, npad, -1.3), (cs, 17), (cs, npad, 1.3)]
hz = [(cs, npad, -1.3), (cs, 20)]
mesh = Mesh.TensorMesh([hx, hy, hz], 'CCN')

Step 2

Generating model and mapping (1D to 3D)

mapping = Maps.ExpMap(mesh)*Maps.SurjectVertical1D(mesh)
siglay1 = 1./(100.)
siglay2 = 1./(500.)
sighalf = 1./(100.)
sigma = np.ones(mesh.nCz)*siglay1
sigma[mesh.vectorCCz <= -100.] = siglay2
sigma[mesh.vectorCCz < -150.] = sighalf
mtrue = np.log(sigma)


fig, ax = plt.subplots(1, 2, figsize=(18*0.8, 7*0.8))
plotLayer(np.log(sigma), mesh.vectorCCz, 'linear', showlayers=True, ax=ax[0])
ax[0].invert_xaxis()
ax[0].set_ylim(-500, 0)
ax[0].set_xlim(-7, -4)
ax[0].set_xlabel('$log(\sigma)$', fontsize=25)
ax[0].set_ylabel('Depth (m)', fontsize=25)
dat = mesh.plotSlice((mapping*mtrue), normal='Y', ind=9, ax=ax[1])
cb = plt.colorbar(dat[0], ax=ax[1])
ax[0].set_title("(a) Conductivity with depth", fontsize=25)
ax[1].set_title("(b) Vertical section", fontsize=25)
cb.set_label("Conductivity (S/m)", fontsize=25)
ax[1].set_xlabel('Easting (m)', fontsize=25)
ax[1].set_ylabel(' ', fontsize=25)
ax[1].set_xlim(-1000., 1000.)
ax[1].set_ylim(-500., 0.)
../../../_images/sphx_glr_plot_dc_schlumberger_array_001.png

Step 3

Design survey: Schulumberger array

Schulumberger array
\[\rho_a = \frac{V}{I}\pi\frac{b(b+a)}{a}\]

Let \(b=na\), then we rewrite above equation as:

\[\rho_a = \frac{V}{I}\pi na(n+1)\]

Since AB/2 can be a good measure for depth of investigation, we express

\[AB/2 = \frac{(2n+1)a}{2}\]
ntx = 16
xtemp_txP = np.arange(ntx)*(25.)-500.
xtemp_txN = -xtemp_txP
ytemp_tx = np.zeros(ntx)
xtemp_rxP = -50.
xtemp_rxN = 50.
ytemp_rx = 0.
abhalf = abs(xtemp_txP-xtemp_txN)*0.5
a = xtemp_rxN-xtemp_rxP
b = ((xtemp_txN-xtemp_txP)-a)*0.5

fig, ax = plt.subplots(1, 1, figsize=(12, 3))
for i in range(ntx):
    ax.plot(
        np.r_[xtemp_txP[i], xtemp_txP[i]],
        np.r_[0., 0.4-0.01*(i-1)], 'k-', lw=1
    )
    ax.plot(
        np.r_[xtemp_txN[i], xtemp_txN[i]],
        np.r_[0., 0.4-0.01*(i-1)], 'k-', lw=1
    )
    ax.plot(xtemp_txP[i], ytemp_tx[i], 'bo')
    ax.plot(xtemp_txN[i], ytemp_tx[i], 'ro')
    ax.plot(
        np.r_[xtemp_txP[i], xtemp_txN[i]],
        np.r_[0.4-0.01*(i-1), 0.4-0.01*(i-1)], 'k-', lw=1
    )

ax.plot(np.r_[xtemp_rxP, xtemp_rxP], np.r_[0., 0.2], 'k-', lw=1)
ax.plot(np.r_[xtemp_rxN, xtemp_rxN], np.r_[0., 0.2], 'k-', lw=1)
ax.plot(xtemp_rxP, ytemp_rx, 'ko')
ax.plot(xtemp_rxN, ytemp_rx, 'go')
ax.plot(np.r_[xtemp_rxP, xtemp_rxN], np.r_[0.2, 0.2], 'k-', lw=1)

ax.grid(True)
ax.set_ylim(-0.2, 0.6)
../../../_images/sphx_glr_plot_dc_schlumberger_array_002.png

Look at the survey in map view

fig, ax = plt.subplots(1, 1, figsize=(6, 4))
ax.plot(xtemp_txP, ytemp_tx, 'bo')
ax.plot(xtemp_txN, ytemp_tx, 'ro')
ax.plot(xtemp_rxP, ytemp_rx, 'ko')
ax.plot(xtemp_rxN, ytemp_rx, 'go')
ax.legend(('A (C+)', 'B (C-)', 'M (P+)', 'N (C-)'), fontsize=14)
mesh.plotSlice(
    np.log10(mapping*mtrue), grid=True, ax=ax, pcolorOpts={'cmap': 'binary'}
)
ax.set_xlim(-600, 600)
ax.set_ylim(-200, 200)
ax.set_title('Survey geometry (Plan view)')
ax.set_xlabel('Easting (m)')
ax.set_ylabel('Northing (m)')
ax.text(-600, 210, '(a)', fontsize=16)

# We generate tx and rx lists:
srclist = []
for i in range(ntx):
    rx = DC.Rx.Dipole(np.r_[xtemp_rxP, ytemp_rx, -12.5], np.r_[xtemp_rxN, ytemp_rx, -12.5])
    locA = np.r_[xtemp_txP[i], ytemp_tx[i], -12.5]
    locB = np.r_[xtemp_txN[i], ytemp_tx[i], -12.5]
    src = DC.Src.Dipole([rx], locA, locB)
    srclist.append(src)
../../../_images/sphx_glr_plot_dc_schlumberger_array_003.png

Step 4

Set up problem and pair with survey

survey = DC.Survey(srclist)
problem = DC.Problem3D_CC(mesh, sigmaMap=mapping)
problem.pair(survey)
problem.Solver = Solver

Step 5

Run survey.dpred to comnpute syntetic data

\[\rho_a = \frac{V}{I}\pi\frac{b(b+a)}{a}\]

To make synthetic example you can use survey.makeSyntheticData, which generates related setups.

data = survey.dpred(mtrue)
survey.makeSyntheticData(mtrue, std=0.01, force=True)

appres = data*np.pi*b*(b+a)/a
appres_obs = survey.dobs*np.pi*b*(b+a)/a
fig, ax = plt.subplots(1, 2, figsize=(16, 4))
ax[1].semilogx(abhalf, appres, 'k.-')
ax[1].set_xscale('log')
ax[1].set_ylim(100., 180.)
ax[1].set_xlabel('AB/2')
ax[1].set_ylabel('Apparent resistivity ($\Omega m$)')
ax[1].grid(True)

dat = mesh.plotSlice((mapping*mtrue), normal='Y', ind=9, ax=ax[0])
cb = plt.colorbar(dat[0], ax=ax[0])
ax[0].set_title("Vertical section")
cb.set_label("Conductivity (S/m)")
ax[0].set_xlabel('Easting (m)')
ax[0].set_ylabel('Depth (m)')
ax[0].set_xlim(-1000., 1000.)
ax[0].set_ylim(-500., 0.)
../../../_images/sphx_glr_plot_dc_schlumberger_array_004.png

Step 6

Run inversion

regmesh = Mesh.TensorMesh([31])
dmis = DataMisfit.l2_DataMisfit(survey)
reg = Regularization.Tikhonov(regmesh)
opt = Optimization.InexactGaussNewton(maxIter=7, tolX=1e-15)
opt.remember('xc')
invProb = InvProblem.BaseInvProblem(dmis, reg, opt)
beta = Directives.BetaEstimate_ByEig(beta0_ratio=1e1)
betaSched = Directives.BetaSchedule(coolingFactor=5, coolingRate=2)
inv = Inversion.BaseInversion(invProb, directiveList=[beta, betaSched])

# Choose an initial starting model of the background conductivity
m0 = np.log(np.ones(mapping.nP)*sighalf)
mopt = inv.run(m0)

Out:

SimPEG.InvProblem will set Regularization.mref to m0.
SimPEG.InvProblem is setting bfgsH0 to the inverse of the eval2Deriv.
                    ***Done using same Solver and solverOpts as the problem***
SimPEG.DataMisfit.l2_DataMisfit assigning default eps of 1e-5 * ||dobs||
model has any nan: 0
============================ Inexact Gauss Newton ============================
  #     beta     phi_d     phi_m       f      |proj(x-g)-x|  LS    Comment
-----------------------------------------------------------------------------
x0 has any nan: 0
   0  9.50e+02  6.39e+03  0.00e+00  6.39e+03    8.81e+03      0
   1  9.50e+02  4.44e+02  1.38e-01  5.76e+02    2.67e+03      0
   2  1.90e+02  2.08e+02  1.38e-01  2.34e+02    6.48e+02      0
   3  1.90e+02  1.11e+02  3.81e-01  1.84e+02    8.16e+01      0
   4  3.80e+01  1.14e+02  3.64e-01  1.28e+02    3.00e+02      0
   5  3.80e+01  4.45e+01  1.28e+00  9.33e+01    1.04e+02      0
   6  7.60e+00  4.67e+01  1.21e+00  5.60e+01    1.30e+02      0
   7  7.60e+00  1.28e+01  3.10e+00  3.64e+01    6.18e+01      0
------------------------- STOP! -------------------------
1 : |fc-fOld| = 1.9581e+01 <= tolF*(1+|f0|) = 6.3903e+02
0 : |xc-x_last| = 1.4227e+00 <= tolX*(1+|x0|) = 2.6641e-14
0 : |proj(x-g)-x|    = 6.1811e+01 <= tolG          = 1.0000e-01
0 : |proj(x-g)-x|    = 6.1811e+01 <= 1e3*eps       = 1.0000e-02
1 : maxIter   =       7    <= iter          =      7
------------------------- DONE! -------------------------

Step 7

Plot the results

appres = data*np.pi*b*(b+a)/a
appres_obs = survey.dobs*np.pi*b*(b+a)/a
appres_pred = invProb.dpred*np.pi*b*(b+a)/a
fig, ax = plt.subplots(1, 2, figsize=(17, 6))
ax[0].plot(abhalf, appres_obs, 'k.-')
ax[0].plot(abhalf, appres_pred, 'r.-')
ax[0].set_xscale('log')
ax[0].set_ylim(100., 180.)
ax[0].set_xlabel('AB/2', fontsize=25)
ax[0].set_ylabel('Apparent resistivity ($\Omega m$)', fontsize=25)
ax[0].set_title('(a)', fontsize=25)
ax[0].grid(True)
ax[0].legend(('Observed', 'Predicted'), loc=1, fontsize=20)
ax[1].plot(1., 1., 'k', lw=2)
ax[1].plot(1., 1., 'r', lw=2)
ax[1].legend(('True', 'Predicted'), loc=3, fontsize=20)
plotLayer(
    (np.exp(mopt)), mesh.vectorCCz, 'log',
    ax=ax[1], **{'lw': 2, 'color': 'r'}
)
plotLayer(
    (np.exp(mtrue)), mesh.vectorCCz, 'log', showlayers=True,
    ax=ax[1], **{'lw': 2}
)
ax[1].set_ylim(-500, 0)
ax[1].set_xlabel('Conductivity (S/m)', fontsize=25)
ax[1].set_ylabel('Depth (m)', fontsize=25)
ax[1].set_title('(b)', fontsize=25)
../../../_images/sphx_glr_plot_dc_schlumberger_array_005.png

Total running time of the script: ( 5 minutes 4.008 seconds)

Generated by Sphinx-Gallery