Part I: Rayleigh

import bayesbay as bb
from bayesbay.discretization import Voronoi1D
import numpy as np
import matplotlib.pyplot as plt
from disba import PhaseDispersion
np.random.seed(30)
THICKNESS = np.array([10, 10, 15, 20, 20, 20, 20, 20, 0])
VS = np.array([3.38, 3.44, 3.66, 4.25, 4.35, 4.32, 4.315, 4.38, 4.5])
VP_VS = 1.77
VP = VS * VP_VS
RHO = 0.32 * VP + 0.77
PERIODS = np.geomspace(3, 80, 20)
RAYLEIGH_STD = 0.02
def initialize_vs(param, positions=None):
    vmin, vmax = param.get_vmin_vmax(positions)
    sorted_vals = np.sort(np.random.uniform(vmin, vmax, positions.size))
    return sorted_vals

vs = bb.prior.UniformPrior(name="vs", 
                                    vmin=[2.2, 2.8, 3.3, 4], 
                                    vmax=[3.9, 4.6, 4.8, 5], 
                                    position=[0, 20, 60, 150],
                                    perturb_std=0.15)
vs.set_custom_initialize(initialize_vs)
pd = PhaseDispersion(THICKNESS, VP, VS, RHO)
phase_vel = pd(PERIODS, mode=0, wave="rayleigh").velocity
d_obs = phase_vel + np.random.normal(0, RAYLEIGH_STD, phase_vel.size)
# For plotting the prior
depth_prior = np.linspace(0, 200)
vmin_prior, vmax_prior = vs.get_vmin_vmax(depth_prior)

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(9, 4), gridspec_kw={'width_ratios': [1, 2.5]})
ax1.fill_betweenx(depth_prior, vmin_prior, vmax_prior, alpha=0.2, label='Prior')
Voronoi1D.plot_tessellation(THICKNESS, 
                            VS, 
                            label='True Vs', 
                            ax=ax1, 
                            color='k', 
                            lw=2, 
                            input_type='extents')
ax1.set_xlabel('Vs [km/s]')
ax1.set_ylabel('Depth [km]')
ax1.set_ylim(np.cumsum(THICKNESS)[-1] + max(THICKNESS), 0)
ax1.grid()
ax1.legend()

ax2.plot(PERIODS, phase_vel, 'r--', label='Rayleigh (true Vs)', lw=0.5)
ax2.plot(PERIODS, d_obs, 'ro', label='Rayleigh obs.')
ax2.set_xlabel('Period [s]')
ax2.set_ylabel('Phase velocity [km/s]')
ax2.grid()
ax2.legend()

plt.tight_layout(w_pad=2)
plt.show()
../_images/f4bb87fb64957a8091282c5b8dce3b995a57505d1c741f5c58f2d7abc1e6107b.png
voronoi = Voronoi1D(
    name="voronoi", 
    vmin=0,
    vmax=150,
    perturb_std=10,
    n_dimensions=None, 
    n_dimensions_min=4,
    n_dimensions_max=15,
    parameters=[vs], 
    birth_from='neighbour'
)
parameterization = bb.parameterization.Parameterization(voronoi)
def forward_sw(state):
    voronoi = state["voronoi"]
    voronoi_sites = voronoi["discretization"]
    thickness = Voronoi1D.compute_cell_extents(voronoi_sites)
    vs = voronoi["vs"]
    vp = vs * VP_VS
    rho = 0.32 * vp + 0.77
    pd = PhaseDispersion(thickness, vp, vs, rho)
    d_pred = pd(PERIODS, mode=0, wave="rayleigh").velocity
    return d_pred
target = bb.Target("rayleigh", 
                   d_obs, 
                   std_min=0.001, 
                   std_max=0.1, 
                   std_perturb_std=0.002)
log_likelihood = bb.LogLikelihood(targets=target, fwd_functions=forward_sw)

inversion = bb.BayesianInversion(
    parameterization=parameterization, 
    log_likelihood=log_likelihood,
    n_chains=20
)
inversion.run(
    sampler=None, 
    n_iterations=350_000, 
    burnin_iterations=75_000, 
    save_every=150,
    verbose=False
)
for chain in inversion.chains:
    chain.print_statistics()
Chain ID: 0
TEMPERATURE: 1
EXPLORED MODELS: 350000
ACCEPTANCE RATE: 157575/350000 (45.02 %)
PARTIAL ACCEPTANCE RATES:
	NoisePerturbation(rayleigh): 42611/49927 (85.35%)
	ParamSpacePerturbation(param_space_name=voronoi):
		BirthPerturbation(voronoi): 10045.00/49686.00 (20.22%)
		DeathPerturbation(voronoi): 10038.00/50036.00 (20.06%)
		ParamPerturbation(voronoi.discretization): 23767.00/49894.00 (47.63%)
		ParamPerturbation(voronoi.vs): 71114.00/150457.00 (47.27%)
Chain ID: 1
TEMPERATURE: 1
EXPLORED MODELS: 350000
ACCEPTANCE RATE: 158312/350000 (45.23 %)
PARTIAL ACCEPTANCE RATES:
	NoisePerturbation(rayleigh): 42762/49912 (85.67%)
	ParamSpacePerturbation(param_space_name=voronoi):
		BirthPerturbation(voronoi): 10253.00/49649.00 (20.65%)
		DeathPerturbation(voronoi): 10245.00/50535.00 (20.27%)
		ParamPerturbation(voronoi.discretization): 23743.00/49854.00 (47.63%)
		ParamPerturbation(voronoi.vs): 71309.00/150050.00 (47.52%)
Chain ID: 2
TEMPERATURE: 1
EXPLORED MODELS: 350000
ACCEPTANCE RATE: 156074/350000 (44.59 %)
PARTIAL ACCEPTANCE RATES:
	NoisePerturbation(rayleigh): 42757/50123 (85.30%)
	ParamSpacePerturbation(param_space_name=voronoi):
		BirthPerturbation(voronoi): 10095.00/49908.00 (20.23%)
		DeathPerturbation(voronoi): 10091.00/50041.00 (20.17%)
		ParamPerturbation(voronoi.discretization): 23725.00/50175.00 (47.28%)
		ParamPerturbation(voronoi.vs): 69406.00/149753.00 (46.35%)
Chain ID: 3
TEMPERATURE: 1
EXPLORED MODELS: 350000
ACCEPTANCE RATE: 157139/350000 (44.90 %)
PARTIAL ACCEPTANCE RATES:
	NoisePerturbation(rayleigh): 42713/49971 (85.48%)
	ParamSpacePerturbation(param_space_name=voronoi):
		BirthPerturbation(voronoi): 9963.00/49944.00 (19.95%)
		DeathPerturbation(voronoi): 9960.00/49988.00 (19.92%)
		ParamPerturbation(voronoi.discretization): 23490.00/50377.00 (46.63%)
		ParamPerturbation(voronoi.vs): 71013.00/149720.00 (47.43%)
Chain ID: 4
TEMPERATURE: 1
EXPLORED MODELS: 350000
ACCEPTANCE RATE: 157877/350000 (45.11 %)
PARTIAL ACCEPTANCE RATES:
	NoisePerturbation(rayleigh): 42717/50089 (85.28%)
	ParamSpacePerturbation(param_space_name=voronoi):
		BirthPerturbation(voronoi): 9938.00/49792.00 (19.96%)
		DeathPerturbation(voronoi): 9929.00/49667.00 (19.99%)
		ParamPerturbation(voronoi.discretization): 23948.00/50342.00 (47.57%)
		ParamPerturbation(voronoi.vs): 71345.00/150110.00 (47.53%)
Chain ID: 5
TEMPERATURE: 1
EXPLORED MODELS: 350000
ACCEPTANCE RATE: 157169/350000 (44.91 %)
PARTIAL ACCEPTANCE RATES:
	NoisePerturbation(rayleigh): 42668/50059 (85.24%)
	ParamSpacePerturbation(param_space_name=voronoi):
		BirthPerturbation(voronoi): 10315.00/50251.00 (20.53%)
		DeathPerturbation(voronoi): 10310.00/49852.00 (20.68%)
		ParamPerturbation(voronoi.discretization): 23940.00/50049.00 (47.83%)
		ParamPerturbation(voronoi.vs): 69936.00/149789.00 (46.69%)
Chain ID: 6
TEMPERATURE: 1
EXPLORED MODELS: 350000
ACCEPTANCE RATE: 156444/350000 (44.70 %)
PARTIAL ACCEPTANCE RATES:
	NoisePerturbation(rayleigh): 42234/49696 (84.98%)
	ParamSpacePerturbation(param_space_name=voronoi):
		BirthPerturbation(voronoi): 10014.00/49954.00 (20.05%)
		DeathPerturbation(voronoi): 10016.00/50122.00 (19.98%)
		ParamPerturbation(voronoi.discretization): 23593.00/50362.00 (46.85%)
		ParamPerturbation(voronoi.vs): 70587.00/149866.00 (47.10%)
Chain ID: 7
TEMPERATURE: 1
EXPLORED MODELS: 350000
ACCEPTANCE RATE: 154587/350000 (44.17 %)
PARTIAL ACCEPTANCE RATES:
	NoisePerturbation(rayleigh): 42702/50076 (85.27%)
	ParamSpacePerturbation(param_space_name=voronoi):
		BirthPerturbation(voronoi): 9982.00/49995.00 (19.97%)
		DeathPerturbation(voronoi): 9977.00/49966.00 (19.97%)
		ParamPerturbation(voronoi.discretization): 23187.00/50014.00 (46.36%)
		ParamPerturbation(voronoi.vs): 68739.00/149949.00 (45.84%)
Chain ID: 8
TEMPERATURE: 1
EXPLORED MODELS: 350000
ACCEPTANCE RATE: 156155/350000 (44.62 %)
PARTIAL ACCEPTANCE RATES:
	NoisePerturbation(rayleigh): 42842/50128 (85.47%)
	ParamSpacePerturbation(param_space_name=voronoi):
		BirthPerturbation(voronoi): 9893.00/49898.00 (19.83%)
		DeathPerturbation(voronoi): 9884.00/49822.00 (19.84%)
		ParamPerturbation(voronoi.discretization): 23483.00/50149.00 (46.83%)
		ParamPerturbation(voronoi.vs): 70053.00/150003.00 (46.70%)
Chain ID: 9
TEMPERATURE: 1
EXPLORED MODELS: 350000
ACCEPTANCE RATE: 158024/350000 (45.15 %)
PARTIAL ACCEPTANCE RATES:
	NoisePerturbation(rayleigh): 42519/49801 (85.38%)
	ParamSpacePerturbation(param_space_name=voronoi):
		BirthPerturbation(voronoi): 10198.00/49930.00 (20.42%)
		DeathPerturbation(voronoi): 10198.00/50138.00 (20.34%)
		ParamPerturbation(voronoi.discretization): 23329.00/50250.00 (46.43%)
		ParamPerturbation(voronoi.vs): 71780.00/149881.00 (47.89%)
Chain ID: 10
TEMPERATURE: 1
EXPLORED MODELS: 350000
ACCEPTANCE RATE: 156907/350000 (44.83 %)
PARTIAL ACCEPTANCE RATES:
	NoisePerturbation(rayleigh): 42610/50036 (85.16%)
	ParamSpacePerturbation(param_space_name=voronoi):
		BirthPerturbation(voronoi): 10195.00/49979.00 (20.40%)
		DeathPerturbation(voronoi): 10187.00/49482.00 (20.59%)
		ParamPerturbation(voronoi.discretization): 23983.00/50121.00 (47.85%)
		ParamPerturbation(voronoi.vs): 69932.00/150382.00 (46.50%)
Chain ID: 11
TEMPERATURE: 1
EXPLORED MODELS: 350000
ACCEPTANCE RATE: 156273/350000 (44.65 %)
PARTIAL ACCEPTANCE RATES:
	NoisePerturbation(rayleigh): 43059/50345 (85.53%)
	ParamSpacePerturbation(param_space_name=voronoi):
		BirthPerturbation(voronoi): 10246.00/50172.00 (20.42%)
		DeathPerturbation(voronoi): 10241.00/50219.00 (20.39%)
		ParamPerturbation(voronoi.discretization): 23557.00/49873.00 (47.23%)
		ParamPerturbation(voronoi.vs): 69170.00/149391.00 (46.30%)
Chain ID: 12
TEMPERATURE: 1
EXPLORED MODELS: 350000
ACCEPTANCE RATE: 157690/350000 (45.05 %)
PARTIAL ACCEPTANCE RATES:
	NoisePerturbation(rayleigh): 42874/50089 (85.60%)
	ParamSpacePerturbation(param_space_name=voronoi):
		BirthPerturbation(voronoi): 10175.00/50286.00 (20.23%)
		DeathPerturbation(voronoi): 10165.00/49920.00 (20.36%)
		ParamPerturbation(voronoi.discretization): 23344.00/49883.00 (46.80%)
		ParamPerturbation(voronoi.vs): 71132.00/149822.00 (47.48%)
Chain ID: 13
TEMPERATURE: 1
EXPLORED MODELS: 350000
ACCEPTANCE RATE: 156574/350000 (44.74 %)
PARTIAL ACCEPTANCE RATES:
	NoisePerturbation(rayleigh): 42724/50039 (85.38%)
	ParamSpacePerturbation(param_space_name=voronoi):
		BirthPerturbation(voronoi): 10157.00/50381.00 (20.16%)
		DeathPerturbation(voronoi): 10150.00/49985.00 (20.31%)
		ParamPerturbation(voronoi.discretization): 23531.00/49806.00 (47.25%)
		ParamPerturbation(voronoi.vs): 70012.00/149789.00 (46.74%)
Chain ID: 14
TEMPERATURE: 1
EXPLORED MODELS: 350000
ACCEPTANCE RATE: 156945/350000 (44.84 %)
PARTIAL ACCEPTANCE RATES:
	NoisePerturbation(rayleigh): 42858/50200 (85.37%)
	ParamSpacePerturbation(param_space_name=voronoi):
		BirthPerturbation(voronoi): 9993.00/50107.00 (19.94%)
		DeathPerturbation(voronoi): 9985.00/49906.00 (20.01%)
		ParamPerturbation(voronoi.discretization): 23571.00/49990.00 (47.15%)
		ParamPerturbation(voronoi.vs): 70538.00/149797.00 (47.09%)
Chain ID: 15
TEMPERATURE: 1
EXPLORED MODELS: 350000
ACCEPTANCE RATE: 155274/350000 (44.36 %)
PARTIAL ACCEPTANCE RATES:
	NoisePerturbation(rayleigh): 42797/50264 (85.14%)
	ParamSpacePerturbation(param_space_name=voronoi):
		BirthPerturbation(voronoi): 10126.00/49942.00 (20.28%)
		DeathPerturbation(voronoi): 10127.00/50141.00 (20.20%)
		ParamPerturbation(voronoi.discretization): 23597.00/49780.00 (47.40%)
		ParamPerturbation(voronoi.vs): 68627.00/149873.00 (45.79%)
Chain ID: 16
TEMPERATURE: 1
EXPLORED MODELS: 350000
ACCEPTANCE RATE: 157937/350000 (45.12 %)
PARTIAL ACCEPTANCE RATES:
	NoisePerturbation(rayleigh): 42835/50035 (85.61%)
	ParamSpacePerturbation(param_space_name=voronoi):
		BirthPerturbation(voronoi): 10291.00/50209.00 (20.50%)
		DeathPerturbation(voronoi): 10288.00/49674.00 (20.71%)
		ParamPerturbation(voronoi.discretization): 23801.00/50011.00 (47.59%)
		ParamPerturbation(voronoi.vs): 70722.00/150071.00 (47.13%)
Chain ID: 17
TEMPERATURE: 1
EXPLORED MODELS: 350000
ACCEPTANCE RATE: 160715/350000 (45.92 %)
PARTIAL ACCEPTANCE RATES:
	NoisePerturbation(rayleigh): 42967/50294 (85.43%)
	ParamSpacePerturbation(param_space_name=voronoi):
		BirthPerturbation(voronoi): 10320.00/50209.00 (20.55%)
		DeathPerturbation(voronoi): 10318.00/49934.00 (20.66%)
		ParamPerturbation(voronoi.discretization): 23699.00/49813.00 (47.58%)
		ParamPerturbation(voronoi.vs): 73411.00/149750.00 (49.02%)
Chain ID: 18
TEMPERATURE: 1
EXPLORED MODELS: 350000
ACCEPTANCE RATE: 156671/350000 (44.76 %)
PARTIAL ACCEPTANCE RATES:
	NoisePerturbation(rayleigh): 42509/49964 (85.08%)
	ParamSpacePerturbation(param_space_name=voronoi):
		BirthPerturbation(voronoi): 10131.00/50394.00 (20.10%)
		DeathPerturbation(voronoi): 10123.00/49778.00 (20.34%)
		ParamPerturbation(voronoi.discretization): 23119.00/49653.00 (46.56%)
		ParamPerturbation(voronoi.vs): 70789.00/150211.00 (47.13%)
Chain ID: 19
TEMPERATURE: 1
EXPLORED MODELS: 350000
ACCEPTANCE RATE: 156817/350000 (44.80 %)
PARTIAL ACCEPTANCE RATES:
	NoisePerturbation(rayleigh): 42613/49979 (85.26%)
	ParamSpacePerturbation(param_space_name=voronoi):
		BirthPerturbation(voronoi): 9960.00/49922.00 (19.95%)
		DeathPerturbation(voronoi): 9963.00/50123.00 (19.88%)
		ParamPerturbation(voronoi.discretization): 23339.00/49955.00 (46.72%)
		ParamPerturbation(voronoi.vs): 70942.00/150021.00 (47.29%)
results = inversion.get_results(concatenate_chains=True)

sampled_voronoi_nuclei = results['voronoi.discretization']
sampled_thickness = [Voronoi1D.compute_cell_extents(n) for n in sampled_voronoi_nuclei]
sampled_vs = results['voronoi.vs']
interp_depths = np.linspace(0, 160, 160)
statistics_vs = Voronoi1D.get_tessellation_statistics(
    sampled_thickness, sampled_vs, interp_depths, input_type='extents'
    )

fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(6, 8), gridspec_kw={'width_ratios': [2.5, 1]})
ax1, cbar = Voronoi1D.plot_tessellation_density(sampled_thickness, 
                                                sampled_vs, 
                                                input_type='extents', 
                                                ax=ax1, 
                                                cmap='binary', 
                                                vmin=0, 
                                                vmax=2000)
Voronoi1D.plot_tessellation(THICKNESS, 
                            VS, 
                            ax=ax1, 
                            color='yellow', 
                            lw=3, 
                            label='True Vs',
                            input_type='extents')
ax1.plot(statistics_vs['mean'], interp_depths, 'b', lw=2, label='Vs Ensemble Mean')
ax1.plot(statistics_vs['median'], interp_depths, 'r', lw=2, label='Vs Ensemble Median')
ax1.set_xlabel("Vs [km/s]")
ax1.set_ylabel("Depth [km]")
ax1.legend()

Voronoi1D.plot_interface_hist(sampled_voronoi_nuclei, bins=75, ec='w', ax=ax2)
ax2.tick_params(labelleft=False)
ax2.set_ylabel('')
ax2.set_ylim(*ax1.get_ylim())
plt.tight_layout()
plt.show()
../_images/d0dc9243985eee9a31d0c7b7d3b2f7f2c292377f2bf56078740c3a44e489f228.png
d_pred = np.array(results['rayleigh.dpred'])
percentiles = np.percentile(d_pred, (10, 90), axis=0)

# Predicted dispersion curves
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(6, 6))
ax1.plot(PERIODS, phase_vel, 'r--', label='Rayleigh (true Vs)', lw=0.5)
ax1.plot(PERIODS, d_obs, 'ro', label='Observations')
ax1.fill_between(PERIODS, percentiles[0], percentiles[1], color='r', alpha=0.2, label='Inferred Data (10th-90th percentiles)', zorder=100)
ax1.set_xlabel('Period [s]')
ax1.set_ylabel('Phase velocity [km/s]')
ax1.grid()
ax1.legend()

# Histogram of inferred data noise
ax2.axvline(x=RAYLEIGH_STD, color='r', lw=3, alpha=0.3, label='True data noise')
pdf, bins, _ = ax2.hist(results['rayleigh.std'], density=True, bins=20, ec='w', zorder=100, label='Posterior')
ax2.fill_between([target.std_min, target.std_max], 1 / (target.std_max - target.std_min), alpha=0.2, label='Prior')
ax2.set_xlabel('Noise standard deviation')
ax2.ticklabel_format(style='sci', axis='x', scilimits=(0,0))
ax2.legend(framealpha=0.9)
plt.tight_layout()
plt.show()
../_images/d6151148bda188ca63ff6695ff37a076aaebed886fdcdb01213468769b055288.png
def get_subplot_layout(n_subplots):
    rows = int(np.sqrt(n_subplots))
    cols = int(np.ceil(n_subplots / rows))
    return rows, cols

rows, cols = get_subplot_layout(len(inversion.chains))
fig, axes = plt.subplots(rows, cols, figsize=(15, 15))
for ipanel, (ax, chain) in enumerate(zip(np.ravel(axes), inversion.chains)):
    saved_states = chain.saved_states
    saved_nuclei = saved_states["voronoi.discretization"]
    saved_vs = saved_states['voronoi.vs']
    
    Voronoi1D.plot_tessellations(saved_nuclei,
                                 saved_vs, 
                                 ax=ax, 
                                 linewidth=0.1, 
                                 color="k", 
                                 bounds=(0, 150))
    Voronoi1D.plot_tessellation(THICKNESS, 
                                VS, 
                                color='yellow', 
                                lw=2, 
                                ax=ax,
                                input_type='extents')
    Voronoi1D.plot_tessellation_statistics(
        saved_nuclei, saved_vs, interp_depths, ax=ax
    )
    
    ax.set_title(f'Chain {chain.id}')
    ax.tick_params(direction='in', labelleft=False, labelbottom=False)
    ax.set_xlabel('')
    ax.set_ylabel('')
    
    if not ipanel % cols:
        ax.set_ylabel('Depth [km]')
        ax.tick_params(labelleft=True)
    if ipanel >= (rows-1) * cols:
        ax.set_xlabel('Vs [km/s]')
        ax.tick_params(labelbottom=True)
    
plt.tight_layout()
plt.show()
../_images/ca5edce484897b8fb6da8f0ebe89db7ebbe8e7558a501d80fee8b0f82e564ce8.png