Surface wave and receiver function - joint inversion (field data)#

Open In Colab

If you are running this notebook locally, make sure you’ve followed steps here to set up the environment. (This environment.yml file specifies a list of packages required to run the notebooks)


What we do in this notebook#

Here we extend the example where CoFI has been used for the inversion of Rayleigh wave phase velocities for a 1D layered earth to a joint inversion where we also account for receiver functions.

In this example, we extend from the synthetic data notebook and apply the joint inversion on a field data presented in the Computer Programs in Seismology (see: https://www.eas.slu.edu/eqc/eqc_cps/TUTORIAL/STRUCT/index.html).

Learning outcomes

  • A demonstration of CoFI’s ability to switch between parameter estimation and ensemble methods.

  • An application of CoFI for a joint inversion, here of Rayleigh wave pahse velocity and receiver function data, to a field dataset

Utilities preparation#

In this example, we look at a joint inversion problem of surface wave and receiver function.

We use pysurf96 for computing the forward step of surface wave, and use espresso for receiver function calculations.

# -------------------------------------------------------- #
#                                                          #
#     Uncomment below to set up environment on "colab"     #
#                                                          #
# -------------------------------------------------------- #

# !pip install -U cofi geo-espresso
# !pip install git+https://github.com/inlab-geo/pysurf96.git
# !git clone https://github.com/inlab-geo/cofi-examples.git
# %cd cofi-examples/examples/sw_rf_joint
import glob
import numpy as np
import scipy
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import arviz as az

import pysurf96
import bayesbay
import cofi
import espresso
np.seterr(all="ignore");
{'divide': 'warn', 'over': 'warn', 'under': 'ignore', 'invalid': 'warn'}

Model vector

# display theory on the 1D model parameterisation
from IPython.display import display, Markdown

with open("../../theory/misc_1d_model_parameterisation.md", "r") as f:
    content = f.read()

display(Markdown(content))
<IPython.core.display.Markdown object>
# layercake model utilities
def form_layercake_model(thicknesses, vs):
    model = np.zeros((len(vs)*2-1,))
    model[1::2] = thicknesses
    model[::2] = vs
    return model

def split_layercake_model(model):
    thicknesses = model[1::2]
    vs = model[::2]
    return thicknesses, vs
# voronoi model utilities
def form_voronoi_model(voronoi_sites, vs):
    return np.hstack((vs, voronoi_sites))

def split_voronoi_model(model):
    voronoi_sites = model[len(model)//2:]
    vs = model[:len(model)//2]
    return voronoi_sites, vs
def voronoi_to_layercake(voronoi_vector: np.ndarray) -> np.ndarray:
    n_layers = len(voronoi_vector) // 2
    velocities = voronoi_vector[:n_layers]
    voronoi_sites = voronoi_vector[n_layers:]
    depths = (voronoi_sites[:-1] + voronoi_sites[1:]) / 2
    thicknesses = depths - np.insert(depths[:-1], 0, 0)
    layercake_vector = np.zeros((2*n_layers-1,))
    layercake_vector[::2] = velocities
    layercake_vector[1::2] = thicknesses
    return layercake_vector

def layercake_to_voronoi(layercake_vector: np.ndarray, first_voronoi_site: float = 0.0) -> np.ndarray:
    n_layers = len(layercake_vector) // 2 + 1
    thicknesses = layercake_vector[1::2]
    velocities = layercake_vector[::2]
    depths = np.cumsum(thicknesses)
    voronoi_sites = np.zeros((n_layers,))
    for i in range(1,len(voronoi_sites)):
        voronoi_sites[i] = 2 * depths[i-1] - voronoi_sites[i-1]
    voronoi_vector = np.hstack((velocities, voronoi_sites))
    return voronoi_vector

Interfacing to pysurf96

# display theory on the using the forward solver
with open("../../theory/geo_surface_wave_dispersion2.md", "r") as f:
    content = f.read()

display(Markdown(content))
<IPython.core.display.Markdown object>
VP_VS = 1.77
RHO_VP_K = 0.32
RHO_VP_B = 0.77
# forward through pysurf96
def forward_sw(model, periods):
    thicknesses, vs = split_layercake_model(model)
    thicknesses = np.append(thicknesses, 10)
    vp = vs * VP_VS
    rho = RHO_VP_K * vp + RHO_VP_B
    return pysurf96.surf96(
        thicknesses,
        vp,
        vs,
        rho,
        periods,
        wave="rayleigh",
        mode=1,
        velocity="phase",
        flat_earth=False,
    )
def forward_sw_interp(model, periods):
    pysurf_periods = np.logspace(
        np.log(np.min(periods)),
        np.log(np.max(periods+1)),
        60,
        base=np.e,
    )
    pysurf_dpred = forward_sw(model, pysurf_periods)
    interp_func = scipy.interpolate.interp1d(pysurf_periods,
                                             pysurf_dpred,
                                             fill_value="extrapolate")
    dpred = interp_func(periods)
    return dpred

Interfacing to Espresso

# obtain the receiver function library
rf_lib = espresso.ReceiverFunctionInversionKnt().rf
t_shift = 5
t_duration = 25
t_sampling_interval = 0.1
gauss = 1
ray_param_s_km = 0.07
# forward through rf in espresso
def forward_rf(
    model,
    t_shift=t_shift,
    t_duration=t_duration,
    t_sampling_interval=t_sampling_interval,
    gauss=gauss,
    ray_param_s_km=ray_param_s_km,
    return_times=False
):
    h, vs = split_layercake_model(model)
    data = rf_lib.rf_calc(ps=0, thik=h, beta=vs, kapa=np.ones((len(vs),))*VP_VS, p=ray_param_s_km,
                      duration=t_duration, dt=t_sampling_interval, shft=t_shift, gauss=gauss)
    if return_times:
        times = np.arange(len(data)) * t_sampling_interval - t_shift
        return data, times
    else:
        return data
t_shift = 5
t_duration = 70
t_sampling_interval = 0.5

def forward_rf_interp(model, gauss, ray_param):
    dpred, times = forward_rf(model, t_shift, t_duration, t_sampling_interval,
                       gauss=gauss, ray_param_s_km=ray_param, return_times=True)
    interp_func = scipy.interpolate.interp1d(times, dpred, fill_value="extrapolate")
    return interp_func(rf_field_times)

Numerical Jacobian

def jacobian(model, data_length, fwd, fwd_kwargs, relative_step=0.01):
    jac = np.zeros((data_length, len(model)))
    original_dpred = fwd(model, **fwd_kwargs)
    for i in range(len(model)):
        perturbed_model = model.copy()
        step = relative_step * model[i]
        perturbed_model[i] += step
        perturbed_dpred = fwd(perturbed_model, **fwd_kwargs)
        derivative = (perturbed_dpred - original_dpred) / step
        jac[:, i] = derivative
    return jac

Plotting

def plot_model(model, ax=None, title="model", **kwargs):
    # process data
    thicknesses = np.append(model[1::2], max(model[1::2]))
    velocities = model[::2]
    y = np.insert(np.cumsum(thicknesses), 0, 0)
    x = np.insert(velocities, 0, velocities[0])

    # plot depth profile
    if ax is None:
        _, ax = plt.subplots()
    plotting_style = {
        "linewidth": kwargs.pop("linewidth", kwargs.pop("lw", 0.5)),
        "alpha": 0.2,
        "color": kwargs.pop("color", kwargs.pop("c", "blue")),
    }
    plotting_style.update(kwargs)
    ax.step(x, y, where="post", **plotting_style)
    if ax.get_ylim()[0] < ax.get_ylim()[1]:
        ax.invert_yaxis()
    ax.set_xlabel("Vs (km/s)")
    ax.set_ylabel("Depth (km)")
    ax.set_title(title)
    return ax
def plot_data(x, y, ax=None, scatter=False, xlabel=None, ylabel=None,
              title="surface wave data", **kwargs):
    if ax is None:
        _, ax = plt.subplots()
    plotting_style = {
        "linewidth": kwargs.pop("linewidth", kwargs.pop("lw", 1)),
        "alpha": 1,
        "color": kwargs.pop("color", kwargs.pop("c", "blue")),
    }
    plotting_style.update(**kwargs)
    if scatter:
        ax.scatter(x, y, **plotting_style)
    else:
        ax.plot(x, y, **plotting_style)
    ax.set_xlabel(xlabel)
    ax.set_ylabel(ylabel)
    ax.set_title(title)
    return ax
def plot_sw_data(rayleigh_phase_velocities, periods, ax=None, scatter=False,
                 title="surface wave data", **kwargs):
    return plot_data(x=periods,
                     y=rayleigh_phase_velocities,
                     ax=ax,
                     scatter=scatter,
                     title=title,
                     xlabel="Periods (s)",
                     ylabel="Rayleigh phase velocities (km/s)",
                     **kwargs)

def plot_rf_data(rf_data, rf_times, ax=None, scatter=False,
                 title="receiver function data", **kwargs):
    return plot_data(x=rf_times,
                     y=rf_data,
                     ax=ax,
                     scatter=scatter,
                     title=title,
                     xlabel="Times (s)",
                     ylabel="Receiver function amplitudes",
                     **kwargs)

Read data#

Rayleigh observations

file_surf_data = "../../data/sw_rf_joint/data/SURF/nnall.dsp"

with open(file_surf_data, "r") as file:
    lines = file.readlines()
    surf_data = []
    for line in lines:
        row = line.strip().split()
        if "C" in row:
            surf_data.append([float(e) for e in row[5:8]])

field_d = np.array(surf_data)
field_d_periods = field_d[:,0]
rayleigh_field_d_obs = field_d[:,1]
ax = plot_sw_data(rayleigh_field_d_obs, field_d_periods, color="brown", s=5, scatter=True,
             label="d_obs")
ax.legend();
surface wave data
<matplotlib.legend.Legend object at 0x7fd0b1daac50>

Receiver function observations

files_rftn_data = glob.glob("../../data/sw_rf_joint/data/RFTN/rf_*.txt")

all_gauss = []
all_ray_params = []
all_rf_field_dobs = []
rf_field_times = None

for file in files_rftn_data:
    gauss, ray_param_s_km = file.split("_")[-2:]
    all_gauss.append(float(gauss))
    all_ray_params.append(float(ray_param_s_km[:-4]))
    dataset = np.loadtxt(file)
    if rf_field_times is None:
        rf_field_times = dataset[:, 0]
    all_rf_field_dobs.append(dataset[:, 1])
def plot_rf_field_data(all_rf_data, all_gauss, all_ray_params, rf_field_times,
                       axes=None, color="darkblue", label=None, **kwargs):
    if axes is None:
        _, axes = plt.subplots(13, 3, figsize=(10, 12))
    for i, ax in enumerate(axes.flat):
        ax.set_xlim(-5, 20)
        gauss = all_gauss[i]
        ray_param = all_ray_params[i]
        rf_dobs = all_rf_data[i]
        title=f"ray (s/km) = {ray_param}, gauss = {gauss}"
        plot_rf_data(rf_dobs, rf_field_times, ax=ax, color=color,
                     title=title, label=label, **kwargs)
        ax.set_ylabel("Amplitude")

    for ax in axes[:-1, :].flat:
        ax.set_xlabel("")
        ax.tick_params(labelbottom=False)
    for ax in axes[:, 1:].flat:
        ax.set_ylabel("")
    plt.tight_layout()
    return axes
plot_rf_field_data(all_rf_field_dobs, all_gauss, all_ray_params, rf_field_times);
ray (s/km) = 0.0716, gauss = 1.0, ray (s/km) = 0.0751, gauss = 1.0, ray (s/km) = 0.0705, gauss = 1.0, ray (s/km) = 0.0713, gauss = 2.5, ray (s/km) = 0.0738, gauss = 1.0, ray (s/km) = 0.0658, gauss = 2.5, ray (s/km) = 0.0698, gauss = 1.0, ray (s/km) = 0.0665, gauss = 1.0, ray (s/km) = 0.0698, gauss = 2.5, ray (s/km) = 0.0746, gauss = 1.0, ray (s/km) = 0.076, gauss = 1.0, ray (s/km) = 0.069, gauss = 2.5, ray (s/km) = 0.0687, gauss = 2.5, ray (s/km) = 0.0698, gauss = 2.5, ray (s/km) = 0.0746, gauss = 1.0, ray (s/km) = 0.0705, gauss = 2.5, ray (s/km) = 0.069, gauss = 1.0, ray (s/km) = 0.0724, gauss = 1.0, ray (s/km) = 0.0658, gauss = 1.0, ray (s/km) = 0.0713, gauss = 1.0, ray (s/km) = 0.0724, gauss = 2.5, ray (s/km) = 0.0687, gauss = 1.0, ray (s/km) = 0.0665, gauss = 2.5, ray (s/km) = 0.0716, gauss = 2.5, ray (s/km) = 0.0787, gauss = 1.0, ray (s/km) = 0.07, gauss = 1.0, ray (s/km) = 0.0732, gauss = 1.0, ray (s/km) = 0.0724, gauss = 1.0, ray (s/km) = 0.0704, gauss = 1.0, ray (s/km) = 0.0724, gauss = 2.5, ray (s/km) = 0.0698, gauss = 1.0, ray (s/km) = 0.0746, gauss = 2.5, ray (s/km) = 0.0739, gauss = 2.5, ray (s/km) = 0.0739, gauss = 1.0, ray (s/km) = 0.0738, gauss = 2.5, ray (s/km) = 0.0716, gauss = 1.0, ray (s/km) = 0.0751, gauss = 2.5, ray (s/km) = 0.076, gauss = 2.5, ray (s/km) = 0.07, gauss = 2.5
array([[<Axes: title={'center': 'ray (s/km) = 0.0716, gauss = 1.0'}, ylabel='Amplitude'>,
        <Axes: title={'center': 'ray (s/km) = 0.0751, gauss = 1.0'}>,
        <Axes: title={'center': 'ray (s/km) = 0.0705, gauss = 1.0'}>],
       [<Axes: title={'center': 'ray (s/km) = 0.0713, gauss = 2.5'}, ylabel='Amplitude'>,
        <Axes: title={'center': 'ray (s/km) = 0.0738, gauss = 1.0'}>,
        <Axes: title={'center': 'ray (s/km) = 0.0658, gauss = 2.5'}>],
       [<Axes: title={'center': 'ray (s/km) = 0.0698, gauss = 1.0'}, ylabel='Amplitude'>,
        <Axes: title={'center': 'ray (s/km) = 0.0665, gauss = 1.0'}>,
        <Axes: title={'center': 'ray (s/km) = 0.0698, gauss = 2.5'}>],
       [<Axes: title={'center': 'ray (s/km) = 0.0746, gauss = 1.0'}, ylabel='Amplitude'>,
        <Axes: title={'center': 'ray (s/km) = 0.076, gauss = 1.0'}>,
        <Axes: title={'center': 'ray (s/km) = 0.069, gauss = 2.5'}>],
       [<Axes: title={'center': 'ray (s/km) = 0.0687, gauss = 2.5'}, ylabel='Amplitude'>,
        <Axes: title={'center': 'ray (s/km) = 0.0698, gauss = 2.5'}>,
        <Axes: title={'center': 'ray (s/km) = 0.0746, gauss = 1.0'}>],
       [<Axes: title={'center': 'ray (s/km) = 0.0705, gauss = 2.5'}, ylabel='Amplitude'>,
        <Axes: title={'center': 'ray (s/km) = 0.069, gauss = 1.0'}>,
        <Axes: title={'center': 'ray (s/km) = 0.0724, gauss = 1.0'}>],
       [<Axes: title={'center': 'ray (s/km) = 0.0658, gauss = 1.0'}, ylabel='Amplitude'>,
        <Axes: title={'center': 'ray (s/km) = 0.0713, gauss = 1.0'}>,
        <Axes: title={'center': 'ray (s/km) = 0.0724, gauss = 2.5'}>],
       [<Axes: title={'center': 'ray (s/km) = 0.0687, gauss = 1.0'}, ylabel='Amplitude'>,
        <Axes: title={'center': 'ray (s/km) = 0.0665, gauss = 2.5'}>,
        <Axes: title={'center': 'ray (s/km) = 0.0716, gauss = 2.5'}>],
       [<Axes: title={'center': 'ray (s/km) = 0.0787, gauss = 1.0'}, ylabel='Amplitude'>,
        <Axes: title={'center': 'ray (s/km) = 0.07, gauss = 1.0'}>,
        <Axes: title={'center': 'ray (s/km) = 0.0732, gauss = 1.0'}>],
       [<Axes: title={'center': 'ray (s/km) = 0.0724, gauss = 1.0'}, ylabel='Amplitude'>,
        <Axes: title={'center': 'ray (s/km) = 0.0704, gauss = 1.0'}>,
        <Axes: title={'center': 'ray (s/km) = 0.0724, gauss = 2.5'}>],
       [<Axes: title={'center': 'ray (s/km) = 0.0698, gauss = 1.0'}, ylabel='Amplitude'>,
        <Axes: title={'center': 'ray (s/km) = 0.0746, gauss = 2.5'}>,
        <Axes: title={'center': 'ray (s/km) = 0.0739, gauss = 2.5'}>],
       [<Axes: title={'center': 'ray (s/km) = 0.0739, gauss = 1.0'}, ylabel='Amplitude'>,
        <Axes: title={'center': 'ray (s/km) = 0.0738, gauss = 2.5'}>,
        <Axes: title={'center': 'ray (s/km) = 0.0716, gauss = 1.0'}>],
       [<Axes: title={'center': 'ray (s/km) = 0.0751, gauss = 2.5'}, xlabel='Times (s)', ylabel='Amplitude'>,
        <Axes: title={'center': 'ray (s/km) = 0.076, gauss = 2.5'}, xlabel='Times (s)'>,
        <Axes: title={'center': 'ray (s/km) = 0.07, gauss = 2.5'}, xlabel='Times (s)'>]],
      dtype=object)
# stacking the data into gauss=1.0 and gauss=2.5 groups
all_rf_field_dobs_1_0 = []
all_rf_field_dobs_2_5 = []
ray_params_1_0 = []
ray_params_2_5 = []
for gauss, ray_param, dobs in zip(all_gauss, all_ray_params, all_rf_field_dobs):
    if gauss == 1:
        all_rf_field_dobs_1_0.append(dobs)
        ray_params_1_0.append(ray_param)
    else:
        all_rf_field_dobs_2_5.append(dobs)
        ray_params_2_5.append(ray_param)

rf_field_dobs_1_0 = np.mean(all_rf_field_dobs_1_0, axis=0)
rf_field_dobs_2_5 = np.mean(all_rf_field_dobs_2_5, axis=0)
ray_param_1_0 = np.mean(ray_params_1_0)
ray_param_2_5 = np.mean(ray_params_2_5)
_, axes = plt.subplots(2, 1, figsize=(5,4))

axes[0].set_xlim(-5, 20)
axes[1].set_xlim(-5, 20)

plot_rf_data(
    rf_field_dobs_1_0,
    rf_field_times,
    title=f"ray (s/km) = {ray_param_1_0}, gauss = 1.0",
    ax=axes[0]
)
axes[0].set_ylabel("Amplitude")
plot_rf_data(
    rf_field_dobs_2_5,
    rf_field_times,
    title=f"ray (s/km) = {ray_param_2_5}, gauss = 2.5",
    ax=axes[1]
)
axes[1].set_ylabel("Amplitude")
plt.tight_layout()
ray (s/km) = 0.07180454545454547, gauss = 1.0, ray (s/km) = 0.07124705882352941, gauss = 2.5

Reference good model

file_end_mod = "../../data/sw_rf_joint/data/JOINT/end.mod"

with open(file_end_mod, "r") as file:
    lines = file.readlines()
    ref_good_model = []
    for line in lines[12:]:
        row = line.strip().split()
        ref_good_model.append([float(row[0]), float(row[2])])

ref_good_model = np.array(ref_good_model)
ref_good_model = form_layercake_model(ref_good_model[:-1,0], ref_good_model[:,1])
_, ax = plt.subplots(figsize=(4,6))
plot_model(ref_good_model, ax=ax, alpha=1);
model
<Axes: title={'center': 'model'}, xlabel='Vs (km/s)', ylabel='Depth (km)'>

Optimisation#

n_dims = 29

init_thicknesses = np.ones((n_dims//2,)) * 10
init_vs = np.ones((n_dims//2+1,)) * 4.0
init_model = form_layercake_model(init_thicknesses, init_vs)
my_reg = cofi.utils.QuadraticReg(
    weighting_matrix="damping",
    model_shape=(n_dims,),
    reference_model=init_model
)
def my_objective(model, fwd_funcs, d_obs_list, lamda=1.0):
    data_misfit = 0
    for (fwd, fwd_kwargs), d_obs in zip(fwd_funcs, d_obs_list):
        d_pred = fwd(model, **fwd_kwargs)
        data_misfit += np.sum((d_obs - d_pred) ** 2)
    reg = my_reg(model)
    return data_misfit + lamda * reg

def my_objective_gradient(model, fwd_funcs, d_obs_list, lamda=1.0):
    data_misfit_grad = 0
    for (fwd, fwd_kwargs), d_obs in zip(fwd_funcs, d_obs_list):
        d_pred = fwd(model, **fwd_kwargs)
        jac = jacobian(model, len(d_obs), fwd, fwd_kwargs)
        data_misfit_grad += -2 * jac.T @ (d_obs - d_pred)
    reg_grad = my_reg.gradient(model)
    return data_misfit_grad + lamda * reg_grad

def my_objective_hessian(model, fwd_funcs, d_obs_list, lamda=1.0):
    data_misfit_hess = 0
    for (fwd, fwd_kwargs), d_obs in zip(fwd_funcs, d_obs_list):
        jac = jacobian(model, len(d_obs), fwd, fwd_kwargs)
        data_misfit_hess += 2 * jac.T @ jac
    reg_hess = my_reg.hessian(model)
    return data_misfit_hess + lamda * reg_hess
fwd_funcs = [
    (forward_sw_interp, {"periods": field_d_periods}),
    (forward_rf_interp, {"gauss": 1, "ray_param": ray_param_1_0}),
    (forward_rf_interp, {"gauss": 2.5, "ray_param": ray_param_2_5})
]

d_obs_list = [rayleigh_field_d_obs, rf_field_dobs_1_0, rf_field_dobs_2_5]

Optimisation with no damping#

lamda = 0

kwargs = {
    "fwd_funcs": fwd_funcs,
    "d_obs_list": d_obs_list,
    "lamda": lamda
}
joint_field_problem_no_reg = cofi.BaseProblem()
joint_field_problem_no_reg.set_objective(my_objective, kwargs=kwargs)
joint_field_problem_no_reg.set_gradient(my_objective_gradient, kwargs=kwargs)
joint_field_problem_no_reg.set_hessian(my_objective_hessian, kwargs=kwargs)
joint_field_problem_no_reg.set_initial_model(init_model)

Define ``InversionOptions``

inv_options_optimiser = cofi.InversionOptions()
inv_options_optimiser.set_tool("scipy.optimize.minimize")
inv_options_optimiser.set_params(method="trust-exact")

Define ``Inversion`` and run

inv_optimiser_no_reg_field = cofi.Inversion(joint_field_problem_no_reg, inv_options_optimiser)
inv_res_optimiser_field_no_reg = inv_optimiser_no_reg_field.run()

Plot results

_, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4), gridspec_kw={'width_ratios': [1, 2.5]})

ax1.set_ylim(100)

plot_model(inv_res_optimiser_field_no_reg.model, ax=ax1, color="green", alpha=1,
           label="model inverted from field data")
plot_model(ref_good_model, ax=ax1, color="red", alpha=1,
           label="reference good model")
plot_model(init_model, ax=ax1, alpha=1, lw=1.5, color="purple",
           label="initial model for damped solution")

field_d_periods_logspace = np.logspace(
    np.log(np.min(field_d_periods)),
    np.log(np.max(field_d_periods+1)),
    60,
    base=np.e,
)

plot_sw_data(rayleigh_field_d_obs, field_d_periods, ax=ax2, color="orange", s=5, scatter=True,
             label="d_obs")
plot_sw_data(forward_sw_interp(inv_res_optimiser_field_no_reg.model, field_d_periods_logspace),
             field_d_periods_logspace, ax=ax2, color="green",
             label="d_pred from inverted model")
plot_sw_data(forward_sw_interp(ref_good_model, field_d_periods_logspace),
             field_d_periods_logspace, ax=ax2, color="red",
             label="d_pred from reference good model")
plot_sw_data(forward_sw_interp(init_model, field_d_periods_logspace),
             field_d_periods_logspace, ax=ax2,
             alpha=1, lw=1.5, linestyle="--", color="purple",
             label="d_pred from initial model")

ax1.legend(loc="lower center", bbox_to_anchor=(0.5, -0.4))
ax2.legend(loc="lower center", bbox_to_anchor=(0.5, -0.46));
model, surface wave data
<matplotlib.legend.Legend object at 0x7fd0a8e9a9d0>
all_rf_dpred = []
all_rf_dpred_init_m = []

for gauss, ray_param in zip(all_gauss, all_ray_params):
    dpred = forward_rf_interp(inv_res_optimiser_field_no_reg.model, gauss, ray_param)
    all_rf_dpred.append(dpred)
    dpred_init_m = forward_rf_interp(init_model, gauss, ray_param)
    all_rf_dpred_init_m.append(dpred_init_m)

axes = plot_rf_field_data(all_rf_dpred, all_gauss, all_ray_params, rf_field_times,
                          color="darkblue", linestyle="dashed",
                          label="d_pred from inverted model")
plot_rf_field_data(all_rf_dpred_init_m, all_gauss, all_ray_params, rf_field_times,
                   axes=axes, color="gray",
                   label="d_pred from starting model")
plot_rf_field_data(all_rf_field_dobs, all_gauss, all_ray_params, rf_field_times,
                   axes=axes, color="brown", linestyle="dashed",
                   label="d_obs")

axes[-1,-1].legend(loc="lower center", bbox_to_anchor=(0.5, -2.5));
ray (s/km) = 0.0716, gauss = 1.0, ray (s/km) = 0.0751, gauss = 1.0, ray (s/km) = 0.0705, gauss = 1.0, ray (s/km) = 0.0713, gauss = 2.5, ray (s/km) = 0.0738, gauss = 1.0, ray (s/km) = 0.0658, gauss = 2.5, ray (s/km) = 0.0698, gauss = 1.0, ray (s/km) = 0.0665, gauss = 1.0, ray (s/km) = 0.0698, gauss = 2.5, ray (s/km) = 0.0746, gauss = 1.0, ray (s/km) = 0.076, gauss = 1.0, ray (s/km) = 0.069, gauss = 2.5, ray (s/km) = 0.0687, gauss = 2.5, ray (s/km) = 0.0698, gauss = 2.5, ray (s/km) = 0.0746, gauss = 1.0, ray (s/km) = 0.0705, gauss = 2.5, ray (s/km) = 0.069, gauss = 1.0, ray (s/km) = 0.0724, gauss = 1.0, ray (s/km) = 0.0658, gauss = 1.0, ray (s/km) = 0.0713, gauss = 1.0, ray (s/km) = 0.0724, gauss = 2.5, ray (s/km) = 0.0687, gauss = 1.0, ray (s/km) = 0.0665, gauss = 2.5, ray (s/km) = 0.0716, gauss = 2.5, ray (s/km) = 0.0787, gauss = 1.0, ray (s/km) = 0.07, gauss = 1.0, ray (s/km) = 0.0732, gauss = 1.0, ray (s/km) = 0.0724, gauss = 1.0, ray (s/km) = 0.0704, gauss = 1.0, ray (s/km) = 0.0724, gauss = 2.5, ray (s/km) = 0.0698, gauss = 1.0, ray (s/km) = 0.0746, gauss = 2.5, ray (s/km) = 0.0739, gauss = 2.5, ray (s/km) = 0.0739, gauss = 1.0, ray (s/km) = 0.0738, gauss = 2.5, ray (s/km) = 0.0716, gauss = 1.0, ray (s/km) = 0.0751, gauss = 2.5, ray (s/km) = 0.076, gauss = 2.5, ray (s/km) = 0.07, gauss = 2.5
<matplotlib.legend.Legend object at 0x7fd0a7b35ed0>

Optimal damping#

lambdas = np.logspace(-6, 6, 15)

my_lcurve_problems = []
for lamb in lambdas:
    my_problem = cofi.BaseProblem()
    kwargs = {
        "fwd_funcs": fwd_funcs,
        "d_obs_list": d_obs_list,
        "lamda": lamb
    }
    my_problem.set_objective(my_objective, kwargs=kwargs)
    my_problem.set_gradient(my_objective_gradient, kwargs=kwargs)
    my_problem.set_hessian(my_objective_hessian, kwargs=kwargs)
    my_problem.set_initial_model(init_model)
    my_lcurve_problems.append(my_problem)

def my_callback(inv_result, i):
    m = inv_result.model
    res_norm = 0
    for (fwd, fwd_kwargs), d_obs in zip(fwd_funcs, d_obs_list):
        d_pred = fwd(m, **fwd_kwargs)
        res_norm += np.sum((d_obs - d_pred) ** 2)
    reg_norm = np.sqrt(my_reg(m))
    print(f"Finished inversion with lambda={lambdas[i]}: {res_norm}, {reg_norm}")
    return res_norm, reg_norm

my_inversion_pool = cofi.utils.InversionPool(
    list_of_inv_problems=my_lcurve_problems,
    list_of_inv_options=inv_options_optimiser,
    callback=my_callback,
    parallel=False
)
all_res, all_cb_returns = my_inversion_pool.run()

l_curve_points = list(zip(*all_cb_returns))
Finished inversion with lambda=1e-06: 4.850830956077422, 4.931520932065487
Finished inversion with lambda=7.196856730011514e-06: 4.850102297614024, 4.908422375008429
Finished inversion with lambda=5.1794746792312125e-05: 4.8532326183548715, 4.866231329874795
Finished inversion with lambda=0.0003727593720314938: 4.852399932988693, 3.2966050933879467
Finished inversion with lambda=0.0026826957952797246: 4.824349841819513, 3.764783668804037
Finished inversion with lambda=0.019306977288832496: 4.882199476895455, 1.9281149915994273
Finished inversion with lambda=0.1389495494373136: 4.930650381577375, 1.7211011175832287
Finished inversion with lambda=1.0: 5.361647931548996, 1.4439410574698817
Finished inversion with lambda=7.196856730011514: 9.115079958605529, 1.0172235109842522
Finished inversion with lambda=51.79474679231202: 26.641506906812104, 0.36817412478412337
Finished inversion with lambda=372.7593720314938: 39.779906394496614, 0.0634702062187113
Finished inversion with lambda=2682.6957952797275: 42.41613741580188, 0.009169567101298428
Finished inversion with lambda=19306.977288832455: 42.805535385853844, 0.0012795524999554082
Finished inversion with lambda=138949.5494373136: 42.86001467903106, 0.00017794969834396122
Finished inversion with lambda=1000000.0: 42.867547117899804, 2.4727298323562565e-05
# print all the lambdas
lambdas
array([1.00000000e-06, 7.19685673e-06, 5.17947468e-05, 3.72759372e-04,
       2.68269580e-03, 1.93069773e-02, 1.38949549e-01, 1.00000000e+00,
       7.19685673e+00, 5.17947468e+01, 3.72759372e+02, 2.68269580e+03,
       1.93069773e+04, 1.38949549e+05, 1.00000000e+06])

Plot L-curve

# plot the L-curve
res_norm, reg_norm = l_curve_points
plt.plot(reg_norm, res_norm, '.-')
plt.xlabel(r'Norm of regularization term $||Wm||_2$')
plt.ylabel(r'Norm of residual $||g(m)-d||_2$')
for i in range(0, len(lambdas), 2):
    plt.annotate(f'{lambdas[i]:.1e}', (reg_norm[i], res_norm[i]), fontsize=8)
surface wave receiver function joint

Optimisation with damping#

lamda = 1

kwargs = {
    "fwd_funcs": fwd_funcs,
    "d_obs_list": d_obs_list,
    "lamda": lamda
}
joint_field_problem = cofi.BaseProblem()
joint_field_problem.set_objective(my_objective, kwargs=kwargs)
joint_field_problem.set_gradient(my_objective_gradient, kwargs=kwargs)
joint_field_problem.set_hessian(my_objective_hessian, kwargs=kwargs)
joint_field_problem.set_initial_model(init_model)

Define ``Inversion`` and run

inv_optimiser_field = cofi.Inversion(joint_field_problem, inv_options_optimiser)
inv_res_optimiser_field = inv_optimiser_field.run()

Plot results

_, (ax1, ax2) = plt.subplots(1, 2, figsize=(10, 4), gridspec_kw={'width_ratios': [1, 2.5]})

ax1.set_ylim(100)

plot_model(inv_res_optimiser_field.model, ax=ax1, color="green", alpha=1,
           label="model inverted from field data")
plot_model(ref_good_model, ax=ax1, color="red", alpha=1,
           label="reference good model")
plot_model(init_model, ax=ax1, alpha=1, lw=1.5, color="purple",
           label="initial model for damped solution")

field_d_periods_logspace = np.logspace(
    np.log(np.min(field_d_periods)),
    np.log(np.max(field_d_periods+1)),
    60,
    base=np.e,
)

plot_sw_data(rayleigh_field_d_obs, field_d_periods, ax=ax2, color="orange", s=5, scatter=True,
             label="d_obs")
plot_sw_data(forward_sw_interp(inv_res_optimiser_field.model, field_d_periods_logspace),
             field_d_periods_logspace, ax=ax2, color="green",
             label="d_pred from inverted model")
plot_sw_data(forward_sw_interp(ref_good_model, field_d_periods_logspace),
             field_d_periods_logspace, ax=ax2, color="red",
             label="d_pred from reference good model")
plot_sw_data(forward_sw_interp(init_model, field_d_periods_logspace),
             field_d_periods_logspace, ax=ax2,
             alpha=1, lw=1.5, linestyle="--", color="purple",
             label="d_pred from initial model")

ax1.legend(loc="lower center", bbox_to_anchor=(0.5, -0.4))
ax2.legend(loc="lower center", bbox_to_anchor=(0.5, -0.46));
model, surface wave data
<matplotlib.legend.Legend object at 0x7fd0a7671b90>
all_rf_dpred = []
all_rf_dpred_init_m = []

for gauss, ray_param in zip(all_gauss, all_ray_params):
    dpred = forward_rf_interp(inv_res_optimiser_field.model, gauss, ray_param)
    all_rf_dpred.append(dpred)
    dpred_init_m = forward_rf_interp(init_model, gauss, ray_param)
    all_rf_dpred_init_m.append(dpred_init_m)

axes = plot_rf_field_data(all_rf_dpred, all_gauss, all_ray_params, rf_field_times,
                          color="darkblue", linestyle="dashed",
                          label="d_pred from inverted model")
plot_rf_field_data(all_rf_dpred_init_m, all_gauss, all_ray_params, rf_field_times,
                   axes=axes, color="gray",
                   label="d_pred from starting model")
plot_rf_field_data(all_rf_field_dobs, all_gauss, all_ray_params, rf_field_times,
                   axes=axes, color="brown", linestyle="dashed",
                   label="d_obs")

axes[-1,-1].legend(loc="lower center", bbox_to_anchor=(0.5, -2.5));
ray (s/km) = 0.0716, gauss = 1.0, ray (s/km) = 0.0751, gauss = 1.0, ray (s/km) = 0.0705, gauss = 1.0, ray (s/km) = 0.0713, gauss = 2.5, ray (s/km) = 0.0738, gauss = 1.0, ray (s/km) = 0.0658, gauss = 2.5, ray (s/km) = 0.0698, gauss = 1.0, ray (s/km) = 0.0665, gauss = 1.0, ray (s/km) = 0.0698, gauss = 2.5, ray (s/km) = 0.0746, gauss = 1.0, ray (s/km) = 0.076, gauss = 1.0, ray (s/km) = 0.069, gauss = 2.5, ray (s/km) = 0.0687, gauss = 2.5, ray (s/km) = 0.0698, gauss = 2.5, ray (s/km) = 0.0746, gauss = 1.0, ray (s/km) = 0.0705, gauss = 2.5, ray (s/km) = 0.069, gauss = 1.0, ray (s/km) = 0.0724, gauss = 1.0, ray (s/km) = 0.0658, gauss = 1.0, ray (s/km) = 0.0713, gauss = 1.0, ray (s/km) = 0.0724, gauss = 2.5, ray (s/km) = 0.0687, gauss = 1.0, ray (s/km) = 0.0665, gauss = 2.5, ray (s/km) = 0.0716, gauss = 2.5, ray (s/km) = 0.0787, gauss = 1.0, ray (s/km) = 0.07, gauss = 1.0, ray (s/km) = 0.0732, gauss = 1.0, ray (s/km) = 0.0724, gauss = 1.0, ray (s/km) = 0.0704, gauss = 1.0, ray (s/km) = 0.0724, gauss = 2.5, ray (s/km) = 0.0698, gauss = 1.0, ray (s/km) = 0.0746, gauss = 2.5, ray (s/km) = 0.0739, gauss = 2.5, ray (s/km) = 0.0739, gauss = 1.0, ray (s/km) = 0.0738, gauss = 2.5, ray (s/km) = 0.0716, gauss = 1.0, ray (s/km) = 0.0751, gauss = 2.5, ray (s/km) = 0.076, gauss = 2.5, ray (s/km) = 0.07, gauss = 2.5
<matplotlib.legend.Legend object at 0x7fd0a6cc8f10>

Fixed-dimensional sampling#

Prepare ``BaseProblem`` for fixed-dimensional sampling

thick_min = 3
thick_max = 10
vs_min = 2
vs_max = 5

def my_log_prior(model):
    thicknesses, vs = split_layercake_model(model)
    thicknesses_out_of_bounds = (thicknesses < thick_min) | (thicknesses > thick_max)
    vs_out_of_bounds = (vs < vs_min) | (vs > vs_max)
    if np.any(thicknesses_out_of_bounds) or np.any(vs_out_of_bounds):
        return float("-inf")
    log_prior = - np.log(thick_max - thick_min) * len(thicknesses) \
                - np.log(vs_max - vs_min) * len(vs)
    return log_prior
# inverse data covariance matrix
Cdinv_rayleigh = np.eye(len(rayleigh_field_d_obs)) / (rayleigh_std**2)
Cdinv_rf_1_0 = np.eye(len(rf_field_dobs_1_0)) / (rf_1_0_std**2)
Cdinv_rf_2_5 = np.eye(len(rf_field_dobs_2_5)) / (rf_2_5_std**2)

Cdinv_list = [Cdinv_rayleigh, Cdinv_rf_1_0, Cdinv_rf_2_5]

def my_log_likelihood(
    model,
    fwd_funcs=fwd_funcs,
    d_obs_list=d_obs_list,
    Cdinv_list=Cdinv_list
):
    log_like_sum = 0
    for (fwd, fwd_kwargs), d_obs, Cdinv in zip(fwd_funcs, d_obs_list, Cdinv_list):
        try:
            d_pred = fwd(model, **fwd_kwargs)
        except:
            return float("-inf")
        residual = d_obs - d_pred
        log_like_sum += -0.5 * residual @ (Cdinv @ residual).T
    return log_like_sum
n_walkers = 60

my_walkers_start = np.ones((n_walkers, n_dims)) * inv_res_optimiser_field.model
for i in range(n_walkers):
    my_walkers_start[i,:] += np.random.normal(0, 0.5, n_dims)
joint_field_problem.set_log_prior(my_log_prior)
joint_field_problem.set_log_likelihood(my_log_likelihood)

Define ``InversionOptions``

inv_options_fixed_d_sampling = cofi.InversionOptions()
inv_options_fixed_d_sampling.set_tool("emcee")
inv_options_fixed_d_sampling.set_params(
    nwalkers=n_walkers,
    nsteps=20_000,
    initial_state=my_walkers_start,
    skip_initial_state_check=True,
    progress=True
)

Define ``Inversion`` and run

inv_fixed_d_sampler_field = cofi.Inversion(joint_field_problem, inv_options_fixed_d_sampling)
inv_res_fixed_d_sampler_field = inv_fixed_d_sampler_field.run()
  0%|          | 0/20000 [00:00<?, ?it/s]
  0%|          | 77/20000 [00:00<00:26, 762.28it/s]
  1%|          | 168/20000 [00:00<00:23, 845.50it/s]
  1%|▏         | 258/20000 [00:00<00:22, 868.37it/s]
  2%|▏         | 349/20000 [00:00<00:22, 882.06it/s]
  2%|▏         | 439/20000 [00:00<00:22, 887.54it/s]
  3%|▎         | 530/20000 [00:00<00:21, 892.33it/s]
  3%|▎         | 621/20000 [00:00<00:21, 896.11it/s]
  4%|▎         | 711/20000 [00:00<00:21, 896.73it/s]
  4%|▍         | 802/20000 [00:00<00:21, 899.26it/s]
  4%|▍         | 893/20000 [00:01<00:21, 900.17it/s]
  5%|▍         | 984/20000 [00:01<00:21, 898.94it/s]
  5%|▌         | 1075/20000 [00:01<00:21, 900.10it/s]
  6%|▌         | 1166/20000 [00:01<00:20, 899.26it/s]
  6%|▋         | 1257/20000 [00:01<00:20, 901.00it/s]
  7%|▋         | 1348/20000 [00:01<00:20, 896.48it/s]
  7%|▋         | 1438/20000 [00:01<00:20, 897.23it/s]
  8%|▊         | 1528/20000 [00:01<00:20, 891.87it/s]
  8%|▊         | 1618/20000 [00:01<00:20, 893.16it/s]
  9%|▊         | 1708/20000 [00:01<00:20, 893.50it/s]
  9%|▉         | 1798/20000 [00:02<00:20, 895.21it/s]
  9%|▉         | 1888/20000 [00:02<00:20, 893.71it/s]
 10%|▉         | 1979/20000 [00:02<00:20, 896.05it/s]
 10%|█         | 2069/20000 [00:02<00:20, 894.76it/s]
 11%|█         | 2159/20000 [00:02<00:19, 895.67it/s]
 11%|█         | 2249/20000 [00:02<00:19, 895.41it/s]
 12%|█▏        | 2339/20000 [00:02<00:19, 888.15it/s]
 12%|█▏        | 2430/20000 [00:02<00:19, 892.54it/s]
 13%|█▎        | 2520/20000 [00:02<00:19, 886.84it/s]
 13%|█▎        | 2609/20000 [00:02<00:19, 886.28it/s]
 14%|█▎        | 2700/20000 [00:03<00:19, 891.58it/s]
 14%|█▍        | 2791/20000 [00:03<00:19, 894.97it/s]
 14%|█▍        | 2881/20000 [00:03<00:19, 896.32it/s]
 15%|█▍        | 2971/20000 [00:03<00:18, 896.68it/s]
 15%|█▌        | 3061/20000 [00:03<00:18, 897.33it/s]
 16%|█▌        | 3151/20000 [00:03<00:18, 894.60it/s]
 16%|█▌        | 3241/20000 [00:03<00:18, 892.96it/s]
 17%|█▋        | 3331/20000 [00:03<00:18, 892.45it/s]
 17%|█▋        | 3422/20000 [00:03<00:18, 895.71it/s]
 18%|█▊        | 3512/20000 [00:03<00:18, 894.33it/s]
 18%|█▊        | 3603/20000 [00:04<00:18, 896.94it/s]
 18%|█▊        | 3693/20000 [00:04<00:18, 897.85it/s]
 19%|█▉        | 3784/20000 [00:04<00:18, 899.51it/s]
 19%|█▉        | 3875/20000 [00:04<00:17, 900.76it/s]
 20%|█▉        | 3966/20000 [00:04<00:17, 901.17it/s]
 20%|██        | 4057/20000 [00:04<00:17, 900.04it/s]
 21%|██        | 4148/20000 [00:04<00:17, 899.66it/s]
 21%|██        | 4238/20000 [00:04<00:17, 898.32it/s]
 22%|██▏       | 4329/20000 [00:04<00:17, 898.90it/s]
 22%|██▏       | 4419/20000 [00:04<00:17, 896.33it/s]
 23%|██▎       | 4509/20000 [00:05<00:17, 895.94it/s]
 23%|██▎       | 4599/20000 [00:05<00:17, 896.70it/s]
 23%|██▎       | 4689/20000 [00:05<00:17, 895.74it/s]
 24%|██▍       | 4779/20000 [00:05<00:17, 893.10it/s]
 24%|██▍       | 4869/20000 [00:05<00:16, 892.98it/s]
 25%|██▍       | 4960/20000 [00:05<00:16, 895.86it/s]
 25%|██▌       | 5051/20000 [00:05<00:16, 898.17it/s]
 26%|██▌       | 5141/20000 [00:05<00:16, 897.55it/s]
 26%|██▌       | 5232/20000 [00:05<00:16, 899.28it/s]
 27%|██▋       | 5322/20000 [00:05<00:16, 894.30it/s]
 27%|██▋       | 5412/20000 [00:06<00:16, 892.38it/s]
 28%|██▊       | 5502/20000 [00:06<00:16, 886.42it/s]
 28%|██▊       | 5591/20000 [00:06<00:16, 884.47it/s]
 28%|██▊       | 5680/20000 [00:06<00:16, 870.87it/s]
 29%|██▉       | 5769/20000 [00:06<00:16, 876.10it/s]
 29%|██▉       | 5857/20000 [00:06<00:16, 872.55it/s]
 30%|██▉       | 5947/20000 [00:06<00:16, 878.20it/s]
 30%|███       | 6035/20000 [00:06<00:16, 872.33it/s]
 31%|███       | 6124/20000 [00:06<00:15, 875.07it/s]
 31%|███       | 6213/20000 [00:06<00:15, 877.12it/s]
 32%|███▏      | 6303/20000 [00:07<00:15, 882.42it/s]
 32%|███▏      | 6392/20000 [00:07<00:15, 881.48it/s]
 32%|███▏      | 6481/20000 [00:07<00:15, 882.40it/s]
 33%|███▎      | 6571/20000 [00:07<00:15, 884.64it/s]
 33%|███▎      | 6660/20000 [00:07<00:15, 882.59it/s]
 34%|███▍      | 6750/20000 [00:07<00:14, 885.41it/s]
 34%|███▍      | 6839/20000 [00:07<00:14, 884.04it/s]
 35%|███▍      | 6928/20000 [00:07<00:14, 880.00it/s]
 35%|███▌      | 7017/20000 [00:07<00:14, 879.62it/s]
 36%|███▌      | 7105/20000 [00:07<00:14, 876.18it/s]
 36%|███▌      | 7193/20000 [00:08<00:14, 868.95it/s]
 36%|███▋      | 7280/20000 [00:08<00:14, 866.12it/s]
 37%|███▋      | 7369/20000 [00:08<00:14, 873.19it/s]
 37%|███▋      | 7457/20000 [00:08<00:14, 875.11it/s]
 38%|███▊      | 7547/20000 [00:08<00:14, 881.65it/s]
 38%|███▊      | 7637/20000 [00:08<00:13, 886.95it/s]
 39%|███▊      | 7726/20000 [00:08<00:13, 885.00it/s]
 39%|███▉      | 7815/20000 [00:08<00:13, 886.26it/s]
 40%|███▉      | 7904/20000 [00:08<00:13, 877.15it/s]
 40%|███▉      | 7994/20000 [00:08<00:13, 883.51it/s]
 40%|████      | 8083/20000 [00:09<00:13, 884.53it/s]
 41%|████      | 8172/20000 [00:09<00:13, 872.49it/s]
 41%|████▏     | 8261/20000 [00:09<00:13, 875.02it/s]
 42%|████▏     | 8351/20000 [00:09<00:13, 882.18it/s]
 42%|████▏     | 8442/20000 [00:09<00:13, 888.18it/s]
 43%|████▎     | 8531/20000 [00:09<00:12, 887.11it/s]
 43%|████▎     | 8622/20000 [00:09<00:12, 891.48it/s]
 44%|████▎     | 8713/20000 [00:09<00:12, 894.06it/s]
 44%|████▍     | 8803/20000 [00:09<00:12, 891.47it/s]
 44%|████▍     | 8893/20000 [00:10<00:12, 885.37it/s]
 45%|████▍     | 8984/20000 [00:10<00:12, 890.20it/s]
 45%|████▌     | 9074/20000 [00:10<00:12, 893.02it/s]
 46%|████▌     | 9164/20000 [00:10<00:12, 893.74it/s]
 46%|████▋     | 9254/20000 [00:10<00:12, 893.56it/s]
 47%|████▋     | 9344/20000 [00:10<00:11, 895.08it/s]
 47%|████▋     | 9434/20000 [00:10<00:11, 896.50it/s]
 48%|████▊     | 9524/20000 [00:10<00:11, 896.02it/s]
 48%|████▊     | 9614/20000 [00:10<00:11, 891.25it/s]
 49%|████▊     | 9704/20000 [00:10<00:11, 888.57it/s]
 49%|████▉     | 9794/20000 [00:11<00:11, 891.10it/s]
 49%|████▉     | 9884/20000 [00:11<00:11, 892.89it/s]
 50%|████▉     | 9974/20000 [00:11<00:11, 893.94it/s]
 50%|█████     | 10064/20000 [00:11<00:11, 877.61it/s]
 51%|█████     | 10152/20000 [00:11<00:11, 870.71it/s]
 51%|█████     | 10240/20000 [00:11<00:11, 868.10it/s]
 52%|█████▏    | 10329/20000 [00:11<00:11, 873.51it/s]
 52%|█████▏    | 10419/20000 [00:11<00:10, 878.39it/s]
 53%|█████▎    | 10508/20000 [00:11<00:10, 879.92it/s]
 53%|█████▎    | 10597/20000 [00:11<00:10, 870.20it/s]
 53%|█████▎    | 10685/20000 [00:12<00:10, 872.64it/s]
 54%|█████▍    | 10776/20000 [00:12<00:10, 881.55it/s]
 54%|█████▍    | 10866/20000 [00:12<00:10, 885.52it/s]
 55%|█████▍    | 10955/20000 [00:12<00:10, 884.71it/s]
 55%|█████▌    | 11044/20000 [00:12<00:10, 868.15it/s]
 56%|█████▌    | 11132/20000 [00:12<00:10, 871.00it/s]
 56%|█████▌    | 11222/20000 [00:12<00:10, 877.47it/s]
 57%|█████▋    | 11312/20000 [00:12<00:09, 882.37it/s]
 57%|█████▋    | 11402/20000 [00:12<00:09, 885.57it/s]
 57%|█████▋    | 11491/20000 [00:12<00:09, 870.43it/s]
 58%|█████▊    | 11579/20000 [00:13<00:09, 863.01it/s]
 58%|█████▊    | 11666/20000 [00:13<00:09, 856.12it/s]
 59%|█████▉    | 11753/20000 [00:13<00:09, 858.99it/s]
 59%|█████▉    | 11841/20000 [00:13<00:09, 864.38it/s]
 60%|█████▉    | 11931/20000 [00:13<00:09, 873.93it/s]
 60%|██████    | 12019/20000 [00:13<00:09, 875.41it/s]
 61%|██████    | 12108/20000 [00:13<00:08, 879.62it/s]
 61%|██████    | 12197/20000 [00:13<00:08, 880.17it/s]
 61%|██████▏   | 12286/20000 [00:13<00:08, 877.97it/s]
 62%|██████▏   | 12374/20000 [00:13<00:08, 873.06it/s]
 62%|██████▏   | 12463/20000 [00:14<00:08, 875.13it/s]
 63%|██████▎   | 12551/20000 [00:14<00:08, 858.28it/s]
 63%|██████▎   | 12639/20000 [00:14<00:08, 863.19it/s]
 64%|██████▎   | 12726/20000 [00:14<00:08, 864.98it/s]
 64%|██████▍   | 12816/20000 [00:14<00:08, 874.58it/s]
 65%|██████▍   | 12906/20000 [00:14<00:08, 881.83it/s]
 65%|██████▍   | 12995/20000 [00:14<00:07, 883.53it/s]
 65%|██████▌   | 13085/20000 [00:14<00:07, 885.41it/s]
 66%|██████▌   | 13174/20000 [00:14<00:07, 881.25it/s]
 66%|██████▋   | 13263/20000 [00:14<00:07, 879.32it/s]
 67%|██████▋   | 13353/20000 [00:15<00:07, 883.73it/s]
 67%|██████▋   | 13443/20000 [00:15<00:07, 886.84it/s]
 68%|██████▊   | 13532/20000 [00:15<00:07, 886.04it/s]
 68%|██████▊   | 13623/20000 [00:15<00:07, 891.36it/s]
 69%|██████▊   | 13713/20000 [00:15<00:07, 891.64it/s]
 69%|██████▉   | 13803/20000 [00:15<00:06, 893.23it/s]
 69%|██████▉   | 13894/20000 [00:15<00:06, 896.34it/s]
 70%|██████▉   | 13985/20000 [00:15<00:06, 897.69it/s]
 70%|███████   | 14075/20000 [00:15<00:06, 884.99it/s]
 71%|███████   | 14164/20000 [00:15<00:06, 874.75it/s]
 71%|███████▏  | 14252/20000 [00:16<00:06, 867.72it/s]
 72%|███████▏  | 14342/20000 [00:16<00:06, 875.39it/s]
 72%|███████▏  | 14432/20000 [00:16<00:06, 881.74it/s]
 73%|███████▎  | 14521/20000 [00:16<00:06, 884.16it/s]
 73%|███████▎  | 14610/20000 [00:16<00:06, 884.54it/s]
 74%|███████▎  | 14700/20000 [00:16<00:05, 887.43it/s]
 74%|███████▍  | 14790/20000 [00:16<00:05, 889.99it/s]
 74%|███████▍  | 14881/20000 [00:16<00:05, 893.61it/s]
 75%|███████▍  | 14971/20000 [00:16<00:05, 894.80it/s]
 75%|███████▌  | 15061/20000 [00:17<00:05, 894.33it/s]
 76%|███████▌  | 15152/20000 [00:17<00:05, 896.33it/s]
 76%|███████▌  | 15243/20000 [00:17<00:05, 897.95it/s]
 77%|███████▋  | 15333/20000 [00:17<00:05, 898.43it/s]
 77%|███████▋  | 15424/20000 [00:17<00:05, 899.61it/s]
 78%|███████▊  | 15514/20000 [00:17<00:04, 898.79it/s]
 78%|███████▊  | 15605/20000 [00:17<00:04, 899.87it/s]
 78%|███████▊  | 15695/20000 [00:17<00:04, 897.58it/s]
 79%|███████▉  | 15786/20000 [00:17<00:04, 898.77it/s]
 79%|███████▉  | 15876/20000 [00:17<00:04, 898.19it/s]
 80%|███████▉  | 15966/20000 [00:18<00:04, 881.15it/s]
 80%|████████  | 16055/20000 [00:18<00:04, 883.21it/s]
 81%|████████  | 16146/20000 [00:18<00:04, 888.72it/s]
 81%|████████  | 16236/20000 [00:18<00:04, 891.32it/s]
 82%|████████▏ | 16326/20000 [00:18<00:04, 879.66it/s]
 82%|████████▏ | 16417/20000 [00:18<00:04, 886.39it/s]
 83%|████████▎ | 16508/20000 [00:18<00:03, 891.04it/s]
 83%|████████▎ | 16599/20000 [00:18<00:03, 894.43it/s]
 83%|████████▎ | 16689/20000 [00:18<00:03, 895.65it/s]
 84%|████████▍ | 16779/20000 [00:18<00:03, 890.76it/s]
 84%|████████▍ | 16869/20000 [00:19<00:03, 891.70it/s]
 85%|████████▍ | 16959/20000 [00:19<00:03, 889.55it/s]
 85%|████████▌ | 17050/20000 [00:19<00:03, 893.77it/s]
 86%|████████▌ | 17140/20000 [00:19<00:03, 894.49it/s]
 86%|████████▌ | 17231/20000 [00:19<00:03, 896.99it/s]
 87%|████████▋ | 17321/20000 [00:19<00:02, 897.20it/s]
 87%|████████▋ | 17412/20000 [00:19<00:02, 899.09it/s]
 88%|████████▊ | 17502/20000 [00:19<00:02, 896.92it/s]
 88%|████████▊ | 17592/20000 [00:19<00:02, 894.62it/s]
 88%|████████▊ | 17682/20000 [00:19<00:02, 885.89it/s]
 89%|████████▉ | 17771/20000 [00:20<00:02, 884.25it/s]
 89%|████████▉ | 17861/20000 [00:20<00:02, 887.51it/s]
 90%|████████▉ | 17950/20000 [00:20<00:02, 885.26it/s]
 90%|█████████ | 18041/20000 [00:20<00:02, 889.65it/s]
 91%|█████████ | 18130/20000 [00:20<00:02, 889.26it/s]
 91%|█████████ | 18220/20000 [00:20<00:01, 890.92it/s]
 92%|█████████▏| 18311/20000 [00:20<00:01, 894.30it/s]
 92%|█████████▏| 18401/20000 [00:20<00:01, 893.92it/s]
 92%|█████████▏| 18492/20000 [00:20<00:01, 896.41it/s]
 93%|█████████▎| 18582/20000 [00:20<00:01, 879.16it/s]
 93%|█████████▎| 18671/20000 [00:21<00:01, 880.01it/s]
 94%|█████████▍| 18761/20000 [00:21<00:01, 884.40it/s]
 94%|█████████▍| 18851/20000 [00:21<00:01, 887.51it/s]
 95%|█████████▍| 18942/20000 [00:21<00:01, 891.47it/s]
 95%|█████████▌| 19032/20000 [00:21<00:01, 891.64it/s]
 96%|█████████▌| 19123/20000 [00:21<00:00, 894.63it/s]
 96%|█████████▌| 19214/20000 [00:21<00:00, 897.02it/s]
 97%|█████████▋| 19304/20000 [00:21<00:00, 888.71it/s]
 97%|█████████▋| 19395/20000 [00:21<00:00, 892.07it/s]
 97%|█████████▋| 19485/20000 [00:21<00:00, 891.95it/s]
 98%|█████████▊| 19575/20000 [00:22<00:00, 893.04it/s]
 98%|█████████▊| 19665/20000 [00:22<00:00, 883.55it/s]
 99%|█████████▉| 19756/20000 [00:22<00:00, 888.44it/s]
 99%|█████████▉| 19845/20000 [00:22<00:00, 882.98it/s]
100%|█████████▉| 19936/20000 [00:22<00:00, 888.82it/s]
100%|██████████| 20000/20000 [00:22<00:00, 887.00it/s]
labels_v = [f"v{i}" for i in range(n_dims//2+1)]
labels_t = [f"t{i}" for i in range(n_dims//2)]
labels = [0] * n_dims
labels[::2] = labels_v
labels[1::2] = labels_t

sampler = inv_res_fixed_d_sampler_field.sampler
az_idata = az.from_emcee(sampler, var_names=labels)
az_idata.get("posterior")
<xarray.Dataset> Size: 279MB
Dimensions:  (chain: 60, draw: 20000)
Coordinates:
  * chain    (chain) int64 480B 0 1 2 3 4 5 6 7 8 ... 51 52 53 54 55 56 57 58 59
  * draw     (draw) int64 160kB 0 1 2 3 4 5 ... 19995 19996 19997 19998 19999
Data variables: (12/29)
    t0       (chain, draw) float64 10MB 8.825 8.825 8.825 ... 10.76 10.76 10.76
    t1       (chain, draw) float64 10MB 9.519 9.519 9.519 ... 10.15 10.15 10.15
    t10      (chain, draw) float64 10MB 9.497 9.497 9.497 ... 9.986 9.986 9.986
    t11      (chain, draw) float64 10MB 9.276 9.276 9.276 ... 9.941 9.941 9.941
    t12      (chain, draw) float64 10MB 9.321 9.321 9.321 ... 10.18 10.18 10.18
    t13      (chain, draw) float64 10MB 9.852 9.852 9.852 9.852 ... 9.6 9.6 9.6
    ...       ...
    v4       (chain, draw) float64 10MB 4.294 4.294 4.294 ... 4.583 4.583 4.583
    v5       (chain, draw) float64 10MB 3.857 3.857 3.857 ... 3.976 3.976 3.976
    v6       (chain, draw) float64 10MB 4.348 4.348 4.348 ... 4.251 4.251 4.251
    v7       (chain, draw) float64 10MB 3.795 3.795 3.795 ... 3.99 3.99 3.99
    v8       (chain, draw) float64 10MB 3.971 3.971 3.971 ... 4.79 4.79 4.79
    v9       (chain, draw) float64 10MB 4.003 4.003 4.003 ... 4.633 4.633 4.633
Attributes:
    created_at:                 2024-04-19T05:01:44.415903+00:00
    arviz_version:              0.18.0
    inference_library:          emcee
    inference_library_version:  3.1.5


flat_samples = sampler.get_chain(discard=10_000, thin=500, flat=True)
rand_indices = np.random.randint(len(flat_samples), size=100)

gs = gridspec.GridSpec(2, 3, width_ratios=[1, 2, 2])
fig = plt.figure(figsize=(12, 5))
ax1 = fig.add_subplot(gs[:, 0])
ax2 = fig.add_subplot(gs[:, 1])
ax3 = fig.add_subplot(gs[0, 2])
ax4 = fig.add_subplot(gs[1, 2])

ax1.set_ylim(100)
ax3.set_xlim(-5, 20)
ax3.set_ylim(-0.2, 0.6)
ax4.set_xlim(-5, 20)
ax4.set_ylim(-0.4, 1.2)

# plot samples and data predictions from samples
for idx in rand_indices:
    sample = flat_samples[idx]
    plot_model(sample, ax=ax1, alpha=0.2, lw=0.5, color="gray")
    plot_sw_data(forward_sw_interp(sample, field_d_periods_logspace),
                 field_d_periods_logspace,
                 ax=ax2, alpha=0.2, lw=0.5, color="gray")
    plot_rf_data(forward_rf_interp(sample, 1, ray_param_1_0), rf_field_times,
                 ax=ax3, alpha=0.2, lw=0.5, color="gray")
    plot_rf_data(forward_rf_interp(sample, 2.5, ray_param_2_5), rf_field_times,
                 ax=ax4, alpha=0.2, lw=0.5, color="gray")

# add labels to samples
sample_0 = flat_samples[rand_indices[0]]
plot_model(sample_0, ax=ax1, alpha=0.5, lw=0.5, color="gray", label="samples")
plot_sw_data(forward_sw_interp(sample_0, field_d_periods_logspace),
             field_d_periods_logspace, ax=ax2,
             alpha=0.5, lw=0.5, color="gray", label="rayleigh_dpred from samples")
plot_rf_data(forward_rf_interp(sample_0, 1, ray_param_1_0), rf_field_times, ax=ax3,
             alpha=0.5, lw=0.5, color="gray", label="rf_dpred from samples")
plot_rf_data(forward_rf_interp(sample_0, 2.5, ray_param_2_5), rf_field_times, ax=ax4,
             alpha=0.5, lw=0.5, color="gray", label="rf_dpred from samples")

# plot reference good model and data observations
plot_model(ref_good_model, ax=ax1, alpha=1, color="r", label="reference good model")
plot_sw_data(rayleigh_field_d_obs, field_d_periods, ax=ax2, scatter=True, color="r", s=4,
          label="rayleigh_dobs")
plot_rf_data(rf_field_dobs_1_0, rf_field_times, ax=ax3, scatter=True, color="r", s=2,
          label="rf_dobs")
plot_rf_data(rf_field_dobs_2_5, rf_field_times, ax=ax4, scatter=True, color="r", s=2,
          label="rf_dobs")

# plot damped optimisation result
plot_model(inv_res_optimiser_field.model, ax=ax1, alpha=1, color="green",
           label="damped solution")
plot_sw_data(forward_sw_interp(inv_res_optimiser_field.model, field_d_periods_logspace),
             field_d_periods_logspace, ax=ax2, color="green",
             label="rayleigh_dpred from damped solution")
plot_rf_data(forward_rf_interp(inv_res_optimiser_field.model, 1, ray_param_1_0),
             rf_field_times, ax=ax3, color="green",
             label="rf_dpred from damped solution")
plot_rf_data(forward_rf_interp(inv_res_optimiser_field.model, 2.5, ray_param_2_5),
             rf_field_times, ax=ax4, color="green",
             label="rf_dpred from damped solution")

# plot initial model for dampied optimsiation
plot_model(init_model, ax=ax1, alpha=1, color="purple",
           label="initial model for damped solution")
plot_sw_data(forward_sw_interp(init_model, field_d_periods_logspace),
             field_d_periods_logspace, ax=ax2, color="purple",
             label="rayleigh_dpred from initial model for damped solution")
plot_rf_data(forward_rf_interp(init_model, 1, ray_param_1_0), rf_field_times,
             ax=ax3, color="purple",
             label="rf_dpred from initial model for damped solution")
plot_rf_data(forward_rf_interp(init_model, 2.5, ray_param_2_5), rf_field_times,
             ax=ax4, color="purple",
             label="rf_dpred from initial model for damped solution")

ax1.legend(loc="upper center", bbox_to_anchor=(0.5, -0.18))
ax2.legend(loc="upper center", bbox_to_anchor=(0.5, -0.18))
ax4.legend(loc="upper center", bbox_to_anchor=(0.5, -0.6))
ax1.grid()
ax2.grid()
ax3.grid()
ax4.grid()
ax3.set_title("receiver function data (ray=1.0)")
ax4.set_title("receiver function data (ray=2.5)")

plt.tight_layout()
model, surface wave data, receiver function data (ray=1.0), receiver function data (ray=2.5)

Trans-dimensional sampling#

Prepare utilities for trans-d sampling

def fwd_for_bayesbay(state, fwd_func, **kwargs):
    vs = state["voronoi"]["vs"]
    voronoi_sites = state["voronoi"]["discretization"]
    depths = (voronoi_sites[:-1] + voronoi_sites[1:]) / 2
    thicknesses = depths - np.insert(depths[:-1], 0, 0)
    model = form_layercake_model(thicknesses, vs)
    return fwd_func(model, **kwargs)
targets = [
    bayesbay.Target("rayleigh", rayleigh_field_d_obs, covariance_mat_inv=1/rayleigh_std**2),
    bayesbay.Target("rf_1_0", rf_field_dobs_1_0, covariance_mat_inv=1/rf_1_0_std**2),
    bayesbay.Target("rf_2_5", rf_field_dobs_2_5, covariance_mat_inv=1/rf_2_5_std**2)
]
forward_funcs = [
    (fwd_for_bayesbay, {"fwd_func": forward_sw_interp, "periods": field_d_periods}),
    (fwd_for_bayesbay, {"fwd_func": forward_rf_interp, "gauss": 1, "ray_param": ray_param_1_0}),
    (fwd_for_bayesbay, {"fwd_func": forward_rf_interp, "gauss": 2.5, "ray_param": ray_param_2_5})
]

my_log_likelihood = bayesbay.LogLikelihood(targets, forward_funcs)
param_vs = bayesbay.prior.UniformPrior(
    name="vs",
    vmin=[2.7, 3.2, 3.75],
    vmax=[4, 4.75, 5],
    position=[0, 40, 80],
    perturb_std=0.15
)

def param_vs_initialize(param, positions):
    vmin, vmax = param.get_vmin_vmax(positions)
    sorted_vals = np.sort(np.random.uniform(vmin, vmax, positions.size))
    for i in range (len(sorted_vals)):
        val = sorted_vals[i]
        vmin_i = vmin if np.isscalar(vmin) else vmin[i]
        vmax_i = vmax if np.isscalar(vmax) else vmax[i]
        if val < vmin_i or val > vmax_i:
            if val > vmax_i: sorted_vals[i] = vmax_i
            if val < vmin_i: sorted_vals[i] = vmin_i
    return sorted_vals

param_vs.set_custom_initialize(param_vs_initialize)
parameterization = bayesbay.parameterization.Parameterization(
    bayesbay.discretization.Voronoi1D(
        name="voronoi",
        vmin=0,
        vmax=150,
        perturb_std=10,
        n_dimensions=None,
        n_dimensions_min=4,
        n_dimensions_max=15,
        parameters=[param_vs],
    )
)
my_perturbation_funcs = parameterization.perturbation_functions
n_chains=12
walkers_start = []
for i in range(n_chains):
    walkers_start.append(parameterization.initialize())

Define ``InversionOptions``

inv_options_trans_d = cofi.InversionOptions()
inv_options_trans_d.set_tool("bayesbay")
inv_options_trans_d.set_params(
    walkers_starting_states=walkers_start,
    perturbation_funcs=my_perturbation_funcs,
    log_like_ratio_func=my_log_likelihood,
    n_chains=n_chains,
    n_iterations=2_000,
    burnin_iterations=1_000,
    verbose=False,
    save_every=200,
)

Define ``Inversion`` and run

inv_trans_d_sampler_field = cofi.Inversion(joint_field_problem, inv_options_trans_d)
inv_res_trans_d_sampler_field = inv_trans_d_sampler_field.run()
saved_states = inv_res_trans_d_sampler_field.models
samples_voronoi = saved_states["voronoi.discretization"]
samples_vs = saved_states["voronoi.vs"]
interp_depths = np.arange(150, dtype=float)
rand_indices = np.random.randint(len(samples_voronoi), size=100)

gs = gridspec.GridSpec(2, 3, width_ratios=[1, 2, 2])
fig = plt.figure(figsize=(12, 5))
ax1 = fig.add_subplot(gs[:, 0])
ax2 = fig.add_subplot(gs[:, 1])
ax3 = fig.add_subplot(gs[0, 2])
ax4 = fig.add_subplot(gs[1, 2])

ax1.set_ylim(100)
ax3.set_xlim(-5, 20)
ax3.set_ylim(-0.2, 0.6)
ax4.set_xlim(-5, 20)
ax4.set_ylim(-0.4, 1.2)

# plot samples and data predictions from samples
for idx in rand_indices:
    sample_voronoi = form_voronoi_model(samples_voronoi[idx], samples_vs[idx])
    sample = voronoi_to_layercake(sample_voronoi)
    plot_model(sample, ax=ax1, alpha=0.2, lw=0.5, color="gray")
    plot_sw_data(forward_sw_interp(sample, field_d_periods_logspace),
                 field_d_periods_logspace,
                 ax=ax2, alpha=0.2, lw=0.5, color="gray")
    plot_rf_data(forward_rf_interp(sample, 1, ray_param_1_0), rf_field_times,
                 ax=ax3, alpha=0.2, lw=0.5, color="gray")
    plot_rf_data(forward_rf_interp(sample, 2.5, ray_param_2_5), rf_field_times,
                 ax=ax4, alpha=0.2, lw=0.5, color="gray")

# add labels to samples
sample_0_voronoi = form_voronoi_model(samples_voronoi[0], samples_vs[0])
sample_0 = voronoi_to_layercake(sample_0_voronoi)
plot_model(sample_0, ax=ax1, alpha=0.5, lw=0.5, color="gray", label="samples")
plot_sw_data(forward_sw_interp(sample_0, field_d_periods_logspace),
             field_d_periods_logspace, ax=ax2,
             alpha=0.5, lw=0.5, color="gray", label="rayleigh_dpred from samples")
plot_rf_data(forward_rf_interp(sample_0, 1, ray_param_1_0), rf_field_times, ax=ax3,
             alpha=0.5, lw=0.5, color="gray", label="rf_dpred from samples")
plot_rf_data(forward_rf_interp(sample_0, 2.5, ray_param_2_5), rf_field_times, ax=ax4,
             alpha=0.5, lw=0.5, color="gray", label="rf_dpred from samples")

# plot reference good model and data observations
plot_model(ref_good_model, ax=ax1, alpha=1, color="r", label="reference good model")
plot_sw_data(rayleigh_field_d_obs, field_d_periods, ax=ax2, scatter=True, color="r", s=4,
          label="rayleigh_dobs")
plot_rf_data(rf_field_dobs_1_0, rf_field_times, ax=ax3, scatter=True, color="r", s=2,
          label="rf_dobs")
plot_rf_data(rf_field_dobs_2_5, rf_field_times, ax=ax4, scatter=True, color="r", s=2,
          label="rf_dobs")

# plot damped optimisation result
plot_model(inv_res_optimiser_field.model, ax=ax1, alpha=1, color="green",
           label="damped solution")
plot_sw_data(forward_sw_interp(inv_res_optimiser_field.model, field_d_periods_logspace),
             field_d_periods_logspace, ax=ax2, color="green",
             label="rayleigh_dpred from damped solution")
plot_rf_data(forward_rf_interp(inv_res_optimiser_field.model, 1, ray_param_1_0),
             rf_field_times, ax=ax3, color="green",
             label="rf_dpred from damped solution")
plot_rf_data(forward_rf_interp(inv_res_optimiser_field.model, 2.5, ray_param_2_5),
             rf_field_times, ax=ax4, color="green",
             label="rf_dpred from damped solution")

# plot initial model for dampied optimsiation
plot_model(init_model, ax=ax1, alpha=1, color="purple",
           label="initial model for damped solution")
plot_sw_data(forward_sw_interp(init_model, field_d_periods_logspace),
             field_d_periods_logspace, ax=ax2, color="purple",
             label="rayleigh_dpred from initial model for damped solution")
plot_rf_data(forward_rf_interp(init_model, 1, ray_param_1_0), rf_field_times,
             ax=ax3, color="purple",
             label="rf_dpred from initial model for damped solution")
plot_rf_data(forward_rf_interp(init_model, 2.5, ray_param_2_5), rf_field_times,
             ax=ax4, color="purple",
             label="rf_dpred from initial model for damped solution")

ax1.legend(loc="upper center", bbox_to_anchor=(0.5, -0.18))
ax2.legend(loc="upper center", bbox_to_anchor=(0.5, -0.18))
ax4.legend(loc="upper center", bbox_to_anchor=(0.5, -0.6))
ax1.grid()
ax2.grid()
ax3.grid()
ax4.grid()
ax3.set_title("receiver function data (ray=1.0)")
ax4.set_title("receiver function data (ray=2.5)")

plt.tight_layout()
model, surface wave data, receiver function data (ray=1.0), receiver function data (ray=2.5)

Watermark#

watermark_list = ["cofi", "espresso", "numpy", "matplotlib", "scipy", "bayesbay"]
for pkg in watermark_list:
    pkg_var = __import__(pkg)
    print(pkg, getattr(pkg_var, "__version__"))
cofi 0.2.8
espresso 0.3.13
numpy 1.26.4
matplotlib 3.8.3
scipy 1.12.0
bayesbay 0.3.0

sphinx_gallery_thumbnail_number = -1

Total running time of the script: (2 minutes 23.430 seconds)

Gallery generated by Sphinx-Gallery