Advanced Server Configuration

Certain advanced functionality requires a more extensive server configuration. This is a bit more work but you can tweak that functionality to best suite your own needs.

You can find a complete example implementation here.

It works by implementing up to three callback functions to:

  • resolve station coordinates from network and station codes
  • resolve event parameters from event ids
  • calculate theoretical travel times

You can choose to implement any number of these - appropriate error messages will be raised if the server requires a callback function this is not available. The rest will keep on working just fine.

Note

Please raise a ValueError within in the callback functions if something does not go as expected. That will be caught by Instaseis; other exceptions will result in a return code 500 (internal server error).

Finite Source Size Setting

Per default the \finite_source route (POST /finite_source) allows at most 1000 point sources for a single finite source. This limit can be changed with the max_size_of_finite_sources parameter passed to the server launching script either on the command line or in the script.

Station Coordinates Callback

This callback function is used for the /coordinates route, as well as network and station based queries in the /seismograms route. It resolves network and station code searches to actual coordinates. How the coordinates are resolved is up to the users but make sure it is fast. Also make sure the coordinates are geocentric. Instaseis has a helper function to aid with that: instaseis.helpers.elliptic_to_geocentric_latitude()

Affected routes:

Function Signature:

def get_station_coordinates(networks, stations):
    ...
    return [...]

Parameters:

  • networks: [list of strings], list of case-insensitive network ids. Wildcards * and ? are allowed.
  • stations: [list of strings], list of case-insensitive station ids. Wildcards * and ? are allowed.

Return Values:

  • A list with one dictionary per found station if successful. Each dictionary has the following keys: "network", "station", "latitude", "longitude".
  • An empty list if no station matching the query has been found.
  • Raise a ValueError for other cases.

Examples:

>>> get_station_coordinates(networks=["IU"], stations["ANMO"])
[{"latitude": 34.94591,
   "longitude": -106.4572,
   "network": "IU",
   "station": "ANMO"}]

>>> get_station_coordinates(networks=["IU", "B*"], stations["ANT*", "ANM?"])
[{"latitude": 39.868,
  "longitude": 32.7934,
  "network": "IU",
  "station": "ANTO"
 }, {
  "latitude": 34.94591,
  "longitude": -106.4572,
  "network": "IU",
  "station": "ANMO"}]

>>> get_station_coordinates(networks=["AA", "BB"], stations["CC", "DD"])
[]

Event Parameters Callback

This callback function has a single purpose: resolve an event identifier to event parameters. Users could choose to call an external web service within that function or query a local database. It is used for the /event route as well as event identifier based queries in the /seismograms route. Make sure the coordinates are geocentric. Instaseis has a helper function to aid with that: instaseis.helpers.elliptic_to_geocentric_latitude()

Affected routes:

Function Signature:

def get_event(event_id):
    ...
    return {...}

Parameters:

  • event_id: [str], event identifier

Return Values:

  • A dictionary with the event parameters and the following keys: "m_rr", "m_tt", "m_pp", "m_rt", "m_rp", "m_tp", "latitude", "longitude", "depth_in_m", "origin_time". The tensor components have to be in Nm.
  • A ValueError will be always be interpreted as a not found event.

Examples:

>>> get_event("B071791B")
{"m_rr": -58000000000000000,
 "m_tt": 78100000000000000,
 "m_pp": -20100000000000000,
 "m_rt": -56500000000000000,
 "m_rp": 108100000000000000,
 "m_tp": 315300000000000000,
 "latitude": -3.8,
 "longitude": -104.21,
 "depth_in_m": 0,
 "origin_time": "1991-07-17T16:41:33.100000Z"}

>>> get_event("random_things")
ValueError: Event not found.

Travel Time Callback

This callback function is used for the /ttimes route and for the phase relative start and end times in the /seismograms route. It receives source and receiver coordinates as well as a phase name and is supposed to return the travel time from source to receiver for that particular phase in seconds. The coordinates can be assumed to be geocentric and the calculations should happen in a spherical planet. Make sure to perform the calculations in the same model that has been used to calculate the databases. Please note that this callback function also receives the info attribute dictionary of the database.

Affected routes:

Function Signature:

def get_travel_time(sourcelatitude, sourcelongitude, sourcedepthinmeters,
                    receiverlatitude, receiverlongitude,
                    receiverdepthinmeters, phase_name, db_info):
    ...
    return ttime

Parameters:

  • sourcelatitude: [float], geocentric source latitude
  • sourcelongitude: [float], source longitude
  • sourcedepthinmeters: [float], source depth in meters
  • receiverlatitude: [float], geocentric receiver latitude
  • receiverlongitude: [float], receiver longitude
  • receiverdepthinmeters: [float], receiver depth in meters
  • phase_name: [str], case-sensitive phase name
  • db_info: The info attribute dictionary of the used database. Useful to calculate travel times for different models depending on the database.

Return Values:

  • Travel time in seconds if successful.
  • None if phase has no arrival for the given source-receiver geometry.
  • Raise a ValueError for other cases, e.g. unknown phase name, invalid source-receiver geometry, …

Examples:

>>> get_travel_time(0.0, 50.0, 300000, 0.0, 0.0, 0.0, "P", {})
504.357

>>> get_travel_time(0.0, 50.0, 300000, 0.0, 0.0, 0.0, "Pdiff", {})
None

>>> get_travel_time(0.0, 50.0, 300000, 0.0, 0.0, 0.0, "bogus", {})
ValueError: Invalid phase name.

Hooking it up to the Instaseis Server

Best have a look at the full example implementation here.

You will have to create a new file and pass the three callback functions to the launch_io_loop() function. The following code snippet will give you a similar command line interface to the default Instaseis server:

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)
import argparse
import os

from instaseis.server.app import launch_io_loop

if __name__ == "__main__":
    parser = argparse.ArgumentParser(
        prog="python -m instaseis.server",
        description='Launch an Instaseis server offering seismograms with a '
                    'REST API.')
    parser.add_argument('--port', type=int, required=True,
                        help='Server port.')
    parser.add_argument('--buffer_size_in_mb', type=int,
                        default=0, help='Size of the buffer in MB')
    parser.add_argument('--max_size_of_finite_sources', type=int,
                        default=1000,
                        help='The maximum allowed number of point sources in '
                             'a single finite source for the /finite_source '
                             'route.')

    parser.add_argument('db_path', type=str,
                        help='Database path')
    parser.add_argument(
        '--quiet', action='store_true',
        help="Don't print any output. Overwrites the 'log_level` setting.")
    parser.add_argument(
        '--log-level', type=str, default='INFO',
        choices=['CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'NOTSET'],
        help='The log level for all Tornado loggers.')

    args = parser.parse_args()
    db_path = os.path.abspath(args.db_path)

    launch_io_loop(db_path=db_path, port=args.port,
                   buffer_size_in_mb=args.buffer_size_in_mb,
                   quiet=args.quiet, log_level=args.log_level,
                   max_size_of_finite_sources=args.max_size_of_finite_sources,
                   station_coordinates_callback=get_station_coordinates,
                   event_info_callback=get_event,
                   travel_time_callback=get_travel_time)