Rayleigh Waves Synthetic Cross-correlation Functions¶
## Distributed python packages
import os
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
import xarray as xr
import datetime
import pandas as pd
import time as ttime
from cartopy import crs as ccrs
from cartopy import feature as cfeature
from math import radians, log
from wmsan.synthetics import *
%matplotlib inline
__author__ = "Lisa Tomasetto"
__copyright__ = "Copyright 2024, UGA"
__credits__ = ["Lisa Tomasetto"]
__version__ = "0.1"
__maintainer__ = "Lisa Tomasetto"
__email__ = "lisa.tomasetto@univ-grenoble-alpes.fr"
__status__ = "Developpement"
Make Nice Plots¶
plt.style.use("ggplot")
SMALL_SIZE = 18
MEDIUM_SIZE = 22
BIGGER_SIZE = 24
plt.rc('font', size=SMALL_SIZE) # controls default text sizes
plt.rc('axes', titlesize=SMALL_SIZE) # fontsize of the axes title
plt.rc('axes', labelsize=MEDIUM_SIZE) # fontsize of the x and y labels
plt.rc('xtick', labelsize=SMALL_SIZE) # fontsize of the tick labels
plt.rc('ytick', labelsize=SMALL_SIZE) # fontsize of the tick labels
plt.rc('legend', fontsize=SMALL_SIZE) # legend fontsize
plt.rc('figure', titlesize=BIGGER_SIZE) # fontsize of the figure title
matplotlib.rcParams['pdf.fonttype'] = 42
Theory¶
We will compute synthetic Cross-correlations between vertical components, using the formulation given in Tomasetto et al.(2024). Using the representation theorem and assuming spatially uncorrelated sources one can write the cross-correlation function between sensors A and B as:
$$ C(r_A, r_B, t) = \mathcal{FT}^{-1}\left[ \int_{\partial D} G(r_A, r, f) G^*(r_B, r, f) S(r,f) dr \right] $$
with:
- $G(r_A, r, f)$ the Green's function between a source in $ r $ and station A in $ r_A $. Here computed in AxiSEM.
- $S(r,f)$ the source PSD at position $ r $. Here given by the square of equivalent vertical force amplitude computed in microseismic_sources.ipynb.
- $^*$ denotes the complex conjugate.
- $\partial D$ the domain of potential sources, here, the ocean's surface.
- $\mathcal{FT}^{-1}$ the Fourier transform.
References¶
Create Synthetic Waveforms Database¶
In the following, we will use an AxiSEM archive file of seismic waveforms computed in ak135f model and sampled at 1 Hz.
First we will read and plot the archive.
[WARNING] Please download the archive before running the following cells!
Avalaible from: https://zenodo.org/records/11126562
Save in /ww3-source-maps/data/
## Open Archive
path_file_axisem = '../../data/NOISE_vertforce_dirac_0-ak135f_1.s_3600s.h5'
fe, time, N = get_synthetic_info(path_file_axisem=path_file_axisem)
distance = np.arange(0, 180.1, 0.1)
M = len(distance)
archive = np.zeros((M, N))
for i in range(M):
archive[i,:] = open_axisem(distance[i], path_file_axisem=path_file_axisem)
## Plot archive
plt.figure()
plt.pcolormesh(time, distance, archive,cmap='seismic', vmin=-10, vmax = 10)
plt.colorbar()
plt.xlabel('time')
plt.ylabel('distance')
plt.xlim(0, 3600)
plt.show()
Tapering¶
Then we taper the synthetics waveforms between two given surface waves velocities.
## Taper
Umin = 2.5
Umax = 4.2
file_name_taper = '../../data/NOISE_vertforce_dirac_0-ak135f_1.s_3600_tapered.s.h5'
if os.path.isfile(file_name_taper):
print('Tapered archive already exists')
## Open tapered archive
h5_file = h5py.File(file_name_taper, 'r')
tapered_archive = h5_file['WAVEFORMS'][:]
else:
tapered_archive = taper_axisem_archive(time, distance, archive_name=path_file_axisem, umin = Umin, umax=Umax)
Tapered archive already exists
## Plot Tapered archive
plt.figure(figsize=(10, 5))
plt.pcolormesh(time, distance, archive, cmap='seismic', vmin=-10, vmax = 10)
plt.plot(distance*(np.pi*6371)/180/Umin, distance, 'r')
plt.plot(distance*(np.pi*6371)/180/Umax, distance, 'g')
plt.colorbar()
plt.xlabel('time')
plt.ylabel('distance')
plt.xlim(0, 3600)
plt.savefig('GF_archive.png', dpi=300, bbox_inches='tight')
file_name_spectrum = '../../data/spectrum_archive_tapered.h5'
if os.path.isfile(file_name_spectrum):
print('Spectrum archive already exists')
## Open Spectrum archive
h5_file = h5py.File(file_name_spectrum, 'r')
spectrum_archive = h5_file['SPECTRUM'][:]
frequency = h5_file['frequency'][:]
distance = h5_file['distance'][:]
print(spectrum_archive.shape)
else:
## Create Spectrum archive from the tapered one
create_spectrum_archive(time, distance, tapered_archive)
Spectrum archive already exists (1801, 14400)
## Spatial Exent
lon_min = -30
lon_max = 30
lat_min = 40
lat_max = 80
extent = [lon_min, lon_max, lat_min, lat_max]
Station Pairs and Metadata¶
## Station Pairs Metadata
df = pd.read_csv('../../data/stations_pair.txt', sep=',', header=None, index_col=False, names=['idA', 'idB', 'latA', 'latB', 'lonA', 'lonB'], dtype={'idA':str, 'idB':str, 'latA':float, 'latB':float, 'lonA':float, 'lonB':float}, engine='python')
# ## Plot
fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(111, projection=ccrs.Mollweide())
ax.set_extent(extent, crs=ccrs.PlateCarree())
ax.add_feature(cfeature.COASTLINE)
ax.add_feature(cfeature.BORDERS, linestyle='-', alpha=1)
ax.add_feature(cfeature.LAKES, alpha=0.5)
ax.add_feature(cfeature.RIVERS)
ax.add_feature(cfeature.STATES)
ax.add_feature(cfeature.LAND, color='linen', zorder=0)
ax.add_feature(cfeature.OCEAN, color='lightsteelblue', zorder=0)
for i in range(len(df)):
ax.annotate(df.idA[i], (df.lonA[i]-0.1, df.latA[i]+0.8), transform = ccrs.PlateCarree(), family = 'sans-serif')
ax.annotate(df.idB[i], (df.lonB[i]+0.1, df.latB[i]-0.8), transform = ccrs.PlateCarree(), family = 'sans-serif')
ax.plot((df.lonA[i], df.lonB[i]),(df.latA[i], df.latB[i]), transform = ccrs.PlateCarree(), color='k', alpha=0.5, linewidth=3)
df.plot.scatter(x='lonA', y='latA', s=30, ax=ax, c='r', transform = ccrs.PlateCarree())
df.plot.scatter(x='lonB', y='latB', s=30, ax=ax, c='r', transform = ccrs.PlateCarree())
plt.show()
Dates¶
## Dates
start = datetime(2008, 11, 16, 0, 0, 0)
end = datetime(2008, 11, 19, 23, 59, 59)
dates = pd.date_range(start, end, freq='3h')
dates_vector = create_date_vect(dates)
Open WAVEWATCHIII Equivalent Force Amplitude¶
## Open Model
path_model = './F/'
Open Synthetics Archive¶
## Open GF archive info
fe , freq, distance, spectrum_synth = open_archive('../../data/spectrum_archive_tapered.h5')
N_time = len(time)
Synthetic Cross-correlation Computation¶
## Compute CCF and save it
### Station Pair Info
id_A = df.idA[0]
id_B = df.idB[0]
coords_staA = (df.lonA[0], df.latA[0])
coords_staB = (df.lonB[0], df.latB[0])
#### Keep Correlations
corr_3h = np.zeros((len(dates_vector), N_time))
### Date Info
for i, date_vect in enumerate(dates_vector):
### CCF
filename_ccf = './CCF/ccf_%s_%s_%d%02d%02d%02d.nc'%(id_A, id_B, date_vect[0],date_vect[1],date_vect[2],date_vect[3]) # File Name Understandable
if os.path.exists(filename_ccf): # Check if file already exists
print('%s already computed'%filename_ccf)
corr = xr.open_dataarray(filename_ccf)
else: # if not, compute CCF
t1 = ttime.perf_counter()
corr, time_corr = ccf_computation(coords_staA, coords_staB, path_model, date_vect, spectrum_synth,extent=extent, fe=fe, N=N_time, comp='Z')
t2 = ttime.perf_counter()
print(" Time for station pair: ", id_A, id_B, " elapsed: ", t2-t1)
try:
corr.to_netcdf('./CCF/ccf_%s_%s_%d%02d%02d%02d.nc'%(id_A, id_B, date_vect[0],date_vect[1],date_vect[2],date_vect[3]))
except:
print("make directory")
os.mkdir('./CCF')
corr.to_netcdf('./CCF/ccf_%s_%s_%d%02d%02d%02d.nc'%(id_A, id_B, date_vect[0],date_vect[1],date_vect[2],date_vect[3]))
corr_3h[i, :] = corr.values
# TAKES 25 MIN #
./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111600.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111603.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111606.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111609.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111612.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111615.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111618.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111621.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111700.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111703.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111706.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111709.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111712.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111715.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111718.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111721.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111800.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111803.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111806.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111809.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111812.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111815.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111818.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111821.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111900.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111903.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111906.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111909.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111912.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111915.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111918.nc already computed ./CCF/ccf_XK.LP51.00_XK.SGF.00_2008111921.nc already computed
Plot¶
## Plot stack
plt.figure()
plt.plot(corr.time, np.mean(corr_3h, axis=0), 'k')
plt.xlim(-100, 100)
plt.xlabel('Lag Time (s)')
plt.title('%s - %s stack'%(id_A, id_B))
plt.show()
## Plot CCF
normalize_corr = np.nanmax(abs(corr_3h))
dates_corr = dates
fig, ax = plt.subplots(figsize=(10,8))
im = plt.pcolormesh(corr.time, np.arange(len(dates_vector)), corr_3h/normalize_corr, cmap='seismic', vmin= -0.8, vmax = 0.8)
#ax.plot(corr.time, corr_3h.T/normalize_corr + np.arange(len(dates_vector)), 'k')
plt.colorbar(im)
ax.set_xlabel('Lag Time (s)')
ax.set_ylabel('Date')
ax.set_title('%s-%s CCF'%(id_A, id_B))
ax.set_xlim(-100, 100)
ax.set_ylim(0, len(dates_vector))
ax.set_yticks(np.arange(len(dates_vector))[::8])
ax.set_yticklabels(dates[::8].strftime('%Y-%m-%dT%H'))
plt.savefig('ccf_%s_%s_synth.png'%(id_A, id_B), dpi=300, bbox_inches='tight')