LANL-Seismoacoustics / pisces

Pisces: A practical seismological database library in Python.
http://lanl-seismoacoustics.github.io/pisces/
Other
17 stars 10 forks source link

attaching response in request.py #78

Open jkmacc-LANL opened 6 months ago

jkmacc-LANL commented 6 months ago

Adding a (couple) variables to pass attach_response = True through to get_wfdisc_rows would be the cleanest implementation. A wfdisc row will have all of the information to get a response correctly in 90-95% of cases. There may be some edge cases where things go sideways, but we can deal with that later. Would need to pass attach_response = True and which calib/calper to use, the variables in wfdisc or the nominal variables in instrument. I think, for the time being, we would make assumptions on final units (Meters/Pascals) on behalf of the user though we could pass an additional variable.

ping: @cnlg-lanl @jkmacc-LANL @jwebster-LANL

Thoughts?

jkmacc-LANL commented 6 months ago

If you added a flag to get_wfdisc_rows, wouldn't you also need to provide Sensor/Instrument tables? This feels out-of-scope for the function. Do you think it's tractable to have a separate function to do the Sensor/Instrument table queries, another one to build ObsPy Response objects from them, and final one to attach them to traces in a Stream?

cnlg-lanl commented 6 months ago

Right...that's a good point. A wfdisc row will have the information to get the response from sensor/instrument, but if a user wants to use the calib and calper from the wfdisc table as opposed to the instrument table that would require recalling the wfdisc table. If we are going to build the responses completely separately and attach, then it would just make sense to build entire inventories and using the obspy attach_response() function...but that comes with it's own adjustments for the network section of the ids. Could also build a response_from_wfid() type function where the wfids from get_wfdisc_rows could be passed through...but that would require outputting the wfids separately since get_waveforms() calls get_wfdisc_rows() and to make it one smooth function would still require the sensor and instrument tables. That's why passing the wfdisc calib/calper info trace.stats variables is one option since then could just go from stream directly to sensor/instrument without have to recall the wfdisc table.

jwebster-LANL commented 6 months ago

FWIW The way I use these functions is I first use the get_wfdisc_rows to get, well, the wfdisc rows which i display in a table. Then someone selects which rows they want to download and I call get_waveforms with the info from the selected rows.

There's probably a better way to do it, but it works for me.

In that workflow, just passing something like "remove_response=True" into get_waveforms would be the more intuitive way to go about it.

cnlg-lanl commented 6 months ago

What do you pass through to get_waveforms? wfids? If that's the case it may be a little simpler as long as the sensor and instrument table are defined in the config file.

jwebster-LANL commented 6 months ago

Right now it looks like just station, start/end times. I could probably change it to wfids, that sounds like a better solution anyway. It would still need the start/end times?

jkmacc-LANL commented 6 months ago

get_waveforms already accepts wfids, iirc. Not that I'm advocating.

cnlg-lanl commented 6 months ago

wfids would give me the start and endtimes of that particular segment, so that would be enough I think

jwebster-LANL commented 6 months ago

hmm, looking at the documentation, if you pass a wfid to get_waveforms, it returns the full wfdisc row waveform(s)... I don't think that's what I want, I just want the portion from the start/end times (ie trimmed).

...Come to think of it, I kinda remember that being the reason I didn't use wfids...

def get_waveforms(session, wfdisc, station=None, channel=None, starttime=None,
                  endtime=None, wfids=None, tol=None, asquery=False):
    """
    Request waveforms.

    Parameters
    ----------
    session : sqlalchemy.orm.Session instance
        Must be bound.
    wfdisc : mapped Wfdisc table class
    station, channel : str, optional
        Desired station, channel code strings
    starttimes, endtimes : float, optional
        Epoch start times, end times.
        Traces will be cut to these times.
    wfids : iterable of int, optional
        Wfdisc wfids.  Obviates the above arguments and just returns full Wfdisc
        row waveforms.
    tol : float
        If provided, a warning is fired if any Trace is not within tol seconds
        of starttime and endtime.
    asquery : bool, optional
        Return the query object instead of the results.  Default, False.
        Useful if additional you desire additional sorting of filtering.
jkmacc-LANL commented 6 months ago

Wouldn't trimming the stream be an ObsPy one-liner? No ideal for long traces, I guess, but hopefully not a deal-breaker...

jwebster-LANL commented 6 months ago

unless the time span straddled one or more files? Maybe not too difficult, but passing a station with start and end times does it for me so why not just do that?

It makes sense that get_waveforms would return a full wfdisc file when passed a wfid, so that shouldn't change I think.

cnlg-lanl commented 6 months ago

Maybe it just makes sense to do a stream to response type function? Ultimately, the trace stats have all of the same fields as a wfdisc row aka station, channel, starttime, endtime

jkmacc-LANL commented 6 months ago

It's also reasonable to imagine tacking any other Wfdisc metadata to Trace.stats, as other readers do. That could give you the option to use .calib and .calper as they came from the database directly from the Trace, without needing to recall the Wfdisc table. I'm happy to add something to Wfdisc.to_trace that would include all the wfdisc row data to tr.stats. Maybe to tr.stats.wfdisc?

cnlg-lanl commented 6 months ago

Working on this...what I realized today is that read_waveform() which is called by wfdisc2trace() already populates the calib from the wfdisc table in the header...if we also have it populate the calper, then everything I would need to compare and check against the sensor/instrument table would already be there and I wouldn't have to go back to wfdisc....but we would have to set a custom value for calper as that's not in the obspy header spec