def adsrc_tf_phase_misfit(t, data, synthetic, dt_new, width, threshold, axis=None, colorbar_axis=None): """ :rtype: dictionary :returns: Return a dictionary with three keys: * adjoint_source: The calculated adjoint source as a numpy array * misfit: The misfit value * messages: A list of strings giving additional hints to what happened in the calculation. """ messages = [] # Compute time-frequency representation via cross-correlation tau_cc, nu_cc, tf_cc = time_frequency.time_frequency_cc_difference( t, data, synthetic, dt_new, width, threshold) # Compute the time-frequency representation of the synthetic tau, nu, tf_synth = time_frequency.time_frequency_transform(t, synthetic, dt_new, width, threshold) # 2D interpolation. Use a two step interpolation for the real and the # imaginary parts. tf_cc_interp = RectBivariateSpline(tau_cc[0], nu_cc[:, 0], tf_cc.real, kx=1, ky=1, s=0)(tau[0], nu[:, 0]) tf_cc_interp = np.require(tf_cc_interp, dtype="complex128") tf_cc_interp.imag = RectBivariateSpline(tau_cc[0], nu_cc[:, 0], tf_cc.imag, kx=1, ky=1, s=0)(tau[0], nu[:, 0]) tf_cc = tf_cc_interp # Make window functionality # noise taper m = np.abs(tf_cc).max() / 10.0 weight = 1.0 - np.exp(-(np.abs(tf_cc) ** 2) / (m ** 2)) nu_t = nu.transpose() # high-pass filter weight *= (1.0 - np.exp((-nu_t ** 2) / (0.002 ** 2))) nu_t_large = np.zeros(nu_t.shape) nu_t_small = np.zeros(nu_t.shape) thres = (nu_t <= 0.005) nu_t_large[np.invert(thres)] = 1.0 nu_t_small[thres] = 1.0 # low-pass filter weight *= (np.exp(-(nu_t - 0.005) ** 4 / 0.005 ** 4) * nu_t_large + nu_t_small) # normalisation weight /= weight.max() # Compute the phase difference. DP = np.imag(np.log(eps + tf_cc / (eps + np.abs(tf_cc)))) # Attempt to detect phase jumps by taking the derivatives in time and # frequency direction. 0.7 is an emperical value. test_field = weight * DP / np.abs(weight * DP).max() criterion_1 = np.abs(np.diff(test_field, axis=0)).max() criterion_2 = np.abs(np.diff(test_field, axis=1)).max() criterion = max(criterion_1, criterion_2) if criterion > 0.7: warning = "Possible phase jump detected" warnings.warn(warning) messages.append(warning) # Compute the phase misfit dnu = nu[1, 0] - nu[0, 0] phase_misfit = np.sqrt(np.sum(weight ** 2 * DP ** 2) * dt_new * dnu) # Sanity check. Should not occur. if np.isnan(phase_misfit): msg = "The phase misfit is NaN." raise Exception(msg) # Make kernel for the inverse tf transform idp = weight * weight * DP * tf_synth / (eps + np.abs(tf_synth) * np.abs(tf_synth)) # Invert tf transform and make adjoint source ad_src, it, I = time_frequency.itfa(tau, nu, idp, width, threshold) # Interpolate to original time axis current_time = tau[0, :] new_time = t[t <= current_time.max()] ad_src = interp1d(current_time, np.imag(ad_src), kind=2)(new_time) if len(t) > len(new_time): ad_src = np.concatenate([ad_src, np.zeros(len(t) - len(new_time))]) # Divide by the misfit. ad_src /= (phase_misfit + eps) ad_src = np.diff(ad_src) / (t[1] - t[0]) # Reverse time and add a leading zero so the adjoint source has the same # length as the input time series. ad_src = ad_src[::-1] ad_src = np.concatenate([[0.0], ad_src]) # Plot if required. if axis: import matplotlib.cm as cm import matplotlib.pyplot as plt weighted_phase_difference = (DP * weight).transpose() abs_diff = np.abs(weighted_phase_difference) max_val = abs_diff.max() mappable = axis.pcolormesh(tau, nu, weighted_phase_difference, vmin=-max_val, vmax=max_val, cmap=cm.RdBu_r) axis.set_xlabel("Seconds since event") axis.set_ylabel("TF Phase Misfit: Frequency [Hz]") # Smart scaling for the frequency axis. temp = abs_diff.max(axis=1) * (nu[1] - nu[0]) ymax = len(temp[temp > temp.max() / 1000.0]) ymax *= nu[1, 0] - nu[0, 0] ymax *= 2 axis.set_ylim(0, ymax) if colorbar_axis: cm = plt.gcf().colorbar(mappable, cax=colorbar_axis) else: cm = plt.gcf().colorbar(mappable, ax=axis) cm.set_label("Phase difference in radian") axis.set_title("Weighted phase difference") ax2 = axis.twinx() ax2.plot(t, data, color="black", alpha=1.0) ax2.plot(t, synthetic, color="red", alpha=1.0) min_value = min(data.min(), synthetic.min()) max_value = max(data.max(), synthetic.max()) value_range = max_value - min_value axis.twin_axis = ax2 ax2.set_ylim(min_value - value_range, max_value + 0.05 * value_range) ax2.set_ylabel("Waveforms: Amplitude [m/s]") axis.set_xlim(0, tau[:, -1][-1]) ax2.set_xlim(0, tau[:, -1][-1]) text = "Misfit: %.4f" % phase_misfit axis.text(x=0.99, y=0.02, s=text, transform=axis.transAxes, bbox=dict(facecolor='orange', alpha=0.8), verticalalignment="bottom", horizontalalignment="right") if messages: message = "\n".join(messages) axis.text(x=0.99, y=0.98, s=message, transform=axis.transAxes, bbox=dict(facecolor='red', alpha=0.8), verticalalignment="top", horizontalalignment="right") ret_dict = { "adjoint_source": ad_src, "misfit": phase_misfit, "messages": messages} return ret_dict
def adsrc_tf_phase_misfit(t, data, synthetic, min_period, max_period, axis=None, colorbar_axis=None): """ :rtype: dictionary :returns: Return a dictionary with three keys: * adjoint_source: The calculated adjoint source as a numpy array * misfit: The misfit value * messages: A list of strings giving additional hints to what happened in the calculation. """ messages = [] # Compute time-frequency representations ---------------------------------- # compute new time increments and Gaussian window width for the # time-frequency transforms dt_new = float(int(min_period / 3.0)) width = 2.0 * min_period # Compute time-frequency representation of the cross-correlation tau_cc, nu_cc, tf_cc = time_frequency.time_frequency_cc_difference( t, data, synthetic, dt_new, width) # Compute the time-frequency representation of the synthetic tau, nu, tf_synth = time_frequency.time_frequency_transform(t, synthetic, dt_new, width) # 2D interpolation to bring the tf representation of the correlation on the # same grid as the tf representation of the synthetics. Uses a two-step # procedure for real and imaginary parts. tf_cc_interp = RectBivariateSpline(tau_cc[0], nu_cc[:, 0], tf_cc.real, kx=1, ky=1, s=0)(tau[0], nu[:, 0]) tf_cc_interp = np.require(tf_cc_interp, dtype="complex128") tf_cc_interp.imag = RectBivariateSpline(tau_cc[0], nu_cc[:, 0], tf_cc.imag, kx=1, ky=1, s=0)(tau[0], nu[:, 0]) tf_cc = tf_cc_interp # compute tf window and weighting function -------------------------------- # noise taper: downweigh tf amplitudes that are very low m = np.abs(tf_cc).max() / 10.0 weight = 1.0 - np.exp(-(np.abs(tf_cc) ** 2) / (m ** 2)) nu_t = nu.transpose() # highpass filter (periods longer than max_period are suppressed # exponentially) weight *= (1.0 - np.exp(-(nu_t * max_period) ** 2)) # lowpass filter (periods shorter than min_period are suppressed # exponentially) nu_t_large = np.zeros(nu_t.shape) nu_t_small = np.zeros(nu_t.shape) thres = (nu_t <= 1.0 / min_period) nu_t_large[np.invert(thres)] = 1.0 nu_t_small[thres] = 1.0 weight *= (np.exp(-10.0 * np.abs(nu_t * min_period - 1.0)) * nu_t_large + nu_t_small) # normalisation weight /= weight.max() # computation of phase difference, make quality checks and misfit --------- # Compute the phase difference. # DP = np.imag(np.log(m + tf_cc / (2 * m + np.abs(tf_cc)))) DP = np.angle(tf_cc) # Attempt to detect phase jumps by taking the derivatives in time and # frequency direction. 0.7 is an emperical value. test_field = weight * DP / np.abs(weight * DP).max() criterion_1 = np.sum([np.abs(np.diff(test_field, axis=0)) > 0.7]) criterion_2 = np.sum([np.abs(np.diff(test_field, axis=1)) > 0.7]) criterion = np.sum([criterion_1, criterion_2]) # criterion_1 = np.abs(np.diff(test_field, axis=0)).max() # criterion_2 = np.abs(np.diff(test_field, axis=1)).max() # criterion = max(criterion_1, criterion_2) if criterion > 7.0: warning = ("Possible phase jump detected. Misfit included. No " "adjoint source computed.") warnings.warn(warning) messages.append(warning) # Compute the phase misfit dnu = nu[1, 0] - nu[0, 0] phase_misfit = np.sqrt(np.sum(weight ** 2 * DP ** 2) * dt_new * dnu) # Sanity check. Should not occur. if np.isnan(phase_misfit): msg = "The phase misfit is NaN." raise LASIFAdjointSourceCalculationError(msg) # compute the adjoint source when no phase jump detected ------------------ if criterion <= 7.0: # Make kernel for the inverse tf transform idp = weight * weight * DP * tf_synth / (m + np.abs(tf_synth) * np.abs(tf_synth)) # Invert tf transform and make adjoint source ad_src, it, I = time_frequency.itfa(tau, nu, idp, width) # Interpolate to original time axis current_time = tau[0, :] new_time = t[t <= current_time.max()] ad_src = interp1d(current_time, np.imag(ad_src), kind=2)(new_time) if len(t) > len(new_time): ad_src = np.concatenate([ad_src, np.zeros(len(t) - len(new_time))]) # Divide by the misfit and change sign. ad_src /= (phase_misfit + eps) ad_src = -1.0 * np.diff(ad_src) / (t[1] - t[0]) # Reverse time and add a leading zero so the adjoint source has the # same length as the input time series. ad_src = ad_src[::-1] ad_src = np.concatenate([[0.0], ad_src]) else: # Criterion failed, no misfit and adjoint source calculated. raise LASIFAdjointSourceCalculationError( "Criterion failed, no misfit has been calculated.") # Plot if required. ------------------------------------------------------- if axis: import matplotlib.cm as cm import matplotlib.pyplot as plt # Primary axis: plot weighted phase difference. ----------------------- weighted_phase_difference = (DP * weight).transpose() abs_diff = np.abs(weighted_phase_difference) mappable = axis.pcolormesh(tau, nu, weighted_phase_difference, vmin=-1.0, vmax=1.0, cmap=cm.RdBu_r) axis.set_xlabel("Seconds since event") axis.set_ylabel("Frequency [Hz]") # Smart scaling for the frequency axis. temp = abs_diff.max(axis=1) * (nu[1] - nu[0]) ymax = len(temp[temp > temp.max() / 1000.0]) ymax *= nu[1, 0] - nu[0, 0] ymax *= 2 axis.set_ylim(0, 2.0 / min_period) if colorbar_axis: cm = plt.gcf().colorbar(mappable, cax=colorbar_axis) else: cm = plt.gcf().colorbar(mappable, ax=axis) cm.set_label("Phase difference in radian") # Secondary axis: plot waveforms and adjoint source. ------------------ ax2 = axis.twinx() ax2.plot(t, ad_src, color="black", alpha=1.0) min_value = min(ad_src.min(), -1.0) max_value = max(ad_src.max(), 1.0) value_range = max_value - min_value axis.twin_axis = ax2 ax2.set_ylim(min_value - 2.5 * value_range, max_value + 0.5 * value_range) axis.set_xlim(0, tau[:, -1][-1]) ax2.set_xlim(0, tau[:, -1][-1]) ax2.set_yticks([]) text = "Misfit: %.4f" % phase_misfit axis.text(x=0.99, y=0.02, s=text, transform=axis.transAxes, bbox=dict(facecolor='orange', alpha=0.8), verticalalignment="bottom", horizontalalignment="right") if messages: message = "\n".join(messages) axis.text(x=0.99, y=0.98, s=message, transform=axis.transAxes, bbox=dict(facecolor='red', alpha=0.8), verticalalignment="top", horizontalalignment="right") ret_dict = { "adjoint_source": ad_src, "misfit_value": phase_misfit, "details": {"messages": messages} } return ret_dict
def adsrc_tf_phase_misfit(t, data, synthetic, min_period, max_period, axis=None, colorbar_axis=None): """ :rtype: dictionary :returns: Return a dictionary with three keys: * adjoint_source: The calculated adjoint source as a numpy array * misfit: The misfit value * messages: A list of strings giving additional hints to what happened in the calculation. """ messages = [] # Compute time-frequency representations ---------------------------------- # compute new time increments and Gaussian window width for the # time-frequency transforms dt_new = float(int(min_period / 3.0)) width = 2.0 * min_period # Compute time-frequency representation of the cross-correlation tau_cc, nu_cc, tf_cc = time_frequency.time_frequency_cc_difference( t, data, synthetic, dt_new, width) # Compute the time-frequency representation of the synthetic tau, nu, tf_synth = time_frequency.time_frequency_transform( t, synthetic, dt_new, width) # 2D interpolation to bring the tf representation of the correlation on the # same grid as the tf representation of the synthetics. Uses a two-step # procedure for real and imaginary parts. tf_cc_interp = RectBivariateSpline(tau_cc[0], nu_cc[:, 0], tf_cc.real, kx=1, ky=1, s=0)(tau[0], nu[:, 0]) tf_cc_interp = np.require(tf_cc_interp, dtype="complex128") tf_cc_interp.imag = RectBivariateSpline(tau_cc[0], nu_cc[:, 0], tf_cc.imag, kx=1, ky=1, s=0)(tau[0], nu[:, 0]) tf_cc = tf_cc_interp # compute tf window and weighting function -------------------------------- # noise taper: downweigh tf amplitudes that are very low m = np.abs(tf_cc).max() / 10.0 weight = 1.0 - np.exp(-(np.abs(tf_cc)**2) / (m**2)) nu_t = nu.transpose() # highpass filter (periods longer than max_period are suppressed # exponentially) weight *= (1.0 - np.exp(-(nu_t * max_period)**2)) # lowpass filter (periods shorter than min_period are suppressed # exponentially) nu_t_large = np.zeros(nu_t.shape) nu_t_small = np.zeros(nu_t.shape) thres = (nu_t <= 1.0 / min_period) nu_t_large[np.invert(thres)] = 1.0 nu_t_small[thres] = 1.0 weight *= (np.exp(-10.0 * np.abs(nu_t * min_period - 1.0)) * nu_t_large + nu_t_small) # normalisation weight /= weight.max() # computation of phase difference, make quality checks and misfit --------- # Compute the phase difference. # DP = np.imag(np.log(m + tf_cc / (2 * m + np.abs(tf_cc)))) DP = np.angle(tf_cc) # Attempt to detect phase jumps by taking the derivatives in time and # frequency direction. 0.7 is an emperical value. test_field = weight * DP / np.abs(weight * DP).max() criterion_1 = np.sum([np.abs(np.diff(test_field, axis=0)) > 0.7]) criterion_2 = np.sum([np.abs(np.diff(test_field, axis=1)) > 0.7]) criterion = np.sum([criterion_1, criterion_2]) # criterion_1 = np.abs(np.diff(test_field, axis=0)).max() # criterion_2 = np.abs(np.diff(test_field, axis=1)).max() # criterion = max(criterion_1, criterion_2) if criterion > 7.0: warning = ("Possible phase jump detected. Misfit included. No " "adjoint source computed.") warnings.warn(warning) messages.append(warning) # Compute the phase misfit dnu = nu[1, 0] - nu[0, 0] phase_misfit = np.sqrt(np.sum(weight**2 * DP**2) * dt_new * dnu) # Sanity check. Should not occur. if np.isnan(phase_misfit): msg = "The phase misfit is NaN." raise LASIFAdjointSourceCalculationError(msg) # compute the adjoint source when no phase jump detected ------------------ if criterion <= 7.0: # Make kernel for the inverse tf transform idp = weight * weight * DP * tf_synth / ( m + np.abs(tf_synth) * np.abs(tf_synth)) # Invert tf transform and make adjoint source ad_src, it, I = time_frequency.itfa(tau, nu, idp, width) # Interpolate to original time axis current_time = tau[0, :] new_time = t[t <= current_time.max()] ad_src = interp1d(current_time, np.imag(ad_src), kind=2)(new_time) if len(t) > len(new_time): ad_src = np.concatenate([ad_src, np.zeros(len(t) - len(new_time))]) # Divide by the misfit and change sign. ad_src /= (phase_misfit + eps) ad_src = -1.0 * np.diff(ad_src) / (t[1] - t[0]) # Reverse time and add a leading zero so the adjoint source has the # same length as the input time series. ad_src = ad_src[::-1] ad_src = np.concatenate([[0.0], ad_src]) else: # Criterion failed, no misfit and adjoint source calculated. raise LASIFAdjointSourceCalculationError( "Criterion failed, no misfit has been calculated.") # Plot if required. ------------------------------------------------------- if axis: import matplotlib.cm as cm import matplotlib.pyplot as plt # Primary axis: plot weighted phase difference. ----------------------- weighted_phase_difference = (DP * weight).transpose() abs_diff = np.abs(weighted_phase_difference) mappable = axis.pcolormesh(tau, nu, weighted_phase_difference, vmin=-1.0, vmax=1.0, cmap=cm.RdBu_r) axis.set_xlabel("Seconds since event") axis.set_ylabel("Frequency [Hz]") # Smart scaling for the frequency axis. temp = abs_diff.max(axis=1) * (nu[1] - nu[0]) ymax = len(temp[temp > temp.max() / 1000.0]) ymax *= nu[1, 0] - nu[0, 0] ymax *= 2 axis.set_ylim(0, 2.0 / min_period) if colorbar_axis: cm = plt.gcf().colorbar(mappable, cax=colorbar_axis) else: cm = plt.gcf().colorbar(mappable, ax=axis) cm.set_label("Phase difference in radian") # Secondary axis: plot waveforms and adjoint source. ------------------ ax2 = axis.twinx() ax2.plot(t, ad_src, color="black", alpha=1.0) min_value = min(ad_src.min(), -1.0) max_value = max(ad_src.max(), 1.0) value_range = max_value - min_value axis.twin_axis = ax2 ax2.set_ylim(min_value - 2.5 * value_range, max_value + 0.5 * value_range) axis.set_xlim(0, tau[:, -1][-1]) ax2.set_xlim(0, tau[:, -1][-1]) ax2.set_yticks([]) text = "Misfit: %.4f" % phase_misfit axis.text(x=0.99, y=0.02, s=text, transform=axis.transAxes, bbox=dict(facecolor='orange', alpha=0.8), verticalalignment="bottom", horizontalalignment="right") if messages: message = "\n".join(messages) axis.text(x=0.99, y=0.98, s=message, transform=axis.transAxes, bbox=dict(facecolor='red', alpha=0.8), verticalalignment="top", horizontalalignment="right") ret_dict = { "adjoint_source": ad_src, "misfit_value": phase_misfit, "details": { "messages": messages } } return ret_dict