Example #1
0
def fktrafo(stream, normalize=True):
	"""
	Calculates the f,k - transformation of the data in stream. Returns the trafo as an array.

	:param st: Stream
	:type st: obspy.core.stream.Stream

	:param inv: inventory
	:type inv: obspy.station.inventory.Inventory

	:param event: Event
	:type event: obspy.core.event.Event

	returns
	:param fkdata: f,k - transformation of data in stream
	:type fkdata: numpyndarray
	"""
	st_tmp = stream.copy()
	ArrayData = stream2array(st_tmp, normalize)
	
	ix = ArrayData.shape[0]
	iK = int(math.pow(2,nextpow2(ix)))
	it = ArrayData.shape[1]
	iF = int(math.pow(2,nextpow2(it)))

	fkdata = np.fft.fft2(ArrayData, s=(iK,iF))
	
	return fkdata
Example #2
0
def ifktrafo(fkdata, stream, normalize=True):
	"""
	Calculates the inverse f,k - transformation of the data in fkdata. Returns the trafo as an array.

	"""
	StreamData= stream2array(stream)
	ix   = StreamData.shape[0]
	iK   = int(math.pow(2,nextpow2(ix)))
	it   = StreamData.shape[1]
	iF   = int(math.pow(2,nextpow2(it)))

	fk_tmp = fkdata.copy()

	ArrayData = np.fft.ifft2(fkdata, s=(iK,iF))
	ArrayData = ArrayData[0:ix, 0:it]

	return ArrayData
Example #3
0
def pocs(data, maxiter, noft, alpha=0.9, beta=None, method='linear', dmethod='denoise', peaks=None, maskshape=None, dt=None, p=None, flow=None, fhigh=None, slidingwindow=False, overlap=0.5):
	"""
	This functions reconstructs missing signals in the f-k domain, using the original data,
	including gaps, filled with zeros. It applies the projection onto convex sets (pocs) algorithm in
	2D.

	Reference: 3D interpolation of irregular data with a POCS algorithm, Abma & Kabir, 2006

	:param data:
	:type  data:

	:param maxiter:
	:type  maxiter:

	:param nol: Number of loops
	:type  nol:
	
	:param alpha: Factor of threshold decrease after each iteration
	:type  alpha: float

	:param method: Method to be used for the pocs algorithm
					-'linear', 'exp', 'mask' or 'ssa'

	:param peaks: Slope values for the mask

	:param maskshape: Shape of the corners of mask, see makemask

	returns:

	:param datap:
	:type  datap:
	"""
	#if not decrease in ('linear', 'exp', None):
	#	msg='No decrease method chosen'
	#	raise IOError(msg)

	ArrayData 	= data.copy()
	ix = ArrayData.shape[0]
	iK = int(math.pow(2,nextpow2(ix)))
	it = ArrayData.shape[1]
	iF = int(math.pow(2,nextpow2(it)))
	fkdata = np.fft.fft2(ArrayData, s=(iK,iF))
	threshold = abs(fkdata.max())
		
	ADold = ArrayData.copy()
	ADnew = ArrayData.copy()
	ADfinal = np.zeros(ArrayData.shape).astype('complex')

	if method in ('linear', 'exp'):
		if slidingwindow:
			if dmethod in ('reconstruct'):
				w_length = int(data.shape[1] / 3.)
				swh = np.hanning(w_length)

				loc = 0.
				inside = True
				while inside:
					curr_win = int(loc)
					ADtemp = ArrayData[:,curr_win:curr_win+w_length].copy()
						
					for i in range(maxiter):
						data_tmp 	= ADtemp.copy()
						fkdata 		= np.fft.fft2(data_tmp, s=(iK,iF))
						fkdata[ np.where(abs(fkdata) < threshold)] 	= 0. + 0j

						if method in ('linear'):
							threshold 	= threshold * alpha
						elif method in ('exp'):
							threshold 	= threshold * sp.exp(-(i+1) * alpha)

						data_tmp 	= np.fft.ifft2(fkdata, s=(iK,iF)).real[0:ix, 0:it].copy()
						ADtemp[noft] 	= data_tmp[noft][:,curr_win:curr_win+w_length].copy()

					

					if loc == 0.:
						ADfinal[:,curr_win:curr_win+w_length] = ADtemp.copy()
					else:
						ADfinal[:,curr_win:curr_win+w_length] = ADfinal[:,curr_win:curr_win+w_length] + ADtemp
						ADfinal[:,curr_win-int(overlap*w_length):int(curr_win)] = ( ADold[:,int((1-overlap)*w_length):] + ADtemp[:,:int(overlap*w_length)] ) / 2.

					ADold = ADtemp.copy()
					threshold = abs(np.fft.fft2(ADold, s=(iK,iF)).max())

					loc += overlap * w_length
					print(loc)
					if loc >= data.shape[1]: inside=False
	
		else:
			if dmethod in ('reconstruct'):
				ADtemp = ArrayData.copy()
				for i in range(maxiter):
					data_tmp 	= ADtemp.copy()
					fkdata 		= np.fft.fft2(data_tmp, s=(iK,iF))
					fkdata[ np.where(abs(fkdata) < threshold)] 	= 0. + 0j

					if method in ('linear'):
						threshold 	= threshold * alpha
					elif method in ('exp'):
						threshold 	= threshold * sp.exp(-(i+1) * alpha)

					data_tmp 	= np.fft.ifft2(fkdata, s=(iK,iF)).real[0:ix, 0:it].copy()
					ADtemp[noft] 	= data_tmp[noft]
					name = str(i) + '.png'
					# plt.ion()
					# plotfk(fkdata)
					# fig = plt.gcf()
					# fig.set_size_inches(7,8)
					# fig.savefig(name)
					# plt.ioff()
				ADfinal = ADtemp.copy()

				threshold = abs(np.fft.fft2(ADfinal, s=(iK,iF)).max())
			
			elif dmethod in ('denoise', 'de-noise'):
				for n in noft:
					ADtemp = ArrayData.copy()
					for i in range(maxiter):
						data_tmp 	= ADtemp.copy()
						fkdata 		= np.fft.fft2(data_tmp, s=(iK,iF))
						fkdata[ np.where(abs(fkdata) < threshold)] 	= 0. + 0j

						if method in ('linear'):
							threshold 	= threshold * alpha
						elif method in ('exp'):
							threshold 	= threshold * sp.exp(-(i+1) * alpha)

						data_tmp 	= np.fft.ifft2(fkdata, s=(iK,iF)).real[0:ix, 0:it].copy()
						ADtemp[n] 	= data_tmp[n]
						#save = 'pocsdata' + str(i) + '.png'
						#plot(data_tmp, ylabel='Distance(m)', xlabel='Time(s)', fs=22, yinfo=2, savefig=save)
					ADfinal = ADtemp.copy()

				threshold = abs(np.fft.fft2(ADfinal, s=(iK,iF)).max())

	elif method in ('mask'):
		W 		= makeMask(fkdata, peaks[0], maskshape)
		ADfinal = ArrayData.copy()
		for n in noft:
			ADtemp 	= ArrayData.copy()
			threshold = abs(W*np.fft.fft2(ADfinal, s=(iK,iF))).max()
			for i in range(maxiter):
				data_tmp 	=ADtemp.copy()
				fkdata 		= W * np.fft.fft2(data_tmp, s=(iK,iF))
				fkdata[ np.where(abs(fkdata) < threshold)] 	= 0. + 0j
				threshold 	= threshold * alpha
				data_tmp 	= np.fft.ifft2(fkdata, s=(iK,iF)).real[0:ix, 0:it].copy()
				ADtemp[n] 	= data_tmp[n]

			ADfinal[n] = ADtemp[n].copy()

	elif method in ('ssa'):

		for n in noft:
			for i in range(maxiter):
				data_tmp 		= ArrayData.copy()
				data_ssa 		= fx_ssa(data_tmp,dt,p,flow,fhigh)
				ArrayData 		= alpha * ArrayData			
				ArrayData[n] 	= (1. - alpha) * data_ssa[n]

			ADfinal[n] = ArrayData[n].copy()

	elif method in ('average'):
		threshold = beta * abs(np.fft.fft2(ArrayData, s=(iK,iF)).max())
		ADtemp = ArrayData.copy()
		for n in noft:
			for i in range(maxiter):
				data_tmp 	= ADtemp.copy()
				fkdata 		= np.fft.fft2(data_tmp, s=(iK,iF))
				fkdata[ np.where(abs(fkdata) < threshold)] 	= 0. + 0j

				ADtemp 		= alpha*data_tmp + (1. - alpha) * np.fft.ifft2(fkdata, s=(iK,iF)).real[0:ix, 0:it]
				ADtemp[n] 	= (1. - alpha) * np.fft.ifft2(fkdata, s=(iK,iF)).real[0:ix, 0:it][n]

			ADfinal = ADtemp.copy()


	elif method == 'maskvary':
				
		ADfinal = ArrayData.copy()
		ADtemp 	= ArrayData.copy()
		for n in noft:
			for i in range(maxiter):
				W 			= makeMask(fkdata, peaks, shape=maskshape, expl_cutoff=i)
				data_tmp 	= ADtemp.copy()
				fkdata 		= W * np.fft.fft2(data_tmp, s=(iK,iF))
				data_tmp 	= np.fft.ifft2(fkdata, s=(iK,iF)).real[0:ix, 0:it].copy()
				ADtemp[n]	= alpha * ArrayData[n].copy()			
				ADtemp[n]  += (1. - alpha) * data_tmp[n]

			ADfinal[n] = ArrayData[n].copy()

	else:
		print('no method specified')
		return

	datap = ADfinal.copy()

	return datap
Example #4
0
def fk_filter(st, inv=None, event=None, ftype='eliminate', fshape=['butterworth', 4, 4], phase=None, polygon=4, normalize=True, stack=False,
					slopes=[-3,3], deltaslope=0.05, slopepicking=False, smoothpicks=False, dist=0.5, maskshape=['boxcar',None], 
					order=4., peakinput=False, eval_mean=1, fs=25):
	"""
	Import stream, the function applies an 2D FFT, removes a certain window around the
	desired phase to surpress a slownessvalue corresponding to a wavenumber and applies an 2d iFFT.
	To fill the gap between uneven distributed stations use array_util.gaps_fill_zeros(). A method to interpolate the signals in the
	fk-domain is beeing build, also a method using a norm minimization method.
	Alternative is an nonequidistant 2D Lombard-Scargle transformation.

	param st: Stream
	type st: obspy.core.stream.Stream

	param inv: inventory
	type inv: obspy.station.inventory.Inventory

	param event: Event
	type event: obspy.core.event.Event

	param ftype: type of method, default is 'eliminate-polygon', possible inputs are:
				 -eliminate
				 -extract
				 -eliminate-polygon
				 -extract-polygon
				 -mask
				 -fk

	type ftype: string

	param fshape: fshape[0] describes the shape of the fk-filter in case of ftype is 'eliminate' or 'extract'. Possible inputs are:
				 -spike (default)
				 -boxcar
				 -taper
				 -butterworth

				  fshape[1] is an additional attribute to the shape of taper and butterworth, for:
				 -taper: fshape[1] = slope of sides
				 -butterworth: fshape[1] = number of poles

				  fshape[3] describes the length of the filter shape, respectivly wavenumber corner points around k=0,
				
				 e.g.: fshape['taper', 2, 4] produces a symmetric taper with slope of side = 2, where the signal is reduced about 50% at k=+-2


	type  fshape: list

	param phase: name of the phase to be investigated
	type  phase: string

	param polygon: number of vertices of polygon for fk filter, only needed 
				   if ftype is set to eliminate-polygon or extract-polygon.
				   Default is 12.
	type  polygon: int
	
	param normalize: normalize data to 1
	type normalize: bool

	param SSA: Force SSA algorithm or let it check, default:False
	type SSA: bool

	param eval_mean: number of linear events used to calculate the average of the area in the fk domain.

	returns:	stream_filtered, the filtered stream.
			


	References: Yilmaz, Thomas

	Author: S. Schneider 2016

	 This program is free software: you can redistribute it and/or modify
	 it under the terms of the GNU General Public License as published
	 by the Free Software Foundation, either version 3 of the License, or
	 any later version.

	 This program is distributed in the hope that it will be useful,
	 but WITHOUT ANY WARRANTY; without even the implied warranty of
	 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	 GNU General Public License for more details: http://www.gnu.org/licenses/
	"""

	# Convert format and prepare Variables.

	# Check for Data type of variables.
	if not type(st ) == Stream:
		print( "Wrong input type of stream, must be obspy.core.stream.Stream" )
		raise TypeError

	if len(fshape) ==  1:
		fshape = [fshape[0], None, None]
	
	st_tmp = st.copy()
	ArrayData = stream2array(st_tmp, normalize)
	
	ix = ArrayData.shape[0]
	iK = int(math.pow(2,nextpow2(ix)))
	
	try:
		yinfo = epidist2nparray(attach_epidist2coords(inv, event, st_tmp))
		dx = (yinfo.max() - yinfo.min() + 1) / yinfo.size
		k_axis = np.fft.fftfreq(iK, dx)	

	except:
		try:
			ymax = st_tmp[0].stats.distance
			ymin = st_tmp[0].stats.distance
			for trace in st_tmp:
				if trace.stats.distance > ymax: ymax = trace.stats.distance
				if trace.stats.distance < ymin: ymin = trace.stats.distance

			dx = (ymax - ymin + 1) / len(st_tmp)
			k_axis = np.fft.fftfreq(iK, dx)

		except:
			print("\nNo inventory or event-information found. \nContinue without specific distance and wavenumber information.")
			yinfo=None
			dx=None
			k_axis=None

	it     = ArrayData.shape[1]
	iF     = int(math.pow(2,nextpow2(it)))
	dt     = st_tmp[0].stats.delta
	f_axis = np.fft.fftfreq(iF,dt)



	# Calc mean diff of each epidist entry if it is reasonable
	# do a partial stack and apply filter.


	"""
	2D Frequency-Space / Wavenumber-Frequency Filter #########################################################
	"""

	# 2D f-k Transformation 
	# Note array_fk has f on the x-axis and k on the y-axis!!!
	# For interaction the conj.-transposed Array is shown!!!


	# Decide when to use SSA to fill the gaps, calc mean distance of each epidist entry
	# if it differs too much --> SSA


	if ftype in ("eliminate"):
		if phase:
			if not isinstance(event, Event) and not isinstance(inv, Inventory):
				msg='For alignment on phase calculation inventory and event information is needed, not found.'
				raise IOError(msg)

			st_al = alignon(st_tmp, inv, event, phase)
			ArrayData = stream2array(st_al, normalize)
			array_fk = np.fft.fft2(ArrayData, s=(iK,iF))
			array_filtered_fk = line_set_zero(array_fk, shape=fshape)

		else:
			array_fk = np.fft.fft2(ArrayData, s=(iK,iF))
			array_filtered_fk = line_set_zero(array_fk, shape=fshape)

	elif ftype in ("extract"):
		if phase:
			if not isinstance(event, Event) and not isinstance(inv, Inventory):
				msg='For alignment on phase calculation inventory and event information is needed, not found.'
				raise IOError(msg)

			st_al = alignon(st_tmp, inv, event, phase)
			ArrayData = stream2array(st_al, normalize)
			array_fk = np.fft.fft2(ArrayData, s=(iK,iF))
			array_filtered_fk = line_cut(array_fk, shape=fshape)

		else:
			array_fk = np.fft.fft2(ArrayData, s=(iK,iF))
			array_filtered_fk = line_cut(array_fk, shape=fshape)

	
	elif ftype in ("eliminate-polygon"):
		array_fk = np.fft.fft2(ArrayData, s=(iK,iF))
		if phase:
			if not isinstance(event, Event) and not isinstance(inv, Inventory):
				msg='For alignment on phase calculation inventory and event information is needed, not found.'
				raise IOError(msg)
			st_al = alignon(st_tmp, inv, event, phase)
			ArrayData = stream2array(st_al, normalize)
			array_fk = np.fft.fft2(ArrayData, s=(iK,iF))
			array_filtered_fk = _fk_eliminate_polygon(array_fk, polygon, ylabel=r'frequency domain f in Hz', \
													  yticks=f_axis, xlabel=r'wavenumber domain k in $\frac{1}{^{\circ}}$', xticks=k_axis, eval_mean=eval_mean, fs=fs)

		else:
			array_filtered_fk = _fk_eliminate_polygon(array_fk, polygon, ylabel=r'frequency domain f in Hz', \
													  yticks=f_axis, xlabel=r'wavenumber domain k in $\frac{1}{^{\circ}}$', xticks=k_axis, eval_mean=eval_mean, fs=fs)


	elif ftype in ("extract-polygon"):
		array_fk = np.fft.fft2(ArrayData, s=(iK,iF))
		if phase:
			if not isinstance(event, Event) and not isinstance(inv, Inventory):
				msg='For alignment on phase calculation inventory and event information is needed, not found.'
				raise IOError(msg)

			st_al = alignon(st_tmp, inv, event, phase)
			ArrayData = stream2array(st_al, normalize)
			array_fk = np.fft.fft2(ArrayData, s=(iK,iF))
			array_filtered_fk = _fk_extract_polygon(array_fk, polygon, ylabel=r'frequency domain f in Hz', \
												yticks=f_axis, xlabel=r'wavenumber domain k in $\frac{1}{^{\circ}}$', xticks=k_axis, eval_mean=eval_mean, fs=fs)
		else:
			array_filtered_fk = _fk_extract_polygon(array_fk, polygon, ylabel=r'frequency domain f in Hz', \
												yticks=f_axis, xlabel=r'wavenumber domain k in $\frac{1}{^{\circ}}$', xticks=k_axis, eval_mean=eval_mean, fs=fs)


	elif ftype in ("mask"):
		array_fk = np.fft.fft2(ArrayData)
		M, prange, peaks = slope_distribution(array_fk, slopes, deltaslope, peakpick=None, mindist=dist, smoothing=smoothpicks, interactive=slopepicking)
		W = makeMask(array_fk, peaks[0], maskshape)
		array_filtered_fk =  array_fk * W
		array_filtered = np.fft.ifft2(array_filtered_fk)
		stream_filtered = array2stream(array_filtered, st_original=st.copy())
		return stream_filtered, array_fk, W


	elif ftype in ("fk"):
		if phase:
			if not isinstance(event, Event) and not isinstance(inv, Inventory):
				msg='For alignment on phase calculation inventory and event information is needed, not found.'
				raise IOError(msg)

			st_al = alignon(st_tmp, inv, event, phase)
			ArrayData = stream2array(st_al, normalize)
			array_fk = np.fft.fft2(ArrayData, s=(iK,iF))
			### BUILD DOUBLE TAPER ###
			#array_filtered_fk = 

		else:
			array_fk = np.fft.fft2(ArrayData, s=(iK,iF))
			### BUILD DOUBLE TAPER ###
			#array_filtered_fk = 


	else:
		print("No type of filter specified")
		raise TypeError

	array_filtered = np.fft.ifft2(array_filtered_fk, s=(iK,iF)).real


	# Convert to Stream object.
	array_filtered = array_filtered[0:ix, 0:it]
	stream_filtered = array2stream(array_filtered, st_original=st.copy())

	return stream_filtered
Example #5
0
def radon_inverse(st, inv, event, p, weights, line_model, inversion_model, hyperparameters):
	"""
	This function inverts move-out data to the Radon domain given the inputs:
	:param st:
	
	:param inv:

	:param event:
	
	:param p:        -- vector of slowness axis you would like to invert to.
	 
	:param weights:  -- weighting vector that determines importance of each trace.
	              		set vector to ones for no preference.

	
	:param line_model: 	select one of the following options for path integration:
	     				'linear'     - linear paths in the spatial domain (default)
	     				'parabolic'  - parabolic paths in the spatial domain.
	
	:param inversion model:	 select one of the following options for regularization schema:
								 'L2'       - Regularized on the L2 norm of the Radon domain (default)
								 'L1'       - Non-linear regularization based on L1 norm and iterative
											  reweighted least sqaures (IRLS) see Sacchi 1997.
								 'Cauchy'   - Non-linear regularization see Sacchi & Ulrych 1995
	
	:param hyperparameters: trades-off between fitting the data and chosen damping.
	
	returns: radon domain is ordered size(R)==[length(p),length(t)], time-axis and distance-axis.
	
	Known limitations:
	 - Assumes evenly sampled time axis.
	 - Assumes move-out data isn't complex.
	
	
	 References: Schultz, R., Gu, Y. J., 2012. Flexible Matlab implementation
	             of the Radon Transform.  Computers and Geosciences.
	
	             An, Y., Gu, Y. J., Sacchi, M., 2007. Imaging mantle 
	             discontinuities using least-squares Radon transform. 
	             Journal of Geophysical Research 112, B10303.
	
	 Author: R. Schultz, 2012
	 Translated to Python by: S. Schneider, 2016
	"""

	# Check for Data type of variables.

	if not isinstance(st, Stream) or not isinstance(inv, Inventory) or not isinstance(event, Event):
		msg = "Wrong input type must be obspy Stream, Inventory and Event" 
		raise TypeError

	if not isinstance(hyperparameters,list):
		msg = "Wrong input type of mu, must be list"
		raise TypeError



	# Define some array/matrices lengths.
	st_tmp = st.copy()
	M = stream2array(st_tmp)
	epi = epidist2nparray(attach_epidist2coords(inv, event, st_tmp))
	delta = np.array([ epi.copy() ])
	ref_dist = np.mean(delta)

	if not weights:
		weights = np.ones(delta.size)

	t = np.linspace(0,st_tmp[0].stats.delta * st_tmp[0].stats.npts, st_tmp[0].stats.npts)
	it=t.size
	print(it)
	iF=int(math.pow(2,nextpow2(it)+1)) # Double length

   
	iDelta=delta.size
	ip=len(p)
	iw=len(weights)
	
	#Exit if inconsistent data is input.
	if M.shape != (iDelta, it):
		print("Dimensions inconsistent!\nShape of M is not equal to (len(delta),len(t)) \nShape of M = (%i , %i)\n(len(delta),len(t)) = (%i, %i) \n" % (M.shape[0],  M.shape[1], iDelta, it) )
		R=0
		return(R)
	if iw != iDelta:
		print("Dimensions inconsistent!\nlen(delta) ~= len(weights)\nlen(delta) = %i\nlen(weights) = %i\n" % (iDelta, iw))
		R=0
		return(R)

	#Exit if improper hyperparameters are entered.
	if inversion_model in ["L1", "Cauchy"]:
		if not len(hyperparameters == 2):
			print("Improper number of trade-off parameters\n")
			R=0
			return(R)
	else: #The code's default is L2 inversion.
		if not len(hyperparameters) == 1:
			print("Improper number of trade-off parameters\n")
			R=0
			return(R)

	#Preallocate space in memory.
	R=np.zeros((ip,it)) 
	Rfft=np.zeros((ip,iF)) + 0j
	A=np.zeros((iDelta,ip)) + 0j
	Tshift=np.zeros((iDelta,ip)) + 0j
	AtA=np.zeros((ip,ip)) + 0j
	AtM=np.zeros((ip,1)) + 0j
	Ident=np.identity(ip)

	#Define some values
	Dist_array=delta-ref_dist
	dF=1./(t[0]-t[1])
	Mfft=np.fft.fft(M,iF,1)
	W=sparse.spdiags(weights.conj().transpose(), 0, iDelta, iDelta).A
	dCOST=0.
	COST_curv=0.
	COST_prev=0.

	#Populate ray parameter then distance data in time shift matrix.
	for j in range(iDelta):
		if line_model == "parabolic":
			Tshift[j]=p
		else: #Linear is default
			Tshift[j]=p
	
	for k in range(ip):
		if line_model == 'parabolic':
			Tshift[:,k]=(2. * ref_dist * Tshift[:,k] * Dist_array.conj().transpose()) + (Tshift[:,k] * (Dist_array**2).conj().transpose())
		else: #Linear is default
			Tshift[:,k]=Tshift[:,k] * Dist_array[0].conj().transpose()

	# Loop through each frequency.
	for i in range( int(math.floor((iF+1)/2)) ):
		print('Step %i of %i' % (i, int(math.floor((iF+1)/2))) )
		# Make time-shift matrix, A.
		f = ((float(i)/float(iF))*dF)
		A = np.exp( (0.+1j)*2*pi*f * Tshift )

		# M = A R ---> AtM = AtA R
		# Solve the weighted, L2 least-squares problem for an initial solution.
		AtA = dot( dot(A.conj().transpose(), W), A )
		AtM = dot( A.conj().transpose(), dot( W, Mfft[:,i] ) )
		mu = abs(np.trace(AtA)) * hyperparameters[0]
		Rfft[:,i] = sp.linalg.solve((AtA + mu*Ident), AtM)

		#Non-linear methods use IRLS to solve, iterate until convergence to solution.
		if inversion_model in ("Cauchy", "L1"):
			
			#Initialize hyperparameters.
			b=hyperparameters[1]
			lam=mu*b

			#Initialize cost functions.
			dCOST = float("Inf")
			if inversion_model == "Cauchy":
				COST_prev = np.linalg.norm( Mfft[:,i] - dot(A,Rfft[:,i]), 2 ) + lam*sum( np.log( abs(Rfft[:,i]**2 + b) ) )
			elif inversion_model == "L1":
				COST_prev = np.linalg.norm( Mfft[:,i] - dot(A,Rfft[:,i]), 2 ) + lam*np.linalg.norm( abs(Rfft[:,i]+1), 1 )
			itercount=1
			
			#Iterate until negligible change to cost function.
			while dCost > 0.001 and itercount < 10:
				
				#Setup inverse problem.
				if inversion_model == "Cauchy":
					Q = sparse.spdiags( 1./( abs(Rfft[:,i]**2) + b), 0, ip, ip).A
				elif inversion_model == "L1":
					Q = sparse.spdiags( 1./( abs(Rfft[:,i]) + b), 0, ip, ip).A
				Rfft[:,i]=sp.linalg.solve( ( lam * Q + AtA ), AtM )
				
				#Determine change to cost function.
				if inversion_model == "Cauchy":
					COST_cur = np.linalg.norm( Mfft[:,i]-A*Rfft[:,i], 2 ) + lam*sum( np.log( abs(Rfft[:,i]**2 + b )-np.log(b) ) )
				elif inversion_model == "L1":
					COST_cur = np.linalg.norm( Mfft[:,i]-A*Rfft[:,i], 2 ) + lam*np.linalg.norm( abs(Rfft[:,i]+1) + b, 1 )
				dCOST = 2*abs(COST_cur - COST_prev)/(abs(COST_cur) + abs(COST_prev))
				COST_prev = COST_cur
				
				itercount += 1

			#Assuming Hermitian symmetry of the fft make negative frequencies the complex conjugate of current solution.
		if i != 0:
			Rfft[:,iF-i] = Rfft[:,i].conjugate()

	R = np.fft.ifft(Rfft, iF)
	R = R[:,0:it]

	return R, t, epi
Example #6
0
def radon_forward(t,p,R,delta,ref_dist,line_model):
	"""
	This function applies the time-shift Radon operator A, to the Radon 
	domain.  Will calculate the move-out data, given the inputs:
	 -t        -- vector of time axis.
	 -p        -- vector of slowness axis you would like to invert to.
	 -R        -- matrix of Radon data, ordered size(R)==[length(p),length(t)].
	 -delta    -- vector of distance axis.
	 -ref_dist -- reference distance the path-function will shift about.

	 -line_model, select one of the following options for path integration:
		 'linear'     - linear paths in the spatial domain (default)
		 'parabolic'  - parabolic paths in the spatial domain.

	Output spatial domain is ordered size(M)==[length(delta),length(t)].

	Known limitations:
	 - Assumes evenly sampled time axis.
	 - Assumes Radon data isn't complex.


	 References: Schultz, R., Gu, Y. J., 2012. Flexible Matlab implementation 
		         of the Radon Transform.  Computers and Geosciences [In Preparation]

		         An, Y., Gu, Y. J., Sacchi, M., 2007. Imaging mantle 
		         discontinuities using least-squares Radon transform. 
		         Journal of Geophysical Research 112, B10303.

	 Author: R. Schultz, 2012
	 Translated to Python by: S. Schneider, 2016
	"""

	# Check for Data type of variables.
	if not isinstance(t, numpy.ndarray) or not isinstance(delta, numpy.ndarray):
		print( "Wrong input type of t or delta, must be numpy.ndarray" )
		raise TypeError

	it=t.size
	iF=int(math.pow(2,nextpow2(it)+1)) # Double length
	iDelta=delta.size
	ip=len(p)

	#Exit if inconsistent data is input.
	if R.shape != (ip, it):
		print("Dimensions inconsistent!\nShape of M is not equal to (len(delta),len(t)) \nShape of M = (%i , %i)\n(len(delta),len(t)) = (%i, %i) \n" % (M.shape[0],  M.shape[1], iDelta, it) )
		M=0
		return(M)

	#Preallocate space in memory.
	Mfft = np.zeros((iDelta, iF)) + 0j
	A = np.zeros((iDelta, ip)) + 0j
	Tshift = np.zeros((iDelta, ip)) + 0j

	#Define some values.
	Dist_array=delta-ref_dist
	dF=1./(t[0]-t[1])
	Rfft=np.fft.fft(R,iF,1)

	#Populate ray parameter then distance data in time shift matrix.
	for j in range(iDelta):
		if line_model == "parabolic":
			Tshift[j,:]=p
		else: #Linear is default
			Tshift[j,:]=p
	
	for k in range(ip):
		if line_model == 'parabolic':
			Tshift[:,k]=(2. * ref_dist * Tshift[:,k] * Dist_array.conj().transpose()) + (Tshift[:,k] * (Dist_array**2).conj().transpose())
		else: #Linear is default
			Tshift[:,k]=Tshift[:,k] * Dist_array.conj().transpose()

	# Loop through each frequency.
	for i in range( int(math.floor((iF+1)/2))-1 ):

		# Make time-shift matrix, A.
		f = ((float(i)/float(iF))*dF)
		A = np.exp( (0.+1j)*2*pi*f * Tshift )
		
		# Apply Radon operator.
		Mfft[:,i]=dot(A, Rfft[:,i])

		# Assuming Hermitian symmetry of the fft make negative frequencies the complex conjugate of current solution.
		if i != 0:
			Mfft[:,iF-i] = Mfft[:,i].conjugate()

	M = np.fft.ifft(Mfft, iF)
	M = M[:,0:it]		

	return(M)