import pandas as pd import matplotlib.pyplot as plt import numpy as np import scipy import random import sys # Written by Boaz Nadler, April/2023 # Demo Code for algorithm developed in # "A statistical approach to estimate seismic monitoring # station biases and error levels # By Y. Radzyner, M. Galun and B. Nadler # Variable notations below are same as in paper N = 10; # N is total number of monitoring stations M = 50000; # M is total number of seismic events y_min = 4; # minimal and maximal generated seismic magnitude strength y_max = 8; n_min = 2; # minimal number of reporting stations for an event n_max = max(7,N-4) #maximal number of reporting stations for an event bias_min = -0.4 bias_max = 0.4 error_min = 0.2 error_max = 0.5 y = np.zeros(M); # this is the array of the true event strengths station_bias = np.zeros(N); # array of station biases station_std = np.zeros(N); # station magnitude error levels X = np.zeros((N,M)) # this will store the observed station magnitudes # generate random data y = np.random.uniform(y_min, y_max, size=M) # actual (true) magnitude events station_bias = np.random.uniform(bias_min,bias_max,size=N) station_std = np.random.uniform(error_min,error_max,size=N) # normalize so that sum of station biases to be zero bias_sum = np.sum(station_bias) station_bias = station_bias - bias_sum/N # generate station data in matrix X for i in np.arange(M): num_reporting_stations = np.random.randint(n_min,n_max) b = np.random.permutation(N) rep_station = b[0:num_reporting_stations] #print(i,' ',b, ' ',num_reporting_stations,' ',reporting_stations) mag_error = station_bias[rep_station] + station_std[rep_station]*np.random.normal(0,1,num_reporting_stations) X[rep_station,i] = y[i] + mag_error #print(i,' ',y[i],' ',X[i,:]) # compute matrices M, D, V as defined in our paper # M[i,j] = number of events reported by both stations i and j # D[i,j] = mean of difference in magnitude of events reported by stations i and j # V[i,j] = variance of magnitude difference in magnitude between stations i and j V = np.zeros((N,N)) D = np.zeros((N,N)) M = np.zeros((N,N)) for i in np.arange(N): idx_i = np.nonzero(X[i,:]) # list of events that station i reported for j in np.arange(i+1,N): idx_j = np.nonzero(X[j,:]) idx_ij = np.intersect1d(idx_i,idx_j,assume_unique=True) #events jointly detected by stations i and j #print(i,' ',j,' ',idx_ij) V[i,j] = np.var(X[i,idx_ij] - X[j,idx_ij]) D[i,j] = np.mean(X[i,idx_ij] - X[j,idx_ij]) D[j,i] = -D[i,j] # D[i,j] = difference in magnitudes between stations i and j V[j,i] = V[i,j] M[i,j] = idx_ij.size #number of events reported by both stations i and j M[j,i] = M[i,j] # function that estimates the stations squared error levels def estimate_station_error_level(M,V): N = len(M) # print("Inside estimate_station_error_level N=",N) # construct matrix A for error level estimation A = np.zeros((N,N)) rhs= np.zeros(N) # least squares for station magnitude error levels A = M.copy(); for i in np.arange(N): rhs[i] = np.sum( np.multiply(V[i,:],M[i,:]) ) A[i,i] = np.sum(M[i,:]) st_var_estimate = np.linalg.inv(A).dot(rhs) #This is the estimate of the magnitude error variance return st_var_estimate # function that estimates the station biases, under normalization of sum equal zero def estimate_station_bias(M,D,st_var_estimate): N = len(M) # least squares for station biases B = np.zeros((N,N)) rhs = np.zeros(N) for k in np.arange(N): sigma2_kj = st_var_estimate[k]+st_var_estimate # this is sigma_k^2+sigma_j^2 M_times_D = np.multiply( M[k,:],D[k,:] ) # vector with entries M[k,j]* D[k,j] rhs[k] = - np.sum( np.divide( M_times_D,sigma2_kj) ) B[k,k] = - np.sum( np.divide(M[k,:] , sigma2_kj) ) for j in np.arange(k+1,N): B[k,j] = M[k,j] / sigma2_kj[j] B[j,k] = B[k,j] # linear system is rank deficient, compute least norm solution bias_estimate, res, rnk, s = scipy.linalg.lstsq(B,rhs) sum_bias = np.sum(bias_estimate) bias_estimate = bias_estimate - sum_bias/N return bias_estimate st_var_estimate = estimate_station_error_level(M,V) st_std_estimate = np.sqrt(st_var_estimate) bias_estimate = estimate_station_bias(M,D,st_var_estimate) print('Estimated Station Biases:') print(bias_estimate) print('True Station Biases:') print(station_bias) print('Difference:') print(bias_estimate-station_bias) plt.figure(1) plt.clf() plt.plot(station_std,st_std_estimate,'ro') plt.plot([error_min, error_max],[error_min, error_max],'k-' ) plt.grid(True) plt.xlabel('magnitude error level $\sigma$') plt.ylabel('estimated error level') plt.show() plt.figure(2) plt.clf() plt.plot(station_bias,bias_estimate,'ro') plt.plot([bias_min, bias_max],[bias_min, bias_max],'k-' ) plt.grid(True) plt.xlabel('station bias $b$') plt.ylabel('estimated station bias') plt.show()