Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding spectral methods to trace object #837

Open
wants to merge 12 commits into
base: master
Choose a base branch
from
Open

adding spectral methods to trace object #837

wants to merge 12 commits into from

Conversation

megies
Copy link
Member

@megies megies commented May 10, 2016

Coming from the 'old school' interactive side, I dearly miss spectral methods attached directly to a trace object. While the spectrogram, which to me is a more sophisticated method is already available as a method, the direct estimation of psd/multi-taper and fft is not available. Certainly this can be done by modules of scipy or mlab BUT what I'm thinking of is to return a complex trace (resulting from a fft), manipulate it and possibly transfer it back to time domain. This means that all trace.stats information should be kept while the sampling or delta needs to be modified to frequency domain (i.e. 1/(time window). This would open up the world of trace manipulation in frequency domain, easy cross-spectral estimates, sub-sample shifts etc AND of course an easy and fast way of displaying the frequency content. Possibly this would result in a new 'trace object' and therefore needs a re-definition of the Trace object itself.
What is the opinion of the developers?

Cheers Jo

@jwassermann jwassermann self-assigned this Jul 10, 2014
@claudiodsf
Copy link
Member

👍 on this. I developed a code for source parameters estimation from spectral inversion, and I needed to build a custom spectral object to deal with spectra.

I don't feel like sharing the code here, since it is an horrible hack created from subclassing a Trace object.

I think that a proper implementation of spectral methods would benefit a significant class of applications.

@megies
Copy link
Member

megies commented Jul 11, 2014

Talked with @jwassermann a bit about it.. while stats header entries would be pretty similar to a time domain Trace, it would probably be good to introduce a derived "frequency domain" Trace object. That way we could raise errors on e.g. mixed multiplications of "time domain Trace" and "frequency domain Trace". Furthermore we could prevent operations on one or the other that only make sense for exactly one type..

I'd propose adding a class BaseTrace and move everything from the current Trace class that applies to both types there. Then derive Trace and a new FrequencyTrace (or some other name) from BaseTrace.

Comments? There's a lot to be thought through properly here...

@krischer
Copy link
Member

Good idea. The BaseTrace class will likely be pretty small as not much functionality can be shared but that is fine.

Just some ideas on how this could work:

import obspy

tr = obspy.read()[0]

# Go to frequency domain, maybe with other methods/options as well.
f_tr = tr.fft()

# data and freqs are stored here.
f_tr.data
f_tr.freqs

# Frequency domain taper, e.g. some filter.
f_tr.taper(...)
f_tr.plot()
...

# Go back to time domain. For that to be possible the starttime
# must also be stored for the frequeny domain trace.
tr = f_tr.ifft()

@claudiodsf
Copy link
Member

f_tr.data would be a complex number.
Should we also store the same information as phase and amplitude? Or maybe provide methods to get them easily.

f_tr.freqs should probably be a function, like tr.times()

@claudiodsf
Copy link
Member

Other thoughts: I would like to have trim() and slice() methods for spectral traces, as well (I'm using them in my custom implementation for the spectral inversion code).

What about streams? Could a Stream object mix Trace and FrequencyTrace objects? Should there be a FrequencyStream?

@krischer
Copy link
Member

There should be FrequencyStream class in my opinion. I would also think that f_tr.data should be a complex number. We could provide f_tr.phase(), and f_tr.amplitude() or some related functions to calculate these things but the actual data value should be complex - many people will want to have access to that.

I agree with f_tr.freqs() being a function.

I don't really get the trim() and slice() operations in the frequency domain. Do you want to cut away frequency bins? Or have the same interpretation as in the time domain, e.g. time shift in the frequency domain and somehow reinterpolate/throw away some frequencies?

@claudiodsf
Copy link
Member

trim() or slice() --or even spectral re-interpolation-- would be useful for people needing to stack spectra or to fit a spectral model using just a part of the spectrum (e.g., ignoring too low or too high frequencies).

@megies
Copy link
Member

megies commented Jul 11, 2014

There should be FrequencyStream class in my opinion.

👍 we can't allow to mix both types.

I would also think that f_tr.data should be a complex number. We could provide f_tr.phase(), and f_tr.amplitude() or some related functions to calculate these things but the actual data value should be complex - many people will want to have access to that.

👍 for storing data as complex. talked with @jwassermann about it, we will need to define properties f_tr.amplitude, f_tr.phase that

  • have getter methods that compute from complex data on the fly
  • have setter methods that update the complex data (first getting the complementary other real data from the stored complex data)

I agree with f_tr.freqs() being a function.

absolutely. we should probably store this in stats like with `starttime/sampling_rate/npts

@QuLogic
Copy link
Member

QuLogic commented Jul 14, 2014

I'd propose adding a class BaseTrace

That proposal appears to coincide with some thoughts on #617, I think?

@megies
Copy link
Member

megies commented Jul 16, 2014

That proposal appears to coincide with some thoughts on #617, I think?

Indeed, although I think there were more people speaking out against #617.

@petrrr
Copy link
Contributor

petrrr commented Jul 17, 2014

Hi guys,
I was not aware of this discussion, but by incidence I proposed something quite similar earlier today (what a coincidence) in #841.

@petrrr petrrr added this to the 0.10.0 milestone Jul 25, 2014
@krischer krischer modified the milestones: 0.10.0, 0.11.0 Jul 29, 2014
@megies megies mentioned this pull request Sep 2, 2015
@megies megies modified the milestones: 0.11.0, 0.12.0 Oct 29, 2015
@megies megies mentioned this pull request Oct 31, 2015
@megies
Copy link
Member

megies commented May 18, 2016

2 I started to build up the frequency_domain_trace.py, but got problems with the circular imports in python, because I generate in the tr.fft Method an instance of the FrequencyDomainTrace class and in the tr_f.ifft Method an instance of the Trace class. Is there a nice way to do it?

If it's gonna be in separate files as @krischer proposed, the only way is to do the from obspy import Trace import not at top of frequency_domain_trace.py but inside the def ifft(self, ...): block (and vice versa with import of FrequencyDomainTrace in trace.py).

@fstettner
Copy link

I moved the FrequencyDomainTrace to the frequency_domain_trace.py like @krischer recommended. Additionally, I built in the numpy rfft, irfft and fftfreq , because of the return type of the scipy version. The fft is now attached to the TimeSeriesTrace with an differentiation of Trace and TimeSeriesTrace. I also started to build up the TimeSeriesFrequencyDomainTrace, that has currently just the ifft implemented.

data2 = tr2.data
data2_conj = np.conj(data2)
cross_spectrum = data2_conj*data1
coherence = (cross_spectrum)**2/(np.abs(data1)*np.abs(data2))
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fstettner, smoothing of spectra is missing here..

In the long run I would propose adding a method to FFTTrace, e.g. def smoothed(self, window, npts) (or width given in Hz).
For now you could just hardcode some fixed window from scipy, see http://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.get_window.html#scipy.signal.get_window. For orientation, Poupinet 1984 used a five point triangular window for 100Hz sampled data.

CC @jwassermann

@fstettner
Copy link

I implemented the conjugate method. The coherence-method also should work now (with hanning).
I still have problems with the normalization of the cross_correlation-method. Are there some ideas?
Are the differentiation and integration method useful implemented?

yf = self.data
xf = np.linspace(0.0, 1.0/(2.0*T), N/2)
fig, ax = plt.subplots()
ax.plot(xf, 2.0/N * np.abs(yf[:N/2]))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should just use the self.frequencies() method which would me much easier.

@megies megies modified the milestones: 1.1.0, 1.2.0 Nov 7, 2016
@ThomasLecocq
Copy link
Contributor

👍 to get this done asap!

@megies
Copy link
Member

megies commented Mar 20, 2017

This was done as a student project and likely needs more work.. :-/

@megies megies modified the milestones: 1.2.0, 1.3.0 Apr 19, 2018
@megies megies modified the milestones: 1.3.0, 2.0.0 Feb 15, 2019
@obspy-bot
Copy link
Contributor

This pull request has been mentioned on ObsPy Forum. There might be relevant details there:

https://discourse.obspy.org/t/trace-remove-response-outputting-disp-or-acc/1259/6

@trichter trichter modified the milestones: 1.3.0, 1.4.0 Jan 6, 2022
@megies megies modified the milestones: 1.4.0, Future release Jun 21, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
.core issues affecting our functionality at the very core enhancement feature request priority: high .signal issues related to our signal processing functionalities
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants