Last updated on August 12th 2016.

17. Customizing LASIF

Sometimes the built-in configuration possibilities of LASIF just don’t cut it and you need to change things in a more profound way. To this end LASIF offers per-project functions that will be called by LASIF on different occasions.

If you still feel the need to modify LASIF directly, please contact the developers. LASIF aims to be a common tool for full waveform inversions and if everyone has a custom LASIF version that is not possible. If necessary we will make it more flexible.

In any case, the per-project functions are stored in the FUNCTION subfolder of an active LASIF project. If you ever want to update these function to the latest official version of LASIF just delete the files and launch any LASIF command.

FUNCTIONS
├── preprocessing_function.py
├── process_synthetics.py
└── window_picking_function.py

17.1. Common Features of the Custom Functions

Warning

LASIF does not keep track of which function has been called for which iteration. This is a choice to make customizing it reasonably easy but it does hurt reproducibility and provenance. It is your responsibility to store the used functions and make a copy if necessary. A simple way to keep track of the differences in these function is to branch based on the current iteration.

All of the custom function are passed an Iteration object, thus code like the following is the preferred way to handle differences in these function per iterations.

def custom_function(arg1, iteration):
    # Assumes iteration names are numeric. You might need a more
    # complicated setup depending on your project.
    if int(iteration.name) < 10:
        do_this()
    else:
        do_that()

To figure out what these iteration objects can do, launch an interactive lasif session and play around a bit.

$ lasif shell
In [1]: iteration = comm.iterations.get("1")

In [2]: iteration.name
Out[2]: '1'

In [3]: iteration.get_process_params()
Out[3]:
{'dt': 0.3,
 'highpass': 0.01,
 'lowpass': 0.025,
 'npts': 2000,
 'stf': 'Filtered Heaviside'}

17.2. Custom Preprocessing

The preprocessing function is used to preprocess all the data. It has the following function signature:

def preprocessing_function(processing_info, iteration):
    ...

It is supposed to read the procesing_info["input_filename"] file, apply the defined processing, and write it to processing_info["output_filename"]. It does not have to return anything; raise an error if something does not work as expected.

The processing_info object also contains event and station information.

{'event_information': {
    'depth_in_km': 22.0,
    'event_name': 'GCMT_event_VANCOUVER_ISLAND...',
    'filename': '/.../GCMT_event_VANCOUVER_ISLAND....xml',
    'latitude': 49.53,
    'longitude': -126.89,
    'm_pp': 2.22e+18,
    'm_rp': -2.78e+18,
    'm_rr': -6.15e+17,
    'm_rt': 1.98e+17,
    'm_tp': 5.14e+18,
    'm_tt': -1.61e+18,
    'magnitude': 6.5,
    'magnitude_type': 'Mwc',
    'origin_time': UTCDateTime(2011, 9, 9, 19, 41, 34, 200000),
    'region': u'VANCOUVER ISLAND, CANADA REGION'},
 'input_filename': u'/.../raw/7D.FN01A..HHZ.mseed',
 'output_filename': u'/.../processed_.../7D.FN01A..HHZ.mseed',
 'process_params': {
    'dt': 0.75,
    'highpass': 0.007142857142857143,
    'lowpass': 0.0125,
    'npts': 2000,
    'stf': 'Filtered Heaviside'},
 'station_coordinates': {
    'elevation_in_m': -54.0,
    'latitude': 46.882,
    'local_depth_in_m': None,
    'longitude': -124.3337},
 'station_filename': u'/.../STATIONS/RESP/RESP.7D.FN01A..HH*'}

17.3. Custom Synthetic Data Processing

LASIF, by default, does not apply any processing (except rotations and component flips to get them to ZNE) to the synthetics but just uses them as is. If your workflow requires the synthetics to be processed, please do it in this function. It will be applied on the fly to each up-to-three component Stream object of synthetic data.

The default implementation is this:

def process_synthetics(st, iteration, event):
    return st

This is very useful for processing the synthetics in any fashion or to shift them time and similar endeavours. Make sure it returns a Stream object.

iteration and event are the Iteration object of the current iteration and a dictionary containing information about the data’s event, respectively.

17.4. Customize Window Picking

Use this function to customize the window picking of LASIF, or even use a completely different window picking algorithm. Its function signature is:

def window_picking_function(data_trace, synthetic_trace, event_latitude,
                            event_longitude, event_depth_in_km,
                            station_latitude, station_longitude,
                            minimum_period, maximum_period,
                            iteration):
    ...

    # Make sure it return a list of tuples, each denoting start and
    # endtime for each picked window.
    return [(obspy.UTCDateTime(...), obspy.UTCDateTime(...)),
            (obspy.UTCDateTime(...), obspy.UTCDateTime(...))]

17.5. Customize the Source Time Function

This function is used to generate source time functions for SES3D. If you don’t use SES3D for the numerical wavefield simulations you can ignore this. Its function signature is:

def source_time_function(npts, delta, freqmin, freqmax, iteration):
    ...
    # Make sure it returns a float64 NumPy array with `npts` samples.
    return np.array(data, dtype=np.float64)