def __toMagnitude(parser, magnitude_el, origin): """ Parses a given magnitude etree element. :type parser: :class:`~obspy.core.util.xmlwrapper.XMLParser` :param parser: Open XMLParser object. :type magnitude_el: etree.element :param magnitude_el: magnitude element to be parsed. :return: A ObsPy :class:`~obspy.core.event.Magnitude` object. """ global CURRENT_TYPE mag = Magnitude() mag.resource_id = ResourceIdentifier(prefix="/".join([RESOURCE_ROOT, "magnitude"])) mag.origin_id = origin.resource_id mag.mag, mag.mag_errors = __toFloatQuantity(parser, magnitude_el, "mag") # obspyck used to write variance (instead of std) in magnitude error fields if CURRENT_TYPE == "obspyck": if mag.mag_errors.uncertainty is not None: mag.mag_errors.uncertainty = math.sqrt(mag.mag_errors.uncertainty) mag.magnitude_type = parser.xpath2obj("type", magnitude_el) mag.station_count = parser.xpath2obj("stationCount", magnitude_el, int) mag.method_id = "%s/magnitude_method/%s/1" % (RESOURCE_ROOT, parser.xpath2obj('program', magnitude_el)) if str(mag.method_id).lower().endswith("none"): mag.method_id = None return mag
def __toMagnitude(parser, magnitude_el, origin): """ Parses a given magnitude etree element. :type parser: :class:`~obspy.core.util.xmlwrapper.XMLParser` :param parser: Open XMLParser object. :type magnitude_el: etree.element :param magnitude_el: magnitude element to be parsed. :return: A ObsPy :class:`~obspy.core.event.Magnitude` object. """ global CURRENT_TYPE mag = Magnitude() mag.resource_id = ResourceIdentifier( prefix="/".join([RESOURCE_ROOT, "magnitude"])) mag.origin_id = origin.resource_id mag.mag, mag.mag_errors = __toFloatQuantity(parser, magnitude_el, "mag") # obspyck used to write variance (instead of std) in magnitude error fields if CURRENT_TYPE == "obspyck": if mag.mag_errors.uncertainty is not None: mag.mag_errors.uncertainty = math.sqrt(mag.mag_errors.uncertainty) mag.mag_errors.confidence_level = 68.3 mag.magnitude_type = parser.xpath2obj("type", magnitude_el) mag.station_count = parser.xpath2obj("stationCount", magnitude_el, int) mag.method_id = "%s/magnitude_method/%s/1" % ( RESOURCE_ROOT, parser.xpath2obj('program', magnitude_el)) if str(mag.method_id).lower().endswith("none"): mag.method_id = None return mag
def __toMagnitude(parser, magnitude_el): """ Parses a given magnitude etree element. :type parser: :class:`~obspy.core.util.xmlwrapper.XMLParser` :param parser: Open XMLParser object. :type magnitude_el: etree.element :param magnitude_el: magnitude element to be parsed. :return: A ObsPy :class:`~obspy.core.event.Magnitude` object. """ mag = Magnitude() mag.mag, mag.mag_errors = __toFloatQuantity(parser, magnitude_el, "mag") mag.magnitude_type = parser.xpath2obj("type", magnitude_el) mag.station_count = parser.xpath2obj("stationCount", magnitude_el, int) mag.method_id = parser.xpath2obj("program", magnitude_el) return mag
def _on_file_save(self): """ Creates a new obspy.core.event.Magnitude object and writes the moment magnitude to it. """ # Get the save filename. filename = QtGui.QFileDialog.getSaveFileName(caption="Save as...") filename = os.path.abspath(str(filename)) mag = Magnitude() mag.mag = self.final_result["moment_magnitude"] mag.magnitude_type = "Mw" mag.station_count = self.final_result["station_count"] mag.evaluation_mode = "manual" # Link to the used origin. mag.origin_id = self.current_state["event"].origins[0].resource_id mag.method_id = "Magnitude picker Krischer" # XXX: Potentially change once this program gets more stable. mag.evaluation_status = "preliminary" # Write the other results as Comments. mag.comments.append( \ Comment("Seismic moment in Nm: %g" % \ self.final_result["seismic_moment"])) mag.comments.append( \ Comment("Circular source radius in m: %.2f" % \ self.final_result["source_radius"])) mag.comments.append( \ Comment("Stress drop in Pa: %.2f" % \ self.final_result["stress_drop"])) mag.comments.append( \ Comment("Very rough Q estimation: %.1f" % \ self.final_result["quality_factor"])) event = copy.deepcopy(self.current_state["event"]) event.magnitudes.append(mag) cat = Catalog() cat.events.append(event) cat.write(filename, format="quakeml")
def _map_netmag2magnitude(self, db): """ Return an obspy Magnitude from an dict of CSS key/values corresponding to one record. Inputs ====== db : dict of key/values of CSS fields from the 'netmag' table Returns ======= obspy.core.event.Magnitude Notes ===== Any object that supports the dict 'get' method can be passed as input, e.g. OrderedDict, custom classes, etc. """ m = Magnitude() m.mag = db.get('magnitude') m.magnitude_type = db.get('magtype') m.mag_errors.uncertainty = db.get('uncertainty') m.station_count = db.get('nsta') posted_author = _str(db.get('auth')) mode, status = self.get_event_status(posted_author) m.evaluation_mode = mode m.evaluation_status = status m.creation_info = CreationInfo( creation_time = _utc(db.get('lddate')), agency_id = self.agency, version = db.get('magid'), author = posted_author, ) m.resource_id = self._rid(m) return m
def _on_file_save(self): """ Creates a new obspy.core.event.Magnitude object and writes the moment magnitude to it. """ # Get the save filename. filename = QtGui.QFileDialog.getSaveFileName(caption="Save as...") filename = os.path.abspath(str(filename)) mag = Magnitude() mag.mag = self.final_result["moment_magnitude"] mag.magnitude_type = "Mw" mag.station_count = self.final_result["station_count"] mag.evaluation_mode = "manual" # Link to the used origin. mag.origin_id = self.current_state["event"].origins[0].resource_id mag.method_id = "Magnitude picker Krischer" # XXX: Potentially change once this program gets more stable. mag.evaluation_status = "preliminary" # Write the other results as Comments. mag.comments.append( \ Comment("Seismic moment in Nm: %g" % \ self.final_result["seismic_moment"])) mag.comments.append( \ Comment("Circular source radius in m: %.2f" % \ self.final_result["source_radius"])) mag.comments.append( \ Comment("Stress drop in Pa: %.2f" % \ self.final_result["stress_drop"])) mag.comments.append( \ Comment("Very rough Q estimation: %.1f" % \ self.final_result["quality_factor"])) event = copy.deepcopy(self.current_state["event"]) event.magnitudes.append(mag) cat = Catalog() cat.events.append(event) cat.write(filename, format="quakeml")
def _parseRecordE(self, line, event): """ Parses the 'error and magnitude' record E """ orig_time_stderr = self._float(line[2:7]) latitude_stderr = self._float(line[8:14]) longitude_stderr = self._float(line[15:21]) depth_stderr = self._float(line[22:27]) mb_mag = self._float(line[28:31]) mb_nsta = self._int(line[32:35]) Ms_mag = self._float(line[36:39]) Ms_nsta = self._int(line[39:42]) mag1 = self._float(line[42:45]) mag1_type = line[45:47] mag1_source_code = line[47:51].strip() mag2 = self._float(line[51:54]) mag2_type = line[54:56] mag2_source_code = line[56:60].strip() evid = event.resource_id.id.split('/')[-1] origin = event.origins[0] self._storeUncertainty(origin.time_errors, orig_time_stderr) self._storeUncertainty(origin.latitude_errors, self._latErrToDeg(latitude_stderr)) self._storeUncertainty( origin.longitude_errors, self._lonErrToDeg(longitude_stderr, origin.latitude)) self._storeUncertainty(origin.depth_errors, depth_stderr, scale=1000) if mb_mag is not None: mag = Magnitude() res_id = '/'.join((res_id_prefix, 'magnitude', evid, 'mb')) mag.resource_id = ResourceIdentifier(id=res_id) mag.creation_info = CreationInfo(agency_id='USGS-NEIC') mag.mag = mb_mag mag.magnitude_type = 'Mb' mag.station_count = mb_nsta mag.origin_id = origin.resource_id event.magnitudes.append(mag) if Ms_mag is not None: mag = Magnitude() res_id = '/'.join((res_id_prefix, 'magnitude', evid, 'ms')) mag.resource_id = ResourceIdentifier(id=res_id) mag.creation_info = CreationInfo(agency_id='USGS-NEIC') mag.mag = Ms_mag mag.magnitude_type = 'Ms' mag.station_count = Ms_nsta mag.origin_id = origin.resource_id event.magnitudes.append(mag) if mag1 is not None: mag = Magnitude() mag1_id = mag1_type.lower() res_id = '/'.join((res_id_prefix, 'magnitude', evid, mag1_id)) mag.resource_id = ResourceIdentifier(id=res_id) mag.creation_info = CreationInfo(agency_id=mag1_source_code) mag.mag = mag1 mag.magnitude_type = mag1_type mag.origin_id = origin.resource_id event.magnitudes.append(mag) if mag2 is not None: mag = Magnitude() mag2_id = mag2_type.lower() if mag2_id == mag1_id: mag2_id += '2' res_id = '/'.join((res_id_prefix, 'magnitude', evid, mag2_id)) mag.resource_id = ResourceIdentifier(id=res_id) mag.creation_info = CreationInfo(agency_id=mag2_source_code) mag.mag = mag2 mag.magnitude_type = mag2_type mag.origin_id = origin.resource_id event.magnitudes.append(mag)
def _parse_first_line_origin(self, line, event, magnitudes): """ Parse the first line of origin data. :type line: str :param line: Line to parse. :type event: :class:`~obspy.core.event.event.Event` :param event: Event of the origin. :type magnitudes: list of :class:`~obspy.core.event.magnitude.Magnitude` :param magnitudes: Store magnitudes in a list to keep their positions. :rtype: :class:`~obspy.core.event.origin.Origin`, :class:`~obspy.core.event.resourceid.ResourceIdentifier` :returns: Parsed origin or None, resource identifier of the origin. """ magnitude_types = [] magnitude_values = [] magnitude_station_counts = [] fields = self.fields['line_1'] time_origin = line[fields['time']].strip() time_fixed_flag = line[fields['time_fixf']].strip() latitude = line[fields['lat']].strip() longitude = line[fields['lon']].strip() epicenter_fixed_flag = line[fields['epicenter_fixf']].strip() depth = line[fields['depth']].strip() depth_fixed_flag = line[fields['depth_fixf']].strip() phase_count = line[fields['n_def']].strip() station_count = line[fields['n_sta']].strip() azimuthal_gap = line[fields['gap']].strip() magnitude_types.append(line[fields['mag_type_1']].strip()) magnitude_values.append(line[fields['mag_1']].strip()) magnitude_station_counts.append(line[fields['mag_n_sta_1']].strip()) magnitude_types.append(line[fields['mag_type_2']].strip()) magnitude_values.append(line[fields['mag_2']].strip()) magnitude_station_counts.append(line[fields['mag_n_sta_2']].strip()) magnitude_types.append(line[fields['mag_type_3']].strip()) magnitude_values.append(line[fields['mag_3']].strip()) magnitude_station_counts.append(line[fields['mag_n_sta_3']].strip()) author = line[fields['author']].strip() origin_id = line[fields['id']].strip() origin = Origin() origin.quality = OriginQuality() try: origin.time = UTCDateTime(time_origin.replace('/', '-')) origin.latitude = float(latitude) origin.longitude = float(longitude) except (TypeError, ValueError): self._warn('Missing origin data, skipping event') return None, None origin.time_fixed = time_fixed_flag.lower() == 'f' origin.epicenter_fixed = epicenter_fixed_flag.lower() == 'f' try: # Convert value from km to m origin.depth = float(depth) * 1000 except ValueError: pass try: origin.depth_type = DEPTH_TYPES[depth_fixed_flag] except KeyError: origin.depth_type = OriginDepthType('from location') try: origin.quality.used_phase_count = int(phase_count) origin.quality.associated_phase_count = int(phase_count) except ValueError: pass try: origin.quality.used_station_count = int(station_count) origin.quality.associated_station_count = int(station_count) except ValueError: pass try: origin.quality.azimuthal_gap = float(azimuthal_gap) except ValueError: pass self.author = author origin.creation_info = self._get_creation_info() public_id = "origin/%s" % origin_id origin_res_id = self._get_res_id(public_id) for i in range(3): try: magnitude = Magnitude() magnitude.creation_info = self._get_creation_info() magnitude.magnitude_type = magnitude_types[i] magnitude.mag = float(magnitude_values[i]) magnitude.station_count = int(magnitude_station_counts[i]) magnitude.origin_id = origin_res_id magnitudes.append(magnitude) event.magnitudes.append(magnitude) except ValueError: # Magnitude can be empty but we need to keep the # position between mag1, mag2 or mag3. magnitudes.append(None) return origin, origin_res_id
def _parse_first_line_origin(self, line, event, magnitudes): """ Parse the first line of origin data. :type line: str :param line: Line to parse. :type event: :class:`~obspy.core.event.event.Event` :param event: Event of the origin. :type magnitudes: list of :class:`~obspy.core.event.magnitude.Magnitude` :param magnitudes: Store magnitudes in a list to keep their positions. :rtype: :class:`~obspy.core.event.origin.Origin`, :class:`~obspy.core.event.resourceid.ResourceIdentifier` :returns: Parsed origin or None, resource identifier of the origin. """ magnitude_types = [] magnitude_values = [] magnitude_station_counts = [] fields = self.fields['line_1'] time_origin = line[fields['time']].strip() time_fixed_flag = line[fields['time_fixf']].strip() latitude = line[fields['lat']].strip() longitude = line[fields['lon']].strip() epicenter_fixed_flag = line[fields['epicenter_fixf']].strip() depth = line[fields['depth']].strip() depth_fixed_flag = line[fields['depth_fixf']].strip() phase_count = line[fields['n_def']].strip() station_count = line[fields['n_sta']].strip() azimuthal_gap = line[fields['gap']].strip() magnitude_types.append(line[fields['mag_type_1']].strip()) magnitude_values.append(line[fields['mag_1']].strip()) magnitude_station_counts.append(line[fields['mag_n_sta_1']].strip()) magnitude_types.append(line[fields['mag_type_2']].strip()) magnitude_values.append(line[fields['mag_2']].strip()) magnitude_station_counts.append(line[fields['mag_n_sta_2']].strip()) magnitude_types.append(line[fields['mag_type_3']].strip()) magnitude_values.append(line[fields['mag_3']].strip()) magnitude_station_counts.append(line[fields['mag_n_sta_3']].strip()) author = line[fields['author']].strip() origin_id = line[fields['id']].strip() origin = Origin() origin.quality = OriginQuality() try: origin.time = UTCDateTime(time_origin.replace('/', '-')) origin.latitude = float(latitude) origin.longitude = float(longitude) except (TypeError, ValueError): self._warn('Missing origin data, skipping event') return None, None origin.time_fixed = time_fixed_flag.lower() == 'f' origin.epicenter_fixed = epicenter_fixed_flag.lower() == 'f' try: # Convert value from km to m origin.depth = float(depth) * 1000 except ValueError: pass try: origin.depth_type = DEPTH_TYPES[depth_fixed_flag] except KeyError: origin.depth_type = OriginDepthType('from location') try: origin.quality.used_phase_count = int(phase_count) origin.quality.associated_phase_count = int(phase_count) except ValueError: pass try: origin.quality.used_station_count = int(station_count) origin.quality.associated_station_count = int(station_count) except ValueError: pass try: origin.quality.azimuthal_gap = float(azimuthal_gap) except ValueError: pass self.author = author origin.creation_info = self._get_creation_info() public_id = "origin/%s" % origin_id origin_res_id = self._get_res_id(public_id) for i in range(3): try: magnitude = Magnitude() magnitude.creation_info = self._get_creation_info() magnitude.magnitude_type = magnitude_types[i] magnitude.mag = float(magnitude_values[i]) magnitude.station_count = int(magnitude_station_counts[i]) magnitude.origin_id = origin_res_id magnitudes.append(magnitude) event.magnitudes.append(magnitude) except ValueError: # Magnitude can be empty but we need to keep the # position between mag1, mag2 or mag3. magnitudes.append(None) return origin, origin_res_id
def _parse_record_e(self, line, event): """ Parses the 'error and magnitude' record E """ orig_time_stderr = self._float(line[2:7]) latitude_stderr = self._float(line[8:14]) longitude_stderr = self._float(line[15:21]) depth_stderr = self._float(line[22:27]) mb_mag = self._float(line[28:31]) mb_nsta = self._int(line[32:35]) ms_mag = self._float(line[36:39]) ms_nsta = self._int(line[39:42]) mag1 = self._float(line[42:45]) mag1_type = line[45:47] mag1_source_code = line[47:51].strip() mag2 = self._float(line[51:54]) mag2_type = line[54:56] mag2_source_code = line[56:60].strip() evid = event.resource_id.id.split('/')[-1] origin = event.origins[0] self._store_uncertainty(origin.time_errors, orig_time_stderr) self._store_uncertainty(origin.latitude_errors, self._lat_err_to_deg(latitude_stderr)) self._store_uncertainty(origin.longitude_errors, self._lon_err_to_deg(longitude_stderr, origin.latitude)) self._store_uncertainty(origin.depth_errors, depth_stderr, scale=1000) if mb_mag is not None: mag = Magnitude() res_id = '/'.join((res_id_prefix, 'magnitude', evid, 'mb')) mag.resource_id = ResourceIdentifier(id=res_id) mag.creation_info = CreationInfo(agency_id='USGS-NEIC') mag.mag = mb_mag mag.magnitude_type = 'Mb' mag.station_count = mb_nsta mag.origin_id = origin.resource_id event.magnitudes.append(mag) if ms_mag is not None: mag = Magnitude() res_id = '/'.join((res_id_prefix, 'magnitude', evid, 'ms')) mag.resource_id = ResourceIdentifier(id=res_id) mag.creation_info = CreationInfo(agency_id='USGS-NEIC') mag.mag = ms_mag mag.magnitude_type = 'Ms' mag.station_count = ms_nsta mag.origin_id = origin.resource_id event.magnitudes.append(mag) if mag1 is not None: mag = Magnitude() mag1_id = mag1_type.lower() res_id = '/'.join((res_id_prefix, 'magnitude', evid, mag1_id)) mag.resource_id = ResourceIdentifier(id=res_id) mag.creation_info = CreationInfo(agency_id=mag1_source_code) mag.mag = mag1 mag.magnitude_type = mag1_type mag.origin_id = origin.resource_id event.magnitudes.append(mag) if mag2 is not None: mag = Magnitude() mag2_id = mag2_type.lower() if mag2_id == mag1_id: mag2_id += '2' res_id = '/'.join((res_id_prefix, 'magnitude', evid, mag2_id)) mag.resource_id = ResourceIdentifier(id=res_id) mag.creation_info = CreationInfo(agency_id=mag2_source_code) mag.mag = mag2 mag.magnitude_type = mag2_type mag.origin_id = origin.resource_id event.magnitudes.append(mag)
def calculate_moment_magnitudes(cat, output_file): """ :param cat: obspy.core.event.Catalog object. """ Mws = [] Mls = [] Mws_std = [] for event in cat: if not event.origins: print "No origin for event %s" % event.resource_id continue if not event.magnitudes: print "No magnitude for event %s" % event.resource_id continue origin_time = event.origins[0].time local_magnitude = event.magnitudes[0].mag #if local_magnitude < 1.0: #continue moments = [] source_radii = [] corner_frequencies = [] for pick in event.picks: # Only p phase picks. if pick.phase_hint.lower() == "p": radiation_pattern = 0.52 velocity = V_P k = 0.32 elif pick.phase_hint.lower() == "s": radiation_pattern = 0.63 velocity = V_S k = 0.21 else: continue distance = (pick.time - origin_time) * velocity if distance <= 0.0: continue stream = get_corresponding_stream(pick.waveform_id, pick.time, PADDING) if stream is None or len(stream) != 3: continue omegas = [] corner_freqs = [] for trace in stream: # Get the index of the pick. pick_index = int(round((pick.time - trace.stats.starttime) / \ trace.stats.delta)) # Choose date window 0.5 seconds before and 1 second after pick. data_window = trace.data[pick_index - \ int(TIME_BEFORE_PICK * trace.stats.sampling_rate): \ pick_index + int(TIME_AFTER_PICK * trace.stats.sampling_rate)] # Calculate the spectrum. spec, freq = mtspec.mtspec(data_window, trace.stats.delta, 2) try: fit = fit_spectrum(spec, freq, pick.time - origin_time, spec.max(), 10.0) except: continue if fit is None: continue Omega_0, f_c, err, _ = fit Omega_0 = np.sqrt(Omega_0) omegas.append(Omega_0) corner_freqs.append(f_c) M_0 = 4.0 * np.pi * DENSITY * velocity ** 3 * distance * \ np.sqrt(omegas[0] ** 2 + omegas[1] ** 2 + omegas[2] ** 2) / \ radiation_pattern r = 3 * k * V_S / sum(corner_freqs) moments.append(M_0) source_radii.append(r) corner_frequencies.extend(corner_freqs) if not len(moments): print "No moments could be calculated for event %s" % \ event.resource_id.resource_id continue # Calculate the seismic moment via basic statistics. moments = np.array(moments) moment = moments.mean() moment_std = moments.std() corner_frequencies = np.array(corner_frequencies) corner_frequency = corner_frequencies.mean() corner_frequency_std = corner_frequencies.std() # Calculate the source radius. source_radii = np.array(source_radii) source_radius = source_radii.mean() source_radius_std = source_radii.std() # Calculate the stress drop of the event based on the average moment and # source radii. stress_drop = (7 * moment) / (16 * source_radius ** 3) stress_drop_std = np.sqrt((stress_drop ** 2) * \ (((moment_std ** 2) / (moment ** 2)) + \ (9 * source_radius * source_radius_std ** 2))) if source_radius > 0 and source_radius_std < source_radius: print "Source radius:", source_radius, " Std:", source_radius_std print "Stress drop:", stress_drop / 1E5, " Std:", stress_drop_std / 1E5 Mw = 2.0 / 3.0 * (np.log10(moment) - 9.1) Mw_std = 2.0 / 3.0 * moment_std / (moment * np.log(10)) Mws_std.append(Mw_std) Mws.append(Mw) Mls.append(local_magnitude) calc_diff = abs(Mw - local_magnitude) Mw = ("%.3f" % Mw).rjust(7) Ml = ("%.3f" % local_magnitude).rjust(7) diff = ("%.3e" % calc_diff).rjust(7) ret_string = colorama.Fore.GREEN + \ "For event %s: Ml=%s | Mw=%s | " % (event.resource_id.resource_id, Ml, Mw) if calc_diff >= 1.0: ret_string += colorama.Fore.RED ret_string += "Diff=%s" % diff ret_string += colorama.Fore.GREEN ret_string += " | Determined at %i stations" % len(moments) ret_string += colorama.Style.RESET_ALL print ret_string mag = Magnitude() mag.mag = Mw mag.mag_errors.uncertainty = Mw_std mag.magnitude_type = "Mw" mag.origin_id = event.origins[0].resource_id mag.method_id = "smi:com.github/krischer/moment_magnitude_calculator/automatic/1" mag.station_count = len(moments) mag.evaluation_mode = "automatic" mag.evaluation_status = "preliminary" mag.comments.append(Comment( \ "Seismic Moment=%e Nm; standard deviation=%e" % (moment, moment_std))) mag.comments.append(Comment("Custom fit to Boatwright spectrum")) if source_radius > 0 and source_radius_std < source_radius: mag.comments.append(Comment( \ "Source radius=%.2fm; standard deviation=%.2f" % (source_radius, source_radius_std))) event.magnitudes.append(mag) print "Writing output file..." cat.write(output_file, format="quakeml")
def calculate_moment_magnitudes(cat, output_file): """ :param cat: obspy.core.event.Catalog object. """ Mws = [] Mls = [] Mws_std = [] for event in cat: if not event.origins: print "No origin for event %s" % event.resource_id continue if not event.magnitudes: print "No magnitude for event %s" % event.resource_id continue origin_time = event.origins[0].time local_magnitude = event.magnitudes[0].mag #if local_magnitude < 1.0: #continue moments = [] source_radii = [] corner_frequencies = [] for pick in event.picks: # Only p phase picks. if pick.phase_hint.lower() == "p": radiation_pattern = 0.52 velocity = V_P k = 0.32 elif pick.phase_hint.lower() == "s": radiation_pattern = 0.63 velocity = V_S k = 0.21 else: continue distance = (pick.time - origin_time) * velocity if distance <= 0.0: continue stream = get_corresponding_stream(pick.waveform_id, pick.time, PADDING) if stream is None or len(stream) != 3: continue omegas = [] corner_freqs = [] for trace in stream: # Get the index of the pick. pick_index = int(round((pick.time - trace.stats.starttime) / \ trace.stats.delta)) # Choose date window 0.5 seconds before and 1 second after pick. data_window = trace.data[pick_index - \ int(TIME_BEFORE_PICK * trace.stats.sampling_rate): \ pick_index + int(TIME_AFTER_PICK * trace.stats.sampling_rate)] # Calculate the spectrum. spec, freq = mtspec.mtspec(data_window, trace.stats.delta, 2) try: fit = fit_spectrum(spec, freq, pick.time - origin_time, spec.max(), 10.0) except: continue if fit is None: continue Omega_0, f_c, err, _ = fit Omega_0 = np.sqrt(Omega_0) omegas.append(Omega_0) corner_freqs.append(f_c) M_0 = 4.0 * np.pi * DENSITY * velocity ** 3 * distance * \ np.sqrt(omegas[0] ** 2 + omegas[1] ** 2 + omegas[2] ** 2) / \ radiation_pattern r = 3 * k * V_S / sum(corner_freqs) moments.append(M_0) source_radii.append(r) corner_frequencies.extend(corner_freqs) if not len(moments): print "No moments could be calculated for event %s" % \ event.resource_id.resource_id continue # Calculate the seismic moment via basic statistics. moments = np.array(moments) moment = moments.mean() moment_std = moments.std() corner_frequencies = np.array(corner_frequencies) corner_frequency = corner_frequencies.mean() corner_frequency_std = corner_frequencies.std() # Calculate the source radius. source_radii = np.array(source_radii) source_radius = source_radii.mean() source_radius_std = source_radii.std() # Calculate the stress drop of the event based on the average moment and # source radii. stress_drop = (7 * moment) / (16 * source_radius ** 3) stress_drop_std = np.sqrt((stress_drop ** 2) * \ (((moment_std ** 2) / (moment ** 2)) + \ (9 * source_radius * source_radius_std ** 2))) if source_radius > 0 and source_radius_std < source_radius: print "Source radius:", source_radius, " Std:", source_radius_std print "Stress drop:", stress_drop / 1E5, " Std:", stress_drop_std / 1E5 Mw = 2.0 / 3.0 * (np.log10(moment) - 9.1) Mw_std = 2.0 / 3.0 * moment_std / (moment * np.log(10)) Mws_std.append(Mw_std) Mws.append(Mw) Mls.append(local_magnitude) calc_diff = abs(Mw - local_magnitude) Mw = ("%.3f" % Mw).rjust(7) Ml = ("%.3f" % local_magnitude).rjust(7) diff = ("%.3e" % calc_diff).rjust(7) ret_string = colorama.Fore.GREEN + \ "For event %s: Ml=%s | Mw=%s | " % (event.resource_id.resource_id, Ml, Mw) if calc_diff >= 1.0: ret_string += colorama.Fore.RED ret_string += "Diff=%s" % diff ret_string += colorama.Fore.GREEN ret_string += " | Determined at %i stations" % len(moments) ret_string += colorama.Style.RESET_ALL print ret_string mag = Magnitude() mag.mag = Mw mag.mag_errors.uncertainty = Mw_std mag.magnitude_type = "Mw" mag.origin_id = event.origins[0].resource_id mag.method_id = "Custom fit to Boatwright spectrum" mag.station_count = len(moments) mag.evaluation_mode = "automatic" mag.evaluation_status = "preliminary" mag.comments.append(Comment( \ "Seismic Moment=%e Nm; standard deviation=%e" % (moment, moment_std))) if source_radius > 0 and source_radius_std < source_radius: mag.comments.append(Comment( \ "Source radius=%.2fm; standard deviation=%.2f" % (source_radius, source_radius_std))) event.magnitudes.append(mag) print "Writing output file..." cat.write(output_file, format="quakeml")
def _parseRecordE(self, line, event): """ Parses the 'error and magnitude' record E """ orig_time_stderr = self._float(line[2:7]) latitude_stderr = self._float(line[8:14]) longitude_stderr = self._float(line[15:21]) depth_stderr = self._float(line[22:27]) mb_mag = self._float(line[28:31]) mb_nsta = self._int(line[32:35]) Ms_mag = self._float(line[36:39]) Ms_nsta = self._int(line[39:42]) mag1 = self._float(line[42:45]) mag1_type = line[45:47] mag1_source_code = line[47:51].strip() mag2 = self._float(line[51:54]) mag2_type = line[54:56] mag2_source_code = line[56:60].strip() evid = event.resource_id.id.split("/")[-1] origin = event.origins[0] self._storeUncertainty(origin.time_errors, orig_time_stderr) self._storeUncertainty(origin.latitude_errors, self._latErrToDeg(latitude_stderr)) self._storeUncertainty(origin.longitude_errors, self._lonErrToDeg(longitude_stderr, origin.latitude)) self._storeUncertainty(origin.depth_errors, depth_stderr, scale=1000) if mb_mag is not None: mag = Magnitude() res_id = "/".join((res_id_prefix, "magnitude", evid, "mb")) mag.resource_id = ResourceIdentifier(id=res_id) mag.creation_info = CreationInfo(agency_id="USGS-NEIC") mag.mag = mb_mag mag.magnitude_type = "Mb" mag.station_count = mb_nsta mag.origin_id = origin.resource_id event.magnitudes.append(mag) if Ms_mag is not None: mag = Magnitude() res_id = "/".join((res_id_prefix, "magnitude", evid, "ms")) mag.resource_id = ResourceIdentifier(id=res_id) mag.creation_info = CreationInfo(agency_id="USGS-NEIC") mag.mag = Ms_mag mag.magnitude_type = "Ms" mag.station_count = Ms_nsta mag.origin_id = origin.resource_id event.magnitudes.append(mag) if mag1 is not None: mag = Magnitude() mag1_id = mag1_type.lower() res_id = "/".join((res_id_prefix, "magnitude", evid, mag1_id)) mag.resource_id = ResourceIdentifier(id=res_id) mag.creation_info = CreationInfo(agency_id=mag1_source_code) mag.mag = mag1 mag.magnitude_type = mag1_type mag.origin_id = origin.resource_id event.magnitudes.append(mag) if mag2 is not None: mag = Magnitude() mag2_id = mag2_type.lower() if mag2_id == mag1_id: mag2_id += "2" res_id = "/".join((res_id_prefix, "magnitude", evid, mag2_id)) mag.resource_id = ResourceIdentifier(id=res_id) mag.creation_info = CreationInfo(agency_id=mag2_source_code) mag.mag = mag2 mag.magnitude_type = mag2_type mag.origin_id = origin.resource_id event.magnitudes.append(mag)
def _read_single_event(event_file, locate_dir, units, local_mag_ph): """ Parse an event file from QuakeMigrate into an obspy Event object. Parameters ---------- event_file : `pathlib.Path` object Path to .event file to read. locate_dir : `pathlib.Path` object Path to locate directory (contains "events", "picks" etc. directories). units : {"km", "m"} Grid projection coordinates for QM LUT (determines units of depths and uncertainties in the .event files). local_mag_ph : {"S", "P"} Amplitude measurement used to calculate local magnitudes. Returns ------- event : `obspy.Event` object Event object populated with all available information output by :class:`~quakemigrate.signal.scan.locate()`, including event locations and uncertainties, picks, and amplitudes and magnitudes if available. """ # Parse information from event file event_info = pd.read_csv(event_file).iloc[0] event_uid = str(event_info["EventID"]) # Set distance conversion factor (from units of QM LUT projection units). if units == "km": factor = 1e3 elif units == "m": factor = 1 else: raise AttributeError(f"units must be 'km' or 'm'; not {units}") # Create event object to store origin and pick information event = Event() event.extra = AttribDict() event.resource_id = str(event_info["EventID"]) event.creation_info = CreationInfo(author="QuakeMigrate", version=quakemigrate.__version__) # Add COA info to extra event.extra.coa = {"value": event_info["COA"], "namespace": ns} event.extra.coa_norm = {"value": event_info["COA_NORM"], "namespace": ns} event.extra.trig_coa = {"value": event_info["TRIG_COA"], "namespace": ns} event.extra.dec_coa = {"value": event_info["DEC_COA"], "namespace": ns} event.extra.dec_coa_norm = { "value": event_info["DEC_COA_NORM"], "namespace": ns } # Determine location of cut waveform data - add to event object as a # custom extra attribute. mseed = locate_dir / "raw_cut_waveforms" / event_uid event.extra.cut_waveforms_file = { "value": str(mseed.with_suffix(".m").resolve()), "namespace": ns } if (locate_dir / "real_cut_waveforms").exists(): mseed = locate_dir / "real_cut_waveforms" / event_uid event.extra.real_cut_waveforms_file = { "value": str(mseed.with_suffix(".m").resolve()), "namespace": ns } if (locate_dir / "wa_cut_waveforms").exists(): mseed = locate_dir / "wa_cut_waveforms" / event_uid event.extra.wa_cut_waveforms_file = { "value": str(mseed.with_suffix(".m").resolve()), "namespace": ns } # Create origin with spline location and set to preferred event origin. origin = Origin() origin.method_id = "spline" origin.longitude = event_info["X"] origin.latitude = event_info["Y"] origin.depth = event_info["Z"] * factor origin.time = UTCDateTime(event_info["DT"]) event.origins = [origin] event.preferred_origin_id = origin.resource_id # Create origin with gaussian location and associate with event origin = Origin() origin.method_id = "gaussian" origin.longitude = event_info["GAU_X"] origin.latitude = event_info["GAU_Y"] origin.depth = event_info["GAU_Z"] * factor origin.time = UTCDateTime(event_info["DT"]) event.origins.append(origin) ouc = OriginUncertainty() ce = ConfidenceEllipsoid() ce.semi_major_axis_length = event_info["COV_ErrY"] * factor ce.semi_intermediate_axis_length = event_info["COV_ErrX"] * factor ce.semi_minor_axis_length = event_info["COV_ErrZ"] * factor ce.major_axis_plunge = 0 ce.major_axis_azimuth = 0 ce.major_axis_rotation = 0 ouc.confidence_ellipsoid = ce ouc.preferred_description = "confidence ellipsoid" # Set uncertainties for both as the gaussian uncertainties for origin in event.origins: origin.longitude_errors.uncertainty = kilometer2degrees( event_info["GAU_ErrX"] * factor / 1e3) origin.latitude_errors.uncertainty = kilometer2degrees( event_info["GAU_ErrY"] * factor / 1e3) origin.depth_errors.uncertainty = event_info["GAU_ErrZ"] * factor origin.origin_uncertainty = ouc # Add OriginQuality info to each origin? for origin in event.origins: origin.origin_type = "hypocenter" origin.evaluation_mode = "automatic" # --- Handle picks file --- pick_file = locate_dir / "picks" / event_uid if pick_file.with_suffix(".picks").is_file(): picks = pd.read_csv(pick_file.with_suffix(".picks")) else: return None for _, pickline in picks.iterrows(): station = str(pickline["Station"]) phase = str(pickline["Phase"]) wid = WaveformStreamID(network_code="", station_code=station) for method in ["modelled", "autopick"]: pick = Pick() pick.extra = AttribDict() pick.waveform_id = wid pick.method_id = method pick.phase_hint = phase if method == "autopick" and str(pickline["PickTime"]) != "-1": pick.time = UTCDateTime(pickline["PickTime"]) pick.time_errors.uncertainty = float(pickline["PickError"]) pick.extra.snr = { "value": float(pickline["SNR"]), "namespace": ns } elif method == "modelled": pick.time = UTCDateTime(pickline["ModelledTime"]) else: continue event.picks.append(pick) # --- Handle amplitudes file --- amps_file = locate_dir / "amplitudes" / event_uid if amps_file.with_suffix(".amps").is_file(): amps = pd.read_csv(amps_file.with_suffix(".amps")) i = 0 for _, ampsline in amps.iterrows(): wid = WaveformStreamID(seed_string=ampsline["id"]) noise_amp = ampsline["Noise_amp"] / 1000 # mm to m for phase in ["P_amp", "S_amp"]: amp = Amplitude() if pd.isna(ampsline[phase]): continue amp.generic_amplitude = ampsline[phase] / 1000 # mm to m amp.generic_amplitude_errors.uncertainty = noise_amp amp.unit = "m" amp.type = "AML" amp.method_id = phase amp.period = 1 / ampsline[f"{phase[0]}_freq"] amp.time_window = TimeWindow( reference=UTCDateTime(ampsline[f"{phase[0]}_time"])) # amp.pick_id = ? amp.waveform_id = wid # amp.filter_id = ? amp.magnitude_hint = "ML" amp.evaluation_mode = "automatic" amp.extra = AttribDict() try: amp.extra.filter_gain = { "value": ampsline[f"{phase[0]}_filter_gain"], "namespace": ns } amp.extra.avg_amp = { "value": ampsline[f"{phase[0]}_avg_amp"] / 1000, # m "namespace": ns } except KeyError: pass if phase[0] == local_mag_ph and not pd.isna(ampsline["ML"]): i += 1 stat_mag = StationMagnitude() stat_mag.extra = AttribDict() # stat_mag.origin_id = ? local_mag_loc stat_mag.mag = ampsline["ML"] stat_mag.mag_errors.uncertainty = ampsline["ML_Err"] stat_mag.station_magnitude_type = "ML" stat_mag.amplitude_id = amp.resource_id stat_mag.extra.picked = { "value": ampsline["is_picked"], "namespace": ns } stat_mag.extra.epi_dist = { "value": ampsline["epi_dist"], "namespace": ns } stat_mag.extra.z_dist = { "value": ampsline["z_dist"], "namespace": ns } event.station_magnitudes.append(stat_mag) event.amplitudes.append(amp) mag = Magnitude() mag.extra = AttribDict() mag.mag = event_info["ML"] mag.mag_errors.uncertainty = event_info["ML_Err"] mag.magnitude_type = "ML" # mag.origin_id = ? mag.station_count = i mag.evaluation_mode = "automatic" mag.extra.r2 = {"value": event_info["ML_r2"], "namespace": ns} event.magnitudes = [mag] event.preferred_magnitude_id = mag.resource_id return event
def write_qml(config, sourcepar): if not config.options.qml_file: return qml_file = config.options.qml_file cat = read_events(qml_file) evid = config.hypo.evid try: ev = [e for e in cat if evid in str(e.resource_id)][0] except Exception: logging.warning('Unable to find evid "{}" in QuakeML file. ' 'QuakeML output will not be written.'.format(evid)) origin = ev.preferred_origin() if origin is None: origin = ev.origins[0] origin_id = origin.resource_id origin_id_strip = origin_id.id.split('/')[-1] origin_id_strip = origin_id_strip.replace(config.smi_strip_from_origin_id, '') # Common parameters ssp_version = get_versions()['version'] method_id = config.smi_base + '/sourcespec/' + ssp_version cr_info = CreationInfo() cr_info.agency_id = config.agency_id if config.author is None: author = '{}@{}'.format(getuser(), gethostname()) else: author = config.author cr_info.author = author cr_info.creation_time = UTCDateTime() means = sourcepar.means_weight errors = sourcepar.errors_weight stationpar = sourcepar.station_parameters # Magnitude mag = Magnitude() _id = config.smi_magnitude_template.replace('$SMI_BASE', config.smi_base) _id = _id.replace('$ORIGIN_ID', origin_id_strip) mag.resource_id = ResourceIdentifier(id=_id) mag.method_id = ResourceIdentifier(id=method_id) mag.origin_id = origin_id mag.magnitude_type = 'Mw' mag.mag = means['Mw'] mag_err = QuantityError() mag_err.uncertainty = errors['Mw'] mag_err.confidence_level = 68.2 mag.mag_errors = mag_err mag.station_count = len([_s for _s in stationpar.keys()]) mag.evaluation_mode = 'automatic' mag.creation_info = cr_info # Seismic moment -- It has to be stored in a MomentTensor object # which, in turn, is part of a FocalMechanism object mt = MomentTensor() _id = config.smi_moment_tensor_template.replace('$SMI_BASE', config.smi_base) _id = _id.replace('$ORIGIN_ID', origin_id_strip) mt.resource_id = ResourceIdentifier(id=_id) mt.derived_origin_id = origin_id mt.moment_magnitude_id = mag.resource_id mt.scalar_moment = means['Mo'] mt_err = QuantityError() mt_err.lower_uncertainty = errors['Mo'][0] mt_err.upper_uncertainty = errors['Mo'][1] mt_err.confidence_level = 68.2 mt.scalar_moment_errors = mt_err mt.method_id = method_id mt.creation_info = cr_info # And here is the FocalMechanism object fm = FocalMechanism() _id = config.smi_focal_mechanism_template.replace('$SMI_BASE', config.smi_base) _id = _id.replace('$ORIGIN_ID', origin_id_strip) fm.resource_id = ResourceIdentifier(id=_id) fm.triggering_origin_id = origin_id fm.method_id = ResourceIdentifier(id=method_id) fm.moment_tensor = mt fm.creation_info = cr_info ev.focal_mechanisms.append(fm) # Station magnitudes for statId in sorted(stationpar.keys()): par = stationpar[statId] st_mag = StationMagnitude() seed_id = statId.split()[0] _id = config.smi_station_magnitude_template.replace( '$SMI_MAGNITUDE_TEMPLATE', config.smi_magnitude_template) _id = _id.replace('$ORIGIN_ID', origin_id_strip) _id = _id.replace('$SMI_BASE', config.smi_base) _id = _id.replace('$WAVEFORM_ID', seed_id) st_mag.resource_id = ResourceIdentifier(id=_id) st_mag.origin_id = origin_id st_mag.mag = par['Mw'] st_mag.station_magnitude_type = 'Mw' st_mag.method_id = mag.method_id st_mag.creation_info = cr_info st_mag.waveform_id = WaveformStreamID(seed_string=seed_id) st_mag.extra = SSPExtra() st_mag.extra.moment = SSPTag(par['Mo']) st_mag.extra.corner_frequency = SSPTag(par['fc']) st_mag.extra.t_star = SSPTag(par['t_star']) ev.station_magnitudes.append(st_mag) st_mag_contrib = StationMagnitudeContribution() st_mag_contrib.station_magnitude_id = st_mag.resource_id mag.station_magnitude_contributions.append(st_mag_contrib) ev.magnitudes.append(mag) # Write other average parameters as custom tags ev.extra = SSPExtra() ev.extra.corner_frequency = SSPContainerTag() ev.extra.corner_frequency.value.value = SSPTag(means['fc']) ev.extra.corner_frequency.value.lower_uncertainty =\ SSPTag(errors['fc'][0]) ev.extra.corner_frequency.value.upper_uncertainty =\ SSPTag(errors['fc'][1]) ev.extra.corner_frequency.value.confidence_level = SSPTag(68.2) ev.extra.t_star = SSPContainerTag() ev.extra.t_star.value.value = SSPTag(means['t_star']) ev.extra.t_star.value.uncertainty = SSPTag(errors['t_star']) ev.extra.t_star.value.confidence_level = SSPTag(68.2) ev.extra.source_radius = SSPContainerTag() ev.extra.source_radius.value.value = SSPTag(means['ra']) ev.extra.source_radius.value.lower_uncertainty =\ SSPTag(errors['ra'][0]) ev.extra.source_radius.value.upper_uncertainty =\ SSPTag(errors['ra'][1]) ev.extra.source_radius.value.confidence_level = SSPTag(68.2) ev.extra.stress_drop = SSPContainerTag() ev.extra.stress_drop.value.value = SSPTag(means['bsd']) ev.extra.stress_drop.value.lower_uncertainty =\ SSPTag(errors['bsd'][0]) ev.extra.stress_drop.value.upper_uncertainty =\ SSPTag(errors['bsd'][1]) ev.extra.stress_drop.value.confidence_level = SSPTag(68.2) if config.set_preferred_magnitude: ev.preferred_magnitude_id = mag.resource_id.id qml_file_out = os.path.join(config.options.outdir, evid + '.xml') ev.write(qml_file_out, format='QUAKEML') logging.info('QuakeML file written to: ' + qml_file_out)