Ejemplo n.º 1
0
    def test_compute_ray_paths(self):
        # careful, the full inventory, catalog test is long (1min)
        # greatcircles = get_ray_paths(
        #        inventory=self.inventory, catalog=self.catalog,
        #        phase_list=['P'], coordinate_system='XYZ',
        #        taup_model='iasp91')

        # this test checks if we get a single P wave greatcircle
        station = obspy.core.inventory.Station(
            code='STA', latitude=0., longitude=30., elevation=0.)
        # make two (identical) stations
        network = obspy.core.inventory.Network(
            code='NET', stations=[station, station])
        inventory = obspy.core.inventory.Inventory(
            source='ME', networks=[network])

        otime = obspy.UTCDateTime('2017-02-03T12:00:00.0Z')
        origin = obspy.core.event.Origin(latitude=0., longitude=90.,
                                         depth=100000., time=otime)
        origin.resource_id = 'smi:local/just-a-test2'
        magnitude = obspy.core.event.Magnitude(mag=7.)
        event = obspy.core.event.Event(origins=[origin],
                                       magnitudes=[magnitude])
        event.resource_id = 'smi:local/just-a-test'
        # make three (identical) events
        catalog = obspy.core.event.Catalog(events=[event, event, event])

        # query for four phases
        greatcircles = get_ray_paths(
            inventory, catalog, phase_list=['P', 'PP', 'S', 'SS'],
            coordinate_system='XYZ', taup_model='iasp91')
        # two stations, three events, 4 phases should yield 24 rays (since all
        # arrivals are encountered in this case)
        self.assertEqual(len(greatcircles), 24)
        # now check details of first ray
        circ = greatcircles[0]
        path = circ[0]
        self.assertEqual(circ[1], 'P')
        self.assertEqual(circ[2], 'NET.STA')
        np.testing.assert_allclose(circ[3], otime.timestamp, atol=1e-5, rtol=0)
        self.assertEqual(circ[4], 7.0)
        self.assertEqual(circ[5], 'smi:local/just-a-test')
        self.assertEqual(circ[6], 'smi:local/just-a-test2')
        self.assertEqual(path.shape, (3, 274))
        # now check some coordinates of the calculated path, start, end and
        # some values in between
        path_start_expected = [
            [6.02712296e-17, 4.63135005e-05, 1.82928142e-03, 1.99453947e-03,
             2.16015881e-03],
            [9.84303877e-01, 9.84224340e-01, 9.81162947e-01, 9.80879369e-01,
             9.80595408e-01],
            [6.02712296e-17, 6.02663595e-17, 6.00790075e-17, 6.00616631e-17,
             6.00442971e-17]]
        np.testing.assert_allclose(path[:, :5], path_start_expected, rtol=1e-5)
        path_end_expected = [
            [8.65195410e-01, 8.65610142e-01, 8.65817499e-01,
             8.66024850e-01, 8.66025746e-01],
            [4.99866617e-01, 4.99932942e-01, 4.99966104e-01,
             4.99999264e-01, 4.99999407e-01],
            [6.11842455e-17, 6.12082668e-17, 6.12202774e-17,
             6.12322880e-17, 6.12323400e-17]]
        np.testing.assert_allclose(path[:, -5:], path_end_expected, rtol=1e-5)
        path_steps_expected = [
            [6.02712296e-17, 5.99694796e-03, 1.55844904e-02,
             2.29391617e-02, 3.12959401e-02, 4.94819381e-02,
             6.59026261e-02, 8.84601669e-02, 1.15734196e-01,
             1.30670566e-01, 1.83202229e-01, 2.21387617e-01,
             3.00609265e-01, 4.51339383e-01, 5.39194024e-01,
             5.84050335e-01, 6.48856399e-01, 6.68018760e-01,
             7.03962600e-01, 7.34809690e-01, 7.59887900e-01,
             7.89619836e-01, 8.04521516e-01, 8.18115511e-01,
             8.36517555e-01, 8.48367905e-01, 8.59911335e-01,
             8.65610142e-01],
            [9.84303877e-01, 9.74082937e-01, 9.58372986e-01,
             9.46927810e-01, 9.34554991e-01, 9.10758513e-01,
             8.91322841e-01, 8.68804460e-01, 8.43147543e-01,
             8.29702355e-01, 7.85418525e-01, 7.55840616e-01,
             7.00517755e-01, 6.14315731e-01, 5.74088249e-01,
             5.56174903e-01, 5.33353699e-01, 5.27297943e-01,
             5.16800783e-01, 5.08777243e-01, 5.04479841e-01,
             5.00872221e-01, 4.99943609e-01, 4.99408317e-01,
             4.99111122e-01, 4.99125255e-01, 4.99180374e-01,
             4.99932942e-01],
            [6.02712296e-17, 5.96465079e-17, 5.86911789e-17,
             5.79996164e-17, 5.72570664e-17, 5.58501220e-17,
             5.47267635e-17, 5.34739746e-17, 5.21120017e-17,
             5.14308206e-17, 4.93839986e-17, 4.82263481e-17,
             4.66770017e-17, 4.66770017e-17, 4.82263481e-17,
             4.93839986e-17, 5.14308206e-17, 5.21120017e-17,
             5.34739746e-17, 5.47267635e-17, 5.58501220e-17,
             5.72570664e-17, 5.79996164e-17, 5.86911789e-17,
             5.96465079e-17, 6.02712296e-17, 6.08831943e-17,
             6.12082668e-17]]
        np.testing.assert_allclose(path[:, ::10], path_steps_expected,
                                   rtol=1e-5)

        # now do the same for RTP coordinate system and also use a different
        # model
        # query for four phases
        greatcircles = get_ray_paths(
            inventory, catalog, phase_list=['P', 'PP', 'S', 'SS'],
            coordinate_system='RTP', taup_model='ak135')
        # two stations, three events, 4 phases should yield 24 rays (since all
        # arrivals are encountered in this case)
        self.assertEqual(len(greatcircles), 24)
        # now check details of first ray
        circ = greatcircles[0]
        path = circ[0]
        self.assertEqual(circ[1], 'P')
        self.assertEqual(circ[2], 'NET.STA')
        np.testing.assert_allclose(circ[3], otime.timestamp, atol=1e-5, rtol=0)
        self.assertEqual(circ[4], 7.0)
        self.assertEqual(circ[5], 'smi:local/just-a-test')
        self.assertEqual(circ[6], 'smi:local/just-a-test2')
        self.assertEqual(path.shape, (3, 270))
        # now check some coordinates of the calculated path, start, end and
        # some values in between
        path_start_expected = [
            [0.984304, 0.984217, 0.981165, 0.980880, 0.980595],
            [1.570796, 1.570796, 1.570796, 1.570796, 1.570796],
            [1.570796, 1.570745, 1.568935, 1.568765, 1.568595]]
        np.testing.assert_allclose(path[:, :5], path_start_expected, rtol=1e-6)
        path_end_expected = [
            [0.998124, 0.999062, 0.999531, 0.999765, 1.000000],
            [1.570796, 1.570796, 1.570796, 1.570796, 1.570796],
            [0.524316, 0.523957, 0.523778, 0.523688, 0.523599]]
        np.testing.assert_allclose(path[:, -5:], path_end_expected, rtol=1e-6)
        path_steps_expected = [
            [0.98430388, 0.97410140, 0.95847208, 0.94717382, 0.93508421,
             0.91210171, 0.89145501, 0.86719412, 0.84963114, 0.83022016,
             0.80548417, 0.78780343, 0.76158226, 0.76488692, 0.79524407,
             0.80633663, 0.83604439, 0.85093573, 0.87434336, 0.89536778,
             0.91994977, 0.93757572, 0.94908037, 0.95919008, 0.97447903,
             0.98726847, 0.99568357],
            [1.57079633, 1.57079633, 1.57079633, 1.57079633, 1.57079633,
             1.57079633, 1.57079633, 1.57079633, 1.57079633, 1.57079633,
             1.57079633, 1.57079633, 1.57079633, 1.57079633, 1.57079633,
             1.57079633, 1.57079633, 1.57079633, 1.57079633, 1.57079633,
             1.57079633, 1.57079633, 1.57079633, 1.57079633, 1.57079633,
             1.57079633, 1.57079633],
            [1.57079633, 1.56464945, 1.55454336, 1.54659206, 1.53738096,
             1.51661510, 1.49423077, 1.46055656, 1.43236890, 1.39637248,
             1.33996718, 1.28795961, 1.16342477, 0.91617848, 0.79110880,
             0.76040906, 0.69478715, 0.66799188, 0.63152179, 0.60344720,
             0.57851384, 0.56322297, 0.55460834, 0.54755404, 0.53770068,
             0.53004245, 0.52531799]]
        np.testing.assert_allclose(path[:, ::10], path_steps_expected,
                                   rtol=1e-6)
Ejemplo n.º 2
0
def _write_vtk_files(inventory,
                     catalog,
                     phase_list=('P'),
                     taup_model='iasp91'):
    """
    internal vtk output routine. Check out the plot_rays routine
    for more information on the parameters
    """
    # define file names
    fname_paths = 'paths.vtk'
    fname_events = 'events.vtk'
    fname_stations = 'stations.vtk'

    # get 3d paths for all station/event combinations
    greatcircles = get_ray_paths(inventory,
                                 catalog,
                                 phase_list=phase_list,
                                 coordinate_system='XYZ',
                                 taup_model=taup_model)

    # sphere radii
    r_earth_m = taup_model.model.radius_of_planet
    r_earth = 1.0
    r_cmb = (r_earth_m - taup_model.model.cmb_depth) / r_earth_m
    r_iocb = (r_earth_m - taup_model.model.iocb_depth) / r_earth_m

    # now assemble all points, stations and connectivity
    stations = []  # unique list of stations
    events = []  # unique list of events
    lines = []  # contains the points that constitute each ray
    npoints_tot = 0  # this is the total number of points of all rays
    istart_ray = 0  # this is the first point of each ray in the loop
    points = []
    for gcircle, name, stlabel, time, magnitude, evid, _ in greatcircles:
        date = UTCDateTime(time)
        evlabel = '{:s} | M{:.1f}'.format(str(date.date), magnitude)
        points_ray = gcircle[:, ::3]  # use every third point
        ndim, npoints_ray = points_ray.shape
        iend_ray = istart_ray + npoints_ray
        connect_ray = np.arange(istart_ray, iend_ray, dtype=int)

        lines.append(connect_ray)
        points.append(points_ray)
        if stlabel not in stations:
            stations.append((gcircle[:, -1], stlabel))

        if evlabel not in events:
            events.append((gcircle[:, 0], evlabel))

        npoints_tot += npoints_ray
        istart_ray += npoints_ray

    # write the ICB, CMB and Earth surface in one file
    with open(fname_stations, 'w') as vtk_file:
        # write some header information
        vtk_header = ('# vtk DataFile Version 2.0\n'
                      'station locations\n'
                      'ASCII\n'
                      'DATASET UNSTRUCTURED_GRID\n'
                      'POINTS {:d} float\n'.format(3))
        vtk_file.write(vtk_header)

        # write a long list of all points
        for location, stlabel in []:
            vtk_file.write('{:.4e} {:.4e} {:.4e}\n'.format(*location))

    # write the ray paths in one file
    with open(fname_paths, 'w') as vtk_file:
        # write some header information
        vtk_header = ('# vtk DataFile Version 2.0\n'
                      '3d ray paths\n'
                      'ASCII\n'
                      'DATASET UNSTRUCTURED_GRID\n'
                      'POINTS {:d} float\n'.format(npoints_tot))
        vtk_file.write(vtk_header)

        # write a long list of all points
        for x, y, z in np.hstack(points).T:
            vtk_file.write('{:.4e} {:.4e} {:.4e}\n'.format(x, y, z))

        # now write connectivity
        nlines = len(lines)
        npoints_connect = npoints_tot + nlines
        vtk_file.write('CELLS {:d} {:d}\n'.format(nlines, npoints_connect))
        for line in lines:
            vtk_file.write('{:d} '.format(len(line)))
            for ipoint in line:
                if ipoint % 30 == 29:
                    vtk_file.write('\n')
                vtk_file.write('{:d} '.format(ipoint))

        # cell types. 4 means cell type is a poly_line
        vtk_file.write('\nCELL_TYPES {:d}\n'.format(nlines))
        for line in lines:
            vtk_file.write('4\n')

    # write the stations in another file
    with open(fname_stations, 'w') as vtk_file:
        # write some header information
        vtk_header = ('# vtk DataFile Version 2.0\n'
                      'station locations\n'
                      'ASCII\n'
                      'DATASET UNSTRUCTURED_GRID\n'
                      'POINTS {:d} float\n'.format(len(stations)))
        vtk_file.write(vtk_header)

        # write a long list of all points
        for location, stlabel in stations:
            vtk_file.write('{:.4e} {:.4e} {:.4e}\n'.format(*location))

    # write the events in another file
    with open(fname_events, 'w') as vtk_file:
        # write some header information
        vtk_header = ('# vtk DataFile Version 2.0\n'
                      'event locations\n'
                      'ASCII\n'
                      'DATASET UNSTRUCTURED_GRID\n'
                      'POINTS {:d} float\n'.format(len(events)))
        vtk_file.write(vtk_header)

        # write a long list of all points
        for location, evlabel in events:
            vtk_file.write('{:.4e} {:.4e} {:.4e}\n'.format(*location))
Ejemplo n.º 3
0
def _plot_rays_mayavi(inventory,
                      catalog,
                      phase_list=['P'],
                      colorscheme='default',
                      animate=False,
                      savemovie=False,
                      figsize=(800, 800),
                      taup_model='iasp91',
                      coastlines='internal',
                      icol=0,
                      event_labels=True,
                      station_labels=True,
                      fname_out=None,
                      view_dict=None):
    """
    internal mayavi plotting routine. Check out the plot_rays routine
    for more information on the parameters
    """
    try:
        from mayavi import mlab
    except Exception as err:
        print(err)
        msg = ("obspy failed to import mayavi. "
               "You need to install the mayavi module "
               "(e.g. conda install mayavi, pip install mayavi). "
               "If it is installed and still doesn't work, "
               "try setting the environmental variable QT_API to "
               "pyqt (e.g. export QT_API=pyqt) before running the "
               "code. Another option is to avoid mayavi and "
               "directly use kind='vtk' for vtk file output of the "
               "radiation pattern that can be used by external "
               "software like paraview")
        raise ImportError(msg)

    if isinstance(taup_model, str):
        from obspy.taup import TauPyModel
        model = TauPyModel(model=taup_model)
    else:
        model = taup_model

    if fname_out is not None:
        offscreen = True
    else:
        offscreen = False

    nphases = len(phase_list)

    greatcircles = get_ray_paths(inventory=inventory,
                                 catalog=catalog,
                                 phase_list=phase_list,
                                 coordinate_system='XYZ',
                                 taup_model=model)

    if len(greatcircles) == 0:
        raise ValueError('no paths found for the input stations and events')

    # define colorschemes
    if colorscheme == 'dark' or colorscheme == 'default':
        # we use the color set that is used in taup, but adjust the lightness
        # to get nice shiny rays that are well visible in the dark 3d plots
        from obspy.taup.tau import COLORS
        ncolors = len(COLORS)
        color_hues = [rgb_to_hls(*hex2color(col))[0] for col in COLORS]
        # swap green and red to start with red:
        color_hues[2], color_hues[1] = color_hues[1], color_hues[2]
        # first color is for the continents etc:
        continent_hue = color_hues[0]
        event_hue = color_hues[-1]
        # the remaining colors are for the rays:
        ray_hues = color_hues[1:-1]

        # now convert all of the hues to rgb colors:
        ray_light = 0.45
        ray_sat = 1.0
        raycolors = [
            hls_to_rgb(ray_hues[(iphase + icol) % (ncolors - 2)], ray_light,
                       ray_sat) for iphase in range(nphases)
        ]
        stationcolor = hls_to_rgb(continent_hue, 0.7, 0.7)
        continentcolor = hls_to_rgb(continent_hue, 0.3, 0.2)
        eventcolor = hls_to_rgb(event_hue, 0.7, 0.7)
        cmbcolor = continentcolor
        bgcolor = (0, 0, 0)

        # sizes:
        sttextsize = (0.015, 0.015, 0.015)
        stmarkersize = 0.01
        evtextsize = (0.015, 0.015, 0.015)
        evmarkersize = 0.05

    elif colorscheme == 'bright':
        # colors (a distinct colors for each phase):
        lightness = 0.2
        saturation = 1.0
        ncolors = nphases + 2  # two extra colors for continents and events
        hues = np.linspace(0., 1. - 1. / ncolors, ncolors)

        raycolors = [
            hls_to_rgb(hue, lightness, saturation) for hue in hues[2:]
        ]

        stationcolor = hls_to_rgb(hues[0], 0.2, 0.5)
        continentcolor = hls_to_rgb(hues[0], 0.6, 0.2)
        eventcolor = hls_to_rgb(hues[1], 0.2, 0.5)
        cmbcolor = continentcolor
        bgcolor = (1, 1, 1)
        # sizes:
        # everything has to be larger in bright background plot because it
        # is more difficult to read
        sttextsize = (0.02, 0.02, 0.02)
        stmarkersize = 0.02
        evtextsize = (0.02, 0.02, 0.02)
        evmarkersize = 0.06
    else:
        raise ValueError('colorscheme {:s} not recognized'.format(colorscheme))

    # assemble each phase and all stations/events to plot them in a single call
    stations_loc = []
    stations_lab = []
    events_loc = []
    events_lab = []
    phases = [[] for iphase in range(nphases)]
    for gcircle, name, stlabel, time, magnitude, evid, _ in greatcircles:
        iphase = phase_list.index(name)
        phases[iphase].append(gcircle)
        date = UTCDateTime(time)
        evlabel = '{:s} | M{:.1f}'.format(str(date.date), magnitude)

        if stlabel not in stations_lab:
            x, y, z = gcircle[0, -1], gcircle[1, -1], gcircle[2, -1]
            stations_loc.append((x, y, z))
            stations_lab.append(stlabel)

        if evlabel not in events_lab:
            x, y, z = gcircle[0, 0], gcircle[1, 0], gcircle[2, 0]
            events_loc.append((x, y, z))
            events_lab.append(evlabel)

    # now begin mayavi plotting
    if offscreen:
        mlab.options.offscreen = True
    fig = mlab.figure(size=figsize, bgcolor=bgcolor)

    # define connectivity of each ray and add it to the scene
    for iphase, phase in enumerate(phases):
        index = 0
        connects = []
        for ray in phase:
            ndim, npoints = ray.shape
            connects.append(
                np.vstack([
                    np.arange(index, index + npoints - 1.5),
                    np.arange(index + 1, index + npoints - .5)
                ]).T)
            index += npoints

        # collapse all points of the phase into a long array
        if len(phase) > 1:
            points = np.hstack(phase)
        elif len(phase) == 1:
            points = phase[0]
        else:
            continue
        connects = np.vstack(connects)

        # Create the points
        src = mlab.pipeline.scalar_scatter(*points)

        # Connect them
        src.mlab_source.dataset.lines = connects

        # The stripper filter cleans up connected lines
        lines = mlab.pipeline.stripper(src)

        color = raycolors[iphase]
        mlab.pipeline.surface(lines, line_width=0.5, color=color)

    # plot all stations
    fig.scene.disable_render = True  # Super duper trick
    stxs, stys, stzs = zip(*stations_loc)
    mlab.points3d(stxs,
                  stys,
                  stzs,
                  scale_factor=stmarkersize,
                  color=stationcolor)

    if station_labels:
        for loc, stlabel in zip(stations_loc, stations_lab):
            mlab.text3d(loc[0],
                        loc[1],
                        loc[2],
                        stlabel,
                        scale=sttextsize,
                        color=stationcolor)

    # plot all events
    evxs, evys, evzs = zip(*events_loc)
    evsource = mlab.pipeline.vector_scatter(evxs, evys, evzs, -np.array(evxs),
                                            -np.array(evys), -np.array(evzs))
    evmarkers = mlab.pipeline.glyph(evsource,
                                    scale_factor=evmarkersize,
                                    scale_mode='none',
                                    color=eventcolor,
                                    mode='cone',
                                    resolution=8)
    evmarkers.glyph.glyph_source.glyph_position = 'head'

    if event_labels:
        for loc, evlabel in zip(events_loc, events_lab):
            mlab.text3d(loc[0],
                        loc[1],
                        loc[2],
                        evlabel,
                        scale=evtextsize,
                        color=eventcolor)
    fig.scene.disable_render = False  # Super duper trick

    # read and plot coastlines
    if coastlines == 'internal':
        from mayavi.sources.builtin_surface import BuiltinSurface
        data_source = BuiltinSurface(source='earth', name="Continents")
        data_source.data_source.on_ratio = 1
    else:
        data_source = mlab.pipeline.open(coastlines)
    coastmesh = mlab.pipeline.surface(data_source,
                                      opacity=1.0,
                                      line_width=0.5,
                                      color=continentcolor)
    coastmesh.actor.actor.scale = np.array([1.02, 1.02, 1.02])

    # plot block sphere that hides the backside of the continents
    rad = 0.99
    phi, theta = np.mgrid[0:np.pi:51j, 0:2 * np.pi:51j]

    x = rad * np.sin(phi) * np.cos(theta)
    y = rad * np.sin(phi) * np.sin(theta)
    z = rad * np.cos(phi)
    blocksphere = mlab.mesh(x, y, z, color=bgcolor)
    blocksphere.actor.property.frontface_culling = True  # front not rendered

    # make CMB sphere
    r_earth = model.model.radius_of_planet
    r_cmb = r_earth - model.model.cmb_depth
    rad = r_cmb / r_earth
    phi, theta = np.mgrid[0:np.pi:201j, 0:2 * np.pi:201j]

    x = rad * np.sin(phi) * np.cos(theta)
    y = rad * np.sin(phi) * np.sin(theta)
    z = rad * np.cos(phi)
    cmb = mlab.mesh(x, y, z, color=cmbcolor, opacity=0.3, line_width=0.5)
    cmb.actor.property.interpolation = 'gouraud'
    # cmb.actor.property.interpolation = 'flat'

    # make ICB sphere
    r_iocb = r_earth - model.model.iocb_depth
    rad = r_iocb / r_earth
    phi, theta = np.mgrid[0:np.pi:101j, 0:2 * np.pi:101j]

    x = rad * np.sin(phi) * np.cos(theta)
    y = rad * np.sin(phi) * np.sin(theta)
    z = rad * np.cos(phi)
    icb = mlab.mesh(x, y, z, color=cmbcolor, opacity=0.3, line_width=0.5)
    icb.actor.property.interpolation = 'gouraud'
    if view_dict is None:
        view_dict = {
            'azimuth': 0.,
            'elevation': 90.,
            'distance': 4.,
            'focalpoint': (0., 0., 0.)
        }
    mlab.view(**view_dict)

    # to make a movie from the image files, you can use the command:
    # avconv -qscale 5 -r 20 -b 9600 -i %05d.png -vf scale=800:752 movie.mp4
    if animate and not offscreen:

        @mlab.show
        @mlab.animate(delay=20)
        def anim():
            iframe = 0
            while 1:
                if savemovie and iframe < 360:
                    mlab.savefig('{:05d}.png'.format(iframe))
                # camera moves from East to West opposite of Earth's rotation
                fig.scene.camera.azimuth(-1.)
                fig.scene.render()
                iframe += 1
                yield

        anim()  # Starts the animation.
    else:
        if offscreen:
            mlab.savefig(fname_out)
        else:
            mlab.show()
Ejemplo n.º 4
0
    def test_compute_ray_paths(self):
        # careful, the full inventory, catalog test is long (1min)
        # greatcircles = get_ray_paths(
        #        inventory=self.inventory, catalog=self.catalog,
        #        phase_list=['P'], coordinate_system='XYZ',
        #        taup_model='iasp91')

        # this test checks if we get a single P wave greatcircle
        station = obspy.core.inventory.Station(
                    code='STA', latitude=0., longitude=30., elevation=0.)
        # make two (identical) stations
        network = obspy.core.inventory.Network(
                    code='NET', stations=[station, station])
        inventory = obspy.core.inventory.Inventory(
                source='ME', networks=[network])

        otime = obspy.UTCDateTime('2017-02-03T12:00:00.0Z')
        origin = obspy.core.event.Origin(latitude=0., longitude=90.,
                                         depth=100000., time=otime)
        origin.resource_id = 'smi:local/just-a-test2'
        magnitude = obspy.core.event.Magnitude(mag=7.)
        event = obspy.core.event.Event(origins=[origin],
                                       magnitudes=[magnitude])
        event.resource_id = 'smi:local/just-a-test'
        # make three (identical) events
        catalog = obspy.core.event.Catalog(events=[event, event, event])

        # query for four phases
        greatcircles = get_ray_paths(
            inventory, catalog, phase_list=['P', 'PP', 'S', 'SS'],
            coordinate_system='XYZ', taup_model='iasp91')
        # two stations, three events, 4 phases should yield 24 rays (since all
        # arrivals are encountered in this case)
        self.assertEqual(len(greatcircles), 24)
        # now check details of first ray
        circ = greatcircles[0]
        path = circ[0]
        self.assertEqual(circ[1], 'P')
        self.assertEqual(circ[2], 'NET.STA')
        np.testing.assert_allclose(circ[3], otime.timestamp, atol=1e-5, rtol=0)
        self.assertEqual(circ[4], 7.0)
        self.assertEqual(circ[5], 'smi:local/just-a-test')
        self.assertEqual(circ[6], 'smi:local/just-a-test2')
        self.assertEqual(path.shape, (3, 274))
        # now check some coordinates of the calculated path, start, end and
        # some values in between
        path_start_expected = [
            [6.02712296e-17, 4.63135005e-05, 1.82928142e-03, 1.99453947e-03,
             2.16015881e-03],
            [9.84303877e-01, 9.84224340e-01, 9.81162947e-01, 9.80879369e-01,
             9.80595408e-01],
            [6.02712296e-17, 6.02663595e-17, 6.00790075e-17, 6.00616631e-17,
             6.00442971e-17]]
        np.testing.assert_allclose(path[:, :5], path_start_expected, rtol=1e-5)
        path_end_expected = [
            [8.65195410e-01, 8.65610142e-01, 8.65817499e-01,
             8.66024850e-01, 8.66025746e-01],
            [4.99866617e-01, 4.99932942e-01, 4.99966104e-01,
             4.99999264e-01, 4.99999407e-01],
            [6.11842455e-17, 6.12082668e-17, 6.12202774e-17,
             6.12322880e-17, 6.12323400e-17]]
        np.testing.assert_allclose(path[:, -5:], path_end_expected, rtol=1e-5)
        path_steps_expected = [
            [6.02712296e-17, 5.99694796e-03, 1.55844904e-02,
             2.29391617e-02, 3.12959401e-02, 4.94819381e-02,
             6.59026261e-02, 8.84601669e-02, 1.15734196e-01,
             1.30670566e-01, 1.83202229e-01, 2.21387617e-01,
             3.00609265e-01, 4.51339383e-01, 5.39194024e-01,
             5.84050335e-01, 6.48856399e-01, 6.68018760e-01,
             7.03962600e-01, 7.34809690e-01, 7.59887900e-01,
             7.89619836e-01, 8.04521516e-01, 8.18115511e-01,
             8.36517555e-01, 8.48367905e-01, 8.59911335e-01,
             8.65610142e-01],
            [9.84303877e-01, 9.74082937e-01, 9.58372986e-01,
             9.46927810e-01, 9.34554991e-01, 9.10758513e-01,
             8.91322841e-01, 8.68804460e-01, 8.43147543e-01,
             8.29702355e-01, 7.85418525e-01, 7.55840616e-01,
             7.00517755e-01, 6.14315731e-01, 5.74088249e-01,
             5.56174903e-01, 5.33353699e-01, 5.27297943e-01,
             5.16800783e-01, 5.08777243e-01, 5.04479841e-01,
             5.00872221e-01, 4.99943609e-01, 4.99408317e-01,
             4.99111122e-01, 4.99125255e-01, 4.99180374e-01,
             4.99932942e-01],
            [6.02712296e-17, 5.96465079e-17, 5.86911789e-17,
             5.79996164e-17, 5.72570664e-17, 5.58501220e-17,
             5.47267635e-17, 5.34739746e-17, 5.21120017e-17,
             5.14308206e-17, 4.93839986e-17, 4.82263481e-17,
             4.66770017e-17, 4.66770017e-17, 4.82263481e-17,
             4.93839986e-17, 5.14308206e-17, 5.21120017e-17,
             5.34739746e-17, 5.47267635e-17, 5.58501220e-17,
             5.72570664e-17, 5.79996164e-17, 5.86911789e-17,
             5.96465079e-17, 6.02712296e-17, 6.08831943e-17,
             6.12082668e-17]]
        np.testing.assert_allclose(path[:, ::10], path_steps_expected,
                                   rtol=1e-5)

        # now do the same for RTP coordinate system and also use a different
        # model
        # query for four phases
        greatcircles = get_ray_paths(
            inventory, catalog, phase_list=['P', 'PP', 'S', 'SS'],
            coordinate_system='RTP', taup_model='ak135')
        # two stations, three events, 4 phases should yield 24 rays (since all
        # arrivals are encountered in this case)
        self.assertEqual(len(greatcircles), 24)
        # now check details of first ray
        circ = greatcircles[0]
        path = circ[0]
        self.assertEqual(circ[1], 'P')
        self.assertEqual(circ[2], 'NET.STA')
        np.testing.assert_allclose(circ[3], otime.timestamp, atol=1e-5, rtol=0)
        self.assertEqual(circ[4], 7.0)
        self.assertEqual(circ[5], 'smi:local/just-a-test')
        self.assertEqual(circ[6], 'smi:local/just-a-test2')
        self.assertEqual(path.shape, (3, 270))
        # now check some coordinates of the calculated path, start, end and
        # some values in between
        path_start_expected = [
            [0.984304, 0.984217, 0.981165, 0.980880, 0.980595],
            [1.570796, 1.570796, 1.570796, 1.570796, 1.570796],
            [1.570796, 1.570745, 1.568935, 1.568765, 1.568595]]
        np.testing.assert_allclose(path[:, :5], path_start_expected, rtol=1e-6)
        path_end_expected = [
            [0.998124, 0.999062, 0.999531, 0.999765, 1.000000],
            [1.570796, 1.570796, 1.570796, 1.570796, 1.570796],
            [0.524316, 0.523957, 0.523778, 0.523688, 0.523599]]
        np.testing.assert_allclose(path[:, -5:], path_end_expected, rtol=1e-6)
        path_steps_expected = [
            [0.98430388, 0.97410140, 0.95847208, 0.94717382, 0.93508421,
             0.91210171, 0.89145501, 0.86719412, 0.84963114, 0.83022016,
             0.80548417, 0.78780343, 0.76158226, 0.76488692, 0.79524407,
             0.80633663, 0.83604439, 0.85093573, 0.87434336, 0.89536778,
             0.91994977, 0.93757572, 0.94908037, 0.95919008, 0.97447903,
             0.98726847, 0.99568357],
            [1.57079633, 1.57079633, 1.57079633, 1.57079633, 1.57079633,
             1.57079633, 1.57079633, 1.57079633, 1.57079633, 1.57079633,
             1.57079633, 1.57079633, 1.57079633, 1.57079633, 1.57079633,
             1.57079633, 1.57079633, 1.57079633, 1.57079633, 1.57079633,
             1.57079633, 1.57079633, 1.57079633, 1.57079633, 1.57079633,
             1.57079633, 1.57079633],
            [1.57079633, 1.56464945, 1.55454336, 1.54659206, 1.53738096,
             1.51661510, 1.49423077, 1.46055656, 1.43236890, 1.39637248,
             1.33996718, 1.28795961, 1.16342477, 0.91617848, 0.79110880,
             0.76040906, 0.69478715, 0.66799188, 0.63152179, 0.60344720,
             0.57851384, 0.56322297, 0.55460834, 0.54755404, 0.53770068,
             0.53004245, 0.52531799]]
        np.testing.assert_allclose(path[:, ::10], path_steps_expected,
                                   rtol=1e-6)