Skip to article frontmatterSkip to article content

Learning goals

  • Understand the impact of frequencies (start from a couple of frequencies, and more)
  • Understand the impact of station spacing
  • Understand the impact of alpha_s, alpha_x, alpha_y
  • Understand the impact of p_s, p_y, p_z
import matplotlib.pyplot as plt
import scipy.sparse as sp
import numpy as np
import unittest
from scipy.constants import mu_0
import discretize
import matplotlib.patheffects as pe
from simpeg.electromagnetics import natural_source as nsem
from simpeg.electromagnetics.static import utils as sutils
from simpeg import (
    maps, utils, optimization, objective_function, inversion, inverse_problem, directives,
    data_misfit, regularization, data
)
from discretize import TensorMesh
from pymatsolver import Pardiso
from scipy.spatial import cKDTree
from scipy.stats import norm
# from dask.distributed import Client, LocalCluster
import dill
from geoana.em.fdem import skin_depth
import discretize.utils as dis_utils
import warnings
warnings.filterwarnings("ignore")

def generate_2d_mesh_for_mt(
    rx_locs, frequencies, sigma_background, 
    z_factor_max=5, 
    z_factor_min=5,
    pfz_down = 1.2,
    pfz_up = 1.5,
    npadz_up = 5,
    x_factor_max = 2,
    spacing_factor = 4,
    pfx = 1.5,
    n_max=1000
):
    # Setting the cells in depth dimension
    f_min = frequencies.min()
    f_max = frequencies.max()
    dz_min = np.round(skin_depth(f_max, sigma_background) / z_factor_max) 
    lz = skin_depth(sigma_background, f_min) * z_factor_max
    # Setting the domain length in z-direction
    for nz_down in range(n_max):
        hz_down = dz_min*pfz_down**np.arange(nz_down)[::-1]
        if hz_down.sum()>lz:
            break
    hz_up = [(dz_min, npadz_up, pfz_up)]
    hz_up = dis_utils.unpack_widths(hz_up)
    hz = np.r_[hz_down, hz_up]   
    # Setting the cells in lateral dimension
    d_station = np.diff(rx_locs[:,0]).min()
    dx_min = np.round(d_station/spacing_factor)
    lx = rx_locs[:,0].max() - rx_locs[:,0].min()
    ncx = int(lx / dx_min)
    lx_pad = skin_depth(sigma_background, f_min) * x_factor_max
    for npadx in range(n_max):
        hx_pad = dis_utils.unpack_widths([(dx_min, npadx, -pfx)])
        if hx_pad.sum()>lx_pad:
            break
    hx = [(dx_min, npadx, -pfx), (dx_min, ncx), (dx_min, npadx, pfx)]  
    
    mesh = discretize.TensorMesh([hx, hz])
    mesh.origin = np.r_[-mesh.h[0][:npadx].sum()+rx_locs[:,0].min(), -hz_down.sum()]   
    print (mesh)
    return mesh

How does changing the station spacing and the number of frequencies impact the recovered model?

input_data = dill.load(open("./synthetic_2d.pik", "rb"))
nskip_freq = 5
nskip_rx = 1
# a decent setup
# nskip_freq = 7
# nskip_rx = 3
relative_error =  0.05
floor_error = 2
alpha_s = 1e-10
alpha_y = 1.
alpha_z = 0.5
rho_0 = 100.
maxIter = 30
use_irls = False
p_s=0
p_y=0
p_z=0

rx_locs = input_data['rx_locs'][::nskip_rx,:]
frequencies = input_data['frequencies'][::nskip_freq]
app_rho_te = input_data['app_rho_te'][::nskip_freq,::nskip_rx]
app_rho_tm = input_data['app_rho_tm'][::nskip_freq,::nskip_rx]
phase_te = input_data['phase_te'][::nskip_freq,::nskip_rx]
phase_tm = input_data['phase_tm'][::nskip_freq,::nskip_rx]


app_rho_err_tm = abs(app_rho_tm) * relative_error
app_rho_err_te = abs (app_rho_te) * relative_error
phase_err_tm = np.ones_like(phase_tm) * floor_error
phase_err_te = np.ones_like(phase_te) * floor_error

mesh = generate_2d_mesh_for_mt(rx_locs, frequencies, 1e-2)
ind_active = mesh.cell_centers[:,1]<0.
print ("Rx")
print (rx_locs)
print ("Freqs")
print (frequencies)

  TensorMesh: 1,088 cells

                      MESH EXTENT             CELL WIDTH      FACTOR
  dir    nC        min           max         min       max      max
  ---   ---  ---------------------------  ------------------  ------
   x     32   -205,859.38    205,859.38  5,000.00 56,953.12    1.50
   y     34   -260,778.01      5,242.03    265.00 43,683.84    1.50


Rx
[[-50000.      0.]
 [-30000.      0.]
 [-10000.      0.]
 [ 10000.      0.]
 [ 30000.      0.]
 [ 50000.      0.]]
Freqs
[1.00000000e-02 1.12883789e-01 1.27427499e+00 1.43844989e+01]
# simulation class for TM mode
rx_list_tm = [
    nsem.receivers.PointNaturalSource(
        rx_locs, orientation="xy", component="apparent_resistivity"
    ),
    nsem.receivers.PointNaturalSource(
        rx_locs, orientation="xy", component="phase"
    ),
]
src_list_tm = [nsem.sources.Planewave(rx_list_tm, frequency=f) for f in frequencies]
survey_tm = nsem.Survey(src_list_tm)

act_map = maps.InjectActiveCells(mesh, ind_active, np.log(1e-8))
exp_map = maps.ExpMap(mesh=mesh)
sigma_map = exp_map * act_map

sim_tm= nsem.simulation.Simulation2DElectricField(
    mesh,
    survey=survey_tm,
    sigmaMap=sigma_map,
    solver=Pardiso,
)

# simulation class for TE mode
rx_list_te = [
    nsem.receivers.PointNaturalSource(
        rx_locs, orientation="yx", component="apparent_resistivity"
    ),
    nsem.receivers.PointNaturalSource(
        rx_locs, orientation="yx", component="phase"
    ),
]
src_list_te = [nsem.sources.Planewave(rx_list_te, frequency=f) for f in frequencies]
survey_te = nsem.Survey(src_list_te)

sim_te = nsem.simulation.Simulation2DMagneticField(
    mesh,
    survey=survey_te,
    sigmaMap=sigma_map,
    solver=Pardiso,
)
dobs_te = np.hstack((app_rho_te, phase_te)).flatten()
dobs_tm = np.hstack((app_rho_tm, phase_tm)).flatten()

std_te = np.hstack((app_rho_err_te, phase_err_te)).flatten()
std_tm = np.hstack((app_rho_err_tm, phase_err_tm)).flatten()
m0 = np.ones(ind_active.sum()) * np.log(1./rho_0)
%%time

te_data_object = data.Data(survey_te, dobs=dobs_te, standard_deviation=std_te) 
tm_data_object = data.Data(survey_tm, dobs=dobs_tm, standard_deviation=std_tm) 
dmis_te = data_misfit.L2DataMisfit(data=te_data_object, simulation=sim_te)
dmis_tm = data_misfit.L2DataMisfit(data=tm_data_object, simulation=sim_tm)
dmis = dmis_te + dmis_tm

# Define the regularization (model objective function)
reg = regularization.Sparse(
    mesh,
    active_cells=ind_active,
    reference_model=m0,
    alpha_s=alpha_s,
    alpha_x=alpha_y,
    alpha_y=alpha_z,
    mapping=maps.IdentityMap(nP=int(ind_active.sum()))
)

# Define how the optimization problem is solved. Here we will use an
# Inexact Gauss Newton approach.
opt = optimization.InexactGaussNewton(maxIter=maxIter, maxIterCG=30, tolX=1e-30)

# Here we define the inverse problem that is to be solved
inv_prob = inverse_problem.BaseInvProblem(dmis, reg, opt)

#######################################################################
# Define MT Inversion Directives
# ------------------------------
#
# Here we define any directives that are carried out during the inversion. This
# includes the cooling schedule for the trade-off parameter (beta), stopping
# criteria for the inversion and saving inversion results at each iteration.
#

# Defining a starting value for the trade-off parameter (beta) between the data
# misfit and the regularization.
starting_beta = directives.BetaEstimate_ByEig(beta0_ratio=1)

# Set the rate of reduction in trade-off parameter (beta) each time the
# the inverse problem is solved. And set the number of Gauss-Newton iterations
# for each trade-off paramter value.
beta_schedule = directives.BetaSchedule(coolingFactor=2, coolingRate=1)

save_dictionary = directives.SaveOutputDictEveryIteration()
save_dictionary.outDict = {}

# Setting a stopping criteria for the inversion.
target_misfit = directives.TargetMisfit(chifact=1e0)

if use_irls:
    reg.norms = np.c_[p_s, p_y, p_z]
    # Reach target misfit for L2 solution, then use IRLS until model stops changing.
    IRLS = directives.Update_IRLS(max_irls_iterations=40, minGNiter=1, f_min_change=1e-5)

    # The directives are defined as a list.
    directives_list = [
        IRLS,
        starting_beta,
        save_dictionary,
    ]
else:
    directives_list = [
        starting_beta,
        beta_schedule,
        save_dictionary,
        target_misfit,
    ]

    
#####################################################################
# Running the MT Inversion
# ------------------------
#
# To define the inversion object, we need to define the inversion problem and
# the set of directives. We can then run the inversion.
#

# Here we combine the inverse problem and the set of directives
mt_inversion = inversion.BaseInversion(inv_prob, directiveList=directives_list)

# Run inversion
recovered_conductivity_model = mt_inversion.run(m0)

Running inversion with SimPEG v0.22.2

                        simpeg.InvProblem is setting bfgsH0 to the inverse of the eval2Deriv.
                        ***Done using same Solver, and solver_opts as the Simulation2DMagneticField problem***
                        
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  2.91e+00  1.31e+05  0.00e+00  1.31e+05    1.90e+04      0              
   1  1.45e+00  1.81e+04  6.40e+00  1.81e+04    3.19e+03      0              
   2  7.27e-01  2.66e+03  3.24e+01  2.68e+03    5.16e+02      0   Skip BFGS  
   3  3.64e-01  5.34e+02  1.24e+02  5.79e+02    1.50e+02      0              
   4  1.82e-01  2.49e+02  1.66e+02  2.79e+02    2.01e+02      0              
   5  9.09e-02  1.04e+02  1.79e+02  1.20e+02    8.81e+01      0              
------------------------- STOP! -------------------------
1 : |fc-fOld| = 0.0000e+00 <= tolF*(1+|f0|) = 1.3100e+04
0 : |xc-x_last| = 3.2308e+00 <= tolX*(1+|x0|) = 1.4129e-28
0 : |proj(x-g)-x|    = 8.8091e+01 <= tolG          = 1.0000e-01
0 : |proj(x-g)-x|    = 8.8091e+01 <= 1e3*eps       = 1.0000e-02
0 : maxIter   =      30    <= iter          =      6
------------------------- DONE! -------------------------
CPU times: user 2min 34s, sys: 8.52 s, total: 2min 43s
Wall time: 52.9 s
survey_te.nD + survey_tm.nD
96
output_dict = save_dictionary.outDict
target_misfit = (dobs_te.size+dobs_tm.size)
iterations = list(output_dict.keys())
n_iteration = len(iterations)
phi_ds = np.zeros(n_iteration)
phi_ms = np.zeros(n_iteration)
betas = np.zeros(n_iteration)
for ii, iteration in enumerate(iterations):
    phi_ds[ii] = output_dict[iteration]['phi_d']
    phi_ms[ii] = output_dict[iteration]['phi_m']
    betas[ii] = output_dict[iteration]['beta']
import matplotlib
matplotlib.rcParams['font.size'] = 14
def tikhonov_curve(iteration, scale='log'):
    fig, ax = plt.subplots(1,1, figsize=(5,5))
    ax.plot(phi_ms, phi_ds)
    ax.plot(phi_ms[iteration-1], phi_ds[iteration-1], 'ro')
    ax.set_xlabel("$\phi_m$")
    ax.set_ylabel("$\phi_d$")
    if scale == 'log':
        ax.set_xscale('log')
        ax.set_yscale('log')
    xlim = ax.get_xlim()
    ax.plot(xlim, np.ones(2) * target_misfit, '--')
    ax.set_title("Iteration={:d}, Beta = {:.1e}".format(iteration, betas[iteration-1]))
    ax.set_xlim(xlim)
    plt.show()
from ipywidgets import interact, widgets
Q_iter = interact(
    tikhonov_curve, 
    iteration=widgets.IntSlider(min=1, max=int(n_iteration), value=n_iteration),
    scale=widgets.RadioButtons(options=['linear', 'log'])
)
Loading...
from matplotlib.colors import LogNorm
mesh_true = input_data['mesh']
sigma_true = input_data['sigma']
iteration = Q_iter.widget.kwargs['iteration']
m = output_dict[iteration]['m']
def foo_model(iteration):
    fig, axs = plt.subplots(2,1, figsize=(10, 6))
    ax, ax1 = axs
    m = output_dict[iteration]['m']
    sigma = np.ones(mesh.nC) * 1e-8
    sigma[ind_active] = np.exp(m)
    sigma_min = 1e-3
    sigma_max = 10
    grid= False
    out = mesh.plot_image(
        sigma, grid=grid, ax=ax, pcolor_opts={'norm':LogNorm(vmin=sigma_min, vmax=sigma_max), 'cmap':'turbo'},
        range_x=(-60000, 60000), range_y=(-30000, 0)
    )
    cb = plt.colorbar(out[0], fraction=0.01, ax=ax)
    cb.set_label("Conductivity (S/m)")
    ax.set_aspect(1)
    ax.set_xlabel("Easting (m)")
    ax.set_ylabel("Elevation (m)")
    ax.plot(rx_locs[:,0], rx_locs[:,1], 'ro')
    out = mesh_true.plot_image(
        sigma_true, grid=grid, ax=ax1, pcolor_opts={'norm':LogNorm(vmin=sigma_min, vmax=sigma_max), 'cmap':'turbo'},
        range_x=(-60000, 60000), range_y=(-30000, 0)
    )
    cb = plt.colorbar(out[0], fraction=0.01, ax=ax1)
    cb.set_label("Conductivity (S/m)")
    ax1.set_aspect(1)
    ax1.set_xlabel("Easting (m)")
    ax1.set_ylabel("Elevation (m)")
Q_misfit = interact(
    foo_model, 
    iteration=widgets.IntSlider(min=1, max=n_iteration, value=iteration),
)    
Loading...
pred_te = sim_te.dpred(m)
pred_tm = sim_tm.dpred(m)
n_freq = len(frequencies)
n_rx = rx_locs.shape[0]
PRED_te = pred_te.reshape((n_freq, 2, n_rx))
PRED_tm = pred_tm.reshape((n_freq, 2, n_rx))

rho_app_te_pred = PRED_te[:,0,:]
rho_app_tm_pred = PRED_tm[:,0,:]

phase_te_pred = PRED_te[:,1,:]
phase_tm_pred = PRED_tm[:,1,:]

DOBS_te = dobs_te.reshape((n_freq, 2, n_rx))
DOBS_tm = dobs_tm.reshape((n_freq, 2, n_rx))

rho_app_te_dobs = DOBS_te[:,0,:]
rho_app_tm_dobs = DOBS_tm[:,0,:]

phase_te_dobs = DOBS_te[:,1,:]
phase_tm_dobs = DOBS_tm[:,1,:]
def foo_pred(irx):
    import matplotlib
    matplotlib.rcParams['font.size'] = 10

    fig, axs = plt.subplots(4,1, figsize=(8, 10))
    ax1, ax2, ax3, ax4 = axs
    ax1.loglog(1./frequencies, rho_app_te_pred[:, irx], color='C0')
    ax1.loglog(1./frequencies, rho_app_te_dobs[:, irx], 'x', color='C0')
    ax2.loglog(1./frequencies, rho_app_tm_pred[:, irx], color='C1')
    ax2.loglog(1./frequencies, rho_app_tm_dobs[:, irx], 'x', color='C1')
    for ax in axs[:2]:
        ax.set_ylim(1, 1000)
        ax.set_ylabel("App. Res. (Ohm-m)")
        ax.grid(which='both', alpha=0.3)
    ax3.semilogx(1./frequencies, phase_te_pred[:, irx], color='C0')
    ax3.semilogx(1./frequencies, phase_te_dobs[:, irx], 'x', color='C0')
    ax4.semilogx(1./frequencies, phase_tm_pred[:, irx]+180, color='C1')
    ax4.semilogx(1./frequencies, phase_tm_dobs[:, irx]+180, 'x', color='C1')
    for ax in axs[2:]:
        ax.set_ylim(0, 90)
        ax.set_ylabel("Phase (degree)")
        ax.grid(which='both', alpha=0.3)
    ax4.set_xlabel("Period (s)")
Q_misfit = interact(
    foo_pred, 
    irx=widgets.IntSlider(min=0, max=int(n_rx)-1, value=0),
)
Loading...