Searching the USGS Earthquake Catalog with Mathematica

This note documents a simple in-class activity that I use in my GEOSC 109H earthquakes and society class. In class I describe the Gutenberg-Richter Relation that represents the distribution of earthquakes as a function of magnitude (a nice paper-based activity is available from SCEC). I show the students a plot from the GCMT catalog in class, in this activity, they use Mathematica to download data for a few randomly chosen years and see if the pattern I showed in class holds up.

The Mathematica notebook begins with a few utility functions that allow students to use web services to acquire seismicity data and a few functions to list some events (which is partly a check to see that things are working). Below is a listing of the search script (an example usage is provided below).

comcatRectangularSearchPath[start_, end_, minlat_, maxlat_, minlon_, maxlon_] := Module[{},
  "http://earthquake.usgs.gov/fdsnws/event/1/query?format=csv&starttime=" <> 
   DateString[start, "ISODateTime"] <> "&endtime=" <> 
   DateString[end, "ISODateTime"] <> 
   "&minmagnitude=4.0&minlatitude=" <> 
   ToString[Round[minlat, 0.001]] <>
   "&maxlatitude=" <> 
   ToString[Round[maxlat, 0.001]] <>
   "&minlongitude=" <> 
   ToString[Round[minlon, 0.001]] <> "&maxlongitude=" <> 
   ToString[Round[maxlon, 0.001]] <> "&orderby=time-asc"
  ]

comcatRectangularSearch[start_, end_, minlat_: - 90, maxlat_: 90, 
  minlon_: - 180, maxlon_: 180] := Module[{path},
   path = comcatRectangularSearchPath[start, end, minlat, maxlat, minlon, maxlon];
  {path, Import[path, "CSV"]}
  ]

The scripts are simplistic, and to avoid errors for some of the more active years, I limit the magnitude range of the analysis to events greater than or equal to magnitude 4.0. The code below is a function to list the largest events in the search results.

listEventHeadings = {"Origin Time", "Latitude", "Longitude", "Depth", 
   "Magnitude", "MagType"};
(**)
EventSummaryString[eq_] := Module[{s0, s1},
  s0 = DateString[eq[[1]]] <> "\t" 
    <> ToString[eq[[2]]] <> "\t"
    <> ToString[eq[[3]]] <> "\t"
    <> ToString[eq[[4]]] <> "\t"
    <> ToString[eq[[5]]] <> "\t"
    <> ToString[eq[[6]]] <> "\t"
  ]
(**)
ListNLargestEvents[cat_, n_] := Module[{sorted, nevents},
  sorted = Sort[cat, #1[[5]] > #2[[5]] &];
  nevents = {#[[1]], #[[2]], #[[2]], #[[4]], #[[5]], #[[6]]} & /@ 
    sorted[[1 ;; n]];
  TableForm[nevents, TableHeadings -> {None, listEventHeadings}]
  ]

Using the Scripts

Students perform searches on individual years using the following commands. The search function is called and it returns a two-item list containing the search string (an http address useful for debugging and the output of the web services search). For convenience, I strip out the origin time and magnitude values of the events and store them in the lists called otimes and mags.

theYEAR = 2005;
(**)
usgsresponse = comcatRectangularSearch[{theYEAR, 1, 1}, {theYEAR + 1, 1, 1, 0, 0}];
headings = usgsresponse[[2]][[1]]
usgscat = Drop[usgsresponse[[2]], 1]

otimes = #[[1]] & /@ usgscat;
mags = #[[5]] & /@ usgscat;

To perform a search for a different year, the just have to edit the cell to change the value of theYEAR. To see a list of the five largest events, the next cell is

(* List the five largest events of the year *)
ListNLargestEvents[usgscat, 5]

The output of that call in this case is (the formatting is better in the notebook, things line up).

Origin Time	Latitude	Longitude	Depth	Magnitude	MagType
2005-03-28T16:09:36.530Z	2.085	2.085	30	8.6	mww
2005-06-13T22:44:33.900Z	-19.987	-19.987	115.6	7.8	mwb
2005-10-08T03:50:40.800Z	34.539	34.539	26	7.6	mwc
2005-09-09T07:26:43.730Z	-4.539	-4.539	90	7.6	mwc
2005-09-26T01:55:37.670Z	-5.678	-5.678	115	7.5	mwb

The Gutenberg-Richter Analysis

I perform the G-R analysis using the classic table listing the numbers of events with magnitude greater than or equal to a sequence of magnitudes. The cumulative distribution is probably a better way to construct the model, but this is a 100-level class for non-science students, the intuitive version is preferred. The code below will tally the numbers that we need, starting a M 4.0 and using bin widths of 0.5 magnitude units.

(* Compute the Gutenberg Richter Statistics *)
m = 4.0;
dm = 0.5;
grStats = {};
While[m <= 8.5,
 ngtm = Length[Select[mags, # >= m &]];
 grStats = Append[grStats, {m, ngtm}];
 m += dm;
 ]
grStats;
counts = Transpose[grStats][[2]];
bcenters = Transpose[grStats][[1]];
(**)
Print["Global earthquake Statistics for the year: " <> ToString[theYEAR] <>"."]
TableForm[{Range[1, Length[counts]], bcenters, counts} // Transpose, 
  TableHeadings -> {None, {"Index", "Magnitude","N \[GreaterEqual] Magnitude"}}, 
  TableAlignments -> Center] // Print
Global earthquake Statistics for the year: 2005.
Index	Magnitude	N >= Magnitude
1	    4.	           15771
2	    4.5	           7886
3	    5.	           1843
4	    5.5	           533
5	    6.	           151
6	    6.5	           45
7	    7.	           11
8	    7.5	           5
9	    8.	           1
10	    8.5	           1

I list the index because to choose the range over which to fit the line to the pattern, we need to insure that none of the bins we use are empty. By default, I start with the second bin and include bins 2 through 8. This seems to work well, but to be honest, having something not work is a good lesson in computing for the students. Next I create a plot of the values, N vs Magnitude.

(**)
mark = Graphics[{EdgeForm[GrayLevel[0.05]], Opacity[0.65], FaceForm[Red], 
    Disk[{0, 0}, 0.05], PlotRangeClipping -> False}];
(**)
pplt = ListPlot[{bcenters, Log10[counts]} // Transpose,
   Frame -> True, Axes -> False,
   FrameLabel -> {"Magnitude", 
     "\!\(\*SubscriptBox[\(Log\), \(10\)]\)[N \[GreaterEqual] M]"},
   RotateLabel -> True, ImageSize -> 512,
   BaseStyle -> {24, FontFamily -> "Helvetica"}, 
   PlotMarkers -> {mark, 0.04}, PlotRange -> {{3, 9.}, {-1, 5}}];
(**)
pplt // Print

Here’s the output

Gutenberg-Richter diagram of USGS catalog magnitudes for the year 2005.

Next we fit a line to the values. I used built-in commands a simply write the slope and intercept values to the notebook. Students can copy and paste the information into an online activity form (in the course management system we use, Canvas).

i0 = 2;
i1 = 8;
Clear[x];
lm = LinearModelFit[{bcenters[[i0 ;; i1]], 
     Log10[counts[[i0 ;; i1]]]} // Transpose, x, x];
(**)
p = lm["BestFitParameters"];
(**)
Print[Style["The best fit line slope: " <> ToString[p[[2]]] <> 
   " intercept: " <> ToString[p[[1]]], {18, Bold}]]

Here’s the output

The best fit line slope: -1.07967 intercept: 8.68685

We can overlay the line onto the data plot using

(**)
lplt = Plot[lm[x], {x, 3, 9}, 
       PlotStyle -> {RGBColor[0.5, 0.5, 0.5], Thickness[0.01]}];
(**)
grplt = Show[pplt, lplt,
   FrameStyle -> AbsoluteThickness[0.85],
   ImageSize -> 512, 
   PlotLabel ->  ToString["Global EQ Activity, Year: " <> ToString[theYEAR]],
   GridLines -> Automatic,
   Epilog -> {Text[ Style["Data Source\nUSGS", 18, Background -> White], 
    {8,3.5}, {1, -1}]}
   ];
(**)
grplt // Print

The display is more formal and summarizes the analysis.

Gutenberg-Richter diagram of USGS catalog magnitudes for the year 2005 with least-squares line fit.

A Global Earthquake Map

I finish with a simple map of the earthquakes. I don’t show the background because I want students to focus on the spatial patterns in earthquake locations. Repeating this for a few years communicates the ideas that the first-order earthquake patterns are the same each year, but each year is different.

elocs = {#[[2]], #[[3]], #[[5]]} & /@ Select[usgscat, #[[5]] > 4 &];

west = -180;
east = 180;
south = -90;
north = 90;

eqradius[m_] := N[10^(0.5*m - 2.25)];
scaleFactor = 10;

GeoGraphics[{GeoStyling[Opacity[0.5], FaceForm[{Red}],EdgeForm[{Black, Thin}]], 
   GeoDisk[{#[[1]], #[[2]]}, 
   Quantity[scaleFactor*Max[{eqradius[#[[3]]], 4}]  , "km"]] & /@ elocs},
  GeoRange -> {{south, north}, {west, east}},
  BaseStyle -> {21, FontFamily -> "Helvetica"},
  GeoProjection -> {"Robinson", "Centering" -> 0, "GridOrigin" -> {0, 0}},
  FrameTicks -> {{None, None}, {Automatic, Automatic}},
  GeoBackground -> None,
  GeoGridLines -> {Range[south, north, 15.0], Range[west, east, 15.0]},
  GeoGridLinesStyle -> {{Opacity[.25], Black}, {Opacity[.25], Black}},
  ImageSize -> 800] // Print

Here’s is the result for 2005. Symbol size is proportional to rupture size, but I exaggerated the the size by a factor of three to make smaller magnitude events visible. I also limit the minimum earthquake radius to 4 km, so the small events are visible.

Map of earthquakes with M≥4.0 for 2005 and contained in the USGS Catalog. Robinson projection. Symbols are scaled relative to the seismic moment.

Note that Mathematica plots circular regions onto the map projection – this leads to an important discussion of map projections and their distortions.

Summary

Mathematica provides useful tools for searching, analyzing and displaying earthquake locations. I used the above code to have students check the validity of the Gutenberg-Richter Relation for earthquakes, stressing the evidence-based nature of this important empirical pattern. We return to similar analyses for individual large earthquakes later in the semester.