\n",
"
PhaseHunter 🏹\n",
"\n",
"
\n",
" \n",
"
Detect P and S seismic phases with uncertainty
\n",
"
\n",
" - Tab 1: Detect seismic phases by selecting a sample waveform or uploading your own waveform in
.npy
format. \n",
" - Tab 2: Select an earthquake from the global earthquake catalogue and PhaseHunter will analyze seismic stations in the given radius.
\n",
" - Waveforms should be sampled at 100 samples/sec and have 3 (Z, N, E) or 1 (Z) channels. PhaseHunter analyzes the first 6000 samples of your file.
\n",
"
\n",
"
Please contact me at anovosel@stanford.edu with questions and feedback
\n",
"
\n",
"\"\"\")\n",
" with gr.Tab(\"Try on a single station\"):\n",
" with gr.Row(): \n",
" # Define the input and output types for Gradio\n",
" inputs = gr.Dropdown(\n",
" [\"data/sample/sample_0.npy\", \n",
" \"data/sample/sample_1.npy\", \n",
" \"data/sample/sample_2.npy\"], \n",
" label=\"Sample waveform\", \n",
" info=\"Select one of the samples\",\n",
" value = \"data/sample/sample_0.npy\"\n",
" )\n",
" with gr.Column(scale=1):\n",
" P_thres_inputs = gr.Slider(minimum=0.01,\n",
" maximum=1,\n",
" value=0.1,\n",
" label=\"P uncertainty threshold (s)\",\n",
" step=0.01,\n",
" info=\"Acceptable uncertainty for P picks expressed in std() seconds\",\n",
" interactive=True,\n",
" )\n",
" \n",
" S_thres_inputs = gr.Slider(minimum=0.01,\n",
" maximum=1,\n",
" value=0.2,\n",
" label=\"S uncertainty threshold (s)\",\n",
" step=0.01,\n",
" info=\"Acceptable uncertainty for S picks expressed in std() seconds\",\n",
" interactive=True,\n",
" )\n",
" with gr.Column(scale=1):\n",
" upload = gr.File(label=\"Upload your waveform\")\n",
" with gr.Row():\n",
" sampling_rate_inputs = gr.Slider(minimum=10,\n",
" maximum=1000,\n",
" value=100,\n",
" label=\"Samlping rate, Hz\",\n",
" step=10,\n",
" info=\"Sampling rate of the waveform\",\n",
" interactive=True,\n",
" )\n",
" order_input = gr.Text(value='ZNE', \n",
" label='Channel order', \n",
" info='Order of the channels in the waveform file (e.g. ZNE)')\n",
"\n",
" button = gr.Button(\"Predict phases\")\n",
" outputs = gr.Image(label='Waveform with Phases Marked', type='numpy', interactive=False)\n",
" \n",
" button.click(mark_phases, inputs=[inputs, upload, \n",
" P_thres_inputs, S_thres_inputs,\n",
" sampling_rate_inputs, order_input], \n",
" outputs=outputs) \n",
" with gr.Tab(\"Select earthquake from catalogue\"):\n",
"\n",
" gr.HTML(\"\"\"\n",
" \n",
"
Using PhaseHunter to Analyze Seismic Waveforms
\n",
"
Select an earthquake from the global earthquake catalogue (e.g. USGS) and the app will download the waveform from the FDSN client of your choice. The app will use a velocity model of your choice to select appropriate time windows for each station within a specified radius of the earthquake.
\n",
"
The app will then analyze the waveforms and mark the detected phases on the waveform. Pick data for each waveform is reported in seconds from the start of the waveform.
\n",
"
Velocities are derived from distance and travel time determined by PhaseHunter picks (v = distance/predicted_pick_time). The background of the velocity plot is colored by DEM.
\n",
"
\n",
" \"\"\")\n",
" with gr.Row(): \n",
" with gr.Column(scale=2):\n",
" client_inputs = gr.Dropdown(\n",
" choices = list(URL_MAPPINGS.keys()), \n",
" label=\"FDSN Client\", \n",
" info=\"Select one of the available FDSN clients\",\n",
" value = \"IRIS\",\n",
" interactive=True\n",
" )\n",
"\n",
" velocity_inputs = gr.Dropdown(\n",
" choices = ['1066a', '1066b', 'ak135', \n",
" 'ak135f', 'herrin', 'iasp91', \n",
" 'jb', 'prem', 'pwdk'], \n",
" label=\"1D velocity model\", \n",
" info=\"Velocity model for station selection\",\n",
" value = \"1066a\",\n",
" interactive=True\n",
" )\n",
"\n",
" with gr.Column(scale=2):\n",
" timestamp_inputs = gr.Textbox(value='2019-07-04T17:33:49-00',\n",
" placeholder='YYYY-MM-DDTHH:MM:SS-TZ',\n",
" label=\"Timestamp\",\n",
" info=\"Timestamp of the earthquake\",\n",
" max_lines=1,\n",
" interactive=True)\n",
" \n",
" source_depth_inputs = gr.Number(value=10,\n",
" label=\"Source depth (km)\",\n",
" info=\"Depth of the earthquake\",\n",
" interactive=True)\n",
" \n",
" with gr.Column(scale=2):\n",
" eq_lat_inputs = gr.Number(value=35.766, \n",
" label=\"Latitude\", \n",
" info=\"Latitude of the earthquake\",\n",
" interactive=True)\n",
" \n",
" eq_lon_inputs = gr.Number(value=-117.605,\n",
" label=\"Longitude\",\n",
" info=\"Longitude of the earthquake\",\n",
" interactive=True)\n",
" \n",
" with gr.Column(scale=2):\n",
" radius_inputs = gr.Slider(minimum=1, \n",
" maximum=200, \n",
" value=50, \n",
" label=\"Radius (km)\", \n",
" step=10,\n",
" info=\"\"\"Select the radius around the earthquake to download data from.\\n \n",
" Note that the larger the radius, the longer the app will take to run.\"\"\",\n",
" interactive=True)\n",
" \n",
" max_waveforms_inputs = gr.Slider(minimum=1,\n",
" maximum=100,\n",
" value=10,\n",
" label=\"Max waveforms per section\",\n",
" step=1,\n",
" info=\"Maximum number of waveforms to show per section\\n (to avoid long prediction times)\",\n",
" interactive=True,\n",
" )\n",
" with gr.Column(scale=2):\n",
" P_thres_inputs = gr.Slider(minimum=0.01,\n",
" maximum=1,\n",
" value=0.1,\n",
" label=\"P uncertainty threshold, s\",\n",
" step=0.01,\n",
" info=\"Acceptable uncertainty for P picks expressed in std() seconds\",\n",
" interactive=True,\n",
" )\n",
" S_thres_inputs = gr.Slider(minimum=0.01,\n",
" maximum=1,\n",
" value=0.2,\n",
" label=\"S uncertainty threshold, s\",\n",
" step=0.01,\n",
" info=\"Acceptable uncertainty for S picks expressed in std() seconds\",\n",
" interactive=True,\n",
" )\n",
" \n",
" button_phases = gr.Button(\"Predict phases\")\n",
" output_image = gr.Image(label='Waveforms with Phases Marked', type='numpy', interactive=False)\n",
" \n",
" # with gr.Row():\n",
" # with gr.Column(scale=2):\n",
" # azimuth_input = gr.Slider(minimum=-180, maximum=180, value=0, step=5, label=\"Azimuth\", interactive=True)\n",
" # elevation_input = gr.Slider(minimum=-90, maximum=90, value=30, step=5, label=\"Elevation\", interactive=True)\n",
"\n",
" # with gr.Row():\n",
" # interpolate_input = gr.Checkbox(label=\"Interpolate\", info=\"Interpolate velocity model\")\n",
" # n_lat_input = gr.Slider(minimum=5, maximum=100, value=50, step=5, label=\"N lat\", info='Number of Lat grid points', interactive=True)\n",
" # n_lon_input = gr.Slider(minimum=5, maximum=100, value=50, step=5, label=\"N lon\", info='Number of Lon grid points', interactive=True)\n",
" # n_depth_input = gr.Slider(minimum=5, maximum=100, value=50, step=5, label=\"N depth\", info='Number of Depth grid points', interactive=True)\n",
" \n",
" # button = gr.Button(\"Look at 3D Velocities\")\n",
" # outputs_vel_model = gr.Image(label=\"3D Velocity Model\")\n",
"\n",
" # button.click(compute_velocity_model, \n",
" # inputs=[azimuth_input, elevation_input, \n",
" # interpolate_input, n_lat_input, \n",
" # n_lon_input, n_depth_input], \n",
" # outputs=[outputs_vel_model])\n",
" \n",
" with gr.Row():\n",
" output_picks = gr.Dataframe(label='Pick data', \n",
" type='pandas', \n",
" interactive=False)\n",
" output_csv = gr.File(label=\"Output File\", file_types=[\".csv\"])\n",
"\n",
" button_phases.click(predict_on_section, \n",
" inputs=[client_inputs, timestamp_inputs, \n",
" eq_lat_inputs, eq_lon_inputs, \n",
" radius_inputs, source_depth_inputs, \n",
" velocity_inputs, max_waveforms_inputs,\n",
" P_thres_inputs, S_thres_inputs],\n",
" outputs=[output_image, output_picks, output_csv])\n",
"\n",
"demo.launch()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"def compute_velocity_model(azimuth, elevation, interpolate, n_lat, n_lon, n_depth):\n",
" filename = list(output_csv.temp_files)[0]\n",
" \n",
" df = pd.read_csv(filename)\n",
" filename = filename.split('/')[-1]\n",
" \n",
" # Current EQ location\n",
" eq_lat = float(filename.split(\"_\")[0])\n",
" eq_lon = float(filename.split(\"_\")[1])\n",
" eq_depth = float(filename.split(\"_\")[2])\n",
"\n",
" # Define the region of interest (latitude, longitude, and depth ranges)\n",
" lat_range = (np.min([df.st_lat.min(), eq_lat]), np.max([df.st_lat.max(), eq_lat]))\n",
" lon_range = (np.min([df.st_lon.min(), eq_lon]), np.max([df.st_lon.max(), eq_lon]))\n",
" depth_range = (0, 50)\n",
"\n",
" # Define the number of nodes in each dimension\n",
" num_points = 100\n",
"\n",
" taup_model = TauPyModel(model='1066a')\n",
"\n",
" # Create the grid\n",
" lat_values = np.linspace(lat_range[0], lat_range[1], n_lat)\n",
" lon_values = np.linspace(lon_range[0], lon_range[1], n_lon)\n",
" depth_values = np.linspace(depth_range[0], depth_range[1], n_depth)\n",
"\n",
" # Initialize the velocity model with constant values\n",
" initial_velocity = 0 # km/s, this can be P-wave or S-wave velocity\n",
" velocity_model = np.full((n_lat, n_lon, n_depth), initial_velocity, dtype=float)\n",
"\n",
" # Loop through the stations and update the velocity model\n",
" for i in range(len(df)):\n",
" if ~np.isnan(df['velocity_p, km/s'].iloc[i]):\n",
"\n",
" ray_path = taup_model.get_ray_paths_geo(source_depth_in_km=eq_depth,\n",
" source_latitude_in_deg=eq_lat,\n",
" source_longitude_in_deg=eq_lon,\n",
" receiver_latitude_in_deg=df.st_lat.iloc[i],\n",
" receiver_longitude_in_deg=df.st_lon.iloc[i],\n",
" phase_list=['P', 'S'])\n",
" \n",
" # HERE IS THE PROBLEM\n",
" print(ray_path[0].path)\n",
"\n",
" # Create the interpolator objects for latitude, longitude, and depth\n",
" interp_latitude = interp1d(np.linspace(0, ray_path[0].path['lat'].max(), len(ray_path[0].path['lat'])), ray_path[0].path['lat'])\n",
" interp_longitude = interp1d(np.linspace(0, ray_path[0].path['lon'].max(), len(ray_path[0].path['lon'])), ray_path[0].path['lon'])\n",
" interp_depth = interp1d(np.linspace(0, ray_path[0].path['depth'].max(), len(ray_path[0].path['depth'])), ray_path[0].path['depth'])\n",
"\n",
" # Resample the ray path to N points\n",
" lat_values_interp = interp_latitude(np.linspace(0, ray_path[0].path['lat'].max(), num_points))\n",
" lon_values_interp = interp_longitude(np.linspace(0, ray_path[0].path['lon'].max(), num_points))\n",
" depth_values_interp = interp_depth(np.linspace(0, ray_path[0].path['depth'].max(), num_points))\n",
"\n",
" # Loop through the interpolated coordinates and update the grid cells with the average P-wave velocity\n",
" for lat, lon, depth in zip(lat_values_interp, lon_values_interp, depth_values_interp):\n",
" lat_index = find_closest_index(lat_values, lat)\n",
" lon_index = find_closest_index(lon_values, lon)\n",
" depth_index = find_closest_index(depth_values, depth)\n",
" \n",
" if velocity_model[lat_index, lon_index, depth_index] == initial_velocity:\n",
" velocity_model[lat_index, lon_index, depth_index] = df['velocity_p, km/s'].iloc[i]\n",
" else:\n",
" velocity_model[lat_index, lon_index, depth_index] = (velocity_model[lat_index, lon_index, depth_index] +\n",
" df['velocity_p, km/s'].iloc[i]) / 2\n",
"\n",
" # Create the figure and axis\n",
" fig = plt.figure(figsize=(8, 8))\n",
" ax = fig.add_subplot(111, projection='3d')\n",
"\n",
" # Set the plot limits\n",
" ax.set_xlim3d(lat_range[0], lat_range[1])\n",
" ax.set_ylim3d(lon_range[0], lon_range[1])\n",
" ax.set_zlim3d(depth_range[1], depth_range[0])\n",
"\n",
" ax.set_xlabel('Latitude')\n",
" ax.set_ylabel('Longitude')\n",
" ax.set_zlabel('Depth (km)')\n",
" ax.set_title('Velocity Model')\n",
" \n",
" # Create the meshgrid\n",
" x, y, z = np.meshgrid(\n",
" np.linspace(lat_range[0], lat_range[1], velocity_model.shape[0]+1),\n",
" np.linspace(lon_range[0], lon_range[1], velocity_model.shape[1]+1),\n",
" np.linspace(depth_range[0], depth_range[1], velocity_model.shape[2]+1),\n",
" indexing='ij'\n",
" )\n",
"\n",
" # Create the color array\n",
" norm = plt.Normalize(vmin=2, vmax=8)\n",
" colors_vel = plt.cm.plasma(norm(velocity_model)) \n",
" \n",
" # Plot the voxels\n",
" if interpolate:\n",
" interpolated_velocity_model = interpolate_vel_model(velocity_model, initial_velocity, lat_values, lon_values, depth_values, n_lat, n_lon, n_depth)\n",
" colors_interp = plt.cm.plasma(norm(interpolated_velocity_model))\n",
" ax.voxels(x, y, z, interpolated_velocity_model > 0, facecolors=colors_interp, alpha=0.5, edgecolor='k')\n",
" \n",
" ax.voxels(x, y, z, velocity_model > 0, facecolors=colors_vel, alpha=1, edgecolor='black')\n",
"\n",
" # Set the view angle\n",
" ax.view_init(elev=elevation, azim=azimuth)\n",
"\n",
" m = cm.ScalarMappable(cmap=plt.cm.plasma, norm=norm)\n",
" m.set_array([])\n",
" plt.colorbar(m)\n",
"\n",
" # Show the plot\n",
" fig.canvas.draw();\n",
" image = np.array(fig.canvas.renderer.buffer_rgba())\n",
" plt.close(fig)\n",
"\n",
" return image"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.8"
},
"orig_nbformat": 4
},
"nbformat": 4,
"nbformat_minor": 2
}