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)
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))
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()